mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 21:28:59 +00:00
docs(process): канон без противоречий, вход автора короче (#701)
Сверка PROCESS.md, ролевых выжимок, AGENTS.md, TESTING.md, CONTRIBUTING.md и скриптов по 26 найденным расхождениям (D1–D26): трейлеры по классам изменений, gate:small как единственный источник состава, пороги ревью, путь реестра мутантов, golden по ci:golden, порядок чтения промпта ревью. - scripts/change-classes.mjs: классы A/B/C/D — один модуль для process-gate и проверки трейлеров. - commit-msg: коммит только с файлами класса C (документация) трейлеров не требует; указанные трейлеры по-прежнему проверяются. - Маршрут автора без docs/STATUS.md: 5345 → 4703 слова. - Промпт ревью читает SCOPE → AGENTS → REVIEWER, как ROUTES.reviewer. Issue: #701 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
@@ -1152,15 +1152,15 @@ jobs:
|
||||
ограничитель: «features are built, improved and accepted only
|
||||
if they serve a job listed here». Первый вопрос к задаче —
|
||||
какую строку Core user jobs она закрывает.
|
||||
2. docs/process/REVIEWER.md — обязанности ревьюера: позиция,
|
||||
2. AGENTS.md — карта пакета, правило №1, классы изменений, треки,
|
||||
трейлеры и ожидание вердикта; сами правила — по его ссылкам.
|
||||
3. docs/process/REVIEWER.md — обязанности ревьюера: позиция,
|
||||
ревью ТЗ, код-ревью, объём гейтов, повторный раунд, находки и
|
||||
вердикт. Раздел PROCESS.md по ссылке открывай, когда пункт
|
||||
касается твоего решения; при расхождении прав PROCESS.md. Если
|
||||
файла в материале нет — читай PROCESS.md §2.4, §2.7, §2.10,
|
||||
§4, §7.2, §8, §12. Задача правит сам конвейер, гейты или
|
||||
процесс — PROCESS.md целиком, §10 в первую очередь.
|
||||
3. AGENTS.md — карта пакета, правило №1, классы изменений, треки,
|
||||
трейлеры и ожидание вердикта; сами правила — по его ссылкам.
|
||||
4. Тело issue #${{ github.event.issue.number }} и все комментарии.
|
||||
5. Если меняется видимое поведение — docs/USER-GUIDE.ru.md:
|
||||
терминология интерфейса берётся оттуда, а не изобретается.
|
||||
|
||||
@@ -29,7 +29,9 @@ leak interactions into View. For work that changes visible behaviour, also read
|
||||
route and `test/entry-cost.test.mjs` keeps this list equal to its routes:
|
||||
|
||||
- author (analysis, spec, implementation, infrastructure): `docs/SCOPE.md` →
|
||||
`AGENTS.md` → `docs/process/AUTHOR.md` → `docs/STATUS.md`;
|
||||
`AGENTS.md` → `docs/process/AUTHOR.md`, then the task packet
|
||||
(`node scripts/task-packet.mjs --issue NN`); the status snapshot
|
||||
(docs/STATUS.md) only when resuming a session or preparing a release;
|
||||
- reviewer (spec or code): `docs/SCOPE.md` → `AGENTS.md` →
|
||||
`docs/process/REVIEWER.md`, then the issue body and its comments;
|
||||
- changing the pipeline, the gates or the process itself: `docs/SCOPE.md` →
|
||||
@@ -100,8 +102,9 @@ batched comment with a proposed default for each question and `blocked` on top o
|
||||
## Commits and branches
|
||||
|
||||
Hooks install themselves on `npm ci` (`prepare` → `scripts/install-hooks.mjs`);
|
||||
`git config core.hooksPath` must print `.githooks`. Every non-merge commit
|
||||
carries **terminal** trailers:
|
||||
`git config core.hooksPath` must print `.githooks`. Every non-merge commit that
|
||||
touches anything outside class C (docs) carries **terminal** trailers; a
|
||||
docs-only commit needs none (`PROCESS.md` §3 п.10, #701):
|
||||
|
||||
```text
|
||||
Issue: #123
|
||||
|
||||
+5
-4
@@ -94,12 +94,11 @@ issue, but they do not replace it or maintain a separate checklist.
|
||||
|
||||
```bash
|
||||
git clone --filter=blob:none https://github.com/Matysh/houseplan-card && cd houseplan-card
|
||||
npm ci # frontend toolchain
|
||||
npm ci # frontend toolchain; `prepare` installs .githooks
|
||||
npm run typecheck # tsc --noEmit (strict)
|
||||
npm test # node:test — pure logic, i18n parity, tap-action security
|
||||
npm run build # tsc + rollup → dist/houseplan-card.js
|
||||
pip install pytest voluptuous && python -m pytest tests_backend -q # pure backend tests
|
||||
npm install # also installs .githooks through the prepare script
|
||||
pip install -r tests_backend/requirements.txt && python -m pytest tests_backend -q # CI pins, HA harness included
|
||||
```
|
||||
|
||||
### Why `--filter=blob:none` (#345)
|
||||
@@ -127,7 +126,9 @@ Linux CI or WSL (`bash scripts/wsl-setup.sh --verify`). Without an importable
|
||||
## Ground rules
|
||||
|
||||
- **Docs in the same commit**: CHANGELOG entry for user-visible changes;
|
||||
`docs/STATUS.md` for state changes; `docs/DEVELOPMENT.md` for new gotchas.
|
||||
`docs/ARCHITECTURE.md` for changes to the data model, WS API or coordinate
|
||||
system; `docs/STATUS.md` for state changes; `docs/DEVELOPMENT.md` for new
|
||||
gotchas. A docs-only commit needs no trailers (`PROCESS.md` §3 п.10).
|
||||
- Every UI string goes through `src/i18n/<lang>.json`; follow the
|
||||
[Translations](#translations) flow for registry and backend parity.
|
||||
- The committed bundle changes only in a release candidate: `npm run
|
||||
|
||||
+50
-23
@@ -7,8 +7,10 @@
|
||||
> агенты/сессии · любой агент может взять любую роль · инфраструктурные задачи
|
||||
> входят в общий флоу сразу на `S7-code-review`.
|
||||
>
|
||||
> **Область действия:** обязателен для владельца и для любого агента. Читается
|
||||
> сразу после `docs/SCOPE.md` и `AGENTS.md`, до `docs/STATUS.md`. Живёт в
|
||||
> **Область действия:** обязателен для владельца и для любого агента. Целиком
|
||||
> его читает тот, кто правит конвейер, гейты или сам процесс, — сразу после
|
||||
> `docs/SCOPE.md` и `AGENTS.md`, до `docs/STATUS.md`; автор и ревьюер входят через
|
||||
> ролевые конспекты ниже и открывают раздел канона по ссылке (#634, #701). Живёт в
|
||||
> репозитории: до августа 2026 канон лежал только в папке владельца, и свежий клон
|
||||
> его не содержал вовсе.
|
||||
>
|
||||
@@ -24,8 +26,9 @@
|
||||
> метках и больше нигде: Project v2 не используется. При расхождении
|
||||
> документации с GitHub побеждает GitHub. Этот файл — единственный полный канон
|
||||
> процесса в репозитории; `AGENTS.md` — его короткое обязательное резюме, а
|
||||
> внешний `CODEX-RUNBOOK.md` — только маршрутизатор к канону и историческим
|
||||
> инструкциям. Текущие версии, состояние конкретных issue, runtime pins и списки
|
||||
> внешние `CODEX-RUNBOOK.md` и `CLAUDE.md` папки владельца — только маршрутизаторы
|
||||
> к канону и историческим инструкциям: правил в них нет, и в замер цены входа
|
||||
> (`scripts/entry-cost.mjs`) они не входят — CI их не видит (#701). Текущие версии, состояние конкретных issue, runtime pins и списки
|
||||
> jobs не копируются в производную прозу: они читаются из своих исполняемых
|
||||
> источников.
|
||||
> При расхождении этого документа с `.github/workflows/*.yml` и `scripts/*`
|
||||
@@ -201,8 +204,9 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready →
|
||||
«Взял: <роль> · сессия <id> · ветка `issue/<NN>-<slug>`».
|
||||
- **WIP-лимиты:** не более **1** issue в «В разработке» на исполнителя, не более
|
||||
**3** одновременно на цикл релиза, не более **2** в «Код-ревью».
|
||||
- **Трассируемость:** ветка `issue/<NN>-<slug>`; каждый коммит несёт трейлеры
|
||||
`Issue: #<NN>` и `User-Visible: yes|no`.
|
||||
- **Трассируемость:** ветка `issue/<NN>-<slug>`; каждый коммит с файлами классов A, B или D несёт трейлеры
|
||||
`Issue: #<NN>` и `User-Visible: yes|no`; коммит только из документации — без
|
||||
трейлеров (§3 п.10, #701).
|
||||
- **Автотесты — часть реализации, а не отдельная фаза.** Каждый AC, помеченный
|
||||
`unit`/`backend`/`smoke`/`golden`, получает свою проверку здесь же.
|
||||
«Тестирование вне жизненного цикла» означает отсутствие фазы ручного
|
||||
@@ -251,7 +255,7 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready →
|
||||
а два теста были записаны в закрытие coverage-ratchet под именами, обещавшими
|
||||
то, чего они не проверяли (#430).
|
||||
|
||||
Мутант в `scripts/mutation-gate.mjs` обязателен, когда защита живёт в
|
||||
Мутант в реестре `scripts/mutation-registry.mjs` обязателен, когда защита живёт в
|
||||
продуктовом коде и проверяется дорогим гейтом (смок, бэкенд, golden): там
|
||||
ревьюер не воспроизведёт отрицательный прогон второй раз. Для чистых юнитов
|
||||
достаточно прогона со снятой защитой, приведённого в документе.
|
||||
@@ -385,7 +389,7 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready →
|
||||
fast-forward (`--commit-if-stale`, коммит класса C) — и публикацией документа ревью ТЗ
|
||||
прямо в `dev`. В ветке задачи индекс не пересобирается — ни при приведении к
|
||||
dev, ни при публикации документа код-ревью: иначе две параллельные задачи
|
||||
конфликтуют на нём по построению. Руками не правится. Конфликт ребейза, в котором **все** пути —
|
||||
конфликтуют на нём по построению. Сам `INDEX.md` руками не правится никогда. Конфликт ребейза, в котором **все** пути —
|
||||
`INDEX.md`, отказом не считается (#643): `scripts/rebase-generated.mjs`
|
||||
пересобирает индекс по каталогу на остановке и продолжает ребейз — так делают
|
||||
приведение к dev, слияние кандидата и авторский `rebase-on-dev.mjs`; индекс
|
||||
@@ -401,9 +405,10 @@ Validate кандидата. Сегодня пять чисел исходник
|
||||
patch-id кандидата слияния: вердикт к работе задачи остаётся в силе. Шаг Validate
|
||||
«индекс ревью совпадает с каталогом» красит push в `dev`, где `INDEX.md`
|
||||
расходится с каталогом (на issue-ветках не судится: их переписывает конвейер).
|
||||
Правка `docs/reviews/`
|
||||
руками — перенос в `legacy/`, удаление — сопровождается
|
||||
`node scripts/reviews-index.mjs` в том же коммите. Прежде чем брать
|
||||
Ручная правка каталога
|
||||
`docs/reviews/` — перенос документов в `legacy/`, удаление — сопровождается
|
||||
пересборкой индекса `node scripts/reviews-index.mjs` в том же коммите: индекс
|
||||
меняет генератор, а не рука. Прежде чем брать
|
||||
задачу по подсистеме, стоит прочитать её строки в индексе: что находили и чем
|
||||
закрывали — там, а не в тысяче файлов. Уроки, пережившие свою задачу,
|
||||
собираются в `docs/LESSONS.md` с датой и ссылкой на источник.
|
||||
@@ -448,8 +453,10 @@ patch-id кандидата слияния: вердикт к работе за
|
||||
|
||||
1. **Никаких изменений в код, если нет issue** и он не помечен «Готово к
|
||||
разработке» или дальше.
|
||||
2. **Issue не может быть взят в разработку**, пока у него нет ТЗ с зелёным ревью,
|
||||
пронумерованных AC с указанием доказательства и назначенного исполнителя.
|
||||
2. **Issue не может быть взят в разработку**, пока у него нет ТЗ в объёме трека
|
||||
(§5: на `track:ask` — с зелёным ревью ТЗ; на `show` — до трёх AC; на `ship` —
|
||||
строка «что меняется и чем проверить»), доказательства для каждого AC и
|
||||
назначенного исполнителя. Инфраструктурная задача ТЗ не пишет (§1).
|
||||
3. **Issue не может быть взят дважды.** Занятие фиксируется назначением, меткой и
|
||||
комментарием с именем ветки. У одного исполнителя одновременно не более одного
|
||||
issue в разработке.
|
||||
@@ -469,14 +476,17 @@ patch-id кандидата слияния: вердикт к работе за
|
||||
правка. Блокирующая находка отправляет текущий issue в «Заблокировано».
|
||||
10. **Каждый коммит класса A и B несёт трейлер `Issue: #NN`**, ветка называется
|
||||
`issue/NN-slug`, а `User-Visible: yes` требует правок в **обоих** changelog в
|
||||
том же коммите. После `cherry-pick -x` служебная строка `(cherry picked
|
||||
том же коммите. Документационный коммит — только файлы класса C — трейлеров
|
||||
не требует (#701, как `skip issue` у CPython); хук `commit-msg`, CI и
|
||||
`process-gate` судят одинаково. Коммит класса D несёт `Release:` (п.12). После `cherry-pick -x` служебная строка `(cherry picked
|
||||
from ...)` должна оставаться выше финального блока трейлеров: перед push
|
||||
проверяем порядок через `git show -s --format=full HEAD`.
|
||||
11. **Документация — в том же коммите, что поведение.** Отдельным «допишу потом»
|
||||
коммитом документация не бывает.
|
||||
12. **Сгенерированное не коммитится само по себе.** Только релизный промоушен или
|
||||
принятие эталонов с доказательством ревью: URL Linux CI run либо хеш
|
||||
аттестованного WSL-артефакта.
|
||||
принятие эталонов с доказательством ревью: трейлер `Release:` и ровно один
|
||||
источник — URL Linux CI run (`Baseline-Reviewed:`) либо хеш аттестованного
|
||||
WSL-артефакта (`Baseline-Reviewed-Local:`), §10.1.
|
||||
13. **Golden-эталоны принимаются только** `npm run golden:accept -- --reviewed` по
|
||||
полному Linux-артефакту: либо GitHub CI, либо `npm run golden:wsl:capture` в
|
||||
WSL/ext4 с clean опубликованным SHA и машинно-проверяемым паспортом. Второй
|
||||
@@ -516,8 +526,9 @@ patch-id кандидата слияния: вердикт к работе за
|
||||
- **Заход и цикл — разные величины.** Заход — сколько раз ревью отработало; он
|
||||
виден в имени документа (`-r1`, `-r2`, …) и нужен, чтобы два документа не
|
||||
затёрли друг друга. Цикл — единица бюджета §4. Заходов законно бывает больше,
|
||||
чем циклов, поэтому порог проверки 7 в `scripts/process-gate.mjs` выше лимита
|
||||
циклов (шесть документов = четыре цикла плюс два ребейза).
|
||||
чем циклов, поэтому порог проверки №7 в `scripts/process-gate.mjs`
|
||||
(`REVIEW_DOC_LIMIT` — шесть документов одного вида на issue) выше лимита
|
||||
циклов: четыре цикла плюс два ребейза.
|
||||
- Метка `review-4` ставится, когда исчерпан **бюджет циклов**; конвейер снимать
|
||||
её не вправе — это решение владельца. Если бюджет пересчитан и оказался ниже
|
||||
лимита, конвейер сообщает пересчёт, но метку не трогает.
|
||||
@@ -628,7 +639,7 @@ patch-id кандидата слияния: вердикт к работе за
|
||||
| Аналитик | разбор, оценки, поверхности | окончательно ставить приоритет |
|
||||
| Автор ТЗ | раздел `## ТЗ` в теле issue | ревьюить своё ТЗ |
|
||||
| Ревьюер ТЗ | `docs/reviews/SPEC-REVIEW-NN-rN.md` | править ТЗ вместо автора |
|
||||
| Разработчик | код, автотесты, документация, changelog | ревьюить свой код, принимать golden |
|
||||
| Разработчик | код, автотесты, документация, changelog | ревьюить свой код; принимать golden — кроме сдвига своей задачи с меткой `ci:golden` по §3 п.13 (§8, #697) |
|
||||
| Ревьюер кода | `docs/reviews/CODE-REVIEW-*-rN.md`, проверка AC | править продуктовый код |
|
||||
| Релиз-менеджер | пре-релиз, стабильный релиз, закрытие issue | добавлять код в релизный коммит |
|
||||
| Владелец | приоритет, ценность, скоуп, отклонение, арбитраж, хотфикс | — |
|
||||
@@ -733,9 +744,16 @@ issue #NN
|
||||
## 8. Гейты
|
||||
|
||||
**Локальный гейт перед выходом из «В разработке»** — минимальный набор,
|
||||
покрывающий изменённые поверхности (действующее правило владельца):
|
||||
покрывающий изменённые поверхности (действующее правило владельца).
|
||||
Обязательную часть исполняет одна команда, `npm run gate:small`
|
||||
(`scripts/gate-small.mjs` — единственный источник её состава, #701): сборка с
|
||||
typecheck, юниты, целостность и бюджет бандла, `no-new-any`, запрет синхронного
|
||||
чтения layout в render, запрет записи смоков в приватное состояние,
|
||||
`lint:unused` и вывод `smoke-select`. Команды ниже — то же самое по отдельности
|
||||
плюс то, что по диффу и AC:
|
||||
|
||||
```
|
||||
npm run gate:small # обязательная часть одной командой
|
||||
npx tsc --noEmit
|
||||
npm test
|
||||
npm run build && node scripts/bundle-policy.mjs --verify HEAD
|
||||
@@ -779,7 +797,8 @@ npx tsc -p tsconfig.junction-parity.json && node scripts/fix-test-build.mjs \
|
||||
(`workflow_dispatch`, только артефакт) с приёмкой вручную: `npm run docs:accept --
|
||||
--reviewed --from=<распакованный артефакт>` (#246). Ручной путь остаётся
|
||||
релиз-менеджеру, если бот недоступен. Съёмка на своей машине даёт байтово другой PNG при том же
|
||||
кадре, и набор из «не того» браузера переписывает все десять файлов без единого
|
||||
кадре, и набор из «не того» браузера переписывает все кадры (их число — в
|
||||
`demo/docs/screenshots.mjs`) без единого
|
||||
содержательного изменения. Приёмка отказывает, если кандидат снят не с этого
|
||||
дерева, не тем капчуром, не называет свой Chromium или неполон; коммит бота
|
||||
проверяет релиз-менеджер, коммит ручной приёмки делает человек.
|
||||
@@ -938,7 +957,8 @@ Performance зелёные на точном SHA, плюс зелёный E2E н
|
||||
включая свежий контейнер облачного агента, без отдельного шага в инструкции.
|
||||
|
||||
- **`commit-msg`** — есть, работает. Отклоняет коммит без терминального
|
||||
`Issue: #NN`, требует ровно один `User-Visible: yes|no`, а для коммитов,
|
||||
`Issue: #NN`, требует ровно один `User-Visible: yes|no` (коммит только из
|
||||
файлов класса C — без трейлеров, §3 п.10, #701), а для коммитов,
|
||||
трогающих `demo/golden/baselines/**`, — `Release:` плюс ровно один источник:
|
||||
`Baseline-Reviewed: <URL GitHub run>` либо
|
||||
`Baseline-Reviewed-Local: sha256:<хеш аттестации>`. Локальный хеш обязан
|
||||
@@ -991,7 +1011,9 @@ Performance зелёные на точном SHA, плюс зелёный E2E н
|
||||
`Baseline-Reviewed: <ссылка на прогон CI>` либо
|
||||
`Baseline-Reviewed-Local: sha256:<хеш аттестации>`;
|
||||
6. релизный коммит не содержит изменений в `src/` и `custom_components/**/*.py`;
|
||||
7. документов ревью на один issue не больше четырёх (`-r1`…`-r4`).
|
||||
7. документов ревью одного вида (`SPEC-REVIEW`, `CODE-REVIEW`) на один issue
|
||||
не больше шести (`-r1`…`-r6`, `REVIEW_DOC_LIMIT`): четыре цикла плюс два
|
||||
ребейза (§4).
|
||||
|
||||
С токеном GitHub:
|
||||
|
||||
@@ -1012,6 +1034,11 @@ Validate стартует от этого push и успевает прочит
|
||||
документ ревью: он ложится в ветку задачи, пока та в `S4-spec-review` или
|
||||
`S7-code-review`, то есть заведомо вне рабочего множества.
|
||||
|
||||
**Инфраструктурный диапазон статуса не требует** (#562, §1): если в диапазоне нет
|
||||
ни одного файла класса A, задача ещё не вошла в поток — она войдёт в него сразу на
|
||||
`S7-code-review`, — и отсутствие S-метки не отказ. Признак механический, по
|
||||
диффу, а не по метке `infra`.
|
||||
|
||||
**При продвижении в `main` не перепроверяются коммиты, уже достижимые из
|
||||
prerelease-тега.** После выпуска беты их issue по §2.8 должны быть закрыты, а
|
||||
stable fast-forward снова включает эти коммиты в диапазон `old-main..candidate`.
|
||||
|
||||
+4
-1
@@ -330,7 +330,10 @@ the `dev` branch. Access to the owner's instances is not documented here.
|
||||
|
||||
- The card module URL contains `?v=<VERSION from const.py>`. Browsers keep the ES module in
|
||||
memory cache: after deploying new JS **bump VERSION in const.py and restart HA**,
|
||||
otherwise a plain F5 will keep the old version.
|
||||
otherwise a plain F5 will keep the old version. This is a deployment step — a
|
||||
release candidate or your own local stand. An ordinary task commit bumps neither
|
||||
`VERSION` nor the committed bundle: both change only in a commit with a
|
||||
`Release:` trailer (#657, `PROCESS.md` §1).
|
||||
- After a page reload the HA frontend (with kiosk-mode) sometimes leaves the view empty
|
||||
("InvalidStateError: Transition was aborted", hui-view is not created for 1–2 min).
|
||||
Cured by repeating the SPA navigation: pushState + a location-changed event, or just waiting.
|
||||
|
||||
+4
-3
@@ -3,8 +3,9 @@
|
||||
Датированные выводы, пережившие свою задачу (#635). Каждый — одной мыслью, со
|
||||
ссылкой на источник: документ ревью, issue, аудит. Полный след решений — в
|
||||
`docs/reviews/INDEX.md` и комментариях issue; здесь только то, что меняет
|
||||
поведение на следующей задаче. Конспекты ролей (#634) и промпт ревьюера
|
||||
ссылаются сюда.
|
||||
поведение на следующей задаче. Читается перед задачей в затронутой подсистеме
|
||||
вместе с её строками в `docs/reviews/INDEX.md` (PROCESS.md, «Индекс документов
|
||||
ревью»); конспекты ролей сюда не ведут — урок, ставший правилом, живёт в каноне.
|
||||
|
||||
| Дата | Урок | Источник |
|
||||
|---|---|---|
|
||||
@@ -12,7 +13,7 @@
|
||||
| 2026-09-06 | Число смоков/тестов не живёт в тексте: считается командой (`npm run inventory`). | #472, PROCESS.md |
|
||||
| 2026-09-09 | Мутанты по диффу — на кандидате, не на каждом push: 48 из 56 часов job-минут за два дня ушли в отменённые прогоны. | #510 |
|
||||
| 2026-09-13 | Ненулевой exit гарда ≠ «мутант пойман»: setup-failure, invalid-mutation и survived различаются; засчитывается только assertion-killed. | #558, #568 |
|
||||
| 2026-09-18 | Ревью на не принятых эталонах — потерянный цикл: `golden:verify` ревьюер гоняет лично, эталоны принимаются до `S7`. | CODE-REVIEW-598-r1 H1 |
|
||||
| 2026-09-18 | Ревью на не принятых эталонах — потерянный цикл: `golden:verify` ревьюер гоняет лично, эталоны принимаются до `S7`. С #697 — только у задачи с `ci:golden`; сдвиг остальных принимает бот на `dev` перед бетой. | CODE-REVIEW-598-r1 H1, #697 |
|
||||
| 2026-09-20 | Стенд ≠ Home Assistant: нативный `<dialog>` и `ha-dialog` — разные ветки; свидетель для реального HA — фикстура #505, а не golden стенда. | аудит 22.09 A/H1, #607, #609 |
|
||||
| 2026-09-20 | Контролируемое поле без `live()` показывает не то число, что в состоянии; клампить на коммите (`change`), не на каждом символе. | аудит 22.09 A/H2, #608 |
|
||||
| 2026-09-21 | Лог без итоговой строки — обрыв, а не «ok»: отчёт судит по маркеру завершения и исходу шага, не по отсутствию `FAIL`. | #604 |
|
||||
|
||||
+4
-2
@@ -3,8 +3,10 @@
|
||||
> The current state for a resuming session: a generated snapshot, the current
|
||||
> cycle and the standing decisions that explain it. Rules are not here — the
|
||||
> process is `PROCESS.md`, release mechanics are `docs/DEVELOPMENT.md` › Release,
|
||||
> and task scope and status live in GitHub Issues and their labels. Update a row
|
||||
> in the same commit as the change it describes (`PROCESS.md` §2.6).
|
||||
> and task scope and status live in GitHub Issues and their labels. The snapshot
|
||||
> below is generated — never edit it by hand; the prose sections after it are
|
||||
> edited by hand, a row in the same commit as the change it describes
|
||||
> (`PROCESS.md` §2.6).
|
||||
|
||||
## Snapshot
|
||||
|
||||
|
||||
+16
-7
@@ -28,10 +28,12 @@ golden, а то, чего автоматика не видит, собрано
|
||||
подложки, Glow и декора проверяет пустую страницу; солнце с азимутом, при
|
||||
котором луч не достигает единственного окна (#89), — та же ошибка в
|
||||
геометрии.
|
||||
4. **Тест, охраняющий механизм, сопровождается мутантом** в
|
||||
`scripts/mutation-registry.mjs`: 2–5 строк патча, воспроизводящего поломку,
|
||||
против которой тест заведён, и тест обязан на ней краснеть. Чистым функциям
|
||||
с обычными юнитами мутант не нужен.
|
||||
4. **Мутант в `scripts/mutation-registry.mjs` обязателен, когда защита живёт в
|
||||
продуктовом коде и проверяется дорогим гейтом** (смок, бэкенд, golden):
|
||||
2–5 строк патча, воспроизводящего поломку, против которой тест заведён, и
|
||||
тест обязан на ней краснеть. Для чистых юнитов достаточно прогона со снятой
|
||||
защитой, приведённого в документе ревью. Правило одно — PROCESS.md §2.7
|
||||
(#701); на треке `show` отсутствие мутанта — Low (§10.4).
|
||||
5. **Тавтологический ассерт — читающий то же свойство, которое код только что
|
||||
выставил, — не пишется вовсе.** Он может упасть только при удалении строки,
|
||||
но не при её неработоспособности.
|
||||
@@ -45,7 +47,8 @@ golden, а то, чего автоматика не видит, собрано
|
||||
|
||||
Проверка: `node scripts/mutation-gate.mjs --check` — якоря патчей живы;
|
||||
полный прогон — workflow `mutation-gate.yml` (шесть чересполосных шардов
|
||||
`--shard=i/6` — при четырёх шард упёрся в потолок 60 минут на 810 мутантах, #604;
|
||||
`--shard=i/6` — при четырёх шард упёрся в потолок 60 минут, #604; число мутантов
|
||||
считает `npm run inventory`, а не этот текст;
|
||||
один зафиксированный commit/tree для всего прогона; каждый артефакт несёт
|
||||
identity и исход шага прогона, а отдельный агрегатор fail-closed отвергает
|
||||
смешанные, неполные или прерванные по таймауту evidence даже при частичном
|
||||
@@ -250,6 +253,12 @@ c._drag = { id, sx, sy }; // private-ok: #NNN состояние жеста, о
|
||||
|
||||
## Локальный набор перед пушем (#343)
|
||||
|
||||
Обязательный локальный набор — `npm run gate:small`; его состав живёт в
|
||||
`scripts/gate-small.mjs` и нигде больше не переписывается (#701). Хук `pre-push`
|
||||
гоняет его сам для веток задач — таблица «В хуке» ниже. Ручной
|
||||
`node scripts/pre-push-gate.mjs` из этого раздела — расширенный прогон:
|
||||
типы, юниты, смоки и мутанты по диффу.
|
||||
|
||||
Красный CI — дорогой способ узнать о проблеме: пять минут ожидания, а при
|
||||
код-ревью ещё и лишний раунд. Прецедент назван в задаче: находка r2-H1 в #329
|
||||
стоила целого раунда и ловилась локальным `npm test`.
|
||||
@@ -736,8 +745,8 @@ See `demo/performance/README.md` for commands and the budget-review contract.
|
||||
Остальные пункты с пометкой `[manual]` описывали обычное поведение режимов,
|
||||
редакторов, диалогов, правил значков и `houseplan-space-card`. Для них нужен
|
||||
смок, а не ручная проверка: задача, которая трогает такую поверхность без
|
||||
смока, пишет его в том же коммите (правило `[auto: …]` выше). Пункты
|
||||
`[manual]` разделов ниже остаются как есть.
|
||||
смока, пишет его в том же коммите (правило `[auto: …]` выше). Оставшиеся пункты
|
||||
`[manual]` разделов выше остаются как есть.
|
||||
|
||||
## Release regression quickies
|
||||
|
||||
|
||||
+11
-10
@@ -94,12 +94,14 @@
|
||||
## Реализация (`S6-in-progress`)
|
||||
|
||||
- Занятие: `Взял: <роль> · сессия <id> · ветка issue/NN-slug`; WIP — одна
|
||||
задача в разработке на исполнителя
|
||||
задача в разработке на исполнителя, не больше трёх на цикл релиза и двух в
|
||||
код-ревью
|
||||
([§2.6](../../PROCESS.md#26-в-разработке--реализация),
|
||||
[§7.2](../../PROCESS.md#72-шаблоны-комментариев)).
|
||||
- Ветка `issue/<NN>-<slug>`; каждый коммит несёт трейлеры `Issue: #<NN>` и
|
||||
`User-Visible: yes|no`. `User-Visible: yes` требует правок в обоих changelog
|
||||
в том же коммите. После `cherry-pick -x` трейлеры остаются последним блоком
|
||||
- Ветка `issue/<NN>-<slug>`; каждый коммит с файлами классов A, B или D несёт
|
||||
трейлеры `Issue: #<NN>` и `User-Visible: yes|no`, коммит только из
|
||||
документации — без трейлеров. `User-Visible: yes` требует правок в обоих
|
||||
changelog в том же коммите. После `cherry-pick -x` трейлеры остаются последним блоком
|
||||
([§2.6](../../PROCESS.md#26-в-разработке--реализация),
|
||||
[§3 п.10](../../PROCESS.md#3-правила)).
|
||||
- Автотесты — часть реализации: каждый AC с пометкой `unit` / `backend` /
|
||||
@@ -137,13 +139,12 @@
|
||||
|
||||
## Гейты перед хендоффом
|
||||
|
||||
- Минимальный набор по изменённым поверхностям: `npx tsc --noEmit`,
|
||||
`npm test`, `npm run build` + `bundle-policy --verify`, `smoke-select` и
|
||||
целевые смоки, `no-new-any`; по диффу — `model-invariants`,
|
||||
- Обязательная часть — `npm run gate:small`: его состав живёт в
|
||||
`scripts/gate-small.mjs` и нигде не переписывается. По диффу и AC сверх
|
||||
него — целевые смоки из вывода `smoke-select`, `model-invariants`,
|
||||
`pytest tests_backend`, junction parity; `golden:verify` — только с меткой
|
||||
`ci:golden`. Команды —
|
||||
в каноне ([§8](../../PROCESS.md#8-гейты)); `npm run gate:small` собирает
|
||||
обязательную часть (`docs/TESTING.md`, «Локальный набор перед пушем»).
|
||||
`ci:golden` ([§8](../../PROCESS.md#8-гейты); `docs/TESTING.md`, «Локальный
|
||||
набор перед пушем»).
|
||||
- Бандл в коммит задачи не идёт: сборка переписывает отслеживаемый `dist/`,
|
||||
перед коммитом — `npm run bundle:clean`; хук `commit-msg` отклоняет пути
|
||||
бандла без трейлера `Release:` (#657, [§1](../../PROCESS.md#1-основное-правило)).
|
||||
|
||||
@@ -83,9 +83,10 @@
|
||||
([§8](../../PROCESS.md#8-гейты)).
|
||||
- Условие честности сужения: ревьюер обязан перечислить, какие гейты прогнал,
|
||||
какие нет и почему ([§8](../../PROCESS.md#8-гейты)).
|
||||
- Зелёный `pytest tests_backend` без Home Assistant скипает `test_ha_*.py` и
|
||||
ничего не доказывает — это «чего не проверял» (`docs/TESTING.md`;
|
||||
[§8](../../PROCESS.md#8-гейты)).
|
||||
- Зелёный `pytest tests_backend` без Home Assistant `test_ha_*.py` не
|
||||
собирает вовсе — их нет ни в `passed`, ни в `skipped` (строка `HA harness NOT
|
||||
collected`), и такой прогон про HA ничего не доказывает — это «чего не
|
||||
проверял» (`docs/TESTING.md`; [§8](../../PROCESS.md#8-гейты)).
|
||||
|
||||
## Повторный раунд
|
||||
|
||||
|
||||
@@ -8,13 +8,8 @@
|
||||
|
||||
Здесь остались только ТЗ, на которые ссылаются живые код, тесты и документы (ADR, `ISOMETRIC.md`, `SUN.md`, `RADAR.md`, `LIGHT.md`, `DECOR-EDITOR.md`, support-relay), и те, на которые ссылаются они сами. Остальные ТЗ выпущенных задач перенесены в [`legacy/specs/`](../../legacy/specs/) (#682): историю не переписываем, ссылки из документов ревью ведут по SHA и живут дальше. Каталог нужен `scripts/task-packet.mjs` и проверке 3 `scripts/process-gate.mjs`.
|
||||
|
||||
## Обязательные release-артефакты ТЗ
|
||||
## Правила ТЗ
|
||||
|
||||
Если задача меняет пользовательское поведение, её ТЗ обязано явно перечислить:
|
||||
|
||||
- записи в `docs/CHANGELOG.md` и `docs/CHANGELOG.ru.md`;
|
||||
- затронутую пользовательскую документацию;
|
||||
- требуемые screenshots/golden и способ их review, если меняется визуал;
|
||||
- release/performance/security artifacts, если они входят в acceptance gate.
|
||||
|
||||
Отсутствие этого раздела не означает, что документация необязательна. Для чистого refactoring ТЗ должно прямо зафиксировать отсутствие пользовательских изменений и перечислить технические доказательства безопасного поведения.
|
||||
Здесь не живут (#701): архив правил не задаёт. Обязательные разделы ТЗ, включая
|
||||
release-артефакты (changelog RU+EN, документация, скриншоты и golden, perf и
|
||||
security, если они входят в приёмку), — `PROCESS.md` §7.1 и DoR `S5-ready` (§2).
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Классы изменений, PROCESS.md §1 — одна таблица на все гейты (#701).
|
||||
//
|
||||
// Вынесена из process-gate.mjs: её читает и `validate-commit-provenance.mjs`
|
||||
// (хук commit-msg), которому process-gate сам импортирует — общий модуль
|
||||
// снимает круговую зависимость.
|
||||
// Порядок важен: D проверяется первым, иначе собранный бандл попадёт в A,
|
||||
// а demo/golden/baselines — в B.
|
||||
const CLASS_D = [
|
||||
/^dist\//,
|
||||
/^custom_components\/houseplan\/frontend\//,
|
||||
/^demo\/srv\/assets\/houseplan-card\.js$/,
|
||||
/^demo\/golden\/baselines\//,
|
||||
];
|
||||
const CLASS_A = [
|
||||
/^src\//,
|
||||
/^custom_components\/houseplan\/.*\.py$/,
|
||||
/^hacs\.json$/,
|
||||
/^custom_components\/.*\/manifest\.json$/,
|
||||
/^custom_components\/.*\/translations\//,
|
||||
];
|
||||
const CLASS_B = [
|
||||
/^test\//, /^tests_backend\//, /^demo\//, /^scripts\//,
|
||||
/^\.github\//, /^\.githooks\//, /^rollup\.config\.mjs$/, /^tsconfig.*\.json$/,
|
||||
/^package(-lock)?\.json$/, /^pytest\.ini$/, /^\.gitignore$/, /^\.gitattributes$/,
|
||||
// Пины toolchain — производные от validate.yml (#496), конфиг сборки.
|
||||
/^\.nvmrc$/, /^\.python-version$/,
|
||||
];
|
||||
const CLASS_C = [
|
||||
/^docs\//, /^README/, /^CHANGELOG/, /^AGENTS\.md$/, /^LICENSE$/,
|
||||
/^CONTRIBUTING\.md$/, /^PROCESS.*\.md$/, /^(CODE|SPEC)-REVIEW-.*\.md$/,
|
||||
// #682: архив выпущенного — документы ревью и ТЗ прошлых линий. Только
|
||||
// Markdown; исполняемого там нет (#678 вынес всё прочее из дерева).
|
||||
/^legacy\//,
|
||||
];
|
||||
|
||||
|
||||
export function classify(path) {
|
||||
if (CLASS_D.some((r) => r.test(path))) return 'D';
|
||||
if (CLASS_A.some((r) => r.test(path))) return 'A';
|
||||
if (CLASS_B.some((r) => r.test(path))) return 'B';
|
||||
if (CLASS_C.some((r) => r.test(path))) return 'C';
|
||||
return '?';
|
||||
}
|
||||
@@ -25,9 +25,12 @@ const ROOT = fileURLToPath(new URL('..', import.meta.url));
|
||||
|
||||
/** Маршруты входа по роли. `budget: null` — только замер, без порога. */
|
||||
export const ROUTES = Object.freeze({
|
||||
// #701: `docs/STATUS.md` ушёл из входа автора — снимок версий и цикла нужен,
|
||||
// когда сессия возобновляет работу или готовит релиз, а задачу ведёт её пакет
|
||||
// (`task-packet.mjs`). Минус 691 слово на каждом входе.
|
||||
author: {
|
||||
budget: 12000, // AC1 #634
|
||||
files: ['docs/SCOPE.md', 'AGENTS.md', 'docs/process/AUTHOR.md', 'docs/STATUS.md'],
|
||||
files: ['docs/SCOPE.md', 'AGENTS.md', 'docs/process/AUTHOR.md'],
|
||||
},
|
||||
reviewer: {
|
||||
budget: 9000,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
// Локальный гейт лёгкого трека одной командой (#479): `npm run gate:small`.
|
||||
//
|
||||
// PROCESS §8 перечисляет автору шесть команд, и в #476 они гонялись
|
||||
// Этот файл — единственный источник состава обязательной части §8 (#701):
|
||||
// канон и конспекты его не переписывают, а называют. В #476 команды гонялись
|
||||
// последовательно, вперемешку с гейтами, к задаче не относящимися. Здесь
|
||||
// обязательная часть §8 начинается параллельно — сборка с typecheck, «новый
|
||||
// код не добавляет any», выбор смоков по диффу. Юниты читают свежий `dist`,
|
||||
|
||||
@@ -8158,6 +8158,29 @@ const MUTANT_DEFINITIONS = [
|
||||
replace: ' .hdr > .head { flex-wrap: wrap; padding: 5px 8px; gap: 6px; }',
|
||||
}],
|
||||
},
|
||||
// #701: документационный коммит трейлеров не требует — и только он.
|
||||
{
|
||||
id: 'docs-only-commit-needs-trailers-again',
|
||||
guard: 'node --test --test-name-pattern="#701" test/commit-provenance.test.mjs',
|
||||
because: '#701 (PROCESS §3 п.10): rule #1 guards product code, not a typo in a guide; a docs-only '
|
||||
+ 'commit carries no Issue/User-Visible trailers',
|
||||
patches: [{
|
||||
file: 'scripts/validate-commit-provenance.mjs',
|
||||
find: ' const exempt = isDocsOnlyCommit(changedFiles) && !issues.length && !visible.length;',
|
||||
replace: ' const exempt = false; // mutant: every commit needs trailers',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'docs-only-exemption-leaks-to-code',
|
||||
guard: 'node --test --test-name-pattern="#701" test/commit-provenance.test.mjs',
|
||||
because: '#701: one file outside class C makes the commit subject to rule #1 again; the exemption '
|
||||
+ 'must not cover a commit that also touches src/**',
|
||||
patches: [{
|
||||
file: 'scripts/validate-commit-provenance.mjs',
|
||||
find: " return changedFiles.length > 0 && changedFiles.every((file) => classify(file.replaceAll('\\\\', '/')) === 'C');",
|
||||
replace: " return changedFiles.length > 0 && changedFiles.some((file) => classify(file.replaceAll('\\\\', '/')) === 'C'); // mutant",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'header-menu-drops-pdf',
|
||||
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
|
||||
|
||||
@@ -39,36 +39,11 @@ import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { resolveValidationRange } from './validate-commit-provenance.mjs';
|
||||
|
||||
// --- классы изменений, PROCESS.md §1 ---
|
||||
// Порядок важен: D проверяется первым, иначе собранный бандл попадёт в A,
|
||||
// а demo/golden/baselines — в B.
|
||||
const CLASS_D = [
|
||||
/^dist\//,
|
||||
/^custom_components\/houseplan\/frontend\//,
|
||||
/^demo\/srv\/assets\/houseplan-card\.js$/,
|
||||
/^demo\/golden\/baselines\//,
|
||||
];
|
||||
const CLASS_A = [
|
||||
/^src\//,
|
||||
/^custom_components\/houseplan\/.*\.py$/,
|
||||
/^hacs\.json$/,
|
||||
/^custom_components\/.*\/manifest\.json$/,
|
||||
/^custom_components\/.*\/translations\//,
|
||||
];
|
||||
const CLASS_B = [
|
||||
/^test\//, /^tests_backend\//, /^demo\//, /^scripts\//,
|
||||
/^\.github\//, /^\.githooks\//, /^rollup\.config\.mjs$/, /^tsconfig.*\.json$/,
|
||||
/^package(-lock)?\.json$/, /^pytest\.ini$/, /^\.gitignore$/, /^\.gitattributes$/,
|
||||
// Пины toolchain — производные от validate.yml (#496), конфиг сборки.
|
||||
/^\.nvmrc$/, /^\.python-version$/,
|
||||
];
|
||||
const CLASS_C = [
|
||||
/^docs\//, /^README/, /^CHANGELOG/, /^AGENTS\.md$/, /^LICENSE$/,
|
||||
/^CONTRIBUTING\.md$/, /^PROCESS.*\.md$/, /^(CODE|SPEC)-REVIEW-.*\.md$/,
|
||||
// #682: архив выпущенного — документы ревью и ТЗ прошлых линий. Только
|
||||
// Markdown; исполняемого там нет (#678 вынес всё прочее из дерева).
|
||||
/^legacy\//,
|
||||
];
|
||||
// Классы изменений (PROCESS.md §1) живут в change-classes.mjs (#701): их
|
||||
// читает и хук commit-msg, который судит, нужен ли коммиту трейлер.
|
||||
import { classify } from './change-classes.mjs';
|
||||
|
||||
export { classify };
|
||||
|
||||
const CHANGELOGS = ['docs/CHANGELOG.md', 'docs/CHANGELOG.ru.md'];
|
||||
|
||||
@@ -90,13 +65,6 @@ export const RULES = {
|
||||
10: 'DoR по моменту коммита',
|
||||
};
|
||||
|
||||
export function classify(path) {
|
||||
if (CLASS_D.some((r) => r.test(path))) return 'D';
|
||||
if (CLASS_A.some((r) => r.test(path))) return 'A';
|
||||
if (CLASS_B.some((r) => r.test(path))) return 'B';
|
||||
if (CLASS_C.some((r) => r.test(path))) return 'C';
|
||||
return '?';
|
||||
}
|
||||
|
||||
// --- разбор коммитов ---
|
||||
// Тело коммита многострочное, поэтому поля режутся не по переводам строк:
|
||||
|
||||
@@ -4,6 +4,7 @@ import { basename } from 'node:path';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { bundleCommitErrors } from './bundle-policy.mjs';
|
||||
import { classify } from './change-classes.mjs';
|
||||
|
||||
const TRAILER = /^([A-Za-z][A-Za-z0-9-]*):\s*(.*?)\s*$/;
|
||||
export const ENFORCEMENT_BOUNDARY = '8e2973fa7a7cb1a80204ff95ecf3f2d7c36ed2ce';
|
||||
@@ -52,15 +53,27 @@ export function terminalTrailers(message) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* #701 (PROCESS.md §3 п.10): документационный коммит — только файлы класса C —
|
||||
* трейлеров не требует, как `skip issue` у CPython. Правило №1 охраняет
|
||||
* продуктовый код, а не опечатку в гайде. Пустой список файлов (сообщение без
|
||||
* `--staged`) — не документационный коммит: судить нечем, правило прежнее.
|
||||
* Трейлеры, если они есть, судятся всегда.
|
||||
*/
|
||||
export function isDocsOnlyCommit(changedFiles = []) {
|
||||
return changedFiles.length > 0 && changedFiles.every((file) => classify(file.replaceAll('\\', '/')) === 'C');
|
||||
}
|
||||
|
||||
export function validateCommitMessage(message, changedFiles = [], { baselineIndex = undefined, authorDate = null } = {}) {
|
||||
const trailers = terminalTrailers(message);
|
||||
const errors = [];
|
||||
const issues = trailers.get('Issue') || [];
|
||||
if (!issues.length || issues.some((value) => !/^#[1-9][0-9]*$/.test(value))) {
|
||||
const visible = trailers.get('User-Visible') || [];
|
||||
const exempt = isDocsOnlyCommit(changedFiles) && !issues.length && !visible.length;
|
||||
if (!exempt && (!issues.length || issues.some((value) => !/^#[1-9][0-9]*$/.test(value)))) {
|
||||
errors.push("missing or invalid terminal 'Issue: #<positive number>' trailer");
|
||||
}
|
||||
const visible = trailers.get('User-Visible') || [];
|
||||
if (visible.length !== 1 || !/^(yes|no)$/.test(visible[0])) {
|
||||
if (!exempt && (visible.length !== 1 || !/^(yes|no)$/.test(visible[0]))) {
|
||||
errors.push("expected exactly one terminal 'User-Visible: yes|no' trailer");
|
||||
}
|
||||
const normalizedFiles = changedFiles.map((file) => file.replaceAll('\\', '/'));
|
||||
|
||||
@@ -105,3 +105,19 @@ test('the audited beta.2 baseline exception is exact and golden-only', () => {
|
||||
assert.equal(validateHistoricalCommit(`${audited.slice(0, -1)}2`, message, changed).length, 2);
|
||||
assert.match(validateHistoricalCommit(audited, 'Update baseline', changed)[0], /Issue/);
|
||||
});
|
||||
|
||||
// #701 (PROCESS.md §3 п.10): трейлеры — только на коммитах с продуктовыми и
|
||||
// инфраструктурными файлами; документационный коммит их не требует.
|
||||
test('#701: документационный коммит (только класс C) трейлеров не требует', () => {
|
||||
assert.deepEqual(validateCommitMessage('docs: fix a typo in the guide', ['docs/USER-GUIDE.md', 'README.md']), []);
|
||||
assert.deepEqual(validateCommitMessage('docs: changelog wording', ['docs/CHANGELOG.md']), []);
|
||||
// Хоть один файл вне класса C — прежнее правило.
|
||||
assert.equal(validateCommitMessage('fix: x', ['docs/USER-GUIDE.md', 'src/card.ts']).length, 2);
|
||||
assert.equal(validateCommitMessage('test: x', ['test/a.test.mjs']).length, 2);
|
||||
assert.equal(validateCommitMessage('build: x', ['dist/houseplan-card.js']).length >= 2, true);
|
||||
// Судить нечем — не документационный коммит.
|
||||
assert.equal(validateCommitMessage('docs: typo', []).length, 2);
|
||||
// Трейлер, если он есть, судится всегда: кривой номер — ошибка и в docs-коммите.
|
||||
assert.equal(validateCommitMessage('docs: typo\n\nIssue: #x', ['docs/a.md']).length, 2);
|
||||
assert.deepEqual(validateCommitMessage('docs: typo\n\nIssue: #9\nUser-Visible: no', ['docs/a.md']), []);
|
||||
});
|
||||
|
||||
@@ -44,3 +44,13 @@ test('#634 entry-cost: AGENTS.md называет те же маршруты в
|
||||
assert.deepEqual(route('reviewer'), ROUTES.reviewer.files);
|
||||
assert.deepEqual(route('changing the pipeline'), ROUTES.canon.files);
|
||||
});
|
||||
|
||||
test('#701 D14: промпт ревьюера читает маршрут reviewer в том же порядке, что AGENTS.md', async () => {
|
||||
const { readFileSync } = await import('node:fs');
|
||||
const { ROUTES } = await import('../scripts/entry-cost.mjs');
|
||||
const workflow = readFileSync(new URL('../.github/workflows/_process.yml', import.meta.url), 'utf8');
|
||||
const start = workflow.indexOf('Прочитай в этом порядке, прежде чем судить:');
|
||||
assert.ok(start > 0, 'нумерованный порядок чтения в промпте найден');
|
||||
const items = [...workflow.slice(start, start + 3000).matchAll(/^\s+(\d)\. (\S+)/gm)].slice(0, 3).map((m) => m[2]);
|
||||
assert.deepEqual(items, ROUTES.reviewer.files);
|
||||
});
|
||||
|
||||
@@ -134,6 +134,7 @@ const HOOK_FILES = [
|
||||
'scripts/pre-push-gate.mjs',
|
||||
'scripts/branch-state.mjs',
|
||||
'scripts/process-gate.mjs',
|
||||
'scripts/change-classes.mjs', // #701: классы изменений — общие для гейта и трейлеров
|
||||
'scripts/validate-commit-provenance.mjs',
|
||||
'scripts/bundle-policy.mjs', // #657: правило бандла в проверке происхождения
|
||||
'scripts/bundle-tree.mjs',
|
||||
|
||||
@@ -50,7 +50,7 @@ const KEY_RULES = {
|
||||
['5-треки-ship-show-ask--метка-владельца', 'ожидаемое поведение уже зафиксировано'],
|
||||
['71-цепочка', 'Владельцу задаются только продуктовые вопросы'],
|
||||
['71-цепочка', 'issue остаётся в `S3-spec` и получает `blocked`'],
|
||||
['26-в-разработке--реализация', 'каждый коммит несёт трейлеры `Issue: #<NN>` и `User-Visible: yes|no`'],
|
||||
['26-в-разработке--реализация', 'каждый коммит с файлами классов A, B или D несёт трейлеры `Issue: #<NN>` и `User-Visible: yes|no`'],
|
||||
['3-правила', '`User-Visible: yes` требует правок в обоих changelog в том же коммите'],
|
||||
['26-в-разработке--реализация', 'шесть классов риска'],
|
||||
['26-в-разработке--реализация', 'Скоуп не расширяется'],
|
||||
|
||||
Reference in New Issue
Block a user