fix(pm): C5 reports a widening tell instead of refusing on it - #19495
Merged
Merged
Conversation
`check-clause2-carriers.mjs`'s C5 limb turned a widening TELL into a hard refusal: a card declaring `Clause-②: no` whose diff carried tells made `--pair N` exit 4, and PD #14 makes `--pair` at 0 a conjunct of the landing predicate. On the maintainer's ruling of 2026-09-21, C5 is demoted to report-only -- the row still prints, with its file:line evidence and its remedy text, and it no longer contributes to a non-zero exit. The operative distinction, from the ruling record: a limb that judges 「is this diff widening a contract」 -- which the matcher itself says it cannot prove (「a tell, never a proof」, 「cannot tell an array element from a call argument」) -- does not hold a hard gate. A limb that asks 「does a review of record exist」 does. So C6 is untouched and stays a hard refusal, as do C2, C3, C8, C9 and the UNJUDGED exit-2 path. `renderPair` is split into the pure `pairRendering` (every line, its stream and the exit, computed before a byte is printed) and a printer, so the self-test can pin what a run PRINTS beside what it EXITS -- "the row still prints" and "the exit is 0" are two claims, and a case pinning only the second stays green over a row that went silent. The exit-0 line gains a `wideningReported` branch so a 0 carrying tells never reads as "narrow". Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-Authored-By: Claude <noreply@anthropic.com>
This was referenced Sep 21, 2026
os-steve
marked this pull request as ready for review
September 21, 2026 04:35
os-steve
enabled auto-merge
September 21, 2026 04:35
This was referenced Sep 21, 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 #19490
What this changes
scripts/pm/check-clause2-carriers.mjs's C5 limb turned a widening tell into a hard refusal: a card declaringClause-②: nowhose diff carried tells made--pair Nexit 4, and PD #14 makes--pairat 0 a conjunct of the landing predicate. C5 is now report-only — the row still prints, with itsfile:lineevidence and its remedy text, and it no longer contributes to a non-zero exit.⛔ C5 and nothing else. C6 stays a hard refusal, as do C2, C3, C8, C9 and the UNJUDGED exit-2 path.
The ruling this stands on
Maintainer ruling of 2026-09-21, recorded at #18917 (comment). Quoted verbatim, untranslated, because paraphrasing a ruling rewrites it:
The operative distinction that record draws, and which the code now carries in its own words (
pairReportRows's docblock):The mechanism
pairReportRows(pair, repo)is new and is where the demotion is: the C5 row is built there instead of being pushed intorows, androws.length === 0is the exit-0 condition. A row that is not in that list cannot refuse.renderPairis split into the purepairRendering(every line, its stream, and the exit, computed before a byte is printed) and a printer. That split is what lets the self-test pin what a run prints beside what it exits — "the row still prints" and "the exit is 0" are two claims, and a case pinning only the second stays green over a row that has gone silent.C5_REPORT_ONLY_DISPOSITION: it says the row is report-only, that the at-tier contract review is what rules on the tell, and ⛔ that report-only is not a clearance and not a verdict that the tell is false.REFUSAL_SENTENCE's remedy is carried verbatim, unchanged.⚑, never✗— a refusal marker beside an exit of 0 is the exit register's banned "0-with-a-message", inverted.greenPairLinegains awideningReportedbranch, so an exit-0 line on a pair that carries tells says so instead of going quiet. Without it a 0 would read as "narrow", which is the silence this file exists against.file:linelist print on every exit this path can answer — including the exit-2 gap path, where the tell would otherwise be lost.Measured, before and after
Exit codes captured BEFORE any pipe.
--pair 19314✗ C5, 7 tells⚑ C5, the same 7 tells--pair 19438✗ C6✗ C6— untouchedBoth halves of requirement 1 verified: 19314 reaches 0 because C5 no longer refuses, not because any tell disappeared.
grep -c 'T2 packages/spec/src/stack.zod.ts'reads 7 on the before log and 7 on the after log —stack.zod.ts:3412through:3418, each with itsT2explanation and its+source line, in both.The after run's exit-0 line says so itself, rather than reading clean:
Pins, and the ablation for each
A new self-test battery,
⭐ the 2026-09-21 ruling: C5 REPORTS its tell, C6 still REFUSES, 24 cases, declared inSELF_TEST_BATTERIESat its measured floor. Self-test: 1075 → 1099 cases, exit 0.Six ablations, each through
scripts/ablation-replace.mjsso the mutation's landing is the tool's own verdict (anchor count 1 to 0, blob hash before and after) and the restore is proved byblob == HEADplus an emptygit diff HEAD:rowsprintReportsbodyfile:line, evidence list, remedy, disposition, markergreenPairLine'swideningReportedbranchC6row push inpairRowspairReportRowsreturn[]wideningUnjudgedreturnnullEvery ablation restored byte-identically before the next;
git diff HEADempty andgit status --porcelainclean after each. The two⛔ CONTROLcases on the clean-diff pair stay green under all six by design — they are the non-vacuity bracket for the reported/clean pair of readings, not pins on code.Gates
node scripts/pm/check-governed-merges.mjs --test scripts/pm/check-clause2-carriers.mjs→ exit 0, NOT governed — ordinary queue landing applies to a PR with exactly this file list. Size: 292 changed lines (+260 / -32), under the 5000-line human-merge threshold.Every command's exit code captured BEFORE any pipe:
node scripts/pm/check-clause2-carriers.mjs --self-test→ 0 (1099 cases)node --check scripts/pm/check-clause2-carriers.mjs→ 0pnpm exec eslint scripts/pm/check-clause2-carriers.mjs→ 0node scripts/check-self-test-wired.mjs→ 0node scripts/check-scripts-symbol-anchors.mjs→ 0node scripts/pm/check-governed-merges.mjs --test→ 0node scripts/pm/dispatch-gates.mjs --commands(35 commands, derived from the merge base, three-dot) — all 0.维护者速读(草稿)
改了什么 — 把全仓落地谓词里的 C5 一肢从「拒绝」降为「只报告」。一张卡声明
Clause-②: no、而 diff 带了扩面迹象时,这条行照旧打印,连同它的file:line证据和补救文字;但它不再让--pair返回非零。C6(本轮是否留下达档复核记录)以及 C2/C3/C8/C9、UNJUDGED 的 exit 2 一律原样不动。为什么改 — 2026-09-21 的裁决:挡住 PR 落地、让开发 agent 排满,本身就是负面因素,哪怕代价是漏几个 bug,因为 bug 还能再改。裁决给出的分界是问题的种类而非严重程度:判断「这个 diff 是不是在扩接口」的肢,其工具自己写着「只是迹象,不是证明」「分不清数组元素和调用实参」,就不该持硬闸;问「有没有一条复核记录」的肢可以。现场证据:
check-widening-tells.mjs14 天改了 18 次,挂着 5 张缺陷卡且全部是误报;PR #19314 全绿、达档复核 PASS 已判定它那 7 条迹象全部为假,仍被这 7 条卡了 12 小时。风险与代价(含回滚) — 代价是真实的:一个确实扩了接口却声明
no的 PR,机器不再拦它,改由达档复核去判——这正是裁决接受的那笔交易。风险被三处收窄:行照打不误、证据照列、exit-0 那行自己写明「本 0 不代表 diff 是窄的」。回滚成本一行:把pairRendering里的const reports = pairReportRows(pair, repo)改回推进rows,6 条用例会立刻转红提示。本 PR 保持 draft,不合并、不入队、不动标签,等维护者看过。席位意见 —
你要做的 — 读一眼上面的分界是否就是你的本意(C5 报告、C6 仍拒);同意就把这个 PR 标 ready 并走队列。合并后
--pair 19314立刻读 0,那张被挡 12 小时的 PR 就能合法落地,不需要任何人自查放行。Acceptance notes
POST /repos/objectstack-ai/objectstack/issuesreturned 201. The branch therefore carries a realissue-NNNNNsegment andCLAIM_BRANCH_SHAPEis satisfied — the predicted exit-2 UNJUDGED consequence does not apply.--pairon THIS PR is adverse on C2, and deliberately so. Card fix(pm): C5 reports a widening tell instead of refusing on it #19490 carries noClaim:comment, because writing one is the owning seat's act and not a dispatched executor's. A seat that wants that reading to clear posts the claim naming this branch.scripts/pm/**is not a governed surface, so PD feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14's--pair-at-0 conjunct does not gate this PR's landing either way.scripts/pm/**is on the changeset fast track (not published), soskip-changesetapplies on the merits; this PR does not hang that label.check-widening-tells.mjs,check-half-states.mjs,check-governed-merges.mjs,AGENTS.mdand all skill text are untouched. The tell is not the defect being fixed here — its force is.Generated by Claude Code