канон 13: файл версии зовётся по владельцу, у задач появилась своя версия формата
Имя `.pm.json` пережило плагин `av-dev-pm` на два месяца и указывало в пустоту. Правило, которое из этого вынуто: имя служебного файла — имя плагина, который его завёл, и по нему же владельца узнают. - `docs/.pm.json` → `docs/.docs.json`, запись 13 журнала. Прежнее имя docs.py не читает намеренно: по этому числу upgrade решает, какие записи применять, и два дома разъехались бы молча ровно там, где это дороже всего. Вместо совместимости — узнавание: check видит старый файл и печатает готовую git mv - у каталога задач появилась своя версия формата — ключ `tasks` в `.tasks.json`, свой журнал версий и своё повышение. До сих пор её не было вовсе, хотя docs.py в комментарии уверенно на неё ссылался: описание опережало механику ровно так, как сказано в решении 195 - число своё, а не копия канонического: плагин ставится в одиночку, и у проекта без docs/ версии канона нет — сверять было бы не с чем - конфиг задач стал обязательным (init и adopt apply пишут его всегда), check сверяет число, `check --fix` его не приписывает: приписанное объявляло бы каталог приведённым к формату, шагов которого никто не делал - переезды 11 и 12 в новый журнал задним числом не переписаны — версия 1 велит догнать формат по журналу канона, называя признаки отставания поимённо (каталог в docs/tasks/, живой SPRINT.md) - запись 60 в DECISIONS со следствиями 200–203; отдельно разведено с решением F, где `.docs.json` отвергался как указатель путей: отвергнут был указатель, а не имя
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: doc-code-drift
|
||||
description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .pm.json, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Зовётся скиллом av-dev-docs:healthcheck — на весь канон разом; он же зовётся шагом adopt и шагом upgrade. Только чтение."
|
||||
description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .docs.json, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Зовётся скиллом av-dev-docs:healthcheck — на весь канон разом; он же зовётся шагом adopt и шагом upgrade. Только чтение."
|
||||
tools: Read, Grep, Glob, Bash
|
||||
model: sonnet
|
||||
color: green
|
||||
@@ -37,7 +37,7 @@ color: green
|
||||
|
||||
## Что тебе дают
|
||||
|
||||
Корень проекта. Читаешь `CLAUDE.md`, `docs/**`, `docs/.pm.json`,
|
||||
Корень проекта. Читаешь `CLAUDE.md`, `docs/**`, `docs/.docs.json`,
|
||||
`openspec/specs/**` — и репозиторий: манифесты зависимостей, конфиги, файлы
|
||||
сборки и CI, дерево пакетов.
|
||||
|
||||
@@ -62,7 +62,7 @@ color: green
|
||||
держит прежнее имя.
|
||||
|
||||
3. **Пути** — все, которые канон обязывает называть: `migrations` из
|
||||
`docs/.pm.json`, `testdata`, временный каталог, пути в запретах `CLAUDE.md`.
|
||||
`docs/.docs.json`, `testdata`, временный каталог, пути в запретах `CLAUDE.md`.
|
||||
Проверка: существует ли. Путь в запрете, которого нет, — находка **особого
|
||||
рода**: запрет, который не на что наложить, читается как соблюдённый, а на
|
||||
деле охраняет пустоту, пока настоящий каталог зовётся иначе.
|
||||
@@ -147,7 +147,7 @@ color: green
|
||||
```
|
||||
факт источник проверено чем итог
|
||||
имя основной ветки CLAUDE.md git branch сошлось
|
||||
путь миграций docs/.pm.json ls РАЗОШЛОСЬ
|
||||
путь миграций docs/.docs.json ls РАЗОШЛОСЬ
|
||||
внешние зависимости architecture.md go.mod 2 не названы
|
||||
единые точки: парсер входа architecture.md grep по формату сошлось
|
||||
настройки БД database.md — не проверено
|
||||
|
||||
@@ -116,8 +116,10 @@ capability: незаполненный канон это переходное с
|
||||
|
||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.pm.json` —
|
||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
||||
|
||||
<!-- /копия: граница-плагинов -->
|
||||
|
||||
@@ -176,7 +178,7 @@ capability), `openspec/config.yaml`.
|
||||
|
||||
Порядок важен — он минимизирует окно, в котором ссылки битые:
|
||||
|
||||
1. `docs/.pm.json` с `{"canon": <текущая версия>}` и путём миграций, если БД есть;
|
||||
1. `docs/.docs.json` с `{"canon": <текущая версия>}` и путём миграций, если БД есть;
|
||||
2. каталоги канона и скелет **по [references/skeletons.md](references/skeletons.md)**:
|
||||
незаполненное — одной честной информативной строкой, а не «TBD»;
|
||||
3. **OpenSpec, если его нет или `config.yaml` остался примером** — **вызови
|
||||
@@ -265,7 +267,7 @@ capability), `openspec/config.yaml`.
|
||||
3. Иначе иди по [changelog.md](references/changelog.md) снизу вверх от версии
|
||||
проекта до текущей и делай названное в каждой записи. Записи независимы и
|
||||
применяются по порядку.
|
||||
4. Подними `canon` в `docs/.pm.json` до текущей.
|
||||
4. Подними `canon` в `docs/.docs.json` до текущей.
|
||||
5. `docs.py check`.
|
||||
6. **Позови судей** — Skill `av-dev-docs:healthcheck`.
|
||||
7. **Позови вычитку** — агент `doc-wording`, но **только по тем документам,
|
||||
@@ -276,7 +278,15 @@ capability), `openspec/config.yaml`.
|
||||
Записи журнала описывают **что сделать проекту**. Если запись этого не говорит —
|
||||
это дефект журнала, и о нём надо сказать, а не догадываться.
|
||||
|
||||
**Шаг 6 обязателен, и вот почему.** `check` сверяет **число** в `.pm.json` с
|
||||
**Каталог задач повышается своим журналом, а не этим.** У него своя версия
|
||||
формата — ключ `tasks` в `<каталог задач>/.tasks.json`, — и двигает её плагин
|
||||
`av-dev-tasks`. Запись канона вправе сказать «позови соседа», но не вправе
|
||||
двигать чужое число: две версии, которые ходят по одному журналу, разъезжаются
|
||||
на первом же проекте, поставившем один плагин без другого. Отстал каталог
|
||||
задач — это скажет `tasks.py check` своей строкой гейта, а повысит скилл
|
||||
`av-dev-tasks:tasks`.
|
||||
|
||||
**Шаг 6 обязателен, и вот почему.** `check` сверяет **число** в `.docs.json` с
|
||||
версией скрипта — и только его. Применена ли запись журнала **по существу**, он
|
||||
не знает: проект несёт `"canon": 6` и может не иметь того, чего требовала любая
|
||||
из пройденных версий. Записи применяются руками (переименовать секцию, проставить
|
||||
|
||||
@@ -65,7 +65,7 @@ CLAUDE.md памятка агенту: что это, ст
|
||||
severity, команды, семантика гейта, запреты
|
||||
AGENTS.md необязателен, лежит рядом; читается теми же
|
||||
docs/
|
||||
.pm.json версия канона и пути, нужные проверкам
|
||||
.docs.json версия канона и пути, нужные проверкам
|
||||
passport.md | passport/ зачем и для кого; чем НЕ является; сценарии
|
||||
architecture.md | architecture/ как сложено — обзор; окружение и эксплуатация
|
||||
database.md | database/ схема хранилища; представление данных и настройки
|
||||
@@ -119,7 +119,7 @@ openspec/
|
||||
| `review.*` | процессный | — (настройка самого конвейера, слой **над** темами) |
|
||||
| `adr.*` | процессный | — |
|
||||
| `research.*` | процессный | — |
|
||||
| `.pm.json` | процессный | — (служебный файл, не документ) |
|
||||
| `.docs.json` | процессный | — (служебный файл, не документ) |
|
||||
|
||||
**Список тем открытый, и это не послабление, а механизм.** Категории
|
||||
`источник` и `процессный` **закрыты** — они перечислены здесь поимённо и
|
||||
@@ -354,8 +354,10 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
### `tasks/`
|
||||
|
||||
**Каталог задач канону не принадлежит.** Его ведёт отдельный плагин
|
||||
`av-dev-tasks` — своим скриптом, своим конфигом `<каталог>/.tasks.json` и своей
|
||||
версией формата. Канон **резервирует место** в `docs/` и внутрь не смотрит:
|
||||
`av-dev-tasks` — своим скриптом, своим конфигом `<каталог>/.tasks.json`, своей
|
||||
версией формата в нём же и своим журналом версий. Канон о том числе не
|
||||
высказывается и его не двигает: повышает каталог задач тот, кто его ведёт.
|
||||
Канон **резервирует место** в `docs/` и внутрь не смотрит:
|
||||
`docs.py` каталог не открывает, его отсутствия не считает дрейфом и согласованность
|
||||
задач не проверяет. Проект, поставивший только канон документов, задач не ведёт
|
||||
вовсе, и отказом это быть не может.
|
||||
@@ -546,7 +548,7 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
архитектуру с кодом» задача без дна, и агент, которому её поставили, выдаёт
|
||||
правдоподобную труху вместо находок.
|
||||
|
||||
## `docs/.pm.json`
|
||||
## `docs/.docs.json`
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -562,12 +564,23 @@ kebab-case.** Причина не эстетическая: имя файла с
|
||||
`migrations` — путь каталога миграций, если БД есть; по нему `docs.py` делает
|
||||
сверку с `database.md`.
|
||||
|
||||
**Имя файла — имя плагина, который его завёл.** Канон документов ведёт
|
||||
`av-dev-docs`, поэтому `.docs.json`; у каталога задач по тому же правилу
|
||||
`.tasks.json`, у конвейера — `openspec/config.yaml`. До версии 13 файл звался
|
||||
`.pm.json` — по плагину `av-dev-pm`, который распался на четыре и которого
|
||||
больше нет; имя пережило владельца и указывало в пустоту. Прежнее имя `docs.py`
|
||||
не читает: два дома для одной версии канона расходятся молча, а переименование
|
||||
стоит одну команду (версия 13 журнала, и `check` называет её сам, когда видит
|
||||
старый файл).
|
||||
|
||||
**Ключа `tasks` здесь больше нет.** Настройки каталога задач вернулись в свой
|
||||
файл `<каталог задач>/.tasks.json`, потому что ведёт их другой плагин: конфиг,
|
||||
лежащий в `docs/`, был бы домом, которого нет у проекта, поставившего учёт работ
|
||||
без канона документов. Состав ключей описывает тот плагин, а не канон. Прежний
|
||||
ключ читается, пока живы непереехавшие проекты, и `tasks.py` говорит о нём
|
||||
замечанием на каждом прогоне — версия 8 журнала просит его убрать.
|
||||
без канона документов. Состав ключей описывает тот плагин, а не канон. Там же —
|
||||
**версия формата задач**, и она своя: у проекта без `docs/` версии канона нет
|
||||
вовсе, сверять её было бы не с чем. Прежний ключ читается, пока живы
|
||||
непереехавшие проекты, и `tasks.py` говорит о нём замечанием на каждом
|
||||
прогоне — версия 8 журнала просит его убрать.
|
||||
|
||||
Ключей будет больше по мере роста проверок; неизвестный ключ `docs.py`
|
||||
игнорирует, отсутствующий — считает «проверка неприменима» и говорит об этом
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
# Журнал версий канона
|
||||
|
||||
Одна запись на версию. Проект знает свою версию из `docs/.pm.json`; `canon
|
||||
Одна запись на версию. Проект знает свою версию из `docs/.docs.json`; `canon
|
||||
upgrade` идёт по записям снизу вверх от версии проекта до текущей и делает то,
|
||||
что в них названо.
|
||||
что в них названо. Записи ниже версии 13 зовут этот файл прежним именем,
|
||||
`docs/.pm.json`, — так и было на день записи, и переписывать историю мы не
|
||||
станем; переименование делает запись 13.
|
||||
|
||||
**Каталог задач этим журналом не повышается.** У него своя версия формата и свой
|
||||
журнал — `references/changelog.md` скилла `av-dev-tasks:tasks`. Записи 8, 11 и 12
|
||||
трогали его в те времена, когда своего числа у него не было; впредь запись канона
|
||||
вправе позвать соседа, но не двигать его версию.
|
||||
|
||||
Правило записи: **что добавилось, что переехало, что удалено, что сделать
|
||||
проекту**. Без последнего пункта запись бесполезна — по ней и работает
|
||||
@@ -13,6 +20,51 @@ upgrade` идёт по записям снизу вверх от версии п
|
||||
|
||||
---
|
||||
|
||||
## Версия 13 — 2026-08-11
|
||||
|
||||
Служебный файл канона переименован: `docs/.pm.json` → `docs/.docs.json`. Имя
|
||||
досталось от плагина `av-dev-pm`, который распался на четыре и которого больше
|
||||
нет: файл пережил владельца и указывал в пустоту. Правило простое и теперь
|
||||
соблюдается всеми тремя: **имя служебного файла — имя плагина, который его
|
||||
завёл**, `.docs.json` — канон, `.tasks.json` — задачи, `openspec/config.yaml` —
|
||||
конвейер.
|
||||
|
||||
**Что изменилось.** `docs.py` читает только новое имя. Прежнее он не читает
|
||||
намеренно: два дома для одной версии канона расходятся молча, а тут расхождение
|
||||
стоило бы дорого — по этому числу `upgrade` решает, какие записи применять.
|
||||
Файл под старым именем `check` узнаёт и называет отдельной строкой с готовой
|
||||
командой, а не жалуется на пропажу.
|
||||
|
||||
**Что появилось у соседа.** У каталога задач теперь есть **своя версия
|
||||
формата** — ключ `tasks` в `<каталог задач>/.tasks.json`, — и свой журнал версий
|
||||
в скилле `av-dev-tasks:tasks`. До сих пор её не было вовсе: формат задач менялся
|
||||
записями этого журнала (8, 11, 12), хотя каталог принадлежит другому плагину и
|
||||
ставится без канона документов. Канон это число не двигает.
|
||||
|
||||
**Что сделать проекту.**
|
||||
|
||||
1. `git mv docs/.pm.json docs/.docs.json` — одним коммитом с шагом 2. Содержимое
|
||||
не меняется: ключи те же.
|
||||
2. **Поправить упоминания прежнего имени** в своих файлах — `CLAUDE.md`, гейт,
|
||||
`README.md`, `docs/**`. Битой ссылкой это чаще всего не выглядит (файл
|
||||
служебный, на него ссылаются прозой), поэтому `docs.py check` таких упоминаний
|
||||
не ловит: ищи `grep -rn '\.pm\.json'` по репозиторию.
|
||||
3. **Объявить версию формата задач**, если каталог задач в проекте есть:
|
||||
`<каталог задач>/.tasks.json` с ключом `"tasks": <версия>`. Файла нет вовсе —
|
||||
заведи, он теперь обязателен: версия не настройка, от которой можно
|
||||
отказаться. Какое число ставить и что сделать перед этим, говорит журнал
|
||||
владельца — **позови скилл `av-dev-tasks:tasks`**, здесь этих шагов нет
|
||||
намеренно: второй перечень чужих шагов разошёлся бы с первым.
|
||||
4. Гейт не меняется: шаги те же, версию задач сторожит `tasks.py check`, который
|
||||
в нём уже стоит.
|
||||
5. `docs/.docs.json`: `"canon": 13`.
|
||||
|
||||
**Чего делать не надо.** Ключи в файле не трогаются, документы не переезжают,
|
||||
записи задач не меняются: версия 13 — про имена служебных файлов и про то, кто
|
||||
чью версию двигает.
|
||||
|
||||
---
|
||||
|
||||
## Версия 12 — 2026-08-09
|
||||
|
||||
Спринты отменены. Работа идёт задача за задачей, и замороженный набор перестал
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
со строкой «запись лежит сжатой и распаковывается целиком».
|
||||
```
|
||||
|
||||
Нет БД — файла нет, и в `docs/.pm.json` нет ключа `migrations`.
|
||||
Нет БД — файла нет, и в `docs/.docs.json` нет ключа `migrations`.
|
||||
|
||||
## `docs/security.md`
|
||||
|
||||
@@ -438,7 +438,7 @@ severity стоит здесь, а не выводится каждым прох
|
||||
говорит (единственный дом, а не форма) — [canon.md](canon.md), раздел
|
||||
`openspec/config.yaml`.
|
||||
|
||||
## `docs/.pm.json`
|
||||
## `docs/.docs.json`
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -452,5 +452,10 @@ severity стоит здесь, а не выводится каждым прох
|
||||
плейсхолдер ломает разбор JSON громко, а отставшее число дало бы дрейф молча.
|
||||
|
||||
Плюс `"migrations": "<путь>"`, если есть БД. Ключа `"tasks"` здесь **нет**:
|
||||
настройки каталога задач переехали в свой файл `<каталог задач>/.tasks.json`,
|
||||
потому что ведёт их другой плагин. Состав ключей — [canon.md](canon.md).
|
||||
настройки каталога задач и версия их формата переехали в свой файл `<каталог
|
||||
задач>/.tasks.json`, потому что ведёт их другой плагин. Состав ключей —
|
||||
[canon.md](canon.md).
|
||||
|
||||
Имя файла — по плагину-владельцу, `av-dev-docs`. До версии 13 он звался
|
||||
`.pm.json`, по распавшемуся `av-dev-pm`; проект с прежним именем `docs.py check`
|
||||
называет отдельной строкой и зовёт переименовать.
|
||||
|
||||
@@ -25,10 +25,19 @@ from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
|
||||
CANON_VERSION = 12
|
||||
CANON_VERSION = 13
|
||||
|
||||
OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4
|
||||
|
||||
# Дом версии канона и путей, нужных проверкам. Имя — от плагина, который файл
|
||||
# завёл: настройки канона документов ведёт `av-dev-docs`, и файл называется по
|
||||
# нему. Прежнее имя досталось от `av-dev-pm` — плагина, который распался на
|
||||
# четыре и которого больше нет; читать его скрипт не умеет намеренно, потому что
|
||||
# два дома для версии канона расходятся молча, а переименование стоит одну
|
||||
# команду и названо записью 13 журнала.
|
||||
CONFIG = "docs/.docs.json"
|
||||
LEGACY_CONFIG = "docs/.pm.json"
|
||||
|
||||
# --- Раскладка канона -------------------------------------------------------
|
||||
|
||||
# Документ канона: имя → (категория, на какой вопрос отвечает).
|
||||
@@ -59,7 +68,7 @@ DOCS = {
|
||||
"review": ("процессный", "настройка конвейера + журнал дефектов"),
|
||||
}
|
||||
|
||||
# Документ, обязательный только при условии: имя → (ключ .pm.json, категория,
|
||||
# Документ, обязательный только при условии: имя → (ключ .docs.json, категория,
|
||||
# пояснение).
|
||||
CONDITIONAL_DOCS = {
|
||||
"database": ("migrations", "источник", "схема хранилища и настройки"),
|
||||
@@ -68,7 +77,7 @@ CONDITIONAL_DOCS = {
|
||||
# Обязательные файлы вне раскладки docs/.
|
||||
REQUIRED = {
|
||||
"CLAUDE.md": "памятка агенту: инварианты с severity, команды, семантика гейта",
|
||||
"docs/.pm.json": "версия канона и пути, нужные проверкам",
|
||||
CONFIG: "версия канона и пути, нужные проверкам",
|
||||
}
|
||||
|
||||
# Файлы, которые документ-каталог обязан держать сверх README.md.
|
||||
@@ -77,15 +86,19 @@ DOC_EXTRA = {
|
||||
}
|
||||
|
||||
# Служебное в docs/ и каталог задач, оставшийся там от прежней раскладки. Формы
|
||||
# у них скрипт не проверяет, и по разным причинам: `.pm.json` не markdown, а
|
||||
# у них скрипт не проверяет, и по разным причинам: `.docs.json` не markdown, а
|
||||
# задачи **принадлежат другому плагину** — `av-dev-tasks`, со своим скриптом,
|
||||
# своим конфигом и своей версией формата.
|
||||
# своим конфигом и своей версией формата (её сторожит `tasks.py check`).
|
||||
#
|
||||
# Дом задач с версии 11 — `tasks/` в корне репозитория, то есть вне `docs/`
|
||||
# вовсе. `docs/tasks/` здесь терпится потому, что непереехавший проект не должен
|
||||
# получать «файл вне канона» вдобавок к записи журнала, которая и так велит ему
|
||||
# переехать. Внутрь скрипт не смотрит ни в том, ни в другом случае.
|
||||
NOT_DOCS = {".pm.json", "tasks"}
|
||||
#
|
||||
# Прежнее имя конфига терпится ровно за тем же: про переименование проект
|
||||
# слышит одну строку — от `check_required`, — а не две, из которых вторая ещё и
|
||||
# зовёт файл лишним.
|
||||
NOT_DOCS = {".docs.json", ".pm.json", "tasks"}
|
||||
|
||||
# Слоты, которых в каноне нет, — с адресом, куда уезжает содержимое. Имена,
|
||||
# совпадающие с темой, отсюда убраны намеренно: `docs/conventions.md` и
|
||||
@@ -251,15 +264,15 @@ def fail(code: int, msg: str) -> NoReturn:
|
||||
|
||||
|
||||
def read_config(root: Path, rep: Report) -> dict:
|
||||
path = root / "docs" / ".pm.json"
|
||||
path = root / CONFIG
|
||||
if not path.exists():
|
||||
return {}
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as exc:
|
||||
fail(ENV, f"docs/.pm.json не разбирается: {exc}")
|
||||
fail(ENV, f"{CONFIG} не разбирается: {exc}")
|
||||
if not isinstance(data, dict):
|
||||
fail(ENV, "docs/.pm.json должен быть объектом")
|
||||
fail(ENV, f"{CONFIG} должен быть объектом")
|
||||
return data
|
||||
|
||||
|
||||
@@ -267,14 +280,14 @@ def read_config(root: Path, rep: Report) -> dict:
|
||||
|
||||
|
||||
def check_version(root: Path, cfg: dict, rep: Report) -> None:
|
||||
if not (root / "docs" / ".pm.json").exists():
|
||||
if not (root / CONFIG).exists():
|
||||
return # об отсутствии файла скажет check_required, второй раз не нужно
|
||||
if "canon" not in cfg:
|
||||
rep.error("в docs/.pm.json нет ключа canon — версия канона не объявлена")
|
||||
rep.error(f"в {CONFIG} нет ключа canon — версия канона не объявлена")
|
||||
return
|
||||
got = cfg["canon"]
|
||||
if not isinstance(got, int):
|
||||
rep.error(f"canon в docs/.pm.json должен быть целым числом, а не {got!r}")
|
||||
rep.error(f"canon в {CONFIG} должен быть целым числом, а не {got!r}")
|
||||
return
|
||||
if got < CANON_VERSION:
|
||||
rep.error(
|
||||
@@ -316,8 +329,21 @@ def doc_home(root: Path, name: str) -> tuple[Path | None, str | None]:
|
||||
|
||||
def check_required(root: Path, cfg: dict, rep: Report) -> None:
|
||||
for rel, what in REQUIRED.items():
|
||||
if not (root / rel).exists():
|
||||
rep.error(f"нет {rel} — {what}")
|
||||
if (root / rel).exists():
|
||||
continue
|
||||
# Файл под прежним именем — это не «нет файла», а незаконченный переезд,
|
||||
# и чинится он одной командой. Без этой ветки проект услышал бы «нет
|
||||
# версии канона» и пошёл заводить второй файл рядом с первым.
|
||||
if rel == CONFIG and (root / LEGACY_CONFIG).exists():
|
||||
rep.error(
|
||||
f"нет {rel} — {what}. Настройки лежат под прежним именем"
|
||||
f" {LEGACY_CONFIG} (от плагина av-dev-pm, которого больше нет):"
|
||||
f" `git mv {LEGACY_CONFIG} {rel}` — журнал канона, версия 13."
|
||||
f" Прежнее имя не читается, поэтому в этом прогоне всё"
|
||||
f" остальное проверено так, будто настроек нет вовсе"
|
||||
)
|
||||
continue
|
||||
rep.error(f"нет {rel} — {what}")
|
||||
|
||||
for name, (kind, what) in DOCS.items():
|
||||
home, complaint = doc_home(root, name)
|
||||
@@ -342,10 +368,10 @@ def check_required(root: Path, cfg: dict, rep: Report) -> None:
|
||||
rep.error(
|
||||
f"нет документа {name} (docs/{name}.md или docs/{name}/),"
|
||||
f" категория «{kind}» — {what}"
|
||||
f" (обязателен: в .pm.json объявлен {key})"
|
||||
f" (обязателен: в .docs.json объявлен {key})"
|
||||
)
|
||||
elif key not in cfg and home is None:
|
||||
rep.skip(f"{name} — в .pm.json нет ключа {key}, проверка неприменима")
|
||||
rep.skip(f"{name} — в .docs.json нет ключа {key}, проверка неприменима")
|
||||
|
||||
|
||||
def check_stray(root: Path, rep: Report) -> None:
|
||||
@@ -527,7 +553,7 @@ def changed_files(root: Path, base: str, rep: Report) -> list[str] | None:
|
||||
def check_migrations(root: Path, cfg: dict, base: str | None, rep: Report) -> None:
|
||||
migrations = cfg.get("migrations")
|
||||
if not migrations:
|
||||
rep.skip("в .pm.json нет ключа migrations — сверка со схемой неприменима")
|
||||
rep.skip("в .docs.json нет ключа migrations — сверка со схемой неприменима")
|
||||
return
|
||||
if not base:
|
||||
rep.skip("база диффа не названа (--base) — сверка миграций со схемой не гонялась")
|
||||
|
||||
@@ -159,8 +159,10 @@ description: Вести содержимое документов канона
|
||||
|
||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.pm.json` —
|
||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
||||
|
||||
<!-- /копия: граница-плагинов -->
|
||||
|
||||
|
||||
@@ -60,8 +60,10 @@ check` и его скрипт; здесь начинается там, где к
|
||||
|
||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.pm.json` —
|
||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
||||
|
||||
<!-- /копия: граница-плагинов -->
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ description: "Завести новый проект — сессия вопро
|
||||
| `passport.md` | `architecture.md` |
|
||||
| `CLAUDE.md` | `database.md` |
|
||||
| `security.md` | `conventions/` |
|
||||
| `docs/.pm.json` | `research/`, `adr/` |
|
||||
| `docs/.docs.json` | `research/`, `adr/` |
|
||||
| | `review.md` — журнал пуст, настройка появится с первым ревью |
|
||||
|
||||
Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет,
|
||||
@@ -97,8 +97,10 @@ description: "Завести новый проект — сессия вопро
|
||||
|
||||
**Присутствие узнаётся вызовом или следом в проекте, но не объявлением.** Перечня
|
||||
установленных плагинов проект не ведёт — он разошёлся бы с действительностью
|
||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.pm.json` —
|
||||
молча. Что сосед здесь работал, видно по заведённому им файлу: `docs/.docs.json` —
|
||||
канон, `<каталог задач>/.tasks.json` — задачи, `openspec/config.yaml` — конвейер.
|
||||
Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего
|
||||
формата: у канона документов и у каталога задач они свои и двигаются порознь.
|
||||
|
||||
<!-- /копия: граница-плагинов -->
|
||||
|
||||
@@ -117,7 +119,8 @@ description: "Завести новый проект — сессия вопро
|
||||
|
||||
**Вызов не разрешился** — проект без конвейера живёт без OpenSpec законно:
|
||||
строка доклада, и дальше; `docs.py check` о каталоге тоже промолчит.
|
||||
4. Заведи `docs/.pm.json` с текущей версией канона.
|
||||
4. Заведи `docs/.docs.json` с текущей версией канона — число берётся из
|
||||
`docs.py version`, а не из памяти.
|
||||
5. Напиши заполняемые документы. **Бриф переезжает в `passport.md`** и
|
||||
отдельным файлом не остаётся: два дома для одного замысла разойдутся на
|
||||
первом же уточнении.
|
||||
|
||||
Reference in New Issue
Block a user