mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Validate / docs (push) Failing after 27s
Validate / provenance (push) Successful in 54s
Validate / process-gate (push) Failing after 44s
Validate / changes (push) Successful in 39s
Validate / reuse (push) Successful in 45s
Validate / hacs (push) Failing after 16s
Validate / hassfest (push) Failing after 13s
Validate / frontend (push) Successful in 6m37s
Validate / backend (push) Failing after 9m6s
Validate / performance_smoke (push) Failing after 18m53s
Validate / smoke (push) Failing after 39m24s
Validate / golden (push) Failing after 11m5s
Section 4 now says what the pipeline does: a cycle is a verdict with blocking findings followed by a return to the author, so a green verdict consumes nothing — the case that cost #225 an arbitration after a failed merge forced a rebase and a third attempt. The canon also separates the two quantities the verdict line carries. The attempt number names the review document, because two runs sharing a number would overwrite each other's artefact; the budget counts blocking cycles only. That is why the document threshold in the process gate sits above the cycle limit, and why the guard reports a recount of review-4 rather than removing the label itself. Issue: #227 User-Visible: no
947 lines
78 KiB
Markdown
947 lines
78 KiB
Markdown
# Процесс работы над House Plan
|
||
|
||
> **Статус документа: канон** (редакция 2026-08-13). Решения владельца, на
|
||
> которых он стоит: прямые коммиты в `dev` **без PR** · канон статуса — **метки**,
|
||
> имена английские · лёгкий трек **включён** · автор и ревьюер — разные модели ·
|
||
> инфраструктурные задачи идут **вне** флоу.
|
||
>
|
||
> **Область действия:** обязателен для владельца и для любого агента. Читается
|
||
> сразу после `docs/SCOPE.md` и `AGENTS.md`, до `docs/STATUS.md`. Живёт в
|
||
> репозитории: до августа 2026 канон лежал только в папке владельца, и свежий клон
|
||
> его не содержал вовсе.
|
||
>
|
||
> **Приоритет источников.** Канонический бэклог — GitHub Issues; статус живёт в
|
||
> метках и больше нигде: Project v2 не используется. При расхождении
|
||
> документации с GitHub побеждает GitHub. При расхождении этого документа с
|
||
> `.github/workflows/*.yml` и `scripts/*` побеждает **фактическая автоматизация**:
|
||
> она исполняется, а описание — нет. Расхождение при этом не игнорируется, а
|
||
> заводится issue с меткой `process`.
|
||
>
|
||
> При расхождении процесса и привычки побеждает процесс.
|
||
|
||
---
|
||
|
||
## 1. Основное правило
|
||
|
||
**Изменение продуктового кода без issue запрещено.** Код меняется только тогда,
|
||
когда issue существует и находится в статусе «Готово к разработке» или дальше.
|
||
Исключения — только §11, и каждое оставляет след.
|
||
|
||
Правило работает лишь при точной границе «продуктового кода», иначе спор
|
||
переносится на границу:
|
||
|
||
| Класс | Что входит | Нужен ли issue |
|
||
|---|---|---|
|
||
| **A. Продукт** | `src/**`, `custom_components/houseplan/**/*.py`, `manifest.json`, `hacs.json`, `src/i18n/*.json`, `custom_components/**/translations/*` | **Да, обязательно.** Только из «Готово к разработке» или дальше |
|
||
| **B. Гейты и инструменты** | `test/**`, `tests_backend/**`, `demo/**`, `scripts/**`, весь `.github/**`, `.githooks/**`, `rollup.config.mjs`, `tsconfig*.json`, `package.json`, `package-lock.json`, `pytest.ini`, `.gitignore`, `.gitattributes` | **Да.** Может использовать issue того изменения, которое покрывает; самостоятельная работа над гейтом получает свой issue (тип `tech-debt`) |
|
||
| **C. Документация** | `docs/**`, `README*`, `CHANGELOG*`, `AGENTS.md`, `CONTRIBUTING.md`, `PROCESS*.md`, `LICENSE`, `(CODE\|SPEC)-REVIEW-*.md` | Документирование A/B в том же коммите — часть DoD своего issue. Самостоятельная работа над документацией — свой issue |
|
||
| **D. Сгенерированное** | `dist/**`, `custom_components/houseplan/frontend/**`, `demo/srv/assets/houseplan-card.js`, `demo/golden/baselines/**` | Никогда не меняется само по себе. Коммит **только** класса D допустим лишь как релизный промоушен или как принятие эталонов с доказательством ревью |
|
||
|
||
Практический смысл таблицы: «я только поправил тест» и «я только пересобрал
|
||
бандл» перестают быть лазейками.
|
||
|
||
Классы неупорядочены, но при пересечении путей **D сильнее A**: собранный бандл
|
||
лежит внутри `custom_components/houseplan/frontend/`, и без этого правила он
|
||
считался бы продуктовым исходником.
|
||
|
||
**Инфраструктурная задача идёт вне флоу** (решение владельца 2026-08-13,
|
||
issue #118). Признак механический: **ни одного файла класса A**. Такая задача
|
||
делается без ТЗ, ревью ТЗ, код-ревью и без прохода по статусам — флоу построен
|
||
для изменений, у которых есть персона и видимое поведение, а в инфраструктуре ТЗ
|
||
пересказывало бы очевидное, и автор с ревьюером оказались бы одной ролью.
|
||
Проверкой служат гейты и CI. Обязательным остаётся issue, трейлеры и зелёные
|
||
`typecheck`, `test`, `build`.
|
||
|
||
Задача, задевающая класс A хотя бы одним файлом, инфраструктурной **не
|
||
является** и идёт полным флоу. «В основном инфраструктурная» не бывает: иначе
|
||
это дорога, по которой продуктовые правки минуют ревью. Признак задан через
|
||
класс файлов, а не через самоощущение исполнителя, именно поэтому.
|
||
|
||
---
|
||
|
||
## 2. Жизненный цикл
|
||
|
||
Восемь рабочих статусов и два служебных. Фазы тестирования в цикле сознательно
|
||
**нет**: найденные позже дефекты заводятся отдельными issue и проходят цикл
|
||
заново. Issue закрывается после выпуска беты.
|
||
|
||
```
|
||
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
|
||
```
|
||
|
||
Переходы `S4-spec-review` и `S7-code-review` выполняются **автоматически**: метка
|
||
порождает событие, событие запускает ревью (§10.4). Остальные ставит исполнитель.
|
||
|
||
### 2.1 Новое — заведение задачи
|
||
|
||
- **Кто:** любой — владелец, агент, пользователь (Telegram, GitHub).
|
||
- **Вход:** проблема в пользовательских терминах; как проявляется или зачем нужно.
|
||
Решение **не требуется** и не приветствуется.
|
||
- **Запрещено:** ставить приоритет, оценивать, писать ТЗ, начинать код.
|
||
|
||
### 2.2 Аналитика и оценка
|
||
|
||
Задача разбирается — и разобранная **сама идёт дальше**. Умолчание изменено
|
||
решением владельца 2026-08-14: раньше аналитика ждала подтверждения по каждому
|
||
пункту, и большинство ожиданий ничего не меняло — issue в основном описаны
|
||
однозначно.
|
||
|
||
- **Кто:** агент-аналитик. Владелец не утверждает переход — он правит асинхронно.
|
||
- **Чек-лист**, результат — комментарием в issue:
|
||
1. дубликаты проверены (ссылки на похожие issue);
|
||
2. в скоупе по `docs/SCOPE.md` и `docs/TOUCH-SUPPORT.md`;
|
||
3. **пользовательская ценность 1–10** и **ценность для разработки** — что
|
||
упрощает или разблокирует;
|
||
4. **сложность и риск 1–10** — трудоёмкость плюс вероятность задеть смежное;
|
||
5. приоритет **P1/P2/P3**;
|
||
6. тип: баг / фича / техдолг;
|
||
7. затронутые поверхности (модули, диалоги, бэкенд, i18n);
|
||
8. трек: обычный / `small` / `trivial` по критериям §5 и §5.1.
|
||
- **Оценки и приоритет ставятся метками сразу, согласие не запрашивается.**
|
||
Комментарий аналитики — уведомление, а не запрос: **молчание владельца —
|
||
согласие**, несогласие он выражает правкой меток или комментарием, и это не
|
||
останавливает работу. Право отклонить задачу (`rejected`) остаётся за
|
||
владельцем на любой стадии.
|
||
- **Вопросов владельцу на этом этапе нет.** Единственный класс вопросов, который
|
||
вообще задаётся владельцу, — продуктовые (§7.1: что человек видит или делает,
|
||
объём видимых изменений), и их место — этап ТЗ, пачкой, с вариантами по
|
||
умолчанию и `blocked`. Вопрос, который можно отложить до ТЗ, не задаётся в
|
||
аналитике; вопрос, не блокирующий написание ТЗ, не задаётся вовсе — вместо
|
||
него в ТЗ пишется блок принятых предположений.
|
||
- **Выход:** `S3-spec` — переход выполняет сам аналитик, не дожидаясь ответа.
|
||
Либо, при явном конфликте со `SCOPE.md`, — предложение отклонить с причиной:
|
||
это единственный случай, когда аналитика останавливается и ждёт владельца.
|
||
|
||
### 2.3 ТЗ в работе — написание ТЗ
|
||
|
||
- **Кто:** автор ТЗ, назначает себя. Статус означает «занято».
|
||
- **Артефакт:** `docs/specs/<NN>-<slug>.md`, где `NN` — **номер issue**.
|
||
Многоэтапная задача: `<NN>-<slug>-stage<N>.md`.
|
||
- **Лёгкий трек:** ТЗ пишется в теле issue, файл не создаётся (§5).
|
||
- **Выход:** полная первая редакция по §7.
|
||
|
||
### 2.4 ТЗ на ревью
|
||
|
||
- **Ревьюер ≠ автор.** Ревьюер получает issue и ТЗ, без устных пояснений автора.
|
||
Его задача — не согласиться, а найти, где ТЗ не выполнимо или не проверяемо.
|
||
- **Артефакт:** `docs/reviews/SPEC-REVIEW-<NN>-r<N>.md`, вердикт
|
||
зелёный / жёлтый / красный. Лёгкий трек — комментарий в issue.
|
||
- **High-находки блокируют.** Medium **в скоупе задачи** чинится в текущем
|
||
issue: без High это жёлтый вердикт, автор правит ТЗ, фикс проходит повторный
|
||
цикл. Medium **вне скоупа** — отдельный issue: чужой скоуп в этой задаче не
|
||
правится. «Оставили в тексте ревью» не считается закрытием ни для одной
|
||
(решение владельца 2026-08-19, #202: отдельный issue дороже правки на месте).
|
||
Low либо правится, либо снимается решением ревьюера с записью.
|
||
- **Выход:** «Готово к разработке» либо возврат в «ТЗ в работе» — не более
|
||
4 циклов (§4). Второй и последующие циклы разбираются по дельте (§2.10).
|
||
|
||
### 2.5 Готово к разработке (DoR)
|
||
|
||
Не работа, а **очередь**: единственный статус, из которого можно трогать код.
|
||
Все пункты обязательны:
|
||
|
||
- ТЗ существует, ревью ТЗ зелёное, ссылки issue ↔ ТЗ на месте;
|
||
- **AC1…ACn** — пронумерованные проверяемые критерии приёмки; у каждого указано,
|
||
чем он доказывается: `unit` / `backend` / `smoke` / `golden` / «ревью кода»;
|
||
- перечислены затронутые файлы и модули;
|
||
- i18n: ключи en + ru перечислены;
|
||
- миграция и compatibility-поля решены по `docs/CONFIG-COMPATIBILITY.md`;
|
||
- влияние на производительность и бюджеты названо (или явно «нет»);
|
||
- влияние на touch по `docs/TOUCH-SUPPORT.md` (View и киоск — блокирующие);
|
||
- release-артефакты по правилу `docs/specs/README.md` (changelog RU+EN,
|
||
документация, golden/скриншоты, performance/security);
|
||
- **откат**: как выключить или вернуть назад (флаг Labs, обратная миграция);
|
||
- открытых продуктовых вопросов нет; риски перечислены.
|
||
|
||
Если хоть один пункт не выполнен — статус не «Готово к разработке», как бы ни
|
||
хотелось начать.
|
||
|
||
### 2.6 В разработке — реализация
|
||
|
||
- **Занятие (claim):** назначить себя, поставить метку, комментарий
|
||
«Взял: <роль> · сессия <id> · ветка `issue/<NN>-<slug>`».
|
||
- **WIP-лимиты:** не более **1** issue в «В разработке» на исполнителя, не более
|
||
**3** одновременно на цикл релиза, не более **2** в «Код-ревью».
|
||
- **Трассируемость:** ветка `issue/<NN>-<slug>`; каждый коммит несёт трейлеры
|
||
`Issue: #<NN>` и `User-Visible: yes|no`.
|
||
- **Автотесты — часть реализации, а не отдельная фаза.** Каждый AC, помеченный
|
||
`unit`/`backend`/`smoke`/`golden`, получает свою проверку здесь же.
|
||
«Тестирование вне жизненного цикла» означает отсутствие фазы ручного
|
||
тестирования, а не отсутствие тестов.
|
||
- **Скоуп не расширяется.** Найденное по пути становится новым issue в «Новое».
|
||
Если находка блокирует — текущий issue уходит в «Заблокировано» со ссылкой.
|
||
Попутных правок «раз уж я здесь» не бывает.
|
||
- **Документация — в том же коммите,** что и поведение (действующая политика
|
||
`docs/STATUS.md`): changelog RU+EN для пользовательского, `STATUS.md` для
|
||
состояния, `DEVELOPMENT.md` для новых грабель, `ARCHITECTURE.md` для дизайна.
|
||
- **Выход:** локальный гейт зелёный (§8), хендофф-комментарий (§7.2).
|
||
|
||
### 2.7 Код-ревью
|
||
|
||
- **Ревьюер ≠ исполнитель**, свежая сессия без контекста реализации.
|
||
- **Артефакт:** `docs/reviews/CODE-REVIEW-<tag|NN>-r<N>.md` в действующем
|
||
формате: скоуп, как проверялось (таблица гейтов с результатами), находки
|
||
High/Medium/Low с воспроизведением, что проверено и корректно, чего не проверял.
|
||
- **Ревьюер отвечает за AC.** Раз ручного тестирования в цикле нет, именно ревью
|
||
кода отвечает на вопрос «оно вообще работает»: каждый AC либо доказан
|
||
автотестом — и ревьюер убедился, что **тест умеет падать**, — либо разобран по
|
||
коду с явной записью «проверено чтением, не исполнением».
|
||
- **High блокируют.** Medium **в скоупе задачи** чинится в текущем issue:
|
||
без High это жёлтый вердикт и возврат автору, фикс проходит повторный цикл.
|
||
Medium **вне скоупа** — отдельный issue (#202).
|
||
- **Выход:** очередь на пре-релиз либо возврат в «В разработке», не более
|
||
4 циклов (§4). Второй и последующие циклы разбираются по дельте (§2.10).
|
||
|
||
### 2.8 Закрытие после выпуска беты
|
||
|
||
- **Вход:** изменение вошло в опубликованную бету/RC, CI Validate зелёный на
|
||
**точном SHA** тега (промоушен-правило: ни одна фича не попадает в стабильный
|
||
релиз, не побывав в бете).
|
||
- **Закрывает** релиз-менеджер, не исполнитель. Комментарий закрытия: тег беты,
|
||
ссылка на прогон CI, ссылка на бюллетень changelog.
|
||
- **Стабильный релиз статусов не двигает** — issue уже закрыты; релизный коммит
|
||
promotion-only, changelog ссылается на закрытые issue.
|
||
- **Что приходит потом:** дефект, найденный на стенде, дома или пользователем, —
|
||
**новый issue** типа «баг» со ссылкой на исходный. Исходный не переоткрывается.
|
||
|
||
### 2.9 Заблокировано / Отклонено
|
||
|
||
- **Заблокировано:** обязательна ссылка на блокирующий issue или внешнюю причину
|
||
и дата пересмотра. Без причины статус не ставится.
|
||
- **Отклонено:** закрытие с записанной причиной (вне скоупа, дубликат, цена не
|
||
оправдана). Тихое закрытие без причины запрещено.
|
||
|
||
### 2.10 Повторный раунд ревью — объём по дельте
|
||
|
||
Решение владельца 2026-08-19 (issue #214). Относится и к ревью ТЗ, и к
|
||
код-ревью, начиная со второго цикла.
|
||
|
||
**Предмет повторного раунда — дельта, а не задача целиком.** Раньше объём
|
||
разбора не был оговорён, промпт ревьюера для всех раундов был одинаковым, и
|
||
повторный цикл заново выводил продуктовую рамку и перепроверял AC, которых
|
||
правка не касалась: r2 по #150 стоил полного прогона конвейера ради одной
|
||
строки в тестовой фикстуре.
|
||
|
||
Порядок:
|
||
|
||
1. найти вердикт предыдущего раунда и **SHA, на котором он получен**; SHA в
|
||
вердикте не назван — это находка;
|
||
2. объявить дельту: `git diff <тот SHA>..HEAD` для кода, дифф файла ТЗ либо тела
|
||
issue для этапа ТЗ;
|
||
3. по каждой находке предыдущего раунда показать, **чем именно она закрыта** —
|
||
строкой кода или текста, а не заявлением автора;
|
||
4. заново проверять только те AC, чьё доказательство дельта задевает;
|
||
5. **раздел «Унаследовано из r<N−1>»** обязателен: что принято без повторной
|
||
проверки, со ссылкой на документ того раунда и SHA. Без перечня сокращение
|
||
превращается в молчаливое доверие.
|
||
|
||
Дешёвые гейты (`typecheck`, `test`, `build` со сверкой копий бандла) гоняются в
|
||
каждом раунде: код изменился, а стоят они минуты. Тяжёлые — по дельте (§10.2).
|
||
|
||
**Разбор остаётся полным**, если дельта не локальна: ребейз на ушедший вперёд
|
||
`dev` (после ребейза это другой код, §7.2), смена контракта поведения, задета
|
||
новая подсистема, либо объём дельты сопоставим с исходной задачей.
|
||
|
||
Сокращается объём **разбора, а не строгость**: правка по замечанию способна
|
||
сломать AC, который предыдущий раунд признал выполненным — так появилась
|
||
регрессия #102. Граница не «только находки», а «находки плюс всё, до чего
|
||
дотягивается дельта».
|
||
|
||
---
|
||
|
||
## 3. Правила
|
||
|
||
Продолжение черновика владельца. Каждое правило проверяемо — глазами или машиной.
|
||
|
||
1. **Никаких изменений в код, если нет issue** и он не помечен «Готово к
|
||
разработке» или дальше.
|
||
2. **Issue не может быть взят в разработку**, пока у него нет ТЗ с зелёным ревью,
|
||
пронумерованных AC с указанием доказательства и назначенного исполнителя.
|
||
3. **Issue не может быть взят дважды.** Занятие фиксируется назначением, меткой и
|
||
комментарием с именем ветки. У одного исполнителя одновременно не более одного
|
||
issue в разработке.
|
||
4. **Статус меняется до действия, а не после.** Взял — поставил метку; отдал на
|
||
ревью — поставил метку. Метка, поставленная задним числом, — дефект процесса.
|
||
5. **Ровно одна метка статуса** на issue в любой момент. Ноль или две — дефект,
|
||
еженедельная гигиена его показывает.
|
||
6. **Автор не ревьюит своё** — ни ТЗ, ни код. Никто не переводит свою работу через
|
||
ревью-гейт.
|
||
7. **Ревью возвращает не более 4 раз.** Пятый заход — решение владельца: разделить,
|
||
отклонить или арбитраж (§4).
|
||
8. **High блокирует. Medium в скоупе чинится в текущем issue** (без High —
|
||
жёлтый вердикт и повторный цикл); Medium вне скоупа становится отдельным
|
||
issue (#202). Low либо правится, либо снимается решением ревьюера с записью
|
||
в документе.
|
||
9. **Скоуп не расширяется.** Всё найденное вне ТЗ — новый issue, а не попутная
|
||
правка. Блокирующая находка отправляет текущий issue в «Заблокировано».
|
||
10. **Каждый коммит класса A и B несёт трейлер `Issue: #NN`**, ветка называется
|
||
`issue/NN-slug`, а `User-Visible: yes` требует правок в **обоих** changelog в
|
||
том же коммите.
|
||
11. **Документация — в том же коммите, что поведение.** Отдельным «допишу потом»
|
||
коммитом документация не бывает.
|
||
12. **Сгенерированное не коммитится само по себе.** Только релизный промоушен или
|
||
принятие эталонов со ссылкой на прогон CI.
|
||
13. **Golden-эталоны принимаются только** `npm run golden:accept -- --reviewed` по
|
||
полному Linux-артефакту. Принятие ради зелёного CI — нарушение процесса.
|
||
14. **Issue закрывается после выпуска беты** с зелёным CI на точном SHA. Не
|
||
раньше, не «по факту наличия кода», не исполнителем.
|
||
15. **Закрытый issue не переоткрывается.** Новый дефект — новый issue со ссылкой.
|
||
16. **Стабильный релиз — promotion-only:** версии, сгенерированные бандлы,
|
||
changelog и release-метаданные. Продуктового кода там нет.
|
||
17. **История `dev` не перезаписывается.** На неё ссылаются теги. Нарушение
|
||
исправляется следующим коммитом плюс issue с меткой `process` — не
|
||
force-push'ем.
|
||
18. **AC доказывает автотест или запись ревьюера.** Фразы «проверил локально, всё
|
||
работает» в процессе не существует: либо тест, который умеет падать, либо
|
||
честное «проверено чтением, не исполнением».
|
||
19. **Параллельных бэклогов нет.** Планы, разборы и приоритеты живут в issue;
|
||
файловые отчёты — разовые и датированные.
|
||
20. **Аварийный хотфикс — только решением владельца** и только по §11.2.
|
||
|
||
---
|
||
|
||
## 4. Лимит циклов ревью: 4
|
||
|
||
Оба ревью-гейта возвращают задачу на правки не более **4 раз**.
|
||
|
||
- **Что считается циклом:** отправка на ревью → вердикт с блокирующими находками
|
||
→ возврат. Уточняющий вопрос без вердикта циклом не считается.
|
||
- **Зелёный вердикт цикла не образует** и бюджет не тратит (решение владельца
|
||
2026-08-20, issue #227): он ничего не вернул на правки. Практический случай —
|
||
зелёное ревью, слияние которого не удалось: конвейер сам предписывает ребейз и
|
||
возврат метки, и этот заход не должен наказываться. Раньше счётчик считал все
|
||
вердикты подряд, и на #225 последовательность жёлтый → зелёный → ребейз дала
|
||
`review-4` на задаче с зелёным ревью и зелёным CI.
|
||
- **Заход и цикл — разные величины.** Заход — сколько раз ревью отработало; он
|
||
виден в имени документа (`-r1`, `-r2`, …) и нужен, чтобы два документа не
|
||
затёрли друг друга. Цикл — единица бюджета §4. Заходов законно бывает больше,
|
||
чем циклов, поэтому порог проверки 7 в `scripts/process-gate.mjs` выше лимита
|
||
циклов (шесть документов = четыре цикла плюс два ребейза).
|
||
- Метка `review-4` ставится, когда исчерпан **бюджет циклов**; конвейер снимать
|
||
её не вправе — это решение владельца. Если бюджет пересчитан и оказался ниже
|
||
лимита, конвейер сообщает пересчёт, но метку не трогает.
|
||
- **Исчерпание лимита — не «пятая попытка», а разбор.** Задача уходит владельцу,
|
||
решение одно из трёх:
|
||
1. **разделить** — issue закрывается как «заменён», вместо него 2–3 меньших с
|
||
ясным скоупом (частый настоящий диагноз: ТЗ было слишком большим);
|
||
2. **отклонить** — цена решения оказалась выше ценности;
|
||
3. **арбитраж владельца** — владелец фиксирует решение в issue, оно принимается
|
||
как есть; несогласие ревьюера записывается, но не блокирует.
|
||
- **Граница между «циклом» и «новым багом»:** до закрытия беты находка ревьюера —
|
||
возврат на правки; после закрытия — новый issue. Иначе лимит 4 обходится
|
||
заведением issue вместо возврата.
|
||
- Для лёгкого трека лимит ревью ТЗ — **2** цикла: задача на три часа, которую
|
||
переписывают трижды, лёгкой не была.
|
||
|
||
---
|
||
|
||
## 5. Лёгкий трек (метка `small`)
|
||
|
||
**Критерии — все одновременно:**
|
||
|
||
- сложность и риск ≤ 3;
|
||
- одна поверхность (один диалог, один модуль, один эндпоинт);
|
||
- нет миграции конфига и новых compatibility-полей;
|
||
- нет нового UX-контракта — меняется поведение в рамках уже описанного;
|
||
- нет влияния на производительность и на touch-контракт.
|
||
|
||
**Что упрощается:**
|
||
|
||
- ТЗ пишется **в теле issue** по шаблону: проблема · контракт · AC1…ACn с
|
||
доказательством · откат. Файл в `docs/specs/` не создаётся;
|
||
- ревью ТЗ — комментарий второго агента, отдельный документ не нужен;
|
||
- лимит ревью ТЗ — 2 цикла.
|
||
|
||
**Что не упрощается:** issue, оценка, статусы, трейлеры коммитов, changelog,
|
||
**код-ревью и его документ**, закрытие после беты. Код-ревью не пропускается
|
||
никогда — именно оно в этом процессе заменяет тестирование. Единственное
|
||
исключение — починка упавшего предрелизного гейта, §11.4.
|
||
|
||
Если по ходу выясняется, что критерий нарушен (появилась миграция, задело второй
|
||
модуль) — метка `small` снимается, issue возвращается в `S3-spec` и получает
|
||
нормальный файл ТЗ. Это не провал, это ранняя диагностика.
|
||
|
||
### 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 выражаются тремя проверяемыми утверждениями или меньше;
|
||
- **ожидаемое поведение уже зафиксировано** — в `docs/USER-GUIDE.ru.md`, в
|
||
каноническом документе подсистемы либо однозначно в самом отчёте. Решать нечего.
|
||
Если есть что решать, это `S3-spec`, и никакая экономия этого не отменяет.
|
||
|
||
Метка ставится в `S2-analysis` вместе с остальными оценками, одним комментарием,
|
||
где владелец утверждает и приоритет.
|
||
|
||
**Что не упрощается:** issue, оценка, статусы, трейлеры, changelog и **код-ревью**.
|
||
Лимит циклов код-ревью — 2, как на лёгком треке.
|
||
|
||
Если по ходу выясняется, что критерий нарушен, метка снимается и issue уходит в
|
||
`S3-spec` за нормальным ТЗ. Как и на лёгком треке, это не провал, а ранняя
|
||
диагностика.
|
||
|
||
**Чем этот трек опасен.** Он убирает единственное место, где решение проверялось
|
||
до написания кода. Признак «решать нечего» держит всю конструкцию, и его нельзя
|
||
подтверждать ощущением — только ссылкой на уже зафиксированное поведение.
|
||
|
||
---
|
||
|
||
## 6. Роли
|
||
|
||
Один агент может исполнять несколько ролей в разных issue, но **не две роли в
|
||
одном артефакте**.
|
||
|
||
| Роль | Делает | Не имеет права |
|
||
|---|---|---|
|
||
| Аналитик | разбор, оценки, поверхности | окончательно ставить приоритет |
|
||
| Автор ТЗ | `docs/specs/NN-*.md` или ТЗ в issue | ревьюить своё ТЗ |
|
||
| Ревьюер ТЗ | `docs/reviews/SPEC-REVIEW-NN-rN.md` | править ТЗ вместо автора |
|
||
| Разработчик | код, автотесты, документация, changelog | ревьюить свой код, принимать golden |
|
||
| Ревьюер кода | `docs/reviews/CODE-REVIEW-*-rN.md`, проверка AC | править продуктовый код |
|
||
| Релиз-менеджер | пре-релиз, стабильный релиз, закрытие issue | добавлять код в релизный коммит |
|
||
| Владелец | приоритет, ценность, скоуп, отклонение, арбитраж, хотфикс | — |
|
||
|
||
**Правило разделения:** ревьюер работает состязательно. Ему передаётся тег или
|
||
диапазон коммитов и ТЗ — не рассказ автора о том, как всё хорошо.
|
||
|
||
**Роли закреплены за исполнителями** (решение владельца 2026-08-12):
|
||
|
||
| Исполнитель | Роли |
|
||
|---|---|
|
||
| **Codex** | аналитик, автор ТЗ, разработчик, релиз-инженер по команде владельца |
|
||
| **Claude** | ревьюер ТЗ, ревьюер кода, вся инфраструктура и дистрибуция |
|
||
| **Владелец** | приоритет, скоуп, арбитраж, закрытие issue, команда на выпуск |
|
||
|
||
Автор и ревьюер — **разные модели**, и это сильнее требования «другая сессия»:
|
||
одна модель, читая свой же артефакт заново, повторяет свои же слепые пятна.
|
||
|
||
Ревью ТЗ и код-ревью держатся в **разных сессиях** Claude: ревьюер кода не должен
|
||
приходить с контекстом того, как обсуждали ТЗ.
|
||
|
||
---
|
||
|
||
## 7. Артефакты и трассируемость
|
||
|
||
### 7.1 Цепочка
|
||
|
||
```
|
||
issue #NN
|
||
↔ ТЗ docs/specs/NN-slug.md (или тело issue при `small`)
|
||
↔ ревью ТЗ docs/reviews/SPEC-REVIEW-NN-rN.md (или комментарий при `small`)
|
||
↔ ветка issue/NN-slug
|
||
↔ коммиты трейлеры Issue: #NN · User-Visible: yes|no
|
||
↔ ревью кода docs/reviews/CODE-REVIEW-<tag|NN>-rN.md
|
||
↔ changelog бюллетень RU+EN со ссылкой на #NN
|
||
↔ бета тег, зелёный CI на точном SHA → закрытие
|
||
```
|
||
|
||
Обязательные разделы ТЗ: **сценарий** · **что человек увидит до и после** ·
|
||
проблема · скоуп и **не-скоуп** · контракт поведения · UX · модель данных и
|
||
миграция · i18n · критерии приёмки AC1…ACn с указанием доказательства · план
|
||
автотестов · риски · откат · release-артефакты.
|
||
|
||
Два первых раздела — продуктовые, и они идут первыми не случайно. **Сценарий:**
|
||
какая персона (`docs/SCOPE.md`), на какой поверхности, в какой момент это
|
||
встретит. **Что человек увидит:** одной фразой, без терминов реализации. ТЗ,
|
||
которое не может ответить на эти два вопроса, описывает работу, а не изменение
|
||
продукта.
|
||
|
||
**Размытое место не додумывается, а выносится владельцу.** Догадка, записанная
|
||
как факт, — худший вид дефекта: она проходит ревью, потому что выглядит решением.
|
||
|
||
Но спрашивать обо всём нельзя: владелец один, и анкета из двадцати пунктов хуже
|
||
угадывания. Порог такой (решение владельца 2026-08-13).
|
||
|
||
**Владельцу задаются только продуктовые вопросы** — что человек видит или делает
|
||
и какой объём видимых изменений входит в этот issue. Поведение в пограничном
|
||
случае; какая из персон важнее в конфликте; что считать приемлемой деградацией;
|
||
относится ли смежное поведение сюда или становится отдельной задачей.
|
||
|
||
**Всё, чего пользователь не наблюдает, агенты решают сами** либо согласовывают
|
||
между собой: где хранится состояние, в каком модуле стоит гвард, именование,
|
||
раскладка файлов, стратегия тестов, механика миграции. Решение записывается явным
|
||
блоком в конце ТЗ — «принято предположительно, поменять свободно», и ревьюер
|
||
вправе его оспорить. Технический спор автора и ревьюера решается вердиктом, а не
|
||
владельцем; до него он доходит только при исчерпании лимита циклов (§4).
|
||
|
||
**Смешанный вопрос делится, а не эскалируется целиком.** «Где живёт это
|
||
состояние» — техническое. «Переживает ли оно перезагрузку страницы и общее ли оно
|
||
для всех экранов» — продуктовое.
|
||
|
||
Вопросы задаются **одним комментарием, пачкой**, каждый в форме: что неясно ·
|
||
что изменится от ответа · **предлагаемый вариант по умолчанию**. Вопрос с готовым
|
||
вариантом стоит владельцу пяти секунд, вопрос без него — пяти минут. Пока ждём
|
||
ответа, issue остаётся в `S3-spec` и получает `blocked`: статус не подменяется,
|
||
`blocked` его дополняет, иначе конвейер считает задачу в работе, а она стоит.
|
||
|
||
### 7.2 Шаблоны комментариев
|
||
|
||
Короткие и однообразные, чтобы читались и человеком, и машиной.
|
||
|
||
- **Аналитика:** `Оценка: ценность N/10 · сложность N/10 · P<1-3> · тип ·
|
||
поверхности: … · дубликаты: … · лёгкий трек: да/нет`
|
||
- **Занятие:** `Взял: <роль> · сессия <id> · ветка issue/NN-slug`
|
||
- **Хендофф:** `Сделано: … · Файлы: … · Гейты: <команда → результат> ·
|
||
НЕ сделано: … · Риски: … · Следующий статус: … · Новые issue: #…`
|
||
- **Вердикт ревью:** `Вердикт: зелёный/жёлтый/красный · заход r<N> ·
|
||
блокирующих циклов K/<лимит> · High: N · Medium: N → в задаче | #… ·
|
||
Документ: docs/reviews/…`
|
||
(«→ #…» — только у Medium вне скоупа; находки в скоупе возвращаются автору.
|
||
Заход — номер прогона ревью, K — израсходованный бюджет §4: зелёные вердикты
|
||
его не тратят, поэтому заход и K расходятся, #227)
|
||
- **Закрытие:** `Выпущено в <тег беты> · CI: <ссылка> · Changelog: <ссылка>`
|
||
|
||
**Вперёд двигает только зелёный вердикт.** Жёлтый и красный возвращают автору;
|
||
разница между ними содержательна для человека, но не для маршрута. Первая
|
||
редакция конвейера (§10.4) пропускала жёлтый при `High: 0`, и первый же живой
|
||
прогон показал, почему это неверно: жёлтый там означал, что AC описывает неверное
|
||
изменение контракта — реализовать такое ТЗ значило бы сделать ошибку по инструкции.
|
||
|
||
### 7.3 Расхождения с текущим состоянием, которые надо закрыть
|
||
|
||
1. **Статус ТЗ дублирует статус issue.** `docs/specs/README.md` держит колонку
|
||
«Статус ТЗ» со своим словарём («черновик решения», «в реализации»,
|
||
«реализовано»). Два источника статуса уже расходятся. Колонку убрать, оставить
|
||
таблицу «issue ↔ ТЗ».
|
||
2. **Ревью до релиза 1.62 живут вне репозитория.** Документы `CODE-REVIEW-*.md` и
|
||
`SPEC-REVIEW-*.md` за прежний период лежат в папке владельца, и переносить их
|
||
задним числом смысла нет: они описывают код, которого уже нет. Новые документы
|
||
ревью кладёт в `docs/reviews/` сам конвейер, в ветку задачи.
|
||
|
||
---
|
||
|
||
## 8. Гейты
|
||
|
||
**Локальный гейт перед выходом из «В разработке»** — минимальный набор,
|
||
покрывающий изменённые поверхности (действующее правило владельца):
|
||
|
||
```
|
||
npx tsc --noEmit
|
||
npm test
|
||
npm run build && cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js \
|
||
&& cmp dist/houseplan-card.js demo/srv/assets/houseplan-card.js
|
||
node demo/smoke_<целевые>.mjs
|
||
npm run golden:verify # если менялся визуал
|
||
python -m pytest tests_backend -q # py3.13, если менялся бэкенд
|
||
```
|
||
|
||
**Объём гейтов на код-ревью соразмерен задаче** (issue #127). Всегда:
|
||
`typecheck`, `npm test`, `npm run build` со сверкой трёх копий бандла. По
|
||
необходимости, определяемой diff'ом и AC: браузерные смоки (их 127 — прогон всех
|
||
уместен только когда задача задевает всё), `golden:verify` при изменении видимого
|
||
результата, `pytest tests_backend` при правках в Python, performance-профили при
|
||
названном в AC влиянии. **Полные наборы — предрелизный гейт, а не гейт ревью.**
|
||
|
||
Условие честности такого сужения: ревьюер обязан перечислить, какие гейты прогнал,
|
||
какие нет и почему. Непрогнанный гейт становится видимым решением, а не молчаливым
|
||
пропуском.
|
||
|
||
**Гейт беты** (условие закрытия issue): CI Validate зелёный на точном SHA тега.
|
||
|
||
Часть гейтов запускается только здесь, то есть **после** пройденного код-ревью.
|
||
Упавший предрелизный гейт автор чинит и повторно прогоняет; зелёный прогон
|
||
достаточен для продолжения релиза, повторное код-ревью не требуется — §11.4.
|
||
|
||
**Гейт стабильного релиза:** полный локальный прогон плюс Validate и Full
|
||
Performance зелёные на точном SHA; статусов issue не касается.
|
||
|
||
---
|
||
|
||
## 9. Метки — канонический статус
|
||
|
||
Статус читается из меток: их видно в списке issue, их читает любой токен с
|
||
доступом к Issues, и по ним же работает конвейер — смена метки порождает событие
|
||
(§10.4). **Project v2 не используется** (решение владельца 2026-08-14): второе
|
||
представление статуса рядом с метками требовало отдельного скоупа токена,
|
||
синхронизации и внимания, а давало вид доски. Два источника одного факта
|
||
расходятся — это уже случалось с колонкой «Статус ТЗ» в `docs/specs/README.md`.
|
||
|
||
**Имена меток английские** (решение владельца 2026-08-12). Русские имена в этом
|
||
документе были только на бумаге; репозиторий с самого начала жил на английских.
|
||
|
||
| Метка | Статус |
|
||
|---|---|
|
||
| `S1-new` | Новое, не разобрано |
|
||
| `S2-analysis` | Аналитика и оценка |
|
||
| `S3-spec` | ТЗ в работе |
|
||
| `S4-spec-review` | ТЗ на ревью |
|
||
| `S5-ready` | Готово к разработке — единственный статус, из которого можно начать трогать код |
|
||
| `S6-in-progress` | В разработке, занято исполнителем |
|
||
| `S7-code-review` | Код-ревью |
|
||
| `S8-merged` | Ревью пройдено, код в `dev`, ждёт беты. Issue закрывается пачкой при выпуске |
|
||
| `blocked` | Ждём внешнего или владельца, **поверх** статусной метки |
|
||
| `rejected` | Отклонено, issue закрыт |
|
||
|
||
Модификаторы: `small` (лёгкий трек, сложность ≤3), `trivial` (короткий трек,
|
||
§5.1), `hotfix`, `process`, `review-4`; приоритет `P1`/`P2`/`P3`; тип `bug`/`feature`/`tech-debt`.
|
||
Тематические метки (`polish`, `infra`, `tests`, `docs`, `security`, `vacuum`)
|
||
ортогональны процессу.
|
||
|
||
Инварианты: **ровно одна `S*`-метка** на открытом issue; закрытый issue статусных
|
||
меток не несёт; `blocked` не заменяет статус, а дополняет его.
|
||
|
||
**Чужой issue берётся в работу так же, как свой — после явного решения
|
||
владельца** (решение владельца 2026-08-13, уточнено в тот же день). Репозиторий
|
||
публичный, отчёты заводят и посторонние; проверка стоит **на входе**, а не на
|
||
каждом шаге.
|
||
|
||
Входом служит присвоение первой статусной метки: пока меток нет, issue вне
|
||
процесса и инварианты на него не распространяются. Как только метка стоит, задача
|
||
в работе, и **кто её завёл, дальше не имеет значения** — статусы, ревью и лимиты
|
||
работают одинаково.
|
||
|
||
Присвоение метки и есть то самое явное решение, причём проверенное платформой:
|
||
метки может ставить только тот, у кого есть право записи в репозиторий. Прежняя
|
||
редакция требовала переоформлять чужой отчёт своим issue со ссылкой на исходный;
|
||
это оказалось работой впустую — на #123 к моменту отказа ТЗ уже было написано.
|
||
|
||
`S8-merged` появился позже остальных и закрывает разрыв, который раньше
|
||
закрывался памятью человека: код принят, но бета ещё не вышла, и issue закрывать
|
||
рано. Без него принятая задача либо висела в `S7-code-review`, либо закрывалась
|
||
досрочно.
|
||
|
||
---
|
||
|
||
## 10. Механизация при прямых коммитах в `dev`
|
||
|
||
Решение владельца — работать без PR. Значит, GitHub не может ничего заблокировать
|
||
на своей стороне: **основной гейт переезжает на клиента, CI остаётся страховкой.**
|
||
|
||
### 10.1 Хуки, которые невозможно забыть поставить
|
||
|
||
`.githooks/` в репозитории, `core.hooksPath` выставляется автоматически при
|
||
установке зависимостей:
|
||
|
||
```json
|
||
"scripts": { "prepare": "node scripts/install-hooks.mjs" }
|
||
```
|
||
|
||
`npm ci` вызывает `prepare` сам — значит, хуки появляются в каждом окружении,
|
||
включая свежий контейнер облачного агента, без отдельного шага в инструкции.
|
||
|
||
- **`commit-msg`** — есть, работает. Отклоняет коммит без терминального
|
||
`Issue: #NN`, требует ровно один `User-Visible: yes|no`, а для коммитов,
|
||
трогающих `demo/golden/baselines/**`, — `Release:` плюс `Baseline-Reviewed:`.
|
||
Реализация — `scripts/validate-commit-provenance.mjs`, тот же скрипт вызывается
|
||
job `provenance` в `validate.yml`.
|
||
- **`pre-push`** — есть, работает. Прогоняет `scripts/process-gate.mjs` по каждому
|
||
пушимому ref и останавливает push при нарушении. Это и есть блокирующий гейт
|
||
вместо PR. Удаление ветки и теги пропускаются: в первом случае проверять нечего,
|
||
во втором коммит уже проверен, когда его пушили. Для новой ветки диапазон
|
||
считается от `merge-base` с `origin/dev`, а не от начала истории — иначе в него
|
||
попали бы все нарушения, совершённые до появления гейта.
|
||
|
||
При возврате `main` в `dev` диапазон merge-коммита содержит второй родитель —
|
||
уже опубликованные в `main` коммиты с закрытыми issue. Для destination `dev`
|
||
общий скрипт pre-push/CI исключает только SHA, доказанно достижимые из
|
||
`origin/main`; сам merge и новые post-merge коммиты остаются под всеми
|
||
проверками. На `main`, beta/issue-ветки и обычный push в `dev` это исключение
|
||
не распространяется (issue #155).
|
||
|
||
Проверка статуса issue требует `gh`, поэтому при его отсутствии хук печатает
|
||
предупреждение и выполняет только офлайн-часть. Это сознательная уступка: хук,
|
||
который не работает в самолёте, отключают целиком, а строгий проход всё равно
|
||
делает CI.
|
||
|
||
**Хук обязан быть исполняемым, и это тише всего ломается.** Git **молча** не
|
||
запускает файл без бита `+x`: гейт сообщает об успехе тем, что его нет. Проверено
|
||
на настоящем push — при `644` от гейта ноль строк и push проходит, при `755` он
|
||
останавливается.
|
||
|
||
Через GitHub API режим не выставляется: файл, отправленный так, приезжает
|
||
`100644`. Поэтому `scripts/install-hooks.mjs` восстанавливает бит при каждой
|
||
установке зависимостей, а `assertHookMode` дополнительно проверяет бит
|
||
`.githooks/commit-msg` в индексе. Правится вручную:
|
||
`git update-index --chmod=+x .githooks/<хук>`.
|
||
|
||
### 10.2 Что проверяет `process-gate.mjs`
|
||
|
||
Реализовано, `scripts/process-gate.mjs`, issue #105. Офлайн, без GitHub API:
|
||
|
||
1. трейлер `Issue: #NN` у каждого коммита класса A/B, допускается несколько;
|
||
2. имя ветки `issue/NN-slug` соответствует трейлерам;
|
||
3. для класса A существует `docs/specs/NN-*.md` — **или** issue помечен `small`.
|
||
Офлайн это предупреждение: лёгкий трек держит ТЗ в теле issue, и без чтения
|
||
меток «ТЗ в issue» неотличимо от «ТЗ не написано». С `--issues` — отказ;
|
||
4. `User-Visible: yes` → правки в обоих changelog в том же коммите;
|
||
5. коммит только класса D невалиден без `Release: vX.Y.Z` либо
|
||
`Baseline-Reviewed: <ссылка на прогон CI>`;
|
||
6. релизный коммит не содержит изменений в `src/` и `custom_components/**/*.py`;
|
||
7. документов ревью на один issue не больше четырёх (`-r1`…`-r4`).
|
||
|
||
С токеном GitHub:
|
||
|
||
8. `--issues` тянет каждый упомянутый issue и требует метку из
|
||
{`S5-ready`, `S6-in-progress`, `S7-code-review`, `S8-merged`}; закрытый,
|
||
недоступный или помеченный `blocked` — отказ (**fail closed**).
|
||
|
||
Три оговорки к проверке 8 выяснились при реализации.
|
||
|
||
**`S8-merged` входит в множество**, хотя по смыслу задача уже принята. Причина
|
||
механическая: конвейер (§10.4) сливает ветку в `dev` **раньше**, чем ставит метку,
|
||
Validate стартует от этого push и успевает прочитать issue уже в `S8-merged`.
|
||
Строгое множество красило бы каждую принятую задачу. Локальная строгость
|
||
возвращается флагом `--no-merged`.
|
||
|
||
**Статус спрашивается только у коммитов класса A/B.** Правило №1 говорит о
|
||
продуктовом коде и инструментах, а не о документации. Иначе краснел бы каждый
|
||
документ ревью: он ложится в ветку задачи, пока та в `S4-spec-review` или
|
||
`S7-code-review`, то есть заведомо вне рабочего множества.
|
||
|
||
**При продвижении в `main` не перепроверяются коммиты, уже достижимые из
|
||
prerelease-тега.** После выпуска беты их issue по §2.8 должны быть закрыты, а
|
||
stable fast-forward снова включает эти коммиты в диапазон `old-main..candidate`.
|
||
Pre-push передаёт целевую remote ref через `--target-ref`, а Validate — через
|
||
`TARGET_REF`; оба исключают только уже опубликованную prerelease-историю. Любой
|
||
post-beta коммит остаётся в проверке и по закрытому issue отклоняется fail-closed.
|
||
|
||
Не реализовано и остаётся долгом:
|
||
|
||
9. `npm run release:prerelease -- --issues=…` не проверяет, есть ли у issue
|
||
зелёный вердикт код-ревью;
|
||
10. закрытие issue и снятие статусных меток при публикации беты делаются руками —
|
||
`node process-labels/apply.mjs cleanup --apply`, а не `publish-prerelease.yml`.
|
||
Пропуск этого шага уже ломал инвариант «закрытый issue без статусной метки».
|
||
|
||
### 10.3 Страховка и разбор
|
||
|
||
- **`process-gate.mjs` — job `process-gate` в `validate.yml`**, без `needs`:
|
||
краснеет сам и не роняет остальные. При прямом push проверка догоняющая: код уже
|
||
в `dev`, CI краснеет после. Это принятая цена отказа от PR: `pre-push` ловит
|
||
нарушение до отправки, а этот job — то, что прошло мимо хука, включая
|
||
`--no-verify` и окружение без установленных зависимостей.
|
||
- **Нарушение не откатывается force-push'ем** (правило 17): исправляющий коммит
|
||
плюс issue с меткой `process`. Починить надо проверку, а не только симптом.
|
||
- **Еженедельная гигиена** (workflow): issue в `S1-new` дольше 14 дней и в
|
||
`S6-in-progress` дольше 7; issue класса A в `S5-ready` без ТЗ; issue с нулём или
|
||
двумя `S*`-метками; коммиты без трейлера за неделю — **цель 0**; rework rate и
|
||
число issue, дошедших до `review-4`; **баги, заведённые после закрытия беты** —
|
||
прямая цена отказа от фазы тестирования.
|
||
|
||
### 10.4 Событийный конвейер: метка как триггер
|
||
|
||
`.github/workflows/process.yml`, issue #114. Смена статусной метки — не запись в
|
||
журнал, а **сообщение**: она порождает событие, событие запускает следующий шаг.
|
||
|
||
```
|
||
S4-spec-review → ревью ТЗ → S5-ready либо возврат в S3-spec
|
||
S7-code-review → код-ревью → слияние в dev → S8-merged либо возврат в S6-in-progress
|
||
```
|
||
|
||
Ревьюер — `anthropics/claude-code-action`. Он читает `docs/SCOPE.md`, `AGENTS.md`,
|
||
этот документ и тело issue, публикует разбор комментарием, заводит issue на Medium-находки
|
||
вне скоупа задачи (#202), кладёт документ в `docs/reviews/` ветки задачи и возвращает вердикт
|
||
структурированным JSON. **Метку переставляет отдельный детерминированный шаг по
|
||
вердикту, а не модель.**
|
||
|
||
Четыре вещи, без которых конвейер молча не работает:
|
||
|
||
1. метки переставляет **PAT**, а не `GITHUB_TOKEN`: GitHub намеренно не порождает
|
||
события от `GITHUB_TOKEN`, чтобы не было циклов, и цепочка обрывалась бы после
|
||
первого шага без ошибок в логах;
|
||
2. `process.yml` обязан лежать в **ветке по умолчанию**: для события `issues`
|
||
GitHub берёт workflow только оттуда, независимо от содержимого `dev`;
|
||
3. слияние в `dev` происходит **до** простановки `S8-merged`, иначе метка врёт в
|
||
промежутке — она утверждает, что код в `dev`;
|
||
4. многострочный текст внутри `run:` — только через heredoc: строка с нулевым
|
||
отступом обрывает блок YAML, и скрипт обрезается без ошибки парсера.
|
||
|
||
**Автор обязан дождаться вердикта, а не заканчивать сессию.** Ревью идёт от десяти
|
||
минут до сорока пяти. Отчёт «передал на ревью» останавливает конвейер там, где он
|
||
мог идти сам: вердикт придёт, а подхватить его будет некому. У агента нет часов —
|
||
он существует только в момент своего хода, поэтому ожидание это опрос: раз в 90
|
||
секунд, не более 30 попыток. Смотреть на метку, а не на комментарий: метка и есть
|
||
состояние. При `blocked` не ждать — задача ждёт владельца.
|
||
|
||
**После прогона ревью метка меняется всегда.** Инвариант появился не сразу: первая
|
||
редакция при конфликте слияния оставляла метку на месте, и это оказалось тупиком —
|
||
автор ждёт смену метки, метка не менялась, и он тридцать раз опрашивал впустую,
|
||
чтобы отчитаться «лимит исчерпан» при зелёном вердикте. Состояние, из которого
|
||
никто не может выйти и о котором никто не узнает, для конвейера хуже громкой
|
||
ошибки.
|
||
|
||
Поэтому зелёное код-ревью с неудавшимся слиянием ведёт не в `S8-merged`, а в
|
||
`S6-in-progress`: работа действительно вернулась к автору, только осталась не
|
||
правка кода, а ребейз. Вердикт при этом в силе, переделывать нечего. После ребейза
|
||
метка `S7-code-review` возвращается и ревью идёт заново — не формальность:
|
||
после ребейза на ушедший вперёд `dev` это другой код.
|
||
|
||
Если метка не сменилась, значит упал сам прогон, а не работа: смотреть логи и
|
||
сообщать владельцу, а не продолжать опрос.
|
||
|
||
Цикл считается **по этапу**: вердикт по ТЗ не расходует бюджет код-ревью. Раньше
|
||
считались все вердикты подряд, и первое код-ревью #89 получило `r2/4`.
|
||
|
||
---
|
||
|
||
## 11. Исключения
|
||
|
||
### 11.1 Лёгкий трек
|
||
|
||
См. §5 — это не исключение из правила №1, а более дешёвый путь по тем же статусам.
|
||
|
||
### 11.2 Аварийный хотфикс (метка `hotfix`, решение владельца)
|
||
|
||
Разрешено писать код до появления issue. Обязательно:
|
||
|
||
- issue создан в **той же сессии до коммита**, метка `hotfix`;
|
||
- ТЗ «как сделано» + раздел «почему нельзя было ждать»;
|
||
- в течение 24 часов задача ретроспективно проходит код-ревью;
|
||
- аварийность названа явно в релизном хендоффе (действующее правило `AGENTS.md`).
|
||
|
||
### 11.3 Гигиена репозитория
|
||
|
||
Механические изменения без изменения поведения (форматирование, мёртвые файлы)
|
||
идут под квартальный umbrella-issue «Гигиена репозитория»; каждый коммит
|
||
ссылается на него. Трассируемость 1:1 сохраняется.
|
||
|
||
### 11.4 Починка предрелизных гейтов без повторного код-ревью
|
||
|
||
Решение владельца 2026-08-13.
|
||
|
||
В цикле реализации гоняется только лёгкий набор — typecheck, unit, build (§8).
|
||
Golden, браузерные смоки, performance и полный HA-харнесс запускаются перед бетой,
|
||
то есть **после** того, как код-ревью пройдено и issue в `S8-merged`. Часть
|
||
проблем физически не может быть найдена раньше.
|
||
|
||
**Если предрелизный гейт упал, автор правит, повторно прогоняет упавшее, и
|
||
зелёного прогона достаточно, чтобы релиз продолжился.** Issue остаётся в
|
||
`S8-merged` и на повторное код-ревью не отправляется.
|
||
|
||
Причина: полный цикл ревью в момент выпуска стоит дороже, чем риск, который он
|
||
здесь снимает. Гейт уже назвал дефект точно, а исправление проверяется тем же
|
||
гейтом — то есть проверка объективна и не зависит от чьего-либо суждения.
|
||
|
||
**Что при этом обязательно:**
|
||
|
||
- прогон упавшего гейта записан в issue: **точная команда и её результат**.
|
||
«Verified» без команды доказательством не является (§8);
|
||
- трейлеры на коммите как обычно, `Issue: #NN` того же issue;
|
||
- при `User-Visible: yes` — правки в оба changelog в том же коммите;
|
||
- эталоны golden принимаются только через `npm run golden:accept -- --reviewed`
|
||
на полном артефакте Linux CI. «Чтобы гейт позеленел» основанием не является.
|
||
|
||
**Границы, за которыми исключение не действует.** Оно про починку названного
|
||
гейтом дефекта, а не про продолжение разработки под видом починки. Правка идёт
|
||
обычным путём — новым issue либо возвратом в `S6-in-progress` — если она:
|
||
|
||
- меняет контракт поведения или добавляет пользователю что-то новое;
|
||
- задевает подсистему, которой в исходной задаче не было;
|
||
- по объёму сопоставима с самой задачей;
|
||
- меняет сам гейт вместо кода — правка теста, чтобы он перестал падать, это не
|
||
починка, а сокрытие. Исключение — когда дефект **в фикстуре** и это доказано
|
||
разбором, как на #89: солнце на азимуте 180° и единственное окно на северной
|
||
стене, поэтому луч честно не строился.
|
||
|
||
Границу определяет автор, и здесь процесс сознательно отдаёт ему то, что в
|
||
остальных местах не доверяет — оценку собственной работы. Плата за скорость в
|
||
единственной точке, где цикл ревью стоит дороже всего. Компенсируется тем, что
|
||
запись в issue публична и релиз-менеджер видит, что именно было сделано перед
|
||
выпуском.
|
||
|
||
Это исключение из правила «код-ревью не пропускается никогда» (§5, §7.1) —
|
||
единственное, и относится только к окну между `S8-merged` и выпуском.
|
||
|
||
---
|
||
|
||
## 12. Запрещено
|
||
|
||
- код без issue или из статуса раньше «Готово к разработке»;
|
||
- ТЗ, написанное после кода (кроме §11.2, и тогда с пометкой «как сделано»);
|
||
- ревью своей работы; перевод своей работы через ревью-гейт;
|
||
- пятый цикл ревью вместо разбора по §4;
|
||
- заведение issue вместо возврата на правки, чтобы обойти лимит циклов;
|
||
- принятие golden-эталонов ради зелёного CI или по частичному артефакту;
|
||
- закрытие issue до выпуска беты с зелёным CI;
|
||
- переоткрытие закрытого issue вместо нового бага;
|
||
- Medium-находки, оставленные как TODO в документе ревью: в скоупе — чинятся
|
||
в текущем issue, вне скоупа — становятся отдельным (#202);
|
||
- **параллельные бэклоги** в файлах (`BACKLOG-*.md`, «планы» в docs);
|
||
- ревью-документы вне репозитория;
|
||
- попутные правки «раз уж я здесь»;
|
||
- фича или материальное изменение поведения в стабильном релиз-коммите;
|
||
- force-push в `dev`;
|
||
- ручное копирование на домашний инстанс.
|
||
|
||
**Нарушение процесса — тоже issue** (метка `process`): если правило удалось
|
||
нарушить незаметно, виновата проверка.
|
||
|
||
---
|
||
|
||
## 13. Внедрение
|
||
|
||
Состояние на 2026-08-13.
|
||
|
||
1. ✅ **Метки созданы, бэклог размечен.** У всех открытых issue владельца ровно
|
||
одна `S*`-метка, инварианты чистые.
|
||
2. ⏳ **Колонку «Статус ТЗ» из `docs/specs/README.md` убрать** — не сделано, §7.3
|
||
п.1. Перенос старых документов ревью в `docs/reviews/` отменён: они описывают
|
||
код, которого уже нет.
|
||
3. ✅ **Гейт написан** — `scripts/process-gate.mjs` плюс job в `validate.yml`,
|
||
issue #105. Прошёл **вне** флоу как инфраструктурная задача (§1, issue #118), а
|
||
не через ТЗ и ревью, как предполагала прежняя редакция этого пункта.
|
||
4. ✅ **Долг ревью списан решением владельца.** Беты `beta.2`…`beta.10` сделаны по
|
||
прежнему процессу и не пересматриваются. Точка отсчёта — релиз 1.62.0; отсчёт
|
||
начинается с первой беты следующей линии.
|
||
5. ⏳ Завести issue на находку «смок `visual_continuity` не умеет падать» — это
|
||
ровно тот класс дефектов, который в процессе без ручного тестирования стоит
|
||
дороже всего.
|
||
6. ✅ `BACKLOG-2026-08-11.md` — разовый отчёт, решения живут в issue.
|
||
7. ✅ `AGENTS.md` переписан целиком, шире блока §14.
|
||
8. ✅ **Канон перенесён в репозиторий** (issue #112). До этого полный процесс жил
|
||
только в папке владельца, а в репозитории лежал файл на 51 строку про трейлеры
|
||
коммитов — из свежего клона канон не был виден вообще.
|
||
9. ✅ **`pre-push` написан** (§10.1, issue #121). Блокирующая проверка на клиенте
|
||
есть; обойти её можно только `--no-verify`, и тогда то же найдёт CI.
|
||
|
||
---
|
||
|
||
## 14. Блок для AGENTS.md
|
||
|
||
```markdown
|
||
## Процесс: код только через issue
|
||
|
||
Изменение продуктового кода без issue запрещено. Код меняется только из статуса
|
||
«Готово к разработке» или дальше. Полные правила, критерии статусов и гейты —
|
||
`docs/PROCESS.md`, читать до начала работы.
|
||
|
||
Жизненный цикл (статус = метка issue): `S1-new` → `S2-analysis` → `S3-spec` →
|
||
`S4-spec-review` → `S5-ready` → `S6-in-progress` → `S7-code-review` → `S8-merged`
|
||
→ закрытие пачкой при выпуске беты. Оба ревью возвращают на правки не более 4
|
||
циклов; пятый заход — разбор у владельца (разделить / отклонить / арбитраж).
|
||
|
||
Ревью запускается **само** от меток `S4-spec-review` и `S7-code-review` и идёт до
|
||
45 минут. Поставив такую метку, автор не заканчивает работу, а ждёт смены метки
|
||
опросом и продолжает по тому, чем она стала.
|
||
|
||
- ветка `issue/<NN>-<slug>`, коммиты с трейлерами `Issue: #NN` и `User-Visible: yes|no`;
|
||
- работаем прямыми коммитами в `dev`, без PR: блокирующий гейт — локальный
|
||
`pre-push` (ставится автоматически через `npm ci`), CI — страховка. Force-push
|
||
в `dev` запрещён;
|
||
- автор ≠ ревьюер, ни для ТЗ, ни для кода;
|
||
- фазы ручного тестирования нет: автотесты пишутся в реализации, AC проверяет
|
||
код-ревью, найденные позже дефекты — новые issue типа «баг»;
|
||
- мелкие задачи (метка `small`, сложность ≤3): ТЗ в теле issue, ревью ТЗ
|
||
комментарием, код-ревью — как обычно;
|
||
- найденное вне скоупа — новый issue, а не попутная правка;
|
||
- issue закрывает релиз-менеджер после выпуска беты, не исполнитель.
|
||
```
|