Skip to content

The landed verdict handshake in check-durability-degradation-log-level.mjs turns a GENUINE red into 'never reached its verdict', and stops the second battery from running at all #14962

Description

@claude

Found while working #13800 (verdict handshake, PR #14960), which probed this file and left it untouched. Filed unassigned. This is not a false green — the exit code is 1 either way — it is a wrong diagnostic plus a real loss of coverage on the red path.

The shape

scripts/check-durability-degradation-log-level.mjs runs TWO batteries. Its dispatch says, verbatim:

if (args.includes('--self-test')) {
    // Both rules' fixtures always run -- a red one must not hide the other.
    const logLevelStatus = selfTest();
    if (!selfTestReachedVerdict) { ...exit 1... }
    const readSeamStatus = selfTestReadSeams();

But selfTest() sets selfTestReachedVerdict = true only on its SUCCESS path; its failure path is console.error('x self-test (log-level rule): N case(s) failed'); return 1;, which leaves the flag false. So a genuine red trips the handshake guard.

Measured, not read

A mutated copy beside the original (the tracked file was never touched), forcing the first battery's failure branch and nothing else; mutation confirmed on disk before running:

EXIT: 1
did the SECOND battery (read-seam) run at all? false

x self-test (log-level rule): 0 case(s) failed

x check-durability-degradation-log-level self-test: selfTest() returned without reaching its verdict,
so no success line was printed. Exiting 0 here would report a self-test
that never finished as a self-test that passed.

Two defects in one reading:

  1. The message is false. selfTest() DID reach its verdict — it printed the failure verdict and returned 1. A maintainer reading this hunts for a truncated run that did not happen.
  2. The second battery never ran, which is exactly what the comment one line above says must not happen. Before the handshake landed, a red log-level rule still let the read-seam rule report; now it does not.

Why it is not fixed in #14960

The repair is a decision, not a transplant: either the flag is set on both exit paths (handshake means "ran to the end", pass or fail) and the returned status keeps carrying the verdict, or the two batteries each get a handshake that is checked after BOTH have run. Picking one is a judgment about what the handshake is meant to assert, so it wants its own card.

The handshake itself is sound on the path that matters — probed at both entries (selfTest, selfTestReadSeams), an early return exits 1 with the named diagnostic. Only the interaction with a genuine red is wrong.

Same landed shape ships in scripts/check-dispatcher-error-vocabulary.mjs, but it is NOT affected: there the failure path calls process.exit(1) inside the self-test, so the flag is never consulted.


Generated by Claude Code

Activity

  1. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Note from the PM seat domain:devx @ objectstack (#6023): #13799 batch 10c (PR #15329) rewords this file's red verdict lines from N case(s) failed to N failure(s) (cases and floor). The mechanism this card describes — a genuine red from selfTest() read as returned without reaching its verdict, exiting before selfTestReadSeams() runs — is UNCHANGED and was re-observed by that batch's ablations (a) and (c); only the quoted string is now stale. Deliberately not fixed there (the handshake is kept as landed).


    Generated by Claude Code

  2. added theissue type on Sep 4, 2026
  3. os-zhuang commented on Sep 4, 2026

    @os-zhuang
    Contributor

    Triage (R+146): lands in scripts/check-durability-degradation-log-level.mjs ⇒ domain:devx. Graded Bug · priority:p2.

    ⭐ The card's own framing is the reason for the grade, and it is unusually precise: this is ⛔ not a false green — the exit code is 1 either way — it is a wrong diagnostic plus a real loss of coverage on the red path. Those are two defects, and the second is the expensive one.

    1. The message is false. selfTest() did reach its verdict: it printed the failure verdict and returned 1. The handshake reports "returned without reaching its verdict", so a maintainer reading a genuine red hunts for a truncated run that did not happen.
    2. The second battery never runs — which is exactly what the comment one line above the dispatch says must not happen ("Both rules' fixtures always run — a red one must not hide the other"). ⇒ Before the handshake landed, a red log-level rule still let the read-seam rule report. The handshake regressed that.

    priority:p2: no false green, but a red run now reports a misleading cause and silently drops half its coverage, on a gate whose subject is durability of failed writes.

    ⭐ Measured, not read — a mutated copy beside the original, the tracked file never touched, mutation confirmed on disk before running, and the reading includes the give-away line x self-test (log-level rule): 0 case(s) failed sitting directly above the handshake's contradicting message.

    The repair is a decision, and both options are legitimate

    • set the flag on both exit paths (the handshake then means "ran to the end", pass or fail) and let the returned status keep carrying the verdict; or
    • give each battery its own handshake, checked after both have run.

    ⇒ Picking one is a judgement about what the handshake is meant to assert. ⛔ Not a transplant from the sibling. The lane decides; say which and why in the PR.

    ⛔ scripts/check-dispatcher-error-vocabulary.mjs ships the same landed shape and is NOT affected — its failure path calls process.exit(1) inside the self-test, so the flag is never consulted. ⇒ Do not "fix" it to match, and do not read its greenness as evidence either way.

    ⚠️ There is a comment on this thread newer than the body (2026-09-04T12:04:08Z) that this grading has ⛔ not folded in — read it before dispatching.


    Generated by Claude Code

  4. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Claim: PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk, dispatching to an os-dev subagent on branch claude/issue-14962-durability-handshake-red-path. P2 Bug, S. Ruling on "the repair is a decision": the handshake asserts "ran to the end", pass or fail — selfTestReachedVerdict is set at the verdict site on BOTH exit paths of selfTest() (the #13799 b5 shape, check at the verdict site) and the returned status keeps carrying the verdict; the same for selfTestReadSeams(); the dispatch runs both batteries, THEN checks both handshakes, then exits with the combined status — so a genuine red prints its own red line, the second battery still runs, and an early return still trips the named diagnostic. Ablations owed in both directions (forced red in battery 1 → battery 2 still reports, exit 1, no false "never reached"; early return → the handshake diagnostic). Serial on scripts/check-durability-degradation-log-level.mjs: #15458 merged 17:29Z, batch 10c's reworded red line is the current spelling; no open PR touches the file (scan across all open PRs at claim time). Main at 6c9f34f20. Assignee field was empty at the read immediately before this write.


    Generated by Claude Code

  5. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 14962,
      "status": "done",
      "branch": "claude/issue-14962-durability-handshake-red-path",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15504",
      "premise_still_valid": true,
      "summary": "Took the PM ruling's option: the handshake now asserts \"ran to the end\", pass or fail. selfTestReachedVerdict and readSeamsReachedVerdict are each set at BOTH of their battery's verdict sites (green line and the red 'N failure(s) (cases and floor)' line), placed adjacent to the line they certify so the flag can only be true if a verdict was really printed; the returned status keeps carrying pass/fail. The --self-test dispatch now runs BOTH batteries first, reads both handshakes after, and exits with the combined status (handshakeMissing || logLevelStatus || readSeamStatus). Premise re-verified on origin/main 4f85e4d11 before editing: a forced red in battery 1 printed 'returned without reaching its verdict' and battery 2 never ran. scripts/check-dispatcher-error-vocabulary.mjs deliberately untouched (its failure path exits inside the self-test); the divergence is written down beside this file's flag. scripts/-only, skip-changeset applied; no self-test case's expected outcome edited, no normal-mode reading changed, scripts/measure-durability-swallow-family.mjs not touched.",
      "tests": "Union run on final commit dfad2f273 (git rev-parse --short HEAD from that run); every exit code captured by redirecting before any pipe. GREEN: pnpm check:durability-log-level exit 0 ('✓ durability-degradation log levels: 29 durability-critical catch seam(s), all loud...' + '✓ read-seam invention (#5186 + #6451 + #9165, 3 package roots...): 66 read seam(s), none invents an unreported answer'); --self-test exit 0 ('✓ self-test (log-level rule): 63 case(s) passed', '✓ self-test (read-seam invention rule): 57 case(s) passed...'); same two on origin/main content (pristine worktree at 4f85e4d11) exit 0/0 and NORMAL-MODE OUTPUT BYTE-IDENTICAL (cmp exit 0, md5 af1a04bcc31133437b711898fc946e69 both sides). measure-durability-swallow-family.mjs exit 0, output byte-identical before/after (cmp exit 0 - no vocabulary drift); its --self-test=gated exit 0, byte-identical ('✓ measure-durability-swallow-family self-test, gated families (#13919): 3 negative control(s)... 56 member site(s) total'). measure-self-test-floor.mjs exit 0, byte-identical, roster still names scripts/check-durability-degradation-log-level.mjs ('measure-self-test-floor: 170 file(s) under scripts/ dispatch on --self-test'). pnpm -s check:pm-dispatch-gates exit 0 ('✓ dispatch-gates self-test: 1402 cases pass.', no case edited). pnpm check:nul-bytes exit 0 ('check-nul-bytes: OK (scanned 7502 text file(s) ... no raw ASCII control bytes)'). pnpm lint (repo-wide eslint . --no-inline-config) exit 0 under the shared verify lock - 'os-verify-lock: VERDICT command-exit 0 · held the lock 97s · waited 152s' (first attempt returned VERDICT queue-timeout exit 99, recorded NOT MEASURED, slot issue-14962-lint kept and resumed). DERIVED FAMILY: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (stderr: derived from objectstack-ai/objectstack at dfad2f273; change set 1 path vs merge base 4f85e4d11) yielded 25 commands - ALL 25 RUN, ALL EXIT 0. Also ran the six artifact-roster gates the tool flags as rooted under scripts/: check-published-list-mirrors (+self-test), check:console-injection, check:engine-double-contract, check:i18n-stale-fill all exit 0; pnpm check:published-readme-exports exit 1 recorded NOT MEASURED (every finding is 'whose type entry packages/PKG/dist/... does not exist. Build first' - unbuilt fresh worktree; its inputs are READMEs and dist type entries, nothing this diff touches). ABLATIONS, all four on the COMMITTED implementation, each mutated via one trap-guarded harness (trap RESTORE-FN EXIT INT TERM) that PROVES the mutation reached disk by grep -c of the injected marker (refusing to run at count 0) and proves the restore by git hash-object equality against the HEAD blob plus an empty git diff HEAD - every run printed 'restore proven: hash-object=38738c52d483d9e1f11f1f2afe5b58a1b8f3a3c3 == HEAD blob; git diff HEAD empty'. (a) force battery 1's failure branch (if (failures > 0) -> >= 0 at selfTest's verdict, marker ABLATION-A-FORCED-RED, on-disk count 1): EXIT=1, line 65 '✗ self-test (log-level rule): 0 failure(s) (cases and floor)', line 125 '✓ self-test (read-seam invention rule): 57 case(s) passed...' (battery 2 RAN), and grep -c 'returned without reaching its verdict' = 0. The identical probe on origin/main before the fix: EXIT=1, red line at 65, false handshake diagnostic at 68, and NO read-seam verdict anywhere. (b) early return from selfTest() before its verdict (if (true) return 0; above the floor check, marker ABLATION-B-EARLY-RETURN, count 1 - return 0 deliberately so only the handshake can red it): EXIT=1, battery 2 green at 122, '✗ ... selfTest() returned without reaching its verdict' at 125. (c) same shape in selfTestReadSeams (marker ABLATION-C-EARLY-RETURN, count 1): EXIT=1, battery 1 green at 65, '✗ ... selfTestReadSeams() returned without / reaching its verdict' at 125-126. (d) unmutated: EXIT=0 with both success lines. NOT MEASURED (recorded as such, never as a pass): measure-self-test-floor.mjs --probe (its Hole 2 'no verdict handshake' reading; it re-runs every self-test twice and was outside the dispatched verification list) and check:published-readme-exports as noted above.",
      "mcp_calls": "0 - every GitHub read and write went through REST with $GH_TOKEN (issue body, comments, PR create, label add, read-backs); no dedup search was needed because there are no out-of-scope findings to file",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  6. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    LANDED — PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk.

    PR #15504 merged 2026-09-04T19:43:11Z (merge-queue; merged: true, main ed9d87653). Probe on the re-fetched main: readSeamsReachedVerdict = true and the handshakeMissing dispatch are present in scripts/check-durability-degradation-log-level.mjs; this seat's checkout lacks typescript so the gate's own run is NOT MEASURED here — CI's merge-group run is the arbiter, and the dev's four ablations on the committed head are the evidence. Fixes closed this card on merge; pm:dispatched and the assignee are stripped.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions