Repository navigation
Commit 2502b8a
fix(pm): the clause-② correction remedy names THIS card's claim comment, never a specimen from another card (#18448)
Fixes #17919
Clause-②: no
A CLI gate script exports no published symbol; the two new exports
(`commentCardNumber`, `correctionTarget`) live in `scripts/pm/`, which
no
package's `files[]` ships. The seat declared this on the card (claim
comment
`5696260545`) and the diff did not move it.
## The finding, and what it actually was
`check-clause2-carriers --pair` reported the clause-② declaration
MISPLACED and,
inside that message, named claim comment `5642248126` — a `Claim:` on
card
**#17366**, unrelated to the card under test.
**The card reads that as the script's *selection*. It is not.** Part (3)
of the
C2 remedy was a module-level constant that carried the id as a
**literal**:
```
'(3) Post ONE new comment on this card whose FIRST line is `Clause-②-correction: 5642248126` — '
```
That is the #17366 specimen, hard-coded into the sentence every C2 row
prints.
Nothing selected it, so no guard over a selection could have fired — a
constant
makes no selection. The id printed on **every card**, in **every C2
state that
carries the remedy**, and had done so since the constant was written.
Verified on the exact blob the 2026-09-13 readings were taken from —
`git cat-file -p aecbb2d | grep -n
5642248126`
→ line 1445, inside `CORRECTION_REMEDY`. Same literal, same sentence.
### Reproduced deterministically, offline, on a card of its own
`--pair-json` judges a pre-fetched pair with no network, so the reading
is
exactly reproducible. A synthetic card #99002 whose whole thread is
comments
`900000001` and `900000002`, judged by the `origin/main` copy of the
script:
```
$ node scripts/pm/check-clause2-carriers.mjs --pair 99001 --pair-json misplaced-pair.json
exit=4
… whose FIRST line is `Clause-②-correction: 5642248126` …
```
An id that is on neither comment of the thread it just read, printed
inside the
verdict about that thread. ⇒ the class is **re-observable today**, on
any card,
without reproducing the non-determinism the evidence upgrade measured.
## The cut, and why it is here rather than where the card offered it
The card offers a guard that the selected comment's `issue_url` matches
the card
under test, explicitly as a shape. Measurement moved it one level up:
the only
ids this file may **print** are ids it **read from the pool the reading
was
built from**, and each is checked against the card under test before it
is
printed.
- `commentCardNumber(row)` — the issue a row *says* it belongs to, read
off
`issue_url`; this is the card's own control, mechanised.
- `correctionTarget(pool, card)` — resolves the printable id from the
SAME pool
`cardDeclaration` built its readings from. A positive disagreement drops
the
id and **says so**; ⛔ absence of `issue_url` is not read as a mismatch
(an
offline document and this file's own fixtures carry rows without it, and
a row
that states nothing contradicts nothing); a reading with no card number
names
no id at all — fail-closed.
- `CORRECTION_REMEDY` is a function of the card under test. When no id
survives,
part (3) names the key and no digits, plus the reason.
⛔ Nothing here resolves a state, a row or an exit code.
## Both directions, judged — this is a gate other PRs must pass
**Does the guard suppress a message that was correct?** No. Word-diff of
the
whole output, same input, `origin/main` copy vs this one:
```
… whose FIRST line is `Clause-②-correction: [-5642248126`-]{+900000001`+} — the numeric id of the
claim comment it corrects, digits [-only-]{+only, read from card #99002's own thread+} — followed by …
```
That is the entire delta. The MISPLACED verdict, its sentence, its
remedy and
`exit=4` are byte-identical. A self-test case asserts each of the five
remedy-
carrying states still renders a non-empty row, so the controls cannot
pass
vacuously.
**Does any exit code move on a pair judged correctly?** No. All 16 open
PRs,
both copies run back to back, 2026-09-16T11:03–11:04Z:
| exit | PRs |
|:--|:--|
| 0 | 18444 18438 18437 18436 18433 18430 18429 18428 18427 18414 18403
18389 18319 |
| 2 | 18420, 17076 |
| 4 | 18131 |
Identical on both copies, 16/16, including all three non-zero verdicts.
## Population — previously unmeasured, now partly measured
- **Which paths**: the remedy is printed by 5 of the 6 C2 branches —
`misplaced`,
`malformed`, and all three `missing` variants. `absent` does not print
it (no
claim comment exists to correct). Enumerated mechanically: the
ablation's own
failure detail names all five.
- **How often**: on the live board at 2026-09-16T10:56Z, sweep over 15
pairs
derived from 16 open PRs — `0` with NO CLAIM COMMENT, `0` with a claim
comment
but NO DECLARATION LINE ⇒ **0 pairs on the printing path today**. The
class is
a failure-path constant, not a standing one.
- ⛔ Still unmeasured: how often the board has historically been on that
path.
## Tests
`--self-test`, the safety net, on `2620cb2b3d`: **715 cases pass, exit
0**
(689 before; +26). New battery `#17919: the correction remedy names THIS
card's
claim comment, never another card's`, floor raised by exactly one.
**Ablation** — the guard removed, part (3) restored to the pre-fix
literal, on a
committed tree, mutation proved on disk before the run and the restore
proved by
`git diff HEAD` and a blob-hash comparison:
```
HEAD blob fe6601d
on-disk: injected marker count=1 (want 1); removed-text count=0 (want 0)
mutated blob 847524b84e71639d317c6e9e9e373cb877c5c672
VERDICT ablation-mutated self-test exit=1 ← 9 cases red
✗ ⭐ …and NOT ONE of them carries a comment id from another card
["misplaced","malformed","missing","missing/describing","missing/inline-key"]
restored blob fe6601d (= HEAD)
git diff HEAD --name-only: []
VERDICT ablation-restored self-test exit=0
```
Direction predicted before the run and observed: **turns red**. The
module is
run directly from source by `node scripts/pm/…` — there is no build and
no
`dist/` between the edit and the run, so the on-disk proof is the whole
preflight.
**Gates**: all 36 commands derived by `node
scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` run at `2620cb2b3d`, exit
0 —
including `pnpm check:pm-clause2-carriers`, `pnpm check:nul-bytes` and
`pnpm check:pm-dispatch-gates` (496.6s; a first run capped at 240s read
`exit=124`, NOT MEASURED, and was re-run to completion). Reconciled:
`dispatch-gates --ran` ⇒ *36 derived, 36 run, 0 NOT-MEASURED, 0 UNRUN*.
No changeset: the root package is private and `scripts/pm/` sits outside
every
workspace package, so no `files[]` can ship it.
## Acceptance notes
Out of scope, noted and ⛔ not filed:
- The `misplaced` sentence says "NOT in the card's claim comment" on
threads that
have **no** claim comment at all (`misplaced` and `malformed` are both
reachable from the thread-wide scan with an empty claim pool). Prose
imprecision on a reachable path, not a wrong verdict; the remedy now
degrades
correctly there. Carrier: whoever next edits `c2Sentence`.
- The file header's illustration of the correction shape (line 367) also
spells
the #17366 specimen id. It is a doc comment inside a paragraph that is
explicitly about #17366, never printed, and left as written.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6)_
Co-authored-by: claude[bot] <noreply@anthropic.com>1 parent 3ab1508 commit 2502b8a
1 file changed
Lines changed: 257 additions & 18 deletions
0 commit comments