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');