fix(smoke): scope §14 usage rollup to O(1) + wrap it legibly (cloud#224) - #225
Merged
Conversation
deploy-staging.yml went red again after #218 — this time FOUR sections earlier. §14, the usage rollup (POST /__test/usage-run), fell off the same O(fleet) cliff the snapshot sweep hit: it enumerated EVERY staging vault, and with the fleet at ~268 (nothing reclaims smoke debris — cloud#224 item 1) the rollup outgrew the runtime's fetch timeout. §14 had NEITHER an explicit AbortSignal NOR a liveCatch, so the throw escaped to main().catch() as a bare, section-less DOMException (empty stack) — pinning it down meant diffing two CI logs to see which section died. Three parts (cloud#224 items 2 + 3): 1. Scope the rollup the way #218 scoped the sweep. /__test/usage-run now takes ?vault=<name>; smoke §14 passes its own fresh arrival vault, so the rollup is O(1), not O(fleet). The param is read AFTER the exposeDevLinks 404 guard (prod gate fully precedes it), the SQL is parameterized (WHERE name = ? LIMIT 1 vs the unchanged ORDER BY name LIMIT ?), and the no-param path is byte-for-byte the old behavior — so the nightly USAGE_CRON and every ops.ts caller are untouched. 2. Make §14 fail legibly. The trigger now carries an explicit, generous timeout and routes through liveCatch: a genuine stall is ADVISORY (couldn't-verify, does not gate), any other throw stays FATAL, and either way it now carries a section label instead of an anonymous crash. 3. Apply cloud#221's own lesson — don't couple a slow setup call to the assertions behind it. The rollup TRIGGER and the console-render ASSERTIONS now live in SEPARATE blocks: an unverified rollup skips the dependent surface checks with a named advisory rather than blinding them. This deliberately does NOT reproduce the shared-try coupling I flagged on §17. The timeout was NOT raised anywhere; scoping is what removes the fleet-size dependency (same fix shape as #218). Does not touch §17, does not reclaim staging data (that is #224 item 1, owned separately). Verified locally in an isolated worktree (app/hub/vault siblings symlinked): root typecheck exit 0; root `bun test` 177 pass / 0 fail (the single env-only failure is spa-build-source.test.ts cloning ../parachute-app, absent in the worktree — 13/0 once the sibling is linked, and the SPA-build path is untouched by this diff); identity vitest 1053 pass / 0 fail across 38 files (1050 + 3 new scope tests), run under singleWorker because the shared box's other-actor workerd exhaust ephemeral ports under the default parallel pool (environmental, same class as #218's run). Did NOT run smoke-staging.ts (creates live debris) — live behavior is reasoned from code. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XLZtmuSs1RirWGMGyCB1QB
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.
What & why
deploy-staging.ymlwent red again right after #218 merged — but four sections earlier, at §14 (the usage rollup). It fell off the same O(fleet) cliff #218 fixed for the snapshot sweep:POST /__test/usage-runenumerated every staging vault, and with the fleet at ~268 (nothing reclaims smoke debris — see #224 item 1) the rollup outgrew the runtime's fetch timeout. It passed at 3m47s ~23h ago and timed out at 4m26s tonight.Worse, §14 had neither an explicit
AbortSignalnor aliveCatch, so the throw escaped uncaught tomain().catch()as a bare, section-lessDOMExceptionwith an empty stack — identifying which section died required diffing two CI logs.This PR is #224 items 2 + 3 (item 1 — actually reclaiming staging debris — is owned separately). It does not touch §17, and does not delete any staging data.
The change
1. Scope the rollup O(1) — exactly the #218 pattern:
/__test/usage-runnow takes?vault=<name>; smoke §14 passes its own fresh arrival vault, so the rollup is O(1) not O(fleet).if (!deps.exposeDevLinks) return c.notFound()guard — the prod 404 gate fully precedes it.WHERE name = ? LIMIT 1(scoped) vs the unchangedORDER BY name LIMIT ?(fleet).USAGE_CRONand everyops.tscaller are untouched.2. Make §14 fail legibly — the trigger now carries an explicit, generous timeout and routes through
liveCatch: a genuine stall is ADVISORY (couldn't-verify, does not gate); any other throw stays FATAL; either way it now has a section label instead of an anonymous crash.3. Apply #221's own lesson — don't couple a slow setup call to the assertions behind it. The rollup trigger and the console-render assertions now live in separate blocks: an unverified rollup skips the dependent surface checks with a named advisory rather than blinding them. This deliberately does not reproduce the shared-
trycoupling I flagged on §17 (that decoupling of §17 is a separate follow-up).The timeout was not raised anywhere — scoping is what removes the fleet-size dependency, same as #218.
Advisory-vs-fatal, restated so the next person can classify without asking
Unchanged from #218's rule, applied here: FATAL = the thing is broken / a wrong answer (every
assert()is fatal; ambiguous stays fatal). ADVISORY = we couldn't verify it right now — the only downgrade isisUnverifiable()(a client-side timeout / network-unreachable while driving live fleet infra). So a rollup that answers wrong (200 butrecorded=0, or the console 200s without the usage markup) is still a fatalassert(); only a couldn't-complete becomes advisory.Verified vs reasoned
Run in an isolated worktree (app/hub/vault siblings symlinked):
bun run typecheck— exit 0.bun test— 177 pass / 0 fail. (One env-only failure first appeared:spa-build-source.test.tsclones../parachute-app, which the worktree lacks; the SPA-build path is untouched by this diff, and the file is 13/0 once the sibling is linked.)singleWorkerbecause the shared box's other-actor workerd exhaust ephemeral ports under the default parallel pool (environmental, same class as fix(smoke): split staging gate advisory-vs-fatal + O(1) snapshot sweep #218's run).scripts/smoke-staging.tsitself (it creates live debris) — live behavior is reasoned from the code and the fix(smoke): split staging gate advisory-vs-fatal + O(1) snapshot sweep #218-identical scoping pattern.🤖 Generated with Claude Code