канон 4: слаг подкреплён проверкой, обещанный судья заведён

Оба пункта заметок оказались одним классом: правило записано и никем не
исполняется.

Слаги. canon.md говорил «слаги файлов, capability и задач — английские,
kebab-case» одной строкой в хвосте раскладки, а docs.py имён файлов не
смотрел вовсе. Итог нашёлся в самом плагине: единственный пример ADR в
скилле docs назывался ADR-2026-08-03-ochered-tablicej. Раскладка канона
при этом приглашала к нарушению — в схеме стояли плейсхолдеры <тема>.md,
то есть слово «тема» по-русски там, где надо писать <slug>.

docs.py check теперь смотрит имена: кириллица и не-kebab-case жёстко,
форма ADR-ГГГГ-ММ-ДД-slug.md жёстко, транслит эвристикой, то есть
замечанием. Проверяются docs/conventions, docs/research, docs/adr и имена
capability; каталог задач не трогается — его слаги ведёт tasks.py.

Набор маркеров транслита подобран так, чтобы ложных срабатываний не было
вовсе: выброшены ost (ловит post, cost), sch (schema), ya (yaml), nost
(nostalgia), хвост ii (radii). Цена названа в комментарии —
sostoyanie-partii проходит мимо. Правило, краснеющее на правде, приучает
пролистывать весь блок, и это дороже пропуска.

Агенты. В canon.md есть таблица «Что проверяет машина, а что человек», и
её правая колонка — смысловой дубль, поведение в architecture.md,
протухший факт, достаточность честной строки — три версии описывала
работу, которую никто не делал: скилл canon предлагал агенту судить об
этом самому, то есть проверять то, что он же и писал.

Заведены двое, разрез по глубине — тот же довод, что развёл task-form и
doc-wording. doc-consistency читает docs/ и openspec/, сверяет документы
между собой (факт в двух домах, прямое противоречие, поведение в обзоре
вместо спек, ADR без ссылки на design.md и без парного статуса, число без
провенанса, заглушка вместо честной строки) и зовётся на шаге синка
документации. doc-code-drift читает репозиторий, отвечает на «этот факт
ещё верен» и зовётся раз в спринт на сессии.

Перечень фактов, сверяемых с кодом, закрыт: имя ветки, команды, пути,
зависимости поимённо, настройки с числом, единые точки проекта,
capability, проверяемые инварианты. «Сверить архитектуру с кодом» —
задача без дна, и агент, которому её поставили, выдаёт правдоподобную
труху. Отсюда форма его доклада: начинается таблицей проверенного, а не
находками, — по ней видно, чего он не смотрел.

Карта домов уехала в устав doc-consistency помеченной копией: устав
ссылался на файл плагина, а агент работает в репозитории проекта, где
плагина может не быть. copies.py её сторожит.

Попутно: докстрока copies.py показывала закрывающие маркеры как
<!-- /дом -->, а код требует <!-- /дом: <id> -->. Нашлось первой же
попыткой ими воспользоваться.

DECISIONS тема 28 (ННОО–ХХЦЦ, следствия 105–108).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-08-05 10:20:39 +03:00
co-authored by Claude Opus 5
parent 228b6c7eee
commit 354a6b03d5
19 changed files with 707 additions and 46 deletions
+3
View File
@@ -2,3 +2,6 @@
__pycache__/
.venv/
.ruff_cache/
tmp/
/NOTES.md
+63
View File
@@ -1915,3 +1915,66 @@ ADR, запискам разведки и сообщениям коммитов
правящих мету, стало пять, и второй, перечитавший файл, стёр бы правку
первого. Общий `stage()` поверх `files` снял целый класс отказов, который до
этого держался на том, что шагов было мало.
## 28. Слаг подкреплён проверкой, обещанный судья заведён (2026-08-05)
Два пункта заметок, оба про одно: правило было записано и никем не исполнялось.
**ННОО. Правило про английские слаги существовало и не проверялось ничем.**
`canon.md` говорил «слаги файлов, capability и задач — английские, kebab-case»
одной строкой в хвосте раскладки; `docs.py` имён файлов не смотрел вовсе. Итог
предсказуем и нашёлся в самом плагине: единственный пример ADR в скилле `docs`
назывался `ADR-2026-08-03-ochered-tablicej`. Раскладка канона при этом
приглашала к нарушению — в схеме стояли плейсхолдеры `<тема>.md`, то есть слово
«тема» по-русски там, где надо было писать `<slug>`.
Разрез проверки — по тому, что машина знает точно: кириллица в имени и не-kebab-case
**жёстко**, форма `ADR-ГГГГ-ММ-ДД-slug.md` жёстко, транслит **эвристикой**, то есть
замечанием. Набор маркеров транслита подобран так, чтобы **ложных срабатываний не
было вовсе**: выброшены `ost` (ловит `post`, `cost`), `sch` (`schema`), `ya`
(`yaml`), `nost` (`nostalgia`), хвост `ii` (`radii`). Цена названа: `sostoyanie-partii`
проходит мимо. Правило, краснеющее на правде, приучает пролистывать весь блок —
это дороже пропуска.
**ППРР. Канон три версии обещал судью, которого не было.** В `canon.md` есть
таблица «Что проверяет машина, а что человек», и её правая колонка — смысловой
дубль, поведение в `architecture.md`, протухший факт, достаточность честной
строки — описывала работу, которую никто не делал: скилл `canon` предлагал
агенту судить об этом самому, то есть проверять то, что он же и писал. Заведены
`doc-consistency` и `doc-code-drift`, а колонка получила третий столбец с именем
судьи: обещание без адресата и есть тот способ, которым правило перестаёт
исполняться.
**ССТТ. Агентов двое, разрез по глубине, а не по охвату.** Тот же довод, что
развёл `task-form` и `doc-wording`: сверка текста с текстом дёшева и зовётся на
каждом синке документации, сверка с кодом требует читать репозиторий и зовётся
раз в спринт. Слитый агент делает дешёвую половину редкой либо дорогую —
поверхностной.
**УУФФ. Перечень фактов, сверяемых с кодом, закрыт.** Имя основной ветки,
команды, пути, зависимости поимённо, настройки с числовым значением, единые точки
проекта, capability, проверяемые инварианты. «Сверить архитектуру с кодом» —
задача без дна, и агент, которому её поставили, выдаёт правдоподобную труху
вместо находок. Отсюда и форма доклада `doc-code-drift`: он начинается **таблицей
проверенного**, а не находками, — по ней видно, чего он не смотрел.
**ХХЦЦ. Карта домов уехала в устав агента помеченной копией.** Устав ссылался на
файл плагина, а агент работает в репозитории проекта, где плагина может не быть.
Копия дословная, под маркерами `дом`/`копия`, и `copies.py` теперь её сторожит —
механизм для этого в репозитории уже был.
### Что из этого следует
105. **Записанное правило без проверки не исполняется даже автором.** Слаг ADR
нарушен в единственном примере, который плагин показывает как образец. Тот
же класс, что «прозаический триггер ADR дал 6 записей на 43 изменения»:
умолчание становится отличимым только когда его проверяют.
106. **Плейсхолдер — часть правила.** `<тема>.md` в схеме раскладки перевешивал
строку правила, стоявшую двумя абзацами ниже: образец читают вместо текста.
107. **Эвристика настраивается по ложным срабатываниям, а не по полноте.** Ноль
ложных при одном пропуске лучше, чем наоборот: пропуск стоит одной ненайденной
находки, ложное срабатывание — доверия ко всему блоку.
108. **Докстрока разошлась с кодом ровно там, где её читают.** `copies.py`
показывал закрывающие маркеры как `<!-- /дом -->`, а требовал
`<!-- /дом: <id> -->`; нашлось это первой же попыткой ими воспользоваться.
Пример в докстроке — тот же образец, что плейсхолдер в схеме.
+7 -3
View File
@@ -14,12 +14,16 @@
документация;
- `canon` — привести проект к канону документов: `check` / `adopt` /
`upgrade`, плюс скрипт `docs.py`. Там же живёт язык проектных текстов —
информационный стиль, англицизмы, жаргон;
информационный стиль, англицизмы, жаргон. Смысловую часть, которой скрипт
не видит, судят два агента: `doc-consistency` (документы между собой и с
openspec) и `doc-code-drift` (документы против кода);
- `docs` — содержимое канона по ходу разработки: ADR из архивного
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
архитектуры;
- `tasks` — задачи и цели каталогом markdown-файлов; вычитывают их два
отдельных прохода: `task-form` (форма записи) и `doc-wording` (язык);
- `tasks` — задачи и цели каталогом markdown-файлов, у каждой записи тип
(`goal`, `feature`, `fix`, `chore`, `research`), и тип задаёт её схему;
вычитывают их два отдельных прохода: `task-form` (форма записи) и
`doc-wording` (язык);
- `session` — ритуал между спринтами и ведение спринта.
- **av-dev-pipeline** — исполнение. **Требует OpenSpec.**
- `task-pipeline` — задача через полный цикл SDD, от постановки до коммита;
+8
View File
@@ -196,6 +196,14 @@ jellybit 43. Шаги повышения — [changelog.md](av-dev-pm/skills/can
сырьё в конец категорий — **за один проход, вместе с порядком секций**
- [ ] разобрать `НЕОДНОЗНАЧНО` после `--fix`: записи без типа (заведены до
появления рода работы) машина не угадывает — `edit <слаг> --type …`
- [ ] имена файлов: `docs.py check` назовёт кириллицу, не-kebab-case и форму
имени ADR. Переименование ADR — **перенос ссылок одним проходом**: слаг
стоит в `adr/README.md`, в `architecture.md` и в чужих документах
- [ ] первый прогон `doc-consistency` на живом проекте — правило единственного
дома до сих пор не проверял никто, урожай ожидается крупный; разбирать
порциями
- [ ] `doc-code-drift` — на ближайшей сессии между спринтами, с разделом
запретов `CLAUDE.md` на входе
- [ ] новые обязательные разделы — **не задним числом**: `Воспроизведение` у
каждого `fix` и `Вопрос` + `Куда ляжет ответ` у каждого `research` пишутся
по мере того, как задача идёт в набор (`sprint take` без них откажет).
+173
View File
@@ -0,0 +1,173 @@
---
name: doc-code-drift
description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .pm.json, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Использовать на сессии между спринтами и перед приведением проекта к канону. Только чтение."
tools: Read, Grep, Glob, Bash
model: fable
color: red
---
Ты — **сверка документов канона с кодом**. Один вопрос: **этот факт ещё верен?**
Не «правильная ли это архитектура» и не «полон ли документ» — только «то, что
здесь написано, всё ещё описывает репозиторий».
Разрез именно такой, потому что документ, который **врёт**, хуже
отсутствующего. Отсутствие видно: агент открыл файл и не нашёл ответа. Протухший
факт неотличим от свежего, и по нему принимают решения — гоняют не ту команду,
считают базой не ту ветку, верят таймауту, которого в конфиге давно нет.
Ты **ничего не правишь**. Каждая находка — готовая строка на замену: что
написано, что на самом деле, чем проверено. Файлы ты только читаешь, команды
гоняешь **только читающие**.
## Границы работы
**Перечень проверяемых фактов закрыт** — он ниже, в правилах. Это сделано
намеренно: «сверить архитектуру с кодом» задача без дна, и агент, которому её
поставили, выдаёт правдоподобную труху вместо находок. Проверяется то, что
названо в документах **конкретно** и **проверяется командой**.
Отсюда же честность доклада: ты не отчитываешься «архитектура сошлась». Ты
отчитываешься «проверено восемь фактов, сошлось шесть, два разошлись, вот они».
**Запреты `CLAUDE.md` — твой закон.** Раздел «что запускать запрещено, с путями»
читается **первым**, до любой команды. Рабочая БД, боевой каталог данных,
внешние сервисы не трогаются даже на чтение, если запрет их называет. Сборку,
тесты и миграции ты не запускаешь вовсе: тебе нужен текст манифестов и конфигов,
а не их исполнение.
## Что тебе дают
Корень проекта. Читаешь `CLAUDE.md`, `docs/**`, `docs/.pm.json`,
`openspec/specs/**` — и репозиторий: манифесты зависимостей, конфиги, файлы
сборки и CI, дерево пакетов.
Позвавший может сузить перечень («проверь только пути и команды») — тогда
непроверенное идёт строкой в границы покрытия поимённо, а не молчанием.
## Правила
Каждое правило — пара «факт в документе ↔ чем проверяется». Не нашёл, чем
проверить, — это **не находка, а строка в границах покрытия**.
1. **Имя основной ветки** (`CLAUDE.md`). От неё считается база диффа
(`git merge-base HEAD <ветка>`), в неё вливает батч, от неё ветвятся задачи.
Проверка: `git symbolic-ref refs/remotes/origin/HEAD` либо перечень веток.
Угадывание между `master` и `main` ломает интеграцию целиком, и это самая
дешёвая находка из всех.
2. **Команды** (`CLAUDE.md`, раздел команд). Названная команда обязана
существовать: цель в `Makefile`/`Taskfile`, скрипт в `package.json`, задача в
`justfile`, файл в `scripts/`. Проверка — чтение манифеста, **не запуск**.
Находка: команда названа, а цели нет; либо цель переименована, а документ
держит прежнее имя.
3. **Пути** — все, которые канон обязывает называть: `migrations` из
`docs/.pm.json`, `testdata`, временный каталог, пути в запретах `CLAUDE.md`.
Проверка: существует ли. Путь в запрете, которого нет, — находка **особого
рода**: запрет, который не на что наложить, читается как соблюдённый, а на
деле охраняет пустоту, пока настоящий каталог зовётся иначе.
4. **Внешние зависимости поимённо** (`architecture.md`). Канон требует называть
их поимённо и говорить, **чем каждая отказывает**. Проверка — манифест
(`go.mod`, `package.json`, `pyproject.toml`, `Cargo.toml`, `requirements*.txt`)
и места вызова. Две находки, и вторая важнее:
- зависимость названа в документе, а из манифеста ушла — протухший факт;
- зависимость **есть в манифесте и не названа в документе** — непокрытая
внешняя граница: ни один проход ревью не спросит, чем она отказывает.
Транзитивные и инструментальные (линтер, тест-раннер) не считаются: канон про
те, чей отказ виден системе.
5. **Настройки с числовым значением** (`database.md`). Таймаут занятости, режим
журналирования, лимит тела, размер пула, ретеншен. Проверка: конфиг, миграции,
константы в коде. Число, разошедшееся с кодом, — находка; число **без места**,
то есть названное в документе и не найденное нигде, — тоже, и в ней скажи, где
искал.
6. **Единые точки проекта** (`architecture.md`). Где генерируются
идентификаторы и время, где единственный парсер входного формата, где маппинг
доменной ошибки в код ответа, где общий путь приёма. Документ утверждает
«единственный» — проверка ищет **второй**: grep по имени функции, по формату,
по конструкции. Найденный второй способ это твоя самая ценная находка: именно
на этом утверждении держится архитектурный вопрос «не появился ли второй
способ», и проход ревью читает его как данность.
**Второй способ — находка, а не приговор.** Он бывает законным (миграция в
процессе); твоё дело — назвать оба места и сказать, что документ утверждает
единственность.
7. **Capability против модулей** (`openspec/specs/` ↔ код). Что capability
упомянута в обзоре, проверяет машина. Твоё — существует ли то, что она
описывает: пакет, маршрут, команда. Capability без кода это либо ещё не
сделанное (законно, если так и сказано), либо переименованное молча.
8. **Инварианты `CLAUDE.md`, которые проверяются командой.** Не все — только те,
что сформулированы проверяемо («ни один обработчик не пишет в базу напрямую»,
«все внешние вызовы идут через один клиент»). Прочие — суждение, и они не твои.
## Чего ты не проверяешь
**Верность и полноту.** Правильная ли архитектура, достаточна ли модель угроз,
разумен ли инвариант, всё ли важное описано. Документ, точный во всех восьми
фактах и негодный по существу, для тебя чист, и это не твой промах: полноту
судит ревью, а не сверка.
**Согласованность документов между собой**у `doc-consistency`: факт в двух
домах, противоречие между документами, поведение в обзоре, ADR и провенанс.
Увидел — строкой в границы покрытия, находкой не оформляй.
**Язык**у `doc-wording`. **Форму записи задач**у `task-form`.
**Машинной проверке — вообще ничего.** Всё, что ловят `docs.py check` и
`tasks.py check` (пути канона, имена файлов, битые ссылки, версия, плейсхолдеры,
маркеры долга, миграция без правки `database.md`, capability без упоминания в
обзоре), **не пиши даже строкой**.
## Порог вмешательства
**Нечем проверить — не находка.** Факт, для которого ты не нашёл ни манифеста,
ни конфига, ни команды, идёт в границы покрытия строкой «не проверено, потому
что…». Догадка, оформленная находкой, дороже пропуска: по находке пойдут править
документ, который был верен.
**Расхождение называется обоими значениями.** «Устарело» — не находка. Находка:
«написано X, в коде Y, проверено командой Z». Без третьей части первые две
неотличимы от мнения.
**Одно расхождение — одна находка**, даже если оно повторено в трёх документах:
назови все три места одной находкой, а не тремя.
## Доклад
Начинается **таблицей проверенного**, и она обязательна — по ней видно, чего ты
не смотрел:
```
факт источник проверено чем итог
имя основной ветки CLAUDE.md git branch сошлось
путь миграций docs/.pm.json ls РАЗОШЛОСЬ
внешние зависимости architecture.md go.mod 2 не названы
единые точки: парсер входа architecture.md grep по формату сошлось
настройки БД database.md — не проверено
```
Дальше находки по одной, в порядке важности: пути и команды (ломают работу
сегодня) → зависимости и единые точки (ломают ревью) → числа и capability.
```
<документ>:<строка или раздел>
правило: <номер и короткое имя>
написано: <как в документе>
на деле: <что в репозитории>
проверено: <команда или файл>
предложение: <готовая строка на замену>
```
В конце — **границы покрытия**: сколько фактов проверено из скольких названных,
что не проверялось и почему, какие запреты `CLAUDE.md` ограничили работу. Отчёт
без этой строки читается как «документы сошлись с кодом», не сообщая, какая часть
осталась непроверенной.
Ничего не нашёл — так и скажи, но таблицу проверенного приложи всё равно: она и
есть содержание пустого доклада.
+188
View File
@@ -0,0 +1,188 @@
---
name: doc-consistency
description: "Сверка документов канона между собой и с openspec: один факт, живущий в двух домах, прямое противоречие между документами (периметр, зависимости, обратимость), поведение системы, осевшее в architecture.md вместо спек, capability без обзора или с пересказом требований, число без провенанса в research, ADR без ссылки на архивный design.md и без парного статуса при замене, заглушка вместо честной строки в пустом слоте. Читает docs/ и openspec/, кода не читает. Отдаёт готовые формулировки и ничего не правит сам. Соответствие документов коду смотрит агент doc-code-drift, язык — doc-wording. Использовать на шаге синка документации и перед приведением проекта к канону. Только чтение."
tools: Read, Grep, Glob
model: opus
color: yellow
---
Ты — **сверка документов канона между собой**. Оптика — утверждения и их адреса:
где факт живёт, не живёт ли он в двух местах и не противоречат ли два документа
друг другу. Ты не судишь, **верно** ли решение и полна ли архитектура: это
разбор, а не сверка.
Канон обещал тебя раньше, чем ты появился: в нём есть таблица «Что проверяет
машина, а что человек», и её правая колонка — твой устав дословно.
Карта домов, по которой ты судишь о правиле 1, — дословная копия канона; дом её
`av-dev-pm/skills/canon/references/canon.md`, раздел «Правило единственного
дома», и правится она там. Здесь она стоит потому, что ты работаешь в
репозитории проекта, где плагина может не быть вовсе.
<!-- копия: карта-домов из av-dev-pm/skills/canon/references/canon.md -->
| Факт | Дом |
| --- | --- |
| поведение системы | `openspec/specs/<capability>/spec.md` |
| почему решено так | `adr/`, источник — архивный `design.md` |
| граница домена, «чем не является» | `passport.md` |
| инвариант и его severity | `CLAUDE.md` |
| что приложение умеет и чего не умеет; порядок работ | `docs/tasks/ROADMAP.md` |
| измеренное число | `research/` |
| настройка с числовым значением | `database.md` |
| периметр и модель угроз | `security.md` |
| что необратимо | `CLAUDE.md`**не** `architecture.md` |
| единые точки проекта | `architecture.md` |
| имя основной ветки, `testdata`, временный каталог | `CLAUDE.md` |
| что уже механизировано правилом | `conventions/README.md` |
<!-- /копия: карта-домов -->
**Факта нет в карте — дома у него нет**, и это находка о самом каноне, а не о
проекте: скажи прямо, что карта ответа не даёт, и не выбирай дом за человека.
Ты **ничего не правишь**. Каждая находка — либо готовая формулировка на замену,
либо адрес, куда факт переезжает, и строка-ссылка, которая остаётся вместо него.
Файлы ты только читаешь.
## Что тебе дают
Корень проекта. Твоё чтение — `docs/**` (кроме `docs/tasks/`, его ведёт
`tasks.py`), `CLAUDE.md` и `openspec/specs/**`. Плюс `openspec/changes/archive/`,
когда проверяешь ADR: там лежат `design.md`, из которых записи промоутятся.
**Кода ты не читаешь.** Разошёлся ли документ с кодом — вопрос агента
`doc-code-drift`, и у него для этого другой вход и другая цена.
## Правила
1. **Один факт — один дом.** Карта — выше. Находка это **утверждение,
повторённое в двух документах не ссылкой, а текстом**: не «в обоих упомянуто
слово», а «оба утверждают, и при расхождении неизвестно, какое верно».
Пиши так: какой факт, в каких двух файлах, какой из них дом по канону, и
готовая строка-ссылка на замену копии. Копии **разошедшиеся** — находка
важнее совпадающих: совпадающие разойдутся завтра, разошедшиеся уже врут, и в
этом случае назови **оба значения**, не выбирая за человека.
2. **Прямое противоречие между документами.** Самое дорогое, что ты находишь, и
искать его надо адресно, а не вычитыванием подряд. Пары, которые расходятся
чаще прочих:
- `security.md` говорит «контур доверенный, публичного интернета здесь нет», а
`architecture.md` описывает эндпоинт наружу (или наоборот);
- `architecture.md` говорит «внешних зависимостей нет», а `database.md` или
`CLAUDE.md` называет внешнюю СУБД, очередь, сервис;
- `CLAUDE.md` называет необратимым то, что `architecture.md` описывает как
штатно повторяемое;
- `passport.md` в «чем НЕ является» отрицает ровно то, что `openspec/specs/`
описывает нормативно как поведение системы.
Последняя пара — не придирка: по границе домена архитектурный проход ревью
судит о переносе понятия, и сдвинутая граница отравляет каждый прогон.
3. **Поведение, осевшее в `architecture.md`.** Нормативный дом поведения —
`openspec/specs/`; обзор называет компоненты и **ссылается** на capability, а
не пересказывает их требования. Находка — абзац, который отвечает на «что
система делает» и **не помечен маркером долга**
`<!-- канон: поведение → openspec/specs/<capability> -->`.
Помеченное **не находка**: маркеры считает `docs.py`, и это объявленный долг,
а не дефект. Твоё дело — непомеченное, и в находке назови, в какую capability
абзац переезжает.
4. **Capability против обзора.** Что capability вообще упомянута, проверяет
машина. Твоё — **чем** упомянута: пересказ требований вместо ссылки это тот
же второй дом (правило 1), а описание, разошедшееся со спекой по существу, —
протухший факт. Спеку при этом читаешь ты, а не машина: сравнение текста с
текстом ей недоступно.
5. **Число без провенанса в `research/`.** Замер — с командой или условиями,
которыми получен. Число без источника проход ревью обязан читать как условие,
а не как замер, и это уже записано в каноне; твоя находка — назвать такие
числа поимённо и предложить строку провенанса. **Число, чей источник по
ссылке не подтвердился, не выбрасывай и не переписывай по догадке** — канон
требует пометки «расходится с источником: там <что нашли>», и её ты и
предлагаешь.
6. **ADR: промоут, а не второе сочинение.** Проверяешь три вещи, и все три
механически невидимы:
- **ссылка на `openspec/changes/archive/<id>/design.md`** — запись цитирует
решение и ссылается; сочинение заново это второй дом обоснования;
- **статус полем меты** (`- **Статус:** заменено на ADR-…` либо `устарело`), а
не абзацем и не заголовком — и статус в записи сходится с таблицей
`adr/README.md`;
- **замена парная**: новая запись пересматривает прежнее решение — у старой
обязан быть статус «заменено на». Односторонняя замена оставляет две
активные записи об одном, и `architecture` прочитает ту, что нашёл первой.
7. **Пустое названо пустым, а не заглушено.** Незаполненный документ канона
держит **одну честную информативную строку**: «внешних зависимостей нет —
смотри на диск и на СУБД». Плейсхолдеры шаблона ловит машина; твоё — строка,
которая **есть, но ничего не сообщает**: «TBD», «будет дополнено», «раздел в
работе», а также честная по форме, но пустая по содержанию («зависимости
описаны ниже» при отсутствии «ниже»). Предлагай готовую строку — ту, которую
проход ревью прочитает **как факт** и не потратит на неё обязательный вопрос.
8. **`security.md` начинается периметром.** «Сервис открыт наружу» и «контур
доверенный» — противоположные постановки под одним заголовком, и враждебный
проход между ними сам не выберет. Периметра нет в первых строках — находка.
Контур ещё не развёрнут — обязаны быть названы **оба** периметра, целевой и
сегодняшний, и сказано прямо, против какого строятся находки.
## Чего ты не проверяешь
Не своё бывает трёх родов, и поступают с ними по-разному.
**Чужому подрядчику — строкой в границах покрытия.** Соответствие документов
коду у `doc-code-drift`; язык (залог, оценки, англицизмы, жаргон, неизвестный
термин, слово в двух смыслах) у `doc-wording`; форма записи задач у `task-form`.
Увидел — назови в конце одной строкой, чтобы находка не пропала, но находкой не
оформляй: две проверки одного места расходятся и начинают спорить.
**Машинной проверке — вообще ничего.** Всё, что ловят `docs.py check` и
`tasks.py check` (отсутствующие пути канона, файлы вне канона, имена файлов и
форма имени ADR, битые ссылки, версия канона, нетронутые плейсхолдеры, число
маркеров долга, миграция без правки `database.md`, capability без упоминания),
**не пиши даже строкой**: это не потерянная находка, а уже проверенное.
**Верность решений.** Правильно ли выбрана архитектура, достаточна ли модель
угроз, разумен ли инвариант — это ревью, а не сверка. Документ, внутренне
согласованный и целиком неверный, для тебя чист, и это не твой промах.
## Порог вмешательства
**Находка без нарушенного правила не делается.** «Мне кажется, тут стоило бы
подробнее» — не находка. Список, где половина пунктов вкусовые, перестают читать
целиком, и вместе с ним пропадают настоящие расхождения.
**Второй дом — только там, где два текста утверждают.** Ссылка на другой документ
вторым домом **не является**, и упоминание факта в проходящей фразе («см.
периметр в `security.md`») тоже. Правило написано против расхождения, а не против
слов.
**Сомневаешься, какой из двух домов канонический, — не выбирай.** Назови оба и
скажи, что карта домов ответа не даёт: это находка о самом каноне, и она
ценнее угаданной.
## Доклад
Находки по одной, в порядке важности: прямые противоречия → факт в двух домах →
поведение в обзоре → ADR и провенанс → пустые слоты. Первые ломают решения,
которые по документам принимают; последние — только цену чтения.
```
<файл> ↔ <файл> (или <файл> — для одиночных)
правило: <номер и короткое имя>
сейчас: <что утверждает каждый>
дом по канону: <адрес> — <почему он>
предложение: <готовая формулировка либо строка-ссылка на замену копии>
```
В конце — **границы покрытия**: сколько документов просмотрено из скольких, какие
не смотрел и почему, читались ли спеки и архив изменений. Отчёт без этой строки
читается как «канон сверен», не сообщая, какая его часть осталась нетронутой.
Туда же — строка «замечено не по моей части»; машинно проверяемое в неё **не
идёт**.
Ничего не нашёл — так и скажи одной строкой. Пустой доклад с границами покрытия
полезнее выдуманного противоречия.
+16 -3
View File
@@ -1,6 +1,6 @@
---
name: doc-wording
description: "Вычитка языка проектных текстов по информационному стилю — документы канона, решения ADR, записки разведки, задачи и цели, вперемешку тоже. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта. Отдаёт готовые формулировки на замену и ничего не правит сам. Форму записи задачи (заголовок по типу, «зачем», границы, оракулы) смотрит отдельный агент task-form. Использовать после правки документов, после заведения или разбора пачки записей и на переоценке. Только чтение."
description: "Вычитка языка проектных текстов по информационному стилю — документы канона, решения ADR, записки разведки, задачи и цели, вперемешку тоже. Смотрит отглагольные существительные и страдательный залог, оценку без факта, стоп-слова и канцелярит, «одна мысль — одно предложение», англицизм при живом русском слове, жаргон и метафоры вместо прямого называния, термин, которого нет в документах проекта, транслит в имени файла. Отдаёт готовые формулировки на замену и ничего не правит сам. Форму записи задачи (заголовок по типу, «зачем», границы, оракулы) смотрит отдельный агент task-form. Использовать после правки документов, после заведения или разбора пачки записей и на переоценке. Только чтение."
tools: Read, Grep, Glob
model: sonnet
color: green
@@ -122,13 +122,26 @@ color: green
**Слово, занятое в другом смысле, — та же находка.** Термин, который в одном
документе проекта значит одно, а здесь другое, ломает оба; назови оба места.
8. **Имя файла — английское слово по сути, а не транслит.** `queue-as-table`, а
не `ochered-tablicej`; `move-parse-strict`, а не `razbor-hoda`. Транслит
нечитаем тому, кто ищет по смыслу, и не сокращается, а имя стоит в ссылках,
коммитах и путях, которые набирают руками.
Кириллицу в имени и не-kebab-case ловят `docs.py` и `tasks.py` — про них
молчи. Твоё — **транслит**, потому что машина проверяет его эвристикой и
ловит не всё: `sostoyanie-partii` проходит мимо неё. Находка — готовое
английское имя на замену плюс напоминание, что переименование это **перенос
ссылок одним проходом**, а не правка одного файла.
## Чего ты не проверяешь
Не своё бывает двух разных родов, и поступают с ними по-разному.
**Чужому подрядчику — строкой в границах покрытия.** Форма записи задачи у
`task-form`; увидел — назови в конце одной строкой, чтобы находка не пропала, но
находкой не оформляй.
`task-form`; согласованность документов между собой (факт в двух домах,
противоречие, поведение в обзоре) у `doc-consistency`; соответствие документов
коду у `doc-code-drift`. Увидел — назови в конце одной строкой, чтобы находка не
пропала, но находкой не оформляй.
**Машинной проверке — вообще ничего.** Всё, что ловят `tasks.py check` и
`docs.py check` (состав и написание секций, наличие разделов, число критериев,
+4 -2
View File
@@ -125,8 +125,10 @@ color: yellow
Не своё бывает двух разных родов, и поступают с ними по-разному.
**Чужому подрядчику — строкой в границах покрытия.** Язык у `doc-wording`;
увидел — назови в конце одной строкой, чтобы находка не пропала, но находкой не
оформляй.
согласованность документов канона между собой у `doc-consistency`, их
соответствие коду у `doc-code-drift` — до задач эти двое не доходят вовсе, но
если ты открыл цель и увидел расхождение в самом документе, оно их. Увидел —
назови в конце одной строкой, чтобы находка не пропала, но находкой не оформляй.
**Машинной проверке — вообще ничего.** Всё, что ловит `tasks.py check` (наличие
разделов, число критериев, состав и написание секций, теги, тег `question` при
+18 -12
View File
@@ -67,23 +67,29 @@ python3 $ds version --dir <корень> # версия кано
capability: незаполненный канон это переходное состояние, а не отказ. Маркеры
долга просто считает числом.
**Ты** судишь о том, чего она не умеет:
Того, чего она не умеет, **ты не судишь сам** — для этого есть два агента, и
разведены они по глубине:
- **смысловой дубль**`docs/specs/recognition.md` описывает то же, что
capability `recognition`. Файлы разные, содержание одно;
- **поведение, оставшееся в `architecture.md`** — раздел на 900 строк с
требованиями вместо обзора;
- **достаточность честной строки** — «внешних зависимостей нет» это факт,
«TBD» — пробел;
- **протухший факт** — документ ссылается на то, чего в коде уже нет.
| Агент | Что смотрит | Читает |
| --- | --- | --- |
| `doc-consistency` | смысловой дубль, прямое противоречие между документами, поведение в `architecture.md` вместо спек, ADR без ссылки и парного статуса, число без провенанса, заглушка вместо честной строки | `docs/`, `openspec/` |
| `doc-code-drift` | протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки проекта, capability | весь репозиторий |
Судит **не тот, кто писал**: самопроверка документа слабее всего ровно там, где
формулировка казалась удачной при написании. Ни один из них ничего не правит —
оба возвращают готовые формулировки, подставляешь ты.
## `check`
1. `docs.py check`, при наличии базы диффа — с `--base`.
2. Прочитай то, что скрипт проверить не может (список выше), по документам,
которых касалась работа. Не «заодно по всему `docs/`».
3. Доклад: вывод скрипта строкой исхода, твои находки поимённо, **граница
покрытия** — что смотрел и чего не смотрел.
2. **Позови `doc-consistency`** на документы, которых касалась работа. Не «заодно
по всему `docs/`»: агент зовётся пачкой, но пачка отбирается работой.
3. **`doc-code-drift`** — не на каждом `check`, а перед приведением проекта к
канону и раз в спринт (шаг сессии). Он дорог: читает репозиторий и гоняет
команды. Позвал — передай ему раздел запретов `CLAUDE.md`.
4. Доклад: вывод скрипта строкой исхода, находки агентов поимённо, **граница
покрытия** — что смотрели и чего не смотрели, и **был ли позван
`doc-code-drift`**: доклад, умолчавший об этом, читается как «с кодом сверено».
Дрейф раскладки чинится переносом; смысловые находки — это либо правка
документа, либо задача, если работы больше чем на абзац.
+52 -14
View File
@@ -58,10 +58,10 @@ docs/
security.md периметр; недоверенный вход; что вне модели
conventions/
README.md индекс, правило промоута, что механизировано
<тема>.md
<slug>.md
research/
README.md как снималось, индекс
<тема>.md наблюдения и числа с провенансом
<slug>.md наблюдения и числа с провенансом
adr/
README.md индекс записей, статусы, правило замены
template.md
@@ -75,8 +75,29 @@ openspec/
changes/archive/ архив изменений с design.md — сырьё для ADR
```
Текст документов — русский; слаги файлов, capability и задач — английские,
kebab-case.
### Имена файлов английские, текст русский
**Текст документов русский; имена файлов, capability и задач — английские,
kebab-case.** Причина не эстетическая: имя файла стоит в ссылках из других
документов, в коммитах и в путях, которые набирают руками, — а кириллица в пути
ломается по-разному в разных местах и не набирается на английской раскладке.
**Транслита не заводим.** Слаг именуется английским словом **по сути**, а не
записью русского латиницей: `queue-as-table`, а не `ochered-tablicej`. Транслит
нечитаем тому, кто ищет по смыслу, и не сокращается.
У ADR имя вдобавок несёт форму — `ADR-ГГГГ-ММ-ДД-slug.md`: по ней записи
сортируются, и по ней же ищется дата решения.
`docs.py check` проверяет кириллицу и kebab-case **жёстко**, форму имени ADR —
тоже, а транслит **эвристикой**, то есть замечанием: английское слово от
транслита машина не отличает. Слаги каталога задач ведёт `tasks.py` — там та же
проверка и тот же разрез.
**Переименование — не правка, а перенос ссылок**: делается одним проходом по
всем местам, где имя упомянуто, иначе останутся битые ссылки. Для задач это
умеет `tasks.py adopt`; для документов канона правит человек, а `docs.py` потом
показывает, что ссылки целы.
## Роли документов
@@ -297,6 +318,7 @@ kebab-case.
Факт живёт ровно в одном файле; остальные ссылаются. Карта на случай спора:
<!-- дом: карта-домов -->
| Факт | Дом |
| --- | --- |
| поведение системы | `openspec/specs/<capability>/spec.md` |
@@ -311,6 +333,7 @@ kebab-case.
| единые точки проекта | `architecture.md` |
| имя основной ветки, `testdata`, временный каталог | `CLAUDE.md` |
| что уже механизировано правилом | `conventions/README.md` |
<!-- /дом: карта-домов -->
## Пустое называется пустым
@@ -347,16 +370,31 @@ kebab-case.
Граница объявляется вслух в каждом отчёте: `check`, отчитавшийся «канон
соблюдён» на проекте, где из шести файлов три лишние, хуже отсутствующего.
| Проверяет `docs.py` | Судит агент |
| --- | --- |
| отсутствующие пути канона | смысловой дубль документа и capability |
| файлы в `docs/` вне канона | поведение, оставшееся в `architecture.md` |
| битые относительные ссылки | протухший факт, разошедшийся с кодом |
| версия канона и её отставание | достаточность честной строки в пустом слоте |
| нетронутый плейсхолдер шаблона | связность и читаемость |
| маркеры долга — числом | |
| миграция изменена, а `database.md` нет | |
| capability без упоминания в `architecture.md` | |
| Проверяет `docs.py` | Судит агент | Какой |
| --- | --- | --- |
| отсутствующие пути канона | смысловой дубль документа и capability | `doc-consistency` |
| файлы в `docs/` вне канона | поведение, оставшееся в `architecture.md` | `doc-consistency` |
| имя файла не kebab-case латиницей; форма имени ADR | транслит в имени — сверх эвристики | `doc-wording` |
| битые относительные ссылки | прямое противоречие между документами | `doc-consistency` |
| версия канона и её отставание | достаточность честной строки в пустом слоте | `doc-consistency` |
| нетронутый плейсхолдер шаблона | ADR без ссылки на `design.md`, замена без парного статуса | `doc-consistency` |
| маркеры долга — числом | **протухший факт, разошедшийся с кодом** | `doc-code-drift` |
| миграция изменена, а `database.md` нет | зависимость в манифесте, не названная в обзоре | `doc-code-drift` |
| capability без упоминания в `architecture.md` | второй способ там, где обзор обещал единственный | `doc-code-drift` |
| | связность и читаемость | `doc-wording` |
**Агентов двое, и разведены они по глубине, а не по охвату.** `doc-consistency`
читает только `docs/` и `openspec/` — сверка текста с текстом дёшева и зовётся на
каждом синке документации. `doc-code-drift` читает репозиторий и гоняет читающие
команды: дорого, и зовётся раз в спринт и перед приведением проекта к канону.
Слитый агент делал бы дешёвую половину редкой, а дорогую — поверхностной; тот же
разрез, что между `task-form` и `doc-wording`.
**Перечень фактов, которые `doc-code-drift` сверяет с кодом, закрыт** — имя
основной ветки, команды, пути, зависимости поимённо, настройки с числовым
значением, единые точки проекта, capability, проверяемые инварианты. «Сверить
архитектуру с кодом» задача без дна, и агент, которому её поставили, выдаёт
правдоподобную труху вместо находок.
## `docs/.pm.json`
+23 -1
View File
@@ -81,6 +81,20 @@ upgrade` идёт по записям снизу вверх от версии п
9. **Алгоритм работы над каждым типом** — отдельным файлом,
`skills/tasks/references/task-<тип>.md`: схема, что проверяет машина, что
человек, и порядок шагов.
10. **Имена файлов проверяются.** Правило «текст русский, имена английские»
стояло в каноне и не было подкреплено ничем: `docs.py` имён не смотрел вовсе.
Теперь смотрит — кириллица и не-kebab-case **жёстко**, форма имени
`ADR-ГГГГ-ММ-ДД-slug.md` жёстко, транслит **эвристикой**, то есть
замечанием. Заодно из раскладки канона убраны плейсхолдеры `<тема>.md`,
приглашавшие называть файлы по-русски.
11. **Два агента вместо обещания.** В каноне была таблица «Что проверяет машина,
а что человек», и её правая колонка три версии описывала судью, которого не
существовало. Судьи заведены и разведены по глубине: **`doc-consistency`**
(документ ↔ документ ↔ openspec: факт в двух домах, прямое противоречие,
поведение в `architecture.md` вместо спек, ADR без ссылки и парного статуса,
число без провенанса, заглушка вместо честной строки) зовётся на шаге синка
документации; **`doc-code-drift`** (документ ↔ код по закрытому перечню
фактов) — раз в спринт на сессии и перед приведением проекта к канону.
**Что сделать проекту:**
@@ -108,7 +122,15 @@ upgrade` идёт по записям снизу вверх от версии п
`research`. Не «заодно по всему беклогу», а порциями переоценки: `check`
ошибкой это не считает, отказывает только `sprint take`. Сколько задач готово
к взятию, печатает блок здоровья `check`.
7. `docs/.pm.json`: `"canon": 4`.
7. Прогнать `python3 docs.py check`: он назовёт имена файлов не по правилу.
Кириллицу и не-kebab-case править обязательно, транслит — по решению
человека. **Переименование ADR это перенос ссылок**: слаг стоит в
`adr/README.md`, в `architecture.md` и в чужих документах, и делается одним
проходом, иначе останутся битые ссылки (их `docs.py` потом и покажет).
8. Позвать `doc-consistency` на документы канона — первый прогон на живом
проекте обычно самый урожайный: правило единственного дома до сих пор никто
не проверял. Разбирать порциями, а не одним заходом.
9. `docs/.pm.json`: `"canon": 4`.
## Версия 3 — 2026-08-04
@@ -216,8 +216,9 @@
## Соглашения
- Имя файла — `ADR-ГГГГ-ММ-ДД-slug.md`, слаг английский, дата — когда решение
реально принято.
- Имя файла — `ADR-ГГГГ-ММ-ДД-slug.md`, дата — когда решение реально принято.
Слаг **английский по сути, а не транслитом**: `queue-as-table`, не
`ochered-tablicej`. Форму имени и слаг проверяет `docs.py check`.
- Записи неизменяемы: передумали — новая запись, старой ставится статус.
- Активная запись статуса не имеет. Значений два: `заменено на ADR-…` и
`устарело`; ставятся полем меты записи — `- **Статус:** …` рядом с датой и
+104 -3
View File
@@ -75,6 +75,105 @@ RETIRED = {
"review": "→ docs/review.md",
}
# --- Слаги в именах файлов --------------------------------------------------
# Текст документов русский, а **имена файлов английские, kebab-case**. Причина
# не в эстетике: имя файла стоит в ссылках из других документов, в коммитах и в
# путях, которые люди набирают руками, — а кириллица в пути ломается по-разному
# в разных местах и не набирается на английской раскладке.
SLUG = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
ADR_NAME = re.compile(r"ADR-(\d{4})-(\d{2})-(\d{2})-(.+)")
CYRILLIC = re.compile(r"[а-яёА-ЯЁ]")
# Признаки транслита — и только они. Отличить английское слово от транслита
# машина не умеет, поэтому находка идёт **замечанием**: кластеры, которых в
# английском практически не бывает, плюс окончания русских падежей.
#
# Слабые маркеры выброшены намеренно, каждый по своему ложному срабатыванию:
# `ost` ловит `post` и `cost`, `sch` — `schema`, `ya` — `yaml`, `nost` —
# `nostalgia`, хвост `ii` — `radii`. Набор подобран так, чтобы ложных
# срабатываний не было вовсе: правило, краснеющее на правде, приучает
# пролистывать весь блок. Цена известна и принята — `sostoyanie-partii`
# проходит мимо.
#
# Тот же приём, что `translit_ish` в tasks.py; скрипты независимы намеренно —
# каждый уезжает в чужой проект в одиночку.
TRANSLIT_CLUSTER = re.compile(r"zh|kh|shch|tsy|iya|ovanie|enie|stvo")
TRANSLIT_TAIL = re.compile(r"(?:ej|oj|ij|yj|yy|aya)$")
def translit_ish(slug: str) -> bool:
if TRANSLIT_CLUSTER.search(slug):
return True
return any(TRANSLIT_TAIL.search(part) for part in slug.split("-"))
def check_slugs(root: Path, rep: Report) -> None:
"""Имена файлов канона: латиница kebab-case, у ADR — ещё и форма имени.
Каталог задач не трогаем: его слаги ведёт и проверяет tasks.py, и вторая
проверка того же места разошлась бы с первой.
"""
docs = root / "docs"
if not docs.is_dir():
return
# Имена, выбранные каноном, а не проектом: их форма задана здесь же.
fixed = {"README.md", "template.md"} | ALLOWED_FILES
for sub in ("conventions", "research", "adr"):
folder = docs / sub
if not folder.is_dir():
continue
for path in sorted(folder.rglob("*.md")):
name = path.name
rel = path.relative_to(root)
if name in fixed:
continue
stem = path.stem
if sub == "adr":
m = ADR_NAME.fullmatch(stem)
if not m:
rep.error(
f"{rel}: имя не по форме ADR-ГГГГ-ММ-ДД-slug.md — "
f"по имени сортируются записи и ищется дата решения"
)
continue
stem = m.group(4)
if CYRILLIC.search(stem):
rep.error(
f"{rel}: кириллица в имени файла — слаги английские, "
f"kebab-case (текст документа при этом русский)"
)
continue
if not SLUG.fullmatch(stem):
rep.error(
f"{rel}: имя не kebab-case латиницей — только строчные "
f"буквы, цифры и одиночные дефисы"
)
continue
if translit_ish(stem):
rep.note(
f"{rel}: имя похоже на транслит («{stem}») — слаг именуется "
f"английским словом по сути, а не записью русского латиницей: "
f"транслит нечитаем тому, кто ищет по смыслу. Проверено "
f"эвристикой: английское слово от транслита машина не отличает"
)
check_capability_slugs(root, rep)
def check_capability_slugs(root: Path, rep: Report) -> None:
specs = root / "openspec" / "specs"
if not specs.is_dir():
return
for folder in sorted(specs.iterdir()):
if not folder.is_dir():
continue
if CYRILLIC.search(folder.name) or not SLUG.fullmatch(folder.name):
rep.error(
f"openspec/specs/{folder.name}/: имя capability — латиница "
f"kebab-case; оно стоит в ссылках из architecture.md и в спеках"
)
DEBT_MARKER = re.compile(r"<!--\s*канон:\s*(.+?)\s*-->")
PLACEHOLDER = re.compile(r"<!--\s*заполнить:\s*(.+?)\s*-->")
MD_LINK = re.compile(r"\[[^\]]*\]\(\s*<?([^)>\s]+)>?(?:\s+[\"'(][^)]*)?\)")
@@ -371,9 +470,10 @@ def report(rep: Report) -> int:
print(f" {msg}")
print(
"\nМашина проверила раскладку, ссылки, версию и две сверки с кодом.\n"
"Смысловые дубли, оставшееся в architecture.md поведение и достаточность\n"
"честной строки в пустом слоте она не проверяет — это суждение агента."
"\nМашина проверила раскладку, имена файлов, ссылки, версию и две сверки\n"
"с кодом. Согласованность документов между собой и с кодом она не\n"
"проверяет — это суждение агентов `doc-consistency` (документ ↔ документ\n"
"↔ openspec) и `doc-code-drift` (документ ↔ код)."
)
if rep.errors:
print(f"\nИтог: дрейф, {len(rep.errors)} пунктов.")
@@ -394,6 +494,7 @@ def cmd_check(args: argparse.Namespace) -> int:
check_version(root, cfg, rep)
check_required(root, cfg, rep)
check_stray(root, rep)
check_slugs(root, rep)
check_links(root, rep)
check_placeholders_and_debt(root, rep)
check_capabilities(root, rep)
+19 -1
View File
@@ -50,11 +50,29 @@ description: Вести содержимое документов канона
Синк документации:
- architecture.md — добавлен воркер свёртки, ссылка на capability reindex
- database.md — миграция 00006, таблица bucket
- adr/ — заведён ADR-2026-08-03-ochered-tablicej: отказ от внешней очереди
- adr/ — заведён ADR-2026-08-03-queue-as-table: отказ от внешней очереди
- research/ — новое о формате не узнано
- passport, security, conventions, review — не требуется: изменение внутреннее
- сверка doc-consistency: находок нет, просмотрено 4 документа из 10
```
## Сверка после синка
Синк правит документы поодиночке, а расходятся они **между собой**: факт,
дописанный в `architecture.md`, уже живёт в `CLAUDE.md`; периметр в
`security.md` не знает про новый эндпоинт. Поймать это на своей же правке нельзя
— поэтому последним шагом синка зовётся агент **`doc-consistency`** на те
документы, которых синк касался.
Он читает `docs/` и `openspec/`, кода не читает, ничего не правит и возвращает
готовые формулировки. Строка его доклада входит в доклад синка — **включая
пустую**: «находок нет, просмотрено N из M» это ответ, а молчание читается как
«не звали».
**Сверку с кодом синк не зовёт.** «Протухший факт, разошедшийся с кодом» смотрит
`doc-code-drift`, он дорог (читает репозиторий) и зовётся раз в спринт на сессии
— не на каждой сделанной задаче.
## ADR — промоут, а не второе сочинение
Обоснование уже написано: `opsx:propose` кладёт `design.md` в каждый change, и
+2 -1
View File
@@ -116,7 +116,8 @@ description: "Ритуал между спринтами и ведение са
Это зависимость, а не список.
1. **Разбор вопросов.**
2. **Разбор прошедшего спринта — про процесс, а не про задачи.**
2. **Разбор прошедшего спринта — про процесс, а не про задачи.** Здесь же
сверка документов канона с кодом — агент `doc-code-drift`, раз в спринт.
3. **Переоценка задач** порциями.
4. **Выбор цели и набор спринта.** Цель называет человек, набор собирает агент и
показывает **до старта работ**.
@@ -54,6 +54,21 @@
Отдельным ритуалом ретроспектива не выделяется: процесс личный,
синхронизировать некого.
**Здесь же зовётся `doc-code-drift`** — сверка документов канона с кодом по
закрытому перечню фактов: имя основной ветки, команды, пути, внешние зависимости
поимённо, настройки с числовым значением, единые точки проекта, capability.
Раз в спринт, а не чаще, и причина в цене: агент читает репозиторий и гоняет
читающие команды. Но и не реже — **спринт это ровно то, что двигает код под
документами**: переименованная цель сборки, ушедшая зависимость, второй способ
делать то, что обзор объявил единственным. Протухший факт неотличим от свежего, и
по нему принимают решения, пока кто-нибудь не наткнётся.
Его находки — обычный материал переоценки: строка на замену идёт в документ сразу,
работа больше чем на абзац становится задачей типа `chore`. **Позвал — скажи в
докладе, что позвал, и приложи его таблицу проверенного**; не позвал — скажи и
это, иначе доклад читается как «с кодом сверено».
## Шаг 3. Переоценка задач
Цель — выкинуть то, что перестало быть задачей, и вернуть остальному честное
@@ -216,6 +231,8 @@
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
- Разбор процесса: что записано и куда.
- Сверка документов с кодом: звался ли `doc-code-drift`, что проверено из
названного, что разошлось.
- Изменения списком: удалено как реализованное (со ссылками), ушло без
реализации (с причинами), понижено до сырья, слито, сменило тип или цель.
- Новый спринт: цель, набор со слагами, дата, состав по типам.
@@ -61,7 +61,7 @@
заводиться и без поштучного вопроса — но карта пользователю предъявляется
всё равно.
6. **Заводи утверждённое** через `tasks.py add`, с тремя добавками:
- **тег партии**`--tag review-ГГГГ-ММ-ДД` (или `audit-<тема>`), чтобы весь
- **тег партии**`--tag review-ГГГГ-ММ-ДД` (или `audit-<slug>`), чтобы весь
заход разбора поднимался одной командой `list --tag …`;
- **тип**`--type`, и он **не по умолчанию `fix`**: починкой считается
расхождение с заявленным поведением, а находка «этого свойства никто не
@@ -57,7 +57,7 @@
выводом в терминалах», а «Какими символами рамки печатаются одинаково в
Терминале, iTerm и `tmux`». Вопроса ещё нет — запись заводится сырьём и
лежит в конце секции, пока вопрос не появится.
2. **Назвать, куда ляжет ответ**: `docs/research/<тема>.md`, ADR, тело этой
2. **Назвать, куда ляжет ответ**: `docs/research/<slug>.md`, ADR, тело этой
задачи. Место называется **заранее**, иначе ответ остаётся в переписке, а
через квартал разведку заказывают заново.
3. **Ограничить рамками**, если разведка может утечь: сколько времени, какие
+5 -2
View File
@@ -15,11 +15,14 @@
<!-- дом: <id> -->
текст
<!-- /дом -->
<!-- /дом: <id> -->
<!-- копия: <id> из <путь к файлу дома> -->
тот же текст
<!-- /копия -->
<!-- /копия: <id> -->
Закрывающий маркер несёт **тот же id**, что открывающий: без него не отличить
конец своего блока от конца соседнего, а вложенных блоков разметка не знает.
Сверяется текст **между** маркерами: построчно, с отброшенными хвостовыми
пробелами и пустыми строками по краям. Всё остальное вокруг копии предисловие,