Skip to content

Commit 7d493ad

Browse files
os-steveclaude
andauthored
fix(pm): claim-branch-unparsed remedy names the whole shape its own rule requires (#19454)
Fixes #19414 Clause-②: no ## What moved Only the `claim-branch-unparsed` remedy sentence in `claimBranchUnparsedGap`, the one docblock line that restates it, and ONE self-test case. The reader is untouched: `CLAIM_SELECTION_RULE`, row C8 and the retraction channel read exactly as they did at `57ceb9d`. Net +21 lines in `scripts/pm/check-clause2-carriers.mjs` (26 insertions / 5 deletions), against the dispatch's ceiling of +30. Placeholder note: the literal in the source spells its two placeholders in angle-bracket form. They are written as `N` and `slug` everywhere in this body, because the body sanitizer eats short angle-bracket fragments, fenced or not. ### Before — `57ceb9d`, the sentence at :4913–:4915 > Remedy — the CLAIMING SEAT, with one comment: name the branch on a `Branch:` line of its OWN (`` Branch: `claude/issue-N-slug` ``), ⛔ not inside the `Claim:` sentence, which no reader parses. The rule sentence printed in the SAME output (`pair.1.claim.rule`) requires both lines: > the GOVERNING claim — the NEWEST comment whose body carries a line beginning `Claim:`/`Claimed:` … AND whose `Branch:` line parses at least one protocol-shaped branch A `Branch:` line alone carries no claim marker, so the comment the old sentence prescribed is not a claim comment at all: `claimGovernance` never sees it, `newest` stays the unparsed claim, and the state does not move. ### After > Remedy — the CLAIMING SEAT, with ONE comment carrying BOTH lines: a line beginning `Claim:` AND a `Branch:` line of its OWN (`` Branch: `claude/issue-N-slug` ``). ⛔ Not the branch named inside the `Claim:` sentence, which no reader parses, and ⛔ not a `Branch:` line posted ALONE in a comment of its own, which carries no claim marker and is therefore not a claim comment at all — that comment leaves this state exactly as it is. ⚠️ Already holding a LIVE claim on this card? Post `Release:` FIRST, so the new comment is the only claim standing — a second `Claim:` under a live one is row C8, ⛔ not a repair. The old warning is kept verbatim in substance — the defect is what the sentence OMITTED, not what it said — and the phrase the existing self-test pins (`` `Branch:` line of its OWN ``) is unmoved. ## The filer's four steps, reproduced on this tree Offline through `--pair-json` (fixtures only, no board read), each exit captured before any pipe: | step | the card thread | exit | |---|---|---| | 1 | one live claim naming its branch INSIDE the `Claim:` sentence | **2** — `claim-branch-unparsed` | | 2 | …the OLD remedy followed: a `Branch:` line of its own, in a comment of its own | **2** — unchanged, the remedy did nothing | | 3 | …`Claim:` + `Branch:` added under the still-live claim | **4** — row C8 | | 4 | …`Release:` then ONE fresh claim | **0** | That is the filer's measured path (2 → 2 → 4 → 0), reproduced before the edit and unchanged after it: the sentence moved, the reader did not. Following the sentence as it NOW reads, from the same step-1 thread — `Release:` first, then ONE comment carrying both lines — reaches **exit 0 in one act** (2 → 0). ## The self-test case is the sentence's own proof Four assertions, beside the existing `#17149` battery: 1. a `Branch:` line posted ALONE leaves the thread at `claim-branch-unparsed` — the old remedy's own outcome, pinned; 2. the comment the sentence now prescribes (both lines, the live claim released first) IS the governing claim — `cardDeclaration(...).state === 'declared'` and zero finding rows; 3. CONTROL: post that same comment WITHOUT the `Release:` and row C8 answers — which is why the sentence names it; 4. the printed sentence carries the whole shape: `ONE comment carrying BOTH lines`, `` `Release:` FIRST `` and `row C8`. `node scripts/pm/check-clause2-carriers.mjs --self-test` → **exit 0**, 1075 cases pass (was 1071). ### Reverse verification — the sentence assertion really can fail Through `scripts/ablation-replace.mjs` (anchor must hit; the write is verified against the disk, and the restore against `HEAD`), from the committed state: ``` ablation-replace: replaced 1 occurrence(s) ablation-replace: ok mutation landed: anchor 1 -> 0, blob de6a171 -> efcc146d09eb ✗ …and the sentence itself carries the whole shape: both lines, the `Release:` before a second claim, and the row one earns ✗ check-clause2-carriers self-test: 1 of 1075 case(s) failed. ablation-replace: ok restored: blob == HEAD (de6a171) and `git diff HEAD` is empty ``` Assertions 1–3 pin the READER, which this diff does not touch; assertion 3 is their non-vacuity control (same thread, one thing changed, C8 instead of silence). ## Verification — all at `5a5b381` - Derived union: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths) → 35 families. Every one run, exit captured before any pipe; reconciled with `--ran`: `35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN`, all exit 0 (`a DERIVED zero — all 35 recorded an exit code and none of them is 3`). - `pnpm check:pm-clause2-carriers` :: exit 0 · `pnpm check:pm-dispatch-gates` :: exit 0 (993s on this box) · `pnpm check:nul-bytes` :: exit 0. - The edited file is a tool script, so its own suite is owed beyond the derived families: its `--self-test` IS that suite (exit 0). The sibling readers that name it also ran green: `check-half-states`, `check-widening-tells`, `check-governed-merges`, `check-governed-queue-guard`, `check-expected-skips` — each `--self-test` :: exit 0. - Lint: not narrowed — the repo-wide `eslint . --no-inline-config` ran here in full, **6947 files, 0 errors, 0 warnings, exit 0** (population read from `--format json`, not estimated). - Control-character scan beyond the gate: `grep -naP` over the edited file → 0 hits. - `node scripts/pm/check-governed-merges.mjs --pr` — `scripts/pm/**` carries no row in `GOVERNED_SURFACES`, so NOT governed is expected. ## Acceptance notes - noted, not filed: the card's wider ask — ONE check over every `scripts/pm/*` remedy text, so a remedy that does not satisfy its own tool's rule fails mechanically — is recorded by the lane grading as a seat item and is deliberately out of this card's scope. Carrier: the `domain:skills` seat's item list. - noted, not filed: `check-harness-current.mjs`'s STALE remedy text is the filer's second instance of the same shape. Not touched here, not re-measured here; it is the other half of the seat item above. Carrier: the same seat item. --- _Generated by [Claude Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8f6d831 commit 7d493ad

1 file changed

Lines changed: 26 additions & 5 deletions

File tree

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

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4886,7 +4886,9 @@ export function declarationLimbTally(pairs) {
48864886
* whether it is the current one. Reporting it as `missing` would send the seat
48874887
* looking for a line that is there; reporting it as a `no` would manufacture a
48884888
* decision nobody made. The sentence names the comment, says what governance
4889-
* did instead, and gives the one remedy — a `Branch:` line of its own.
4889+
* did instead, and gives the one remedy — ONE comment carrying a `Claim:` line AND
4890+
* a `Branch:` line of its own, with a `Release:` first when the seat already holds
4891+
* a live claim here.
48904892
*
48914893
* ⛔ Nothing here widens the branch reader. The remedy is on the WRITE side,
48924894
* which is the same call the sibling script's H34 makes for the claim marker
@@ -4910,10 +4912,14 @@ export function claimBranchUnparsedGap(pair, decl) {
49104912
`parses to ZERO branches, so the carrier this limb reads cannot be resolved. ${instead}. ` +
49114913
'An unparsed claim is an UNCLASSIFIED result, ⛔ never an absent declaration and ⛔ never a ' +
49124914
'declared `no` — this pair is missing from the readings above, not clean in them and not ' +
4913-
'adverse in them. Remedy — the CLAIMING SEAT, with one comment: name the branch on a ' +
4914-
'`Branch:` line of its OWN (`` Branch: `claude/issue-<n>-<slug>` ``), ⛔ not inside the ' +
4915-
'`Claim:` sentence, which no reader parses. ⚠️ A whole shift of claims reading this way is a ' +
4916-
'SEAT TEMPLATE fault, not a typo. ' +
4915+
'adverse in them. Remedy — the CLAIMING SEAT, with ONE comment carrying BOTH lines: a line ' +
4916+
'beginning `Claim:` AND a `Branch:` line of its OWN (`` Branch: `claude/issue-<n>-<slug>` ``). ' +
4917+
'⛔ Not the branch named inside the `Claim:` sentence, which no reader parses, and ⛔ not a ' +
4918+
'`Branch:` line posted ALONE in a comment of its own, which carries no claim marker and is ' +
4919+
'therefore not a claim comment at all — that comment leaves this state exactly as it is. ' +
4920+
'⚠️ Already holding a LIVE claim on this card? Post `Release:` FIRST, so the new comment is ' +
4921+
'the only claim standing — a second `Claim:` under a live one is row C8, ⛔ not a repair. ' +
4922+
'⚠️ A whole shift of claims reading this way is a SEAT TEMPLATE fault, not a typo. ' +
49174923
NEVER_WRITES
49184924
);
49194925
}
@@ -8174,6 +8180,21 @@ export async function selfTest() {
81748180
t('…and that a whole shift reading this way is a TEMPLATE fault rather than a typo', says(unparsedGap, 'SEAT TEMPLATE fault'));
81758181
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'));
81768182
t('⛔ nor any other finding row on this pair', pairRows(unparsedPair).length === 0);
8183+
// #19414 — the REMEDY is its own proof. The sentence used to name ONE line of
8184+
// the shape the rule requires, so a seat that followed it walked exit 2 → exit 2
8185+
// → C8's exit 4 before reaching 0 (measured on card #19306 / PR #19411). The
8186+
// case below WRITES the comment the sentence now prescribes and reads it back
8187+
// through this same reader.
8188+
const U14 = (id, at, body) => ({ id, created_at: at, user: { login: 'os-seat-ai' }, body });
8189+
const U14_HELD = U14(7400000001, '2026-09-20T01:00:00Z', 'Claim: round 1 · claude/issue-4242-remedy\nClause-②: no');
8190+
const U14_BRANCH_ONLY = U14(7400000002, '2026-09-20T02:00:00Z', 'Branch: `claude/issue-4242-remedy`');
8191+
const U14_RELEASE = U14(7400000003, '2026-09-20T03:00:00Z', 'Release: session `session_01Demo`, cause: 本席重发认领, 去向: `pm:queue`');
8192+
const U14_CLAIM = U14(7400000004, '2026-09-20T04:00:00Z', 'Claim: round 2\nBranch: `claude/issue-4242-remedy`\nClause-②: no');
8193+
const U14_CODES = (rows) => pairRows(pair({ cardComments: rows })).map((r) => r.code);
8194+
t('⭐ #19414: a `Branch:` line posted ALONE carries no claim marker, so the OLD remedy left the thread in this SAME state', cardDeclaration([U14_HELD, U14_BRANCH_ONLY]).state === 'claim-branch-unparsed');
8195+
t('⭐ …and the comment the sentence NOW prescribes is the governing claim — both lines in one comment, the live claim released first', cardDeclaration([U14_HELD, U14_BRANCH_ONLY, U14_RELEASE, U14_CLAIM]).state === 'declared' && U14_CODES([U14_HELD, U14_BRANCH_ONLY, U14_RELEASE, U14_CLAIM]).length === 0);
8196+
t('⛔ CONTROL: post that same comment WITHOUT the `Release:` and row C8 answers instead — which is why the sentence names it', U14_CODES([U14_HELD, U14_BRANCH_ONLY, U14_CLAIM]).includes('C8'));
8197+
t('…and the sentence itself carries the whole shape: both lines, the `Release:` before a second claim, and the row one earns', says(unparsedGap, 'ONE comment carrying BOTH lines') && says(unparsedGap, '`Release:` FIRST') && says(unparsedGap, 'row C8'));
81778198
// The live specimen, and the fallback shape at its sharpest: the older claim
81788199
// names a DIFFERENT branch, so every reader downstream probes the wrong ref.
81798200
const liveDecl = cardDeclaration([LIVE_PARSES, LIVE_BRANCHLESS]);

0 commit comments

Comments
 (0)