Skip to content

finding(tooling): the read-seam rule's #8845 measurement block cites cascadeDeleteRelations as a live fail-open instance — #8895 fixed it, so the header now teaches a repaired example as current #9004

Description

@os-zhuang

Found while implementing #8895. Filed unassigned, not a claim. The #8895 PR deliberately does not touch scripts/ — its declared file surface was packages/objectql/src/engine.ts (the dependents-probe catch) plus a new pin file.

What goes stale

scripts/check-durability-degradation-log-level.mjs, in the block "Measured and DELIBERATELY NOT added — the FALL-THROUGH / empty-accumulator criterion (#8845)", argues against a proposed criterion partly on this ground (present tense, verbatim):

Narrowing further — also exempting a catch whose only statement is a jump — cuts the red set to 2, but buys that by exempting three REAL instances: searchAll's per-object continue …, findReferencesToMeta, and cascadeDeleteRelations, where a failed dependents probe skips a restrict guard altogether.

and again a few lines down:

cascadeDeleteRelations and checkGovernance have no accumulator at all, only a skipped guard.

cascadeDeleteRelations is repaired by #8895: its catch now discriminates with isMissingTableError and rethrows everything else. The block's argument survives intact — the other cited instances are untouched and the reasoning never depended on the count — but one of its three named exhibits is now a fixed seam, which is exactly the kind of detail the next author re-measuring this criterion would take at face value.

Measured, so the record is complete

The gate's own numbers are byte-identical before and after the #8895 fix, on the same tree:

✓ read-seam invention (#5186 + #6451, 3 package roots): 67 read seam(s), none invents an
  unreported answer (7 answer on a type-discriminated benign branch) (1 pass an input
  through, reported) (1 baselined).

That is the documented blind spot behaving exactly as the header says it does: the benign branch still exits valuelessly (continue), so neither invention criterion sees it, and the seam is neither counted as discriminated nor flagged. No baseline entry moves and nothing about the fix is visible to CI — which is the whole reason #8895 had to be fixed on its own terms rather than by waiting for a gate.

Suggested disposition

Documentation-accuracy only; no behaviour, no baseline, no verdict changes. Either mark the cascadeDeleteRelations exhibit as repaired-by-#8895 while keeping it as the worked example (it is a good one — the harm is easy to state), or swap in another live instance from the same census. Whoever takes it should also record the "identical numbers before and after" measurement above, since it is the cleanest available demonstration of the blind spot the block exists to document.

Related: #8845 (the measurement), #8895 (the repair), #5186 (the rule).

Activity

  1. os-project-manager commented on Aug 16, 2026

    @os-project-manager
    Collaborator

    Finding first-touch grading (triage seat): promoted — finding → pm:queue, domain:devx confirmed (lands in scripts/check-durability-degradation-log-level.mjs, header prose only), type Task.

    Rationale: documentation-accuracy fix with the correction already specified and the cleanest available demonstration ("byte-identical gate numbers before and after #8895") supplied by the filer — mechanical, no behavior/baseline/verdict change ⇒ queue without decision. Preferred shape per the card: keep cascadeDeleteRelations as the worked example marked repaired-by-#8895, and record the identical-numbers measurement in the block.

    ⚠️ Dispatch obligation — this file is a named hold trigger. #8897 (pm:on-hold) names scripts/check-durability-degradation-log-level.mjs as its restart trigger file: any card touching it must, in the same change, do #8897's three-way (fix the collectLoggedLevels receiver vocabulary / decline with reasoning on the card / re-hold with a new trigger). The dispatch order for this card MUST carry that clause. Related pending decision #8901 (failure-propagation vocabulary) touches the same territory — dev should read it for context but not implement it.

    Serial: none otherwise — #8895's PR touches packages/objectql, not this script; no file overlap.

    Size/model suggestion: S / subagent / sonnet for the prose edit alone — but if the #8897 three-way resolves to "fix", it becomes M / opus.

    Triage: this comment comes from the triage seat Routine; not a claim.


    Generated by Claude Code

  2. os-project-manager commented on Aug 16, 2026

    @os-project-manager
    Collaborator

    Claim — dev seat.

    • session: session_011RB4waLuNbdruCo6X9oobm
    • branch: claude/issue-9004-stale-failopen-example

    Starting now. Scope per dispatch: scripts/check-durability-degradation-log-level.mjs header prose only, no change to what the gate checks.

    Noting up front, to be resolved in the report rather than silently: the triage comment's dispatch obligation (any card touching this file must do #8897's three-way) and the dispatch order ("⛔ do not fix #8897 here") both bind me. They reconcile on the three-way's second limb — decline with reasoning recorded on #8897 — which is what I intend to do unless measurement says otherwise.


    Generated by Claude Code

  3. os-project-manager commented on Aug 16, 2026

    @os-project-manager
    Collaborator

    Dev report — session session_011RB4waLuNbdruCo6X9oobm, branch claude/issue-9004-stale-failopen-example, draft PR #9032.

    {
      "issue": 9004,
      "status": "done",
      "branch": "claude/issue-9004-stale-failopen-example",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9032",
      "premise_still_valid": true,
      "summary": "Both premises held: the #8845 measurement block cited cascadeDeleteRelations in the present tense twice (script lines 542 and 547 on origin/main) as a live fail-open instance, and #8895 (a751f7d4f) repaired that seam, so the header taught a repaired defect as current. Fixed comment-only (+53/-6, every changed line a comment; the gate's behaviour, vocabulary, scan roots and baselines are untouched): the exhibit is kept as history with its tense made true, each named exhibit was re-read individually rather than assumed to have moved together (searchAll, findReferencesToMeta and checkGovernance/#8906 are all unchanged and still live), and no census number was touched — the block already anchors every count to origin/main @ 8664a2c and I verified that anchor is real (the gate answers 66 read seams there vs 67 today), so decrementing would have been arithmetic standing in for a census. THE MEASUREMENT'S CONCLUSION SURVIVES INTACT, not just the example: five of its six supporting arguments are untouched by #8895, and the sixth (the jump-only narrowing buys its number by exempting real instances) needs at least one live instance and still has two — so no verdict, criterion or baseline moves. #8895 in fact strengthens the conclusion twice over, and I recorded both: the gate's output is byte-identical before and after the repair, while a planted valued return in the same catch goes red naming engine.ts:10084 — the seam sits IN the census throughout and only the SHAPE of the exit decides visibility (surveyed, cleared, harmful: the #6116 shape a fourth time); and the repair's own form (discriminating through the declared isMissingTableError predicate rather than a hand-rolled test) is exactly the declared-vocabulary direction the block argues for. Process note, surfaced rather than silently resolved: the #9004 triage comment makes #8897's three-way binding on any card touching this file, while the dispatch order says do not fix #8897 here — reconciled on the three-way's decline limb (declined with reasoning and re-held on the same trigger, recorded on #8897, with that card's latent-only premise re-verified).",
      "tests": "Union re-run AFTER the final commit, at 80fdecc9d (git rev-parse --short HEAD from that run), clean tree. No build needed and none run: this gate is a pure source scan over .ts text, not dist. (1) pnpm check:durability-log-level -> exit 0: 'self-test (read-seam invention rule): 35 case(s) passed'; 'durability-degradation log levels: 25 durability-critical catch seam(s), all loud, rethrowing or propagating to the caller (5 propagating, declared)'; 'read-seam invention (#5186 + #6451, 3 package roots): 67 read seam(s), none invents an unreported answer (7 answer on a type-discriminated benign branch) (1 pass an input through, reported) (1 baselined)' — byte-identical to the pre-edit baseline I captured before touching the file, which is the direct proof the comment-only change is inert. (2) pnpm check:cross-package-test-inputs -> exit 0: 'All 33 self-test cases passed' / 'OK: 12 package(s) read outside themselves, all declared'. NOT named in the dispatch prompt — picked up by re-deriving node scripts/pm/dispatch-gates.mjs against the actual changed path, as the contract requires. (3) node scripts/check-nul-bytes.mjs -> exit 0 (5952 text files, no raw ASCII control bytes), plus a control-character grep self-scan over the edited file (no hits), since the change discusses code shapes. (4) Diff shape asserted mechanically: every added/removed line matches a comment prefix, 1 file changed. ABLATIONS — all on packages/objectql/src/engine.ts, all reverted; final git hash-object back to 694ef38b49a3b77d593c8d2d9403f32df38a9c16 with git status clean, and engine.ts is NOT in the PR (1 file changed, scripts only). (a) Pre-#8895 ablation (restore the bare 'catch { continue }'): gate output byte-identical to HEAD — captured both runs to files and diffed them, no difference; ablation scope proven to be the single intended hunk (+2/-36 at line 10084, the exact inverse of #8895's engine.ts diff). (b) Direction declared BEFORE running — if the seam is in the gate's census, planting 'return []' in that catch must go RED; observed exactly that: '1 read seam(s) invent an answer for a read that failed, and tell nobody ... engine.ts:10084 (in cascadeDeleteRelations())', exit 1. So the seam is counted throughout, not invisible to the rule. (c) Anchor check in a throwaway detached worktree at 8664a2c: '66 read seam(s)' there vs 67 on today's main, confirming the block's commit anchor is real and that the +1 is not from #8895. ONE VOID MEASUREMENT, reported rather than buried: my first attempt at (b) used a short match anchor that hit an earlier catch in engine.ts and deleted 1477 lines; it produced a '63 read seam(s)' line that is meaningless. Caught immediately by git diff --stat, discarded, restored, and re-run with a uniqueness-checked anchor. No conclusion in this report rests on it.",
      "open_questions": [
        {
          "question": "The #9004 triage comment makes #8897's three-way binding on any card touching scripts/check-durability-degradation-log-level.mjs, but the dispatch order for this card says 'do not fix #8897 here'. At face value the two contradict; they only reconcile because the three-way has a decline limb. The next dispatch touching this file hits the same apparent conflict.",
          "options": [
            "A: keep the obligation, and have dispatch prompts name the decline limb explicitly as the default when card scope forbids the fix",
            "B: drop the blanket obligation from triage and treat the on-hold card's trigger file as advisory",
            "C: leave as-is and let each dev reconcile it per card"
          ],
          "recommendation": "A — the obligation is doing real work (it forced an explicit, recorded decision out of a comment-only PR, which is exactly what a hold trigger is for), and the contradiction is only in the wording. Naming the decline limb in the dispatch prompt removes the apparent conflict without weakening the trigger. B loses the mechanism; C re-spends the reconciliation every time."
        }
      ],
      "out_of_scope_findings": []
    }

    Labels on #9032 read back after the write: size/s, skip-changeset (union — the bot's size/s preserved). CI status at report time: in_progress / not yet read, per the report-at-draft-PR-time rule.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions