From 565f518dcde65d4fc9753e5ae8d91010c61a742d Mon Sep 17 00:00:00 2001 From: Matysh Date: Thu, 13 Aug 2026 21:59:55 +0300 Subject: [PATCH 1/2] perf: make review scope and ceremony fit the size of the task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The owner's report: the process works but every stage takes a long time even on simple bugs. Two causes, and neither was the one that first comes to mind. The reviewer ran everything regardless. On #89 it installed Chromium, ran all 127 smoke files and a full golden capture — right for a task rated 10/10 for complexity, absurd for a bug about a room divider. Full suites are the pre-beta gate; the review now runs typecheck, unit and build always, and smokes, golden, pytest or performance only where the diff and the AC call for them. The price of narrowing it is honesty: the reviewer must list which gates it ran, which it did not, and why, so a skipped gate is a visible decision rather than a silent one. The reviewer also built its own environment out of model turns, with no npm cache and no browser cache, paid for from the same forty-five minutes. The workflow now installs dependencies and Chromium as ordinary cached steps, after switching to the task branch so the lockfile is the branch's own. Second, ceremony did not scale down. The light track makes a spec cheap; the new trivial track does without one — S2-analysis straight to S5-ready, no spec review, AC in the issue body. It is deliberately hard to qualify for: a bug on one surface, no new UX contract, no migration, no i18n, no perf or touch effect, three checkable AC at most, and expected behaviour already on record. Nothing left to decide is the criterion that holds the whole thing up, and it cannot be met by feeling sure. Code review is never skipped on either track. It is what stands in for testing here, so it is the one stage speed may not buy. Issue: #127 Issue: #128 User-Visible: no --- .github/workflows/process.yml | 69 +++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/.github/workflows/process.yml b/.github/workflows/process.yml index b346a7c2..274aaa59 100644 --- a/.github/workflows/process.yml +++ b/.github/workflows/process.yml @@ -48,6 +48,7 @@ jobs: BLOCKED: ${{ contains(github.event.issue.labels.*.name, 'blocked') }} EXHAUSTED: ${{ contains(github.event.issue.labels.*.name, 'review-4') }} SMALL: ${{ contains(github.event.issue.labels.*.name, 'small') }} + TRIVIAL: ${{ contains(github.event.issue.labels.*.name, 'trivial') }} NUM: ${{ github.event.issue.number }} run: | # Этап определяется первым: от него зависит, какие вердикты считать. @@ -58,8 +59,9 @@ jobs: *) echo "метка $LABEL конвейер не запускает" ;; esac - # Лимит циклов: 4 обычный, 2 на лёгком треке (PROCESS.md §4). - limit=4; [ "$SMALL" = "true" ] && limit=2 + # Лимит циклов: 4 обычный, 2 на лёгком и коротком треке (PROCESS.md §4). + limit=4 + if [ "$SMALL" = "true" ] || [ "$TRIVIAL" = "true" ]; then limit=2; fi # Счётчик считает вердикты ТОЛЬКО своего этапа. Раньше он брал все # подряд, и вердикт по ТЗ съедал цикл из бюджета код-ревью: на #89 @@ -134,8 +136,15 @@ jobs: fetch-depth: 0 ref: dev + # Окружение готовит workflow, а не модель своими ходами. Раньше промпт + # велел ревьюеру самому выполнить `npm ci`: минуты уходили на установку без + # кэша, платились из бюджета 45 минут и из лимитов подписки, а ходы модели + # тратились на работу инфраструктуры. В validate.yml кэш стоит на всех + # тяжёлых job, здесь его не было. - uses: actions/setup-node@v4 - with: { node-version: 22 } + with: + node-version: 22 + cache: npm # Материал ревью живёт в ветке задачи: ТЗ в docs/specs/ и код коммитятся # в issue/-slug. Если ветка запушена — переключаемся на неё, иначе @@ -156,6 +165,24 @@ jobs: echo "МАТЕРИАЛ НЕ ЗАПУШЕН" >> "$GITHUB_STEP_SUMMARY" fi + # Зависимости ставятся ПОСЛЕ переключения на ветку задачи: lockfile мог + # измениться именно в ней, и установка по копии из dev дала бы не то дерево. + - name: Установить зависимости + run: npm ci + + # Браузер нужен не всякому ревью (см. правило выбора гейтов в промпте), + # но когда нужен — качать его заново дороже, чем держать в кэше. + - name: Кэш браузеров Playwright + id: pw + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }} + + - name: Установить Chromium + if: steps.pw.outputs.cache-hit != 'true' + run: npx playwright install --with-deps chromium + - name: Review id: review uses: anthropics/claude-code-action@v1 @@ -205,10 +232,38 @@ jobs: По каждому AC: либо он доказан автотестом и ты убедился, что тест умеет падать, либо разобран по коду с явной записью «проверено чтением, не исполнением». «Verified» без названной команды и её - результата доказательством не является. Зависимостей в рабочей - копии нет: перед гейтами выполни `npm ci`. Проверь трейлеры Issue и - User-Visible, при User-Visible: yes — правки в оба changelog в том же - коммите. + результата доказательством не является. Зависимости уже установлены + workflow, Chromium тоже — `npm ci` выполнять не нужно. Проверь + трейлеры Issue и User-Visible, при User-Visible: yes — правки в оба + changelog в том же коммите. + + **Объём гейтов соразмерен задаче.** Прогонять весь набор на каждой + правке — не тщательность, а потеря времени: полные наборы это + предрелизный гейт (PROCESS.md §8), а не гейт ревью. + + Всегда, они дешёвые: + `npx tsc --noEmit`, `npm test`, `npm run build` со сверкой трёх + копий бандла. + + По необходимости, и «необходимость» определяется diff'ом и AC: + - браузерные смоки `demo/smoke_*.mjs` — названные в AC плюс + относящиеся к тронутым поверхностям. Их 127; прогон всех уместен + только когда задача действительно задевает всё; + - `npm run golden:verify` — если diff может изменить видимый + результат: рендер, геометрия, стили, слои; + - `python -m pytest tests_backend -q` — если тронут + `custom_components/**/*.py`; + - performance-профили — если названы в AC либо тронуты + чувствительные к перфу пути. + + Дисциплина «тест должен уметь падать» не отменяется, но применяется к + тем тестам, которые ты прогонял. + + **В комментарии обязателен перечень: какие гейты прогнал, какие нет и + почему.** Это условие честности такого сужения: непрогнанный гейт + становится видимым решением, а не молчаливым пропуском. Раздел «чего + не проверял» в документе ревью — не формальность, а главный его + раздел на коротких задачах. Ты НЕ правишь ни ТЗ, ни продуктовый код. Только оцениваешь. From 888e90450a415c1c070a087c0486cadc36badd6e Mon Sep 17 00:00:00 2001 From: Matysh Date: Thu, 13 Aug 2026 22:07:42 +0300 Subject: [PATCH 2/2] perf: make review scope and ceremony fit the size of the task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The owner's report: the process works but every stage takes a long time even on simple bugs. Two causes, and neither was the one that first comes to mind. The reviewer ran everything regardless. On #89 it installed Chromium, ran all 127 smoke files and a full golden capture — right for a task rated 10/10 for complexity, absurd for a bug about a room divider. Full suites are the pre-beta gate; the review now runs typecheck, unit and build always, and smokes, golden, pytest or performance only where the diff and the AC call for them. The price of narrowing it is honesty: the reviewer must list which gates it ran, which it did not, and why, so a skipped gate is a visible decision rather than a silent one. The reviewer also built its own environment out of model turns, with no npm cache and no browser cache, paid for from the same forty-five minutes. The workflow now installs dependencies and Chromium as ordinary cached steps, after switching to the task branch so the lockfile is the branch's own. Second, ceremony did not scale down. The light track makes a spec cheap; the new trivial track does without one — S2-analysis straight to S5-ready, no spec review, AC in the issue body. It is deliberately hard to qualify for: a bug on one surface, no new UX contract, no migration, no i18n, no perf or touch effect, three checkable AC at most, and expected behaviour already on record. Nothing left to decide is the criterion that holds the whole thing up, and it cannot be met by feeling sure. Code review is never skipped on either track. It is what stands in for testing here, so it is the one stage speed may not buy. Issue: #127 Issue: #128 User-Visible: no --- AGENTS.md | 9 +++++++++ scripts/process-gate.mjs | 12 +++++++++--- test/process-gate.test.mjs | 3 ++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9c0c5754..fd6e29ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,6 +41,15 @@ of a status and `rejected` on a closed issue. Exactly one `S*` label per open issue. [GitHub Projects (v2)](https://github.com/users/Matysh/projects/1) is a human-facing view synchronised from the labels, not the source of truth. +Two shortcuts exist for small work. `small` — the light track: the spec lives in +the issue body and its review is a comment. `trivial` — the short track: no spec +stage at all, `S2-analysis` straight to `S5-ready`, with the AC written into the +issue body first. `trivial` requires a bug confined to one surface with no new UX +contract, no migration, no i18n, no perf or touch impact, at most three checkable +AC, **and expected behaviour already on record** — nothing left to decide. Code +review is never skipped on either track; it is what stands in for testing. +`PROCESS.md` §5 and §5.1 hold the criteria. + An issue filed by an outsider is worked exactly like one of the owner's own, once the owner has decided to take it. The check sits **at the entrance**, not on every step: while an issue carries no status label it is outside the process and the diff --git a/scripts/process-gate.mjs b/scripts/process-gate.mjs index 0d3bd9b2..24af7265 100644 --- a/scripts/process-gate.mjs +++ b/scripts/process-gate.mjs @@ -67,6 +67,12 @@ const CLASS_C = [ const CHANGELOGS = ['docs/CHANGELOG.md', 'docs/CHANGELOG.ru.md']; +// Метки, при которых файла ТЗ в docs/specs/ быть не должно: на лёгком треке ТЗ +// живёт в теле issue (§5), на коротком — там же, и ревью ТЗ вообще не проводится +// (§5.1, issue #128). Офлайн эти случаи неотличимы от «ТЗ не написано», поэтому +// проверка 3 краснеет только когда метки прочитаны. +export const NO_SPEC_FILE = ['small', 'trivial']; + export const ALLOWED_STATUS = ['S5-ready', 'S6-in-progress', 'S7-code-review', 'S8-merged']; export const STRICT_STATUS = ['S5-ready', 'S6-in-progress', 'S7-code-review']; @@ -235,12 +241,12 @@ export function checkSpecs(commits, specFiles, labelsOf = null) { if (labels === null) { out.push({ level: 'warn', rule: 3, sha: c.short, - msg: `класс A по ${t}, но ТЗ docs/specs/${nn}-*.md не найдено — допустимо только при метке small`, + msg: `класс A по ${t}, но ТЗ docs/specs/${nn}-*.md не найдено — допустимо при метке small или trivial`, }); - } else if (!labels.includes('small')) { + } else if (!labels.some((l) => NO_SPEC_FILE.includes(l))) { out.push({ level: 'fail', rule: 3, sha: c.short, - msg: `класс A по ${t}: ТЗ docs/specs/${nn}-*.md нет, и метки small на issue нет — код без ТЗ`, + msg: `класс A по ${t}: ТЗ docs/specs/${nn}-*.md нет, и метки ${NO_SPEC_FILE.join(' / ')} на issue нет — код без ТЗ`, }); } } diff --git a/test/process-gate.test.mjs b/test/process-gate.test.mjs index 01556f25..761b2f13 100644 --- a/test/process-gate.test.mjs +++ b/test/process-gate.test.mjs @@ -134,8 +134,9 @@ test('a class A commit without a spec warns offline and fails with labels', () = assert.equal(offline[0].level, 'warn'); assert.equal(offline[0].rule, 3); - // С метками: small оправдывает отсутствие файла, его отсутствие — нет. + // С метками: small и trivial оправдывают отсутствие файла, их отсутствие — нет. assert.deepEqual(checkSpecs([c], [], () => ['small', 'S5-ready']), []); + assert.deepEqual(checkSpecs([c], [], () => ['trivial', 'S5-ready']), []); const strict = checkSpecs([c], [], () => ['S5-ready']); assert.equal(strict.length, 1); assert.equal(strict[0].level, 'fail');