- DECISIONS.md (4040 строк, 65 тем) → decisions/, файл на тему плюс указатель; - буквенные метки решений заменены сквозными Р1–Р234, следствия получили префикс С при прежних номерах: схема букв выродилась до пятибуквенных и сломалась — `АЕАКЛ` была занята и темой 53, и темой 65; - 42 перекрёстные ссылки переписаны под новые номера и стали живыми; где номер означал тему, а слово стояло «решение», формулировка исправлена.
102 lines
9.9 KiB
Markdown
102 lines
9.9 KiB
Markdown
# 19. Роадмап — состояние проекта, а не очередь работ (2026-08-04)
|
||
|
||
## Что было
|
||
|
||
Основной инструмент владельца — роадмап и набор целей: «на каком этапе проект,
|
||
что сделали и что осталось». Оценка идёт по **поведению**, а не по внутреннему
|
||
устройству: что приложение уже может делать и чего ещё не может. Отсюда
|
||
требование к формулировкам: цель отвечает на «что приложение будет делать»,
|
||
задача — на «что для этого нужно сделать».
|
||
|
||
Разбор показал, что инструмент отвечал ровно на половину этого вопроса.
|
||
|
||
## Решено
|
||
|
||
**Р77. Достигнутая цель из роадмапа не исчезает.** `close --implemented` удалял
|
||
у цели и файл, и строку — роадмап по построению показывал только «что осталось».
|
||
Свидетельство нашлось в самом роадмапе healthlog: там руками заведена секция
|
||
«Что уже пройдено» на двадцать строк прозы, и заканчивается она фразой «Эти
|
||
звенья целями не заведены: закрытая цель записи не оставляет, ей хватает коммита
|
||
и спеки». Обходной путь и его причина записаны рукой владельца. Теперь строка с
|
||
датой переезжает в секцию достигнутого; файл удаляется по-прежнему.
|
||
|
||
Вторым домом поведения это не делает: нормативное поведение живёт в
|
||
`openspec/specs/`, роадмап отвечает **когда и в каком порядке** оно появилось —
|
||
другой вопрос. Ссылки на файл в строке нет намеренно: файла больше нет, а битая
|
||
ссылка это законная ошибка `check`. Форма строки — как в `REJECTED.md`, и по той
|
||
же причине.
|
||
|
||
**Р78. Цель — возможность приложения, задача — шаг к ней.** Заголовок цели
|
||
отвечает на «что приложение будет уметь»: не «Работа со слиянием», а «Исход
|
||
слияния не зависит от порядка доставки». **Свойство поведения — тоже
|
||
возможность**: «сообщает о своём состоянии», «исход не зависит от порядка» —
|
||
законные цели, переформулировки в функцию не требуют. Единственный настоящий
|
||
чужак — работа над инструментом и процессом: на вопрос «что приложение будет
|
||
уметь» она не отвечает и живёт в отдельной секции роадмапа.
|
||
|
||
**Р79. Тест готовности задачи сменил защиту.** Требование «что станет наблюдаемо
|
||
иначе снаружи» переехало к цели. У задачи вместо него — **какую строку
|
||
«Завершения» своей цели она двигает**. «Отрефакторить X» проваливает тест не
|
||
потому, что невидим снаружи, а потому, что не находит строки, к которой
|
||
относится. Побочная выгода: видно и обратное — строка «Завершения», к которой не
|
||
относится ни одна задача, это незакрытая часть возможности. Отсюда требование к
|
||
«Завершению» быть **списком**, а не абзацем: на абзац не сошлёшься.
|
||
|
||
**Р80. Цель обязательна не у всякой задачи.** Прежнее правило — «у каждой задачи
|
||
должен быть `goal:`, иначе она не попадёт ни в один спринт» — было угрозой, а не
|
||
аргументом, и заставляло операционную работу выдумывать себе направление.
|
||
Граница проходит по роду работы: `feature` без цели не бывает (новая возможность
|
||
и есть содержание цели), `fix`, `chore` и `research` живут без цели законно и
|
||
входят в набор спринта помимо его цели. Это второй раз, когда род работы
|
||
окупается, — и первый, когда он что-то определяет за пределами отбора.
|
||
|
||
**Р81. Тип `[epic]` упразднён.** Зонтик между целью и задачами не нужен:
|
||
зонтиком стала цель, а слишком крупный шаг дробится на шаги помельче под ней.
|
||
Замер: ноль употреблений на 97 записей двух живых проектов, при том что тип
|
||
занимал место в словаре, тесте готовности, автомате переходов, `split.md` и трёх
|
||
местах `tasks.py`.
|
||
|
||
**Р82. Имена секций роадмапа — `Готово` / `Запланировано` / `Направления` /
|
||
`Разработка`.** Первый набор (`умеет` / `строим` / `станок`) прожил один заход и
|
||
был признан неудачным. Из четырёх предложенных имён отвергнуто одно, и по
|
||
проверяемой причине: **`окружение` уже занято** — в `architecture.md` это боевое
|
||
окружение приложения, «где работает, что рядом, кто перезапускает», и одно слово
|
||
в двух смыслах развело бы документы канона. Взято `Разработка`.
|
||
|
||
Принятый компромисс назван вслух: `Готово` слегка тянет обратно в трекерную рамку
|
||
«состояние работы», тогда как секция про **возможность**. Перевесила читаемость с
|
||
первого взгляда, а смысл несут заголовки целей внутри секции. Так же принято, что
|
||
цель в `Запланировано` может быть уже наполовину построена: это очередь, а не
|
||
«не начато», а «в работе» живёт в `SPRINT.md`.
|
||
|
||
**Р83. Секции роадмапа канонические, секции беклога — нет.** Разница выведена, а
|
||
не назначена: у секций роадмапа есть **семантика** (достигнутое, очередь,
|
||
долгое, не про продукт), в первую пишет сам `close`, и роадмап, названный
|
||
по-своему, читался бы только своим автором. Секции беклога (`Ядро`, `Инфра`)
|
||
семантики не несут — это полки. Поэтому `check` проверяет у роадмапа три вещи:
|
||
состав закреплён (чужая секция — ошибка), все четыре обязаны быть, язык один на
|
||
весь индекс; `--roadmap-sections` у `init` упразднён. Английский набор — `Done`
|
||
| `Planned` | `Directions` | `Tooling`.
|
||
|
||
Проверено на том самом случае, ради которого правило и заводилось: секция «Что
|
||
уже пройдено», которую healthlog вёл руками, теперь называется ошибкой поимённо.
|
||
|
||
## Что из этого следует
|
||
|
||
**С78. Ключа `tasks.achieved_section` не появилось.** Секция достигнутого
|
||
опознаётся по каноническому имени в любом из двух языков, и лишний knob не
|
||
заводится: канонический состав отвечает на тот же вопрос надёжнее конфига.
|
||
|
||
**С79. `reopen` цели снимает строку достигнутого.** Иначе роадмап продолжает
|
||
утверждать, что приложение умеет то, что вернулось в работу.
|
||
|
||
**С80. Прозаический раздел в индексе — дрейф.** Любой `##` проверка считает
|
||
секцией, поэтому «Что уже пройдено» и «Почему в таком порядке» в healthlog
|
||
формально были двумя лишними секциями, куда могла уехать задача. При повышении
|
||
они разбираются: звенья — строками в `Готово`, обоснование очереди — прозой
|
||
внутри `Запланировано`.
|
||
|
||
**С81. Правил стало пять, и нулевое — про смысл, а не про механику.** «Цель —
|
||
возможность, задача — шаг к ней» стоит перед правилами о гниении беклога и
|
||
производности индексов, потому что из него следует, зачем эти механики нужны.
|