fix(pm): a key-INITIAL clause-② line that QUOTES the spelling is not a declaration - #17799
Merged
os-sales merged 4 commits intoSep 12, 2026
Merged
Conversation
…tion `CLAUSE2_KEY_LINE` decides "is this a declaration?" by position, and a standing-rules bullet teaching the spelling puts the key in exactly the position a declaration does. A claim comment whose only key-initial line was such a bullet had its EXPLANATION read as the card's declaration. Two structural tells now demote a matching line to a describing near miss (card state `missing`, a new `describing` reason with its own remedy sentence): the fixed key named more than once on the line, and the key held inside an inline-code span the line goes on talking outside of. A describing line is skipped rather than returned, so a real declaration written below one is now read. Separately, `readValueToken` refuses a value token followed immediately by an alternation: `yes|no` is a menu, not a choice, and it now reads `malformed` alongside the `<yes|no>` placeholder it has always matched. The pre-registered flip case in the #17366 battery is flipped, not deleted. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
The assertion states a general property and had one case under it: prose before the key, which the line-anchored reader never matched at all, so it passed for a reason narrower than the sentence it was written under. Both halves are now asserted at that sentence, and a new battery carries the mechanism from the measured specimens — the filing card's standing-rules bullet and the dispatch-template bullet a second seat measured, where the defect fired fail-OPEN into a landing pre-check. The battery pins each tell alone so neither can be carrying the other, the skip-not-return property (a declaration below a quotation is now read, which is what a seat had to arrange by hand), the row's three distinct remedies, and controls that the reasoning-after-value allowance and the four-valued reading are both intact. The `pool = claimRows` fallback is pinned as measured-unreachable on both arms rather than rewritten. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
…ing-line Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
…ing-line Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
os-sales
marked this pull request as ready for review
September 12, 2026 06:04
os-sales
deleted the
claude/issue-17098-clause2-key-initial-describing-line
branch
September 12, 2026 06:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #17098
CLAUSE2_KEY_LINEdecided "is this line a declaration?" by POSITION, and a standing-rules bullet teaching the spelling puts the key in exactly the position a declaration does. On a claim comment whose only key-initial line was such a bullet, the EXPLANATION became the card's declaration — and the gate's own self-test asserted the opposite property in general terms while the single fixture under it covered only the half where prose comes first.Measured before the change, on
15805ea3declared/yesmissing, reasondescribing5636056726)declared/yesmissing, reasondescribingdeclared/yesmalformeddeclared/yesmissing, reasondescribingmissingmissing, unchangeddeclareddeclaredmalformedLive board, the same sweep before and after: 17 open PRs, 16 pairs,
absent 0 / missing 0 / sibling 0, 0 finding rows, 4 unjudged — identical in both runs. A separate scan of the 15 claim comments behind those pairs found 0 with more than one key-initial declaration line and 0 producing a describing residue, so this change moves no verdict on the board today.The change
1. Two STRUCTURAL tells demote a matching line to a describing near miss (
clause2LineDescribes), decided without reading a word of what the seat wrote:CLAUSE2_KEY_LINEgains two capture groups so the match can say which backticks it consumed. The decoration it tolerates is byte-identical.2. A describing line is SKIPPED, not returned. The scan continues past it, so a real declaration written below a standing-rules bullet is now read. That is what a seat had to arrange by hand on three live cards, where position was the whole remedy.
3.
readValueTokenrefuses a value token followed immediately by an alternation. The token class ends at word characters, so a menu opened with a valid token and returned the first alternative. It is refused there rather than as a describing tell, and the card's open question about which state is safer is answered on the four axes in the function header: one state per fact (the same slot already readsmalformedfor an angle-bracket placeholder, for an uppercase value and for an empty one); the two remedies are not interchangeable (malformedsays CHOOSE, the describing remedy says ADD a line above, and for an unfilled template the act that exists is choosing); strictness is identical either way, both a C2 row at exit 4; and the only line where this tell is the sole one is an undecorated unfilled template, so it costs nothing on any measured occurrence. The refusal is ADJACENCY, never a scan — a pipe later in the reasoning is the seat's argument and is untouched.4. The describing residue gets its own remedy sentence. It says the line quotes the spelling rather than declaring a value, that there is nothing to fix on the quoted line, and that what is owed is a declaration of its own above it. Three residues, three distinct sentences.
5. The one-sided fixture is finished where it is written. Both halves are now asserted at the sentence that claims them, and a new battery (48 cases, registered and floored) carries the mechanism from the measured specimens, each tell pinned alone so neither can be carrying the other, plus controls that #12297's reasoning-after-value allowance and #13914's four-valued reading are intact. The pre-registered FLIP TRIGGER in the #17366 battery is flipped rather than deleted, as its own comment instructed.
Second consumer, worth naming:
check-changeset-no-major.mjsimports this samereadClause2Lineand applies it to the PR BODY, so the fix reaches the Check Changeset axis too.Reverse verification
Both discriminators ablated from the committed state, each proven on disk by blob hash and restored byte-identically.
clause2LineDescribesforced tofalse: the card's own specimen readsdeclared/yesagain — the defect, reproduced — and the self-test goes red on 19 of 546 cases. HEAD blobaecbb2d8, mutated0074f87f, restoredaecbb2d8,git diff HEADempty.declared/yesagain, 6 of 546 cases red. HEAD blobaecbb2d8, mutated87edf047, restoredaecbb2d8,git diff HEADempty.The two are independent — under ablation 1 the template bullet falls to
malformedrather than todeclared, because the alternation refusal still stands — so each carries its own half and neither is dead weight.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 35 families from the real change set (1 path, three-dot against the merge base). All 35 run, all exit 0, reconciled with--rancarrying exit codes: 35 derived, 35 run, 0 NOT-MEASURED (a DERIVED zero), 0 UNRUN. Seven first returned exit 3, PREREQUISITE NOT MET, and were re-run green afterpnpm install; that exit is not a finding and is not recorded as one.pnpm check:pm-clause2-carriers— 546 cases pass (493 before).pnpm check:nul-bytes— OK over 8451 text files; a separate control-character scan of the changed file matches nothing.pnpm check:pm-dispatch-gates— 1678 cases pass.origin/mainmoved twice during the round and was merged in both times; the derivation above is from the merged tree and carries no STALE TREE warning.This PR body is itself a specimen
The line below names the key twice and is therefore a quotation, not a judgement. Under the reader this PR replaces it would have been read as this PR's declaration; under the reader this PR ships it is skipped, and the real declaration further down is what the Check Changeset axis reads.
Clause-②: yes/Clause-②: no— the two spellings, quoted here on purpose.Measured on this body: the reader as it stood at
15805ea3stops at that bullet and returnsdeclared/yes— a falseyeson the Check Changeset contract axis, for a PR that touches nothing published. The reader this PR ships returnsdeclared/nofrom the line under the acceptance notes.Acceptance notes
malformedrather than first-match-wins) is NOT in this PR. Noted, not filed — it is already Direction 3 on the card this PR closes, and it is not a reproducible defect, a contract violation or a metadata-authoring trap. Measured pull today is zero: 15 claim comments behind the 16 open pairs, none with more than one key-initial declaration line. Doing it honestly also needs its own remedy sentence, because the existingmalformedsentence ("carries the key but not one of the two values, so there is no reading") would be false of a conflict where two lines each carry a valid value — a second field threaded through three layers, which is a separable change and a second review round. The describing fix already removes the mechanism by which a QUOTATION produced a second value, which is what every measured occurrence was.references/contract-review.mdis not touched, and the reason is a measurement. The dispatch pointer asked for one clause naming exit 2 (UNJUDGED) on the declaration limb, if it fits by density. It does not: L43 is 115 bytes againstcheck:pm-skill-ratchet's 120-byte per-line budget, and the file is 60 lines against a ceiling of 60 with zero headroom. The shortest clause that names exit 2 and keeps the line's meaning costs 11 bytes (126 B, over); the tightest rewrite merging exits 3 and 2 into one clause is 124 B, still over. Wrapping is the ratchet's own prescribed remedy and costs a line the ceiling cannot pay. The only legal currency is deleted content, and deleting content elsewhere in a governed file for an optional clause is scope this card does not carry.cardDeclaration'spool = claimRowsfallback is measured DEAD, and is left exactly as it stands. Both arms are pinned in the new battery so the measurement is re-runnable: a governing claim always leaves a non-empty pool because it came from a row matching the same predicateclaimRowsfilters on; and a null governing claim can only reach that line when there were no claim comments at all, since every other way of producing one also setsmalformedand returnsclaim-branch-unparsedabove it. A dead branch is a report line, not a rewrite.claude/issue-17098-contract-review-exit-2was pushed as the routing probe for the governed clause above and now holds zero commits — it is byte-identical toorigin/mainat8fa3fe63dand has no PR. This session cannot delete it:git push --deleteis cut off at the transport and the REST ref delete returns 403.--jsonoutput still does not expose the matched declaration line for PASSING pairs, so a green pair cannot be audited for WHY it passed. That is the second seat's own note on the card (5636056726), it is unchanged by this PR, and it is recorded here rather than filed.Clause-②: no
skip-changeset:
scripts/pm/**publishes nothing — no package'sfiles[]ships it, and this diff moves no runtime, spec or exported surface.Generated by Claude Code