From 1513a006399eca587bb8da9a95a1bd1373504afb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 23:38:19 +0000 Subject: [PATCH 1/3] feat(pm): record-recognisers reads a declared narrowing as a fourth review face MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ruling record 6203713500 (letter B): ruling C stands as a face of its own under B-prime. `REVIEW_FACES` gains `narrowing`, mapped to `CONTRACT_REVIEW_TIER` exactly, keyed on the `Clause-②: no (narrowing)` line read by `readClause2Line`. `reviewFace` takes the declaration's carrier texts as an optional third input; a path list that decides no face and carries a changeset nobody read is UNREADABLE, never the cheaper tier, and `--template/--brief-template --paths` refuse it in favour of `--face`. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01VqGhCg9wdsBXPz54uAEWg7 --- scripts/pm/record-recognisers.mjs | 201 ++++++++++++++++++++++++++---- 1 file changed, 174 insertions(+), 27 deletions(-) diff --git a/scripts/pm/record-recognisers.mjs b/scripts/pm/record-recognisers.mjs index fd2bf18e5a4..1f529f69a9c 100644 --- a/scripts/pm/record-recognisers.mjs +++ b/scripts/pm/record-recognisers.mjs @@ -102,8 +102,9 @@ import { // scope, so the edge adds no new module to this file's graph and no cycle. import { SELF_REPO_ID, governedPathsIn } from './check-governed-merges.mjs'; // How an unreadable `Served-tier:` line is quoted back — the declaration -// line's own quoting helper, one spelling for both. -import { quoteLine } from './clause2-line.mjs'; +// line's own quoting helper, one spelling for both. The narrowing face is read +// with the fleet's ONE reader of that line, imported, ⛔ never a second parser. +import { quoteLine, readClause2Line } from './clause2-line.mjs'; // ── the tier FOLLOWS THE FACE that made the review owed ───────────────────── @@ -122,55 +123,124 @@ import { quoteLine } from './clause2-line.mjs'; // ⛔ EXACT, never a floor. `servedTierStands` accepts the one NAME the face maps // to and nothing else: this regime orders no tiers (`deriveTier` refuses to // rank two either), so "at least this tier" is a reading no file here can make. +// +// ⭐ The FOURTH face (ruling record 6203713500 on #23692, letter B, maintainer +// 「B (推荐)」): ruling C (6053791774) stands as a face of its own under B′ — a +// `Clause-②: no` card that NARROWS a published accept set owes one isolated +// review at `CONTRACT_REVIEW_TIER`. The face is keyed on the one spelling +// `Clause-②: no (narrowing)`, written at the start of a line in the PR body and +// the changeset, and it is exact like the other three: a `TIER_DEFAULT` record +// does not stand on it. Records written at `TIER_DEFAULT` on narrowing-only pull +// requests before this face existed are not re-opened (the ruling's item 3). /** - * The three contract faces, in the order a mixed diff is decided in, each with + * The four contract faces, in the order a mixed diff is decided in, each with * the NAME of the tier it maps to. * * `id` the token `--template --face` takes and `reviewFace` answers with * `tierName` the ONE `Served-tier:` token a review owed on this face stands with * `words` how the face is named to a human * - * ⭐ The face is read off the PULL REQUEST'S FILE LIST — its committed tree, the - * way the queue guard already classifies governed paths — and ⛔ never off a - * seat's statement. The declaration face is what REMAINS: it is asked only of a - * review already owed, and a review owed with no schema and no governed path in - * the diff was owed by the `Clause-②: yes` line alone. + * ⭐ The face is read off the PULL REQUEST'S COMMITTED TREE, and ⛔ never off a + * seat's statement: its FILE LIST decides the schema and rules faces, the way + * the queue guard already classifies governed paths; the `Clause-②:` line its + * committed changeset carries decides the narrowing face, read by + * `readClause2Line` (`clause2-line.mjs`, the fleet's one reader of that line). + * The declaration face is what REMAINS: it is asked only of a review already + * owed, and a review owed with no schema, no governed path and no narrowing + * declared was owed by the `Clause-②: yes` line alone. */ export const REVIEW_FACES = Object.freeze([ Object.freeze({ id: 'schema', tierName: CONTRACT_REVIEW_TIER_NAME, words: 'a published schema (the clause-② suspect surface, test files excepted)' }), Object.freeze({ id: 'rules', tierName: CONTRACT_REVIEW_TIER_NAME, words: 'governed rules text (a path the governed register names)' }), - Object.freeze({ id: 'declaration', tierName: TIER_DEFAULT_NAME, words: 'the `Clause-②: yes` declaration alone (no schema and no governed path in the diff)' }), + Object.freeze({ id: 'narrowing', tierName: CONTRACT_REVIEW_TIER_NAME, words: 'a `Clause-②: no (narrowing)` declaration (a published accept set narrowed; no schema and no governed path in the diff)' }), + Object.freeze({ id: 'declaration', tierName: TIER_DEFAULT_NAME, words: 'the `Clause-②: yes` declaration alone (no schema, no governed path and no narrowing declared)' }), ]); /** Every NAME a `Served-tier:` line can stand with — one per tier the faces map to, and nothing else. */ export const REVIEW_TIER_NAMES = Object.freeze([...new Set(REVIEW_FACES.map((face) => face.tierName))]); +/** + * Is this path a CHANGESET — the committed carrier of the `Clause-②:` line? A + * `.changeset/*.md` file other than the directory's own README. Pure. + */ +function isChangesetPath(path) { + const p = String(path ?? ''); + return /^\.changeset\/[^/]+\.md$/.test(p) && p !== '.changeset/README.md'; +} + /** * Which face made this review owed, and the NAME of the tier it is therefore - * served at. Pure over the path list. + * served at. Pure over its inputs. * * ⭐ A mixed diff maps to `CONTRACT_REVIEW_TIER` on ONE hit: a schema or governed * path anywhere in the list decides it, the way one governed path governs the * whole pull request. `hits` names the paths that decided, so a reader can see * why — and, since ruling record 6184805932 on #23578 (letter C), it is also the - * SCOPE FLOOR of that review: `reviewScopeHits` below. + * SCOPE FLOOR of that review: `reviewScopeHits` below. A path-decided reading + * never consults `declaredOn`: every face after `rules` maps to a tier the paths + * already decided or to a cheaper one, so the declaration cannot move it. + * + * ⭐ Where the paths decide nothing, the DECLARATION decides (ruling record + * 6203713500, letter B), read out of `declaredOn` — the texts the `Clause-②:` + * line is written on: each committed changeset's text, and the PR body's where + * the caller holds it. A `narrowing` arm on ANY of them ⇒ the narrowing face + * (`yes (narrowing)` included: a diff that widens and narrows owes the narrowing + * face's review, as a mixed diff owes its one hit's); else a `malformed` line on + * any ⇒ UNREADABLE, because `readClause2Line` collapses a near-miss arm such as + * `(narrowed)` into `malformed` exactly so that it cannot read as no narrowing; + * else the declaration face. + * + * ⛔ A caller that hands NO declaration cannot say which of the two it is on, and + * gets the reading the committed tree still allows. A tree with a changeset in + * it carries a declaration carrier nobody read: UNREADABLE, ⛔ never the + * cheaper tier — the queue guard's rule for a pair with no path list, one level + * down. A tree with NO changeset carries no narrowing declaration at all — a + * narrowing of a published accept set moves a published package, and that + * ships a changeset — so the declaration face stands there exactly as before. + * UNREADABLE answers `face: null` and `tierName: null`, which `servedTierStands` + * stands nothing on, and a `reason`. * * `repo` selects the governed register's rows, as `governedPathsIn` requires; * this file runs in `objectstack-ai/objectstack`, so that is the default. * * @param {string[]} paths the pull request's changed paths (its committed tree) - * @returns {{ face: 'schema'|'rules'|'declaration', tierName: string, words: string, hits: { schema: string[], rules: string[] } }} + * @param {{ declaredOn?: string[] }} [declaration] the texts its `Clause-②:` line is read from + * @returns {{ face: 'schema'|'rules'|'narrowing'|'declaration', tierName: string, words: string, hits: { schema: string[], rules: string[] } } + * | { face: null, tierName: null, words: string, hits: { schema: string[], rules: string[] }, reason: string }} */ -export function reviewFace(paths, repo = SELF_REPO_ID) { +export function reviewFace(paths, repo = SELF_REPO_ID, { declaredOn } = {}) { const list = (Array.isArray(paths) ? paths : []).filter((p) => typeof p === 'string' && p !== ''); // ⭐ No mandate rows are passed: the question is the SUSPECT surface alone, and // an empty mandate table cannot make `deriveTier` throw on an ambiguity. const schema = [...new Set(deriveTier(list, [], SUSPECT_TIER_GLOBS).suspects.map((s) => s.path))]; const rules = governedPathsIn(list, repo).flatMap((surface) => surface.files); - const id = schema.length > 0 ? 'schema' : rules.length > 0 ? 'rules' : 'declaration'; - const face = REVIEW_FACES.find((f) => f.id === id); - return { face: face.id, tierName: face.tierName, words: face.words, hits: { schema, rules } }; + const hits = { schema, rules }; + const unreadable = (reason) => ({ face: null, tierName: null, words: 'no face — UNREADABLE', hits, reason }); + // ⛔ A face this table does not hold reads UNREADABLE, never a neighbouring face. + const named = (id) => { + const face = reviewFaceById(id); + return face === null ? unreadable(`REVIEW_FACES holds no \`${id}\` face`) : { face: face.id, tierName: face.tierName, words: face.words, hits }; + }; + if (schema.length > 0) return named('schema'); + if (rules.length > 0) return named('rules'); + const texts = (Array.isArray(declaredOn) ? declaredOn : []).filter((text) => typeof text === 'string'); + if (texts.length === 0) { + const carrier = list.find(isChangesetPath); + return carrier === undefined + ? named('declaration') + : unreadable( + `the paths decide no face and the committed tree carries ${carrier}, whose \`Clause-②:\` line decides between ` + + 'the narrowing face and the declaration face, but no declaration was handed to read it', + ); + } + const readings = texts.map((text) => readClause2Line(text)); + if (readings.some((r) => r?.kind === 'declared' && r.arm === 'narrowing')) return named('narrowing'); + const malformed = readings.find((r) => r?.kind === 'malformed'); + if (malformed !== undefined) { + return unreadable(`a \`Clause-②:\` line reads malformed (${malformed.line}), so whether it declares a narrowing is unknown`); + } + return named('declaration'); } /** The face a `--face` argument names, or null — the CLI's half of the same table. */ @@ -193,7 +263,11 @@ export function reviewFaceById(id) { * * ⭐ Read off `reviewFace`, never a second classification: the same function the * queue guard decides the face with, so the floor cannot name a path the face - * did not count, nor miss one it did. Empty exactly on the declaration face. + * did not count, nor miss one it did. Empty on every face the paths did not + * decide. ⭐ The narrowing face is at tier with NO path hit: its declaration is + * the pull request's own and names no path, so its floor is every path of the + * diff outside the prose faces — stated in the brief and on the `Scope:` line, + * which the reviewer fills, since no path list read here can single them out. * * @param {string[]} paths the pull request's changed paths (its committed tree) * @returns {string[]} @@ -975,9 +1049,10 @@ export function contractReviewTemplateLines(values = {}) { RECORD_TEMPLATE_FENCE_END, '', " · Served-tier the NAME of the tier the review's FACE maps to, ⛔ never a model id, and EXACT:", - " the PR's file list decides it. A published schema or a governed path anywhere in it ⇒", - ` ${CONTRACT_REVIEW_TIER_NAME} (--face schema or rules); neither, the review owed by the`, - ` \`Clause-②: yes\` line alone ⇒ ${TIER_DEFAULT_NAME} (--face declaration).`, + " the PR's committed tree decides it. A published schema or a governed path anywhere in it ⇒", + ` ${CONTRACT_REVIEW_TIER_NAME} (--face schema or rules); neither, a \`Clause-②: no (narrowing)\``, + ` line on the PR body and changeset ⇒ ${CONTRACT_REVIEW_TIER_NAME} too (--face narrowing); else`, + ` the review owed by the \`Clause-②: yes\` line alone ⇒ ${TIER_DEFAULT_NAME} (--face declaration).`, faceLine, ' An at-tier/total stamp control may precede it — 75/75, then the constant.', ' · Head-sha the head you reviewed, 7 to 40 hex in a span of ITS OWN; a span holding', @@ -991,7 +1066,8 @@ export function contractReviewTemplateLines(values = {}) { ' file those hunks make a claim about, read in the cited code (`+ none` if none); a rule', ' and the gate that enforces it are one change, read as one. Widen it freely, ⛔ never', ` below \`hits\`. The rest of a mixed diff is the lane seat's ACCEPT at ${TIER_DEFAULT_NAME},`, - ' named there; Tier H approval and the queue guard stay whole-PR.', + ' named there; Tier H approval and the queue guard stay whole-PR. On the narrowing', + ' face the floor is every path of the diff outside the prose faces: no path is a remainder.', ' · Implemented-by the identity that produced the diff, read off the implementation claim:', " a mode:subagent dev's BRANCH, a mode:remote dev's session id.", ' · Reviewed-by the session that RENDERS or ADOPTS the verdict — a session only. Prose', @@ -1012,6 +1088,13 @@ export function contractReviewTemplateLines(values = {}) { * seat print one face's tier beside another face's scope. An empty list is no * list, and is refused rather than read as the declaration face. * + * ⛔ A list `reviewFace` reads UNREADABLE is refused too, with its reason: the + * paths decide no face and the committed tree carries a changeset whose + * `Clause-②:` line decides between the narrowing and the declaration face, + * which no path list can read. Printing the declaration face there is the + * cheaper tier for a narrowing, which is how this command answered a + * narrowing-only pull request before the narrowing face existed. + * * @returns {{ face: object|null, scopeHits: string[]|undefined }} */ function templateFace({ face: faceId, paths }) { @@ -1022,7 +1105,14 @@ function templateFace({ face: faceId, paths }) { if (!Array.isArray(paths) || paths.length === 0) { throw new Error('record-recognisers: `paths` names no path — the face and the scope are read off a non-empty file list'); } - return { face: reviewFaceById(reviewFace(paths).face), scopeHits: reviewScopeHits(paths) }; + const read = reviewFace(paths); + if (read.face === null) { + throw new Error( + `record-recognisers: ${read.reason} — read that \`Clause-②:\` line and pass \`--face narrowing\` ` + + 'when it carries the `(narrowing)` arm, else `--face declaration`, in place of `--paths`', + ); + } + return { face: reviewFaceById(read.face), scopeHits: reviewScopeHits(paths) }; } const face = faceId === undefined ? null : reviewFaceById(faceId); if (faceId !== undefined && face === null) { @@ -1067,6 +1157,10 @@ export const BRIEF_TEMPLATE_PLACEHOLDERS = Object.freeze({ * then the record command is `--template --paths` over those hits, which reads * back the same face and the same floor — plus the files those hunks claim * about; widen freely, ⛔ never below `hits`. The declaration face states none. + * The narrowing face is at tier with no `hits` (ruling record 6203713500, + * letter B): its floor is every path of the diff outside the prose faces, and + * the brief says so instead of handing the reviewer an empty floor and the whole + * diff as the lane seat's remainder. */ export function contractReviewBriefLines(values = {}) { const { pr, card, headSha, scratchpad } = { ...BRIEF_TEMPLATE_PLACEHOLDERS, ...values }; @@ -1079,7 +1173,12 @@ export function contractReviewBriefLines(values = {}) { ? ` --paths ${scopeHits.join(' ')}` : face === null ? '' : ` --face ${face.id}`; const templateCommand = `node scripts/pm/record-recognisers.mjs --template${templateArgs}`; - const scopeLines = !atTier ? [] : [ + const scopeLines = !atTier ? [] : face?.id === 'narrowing' ? [ + "Scope — the narrowing is declared of the whole PR, never of a path: the floor is EVERY path of the diff outside the", + ` prose faces (${RECORD_SCOPE_PLACEHOLDERS.hits}), plus every file those hunks make a claim about, read in the cited code.`, + " Widen it freely, ⛔ never below that floor; the record's `Scope:` line lists both. No path is the lane seat's remainder.", + '', + ] : [ `Scope — the paths \`reviewFace\` returns as \`hits\` on this PR's file list: ${scopeHits?.join(', ') ?? RECORD_SCOPE_PLACEHOLDERS.hits};`, ' plus every file those hunks make a claim about, read in the cited code — a rule and the gate that enforces it', " are one change, read as one. Widen it freely, ⛔ never below `hits`; the record's `Scope:` line lists both.", @@ -1264,13 +1363,15 @@ const SELF_TEST_BATTERIES = Object.freeze({ '⭐ B′ with C: the review tier FOLLOWS THE FACE that made it owed — exact, never a floor': 23, '⭐ B′ with C: a PROSE-ONLY head move keeps the record, decided on the COMMITTED trees like a regeneration': 6, '⭐ letter C: the at-tier review of a mixed diff is SCOPED — `hits` plus what they claim, on the `Scope:` line': 18, + '⭐ letter B: a declared NARROWING is a face of its own at CONTRACT_REVIEW_TIER — keyed on the `Clause-②:` line, exact': 17, }); // DELETING an entry silences that battery's floor exactly as effectively as // zeroing it, so the roster's own size is pinned too. // Raised 8 → 10 with the two B′-with-C batteries (ruling record 6159870311): two batteries, two rows. // Raised 10 → 11 with the letter-C scope battery (ruling record 6184805932): one battery, one row. -const SELF_TEST_BATTERY_FLOOR = 11; +// Raised 11 → 12 with the letter-B narrowing-face battery (ruling record 6203713500): one battery, one row. +const SELF_TEST_BATTERY_FLOOR = 12; // The key an assertion is filed under when no battery is open. It is not a // declared battery, so it reds by the same set difference rather than silently @@ -1449,7 +1550,7 @@ export async function selfTest() { t('⭐ a MIXED diff maps to CONTRACT_REVIEW_TIER on ONE hit, and names the path that decided', reviewFace([...DECL_PATHS, SCHEMA_PATH]).tierName === CONTRACT_REVIEW_TIER_NAME && reviewFace([...DECL_PATHS, 'AGENTS.md']).tierName === CONTRACT_REVIEW_TIER_NAME && reviewFace([...DECL_PATHS, 'AGENTS.md']).hits.rules.join() === 'AGENTS.md'); t('…a schema hit outranks a governed one in the NAMING only — both faces map to the same tier', reviewFace([SCHEMA_PATH, 'AGENTS.md']).face === 'schema' && reviewFace([SCHEMA_PATH, 'AGENTS.md']).tierName === reviewFace(['AGENTS.md']).tierName); t('⛔ a spec TEST file is not a published schema — the suspect surface\'s own exception, imported, not respelled', reviewFace([SCHEMA_TEST_PATH]).face === 'declaration'); - t('the faces are the closed set of three, and they map to exactly two NAMES', REVIEW_FACES.map((f) => f.id).join() === 'schema,rules,declaration' && REVIEW_TIER_NAMES.join() === `${CONTRACT_REVIEW_TIER_NAME},${TIER_DEFAULT_NAME}`); + t('the faces are the closed set of four, in decision order, and they map to exactly two NAMES', REVIEW_FACES.map((f) => f.id).join() === 'schema,rules,narrowing,declaration' && REVIEW_TIER_NAMES.join() === `${CONTRACT_REVIEW_TIER_NAME},${TIER_DEFAULT_NAME}`); t('⭐ a TIER_DEFAULT record STANDS on a declaration-only pull request', servedTierStands(AT(TIER_DEFAULT_NAME), reviewFace(DECL_PATHS).tierName)); t('⛔ …and does NOT stand on a schema pull request, nor on a governed one', !servedTierStands(AT(TIER_DEFAULT_NAME), reviewFace([SCHEMA_PATH]).tierName) && !servedTierStands(AT(TIER_DEFAULT_NAME), reviewFace([RULES_PATH]).tierName)); t('⭐ a CONTRACT_REVIEW_TIER record stands on the schema and governed faces', servedTierStands(AT(CONTRACT_REVIEW_TIER_NAME), reviewFace([SCHEMA_PATH]).tierName) && servedTierStands(AT(CONTRACT_REVIEW_TIER_NAME), reviewFace([RULES_PATH]).tierName)); @@ -1502,6 +1603,41 @@ export async function selfTest() { t('⛔ `--paths` naming no path, or beside `--face` in either order, is REFUSED with the usage, exit 1', [['--template', '--paths'], ['--template', '--paths', RULES_PATH, '--face', 'rules'], ['--brief-template', '--face', 'rules', '--paths', RULES_PATH], ['--template', '--paths', '']].every((args) => { const r = faceCli(args); return r.status === 1 && says(r.stderr, 'usage:'); })); t('…and the usage names the flag, the face it reads and the line it fills', ['--paths PATH...', '`reviewFace`', '`Scope:`'].every((s) => usageLines().join('\n').includes(s))); + // -- letter B: a declared NARROWING is a face of its own ------------------- + // + // Ruling record 6203713500 on #23692 (letter B, maintainer 「B (推荐)」): ruling + // C stands as a fourth face under B′ — a `Clause-②: no (narrowing)` line puts + // the review at CONTRACT_REVIEW_TIER, exactly. The specimen path list is the + // shape that met it: one changeset, one non-schema source file, one test — no + // schema and no governed path, so the paths decide nothing and the line does. + battery('⭐ letter B: a declared NARROWING is a face of its own at CONTRACT_REVIEW_TIER — keyed on the `Clause-②:` line, exact'); + const NARROW_PATHS = [ + ['.changeset', 'overlay-package-mismatch.md'].join('/'), + ['packages', 'metadata-protocol', 'src', 'protocol.ts'].join('/'), + ['packages', 'runtime', 'src', 'overlay-promotion.test.ts'].join('/'), + ]; + const carrier = (line) => `---\n"@objectstack/metadata-protocol": minor\n---\n\n${line}\n\nTwo promotion doors now refuse what they accepted.\n`; + const NARROW_ON = { declaredOn: [carrier('Clause-②: no (narrowing)')] }; + const faceOn = (line, paths = NARROW_PATHS) => reviewFace(paths, undefined, { declaredOn: [carrier(line)] }); + t('⭐ a narrowing-only path list with `Clause-②: no (narrowing)` on its changeset ⇒ the narrowing face, CONTRACT_REVIEW_TIER', (() => { const f = reviewFace(NARROW_PATHS, undefined, NARROW_ON); return f.face === 'narrowing' && f.tierName === CONTRACT_REVIEW_TIER_NAME && f.hits.schema.length === 0 && f.hits.rules.length === 0; })()); + t('⭐ …and a CONTRACT_REVIEW_TIER record STANDS on that pull request — the ruling\'s first half', servedTierStands(AT(CONTRACT_REVIEW_TIER_NAME), reviewFace(NARROW_PATHS, undefined, NARROW_ON).tierName)); + t('⛔ …and a TIER_DEFAULT record does NOT — exact, the ruling\'s second half', !servedTierStands(AT(TIER_DEFAULT_NAME), reviewFace(NARROW_PATHS, undefined, NARROW_ON).tierName)); + t('⭐ the line read off the PR BODY counts as well — the ruling writes the one spelling on both carriers', reviewFace(NARROW_PATHS, undefined, { declaredOn: [carrier('Clause-②: no'), 'Fixes #1\nClause-②: no (narrowing)\n'] }).face === 'narrowing'); + t('⭐ `Clause-②: yes` still maps to the declaration face and TIER_DEFAULT, and only that NAME stands on it', (() => { const f = faceOn('Clause-②: yes'); return f.face === 'declaration' && f.tierName === TIER_DEFAULT_NAME && servedTierStands(AT(TIER_DEFAULT_NAME), f.tierName) && !servedTierStands(AT(CONTRACT_REVIEW_TIER_NAME), f.tierName); })()); + t('…and so do a bare `no`, a `yes (widening)` and a carrier with no line at all — the declaration face is what REMAINS', ['Clause-②: no', 'Clause-②: yes (widening)', 'no declaration here'].every((line) => faceOn(line).face === 'declaration')); + t('⭐ `yes (narrowing)` is the narrowing face — a diff that widens and narrows owes the narrowing review, as a mixed diff owes its hit', faceOn('Clause-②: yes (narrowing)').face === 'narrowing'); + t('⛔ a near-miss arm reads malformed and the face UNREADABLE — standing NO name, never a narrowing read as none', ['Clause-②: no (narrowed)', 'Clause-②: no (Narrowing)', 'Clause-②: no (widening)'].every((line) => { const f = faceOn(line); return f.face === null && f.tierName === null && /malformed/.test(f.reason) && !servedTierStands(AT(TIER_DEFAULT_NAME), f.tierName) && !servedTierStands(AT(CONTRACT_REVIEW_TIER_NAME), f.tierName); })); + t('⛔ ONE reader: a line that QUOTES the spelling is no declaration, exactly as `readClause2Line` reads it', faceOn('- `Clause-②: no (narrowing)` is the spelling the ruling fixed').face === 'declaration'); + t('⛔ NO declaration handed on a tree that carries a changeset ⇒ UNREADABLE, never the cheaper tier — and the reason names the carrier', (() => { const f = reviewFace(NARROW_PATHS); return f.face === null && f.tierName === null && f.reason.includes(NARROW_PATHS[0]) && !servedTierStands(AT(TIER_DEFAULT_NAME), f.tierName); })()); + t('⛔ …an EMPTY `declaredOn`, or one holding no text, is no declaration either', [[], [null, 7]].every((declaredOn) => reviewFace(NARROW_PATHS, undefined, { declaredOn }).face === null)); + t('⭐ a tree with NO changeset and no declaration keeps the declaration face, exactly as the two-argument call read it', JSON.stringify(reviewFace(DECL_PATHS)) === JSON.stringify({ face: 'declaration', tierName: TIER_DEFAULT_NAME, words: reviewFaceById('declaration').words, hits: { schema: [], rules: [] } })); + t('⭐ a schema or governed path decides BEFORE the line is read — byte-identical with a narrowing, a malformed line or none', [[SCHEMA_PATH], [RULES_PATH], [...NARROW_PATHS, RULES_PATH]].every((list) => ['Clause-②: no (narrowing)', 'Clause-②: no (narrowed)'].every((line) => JSON.stringify(reviewFace(list, undefined, { declaredOn: [carrier(line)] })) === JSON.stringify(reviewFace(list))))); + const NARROW_TPL = contractReviewTemplateLines({ face: 'narrowing' }).join('\n'); + t('⭐ `--face narrowing`\'s record carries CONTRACT_REVIEW_TIER and the `Scope:` line, round-trips through the locator and STANDS on the narrowing face', (() => { const r = declOn(NARROW_TPL); return readServedTier(NARROW_TPL).value === CONTRACT_REVIEW_TIER_NAME && scopeLineOf(NARROW_TPL).length === 1 && r.state === 'found' && servedTierStands(r.served, reviewFace(NARROW_PATHS, undefined, NARROW_ON).tierName) && !servedTierStands(r.served, faceOn('Clause-②: yes').tierName); })()); + t('⭐ `--template --face narrowing` prints exactly that template and exits 0', (() => { const r = faceCli(['--template', '--face', 'narrowing']); return r.status === 0 && r.stdout === `${NARROW_TPL}\n`; })()); + t('⭐ the narrowing brief reviews at CONTRACT_REVIEW_TIER, hands `--face narrowing`, and floors the WHOLE diff — no lane remainder', (() => { const b = contractReviewBriefLines({ face: 'narrowing' }).join('\n'); return b.includes(`You review at \`${CONTRACT_REVIEW_TIER_NAME}\``) && b.includes('--template --face narrowing`') && says(b, 'EVERY path of the diff outside the') && !says(b, `ACCEPT at \`${TIER_DEFAULT_NAME}\``) && !isModelIdentifierToken(b); })()); + t('⛔ `--paths` over that list is REFUSED, exit 1, naming `--face narrowing` — the brief that once answered TIER_DEFAULT there', ['--template', '--brief-template'].every((flag) => { const r = faceCli([flag, '--paths', ...NARROW_PATHS]); return r.status === 1 && r.stdout === '' && says(r.stderr, 'usage:') && says(r.stderr, '--face narrowing') && says(r.stderr, NARROW_PATHS[0]); })); + // -- the 2026-09-20 ruling: a PURE REGENERATION keeps the record ----------- battery('⭐ the 2026-09-20 ruling: a pure-regeneration head move KEEPS the record, decided on the COMMITTED trees'); const NEW_HEAD = 'e1ae0257'; // the head a whole-tree regeneration produced, as the board abbreviated it. @@ -1800,7 +1936,8 @@ export async function selfTest() { + 'recognition dialects, the unsigned and newest-governs readings and the two gaps; the head sha in a span ' + 'of its own; the `Served-tier:` reading — at tier, below, missing, unreadable, the identifier refused by ' + 'value and by shape, the stamp control and the measured continuation spelling; the review tier read off ' - + 'the face — schema, governed rules text, the declaration alone — exact in both directions; the at-tier scope ' + + 'the face — schema, governed rules text, a declared narrowing read off the `Clause-②:` line, the declaration alone — ' + + 'exact in both directions, an unread or malformed declaration UNREADABLE; the at-tier scope ' + 'floor read off `hits` onto the `Scope:` line and into the brief, `--paths` included; the pure-regeneration carry and the prose-only carry decided on committed trees in ' + 'every direction; the copyable template, per face, driven back through the ' + 'locator and printed by `--template`; the `Local-runs:` reading — none, one named probe, absent not re-judged, ' @@ -1833,6 +1970,7 @@ export function usageLines() { ` the record's \`Served-tier:\` names that tier. Without it: ${CONTRACT_REVIEW_TIER_NAME}.`, " --paths PATH... the PR's changed paths, in place of --face: `reviewFace` reads the face off them, and the", " at-tier record's `Scope:` line and the brief print its `hits` filled in. Not with --face.", + " Refused when the paths decide no face and one is a changeset: its `Clause-②:` line decides.", " --self-test run this file's own battery, offline -- no board is read", ' --help, -h print this text and exit 0', '', @@ -1885,7 +2023,16 @@ if (isEntrypoint(import.meta.url)) { // ⛔ `--face` takes one of the table's ids or the whole line is refused below — a face this file does // not map would print a tier no reader accepts. const asked = templateArgsOf(argv.slice(1)); - const lines = argv[0] === '--template' ? contractReviewTemplateLines(asked) : contractReviewBriefLines(asked); + let lines; + try { + lines = argv[0] === '--template' ? contractReviewTemplateLines(asked) : contractReviewBriefLines(asked); + } catch (error) { + // A path list the face cannot be read off is refused like any other bad line: usage, the reason, exit 1. + for (const line of usageLines()) console.error(line); + console.error(''); + console.error(String(error?.message ?? error)); + process.exit(1); + } for (const line of lines) console.log(line); process.exit(0); } else if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) { From d0634c3f0c0d1edfc94c3fa0e34cf79b23adc292 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 23:41:15 +0000 Subject: [PATCH 2/3] docs(pm-dispatch): contract-review names the narrowing declaration as its fourth face MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ruling record 6203713500 (letter B): the three contract faces become four. A `Clause-②: no (narrowing)` line at line start in the PR body and the changeset puts the review at `CONTRACT_REVIEW_TIER`, its floor the whole diff outside the prose faces; the face reads the committed file list and the declaration line. Net +1 line (47 to 48, ceiling 60 unchanged). Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01VqGhCg9wdsBXPz54uAEWg7 --- .../pm-dispatch/references/contract-review.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.claude/skills/pm-dispatch/references/contract-review.md b/.claude/skills/pm-dispatch/references/contract-review.md index a5330375de6..2b8921d6701 100644 --- a/.claude/skills/pm-dispatch/references/contract-review.md +++ b/.claude/skills/pm-dispatch/references/contract-review.md @@ -5,18 +5,19 @@ ## 复核归属与资格(按面) - 欠不欠按面判 ⛔ 不按车道:diff 或认领申报碰下列任一面即欠达档复核,交付后当轮完成。 -- 复核面 = 契约三面:`Clause-②: yes` 申报、已发布 schema(`packages/spec/src/**` 非测试)、governed - 规则文本(统一定义见 SKILL.md);`.changeset`/CHANGELOG、`content/docs/**`、`apps/docs/**` 散文面 - ⛔ 不在其列:派发席按本席档在 ACCEPT 逐句对照 diff 核,ACCEPT 点名所核句。 -- 档随面定:已发布 schema 或 governed 规则文本 ⇒ `CONTRACT_REVIEW_TIER`,混合 diff 一处命中即此档; - 仅凭 `Clause-②: yes` 申报而欠 ⇒ `TIER_DEFAULT`,由车道席位作复核记录;档恰等不作下限。 +- 复核面 = 契约四面:`Clause-②: yes` 申报、收窄申报、已发布 schema(`packages/spec/src/**` 非测 + 试)、governed 规则文本(统一定义见 SKILL.md);`.changeset`/CHANGELOG、`content/docs/**`、`apps/docs/**` 散 + 文面 ⛔ 不在其列:派发席按本席档在 ACCEPT 逐句对照 diff 核,ACCEPT 点名所核句。 +- 档随面定:已发布 schema 或 governed 规则文本 ⇒ `CONTRACT_REVIEW_TIER`,混合 diff 一处命中即此 + 档;PR 正文与 changeset 行首 `Clause-②: no (narrowing)` 申报的收窄亦此档,范围为散文面外全 + diff;仅凭 `Clause-②: yes` 申报而欠 ⇒ `TIER_DEFAULT`,由车道席位作复核记录;档恰等不作下限。 - 混合 diff 的达档复核范围 = `reviewFace` 的 `hits` 路径 + 其 hunk 所断言的文件(读被引代码); 规则与执行它的门禁同属一改者一并读;记录 `Scope:` 行列两者,可放宽、⛔ 不窄于 `hits`。 - 余部归车道席位 `TIER_DEFAULT` 的 ACCEPT,同单独开 PR 并点名;Tier H 人批与队列守卫仍整张 PR。 -- 面读 PR 已提交树的文件清单,同队列守卫判 governed 路径;⛔ 不读席位自述。 +- 面读 PR 已提交树的文件清单(同队列守卫判 governed 路径)与申报行;⛔ 不读席位自述。 - 已发布面以包的 `exports` 映射为准:从入口类型图不可达、且 `exports` 不可寻址的 `.d.ts` 是 出货字节,⛔ 不是已发布接受集,改它不触发 `Clause-②: yes`;legacy node10 深引用在契约之外。 -- 三面皆不碰 ⇒ CI 加席位自读 ⛔ 不起第二个 agent;谁跑 = 派发席,达档者席内审。 +- 四面皆不碰 ⇒ CI 加席位自读 ⛔ 不起第二个 agent;谁跑 = 派发席,达档者席内审。 - 复核记录 = 一条评论落 PR 或卡,席内与子代理同形;散文、dev 自评、`os-dev-report` 恒不算。 - 同形 = `## Contract review` 题头、所审 head sha 独占码段、①②③ 逐项、独立性对、PASS/FAIL。 - 同形含首行 `Served-tier:`:值写该面所定档的常量名 `CONTRACT_REVIEW_TIER` 或 `TIER_DEFAULT`; From 4c15f575b1ea6c1495f443aff575c58bb6e193c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 23:44:08 +0000 Subject: [PATCH 3/3] test(pm): narrowing-face cases go red by name, never crash the battery Without the `narrowing` row the template and brief calls refuse; each case now turns that refusal into its own red so an ablation reads the case names. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01VqGhCg9wdsBXPz54uAEWg7 --- scripts/pm/record-recognisers.mjs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/pm/record-recognisers.mjs b/scripts/pm/record-recognisers.mjs index 1f529f69a9c..e0e82d93bad 100644 --- a/scripts/pm/record-recognisers.mjs +++ b/scripts/pm/record-recognisers.mjs @@ -1632,10 +1632,13 @@ export async function selfTest() { t('⛔ …an EMPTY `declaredOn`, or one holding no text, is no declaration either', [[], [null, 7]].every((declaredOn) => reviewFace(NARROW_PATHS, undefined, { declaredOn }).face === null)); t('⭐ a tree with NO changeset and no declaration keeps the declaration face, exactly as the two-argument call read it', JSON.stringify(reviewFace(DECL_PATHS)) === JSON.stringify({ face: 'declaration', tierName: TIER_DEFAULT_NAME, words: reviewFaceById('declaration').words, hits: { schema: [], rules: [] } })); t('⭐ a schema or governed path decides BEFORE the line is read — byte-identical with a narrowing, a malformed line or none', [[SCHEMA_PATH], [RULES_PATH], [...NARROW_PATHS, RULES_PATH]].every((list) => ['Clause-②: no (narrowing)', 'Clause-②: no (narrowed)'].every((line) => JSON.stringify(reviewFace(list, undefined, { declaredOn: [carrier(line)] })) === JSON.stringify(reviewFace(list))))); - const NARROW_TPL = contractReviewTemplateLines({ face: 'narrowing' }).join('\n'); - t('⭐ `--face narrowing`\'s record carries CONTRACT_REVIEW_TIER and the `Scope:` line, round-trips through the locator and STANDS on the narrowing face', (() => { const r = declOn(NARROW_TPL); return readServedTier(NARROW_TPL).value === CONTRACT_REVIEW_TIER_NAME && scopeLineOf(NARROW_TPL).length === 1 && r.state === 'found' && servedTierStands(r.served, reviewFace(NARROW_PATHS, undefined, NARROW_ON).tierName) && !servedTierStands(r.served, faceOn('Clause-②: yes').tierName); })()); - t('⭐ `--template --face narrowing` prints exactly that template and exits 0', (() => { const r = faceCli(['--template', '--face', 'narrowing']); return r.status === 0 && r.stdout === `${NARROW_TPL}\n`; })()); - t('⭐ the narrowing brief reviews at CONTRACT_REVIEW_TIER, hands `--face narrowing`, and floors the WHOLE diff — no lane remainder', (() => { const b = contractReviewBriefLines({ face: 'narrowing' }).join('\n'); return b.includes(`You review at \`${CONTRACT_REVIEW_TIER_NAME}\``) && b.includes('--template --face narrowing`') && says(b, 'EVERY path of the diff outside the') && !says(b, `ACCEPT at \`${TIER_DEFAULT_NAME}\``) && !isModelIdentifierToken(b); })()); + // ⛔ A refusal inside a case is that case's red, never the battery's crash: without the row these + // calls throw, and the run must still NAME every case that went red (the ablation reads them). + const safely = (fn) => { try { return fn(); } catch { return false; } }; + const NARROW_TPL = safely(() => contractReviewTemplateLines({ face: 'narrowing' }).join('\n')) || ''; + t('⭐ `--face narrowing`\'s record carries CONTRACT_REVIEW_TIER and the `Scope:` line, round-trips through the locator and STANDS on the narrowing face', NARROW_TPL !== '' && (() => { const r = declOn(NARROW_TPL); return readServedTier(NARROW_TPL).value === CONTRACT_REVIEW_TIER_NAME && scopeLineOf(NARROW_TPL).length === 1 && r.state === 'found' && servedTierStands(r.served, reviewFace(NARROW_PATHS, undefined, NARROW_ON).tierName) && !servedTierStands(r.served, faceOn('Clause-②: yes').tierName); })()); + t('⭐ `--template --face narrowing` prints exactly that template and exits 0', (() => { const r = faceCli(['--template', '--face', 'narrowing']); return NARROW_TPL !== '' && r.status === 0 && r.stdout === `${NARROW_TPL}\n`; })()); + t('⭐ the narrowing brief reviews at CONTRACT_REVIEW_TIER, hands `--face narrowing`, and floors the WHOLE diff — no lane remainder', safely(() => { const b = contractReviewBriefLines({ face: 'narrowing' }).join('\n'); return b.includes(`You review at \`${CONTRACT_REVIEW_TIER_NAME}\``) && b.includes('--template --face narrowing`') && says(b, 'EVERY path of the diff outside the') && !says(b, `ACCEPT at \`${TIER_DEFAULT_NAME}\``) && !isModelIdentifierToken(b); })); t('⛔ `--paths` over that list is REFUSED, exit 1, naming `--face narrowing` — the brief that once answered TIER_DEFAULT there', ['--template', '--brief-template'].every((flag) => { const r = faceCli([flag, '--paths', ...NARROW_PATHS]); return r.status === 1 && r.stdout === '' && says(r.stderr, 'usage:') && says(r.stderr, '--face narrowing') && says(r.stderr, NARROW_PATHS[0]); })); // -- the 2026-09-20 ruling: a PURE REGENERATION keeps the record -----------