задачи: починена смена стадии, разобраны находки ревью плагина

Команда stage была дефектна по шести пунктам, и все шесть подтверждены
прогоном: не звала raw_last (переход оставлял каталог красным), не
переписывала шапку беклога (индекс продолжал объявлять прежнюю стадию),
шла в обход write_config, молча пропускала файлы с непересобираемой метой,
ломалась на беклоге без заголовков и схлопывала полки при первом
объявлении стадии.

Объявление и смена разведены: объявление беклога не трогает вовсе, смена
трогает состав секций только по явному --sections, а слить полки скрипт
не берётся ни в одном случае. Абзац шапки размечен парой «стадия», и
расхождение с конфигом стало обычным дрейфом.

Отказ по недостающей строке индекса запирал запись, пережившую упразднение
роадмапа: edit, close и reopen теперь заводят или пропускают строку сами.
Прочее: регистр stage нормализуется при чтении; --fix снимает мёртвые теги
и у неразобранных записей; move отказывает переставлять сырьё; adopt
держит место сырья; docs.py bump двигает одну запись журнала за раз;
tasks.py получил перечень упразднённых адресов, и гейт наконец видит
собственное упразднение ROADMAP.md.

Запись «Версия 3» переписана по прогону на игрушечном проекте: прежний
порядок шагов был неисполним. Закрыты дыры модели стадий (пересмотр плана
стройки стал сценарием, приёмка отвязана от груминга, from-review,
research и adopt получили развилку по стадии, перечень осей пересчитан) и
находки, старшие этой сессии: review-triage получил режим без метки, три
списка проектных копий сведены к дому с проверяемыми копиями, пять
пересказов правил стали помеченными копиями или ссылками, language.md
перестал объявлять юрисдикцию над чужим плагином.
This commit is contained in:
av
2026-08-13 15:08:29 +03:00
parent 8d8c1656e5
commit ed83ec7dc0
28 changed files with 678 additions and 259 deletions
+5 -4
View File
@@ -255,10 +255,11 @@ capability), `openspec/config.yaml`.
**Задачи `docs.py` не проверяет** — их ведёт другой скилл, и согласованность
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
его отчёт идёт в доклад отдельной строкой, и пункт «задачи без цели» в нём
зелёным не станет: цели не сочиняются адаптацией (запрет записан у того, кто
ведёт задачи), их проставляет человек порциями переоценки на первом груминге —
скилл `av-dev:task-groom`.
его отчёт идёт в доклад отдельной строкой, и зелёным он сразу не станет:
у перенесённых записей нет критериев приёмки, а `check` без объявленной
**стадии** отказывает вовсе. Стадию называет человек (`tasks.py stage
build|support`) — машина её не выводит: список пунктов одинаково выглядит и
планом стройки, и очередью правок.
### 5. Объяви переходное состояние
+4 -3
View File
@@ -351,9 +351,10 @@ kebab-case.** Причина не эстетическая: имя файла с
скриптом и своими проверками; где каталог лежит и как названы его части, говорит
секция `[tasks]` в `.av-dev.toml`. Версия там одна на всю раскладку: канон и
каталог задач двигаются вместе, потому что ведёт их один плагин.
Канон **резервирует место** в `docs/` и внутрь не смотрит:
`docs.py` каталог не открывает, его отсутствия не считает дрейфом и согласованность
задач не проверяет. Проект, не заведший каталог задач, их не ведёт
Каталог лежит **в корне репозитория, вне `docs/`** — места в `docs/` канон ему
больше не резервирует (переезд — запись 11 закрытого журнала), а внутрь не
смотрит и подавно: `docs.py` каталог не открывает, его отсутствия не считает
дрейфом и согласованность задач не проверяет. Проект, не заведший каталог задач, их не ведёт
вовсе, и отказом это быть не может.
Раскладку, форму записи и команды держит скилл `av-dev:task-track`. Ниже — то,
+50 -17
View File
@@ -40,27 +40,60 @@
`[tasks] completion_heading` — тоже. Появились ключ `[tasks] stage`, команда
`tasks.py stage` и флаги `init --stage`, `adopt scan --stage`.
**Что сделать проекту.**
**Что сделать проекту. Порядок шагов обязателен**, и первый шаг — не команда:
пока в `[tasks]` лежит упразднённый ключ, **любая** подкоманда `tasks.py`
отвечает кодом 3 и работать нечем.
1. **Разобрать цели.** У каждой записи типа `goal` в `tasks/items/` два исхода, и
выбирает человек: она становится обычной задачей (`edit <слаг> --type
feature|fix|chore|research`) либо уходит (`close <слаг> --reason …`). Задачи,
носившие её тег, живут дальше сами по себе. Скрипт этого не решает и говорит
1. **Вычистить конфиг руками.** Из секции `[tasks]` в `.av-dev.toml` удалить
ключи `roadmap` (или `plan`) и `completion_heading`. Каждый из них — код 3 на
любой команде, и названы они здесь оба: второй легко пропустить, потому что
его упразднение не видно по имени файла.
2. **Удалить `tasks/ROADMAP.md`.** Секция `Готово` уходит вместе с ним и **не
переносится**: «что приложение умеет» отвечают спеки, «когда это появилось» —
`git log` беклога. Проект без `openspec/specs/` теряет здесь единственный
связный перечень достигнутого — если он нужен, сохрани его сам до удаления
(документом проекта, не задачами).
3. **Прогнать `tasks.py check --fix`.** Он снимет теги `goal:<слаг>` и
`decomposed`, переименует поле `Секция``Категория` и перепишет старую
форму меты — **в том числе у самих записей типа `goal`**. Записи `goal` при
этом останутся: во что превращается цель, машина не решает и говорит
`НЕОДНОЗНАЧНО`.
2. **Перенести содержимое `ROADMAP.md`.** Секция `Готово` **удаляется**: «что
приложение умеет» отвечают спеки, «когда это появилось» — `git log`. Строки
`Запланировано`, `Направления` и `Сопровождение` — это цели, и они разбираются
шагом 1. Затем удалить сам файл и ключ `roadmap` из `.av-dev.toml`, если он там
был.
3. **Объявить стадию**`tasks.py stage build` или `tasks.py stage support`.
4. **Разобрать цели поштучно.** У каждой два исхода, и выбирает человек: она
становится задачей (`edit <слаг> --type feature|fix|chore|research`) либо
уходит (`close <слаг> --reason …`). Строки в беклоге у неё нет — её жильём
был роадмап, — и `edit --type` заведёт её сам, в первую секцию и в конец,
сказав об этом; место назначь потом. Раздел `Завершение` в теле переехавшей
записи **удали руками**: схеме нового типа он не принадлежит, и `check`
оставит о нём замечание. Задачи, носившие тег цели, живут дальше сами по
себе — разбирать их не нужно.
5. **Объявить стадию**`tasks.py stage build` или `tasks.py stage support`.
Приложение ещё строится и список работ линеен по зависимости — `build`;
работает и правится точечно — `support`. Без ключа `check` отказывает: порядок
строк нечем прочитать. На `build` секция беклога обязана остаться **одна**
слить полки надо руками, порядок строк в слитом списке знает только человек.
4. **Поднять версию**`docs.py bump`. Последним шагом.
5. `docs.py check` и `tasks.py check --dir <каталог задач>` — до отсутствия
дрейфа. Теги `goal:` и `decomposed`, поле `Секция` и старую форму меты снимет
`tasks.py check --fix`.
строк нечем прочитать.
**Объявление беклог не трогает** — ни секций, ни файлов: оно называет то, что
уже верно. Поэтому проекту с несколькими полками, объявляющему `build`,
команда откажет и назовёт выход: слить полки самому (`move <слаг> --section
<куда> --reason …`), потому что порядок строк в слитом списке знает только
человек. Флаг `--sections` при объявлении не принимается — он для **смены**
стадии, где сливать просят явно.
6. **Поправить шапку `BACKLOG.md`.** Абзац про стадию теперь размечен парой
`<!-- стадия -->``<!-- /стадия -->`, и по нему `check` сверяет шапку с
конфигом. В беклоге, заведённом до этой версии, разметки нет — `stage` об
этом скажет. Возьми готовый абзац из свежего каталога (`tasks.py init` во
временном месте) или напиши сам: он объясняет, что значит порядок строк, и
читают вместо документации именно его.
7. **Поднять версию**`docs.py bump`. Последним шагом. Он двигает **одну**
запись за раз: отставшему на две записи проекту зовётся дважды, следом за
шагами каждой.
8. `docs.py check` и `tasks.py check --dir <каталог задач>` — до отсутствия
дрейфа.
**Проект, не прошедший записи 1 и 2, начинает с этой.** Их собственные шаги
велят гонять `tasks.py check` до зелёного, а он на упразднённом ключе отвечает
кодом 3 — то есть пройти их сегодня нельзя, не сделав шаг 1 отсюда. Записи от
этого не переписываются: порядок между ними прежний, добавлено одно условие
входа.
---
+15 -2
View File
@@ -702,9 +702,22 @@ def cmd_bump(args: argparse.Namespace) -> int:
if was is not None and was > LAYOUT_VERSION:
fail(ENV, f"проект на версии {was}, скрипт знает {LAYOUT_VERSION}:"
f" устарел плагин, обнови маркетплейс")
conf.set_version(root, LAYOUT_VERSION)
# Двигается **одна** запись за раз, а не сразу до текущей: число объявляет
# пройденными шаги журнала, и прыжок через запись объявил бы пройденным то,
# чего никто не делал. Отставшему на три записи проекту `bump` зовётся три
# раза — по разу на запись, следом за её шагами.
#
# Версии нет вовсе — случай другой: проект не жил ни одной записью журнала,
# его раскладку только что вывели сегодняшним форматом (`adopt`), и
# объявлять ему нечего, кроме текущего числа.
target = LAYOUT_VERSION if was is None else was + 1
conf.set_version(root, target)
print(f"версия раскладки: {was if was is not None else 'не была объявлена'}"
f"{LAYOUT_VERSION} в {CONFIG}")
f"{target} в {CONFIG}")
if target < LAYOUT_VERSION:
print(f" до текущей ({LAYOUT_VERSION}) осталось записей журнала:"
f" {LAYOUT_VERSION - target}. Пройди шаги следующей и позови bump"
f" снова — по разу на запись")
return OK