- DECISIONS.md (4040 строк, 65 тем) → decisions/, файл на тему плюс указатель; - буквенные метки решений заменены сквозными Р1–Р234, следствия получили префикс С при прежних номерах: схема букв выродилась до пятибуквенных и сломалась — `АЕАКЛ` была занята и темой 53, и темой 65; - 42 перекрёстные ссылки переписаны под новые номера и стали живыми; где номер означал тему, а слово стояло «решение», формулировка исправлена.
9.9 KiB
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. Правил стало пять, и нулевое — про смысл, а не про механику. «Цель — возможность, задача — шаг к ней» стоит перед правилами о гниении беклога и производности индексов, потому что из него следует, зачем эти механики нужны.