From 57ce10721fc0e48cb48b20798d7fb861b4c75ba8 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 22:13:21 +0300 Subject: [PATCH] process: tracks ship/show/ask are set by the owner's label (#695) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The analysis of 85 closed tasks #600-#691 showed that the light track cost as much as the full one (115 min and 12 events vs 102 and 13) and that the owner had no label to choose the route. The owner accepted the proposal on 2026-09-28. - PROCESS §5 is the track table: track:ship (S1 -> S5, one line under "## ТЗ", <= 30 src lines, batch review before the beta), track:show (default, S2 -> S5, up to three AC, no spec review, 2 code cycles), track:ask (full route). The owner's label beats the criteria, which become a hint; any agent may raise a track, only the owner lowers it. - §5.1: ci:full / ci:golden / ci:mutants order heavy checks on any track; small and trivial read as track:show, no label as track:ask, an infrastructure task as track:show. - §2, §2.2, §2.4, §2.5, §4, §7.1, §7.2, §9, §11 follow; AUTHOR/REVIEWER digests and AGENTS.md follow with the digest test and its mutants. - task-packet.mjs reports the track via trackFromLabels(); the pipeline reads track:show/track:ship for the cycle limit of 2 and lets an explicit track:ask win. Pipeline behaviour by track is #696. Issue: #695 User-Visible: no Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd --- .github/workflows/_process.yml | 9 +- AGENTS.md | 15 +-- PROCESS.md | 177 +++++++++++++++++---------------- docs/process/AUTHOR.md | 43 ++++---- docs/process/REVIEWER.md | 8 +- scripts/mutation-registry.mjs | 8 +- scripts/task-packet.mjs | 21 +++- test/process-digests.test.mjs | 4 +- test/task-packet.test.mjs | 19 +++- 9 files changed, 179 insertions(+), 125 deletions(-) diff --git a/.github/workflows/_process.yml b/.github/workflows/_process.yml index d590052e..a00aec35 100644 --- a/.github/workflows/_process.yml +++ b/.github/workflows/_process.yml @@ -84,8 +84,15 @@ jobs: BLOCKED=false; EXHAUSTED=false; SMALL=false; TRIVIAL=false has blocked && BLOCKED=true has review-4 && EXHAUSTED=true + # Треки #695 (PROCESS §5): `track:show` и `track:ship` — лимит 2, как у + # прежних `small`/`trivial`; `track:ask` — 4. Пока #696 не влит, `ship` + # проходит конвейер как `show`. has small && SMALL=true has trivial && TRIVIAL=true + has track:show && SMALL=true + has track:ship && SMALL=true + # Явный `track:ask` главнее прежних меток: метка владельца окончательна. + if has track:ask; then SMALL=false; TRIVIAL=false; fi # Этап определяется первым: от него зависит, какие вердикты считать. stage=""; marker="" @@ -102,7 +109,7 @@ jobs: stage="" fi - # Лимит циклов: 4 обычный, 2 на лёгком и коротком треке (PROCESS.md §4). + # Лимит циклов: 4 на `track:ask`, 2 на `track:show`/`track:ship` (PROCESS.md §4, §5). limit=4 if [ "$SMALL" = "true" ] || [ "$TRIVIAL" = "true" ]; then limit=2; fi diff --git a/AGENTS.md b/AGENTS.md index 47795b29..ff8938d0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,13 +73,16 @@ overlap. The committed bundle changes only in a commit with a `Release:` trailer (#657); an ordinary task restores it with `npm run bundle:clean` before committing. -**Tracks** (`PROCESS.md` §5, §5.1): `small` is the default — the spec lives in the -issue body and its review is a comment; taking the full track means naming the -`small` criterion the task fails. `trivial` skips the spec stage for a bug whose -expected behaviour is already on record. An **infrastructure** task — not a single +**Tracks** (`PROCESS.md` §5, §5.1): the label `track:ship`, `track:show` or +`track:ask` sets the route, and the owner's label beats the criteria. `show` is the +default: up to three AC in the issue body, no spec review, `S2` → `S5`. `ship` is a +one-sentence change within fixed limits, `S1` → `S5`. `ask` is the full route with +a spec review. Any agent may raise a track with a reason; only the owner lowers it. +`small` and `trivial` read as `show`. An **infrastructure** task — not a single class A file — skips analysis and spec and enters at `S7-code-review` -(`PROCESS.md` §1). Code review is never skipped on any track: it checks scope, -risks and the evidence from executed tests, but does not replace executing them. +(`PROCESS.md` §1). Every change is code-reviewed; on `ship` the review moves to a +batch review of the beta range before the tag. Review checks scope, risks and the +evidence from executed tests, but does not replace executing them. ## Specs diff --git a/PROCESS.md b/PROCESS.md index 06de29ef..d2d5a493 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -3,7 +3,7 @@ > **Статус документа: канон** (редакция 2026-08-13, ролевое уточнение > 2026-09-13). Решения владельца, на > которых он стоит: прямые коммиты в `dev` **без PR** · канон статуса — **метки**, -> имена английские · лёгкий трек **включён** · автор и ревьюер — независимые +> имена английские · треки `ship`/`show`/`ask` задаёт метка владельца (#695) · автор и ревьюер — независимые > агенты/сессии · любой агент может взять любую роль · инфраструктурные задачи > входят в общий флоу сразу на `S7-code-review`. > @@ -93,8 +93,9 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready → → S6-in-progress → S7-code-review ⟲ → S8-merged → закрыт при выпуске беты служебные: blocked (поверх статуса) rejected (закрыт) -⟲ — возврат на правки, не более 4 циклов (§4), на лёгком и коротком треке 2 -короткий трек (`trivial`, §5.1) идёт S2-analysis → S5-ready, минуя S3 и S4 +⟲ — возврат на правки, не более 4 циклов (§4), на треке `show` 2 +трек `show` (§5) идёт S2-analysis → S5-ready, минуя S3 и S4 +трек `ship` (§5) идёт S1-new → S5-ready, минуя S2, S3 и S4 инфраструктурный трек (§1): без S → S7-code-review ⟲ S6-in-progress → S8-merged ``` @@ -125,9 +126,10 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready → 5. приоритет **P1/P2/P3**; 6. тип: баг / фича / техдолг; 7. затронутые поверхности (модули, диалоги, бэкенд, i18n); - 8. трек — **по умолчанию `small`** (§5). Если задача идёт полным треком, - называется критерий §5, который она не проходит: «обычный трек» без - названного критерия обоснованием не является. + 8. трек — метка `track:ship`, `track:show` или `track:ask` (§5), **по + умолчанию `track:show`**. Для `track:ask` называется критерий §5, которого + задача не проходит; `track:ship` предлагается, когда правка описывается + одним предложением. Метка владельца главнее предложения аналитика. - **Оценки и приоритет ставятся метками сразу, согласие не запрашивается.** Комментарий аналитики — уведомление, а не запрос: **молчание владельца — согласие**, несогласие он выражает правкой меток или комментарием, и это не @@ -139,7 +141,8 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready → умолчанию и `blocked`. Вопрос, который можно отложить до ТЗ, не задаётся в аналитике; вопрос, не блокирующий написание ТЗ, не задаётся вовсе — вместо него в ТЗ пишется блок принятых предположений. -- **Выход:** `S3-spec` — переход выполняет сам аналитик, не дожидаясь ответа. +- **Выход:** на `track:ask` — `S3-spec`, на `track:show` — `S5-ready` после AC в + теле issue (§5); переход выполняет сам аналитик, не дожидаясь ответа. Либо, при явном конфликте со `SCOPE.md`, — предложение отклонить с причиной: это единственный случай, когда аналитика останавливается и ждёт владельца. @@ -159,7 +162,7 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready → - **Ревьюер ≠ автор.** Ревьюер получает issue и ТЗ, без устных пояснений автора. Его задача — не согласиться, а найти, где ТЗ не выполнимо или не проверяемо. - **Артефакт:** `docs/reviews/SPEC-REVIEW--r.md`, вердикт - зелёный / жёлтый / красный. Лёгкий трек — комментарий в issue. + зелёный / жёлтый / красный. Ревью ТЗ проходит только трек `ask` (§5). - **High-находки блокируют.** Medium **в скоупе задачи** чинится в текущем issue: без High это жёлтый вердикт, автор правит ТЗ, фикс проходит повторный цикл. Medium **вне скоупа** — отдельный issue: чужой скоуп в этой задаче не @@ -174,7 +177,9 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready → Не работа, а **очередь**: единственный статус, из которого можно трогать код. Все пункты обязательны: -- ТЗ существует, ревью ТЗ зелёное, ссылки issue ↔ ТЗ на месте; +- ТЗ существует, на `track:ask` ревью ТЗ зелёное, ссылки issue ↔ ТЗ на месте. + На `ship` и `show` ТЗ — в объёме §5, а пункты ниже закрываются одним словом + «нет»: критерии трека их исключают, иначе это `track:ask`; - **AC1…ACn** — пронумерованные проверяемые критерии приёмки; у каждого указано, чем он доказывается: `unit` / `backend` / `smoke` / `golden` / «ревью кода»; - перечислены затронутые файлы и модули; @@ -516,90 +521,91 @@ dev, ни при публикации документа код-ревью: ин - **Граница между «циклом» и «новым багом»:** до закрытия беты находка ревьюера — возврат на правки; после закрытия — новый issue. Иначе лимит 4 обходится заведением issue вместо возврата. -- Для лёгкого трека лимит ревью ТЗ — **2** цикла: задача на три часа, которую - переписывают трижды, лёгкой не была. +- На треке `show` лимит код-ревью — **2** цикла, ревью ТЗ на нём нет (§5): + задача на три часа, которую переписывают трижды, лёгкой не была. --- -## 5. Лёгкий трек (метка `small`) — путь по умолчанию +## 5. Треки `ship`, `show`, `ask` — метка владельца -Умолчание изменено решением владельца 2026-08-27, issue #338. Прежде полный трек -был бесплатен, а выбор лёгкого требовал обоснования. Фактическая цена: **2.9 -ревью-документа на задачу** в среднем и до шести на одну issue (#329, #316, -#290) — при том что Medium-находки всё равно чинятся в той же задаче, без -отдельного цикла. +Решение владельца 2026-09-28, issue #695. Разбор 85 закрытых задач #600–#691 +показал, что прежний лёгкий трек (`small`) стоил почти столько же, сколько +полный: медиана 115 мин и 12 событий против 102 мин и 13. Дешевле был только +короткий (`trivial`) — 41 мин и 5 событий. Трек определялся формальными +критериями, и у владельца не было метки, чтобы задать его самому. -**Порог не изменился.** Критерии ниже те же и по-прежнему обязательны все -одновременно. Изменилась сторона доказательства: теперь обосновывается не выбор -лёгкого трека, а отказ от него — в `S2-analysis` называется критерий, который -задача не проходит. Полный трек остаётся тем, чем был, для геометрии, миграций -конфига и публичных контрактов: там критерии нарушаются сами, и назвать -нарушенный несложно. +**Трек задаёт метка `track:ship`, `track:show` или `track:ask`. Метка владельца +главнее критериев:** критерии ниже — подсказка аналитика, а не приговор. -Инверсия умолчания не отменяет ничего из §5 ниже и ничего из §4: бюджет четырёх -циклов, арбитраж владельца, обязательность ТЗ на полном треке и правило «ревью -до мержа» остаются как были. Меняется только стоимость пути по умолчанию. +- Аналитик предлагает трек в «Оценке» (§7.2) и ставит метку. По умолчанию — + `track:show`. +- Владелец ставит или меняет любую из трёх меток в любой момент; его метка + окончательна. +- Повысить трек (`ship` → `show` → `ask`) вправе любой агент, с причиной в + комментарии. Понизить — только владелец. +- Трек пересматривается, когда владелец снял усложнявший пункт (#688): + аналитик предлагает понижение, решает владелец. -**Критерии — все одновременно; нарушенный называется явно:** +| | `track:ship` | `track:show` | `track:ask` | +|---|---|---|---| +| Для чего | документация, текст, очевидная правка в несколько строк, CSS-мелочь | баг и полировка в рамках описанного поведения; инфраструктура (§1) по умолчанию | геометрия, миграции конфига, публичные контракты, перф и touch, новый UX-контракт | +| Маршрут | `S1-new` → `S5-ready` → `S6` → `S7` → `S8` | `S1-new` → `S2-analysis` → `S5-ready` → `S6` → `S7` → `S8` | полный, §2 | +| ТЗ | строка «что меняется и чем проверить» в теле issue под `## ТЗ` | «Оценка» (§7.2) и до трёх AC в теле issue под `## ТЗ` | полное ТЗ по §7.1 | +| Ревью ТЗ | нет | нет | да, лимит 4 цикла | +| Локальный гейт | `npm run gate:small` | `npm run gate:small` плюс смоуки `smoke-select` и AC | §8 | +| Код-ревью | до слияния нет; пакетное ревью диапазона перед бетой | модель: корректность и AC | документ ревью, как в §2.7 | +| Лимит циклов код-ревью | — | 2 | 4 | + +**Рамки `track:ship`** механические: дифф `src/**` не больше 30 строк, без новых +файлов в `src/**`, без ключей i18n, без полей конфига и без Python. Выход за +рамки переводит задачу в `track:show`. Рамки и слияние `ship` без ревью модели +проверяет и исполняет конвейер (§10.4, #696); **пока #696 не влит**, `ship` и +`show` проходят конвейер как прежний лёгкий трек — с мутантами по диффу и +документом код-ревью. + +**Подсказка аналитику.** `track:show` уместен, когда выполнено всё сразу: - сложность и риск ≤ 3; - одна поверхность (один диалог, один модуль, один эндпоинт); - нет миграции конфига и новых compatibility-полей; - нет нового UX-контракта — меняется поведение в рамках уже описанного; -- нет влияния на производительность и на touch-контракт. - -**Что упрощается:** - -- ТЗ короче: проблема · контракт · AC1…ACn с доказательством · откат - (в теле issue, как и на полном треке с 2026-09-10); -- ревью ТЗ — комментарий второго агента, отдельный документ не нужен; -- лимит ревью ТЗ — 2 цикла. - -**Что не упрощается:** issue, оценка, статусы, трейлеры коммитов, changelog, -**код-ревью и его документ**, закрытие после беты. Код-ревью не пропускается: -оно проверяет скоуп, риски и качество доказательств, но не заменяет исполнение -тестов. Единственное исключение из повторного ревью — починка упавшего -предрелизного гейта, §11.4. - -Если по ходу выясняется, что критерий нарушен (появилась миграция, задело второй -модуль) — метка `small` снимается, issue возвращается в `S3-spec` и получает -полное ТЗ в теле issue по §7.1. Это не провал, это ранняя диагностика. - -### 5.1 Короткий трек (метка `trivial`) - -Решение владельца 2026-08-13, issue #128. Лёгкий трек делает ТЗ дешёвым; короткий -обходится без него совсем. - -**Маршрут:** `S1-new` → `S2-analysis` → `S5-ready` → `S6-in-progress` → -`S7-code-review` → `S8-merged`. Стадии `S3-spec` и `S4-spec-review` пропускаются. - -`S2-analysis` остаётся: это комментарий, а не прогон CI, и именно там владелец -решает приоритет и ценность. AC пишет автор в теле issue при переводе в -`S5-ready` — до перехода, иначе ревьюеру нечего будет сверять. - -**Критерии, все обязательны:** - -- тип `bug`; -- правка ограничена одной поверхностью, нового UX-контракта нет; -- нет миграции конфига, новых ключей i18n, влияния на перф и touch; -- AC выражаются тремя проверяемыми утверждениями или меньше; +- нет влияния на производительность и на touch-контракт; - **ожидаемое поведение уже зафиксировано** — в `docs/USER-GUIDE.ru.md`, в каноническом документе подсистемы либо однозначно в самом отчёте. Решать нечего. - Если есть что решать, это `S3-spec`, и никакая экономия этого не отменяет. -Метка ставится в `S2-analysis` вместе с остальными оценками, одним комментарием, -где владелец утверждает и приоритет. +Невыполненный пункт — повод предложить `track:ask` с названным критерием. +`track:ship` — когда правку можно описать одним предложением и она укладывается +в рамки выше. -**Что не упрощается:** issue, оценка, статусы, трейлеры, changelog и **код-ревью**. -Лимит циклов код-ревью — 2, как на лёгком треке. - -Если по ходу выясняется, что критерий нарушен, метка снимается и issue уходит в -`S3-spec` за нормальным ТЗ. Как и на лёгком треке, это не провал, а ранняя +**Чем опасны `show` и `ship`.** Они убирают место, где решение проверялось до +написания кода. Признак «решать нечего» держит всю конструкцию, и его нельзя +подтверждать ощущением — только ссылкой на уже зафиксированное поведение. Если по +ходу выясняется, что решать есть что, трек повышается до `ask`: issue уходит в +`S3-spec` и получает полное ТЗ в теле issue по §7.1. Это не провал, а ранняя диагностика. -**Чем этот трек опасен.** Он убирает единственное место, где решение проверялось -до написания кода. Признак «решать нечего» держит всю конструкцию, и его нельзя -подтверждать ощущением — только ссылкой на уже зафиксированное поведение. +**Что не меняется ни на одном треке:** issue и правило №1 (§1), трейлеры +коммитов, changelog для видимого изменения, зелёный Validate на точном SHA тега +беты (§8), гейты стабильного релиза. Качество держится на `dev` и на кандидате +беты, а не на каждой ветке. + +### 5.1 Метки тяжёлых проверок и прежние метки + +Тяжёлые проверки заказываются метками на любом треке — ставит владелец или +автор с причиной в комментарии: + +| Метка | Что включает | +|---|---| +| `ci:full` | полный Validate на ветке задачи | +| `ci:golden` | golden на ветке и приёмку сдвинутых кадров в самой задаче | +| `ci:mutants` | мутанты по диффу на кандидате ревью и слияния | + +Как конвейер читает эти метки, описывает §10.4; до #696 и #697 действуют +прежние правила §8. + +**Прежние метки.** `trivial` и `small` читаются как `track:show`; продуктовая +задача без трековой метки — как `track:ask`; инфраструктурная задача (§1) без +трековой метки — как `track:show`. Новые задачи получают только `track:*`. --- @@ -643,7 +649,7 @@ dev, ни при публикации документа код-ревью: ин ``` issue #NN ↔ ТЗ тело issue, раздел `## ТЗ` (хеш тела — в якорях ревью) - ↔ ревью ТЗ docs/reviews/SPEC-REVIEW-NN-rN.md (или комментарий при `small`) + ↔ ревью ТЗ docs/reviews/SPEC-REVIEW-NN-rN.md (только `track:ask`) ↔ ветка issue/NN-slug ↔ коммиты трейлеры Issue: #NN · User-Visible: yes|no ↔ ревью кода docs/reviews/CODE-REVIEW--rN.md @@ -695,7 +701,7 @@ issue #NN Короткие и однообразные, чтобы читались и человеком, и машиной. - **Аналитика:** `Оценка: ценность N/10 · сложность N/10 · P<1-3> · тип · - поверхности: … · дубликаты: … · лёгкий трек: да/нет` + поверхности: … · дубликаты: … · трек: ship/show/ask (причина)` - **Занятие:** `Взял: <роль> · сессия · ветка issue/NN-slug` - **Хендофф:** `Сделано: … · Файлы: … · Гейты: <команда → результат> · НЕ сделано: … · Риски: … · Следующий статус: … · Новые issue: #…` @@ -850,8 +856,10 @@ Performance зелёные на точном SHA, плюс зелёный E2E н | `blocked` | Ждём внешнего или владельца, **поверх** статусной метки | | `rejected` | Отклонено, issue закрыт | -Модификаторы: `small` (лёгкий трек, сложность ≤3), `trivial` (короткий трек, -§5.1), `hotfix`, `process`, `review-4`; приоритет `P1`/`P2`/`P3`; тип `bug`/`feature`/`tech-debt`. +Модификаторы: трек `track:ship`/`track:show`/`track:ask` (§5), тяжёлые проверки +`ci:full`/`ci:golden`/`ci:mutants` (§5.1), `hotfix`, `process`, `review-4`; +приоритет `P1`/`P2`/`P3`; тип `bug`/`feature`/`tech-debt`. Прежние `small` и +`trivial` читаются как `track:show` (§5.1). Тематические метки (`polish`, `infra`, `tests`, `docs`, `security`, `vacuum`) ортогональны процессу. @@ -1230,9 +1238,10 @@ npm ci, Python и Chromium, оставаясь исполненной job: до ## 11. Исключения -### 11.1 Лёгкий трек +### 11.1 Треки -См. §5 — это не исключение из правила №1, а более дешёвый путь по тем же статусам. +См. §5 — треки `ship` и `show` не исключения из правила №1, а более дешёвые пути +по тем же статусам. ### 11.2 Аварийный хотфикс (метка `hotfix`, решение владельца) @@ -1295,8 +1304,10 @@ Golden, браузерные смоки, performance и полный HA-харн запись в issue публична и релиз-менеджер видит, что именно было сделано перед выпуском. -Это исключение из правила «код-ревью не пропускается никогда» (§5, §7.1) — -единственное, и относится только к окну между `S8-merged` и выпуском. +Это исключение из правила «каждое изменение проходит код-ревью» (§5, §7.1). +Второе такое место — трек `ship` (§5): его ревью не отменяется, а переносится +на пакетное ревью диапазона перед бетой. Здесь же исключение относится только к +окну между `S8-merged` и выпуском. ### 11.5 Независимое ревью линии перед стабильным релизом diff --git a/docs/process/AUTHOR.md b/docs/process/AUTHOR.md index f3c915f2..79dff7c6 100644 --- a/docs/process/AUTHOR.md +++ b/docs/process/AUTHOR.md @@ -40,21 +40,28 @@ - Чек-лист комментарием: дубликаты, скоуп по `docs/SCOPE.md` и `docs/TOUCH-SUPPORT.md`, ценность, сложность и риск, приоритет, тип, поверхности, трек. Оценки ставятся метками сразу; молчание владельца — - согласие; в `S3-spec` аналитик переводит сам. Останавливается аналитика + согласие; дальше аналитик переводит сам: `track:ask` — в `S3-spec`, + `track:show` — в `S5-ready`. Останавливается аналитика только на конфликте со `SCOPE.md` ([§2.2](../../PROCESS.md#22-аналитика-и-оценка)). - Шаблон: `Оценка: ценность N/10 · сложность N/10 · P<1-3> · тип · - поверхности: … · дубликаты: … · лёгкий трек: да/нет` + поверхности: … · дубликаты: … · трек: ship/show/ask (причина)` ([§7.2](../../PROCESS.md#72-шаблоны-комментариев)). -- Лёгкий трек `small` — путь по умолчанию: обосновывается не выбор лёгкого - трека, а отказ от него — называется нарушенный критерий. Критерии, все - сразу: сложность и риск ≤ 3; одна поверхность; нет миграции конфига; - нет нового UX-контракта; нет влияния на перф и touch - ([§5](../../PROCESS.md#5-лёгкий-трек-метка-small--путь-по-умолчанию)). -- Короткий трек `trivial`: `S2-analysis` → `S5-ready`, AC автор пишет в теле - issue до перехода. Тип `bug`, одна поверхность, без i18n, миграции, перфа и - touch, не больше трёх AC, и ожидаемое поведение уже зафиксировано — решать - нечего ([§5.1](../../PROCESS.md#51-короткий-трек-метка-trivial)). +- Трек задаёт метка `track:ship`, `track:show` или `track:ask`, по умолчанию + `track:show`. Метка владельца главнее критериев; повысить трек может любой + агент с причиной в комментарии, понизить — только владелец + ([§5](../../PROCESS.md#5-треки-ship-show-ask--метка-владельца)). +- `track:show`: `S2-analysis` → `S5-ready`, до трёх AC автор пишет в теле + issue до перехода; ревью ТЗ нет, лимит код-ревью 2. Уместен, когда всё + сразу: сложность и риск ≤ 3, одна поверхность, нет миграции, нового + UX-контракта, влияния на перф и touch, и ожидаемое поведение уже + зафиксировано — решать нечего ([§5](../../PROCESS.md#5-треки-ship-show-ask--метка-владельца)). +- `track:ship`: `S1-new` → `S5-ready`, в теле issue строка «что меняется и чем + проверить». Рамки: дифф `src/**` до 30 строк, без новых файлов, i18n, полей + конфига и Python ([§5](../../PROCESS.md#5-треки-ship-show-ask--метка-владельца)). +- Тяжёлые проверки на любом треке — метками `ci:full`, `ci:golden`, + `ci:mutants`; прежние `small` и `trivial` читаются как + `track:show` ([§5.1](../../PROCESS.md#51-метки-тяжёлых-проверок-и-прежние-метки)). ## ТЗ (`S3-spec`) @@ -63,9 +70,9 @@ - Обязательные разделы: сценарий · что человек увидит до и после · проблема · скоуп и не-скоуп · контракт поведения · UX · модель данных и миграция · i18n · AC1…ACn с доказательством · план автотестов · риски · откат · - release-артефакты. На лёгком треке короче: проблема · контракт · AC · откат - ([§7.1](../../PROCESS.md#71-цепочка), - [§5](../../PROCESS.md#5-лёгкий-трек-метка-small--путь-по-умолчанию)). + release-артефакты. На `track:show` — до трёх AC, на `track:ship` — одна + строка ([§7.1](../../PROCESS.md#71-цепочка), + [§5](../../PROCESS.md#5-треки-ship-show-ask--метка-владельца)). - Размытое место не додумывается. Владельцу задаются только продуктовые вопросы — что человек видит или делает и какой объём видимых изменений входит в issue. Всё, чего пользователь не наблюдает, автор решает сам и @@ -74,13 +81,13 @@ - Вопросы — одним комментарием, пачкой: что неясно · что изменится от ответа · вариант по умолчанию. Пока ждём ответа, issue остаётся в `S3-spec` и получает `blocked` ([§7.1](../../PROCESS.md#71-цепочка)). -- DoR перед `S5-ready`: зелёное ревью ТЗ; пронумерованные AC со способом +- DoR перед `S5-ready`: на `track:ask` зелёное ревью ТЗ; пронумерованные AC со способом доказательства (`unit` / `backend` / `smoke` / `golden` / «ревью кода»); файлы и модули; ключи i18n en + ru; миграция по `docs/CONFIG-COMPATIBILITY.md`; перф; touch; release-артефакты; откат; нет - открытых продуктовых вопросов - ([§2.5](../../PROCESS.md#25-готово-к-разработке-dor)). -- Лимит — 4 цикла ревью, на лёгком и коротком треке 2; зелёный вердикт цикла + открытых продуктовых вопросов. На `ship` и `show` пункты DoR закрываются + словом «нет» ([§2.5](../../PROCESS.md#25-готово-к-разработке-dor)). +- Лимит — 4 цикла ревью, на `track:show` 2 цикла код-ревью; зелёный вердикт цикла не тратит; исчерпание — решение владельца: разделить, отклонить, арбитраж ([§4](../../PROCESS.md#4-лимит-циклов-ревью-4)). diff --git a/docs/process/REVIEWER.md b/docs/process/REVIEWER.md index 213c386f..a4b73f69 100644 --- a/docs/process/REVIEWER.md +++ b/docs/process/REVIEWER.md @@ -24,8 +24,8 @@ ## Ревью ТЗ -- Артефакт — `docs/reviews/SPEC-REVIEW--r.md`, на лёгком треке — - комментарий ([§2.4](../../PROCESS.md#24-тз-на-ревью)). +- Артефакт — `docs/reviews/SPEC-REVIEW--r.md`. Ревью ТЗ проходит только + трек `ask` ([§2.4](../../PROCESS.md#24-тз-на-ревью)). - ТЗ живёт в теле issue, раздел `## ТЗ`; `docs/specs/` — архив до 2026-09-10 ([§2.3](../../PROCESS.md#23-тз-в-работе--написание-тз)). - Проверить обязательные разделы, однозначность каждого AC и способ его @@ -122,8 +122,8 @@ ([§7.2](../../PROCESS.md#72-шаблоны-комментариев)). - Вперёд двигает только зелёный вердикт; жёлтый и красный возвращают автору ([§7.2](../../PROCESS.md#72-шаблоны-комментариев)). -- Зелёный вердикт цикла не образует; лимит — 4 цикла, на лёгком и коротком - треке 2; бюджет считается по этапу +- Зелёный вердикт цикла не образует; лимит — 4 цикла, на `track:show` 2; + бюджет считается по этапу ([§4](../../PROCESS.md#4-лимит-циклов-ревью-4), [§10.4](../../PROCESS.md#104-событийный-конвейер-метка-как-триггер)). - Запрещено: Medium-находки, оставленные как TODO в документе ревью; diff --git a/scripts/mutation-registry.mjs b/scripts/mutation-registry.mjs index 5f177680..0b2711df 100644 --- a/scripts/mutation-registry.mjs +++ b/scripts/mutation-registry.mjs @@ -10262,8 +10262,8 @@ const MUTANT_DEFINITIONS = [ + 'the digest must fail as soon as an anchor goes stale (#634 AC2)', patches: [{ file: 'docs/process/AUTHOR.md', - find: '[§5.1](../../PROCESS.md#51-короткий-трек-метка-trivial)', - replace: '[§5.1](../../PROCESS.md#51-короткий-трек)', + find: '[§5.1](../../PROCESS.md#51-метки-тяжёлых-проверок-и-прежние-метки)', + replace: '[§5.1](../../PROCESS.md#51-метки-тяжёлых-проверок)', }], }, { @@ -10273,8 +10273,8 @@ const MUTANT_DEFINITIONS = [ + 'digest invented or a paraphrase nobody can check (#634 AC2)', patches: [{ file: 'docs/process/AUTHOR.md', - find: ' нечего ([§5.1](../../PROCESS.md#51-короткий-трек-метка-trivial)).', - replace: ' нечего.', + find: ' `track:show` ([§5.1](../../PROCESS.md#51-метки-тяжёлых-проверок-и-прежние-метки)).', + replace: ' `track:show`.', }], }, { diff --git a/scripts/task-packet.mjs b/scripts/task-packet.mjs index 8b4f2a6e..bd91fbc8 100644 --- a/scripts/task-packet.mjs +++ b/scripts/task-packet.mjs @@ -40,8 +40,8 @@ export function rightsFor(status, labels = [], { infrastructure = false, infrast lines.push('метка infra — только подсказка, не доказательство и не право: до ветки проверь предполагаемые пути; без class A начинай сразу, при любом class A нужен продуктовый S-flow (#562)'); } switch (status) { - case 'S1-new': lines.push('следующий шаг: аналитика (S2) — оценки метками, критерий лёгкого трека, затем ТЗ'); break; - case 'S2-analysis': lines.push('следующий шаг: ТЗ (S3); трек по умолчанию small — отказ от него обосновать названным критерием §5'); break; + case 'S1-new': lines.push('следующий шаг: аналитика (S2) — оценки и трек метками (по умолчанию track:show); на track:ship — строка «что меняется и чем проверить» под «## ТЗ», затем S5'); break; + case 'S2-analysis': lines.push('следующий шаг: track:show — до трёх AC под «## ТЗ», затем S5; track:ask — ТЗ (S3) с названным критерием §5'); break; case 'S3-spec': lines.push('следующий шаг: ТЗ готово → push ветки → метка S4-spec-review (метку после push)'); break; case 'S4-spec-review': lines.push('идёт ревью ТЗ: ждать вердикт (scripts/wait-verdict.mjs), не править материал'); break; case 'S5-ready': lines.push('следующий шаг: ветка issue/NN-slug от dev, код по ТЗ, метка S6-in-progress'); break; @@ -126,6 +126,21 @@ export function branchIsInfrastructure(changedFiles = []) { const PRE_CODE_STATUSES = ['S1-new', 'S2-analysis', 'S3-spec', 'S4-spec-review', 'S5-ready']; +/** + * Трек продуктовой задачи по меткам (PROCESS §5, #695): `track:ship`, + * `track:show` или `track:ask`; прежние `trivial` и `small` читаются как `show` + * (§5.1), задача без трековой метки — как `ask`. Метки трека не доказывают + * продуктовый поток: инфраструктурной задаче владелец тоже может поставить + * `track:*`, чтобы задать ей цену конвейера. + */ +export function trackFromLabels(labels = []) { + if (labels.includes('track:ship')) return 'ship'; + if (labels.includes('track:show')) return 'show'; + if (labels.includes('track:ask')) return 'ask'; + if (labels.includes('trivial') || labels.includes('small')) return 'show'; + return 'ask'; +} + /** * Признаки продуктового S-flow (#632). Инфраструктурная задача входит в поток * сразу на S7 и никогда не несёт S1–S5, ТЗ и ревью ТЗ; поэтому любой из этих @@ -165,7 +180,7 @@ export function buildPacket(inputs) { const track = infrastructure ? 'инфраструктурный' : infrastructureHint ? 'инфраструктурный (предварительно; подтвердить путями/diff)' - : labels.includes('trivial') ? 'trivial' : labels.includes('small') ? 'small' : 'полный'; + : trackFromLabels(labels); const stage = status === 'S4-spec-review' || status === 'S3-spec' || status === 'S5-ready' ? 'spec' : 'code'; const verdict = lastVerdict(comments, reviewDocs, stage); // ТЗ живёт в теле issue (#517); архивный файл — источник только у задач до diff --git a/test/process-digests.test.mjs b/test/process-digests.test.mjs index 0855a8a9..87cba7c2 100644 --- a/test/process-digests.test.mjs +++ b/test/process-digests.test.mjs @@ -46,8 +46,8 @@ const KEY_RULES = { ['1-основное-правило', 'D сильнее A'], ['3-правила', 'Ровно одна метка статуса'], ['3-правила', 'Статус меняется до действия, а не после'], - ['5-лёгкий-трек-метка-small--путь-по-умолчанию', 'обосновывается не выбор лёгкого трека, а отказ от него'], - ['51-короткий-трек-метка-trivial', 'ожидаемое поведение уже зафиксировано'], + ['5-треки-ship-show-ask--метка-владельца', 'Метка владельца главнее критериев'], + ['5-треки-ship-show-ask--метка-владельца', 'ожидаемое поведение уже зафиксировано'], ['71-цепочка', 'Владельцу задаются только продуктовые вопросы'], ['71-цепочка', 'issue остаётся в `S3-spec` и получает `blocked`'], ['26-в-разработке--реализация', 'каждый коммит несёт трейлеры `Issue: #` и `User-Visible: yes|no`'], diff --git a/test/task-packet.test.mjs b/test/task-packet.test.mjs index 15b9c047..0e5af118 100644 --- a/test/task-packet.test.mjs +++ b/test/task-packet.test.mjs @@ -2,6 +2,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import { branchIsInfrastructure, buildPacket, evidenceFor, productFlowEvidence, extractAcceptanceCriteria, lastVerdict, ownerDecisions, renderPacket, rightsFor, + trackFromLabels, } from '../scripts/task-packet.mjs'; import { materialAnchorBlock } from '../scripts/review-doc-guard.mjs'; @@ -87,7 +88,7 @@ test('пакет собирается и рендерится: статус, м validate: { status: 'зелёный', url: 'https://run' }, }); assert.equal(packet.status, 'S6-in-progress'); - assert.equal(packet.track, 'small'); + assert.equal(packet.track, 'show'); assert.equal(packet.material.treeMatchesVerdict, true); assert.deepEqual(packet.unverified, ['AC2']); const md = renderPacket(packet); @@ -118,7 +119,7 @@ test('#562: the infra label alone never grants the accelerated track', () => { labels: ['infra', 'S6-in-progress'], branch: { name: 'issue/999-product', tip: 'e'.repeat(40), base: 'f'.repeat(40), ahead: 1, behind: 0, treeWithoutReviews: null, infrastructure: false }, }); - assert.equal(packet.track, 'полный'); + assert.equal(packet.track, 'ask'); assert.ok(packet.rights.some((l) => l.includes('продуктовый код трогать МОЖНО'))); }); @@ -152,7 +153,7 @@ test('#632: product S6 issue keeps class A rights while its diff has no class A branch: { name: 'issue/607-ha-dialog-close', tip: 'e'.repeat(40), base: 'f'.repeat(40), ahead: 1, behind: 0, treeWithoutReviews: null, infrastructure: true }, reviewDocs: [{ name: 'SPEC-REVIEW-607-r1.md', text: 'Вердикт: зелёный' }], }); - assert.equal(packet.track, 'small'); + assert.equal(packet.track, 'show'); assert.ok(packet.rights.some((l) => l.includes('продуктовый код трогать МОЖНО'))); assert.ok(packet.rights.every((l) => !l.includes('файлы класса A трогать НЕЛЬЗЯ'))); assert.match(renderPacket(packet), /Продуктовый поток: .*ТЗ/); @@ -192,7 +193,7 @@ test('#632 r1: trivial issue in S6/S7 keeps class A rights without any spec arte labels, branch: { name: 'issue/612-x', tip: 'e'.repeat(40), base: 'f'.repeat(40), ahead: 1, behind: 0, treeWithoutReviews: null, infrastructure: true }, }); - assert.equal(packet.track, 'trivial', labels.join(',')); + assert.equal(packet.track, 'show', labels.join(',')); assert.ok(packet.rights.some((l) => l.includes('продуктовый код трогать МОЖНО')), labels.join(',')); assert.ok(packet.rights.every((l) => !l.includes('файлы класса A трогать НЕЛЬЗЯ')), labels.join(',')); } @@ -217,3 +218,13 @@ test('#517 AC5: AC берутся из тела issue, файл ТЗ — тол const neither = buildPacket({ ...base, issue: { ...base.issue, body: 'ничего' }, specs: [] }); assert.deepEqual(neither.acceptance, []); }); + +test('#695: трек по меткам — track:* главнее прежних, по умолчанию ask', () => { + assert.equal(trackFromLabels(['track:ship']), 'ship'); + assert.equal(trackFromLabels(['track:show', 'bug']), 'show'); + assert.equal(trackFromLabels(['track:ask', 'small']), 'ask', 'явная метка владельца главнее прежней'); + assert.equal(trackFromLabels(['trivial']), 'show'); + assert.equal(trackFromLabels(['small']), 'show'); + assert.equal(trackFromLabels(['bug', 'P2']), 'ask'); + assert.equal(trackFromLabels([]), 'ask'); +});