Files
dev-skills/decisions/67-goal-removed-project-stages.md
T
av 6251157d8d журнал решений: тема 67 дополнена по итогам ревью
- снята выдуманная ссылка: фраза «цель и приоритет — независимые оси»
  приписывалась теме 19, а стояла в правиле 4 самого скилла;
- перечень отменяемого доведён до полного: тема 19 целиком кроме Р81,
  половина Р83, плюс Р69, Р84, С85, Р101, Р103, Р105, Р106, Р107, Р110,
  Р128 из тем 17, 20, 25, 26, 27, 31;
- Р242 приведён к тому, что команда делает на самом деле; заведены Р244
  (объявление стадии и её смена — разные операции) и С234–С236.
2026-08-13 15:08:43 +03:00

18 KiB
Raw Blame History

67. Цель упразднена, у проекта появилась стадия (2026-08-13)

Что было

Учёт работ вёлся двумя индексами. ROADMAP.md отвечал на «что приложение умеет и чего ещё не умеет» целями (тип goal), BACKLOG.md — на «что можно взять» задачами; связь шла тегом goal:<слаг>, обязательным у feature. Модель сложилась темой 19 и держалась до сих пор.

Владелец назвал наблюдение: у его проектов две стадии. Сперва активная разработка, где нужен один список от базы к деталям, чтобы построить приложение целиком; потом сопровождение и расширение, где правки точечные, а архитектура не меняется. Цель в обеих не понадобилась ни разу.

Разбор подтвердил, что это не вкус, а свойство: цель — зонтик над параллельными направлениями, она нужна там, где список работ нельзя выстроить в один порядок и очередь идёт поперёк направлений. Так это и было записано — правилом 4 скилла task-track: «Цель и приоритет — независимые оси: очередь может идти поперёк целей, и это законно». У проекта на одного человека параллельных направлений не бывает, и зонтик не стоял ни над чем.

Заодно нашлось, что роадмап отвечал на свой вопрос наполовину: «чего ещё не умеет» — это «что осталось в беклоге», то есть пересказ второго индекса.

Решено

Р237. Тип goal и индекс ROADMAP.md упразднены. Индекс остался один. Вместе с целью ушли теги goal:<слаг> и decomposed, поле меты Секция, раздел Завершение, команды list --goal, edit --goal, edit --section и ключи [tasks] roadmap, [tasks] completion_heading. Тип [epic], упразднённый темой 19 в пользу цели, не вернулся: слишком крупный шаг дробится на шаги помельче, и они встают в списке подряд — соседство и есть тот ответ, ради которого заводили зонтик.

Отменённое названо поимённо, потому что журнал задним числом не переписывается, и решение, оставшееся без ссылки, читается через год как действующее. Отменены:

  • тема 19 целиком, кроме Р81. Р77 (достигнутая цель не исчезает) — см. Р238; Р78 (цель — возможность, задача — шаг к ней); Р79 (тест готовности спрашивает, какую строку «Завершения» задача двигает) — вопрос снят вместе с разделом, у ready его нет; Р80 (цель обязательна не у всякой задачи); Р82 (имена секций роадмапа); С78 и С79 (ключ секции достигнутого, reopen снимает её строку); С81 (правил пять, нулевое про цель — их шесть, и нулевое про стадию). Р81 подтверждён: зонтика между планом и задачей по-прежнему нет.
  • Р83 — наполовину. «Секции роадмапа канонические» отменено вместе с роадмапом; «секции беклога — нет» пересмотрено: Р240 завёл машинную проверку их состава. Имена по-прежнему называет проект — проверяется только количество, и только на стройке.
  • Р69 (PLAN.mdROADMAP.md вместе с ключом конфига): файл и ключ упразднены.
  • Р84 (заголовок отвечает на вопрос своего типа, и форм три) — форм две. С85 (мелкая цель даёт две задачи) — предмета нет.
  • Р101 и Р103 в части «секция Сопровождение в роадмапе»: место работ по сопровождению — задачи chore, и дом словаря это уже говорит.
  • Р105 и Р106 (порядок секций роадмапа канонический, позиции считаются из кортежа) — предмета нет.
  • Р107 в части «ось из пяти значений» — их четыре. Р110 (поле места названо по типу) отменён его собственным доводом: имена развели потому, что «какое поле обязательно, решает тип», а типов, у которых оно называлось бы «Секцией», не осталось. Это ровно тот случай, который С101 назвал образцовым.
  • Р128 (порядок отмены цели: сперва задачи, потом цель) — процедура мертва целиком.

Тема 39 (цель у спринта) сюда не входит: её предмет умер раньше, вместе со спринтами (тема 57).

Р238. Секция Готово удалена, а не перенесена. Р77 заводил её потому, что роадмап иначе показывал только «что осталось». Довод верен ровно для роадмапа: у самого факта «что приложение умеет» есть два дома и без него — нормативное поведение в openspec/specs/, а когда и в каком порядке оно появилось — git log индекса, где закрытие лежит отдельным коммитом учёта. Третий дом списком строк был бы дублем, который никто не сверяет.

Оговорка, без которой довод неполон: OpenSpec обязателен не всем. Канон допускает проект без него, и там из двух домов остаётся один — история git. Связного перечня достигнутого у такого проекта не будет, и запись журнала версий говорит об этом прямо: нужен — сохрани сам, до удаления файла.

Р239. Стадия проекта — новая ось: build и support. Решает она что значит порядок строк беклога: на стройке зависимость («раньше нельзя»), на доработке важность («раньше лучше»). Из этого выведено остальное — сколько у беклога секций, как его пополняют, что значит его опустошение и применим ли груминг.

Ось заведена потому, что стадии переворачивают два действующих правила скилла, а не украшают их. Правило 1 («беклог гниёт с той стороны, где его пополняют») оказалось правилом доработки: на стройке список пишется вперёд целиком, и фильтр «не заводи то, чего не делаешь сейчас» запретил бы написать план дальше первого шага. Правило 4 («приоритет назначает человек») на стройке половину порядка не назначает, а обнаруживает: зависимость видна, а не выбрана.

Р240. На стройке секция беклога ровно одна. Порядок там — зависимость, и разложенный по полкам список перестаёт быть планом: два шага из разных секций уже не сравнить. На доработке полки законны, потому что правки независимы. Проверяет check; слить секции сам он не берётся — в каком порядке пойдут строки слитых полок, знает только человек.

Р241. Стадия объявляется явно, молчание ответом не считается. init --stage обязателен, check без ключа [tasks] stage отказывает, check --fix его не подставляет. Это тот же довод, что у С145: необязательное значение, которое всё же решают, заводится парой «значение или явный отказ», иначе забытый ключ и осознанный выбор неотличимы. Здесь он сильнее обычного: без стадии нечем прочитать индекс — переставить строку значит на стройке сломать план, а на доработке принять решение о важности.

Р242. Переход стадии — команда, а не правка конфига. tasks.py stage переписывает ключ конфига и шапку беклога, где и объявлен смысл порядка строк. Датой перехода служит коммит: отдельного журнала ради одной строки не заводится. Запретить переход раньше времени скрипт не берётся — «приложение построено» решает человек, а не счётчик строк; check лишь напоминает о пустом беклоге стройки.

Р243. Русское имя стадии — «доработка», а не «поддержка». Слово «поддержка» уже запрещено домом shared/operations.md как синоним «сопровождения»: в нём слышится помощь пользователю. Третье техническое значение сделало бы его окончательно нечитаемым. Английское имя осталось support — оно в идентификаторе, а идентификатор и есть дом.

Р244. Объявление стадии и её смена — разные операции, и различает их факт, а не флаг. Была стадия названа раньше или нет, скрипт знает сам. Объявление беклога не трогает вовсе: оно называет то, что уже верно, и переразметить при этом чужие полки значило бы подменить ответ вопросом о нём. Смена трогает шапку и конфиг, а состав секций — только если об этом попросили явно (--sections). Первая редакция команды сливала полки в умолчание при любом вызове, и запись журнала версий, велевшая объявить стадию, тем самым молча теряла у проекта лишние полки.

Что из этого следует

С228. Груминг стал операцией одной стадии. На стройке оба его вопроса отвечены заранее: «что важно» — первая строка плана, назначенная зависимостью, а «что перестало быть важным» возникает не порциями, а разом, при смене замысла, — и тогда пересматривается план целиком. Порционный разбор там вреден: он вынимает шаги из списка, порядок которого и есть его содержание.

С229. Тест декомпозиции ослаб на одной стадии и только на ней. Условие «части мерджатся независимо, порядок между ними значит план реализации» было записано для беклога-очереди. Беклог стройки весь состоит из упорядоченных зависимостью шагов, и «сперва А, потом Б» там не повод не дробить. Осталось общее условие: каждая часть мерджится сама по себе.

С230. Залежалость считается только на доработке. На стройке долгое лежание — нормальное состояние шага, до которого не дошла очередь: он стоит там, где стоит, по зависимости, и переоценивать его нечем.

С231. Стадия не влияет на метку ревью, глубину и тип задачи. Клетка в перечне осей названа пустой явно: изменение на стройке ничем не проще того же изменения на доработке. Мысль «на стройке всё small, приложения ведь ещё нет» разбивается о первый же шаг, кладущий схему хранилища.

С232. Запись типа goal машина не переводит. check --fix снимает теги и переименовывает поля, но во что превращается сама цель — в задачу или в ничто, — решает человек, и она уезжает в НЕОДНОЗНАЧНО. Это то же правило, что у типа, которого неоткуда взять.

С233. adopt получил стадию входным параметром. Список пунктов одинаково выглядит и планом стройки, и очередью правок; вывести стадию из материала нельзя. Заодно нумерованные шаги источника перестали быть кандидатами в цели и стали порядком записей: номер источника — единственное место, где чужая раскладка называет зависимость.

С234. Абзац, который читают вместо документации, обязан быть проверяемым. Шапка беклога объявляет стадию и смысл порядка строк, и первая редакция команды её не трогала: файл продолжал утверждать прежнее, пока конфиг говорил новое. Отсюда разметка <!-- стадия --> — не украшение, а то, что делает расхождение шапки с конфигом обычным дрейфом: check его называет, check --fix правит.

С235. Упразднение адреса регистрируется у владельца перечня. tasks/ROADMAP.md не попал в карту упразднённого, и гейтовый шаг, заведённый ровно затем, чтобы ловить протухшие адреса, молчал про тот адрес, который упразднили в том же коммите. Перечень упразднённого теперь есть у обоих владельцев раскладки, а не только у канона.

С236. Отказ по недостающей строке индекса запирал запись навсегда. edit, close и reopen требовали строку, которой у пережившей упразднение индекса записи нет и взяться неоткуда: восстановить её нельзя, пока не сменишь тип, а тип не сменить, пока нет строки. Все три теперь заводят или пропускают строку сами и говорят об этом. Правило осталось прежним: позицию назначает человек, и машинная встаёт в конец.