Datarim Doctor
Спецификация тонкой индексной схемы оперативных файлов: канонический regex, контракт YAML frontmatter, семантика 5-проходной миграции, контракт защиты от потери данных. Загружается по требованию /dr-doctor и /dr-init.
Обзор
Datarim Doctor — runtime-модуль знаний, определяющий контракт тонкого индекса. Оперативные файлы (tasks.md, backlog.md, activeContext.md) хранят однострочные указатели на задачи; описания живут в файлах datarim/tasks/{TASK-ID}-task-description.md с замкнутой YAML-схемой. progress.md и rolling-лог activeContext.md § Последние завершённые упразднены — история завершённых задач хранится только в documentation/archive/ и git log.
Зачем тонкие индексы
Оперативные файлы — это индексы, а не содержимое. Каждая строка отвечает на четыре вопроса: какая задача, какой статус, где описание. Никакой прозы, требований, контента плана в tasks.md / backlog.md.
- Ограниченный контекст — агенты читают 1 KB индекс, а не 100 KB монолит.
- Единый источник правды на задачу — описание, ACs, ограничения в одном файле.
- Greppable состояние — формат строки машиночитаем; смена статуса = 1-line diff.
- Идемпотентные миграции —
/dr-doctorможно запускать сколько угодно раз без drift.
Канонический Regex
^- ([A-Z]{2,10}-[0-9]{4}) · (STATUS) · P[0-3] · L[1-4] · (.{1,80}) → tasks/\1-task-description\.md$
Наборы статусов:
tasks.md:in_progress | blocked | not_startedbacklog.md:pending | blocked-pending | cancelled
Разделитель: · (U+00B7 MIDDLE DOT). Стрелка: → (U+2192). Заголовок: 1–80 символов, single-line, без →.
Контракт description-файла
У каждой задачи есть description-файл по пути datarim/tasks/{TASK-ID}-task-description.md с замкнутой 12-ключевой YAML-frontmatter:
---
id: <TASK-ID>
title: <string> # ≤ 80 символов
status: <enum>
priority: <enum> # P0|P1|P2|P3
complexity: <enum> # L1|L2|L3|L4
type: <string>
project: <string>
started: <date> # YYYY-MM-DD
parent: <TASK-ID|null>
related: <list[TASK-ID]>
prd: <relpath|null>
plan: <relpath|null>
---
Тело: 5 канонических секций (Overview / Acceptance Criteria / Constraints / Out of Scope / Related), не более 250 строк. Опционально ## Implementation Notes и ## Decisions.
Алгоритм миграции (6 проходов)
- Pass 1 — Description-файлы (build cache): обход legacy
### TASK-ID:заголовков; извлечение status/priority/complexity/type/started/parent/related/prd/plan; запись per-task файла с frontmatter. - Pass 2 — Оперативные файлы: перезапись
tasks.mdиbacklog.mdв виде одностройных индексов, сгруппированных по разделам. - Pass 3 — activeContext.md: конверсия legacy
**Current Task:**в список## Active Tasks(Active-Tasks-only mirror, ≤30 строк). - Pass 4 — миграция backlog-archive: AWK section-state machine + per-ID dispatch разделяет legacy
backlog-archive.mdнаdocumentation/archive/cancelled/(синтезированные стабы) и area-specificarchive-{TASK-ID}.mdдля завершённых записей (verify-or-synthesise вgeneral/); флаг--no-promptдля CI. - Pass 5 — post-fix re-scan: композиция existing scan dispatch в dry-run после
--fix; проверяет post-fix zero findings + сохранность.pre-v2.baksidecar + идемпотентность повторного запуска (второй--fix= no-op). - Pass 6 — миграция архивных секций оперативных файлов (TUNE-0085 v1.21.5, hardened TUNE-0088 v1.21.6): убирает
## Archivedизtasks.md/backlog.mdи### Archived/### Recently Archived/## Последние завершённыеизactiveContext.md— секции, нарушающие канонический thin-index контракт («one section only», v1.19.1). Четыре формы bullet'ов auto-detected (S1 arrow-link, S2 status-paren, S4 mid-bold-context, S3 plain-bold). Compound task IDs поддержаны (напримерDEV-1212-S8,DEV-1196-FOLLOWUP-lock-ownership-doc). Явный pointer→ documentation/archive/{path}.mdв теле bullet'а имеет приоритет над хардкоженным mappingprefix_to_area. Per bullet: verify canonical archive по resolved path → strip; отсутствует → defensivefindпо area subdirs (depth ≤ 3) — если найден с literal ID, strip-with-warning; иначе synthesise stub; collision → respect--conflict-policy. Headerless fallback: операционные файлы без archive-заголовка обрабатываются построчно; bullets с явным non-terminal статусом (in_progress,not_started,blocked, …) проходят как active content.
Idempotency guard: если нет ### TASK-ID: заголовков, нет legacy backlog-archive.md, и все bullet-строки совпадают с canonical regex — выход 0 немедленно. Дешёвый probe для /dr-init.
Контракт защиты от потери данных
Defence-in-depth вокруг --fix (TUNE-0077):
- Pre-write tarball backup —
umask 077архив пишется в${DATARIM_DOCTOR_BACKUP_DIR:-/tmp}/datarim-backup-{TS}.tgzдо любой мутации. Путь печатается в success-summary. - Sidecar copy — каждый legacy-файл, мутируемый Pass 4, получает
.pre-v2.bakрядом в дереве (operator-visible). - Инвариант —
EMITTED_COUNT >= PARSED_COUNT. Doctor считает task-записи до и после rewrite. Нарушение запускаетrestore_backup_and_die(): rm-rf мутированного состояния,tar -xzftarball обратно, exit 2. - Symlink-default uniformity — при
install.shdefault режиме~/.claude/scripts/datarim-doctor.sh— directory-symlink на canonical Datarim repo. Расхождение невозможно by construction; чужой v2-бинарь нельзя молча подложить.
Точки входа Self-Heal
/dr-doctor(всегда)/dr-init— probesdatarim-doctor.sh --quiet; предлагает/dr-doctor --fixпри non-compliance./dr-archivepre-archive gate —pre-archive-check.shпроверяет формат строк; обход через--no-schema-checkтолько во время in-flight миграции.
Edge cases
- Bash 3.2 (macOS по умолчанию) — инструмент использует two-pass grep+awk parser, БЕЗ NUL-delimited reads.
- Заголовок с символом → — экранирован или отклонён (regex запрещает). Оператор должен переименовать.
- Параллельный запуск —
flockна$DATARIM_ROOT/.dr-doctor.lock. Второй экземпляр выходит с кодом 3. - Path traversal — лексическая канонизация через
scripts/lib/canonicalise.sh(без I/O). Инструмент выходит с кодом 4.
Загружается
/dr-doctor(всегда)/dr-initself-heal (когда probe возвращает exit 1)/dr-archiveline-format gate (при сбое — для объяснения non-compliance)