fix(process): reconcile canon and hints with the pipeline after #707–#730 (#748)

Five places still described the pipeline as it was before code that is
already in dev:

- the S3 hint of the task packet told the author to push the branch, while
  the spec lives in the issue body (§2.3, #517) and nothing is pushed
  before S5 (§11.8);
- process-gate printed «FAIL п.9 Gates: light» for a trailer nobody writes
  or reads, while §10.2 item 9 is the unimplemented release:prerelease
  verdict check. The check is removed; a contract test ties every RULES key
  to an implemented item of §10.2 and every finding number to a RULES key;
- §10.4 item 4 demanded a heredoc in run:, while #723/#730 and their tests
  demand the opposite: commit messages echo line by line into a file,
  comment and summary texts come from code;
- the ship merge comment, AUTHOR.md, REVIEWER.md and AGENTS.md named only
  the pre-beta document, though since #727 the night reads ship code first;
- the nightly publication committed «docs: ship review for nightly …
  перед бетой» with the beta step's Issue: #696. It now has its own
  subject (the document name), body and Issue: #727; the beta message is
  unchanged.

The browser-guard inventory note still said growth above 200 fails
mutation-gate --check; since #699 it is a guideline and --check warns. Its
counts now match the inventory: 205, lifecycle 90.

Issue: #748
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:
Claude
2026-10-01 16:49:55 +00:00
committed by claude[bot]
parent 146da62b0d
commit cf7a9cc4fc
14 changed files with 115 additions and 33 deletions
+30 -3
View File
@@ -16,6 +16,7 @@ import {
checkIssueStatuses,
checkReviewDocLimit,
REVIEW_DOC_LIMIT,
RULES,
checkFrozenSpecs,
checkSpecs,
clampIssueBranchRange,
@@ -153,14 +154,40 @@ test('a release commit allows only proven canonical version declarations', () =>
assert.deepEqual(rules(evaluateCommit(mixed)), [6]);
});
test('Gates: light is refused on release and generated commits', () => {
// #748 AC1: номер правила гейта — номер пункта PROCESS.md §10.2. Проверка
// «Gates: light» жила под п.9 с первого коммита гейта, а канон под п.9 держит
// нереализованное (вердикт код-ревью в release:prerelease): вывод печатал
// «FAIL п.9 Gates: light» правила, которого в каноне нет.
test('#748 AC1: номера RULES — пункты §10.2 вне «Не реализовано», и только они', () => {
const canon = readFileSync(fileURLToPath(new URL('../PROCESS.md', import.meta.url)), 'utf8').replace(/\r\n?/g, '\n');
const start = canon.indexOf('\n### 10.2 ');
const end = canon.indexOf('\n### 10.3 ', start);
assert.ok(start > 0 && end > start, 'PROCESS.md: раздел §10.2 найден');
const section = canon.slice(start, end);
const cut = section.indexOf('\nНе реализовано');
assert.ok(cut > 0, '§10.2: блок «Не реализовано» найден');
const items = (text) => [...text.matchAll(/^(\d+)\. /gm)].map((m) => Number(m[1])).sort((a, b) => a - b);
const implemented = items(section.slice(0, cut));
const pending = items(section.slice(cut));
assert.ok(implemented.length >= 8 && pending.length >= 1, `§10.2: пункты разобраны (${implemented} / ${pending})`);
const keys = Object.keys(RULES).map(Number).filter((n) => n !== 0).sort((a, b) => a - b);
assert.deepEqual(keys, implemented, 'каждый ключ RULES, кроме 0, — реализованный пункт §10.2, и наоборот');
for (const n of pending) assert.ok(!(n in RULES), `п.${n} §10.2 не реализован — ключа RULES[${n}] быть не должно`);
// Номер находки в коде — тоже ключ RULES: иначе вывод печатает «п.N undefined».
const source = readFileSync(fileURLToPath(new URL('../scripts/process-gate.mjs', import.meta.url)), 'utf8');
const used = new Set([...source.matchAll(/\b(?:fail|warn)\((\d+),|\brule: (\d+)/g)].map((m) => Number(m[1] ?? m[2])));
assert.ok(used.size >= 8, `номера находок разобраны: ${[...used]}`);
for (const n of used) assert.ok(n in RULES, `находка п.${n} без RULES[${n}]`);
});
test('#748 AC1: мёртвого трейлера «Gates: light» гейт не судит', () => {
assert.deepEqual(
rules(evaluateCommit(commit('Release v1.62.0', 'Release: v1.62.0\nGates: light', ['dist/a.js']))),
[9],
[],
);
assert.deepEqual(
rules(evaluateCommit(commit('Rebuild', 'Issue: #1\nBaseline-Reviewed: x\nGates: light', ['dist/a.js']))),
[9],
[],
);
});