Skip to content

measure the read-seam rule's catch-fallthrough blind spot, and record the decision NOT to extend (#8845) - #8898

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-8845-durability-catch-fallthrough
Aug 15, 2026
Merged

measure the read-seam rule's catch-fallthrough blind spot, and record the decision NOT to extend (#8845)#8898
os-project-manager merged 1 commit into
mainfrom
claude/issue-8845-durability-catch-fallthrough

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#8845

This card was a measurement, and the measurement came back "do not extend". No criterion is added, no vocabulary entry, no baseline entry, no behaviour change. What lands is the measurement itself, written into the checker's own header — which is this repo's declared authority on the rule — so the next author who notices this blind spot reads the numbers before re-proposing the criterion.

Measurement taken on origin/main @ 8664a2c (the scan roots move fast — packages/metadata-protocol/src/protocol.ts took 10 commits in two days, so the sha is load-bearing). Gate union re-run at final HEAD 647f5cf.

1. The premise holds, but the mechanism in the card is slightly wrong

The card says "There is no return statement to classify". That is not quite it, and the difference matters for anyone trying to fix it.

walkBenignPaths()does model the fall-off-the-end exit — it pushes { benign, expr: undefined, node: block }. The exit exists in the model. What happens next is that inventedEmptyValue() and identityPassThrough() both answer undefined for a valueless exit, so it is never classified as an invention. That is the same deliberate exclusion the empty-value table already documents for a bare return; ("in a Promise-of-void method that is not an invented answer at all"), which happens to cover fall-through too.

So the honest statement is: the shape is not invisible to the AST, it is declined by a documented exclusion whose stated reason does not extend to this case.

Confirmed, and both of the card's other claims with it:

Two-direction ablation, because a green run proves nothing here

This gate reads package source, not dist/, so no rebuild is involved. Planted in a scan root (packages/metadata-protocol/src/build-probes.ts), then reverted:

planted seamgate
catch { return []; }exit 1, RED — proves the gate is live on this file
same read, rewritten to fall through into an empty accumulatorexit 0, GREEN — census goes 66 seams to 67 and the planted seam is listed as no invented answer

Surveyed, counted, cleared. That is the #6116 shape a third time, and it is the evidence the card asked for.

2. The census

The scan roots' 66 read seams, narrowing one criterion at a time. Every number below is a first measurement, not a ratchet being raised — nothing here was previously counted.

criterionseams
read seams in scope (matches --list exactly)66
catch has no return anywhere46
catch has a valueless exit41
... and is silent (no log at any level, helpers followed)31
... and that exit is not type-discriminated25
... and an empty accumulator sits above the try, written inside, read below15

3. Why 15 is not #6451's zero

#6451 landed because its true new red set was zero — "the strongest possible answer to is the red set controllable". This one would land 15 red on a ledger that holds one entry.

7 of the 15 are already correct and would each need a baseline entry. Every one of them delivers the failure: errors++ into a returned { deleted, errors } (history-cleanup, 3 seams), issues.push into the returned probe report (build-probes), failed.push into the returned envelope (deletePackage, discardPackageDrafts), and report.unreadableObjects.push in the dangling-reference audit — a seam written precisely so its report "cannot be mistaken for a clean bill of health". Baselining seven correct seams to land a criterion is the "baselined into uselessness" outcome the card names, in advance.

The obvious exemption for them is unsound, and measurably so. "The catch WROTE something that is read later" cuts 15 to 7 — but it is inferred, not declared, which is the one discipline this file holds everywhere ("Declared, never inferred"); it would be a second de-facto propagation vocabulary drifting alongside the real one; and it clears publishPackageDrafts, whose catch pushes a fabricated revert-plan entry (existedBefore: false, prevVersion: null) after a failed read. An exemption that fires on an invention is not an exemption.

Narrowing further makes it worse, not better. Also exempting a catch whose only statement is a jump cuts the red set to 2 — diffMetaItem and one other. That looks like a clean result and is the trap: it buys the number by exempting three real instances, including cascadeDeleteRelations, where a failed dependents probe skips a restrict guard outright. Tuning a criterion until only the instance you already knew about is red is how a gate stops meaning anything.

Even the true positives are flagged for the wrong reason.findReferencesToMeta's harm lives in out, not the flagged items; cascadeDeleteRelations and checkGovernance have no accumulator at all, only a skipped guard. A message naming the wrong variable teaches the wrong fix.

And the scope is the only thing holding the line. Drop the READ vocabulary and this shape matches 91 of the 314 catch clauses in the three roots. Since #5186 explicitly defers "evaluate widening as its own issue", that is the cliff the criterion would be sitting on.

4. What the shape actually needs

Not a looser invention criterion — a declared failure-propagation vocabulary for the read-seam rule. The log-level rule has one (FAILURE_PROPAGATION_CALLEES / _SITES); the two rules share none, on purpose. With one, "the catch reported it" becomes a declared, checkable fact and the 7 already-correct seams stop being noise. That is a design question with a maintainer in it, not a criterion extension, so it is left un-taken here and written down instead.

5. diffMetaItem was not touched

Its disposition stays with #8833, where the maintainer has three candidate response contracts. Since no criterion was added, no extension went red on it and there was nothing to resolve. Its seam is unchanged.

6. On the CI wiring question raised in #8889

Checked, because if the gate were not running on source changes then "green on main" would mean "did not run" and the card's baseline would be different. It runs..github/workflows/lint.yml has no paths: filter at all — not on the on: block, not on the lint job, not on the step. Every paths hit in that file is prose explaining why there deliberately is none. So the gate executes on every PR to main, every merge-queue build and every push to main, whatever changed.

The derivation that names the gate is scripts/pm/dispatch-gates.mjs, which reads path literals out of each check script's own source — and it resolves correctly:

$ node scripts/pm/dispatch-gates.mjs packages/metadata-protocol/src/protocol.ts
- pnpm check:durability-log-level [lint.yml] matched via
packages/metadata-protocol/src/protocol.ts ⇢ 'packages/metadata-protocol/src'

main-green is therefore a real measurement, and the answer to "would an extension ever fire in CI" is yes.

7. Findings filed, not fixed

The census turned up seams that are defects on their own terms. Filed unassigned, none touched here:

Verification

Union re-run after the final commit, at 647f5cf:

$ node scripts/check-durability-degradation-log-level.mjs # exit 0
✓ durability-degradation log levels: 25 durability-critical catch seam(s), all loud, ...
✓ read-seam invention (#5186 + #6451, 3 package roots): 66 read seam(s), none invents
an unreported answer (7 answer on a type-discriminated benign branch)
(1 pass an input through, reported) (1 baselined)
$ node scripts/check-durability-degradation-log-level.mjs --self-test # exit 0
✓ self-test (read-seam invention rule): 35 case(s) passed
✓ self-test (log-level rule): passed
$ node scripts/check-nul-bytes.mjs # exit 0
check-nul-bytes: OK (scanned 5894 text file(s); no raw ASCII control bytes)

Gate families re-derived against the real diff (node scripts/pm/dispatch-gates.mjs scripts/check-durability-degradation-log-level.mjs): check:durability-log-level only, plus the convention-scoped check:nul-bytes. Both above. Judged by exit status, not by grepping output.

Comment-only change to a root scripts/ file, which is not a published package, so no changeset — skip-changeset applied.


Generated by Claude Code

…am rule to catch-fallthrough (#8845)
The read-seam invention rule (#5186, extended by #6451) classifies a `catch`
by the expression it returns, so a `catch` that returns nothing and lets an
accumulator declared above the `try` answer for a read that never happened is
cleared. Measured under the same 收窄先行 discipline #6451 followed, across the
same three scan roots, before proposing any criterion.
The measurement argues against the extension, so it is the measurement that is
recorded — in the file's own header, which is this repo's authority on the
rule — rather than the criterion being added:
- 66 read seams; 41 have a valueless exit; 25 silent and undiscriminated;
15 also have an empty accumulator declared above, written inside, read below.
- 7 of the 15 are already correct and would need baseline entries on a ledger
that holds exactly one today.
- The obvious exemption ("the catch wrote something read later") is inferred
rather than declared, and clears a seam that pushes a fabricated revert-plan
entry after a failed read.
- Narrowing to a red set of 2 buys it by exempting three real instances,
including a skipped `restrict` guard in cascadeDeleteRelations.
- Dropping the READ vocabulary matches 91 of 314 catch clauses in the roots.
No behaviour change: no criterion, no vocabulary entry, no baseline entry.
Header comment only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011RB4waLuNbdruCo6X9oobm
@vercel

vercelBot commented Aug 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 15, 2026 3:48pm

Request Review

@os-project-manageros-project-manager added tooling skip-changeset PR has no user-facing published change; bypasses the changeset gate labels Aug 15, 2026 — with Claude
@os-project-manager
os-project-manager marked this pull request as ready for review August 15, 2026 16:00
@os-project-manager
os-project-manager added this pull request to the merge queueAug 15, 2026
Merged via the queue into main with commit 7c85bbeAug 15, 2026
28 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-8845-durability-catch-fallthrough branch August 15, 2026 16:08
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-project-manager@claude