av-dev-tasks: починены находки ревью, добавлена адаптация чужого репозитория

- атомарность: sprint drop и move собирают план правок целиком и пишут
  одним проходом; раньше отказ на втором слаге оставлял первый файл
  переписанным при нетронутом индексе
- хук переехал в мета-строку файла: индекс стал производным, и check --fix
  больше не теряет текст, восстанавливая строку
- механизировано то, что было записано, но не проверялось: слаг спринта и
  автотег, отказ по факту непустого раздела вопросов, число критериев,
  пометка decomposed, покрытие причин
- reopen возвращает закрытую задачу: без него порядок «пайплайн доложил →
  приёмщик судит → владелец закрывает» был односторонним
- скилл adopt: приходит в чужой репозиторий и выводит заполненный каталог
  задач. На копии беклога healthlog — 12 целей, 38 задач, 36 переименований,
  86 ссылок в 36 файлах, check зелёный
This commit is contained in:
av
2026-08-03 11:45:23 +03:00
parent 9219f4a5cd
commit 20dca29add
8 changed files with 1768 additions and 329 deletions
@@ -11,7 +11,7 @@
```markdown
# Тай-брейк при равной полноте
**Секция:** ядро — вышла из спринта: остаток писал нерешённое в журнал · **Теги:** goal:merge-robustness, sprint:2026-08
**Секция:** ядро — вышла из спринта: остаток писал нерешённое в журнал · **Хук:** порядок канонических форм берёт меньшее в 96% случаев — для накопительных это систематический недосчёт · **Теги:** goal:merge-robustness, sprint:2026-08-03
При столкновении точек выигрывает более полная, но при равной полноте побеждает
последняя доставка — а она систематически беднее первой.
@@ -35,9 +35,14 @@
префикс виден прямо в индексе, где и принимается решение «брать или не брать».
- **Мета-строка** — первая непустая строка после заголовка. Обязательна секция,
причина после тире желательна (именно она объясняет, почему задача здесь
оказалась — в том числе «вышла из спринта: …»), теги опциональны. Поля
оказалась — в том числе «вышла из спринта: …»), хук и теги опциональны. Поля
разделяются ` · `, порядок свободный. `·` — служебный разделитель: в тексте
причины его быть не должно.
причины и хука его быть не должно.
- **Хук живёт здесь, а не только в индексе.** Строка индекса его повторяет и
производна от него: `check` сверяет, `check --fix` восстанавливает пропавшую
строку **вместе с хуком**. Пока хук лежал только в индексе, штатная починка
дрейфа теряла его молча и навсегда — а хук это единственное, по чему задачу
выбирают, не открывая.
- **Тело** — одна фраза «что станет наблюдаемо иначе», критерии приёмки, рамки,
контекст, ссылки. Пишется на языке документации проекта.
@@ -46,11 +51,18 @@
### Критерии приёмки
2–5 проверяемых утверждений, **у каждого назван оракул**. Не «работает
корректно», а «повторный прогон даёт тот же отпечаток — оракул: команда сверки».
Это не второе определение готовности, а проектная конкретизация вопроса «по чему
видно, что закончено» из теста готовности ниже: там сказано «признак
завершённости», здесь — «признак плюс чем проверяется».
2–5 проверяемых утверждений **списком** `- …`, **у каждого назван оракул**. Не
«работает корректно», а «повторный прогон даёт тот же отпечаток — оракул:
команда сверки». Это не второе определение готовности, а проектная
конкретизация вопроса «по чему видно, что закончено» из теста готовности ниже:
там сказано «признак завершённости», здесь — «признак плюс чем проверяется».
**Что из этого механизировано.** `check` и `sprint take` считают пункты: меньше
двух — отказ («— работает» одной строкой больше не проходит), больше пяти —
замечание, обычно это признак, что задача крупнее задачи. Наличие оракула
проверяется **эвристикой** — словом «оракул» в пункте, — и потому даёт только
замечание: настоящий оракул от слова «оракул» машина не отличает, и делать вид,
что проверено больше проверенного, хуже, чем не проверять вовсе.
**У идей критериев нет — именно поэтому они идеи.**
@@ -71,9 +83,14 @@
### Вопросы
Неразобранное решение человека живёт разделом `## Вопросы` **плюс тегом
`question`**. Тег — то, по чему вопрос виден снаружи файла (`list --questions`) и
чем работает правило «задача с открытым вопросом в набор не берётся». Раздел без
тега или тег без раздела — дрейф, `check` о нём скажет.
`question`**. Раздел без тега или тег без раздела — дрейф, `check` о нём скажет.
**Судит факт, а не метка.** Отказ во взятии даёт **непустой раздел «Вопросы»**,
независимо от того, стоит ли тег: иначе забывший тег проходил бы, а поставивший
спотыкался — стимул ровно обратный записанному правилу. Тег производен: он нужен
отбору снаружи файла (`list --questions`, `list --tag question`), и его
отсутствие при непустом разделе — замечание, а не лазейка. Тег без раздела тоже
отказ, но с другим советом: либо вопрос записан не туда, либо тег пора снять.
Ответ записывается в тело, тег снимается `edit <slug> --rm-tag question`, а хук
переписывается: «Решено: …» на вопрос «почему это лежит в беклоге» уже не
@@ -98,10 +115,12 @@
- **Задачи цели здесь не перечисляются.** Перечень даёт
`tasks.py list --goal <слаг>`; хранимый список стал бы третьим индексом и
поехал бы на первой же закрытой задаче.
- **Раздел «Завершение»** — то, по чему видно, что цель достигнута. Он же
отличает «цель ещё не декомпозирована» от «все её задачи закрыты»: пометка
вроде тега `decomposed` или строки в теле ставится, когда цель разложена на
задачи.
- **Раздел «Завершение»** — то, по чему видно, что цель достигнута.
- **Тег `decomposed`** отличает «цель ещё не разобрана» от «все её задачи
закрыты» — два состояния, у которых снаружи один и тот же признак: задач нет.
Пометка именно **тегом**, а не строкой в теле: только так она проверяется.
`check` требует его у цели без задач, `check --fix` сам ставит его цели, у
которой задачи есть, а цель с тегом и без задач — прямое приглашение закрыть.
- Цель живёт в `PLAN.md` и **никогда** — в `BACKLOG.md` или `SPRINT.md`.
## Слаг
@@ -137,13 +156,27 @@
Секции — **единственные заголовки `##` в индексе**: любой другой `##` в
преамбуле проверка сочтёт секцией. Внутри секции беклога порядок значения не
имеет — порядка в беклоге нет вовсе. В **линии** плана порядок значим и
имеет — порядка в беклоге нет вовсе.
**Секции «блокеры» среди них нет.** Блокер — состояние, а не полка: он живёт до
ответа человека, а следы остаются вопросами в файлах задач распущенного спринта.
Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила
бы правилу «эскалируем немедленно», поэтому `init` её не заводит, а `check`
говорит о ней в чужом беклоге. Переезжаешь с такой секцией — удали её. В **линии** плана порядок значим и
обосновывается прозой; двигают строку `move <slug> --section линия --after
<другой>`.
Индексы **производны**: расходятся с файлом — правим индексы (`check --fix`).
Строку руками не пишут.
Отсюда же ответ на «а если оборвётся посередине». Мутация сперва проверяет всё
и складывает правки, и только потом пишет: сначала все временные файлы, потом
переименования подряд. Полной транзакции на несколько файлов файловая система не
даёт, но окно сжато до цепочки переименований, а **всё, что в нём может
разъехаться, — производное**: файлы целы, индексы восстанавливает `check --fix`.
Поэтому отказ на второй задаче из пяти не оставляет первую переписанной при
нетронутых индексах.
`SPRINT.md` и есть артефакт заморозки: без него набор существует только в
контексте сессии, и нарушение заморозки ненаблюдаемо.
@@ -174,7 +207,16 @@
попадёт ни в один спринт.
- `question` — в файле есть неразобранный раздел «Вопросы».
- `sprint:<слаг>` — задача заведена в этом спринте; по нему отбирается первая
порция разбора («урожай спринта»).
порция разбора («урожай спринта»). **Ставится сам**: слаг спринта заводит
`sprint start` (по умолчанию — дата начала, он же пишется в `SPRINT.md`), и
`add` при открытом спринте помечает заводимое. Тег, который надо помнить
ставить руками, не ставится никогда — а на нём висит правило «первая порция
разбора — урожай прошедшего спринта».
- `decomposed` — на цели: разложена на задачи (см. «Файл цели»).
Отбор — `list --tag a,b`: перечисленные через запятую теги требуются **все
сразу** (это И, не ИЛИ). Тег, которого нет ни у одной задачи, `list` называет
вслух: молчаливый ноль читается как «таких задач нет», а чаще это опечатка.
Свои теги проект заводит свободно (партия ревью `review-ГГГГ-ММ-ДД`, тема,
источник) — словарь не фиксирован. В индексы теги не выносим: индексы