Skip to content

fix(pm): name PLACEMENT, not spelling, when a Clause-② key sits mid-line - #17201

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-16931-clause2-inline-key-diagnostic
Sep 9, 2026
Merged

fix(pm): name PLACEMENT, not spelling, when a Clause-② key sits mid-line#17201
baozhoutao merged 1 commit into
mainfrom
claude/issue-16931-clause2-inline-key-diagnostic

Conversation

@claude

@claude claude Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #16931

Domain: domain:devx · Type: Bug · Route: A (change what the tool SAYS, not what it ACCEPTS)
Clause-②: no — diagnostic text only. The accept set, the state union, the values, the tallies and the exit register are unchanged, and that is measured below rather than argued.

The defect

check-clause2-carriers read a compact claim header like

Domain: `domain:cli` · Clause-②: no

as missing and shipped a remedy naming only spelling — so a seat was sent to hunt a typo on a line spelled exactly right. Both existing patterns anchor at start-of-line and tolerate only line-start decoration before the key, so the line was invisible twice: not read as a declaration (correct), and not quoted back as a near miss either — which is the whole job of the mechanism that exists so a near miss is not silently read as an absence.

What changed

One reporting-only detector: the fixed key followed by its colon, at a position no line-start decoration explains. The near miss now carries a reason, and the C2 row picks its remedy sentence from it.

  • reason: 'inline-key' — the key is spelled exactly right and simply not at the start of a line. The row names PLACEMENT, quotes the offending line, says in as many words that the spelling is not the defect and that there is no typo to find, and sends the seat to put the line on one of its own.
  • reason: 'spelling' — everything the near-miss reporter already caught. Sentence unchanged.

CLAUSE2_KEY_LINE is untouched. The standing red line is preserved verbatim in the file and in the row it still prints: 「a predicate that reads prose is a heuristic, and the measured terminus of that direction is a check that can barely fail」.

⭐ Both direction controls, measured before and after

This reader decides "is this line a declaration?" by position alone, so #16931 and #17098 are the same regex from opposite directions, and any change that loosened position here would make #17098 worse. Both directions were measured on the same harness, against origin/main c3756ff096 and against this branch.

Forward — this card's shape

before after
readClause2Line null (invisible to both patterns) {kind: 'near-miss', reason: 'inline-key', line: …}
cardDeclaration {state: 'missing'}, no detail {state: 'missing', detail: THE_LINE, nearMissReason: 'inline-key'}
C2 row "the DECLARATION LINE is what is missing … the fixed spelling is …" "the PLACEMENT of the key is what is wrong, NOT its spelling … ⛔ There is no typo to find on that line."

⭐ The state is the same in both columns. The line is exactly as unread as it was; only the sentence moved.

Reverse control — #17098's direction, ⛔ not fixed here and ⛔ not regressed

Input: a key-INITIAL bullet that merely describes, - **Clause-②: yes/Clause-②: no** — the value alone on its line, machine-read.

before after
readClause2Line {kind: 'declared', value: 'yes', …} {kind: 'declared', value: 'yes', …}
cardDeclaration {state: 'declared', value: 'yes', …} {state: 'declared', value: 'yes', …}

Byte-identical — the diff of the two harness runs shows no line for this control at all. Today that reading is #17098's defect; ⛔ this PR neither fixes it nor moves it. Structurally it cannot: the declaration reader returns on the first key-line match before the reporter is consulted, and the detector is written to fire only where the key is NOT at line start. That property is pinned by a case of its own (a key-initial line is never reasoned inline-key), rather than by pinning what the defective direction currently reads — pinning today's declared would put a fixture in #17098's way.

Neighbours held

Clause-②: no still declares; the #12297 reasoning-after-value allowance (Clause-②: yes — widens the accept set) still declares; the existing prose near miss still prints the same row text and only gains its reason field.

A side effect measured, not glossed

A claim comment that merely DESCRIBES with prose before the key (the :2272 fixture's shape) also carries the key mid-line, so it now takes the placement sentence too. That is why the sentence is worded to state only what is measurable — the thread carries the key, not at the start of a line — and never that the judgement was made. Its state is unchanged (missing), and the remedy it now gives (put the declaration on a line of its own) is correct for that shape as well.

End-to-end, on this card's own thread

Running the reader over the real comment thread of #16931: before, the row carried no quotation at all; after, it quotes the triage bullet that spells the key mid-line. The state is absent in both readings, unchanged.

Ablation

Fix committed first. Mutating the detector's single decision (return true for a key preceded by non-decoration) to return false, proved on disk by blob hash 60b7562c…71d6a50b…:

  • self-test exit 1, 10 of 319 cases red — every case that pins the new diagnostic.
  • The controls in that batch stayed green by design: state still missing, no value, tally still one not-read card, the standing refusal still printed. Those hold in both worlds, which is what makes them controls.
  • Restored with git checkout HEAD -- PATH; blob back to 60b7562c…, git diff HEAD empty, git status --porcelain empty, self-test exit 0 / 319 pass.

Test evidence

  • pnpm check:pm-clause2-carriers — exit 0, 319 cases (was 299 on origin/main; 20 added across the two existing batteries, which carry floors rather than equalities, so SELF_TEST_BATTERIES is untouched).
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 35 commands; all 35 run; reconciled with --ran: 35 derived famil(ies) accounted for — 35 run, 0 NOT-MEASURED, exit 0.
  • Seven of the 35 first returned exit 3 — PREREQUISITE NOT MET (pnpm install had not run in this worktree). That is NOT MEASURED, not a finding; all seven were re-run after install and returned 0.
  • Also run, because they name this file: pnpm check:pm-half-states (0), pnpm check:pm-widening-tells (0). Their reference is to this file's decoration tolerance as a precedent; that tolerance is unchanged.
  • No separate vitest suite exists for this script — its suite IS its --self-test, wired as check:pm-clause2-carriers.
  • eslint . --no-inline-config --format json at 59adc6d0: 6438 files, 0 errors, 0 warnings. Whole population, not a narrowing.

Changeset

None. scripts/pm/** ships in no package's files[] and the repo root package is private, so nothing published moves. lint.yml states the case in its own words: "this PR edits a CI-internal script" is the textbook skip-changeset case.

验收备注 — noted, not filed

🤖 Generated with Claude Code

https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU


Generated by Claude Code

check-clause2-carriers read a claim header like `Domain: X · Clause-②: no`
as `missing` and shipped a remedy naming only spelling -- so a seat was sent
to hunt a typo on a line spelled exactly right. Both existing patterns anchor
at `^`, so the line reached neither the declaration reader nor the near-miss
reporter whose whole job is to stop a near miss reading as an absence.

Adds a reporting-only detector for the fixed key preceded by something other
than line-start decoration. The near miss now carries a reason, and the C2 row
picks its remedy sentence from it: placement, with the offending line quoted
and an explicit "there is no typo to find".

CLAUSE2_KEY_LINE is untouched and the state union is unchanged -- the shape
still reads `missing`, carries no value, and keeps its exit. What moved is the
sentence, not the accept set: this reader decides "is this a declaration?" by
position alone, so loosening position here would promote merely-describing
prose into candidate declarations in the opposite direction.

Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/m label Sep 9, 2026
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 9, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 9, 2026 16:36
@baozhoutao
baozhoutao enabled auto-merge September 9, 2026 16:36
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit 22c94e5 Sep 9, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-16931-clause2-inline-key-diagnostic branch September 9, 2026 17:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants