fix(pm): give the clause-② declaration limb a self-solvable exit - #17738
Merged
Merged
Conversation
The declaration limb was a one-way door: a seat that wrote the line as prose had no sanctioned act that repaired it -- no edit-an-issue-comment call in the MCP set, a second `Claim:` forbidden by protocol, and the checker forbidden from filling the line in. Adds one machine-readable correction shape (a dedicated comment whose first line is `Clause-②-correction: <digits>`, carrying the declaration in the SAME fixed spelling and the claiming session's `Session:` line), read as superseding that claim's declaration, and rewrites the C2 remedy text to name who can act and how. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
…te control Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
This was referenced Sep 12, 2026
os-sales
marked this pull request as ready for review
September 12, 2026 01:52
This was referenced Sep 12, 2026
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 #17366
The defect: a carrier a seat could write into and not get out of
The clause-② declaration limb had one carrier — the
Clause-②:line inside the card'sgoverning
Claim:comment — and repairing a line already written there was not an act everyseat can perform. The MCP GitHub tool set carries no edit-an-issue-comment call; the claim
protocol forbids a second
Claim:; and this gate's own refusal forbids the checker fillingthe line in ("the declaration IS the judgement"). Three closed doors, and a seat that wrote
the line as prose was left waiting for somebody outside the repository to retype it.
That was measured five times in one shift across two roles, and the fifth instance was still
holding a PR with 38 checks and 0 failures out of the queue when the card was filed.
The fix: one correction comment, read by the same reader
A dedicated comment whose FIRST line is a fixed key naming, in digits, the claim comment it
corrects:
The newest correction naming the card's governing claim supersedes that claim's declaration,
in both directions — so a wrong VALUE is repaired by the same act as an unreadable one. The
declaration inside it is read by the SAME
CLAUSE2_KEY_LINEthrough the SAMEreadClause2Line, so what moved is WHICH COMMENT may carry the declaration, never what countsas an answer. That is the move #16304 already made when it asked a sibling CARD.
Attribution is the
Session:line the claim protocol already makes mandatory(SKILL.md 〈模板与表〉, 「session ID 不可省」, and 「
mode:subagent的 dev 与 PM 同会话同 ID」 —so the session is exactly the granularity of "the claiming seat"). A correction is attributed
when its
Session:equals the governing claim's. That is a DECLARED identity, never a verifiedone: the value is copyable text and this fleet writes under one GitHub login, so the comparison
is on what the comments SAY — the same ceiling C4 already works at.
⛔ No branch of it may become a new one-way door, which is the defect being removed. A
correction naming another comment, declaring a different session, carrying no
Session:line,or carrying a prose declaration is IGNORED WITH A PRINTED REASON that names an act the claiming
seat can perform. And a governing claim that carries no
Session:line at all leaves nothing tocompare: the correction APPLIES, with a note saying attribution could not be verified and why.
Refusing there would have rebuilt the door one room over.
The C2 remedy text was rewritten to name WHO can act and HOW, in three parts: the claim template
in SKILL.md 〈模板与表〉 that already carries the literal
Clause-②: yes | noline and should becopied rather than composed; the fact that an already-posted claim comment is not editable from
every seat; and the one comment that repairs it. It replaces "add the line to that claim comment",
which named an act the claiming seat may have no tool for.
What deliberately did not move
CLAUSE2_KEY_LINE,readValueTokenandCLAUSE2_VALUESare untouched. Thecard's five measured prose spellings are pinned as negatives in the self-test.
keep their meanings and their numbers.
check-half-states.mjs.CLAIM_COMMENT_MARKERis still imported, not restated, and notwidened. A correction is not a claim comment and never enters the claim pool.
restate it.
Acceptance, all four from the card
the reader over the template's own key with each value substituted, plus the new remedy text
that sends the seat to the template rather than to a regex.
as not-declared, from the line reader and from a claim comment. The
#1spelling is reportedas a SPELLING near miss; the
#2–#5spelling reaches no pattern at all, because 条款② carriesno
Clausetoken — pinned as a measured fact.adding ONE correction comment clears it, with the claim comment byte-identical across the two
threads, no second
Claim:, and the governing claim unmoved. Demonstrated end to end throughthe offline
--pair-jsonpath:--pairexit 4 with the broken claim, exit 0 with the onecomment added.
case); deleting the template pointer and the who-can-act remedy reds 7 (criterion 1's new half).
Both legs were mutated on disk, proved to have landed, then restored to a blob hash equal to
HEAD's.
Acceptance notes
UNFILLED —
Clause-②: yes | no— reads as a declaredyes, becausereadValueTokentakes thefirst token after the colon and treats the rest as the seat's argument. That is an instance of
the population [finding]
check-clause2-carriersreads a key-INITIAL describing line as a declaration — its own self-test asserts the general property "only DESCRIBES ⇒ MISSING", and the fixture pinning it covers only the other half #17098 is already open against (a key-INITIAL DESCRIBING line read as adeclaration), so it is ⛔ not filed again here. The case carries a pre-registered FLIP TRIGGER:
when [finding]
check-clause2-carriersreads a key-INITIAL describing line as a declaration — its own self-test asserts the general property "only DESCRIBES ⇒ MISSING", and the fixture pinning it covers only the other half #17098 lands, the expectation becomeskind !== 'declared'and the case flips with it inthat PR. ⛔ It is not to be deleted and its green today is not an endorsement.
Clause-②:is read byscripts/check-changeset-no-major.mjs, not by thisgate:
--pairreads the PR side for LABELS (C1) only, and noreadClause2Linecall here takes aPR body. The card-side correction shape therefore has no PR-side counterpart to add, and the
PR-body carrier is editable by the seat anyway — which is exactly the asymmetry the card names.
check-scripts-symbol-anchors,check-self-test-wiredandcheck-self-test-workflow-commandsall pass unchanged: the
check:pm-clause2-carriersstep inlint.ymlalready runs the self-test,and no second step was added.
Gates, on head
dbbfbb596node scripts/pm/dispatch-gates.mjs --commandsderives 35 families for this one-path diff; all 35ran and all 35 exited 0 (
--ranreconciliation: "35 derived famil(ies) accounted for — 35 run, 0NOT-MEASURED (a DERIVED zero — all 35 recorded an exit code and none of them is 3)"). Lint is the
declared narrowing rather than the repo-wide run CI owns: the receiving population is
eslint.config.mjs's base block (files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']),--format jsonreports 1 file linted with 0 errors and 0 warnings, and the config "never enables type-awarelinting (no
parserOptions.project, no typed@typescript-eslintrules) for ANY file"(
eslint.config.mjs, its own words) — so this diff cannot move the verdict on any file it does nottouch.
node scripts/pm/check-governed-merges.mjs --test scripts/pm/check-clause2-carriers.mjsprints "✅ NOT governed — ordinary queue landing applies".
--pair 17738exits 0.Generated by Claude Code
Generated by Claude Code