Skip to content

Commit 277884e

Browse files
committed
test(pm): pin the zero-branch claim state in both self-tests
H60's battery replays the measured inline spellings (2026-09-08/09 on one card, and the four a second seat's template produced inside three seconds on 2026-09-12) and pins that the branch reader's accept set did NOT move: the state is the fix, so the next unrecognised spelling is loud on its first occurrence instead of being bought one widening at a time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
1 parent 28352ce commit 277884e

2 files changed

Lines changed: 191 additions & 4 deletions

File tree

‎scripts/pm/check-clause2-carriers.mjs‎

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ import {
568568
deliveryEvidence,
569569
deliveryEvidenceNote,
570570
claimGovernance,
571+
claimedBranches,
571572
governingClaim,
572573
isGateSemanticLabel,
573574
labelNames,
@@ -620,15 +621,16 @@ const SELF_TEST_BATTERIES = Object.freeze({
620621
'the exit register is distinct in every direction it must be': 6,
621622
'the argv contract and the board provenance (#16623)': 42,
622623
'#17366: the correction comment — the self-solvable exit, and the three things it is not': 65,
624+
'#17149: a claim that parses to ZERO branches — malformed, never absent': 26,
623625
});
624626

625627
// DELETING an entry silences that battery's floor exactly as effectively as
626628
// zeroing it, so the roster's own size is pinned too.
627629
// Raised by exactly the one battery #16304 adds, again by exactly the one
628630
// #17302 adds, and again by exactly the one #17366 adds, so the roster's
629631
// existing slack is preserved rather than tightened or loosened as a side
630-
// effect.
631-
const SELF_TEST_BATTERY_FLOOR = 17;
632+
// effect, and once more by the one #17149 adds.
633+
const SELF_TEST_BATTERY_FLOOR = 18;
632634

633635
// The key an assertion is filed under when no battery is open. It is not a
634636
// declared battery, so it reds by the same set difference rather than silently
@@ -4322,6 +4324,101 @@ export function selfTest() {
43224324
t('⛔ no new exit code was minted for the correction reading', new Set([EXIT_OK, EXIT_USAGE, EXIT_INCOMPLETE, EXIT_PREREQUISITE_NOT_MET, EXIT_PAIR_ADVERSE]).size === 5);
43234325
t('…and a repaired pair answers with the SAME code a never-broken one does', pairRows(repairedPair).length === pairRows(pair({ cardComments: [CLAIMED('Clause-②: no')] })).length);
43244326

4327+
// -- #17149: a claim that parses to ZERO branches ---------------------------
4328+
//
4329+
// The declaration limb's carrier is the GOVERNING claim, and governance is
4330+
// resolved by a branch parse. A newest claim comment naming no parseable
4331+
// branch used to be discarded, and this file then read a declaration off a
4332+
// comment the seat had already replaced — silently, and in BOTH directions:
4333+
// wrong when the two disagreed, and right-for-the-wrong-reason when they
4334+
// agreed. The fixtures below are the MEASURED bodies, quoted rather than
4335+
// paraphrased, so a future widening of the branch reader cannot make this
4336+
// battery pass by accident.
4337+
battery('#17149: a claim that parses to ZERO branches — malformed, never absent');
4338+
// Card #16322, comments 5593513389 (2026-09-08T23:46:51Z) and 5594909614
4339+
// (2026-09-09T02:35:21Z) as posted: the branch named INSIDE the `Claim:`
4340+
// sentence, with no `Branch:` line anywhere. The two declare OPPOSITE values,
4341+
// which is what made the fallback visible at all.
4342+
const INLINE_OLD = {
4343+
id: 5593513389,
4344+
created_at: '2026-09-08T23:46:51Z',
4345+
body:
4346+
'Claim: session_01ADLdAs2pVcH17h9tZKWMBg — branch `claude/issue-16322-analytics-daterange-closed-vocabulary-drivers`\n\n' +
4347+
'Clause-②: no\n',
4348+
};
4349+
const INLINE_NEW = {
4350+
id: 5594909614,
4351+
created_at: '2026-09-09T02:35:21Z',
4352+
body:
4353+
'Claim: session_01ADLdAs2pVcH17h9tZKWMBg — branch `claude/issue-16322-analytics-daterange-closed-vocabulary-drivers`\n\n' +
4354+
'Clause-②: yes\n',
4355+
};
4356+
// The live board, 2026-09-12T02:53Z: the same spelling, a different seat's
4357+
// template, four claims inside three seconds. Card #16175's newest claim is
4358+
// branchless while its 2026-09-06 claim parses — and names a DIFFERENT branch.
4359+
const LIVE_BRANCHLESS = {
4360+
id: 5642984850,
4361+
created_at: '2026-09-12T02:53:06Z',
4362+
body: 'Claim: session_012GKcPZbMoGq7WPzKLfRBTU · claude/issue-16175-staleness-mtime-false-refusal\nClause-②: no\n',
4363+
};
4364+
const LIVE_PARSES = {
4365+
id: 5557414924,
4366+
created_at: '2026-09-06T06:19:06Z',
4367+
body: 'Claim: PM loop\nBranch: `claude/issue-16175-regen-sibling-stale-rules`\nClause-②: no\n',
4368+
};
4369+
const INLINE_THREAD = [INLINE_OLD, INLINE_NEW];
4370+
const unparsedDecl = cardDeclaration(INLINE_THREAD);
4371+
t('⭐ the measured inline spelling reads CLAIM-BRANCH-UNPARSED — the carrier could not be resolved', unparsedDecl.state === 'claim-branch-unparsed');
4372+
t('⛔ …and NOT `declared`: the line that IS on the thread belongs to a comment this run cannot confirm is current', unparsedDecl.state !== 'declared');
4373+
t('⛔ …nor `absent`, which would say no claim comment was written — one was', unparsedDecl.state !== 'absent');
4374+
t('⛔ …nor `missing`, which would send the seat looking for a line that is there', unparsedDecl.state !== 'missing');
4375+
t('…and it carries NO value — an unclassified result is never a reading', unparsedDecl.value === undefined);
4376+
t('the state names the comment it could not parse', unparsedDecl.malformedClaim?.id === 5594909614);
4377+
// The BEFORE-state, quantified rather than recalled: both comments carry a
4378+
// readable line, and they DISAGREE. That is why reading the wrong one was a
4379+
// wrong answer and not merely an unlucky one.
4380+
t('⭐ the superseded comment carried a readable declaration, and the two DISAGREE', readClause2Line(INLINE_OLD.body)?.value === 'no' && readClause2Line(INLINE_NEW.body)?.value === 'yes');
4381+
// The reading PRINTS, and it is UNJUDGED (exit 2) rather than a verdict.
4382+
const unparsedPair = pair({ cardComments: INLINE_THREAD });
4383+
const unparsedGap = pairUnjudged(unparsedPair);
4384+
t('⭐ the pair is UNJUDGED and the reading prints in full — ⛔ never silence', typeof unparsedGap === 'string' && unparsedGap.length > 0);
4385+
t('…naming the comment id, so a reader can open it', says(unparsedGap, '5594909614'));
4386+
t('…and the remedy, which is a `Branch:` line of its OWN', says(unparsedGap, '`Branch:` line of its OWN'));
4387+
t('…and saying in as many words that this is not a declared `no`', says(unparsedGap, 'never a declared `no`'));
4388+
t('…and that a whole shift reading this way is a TEMPLATE fault rather than a typo', says(unparsedGap, 'SEAT TEMPLATE fault'));
4389+
t('⛔ and it raises NO C2 finding — an unclassified result must never be rendered as an adverse verdict', pairRows(unparsedPair).every((r) => r.code !== 'C2'));
4390+
t('⛔ nor any other finding row on this pair', pairRows(unparsedPair).length === 0);
4391+
// The live specimen, and the fallback shape at its sharpest: the older claim
4392+
// names a DIFFERENT branch, so every reader downstream probes the wrong ref.
4393+
const liveDecl = cardDeclaration([LIVE_PARSES, LIVE_BRANCHLESS]);
4394+
t('⭐ the live 2026-09-12 specimen reads the same way', liveDecl.state === 'claim-branch-unparsed');
4395+
t('…and the state names the older claim governance would have fallen back to', liveDecl.governingClaim?.createdAt === '2026-09-06T06:19:06Z');
4396+
t('⚠️ …whose branch is a DIFFERENT one, so the fallback is not even about the same work', liveDecl.governingClaim?.branches.join(',') === 'claude/issue-16175-regen-sibling-stale-rules');
4397+
t('…and the printed gap names that older claim rather than leaving the reader to guess', says(pairUnjudged(pair({ cardComments: [LIVE_PARSES, LIVE_BRANCHLESS] })), 'claude/issue-16175-regen-sibling-stale-rules'));
4398+
// CONTROLS — the accept set did not move in either direction.
4399+
t('⛔ CONTROL: a well-formed newest claim still governs, and its value is read', cardDeclaration([
4400+
{ id: 1, created_at: '2026-08-30T09:00:00Z', body: 'Claim: old\nBranch: `claude/issue-1-old`\nClause-②: yes' },
4401+
{ id: 2, created_at: '2026-08-31T09:00:00Z', body: 'Claim: new\nBranch: `claude/issue-1-new`\nClause-②: no' },
4402+
]).value === 'no');
4403+
t('⛔ CONTROL: an OLDER branchless claim is spent and raises nothing — governance is correct', cardDeclaration([
4404+
{ id: 1, created_at: '2026-08-30T09:00:00Z', body: 'Claim: session_x · claude/issue-1-old\nClause-②: yes' },
4405+
{ id: 2, created_at: '2026-08-31T09:00:00Z', body: 'Claim: new\nBranch: `claude/issue-1-new`\nClause-②: no' },
4406+
]).state === 'declared');
4407+
t('⛔ CONTROL: #16170\'s bulleted `Branch:` directive still parses, so its card is unaffected', cardDeclaration([
4408+
{ id: 1, created_at: '2026-08-31T09:00:00Z', body: 'Claim: PM loop\n- Branch: `claude/issue-15511-zh-gap-helptext`\nClause-②: no' },
4409+
]).state === 'declared');
4410+
t('⛔ CONTROL: a thread with no claim comment at all still reads ABSENT', cardDeclaration([{ id: 1, body: 'a triage note', created_at: '2026-08-31T10:00:00Z' }]).state === 'absent');
4411+
t('⛔ CONTROL: an unreadable thread still reads UNREADABLE — never the new state', cardDeclaration(null).state === 'unreadable');
4412+
// The new state is nobody else's state: it is counted into neither not-read
4413+
// population and it is not the #16304 fourth reading's `absent`.
4414+
t('the tally counts it under NEITHER not-read population', declarationLimbTally([unparsedPair]).absent === 0 && declarationLimbTally([unparsedPair]).missing === 0);
4415+
t('⛔ …and the fourth reading is unavailable on it — a sibling cannot answer a question this card could not ask', readsSiblingDeclaration(unparsedPair, [unparsedPair, pair({ card: 999, cardComments: [CLAIM('Clause-②: yes')] })]) === false);
4416+
t('⛔ …nor is such a card a CARRIER for a sibling of its own', siblingDeclarations(pair({ card: 999, pr: 13910 }), [pair({ card: 999, pr: 13910 }), { pr: 13910, card: 13476, cardComments: INLINE_THREAD }]).length === 0);
4417+
// ⛔ The fix is the STATE, not a widening: the branch reader's accept set is
4418+
// byte-identical, which is what keeps the next unrecognised spelling loud.
4419+
t('⛔ the branch reader was NOT widened — the inline spelling still parses to zero', claimedBranches(INLINE_NEW.body).length === 0);
4420+
t('⛔ …and the claim marker still matches it, which is what makes the two-anchor split a STATE', CLAIM_COMMENT_MARKER.test(INLINE_NEW.body) === true);
4421+
43254422
// -- The floor: every declared battery RAN, and ran its cases (#13489) -----
43264423
//
43274424
// Evaluated after every battery has had its chance and BEFORE the verdict, so
@@ -4382,7 +4479,9 @@ export function selfTest() {
43824479
'the correction comment that supersedes a claim declaration with the five measured prose ' +
43834480
'spellings held out as negatives, ' +
43844481
'the three read paths with their offline reader, the argv contract with its usage and its '
4385-
+ 'refusal, the board provenance line, and the exit register).',
4482+
+ 'refusal, the board provenance line, the claim whose `Branch:` line parses to ZERO '
4483+
+ 'branches — reported as an unresolvable carrier rather than discarded — and the exit '
4484+
+ 'register).',
43864485
);
43874486

43884487
selfTestReachedVerdict = true;

0 commit comments

Comments
 (0)