Repository navigation
test(flow-scheduled): the SLA backlog block waits for its sweep's notification deliveries before its no-op test runs - #2176
Merged
objectstack-fleet[bot] merged 2 commits intoOct 19, 2026
Conversation
…ification deliveries before its no-op test runs The no-op test of the `case_sla_monitor` block spent ~2.2 s of its 5 s budget outside its own ~3 ms sweep, and timed out on slower CI runners (four times). Measured: the block's backlog sweep enqueues 60 notification deliveries (30 notifications, inbox and email), the messaging dispatcher works them off after the sweep returns, and on the synchronous SQLite driver that drain starves every macrotask until it ends, so the next test's session lookup waited it out. The email channel fails every attempt on this boot and the outbox retries it about 1 s later, which landed in the same window. `settleNotificationDeliveries` (test/helpers/verify-stack.ts) runs a function inside `observeWrites` and waits, on the dispatcher's acknowledging writes, for every delivery enqueued meanwhile to be finished or parked beyond one test's budget. The block's beforeAll runs its sweep through it. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDgipbNowaGa139em8Fhoq
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDgipbNowaGa139em8Fhoq
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
Author
席位核对ACCEPT (#2157, maintainer direct dispatch 「同意你的建议」, CI health) · PR #2176 at
|
This was referenced Oct 19, 2026
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.
Fixes #2157
Clause-②: no
The
case_sla_monitorblock oftest/flow-scheduled.test.tsnow waits, in its ownbeforeAll, for the notification deliveries its backlog sweep enqueues. Its no-op test (line 195 onmain) no longer spends ~2.2 s of its 5 s budget waiting for them. That wait is what timed it out on slower CI runners (PR #2151, PR #2155,maina3653187, PR #2173).Where the time went (measured on
0a8f73d, before any edit)The instrumentation ran in the worktree only and is not in this diff. Six runs, through the shared lock, on a quiet 4-core box:
openCase: the account seedopenCase:verify.hooks.run('crm_case', 'insert', …, { as: agent.token })openCase: the system updatesweep('case_sla_monitor')(selected=0)Inside that insert,
contextFor'sauth.api.getSessiontook 1792–2452 ms; the rest ofcontextFortook 14 ms. The call issued its first engine operation (thesys_sessionread) only about 2.08 s after it started. Over the same window the engine received 782 operations that the test did not make. All came from the messaging outbox dispatcher, which was working off the backlog sweep's deliveries: 46sys_emailinserts, 30sys_inbox_messageinserts, 92sys_notification_deliveryupdates, 152 delivery reads, and others.The mechanism, measured step by step:
selected=34 acted=17). It notifies 30 times: 17case_sla_breachand 13urgent_task, each on two channels (inbox and email), so 60sys_notification_deliveryrows.notifyonly enqueues them; the dispatcher delivers them after the sweep has returned.driver-sqlite-wasm), that drain is one long run of promise continuations. A 5 mssetIntervalpinger fired 9 times in 2.5 s, with gaps of 1673, 604 and 132 ms. The pinger cannot fire during the drain, and neither cangetSessionresume while it waits on a macrotask. This is not a connection lock: no driver transaction was opened in the window (0beginTransaction).VALIDATION_FAILED: from address required (set options.defaultFrom or pass input.from), because the harness'sEmailServicePluginhas no from address. The outbox retries a failed delivery about 1 s later, then about 10 s later (its schedule is 1 s, 10 s, 60 s, 10 min, 1 h, with ±20% jitter). Each attempt writes one failedsys_emailrow.PM hypothesis 1 holds: the time is the backlog sweep's async aftermath. The aftermath is specifically the outbox drain, plus the email channel's first retry wave.
The change
settleNotificationDeliveries(stack, fn), new intest/helpers/verify-stack.tsnext tonotificationsTo. It runsfninside the handle's own door,verify.observeWrites. It then waits onobservation.settled(…)for the dispatcher's acknowledging update of every delivery inserted while the observation is open. It keeps waiting until each delivery is finished (success,suppressed,dead) or parked for a retry more than one test's budget (5 s) away. An attempt is the acknowledgement update addressed to the row's id; it is the one write that stampslast_attempted_at. The wait is on the work, not on the clock. Eachsettledcall has a 60 s bound, which is a failure bound and not a wait: it fails loudly, naming every observed write, well inside the hook's unchanged 120 s budget.beforeAllruns its backlog sweep through it. No other block uses it.skip, no retry wrapper, no sleep. Everyitkeeps exactly what it asserts.Waiting only for each delivery's first attempt is not enough. A prototype that did so left the no-op test at 630 ms: the 1 s retry wave landed in its window, and
getSessiontook 503 ms of it. That is why the wait also covers retries due soon.Before and after
0a8f73d, quiet, whole filegetSession12.6 ms, no macrotask gap over 50 ms)0a8f73dunder load (vitest pinned to one core withtaskset -c 0, beside two busy-loop processes on the same core,--maxWorkers=1, this block only)Error: Test timed out in 5000ms, the CI failure shapeThe block's
beforeAllnow spends 747 ms on the sweep and 3373 ms in the wait, inside its own budget. On return all 30 inbox deliveries aresuccessand all 30 email deliveries are parked about 10 s out. The whole file passes 54 of 54 in 65.6 s, against 61.1 s on0a8f73d. The probe and load runs were on249abb7;03f3df4adds only the changeset.Verification (
pnpm verifyon03f3df4, through the shared lock)pnpm validate && pnpm typecheck && pnpm lint && pnpm lint:i18n-gate && pnpm hygiene && pnpm hygiene:tokens && pnpm build) printedVERDICT command-exit 0.tsccoverstest/**.pnpm exec vitest run --shard=1/3: 66 files, 1102 passed,VERDICT command-exit 0.--shard=2/3: 65 files, 1223 passed and 1 skipped,VERDICT command-exit 0.--shard=3/3: 65 files, 1579 passed,VERDICT command-exit 0.CI runs the rest.
Acceptance notes
These were observed during measurement. None was filed, and none is changed here.
sys_emailrows asynchronously, long after the run that notified: 46 of them landed inside this test's 2.1 s window. Because every notification email fails on this boot and is retried about 1 s, 10 s and 60 s later, any test that reads "the firstsys_emailinsert" off an engine recorder can catch another notification's attempt.test/script-bodies.test.ts:366does exactly that.settleNotificationDeliverieswaits only for deliveries enqueued while it is open, and only for retries due within 5 s, so it does not cover an earlier test's 10 s or 60 s wave. Two candidates for that dispatch:EmailServicePlugina from address, so each notification email is one attempt and the retry tail disappears suite-wide (a boot change for every suite);VALIDATION_FAILED: from address required, a configuration error that no retry can cure, asretryable. Each notification email is therefore attempted five times over about 71 minutes, and each attempt writes a failedsys_emailrow. Carrier: none.driver-sqlite-wasm, the dispatcher's drain of 60 deliveries held the event loop's macrotask phase for about 2.2 s, measured in-process. A server on that driver would not answer requests during such a burst. The HTTP door was not measured. Carrier: none.@objectstack/verifyalready offers the path this needed (observeWriteswithsettled), so no platform gap is reported. The helper does rely on the outbox's acknowledgement stampinglast_attempted_at, a declared field ofsys_notification_delivery.Generated by Claude Code