Uh oh!
There was an error while loading. Please reload this page.
test(showcase): make the inbox watch report WHY it ended, so a timeout cannot pass as an absence - #10158
Merged
os-warren merged 1 commit intoAug 20, 2026
Conversation
…t cannot pass as an absence `deliveredInbox` returned whatever it had accumulated when its 5 s deadline passed. Nothing distinguished that from a real absence, and both halves of the consequence were measurable on this file: - the two positive call sites failed with an annotation asserting something untrue — "the Notify: Cleared inbox message was never delivered" for a message that had been delivered, the poll having merely given up; - the negative call site PASSED on a give-up: with a matching row inserted 200 ms into the window, `expect(...).toHaveLength(0)` stayed green. The mechanism is that a `find` issued at T and resolving at T+delta reports the inbox as it stood at T, while the deadline is checked at T+delta. So the verdict no longer rests on that sample: once the deadline has passed, one final fresh read is taken and that read is judged. `sys_inbox_message` rows accumulate, so a single read after the window sees everything the window delivered. The watch now returns a discriminated verdict. `deliveredInbox` throws on timeout, naming it a timeout rather than an absence; `inboxStayedQuiet` returns the verdict and the negative call site asserts on `outcome`, which a watch that gave up cannot produce. The 5 s window is unchanged, and the new budget knob is clamped to it, so shrinking the window is expressible and widening it is not. Fixes#10106 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PnJHU45vPJj5UQrxe946Bx
os-warren
marked this pull request as ready for review
August 20, 2026 12:15
os-warren
enabled auto-merge
August 20, 2026 12:15
Uh oh!
There was an error while loading. Please reload this page.
os-warren
deleted the
claude/issue-10106-delivered-inbox-timeout-distinguishable
branch
August 20, 2026 12:37
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 freeto 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#10106
The premise, re-established before anything was changed
The card names two halves. Both were measured on this branch against a fixed base, not reasoned about.
Half 1 — the helper returns short on timeout. The shipped
deliveredInboxbody was copied verbatim into a probe (one mechanical substitution:booted.inbox()became an injectedinbox(); the5_000budget untouched) and driven by an inbox whose read is issued at one moment and resolves much later, reporting the rows that existed at issue — what a realfinddoes. With the row landing at t = 1 s, squarely inside the window, the helper answered[].Half 2 — the stays-empty call site passes for the wrong reason. Shown on the real call site, on the real stack, with the base held fixed. One mutation, applied identically to both versions of the file: a matching
sys_inbox_messagerow inserted 200 ms into the window, plus a first inbox read issued at t = 0 that resolves at t = 5.5 s reporting the inbox as it stood at issue.origin/main(blob332973b0)Tests 4 passed— green, with the message sitting in the inboxa9fd1ae5)AssertionError: the inbox did not stay quiet: delivered after 5502 ms with 1 match(es) … expected 'delivered' to be 'quiet'An earlier run of the same shape also produced the misdiagnosis half on the two positive call sites, verbatim from the log:
AssertionError: the Notify: Cleared inbox message was never delivered: expected [] to have a length of 1— for a message that had been delivered.The mechanism, since it decides the shape of the repair
A
findissued at T and resolving at T+Δ reports the inbox as it stood at T, while the deadline is checked at T+Δ. Under the load this suite runs under, Δ can straddle the whole window — so the verdict about the window rested on a sample taken near its start. Raising the budget moves Δ's threshold and changes nothing structural, which is why the card rules it out and why this PR does not touch the 5 s.What changed
watchInboxreturns a discriminated verdict —delivered/quiet/timeout— instead of a bare array that conflated the last two.sys_inbox_messagerows accumulate and are never retracted, so a single read past the window sees everything the window delivered — which is why one terminal read is sufficient evidence, and why poll density is not the thing to guard (no arbitrary sample-count threshold was invented, and nothing here is load-sensitive).deliveredInbox(the positive face) throws on timeout, naming a timeout rather than an absence. The two positive annotations were retuned to the only direction still left to them: duplicate delivery.inboxStayedQuiet(the negative face) returns the verdict; the call site asserts onoutcome.'quiet'is constructible at exactly one place — after the terminal post-deadline read — so a watch that gave up cannot satisfy a stays-empty assertion.Five contract pins were added for the helper itself. They boot no kernel — the subject is which verdict
watchInboxreturns, whose only input isbooted.inbox— and they run in milliseconds. One is deliberately a vacuity control (an empty window really does read as quiet), so the load-bearing pin above it cannot be green for the trivial reason.Ablation
Predicted first: neuter the terminal fresh read (
const final = hits, i.e. judge the pre-deadline sample as before) ⇒ exactly one test red —stays-quiet is NOT satisfiable by a watch that gave up— withexpected 'quiet' to be 'delivered'; the other eight, including the vacuity control, stay green.Observed:
Tests 1 failed | 8 passed (9),AssertionError: a row delivered inside the window was reported as quiet: expected 'quiet' to be 'delivered'.Restore proved byte-identical.
git hash-objecton the file:a9fd1ae5c5fc52ee59ba81e4d7f35157ece1027cbefore the mutation,242cf9981005df75d905fef1482a4d8e12531d5awhile ablated,a9fd1ae5c5fc52ee59ba81e4d7f35157ece1027cafter restore. The same proof was carried across the two directional mutations.No rebuild was needed for the mutation to be observable, and this is argued from the files rather than assumed. The subject is a
.test.tsexecuted by vitest from source;watchInboxand its pins live in that one file and cross no package boundary. The pin that observes the ablation drives a fake inbox and reaches none of the workspace packages'dist/, so nothing about the measurement is a function of build state. (The workspace closure was built forcheck:type-check-debt, which refuses to measure without it.)Sibling sweep
Swept for other helpers of the same return-short-on-timeout shape. Counter-check carried, because a zero result is not a finding: the same grep for
toHaveLength(0)overorigin/main's copy of this file returns the known-present call site at line 362, so the search works.Wall-clock waiters exist elsewhere (
packages/metadata-fs/test/external-write-resync.test.ts,watch-write-registration.test.ts,packages/metadata/src/metadata-repository-fs-dot-root.test.ts) and share the silent give-up shape, but every one of their call sites is followed by a positive or exactness assertion on the same predicate, so a timeout there yields a red, never a green — a different and milder class.packages/metadata-fs/test/watch-dot-root.test.tsonce carried the false-pass class outright and was rewritten under #7408 to bracket the quiet window between two observable deliveries; its docblock is prior art for this repair and states the rule directly — you cannot wait for an absence. Nothing new to file.Deliberately not done
watch-dot-root.test.tscase 2's 4s quiet window degrades to a false pass under load — measured against 32-36s delivery #7408's stronger shape brackets the quiet window between two deliveries the test can observe. Here the stranded run emits nothing to bracket against, so manufacturing a sentinel would change what the case exercises, and its ordering against the target message would need an argument this branch cannot make. The verdict-based repair removes the false-pass mechanism the card named; the bracket would be a further strengthening, recorded rather than silently skipped.check-type-check-coverage.mjsledger entry was raised —--re-measurereportssurplus: none — every entry sits exactly at its measurement.@objectstack/example-showcase, aprivate: trueexample package: the PR releases nothing, which isskip-changeset's own stated case inpr-automation.yml. The label is applied rather than a changeset written.Verification
All on
b885a00c0, clean worktree, exit codes captured before any pipe.Gate union derived with
node scripts/pm/dispatch-gates.mjs(no paths passed) after the final commit, each quoting the gate's own verdict line:check:cross-package-test-inputsOK: 12 package(s) read outside themselves, all declared…(+All 57 self-test cases passed.)scripts/check-cross-package-test-inputs.mjsOK: 12 package(s) read outside themselves, all declared…check:query-options-erasure✓ query-options-erasure ratchet holds: 67 unswept non-test site(s)… none newcheck:type-check-coverageOK — 64/77 workspace packages type-checked…check:type-check-debt--re-measure: OK — 33 ledger entr(ies) re-measured… none above its recorded numbercheck:engine-double-contractOK — 329 pinned, 133 in the DEBT ledger, 2 exempt.check:where-matcher✓ where-matcher conformance holds: 264 matcher(s) discovered…check:nul-bytes✓ check-nul-bytes --self-test: 75 assertions over a temp git repocheck:test-source-aliascheck-test-source-alias OK — 72 packages with tests scannedNote on cost: the suite's wall clock is unchanged in shape — the negative call site burned the full window before this change and still does. The added pins are millisecond-budgeted and sleep-driven, so they carry no load sensitivity into the merge queue.
Generated by Claude Code