test(daemon): seeded concurrency torture lane for session/lease/lock invariants - #1439
test(daemon): seeded concurrency torture lane for session/lease/lock invariants#1439devin-ai-integration[bot] wants to merge 9 commits into
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
Size Report
Startup median (7 runs, lower is better):
Top changed chunks: no changes in the largest emitted chunks. |
…invariants Refs #1416 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Review findings for
Residual scope: Checks: Integration Tests, typecheck, lint/format, layering, Fallow, and no-test-only-DI are green on this head. Coverage is red only for the independently reproducible inherited |
4fba061 to
72f48d6
Compare
- derive each op's lock plan from production resolveRequestExecutionLockKeys via a fake device-inventory provider, so reverting the router's same-device serialization trips the overlap invariant (verified) - assert exact replay: full scheduler trace, terminal outcome, contention - assert real same-device lock contention in the sweep + a forced 2-client case - split harness into bindings/invariants/envelope modules (all <500 LOC) - emit #1430 scheduled-lane envelope (schema/SHA/hash/seed range/duration/result) and upload it from the nightly workflow Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@thymikee thanks — all four review points addressed in 1 (P1) — real lock plan, revert-sensitive. The lane no longer hand-writes 2 (P1) — #1430 envelope. The nightly now emits the standard envelope via 3 (P2) — determinism + contention proof. Replay now asserts equality on the full ordered scheduler trace ( 4 (P2) — locality. Residual scope. Agreed and now stated precisely in
|
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Re-review at head f5abcf6: request changes. The split and imported lock-plan helper are improvements, but five blockers remain. First, the harness models lock grants and calls resolveRequestExecutionLockKeys directly; it does not exercise shouldLockSessionExecution through runLocked and withRequestExecutionLocks, so bypassing the production application path can still leave this lane green. Second, the claimed forced same-device test calls runTorture without pinnedDevice, so doOpen still randomly picks from DEVICE_POOL. Third, the sweep writes a passing envelope before the separate contention test runs; that later test can fail while the always-uploaded envelope still says pass. Fourth, issue 1430 remains open and this PR adds no shared health/freshness watcher, so the scheduled lane is not born monitored. Fifth, exact replay is only checked in the explicit TORTURE_SEED branch, not the normal CI/nightly sweep. All reported PR checks are green and local default/explicit replay validation passed, but they do not close these guardrail gaps. |
…lay + forced-device contention; whole-lane envelope Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@thymikee thanks — pushed 1. Production application-path coupling ( 2. Forced same-device test wasn't actually pinned. Fixed — it now passes 3. Envelope could publish 5. Replay only checked under 4. #1430 shared health/freshness watcher. This one I want to confirm scope on. #1430 is a separate Track E issue whose deliverables are (a) the standard envelope — which #1416 "MUST ship with" and now does — and (b) "a scheduled health job that reads last-run/last-success for every |
|
Re-review at head 0890152: pinnedDevice contention, whole-lane result handling, and replay during the normal sweep are fixed. Two P1 blockers remain. First, the harness still manually mirrors shouldLockSessionExecution and resolveRequestExecutionLockKeys, then grants modeled scheduler locks; it never exercises createRequestExecutionScope().runLocked() through withRequestExecutionLocks. Breaking or bypassing the production lock application can therefore leave this lane green. Add a real-scope same-device contention guard or narrow the claim. Second, issue 1412 requires issue 1430 health/freshness monitoring to land with new scheduled lanes; this PR still only uploads an envelope and cannot detect a lane that misses two cadences. The envelope duration also excludes the later forced-contention guard. Current PR checks are green, but these architecture/observability gaps still block readiness. |
…e envelope duration Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@thymikee pushed P1 #1 — real-scope serialization guard (closed). Added Envelope duration (fixed). P1 #2 — #1430 health/freshness monitoring. I've asked the repo owner to make the scope call rather than guess, because this is where I need direction: the cross-workflow "reads last-run/last-success for every |
|
Re-review at head 8e9001e: the real production lock application guard is fixed and Integration Tests exercise createRequestExecutionScope().runLocked through withRequestExecutionLocks. Two P1 blockers remain. First, default and forced sweeps include seed 0 and failures print TORTURE_SEED=0, but optionalIntFromEnv rejects values less than or equal to zero; the promised replay command therefore cannot replay a seed-0 failure. Allow non-negative replay seeds separately from positive client/op counts and add a seed-0 regression. Second, issue 1412 still requires issue 1430 health/freshness monitoring to land with this scheduled lane; this branch has only the envelope, not a cadence watcher. Current checks are green, but these contracts remain unfulfilled. |
…D must accept 0) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@thymikee good catch — pushed P1 #1 — seed 0 unreplayable (fixed). P1 #2 — #1430 monitoring. This is a deliberate scope call I've put to the repo owner rather than deciding unilaterally, and it's the one open item. My read: #1416's stated contract ("new lanes MUST ship with the standard envelope") is satisfied — the lane emits the envelope and carries a |
|
Re-reviewed the latest delta. The seed-0 replay blocker is fixed: One required blocker remains: #1430 cadence health/freshness is still not implemented on this branch. The scheduled lane emits an envelope, but nothing detects or reports two missed/failed cadences, so the lane can silently go dark. No new regression was found in this one-file delta. |
Discovers schedule: workflows from .github/workflows/, reads recent scheduled runs via the GitHub API, and opens/pings a tracking issue when a lane misses or fails two consecutive cadences. Pure model unit-tested and gated on PRs; API I/O + issue open/ping run nightly. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@thymikee the maintainer asked me to land the watcher here, so #1430's cadence health/freshness monitoring is now in this PR — pushed New
Structure / testability: all decision logic is pure in New lanes need zero wiring: emitting the standard envelope + a |
|
The #1430 watcher plumbing is now present, but the two-cadence rule is still violated. Current tests cover two failures and an old success, but not the required zero-run/one-failure grace. Carry enough first-observed/registration timing to distinguish a newborn lane, require two completed failed cadences (or equivalent elapsed evidence), and add both grace regressions. All current checks are green; there is still no live scheduled/API alert run. |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Re: the two-cadence rule violation — fixed in
Added both grace regressions in
The existing dark/failing tests now register the lane well past the grace window so they still assert unhealthy. On the live alert run: the watcher is |
|
The model-level zero-run/one-failure grace is fixed, but the production anchor still makes the generic #1430 contract wrong. The new tests inject a recent timestamp and miss this mapping. Derive or durably record the schedule-introduction/first-observed time and test the production mapping. All head checks are green; no live scheduled/API issue-write run exists yet. |
…w age Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Re: the production anchor still being wrong ( New pure mapping
So adding a schedule to an old workflow now anchors on the recent introduction commit, not the ancient file age. The watcher workflow checks out with Tested the production mapping directly (not just injected timestamps):
Model suite 16/16; |
Summary
Adds a deterministic, seeded concurrency torture lane (#1416, umbrella #1412 Track A) that turns accidental ordering failures in session/lease/lock handling into a reproducible test asset. N logical clients drive randomized-but-seeded interleavings of
open/mutate/close/takeover/killagainst the realSessionStore+LeaseRegistry(plus an in-memory device-claim model). After every run it asserts: no leaked leases or claims, no cross-session state bleed, every lock released after owner death, and the session store stays consistent — and it pins the router's same-device serialization under 100+ interleavings.Why a scheduler, not just a seed (issue review amendment)
So all concurrency is routed through an instrumented dispatcher — the single source of ordering. Every client runs as a fiber that only advances when the scheduler resumes it, and mutual exclusion is granted by the scheduler too:
Determinism contract that makes replay exact: fibers yield only through scheduler-owned promises; everything between yields is synchronous (the real
SessionStore/LeaseRegistrynever await); no wall clock, no timers, no real I/O. A seed therefore fully determines execution order, so failures replay bit-for-bit.What is real vs modeled (the amendment's "say which and why")
SessionStore(session map/consistency) andLeaseRegistry(allocation, per-device exclusivity, release, scope checks).RequestExecutionLockKeys viawithKeyedLock(request-binding.ts+request-execution-scope.ts). The harness reuses the exact key shape (session:*,device:*) and the same lock ORDER (session before device, so no lock-order deadlock) but grants locks through the scheduler mutex, because a seed cannot reproduce Node's native microtask hand-off insidewithKeyedLock. The invariant under test is identical (critical sections for one device never overlap; serialization is total) and now seed-deterministic.withKeyedLock's reentrancy/cleanup stay covered by their own unit tests.device-claims.tsis a filesystem/OS process lock) and process "kill" — both out of scope for a scheduling-torture lane (test: seeded concurrency torture lane for session/lease/lock invariants #1416: no real devices / wall-clock stress).InMemoryClaimRegistrypreserves the invariants (one live claim per device key, owner-token-gated clear, dead-owner reclaimable).Value demonstrated
Beyond passing 100+ interleavings (default 128, verified up to 2000 seeds locally), the lane bites: temporarily granting each same-device critical section a non-shared lock key makes it fail deterministically and print the seed + replay command, e.g.
Files
test/integration/concurrency-torture.test.ts—node --testentry: sweepsTORTURE_RUNSseeds (default 128) or replays one viaTORTURE_SEED; prints the seed on any failure and self-checks replay determinism.test/integration/concurrency-torture/{prng,deterministic-scheduler,claim-registry,harness}.ts— seeded splitmix32 PRNG, the dispatcher, the claim model, and the world/invariants..github/workflows/concurrency-torture-nightly.yml— nightly (0 5 * * *) + manual sweep over a large seed range; offline, no devices.docs/agents/testing.md— documents the lane, theTORTURE_SEED=<n>replay flag, and the real-vs-modeled boundary.package.json—test:concurrency-torturescript.Seed replay (documented)
Validation
pnpm format:check,pnpm lint,pnpm typecheck,pnpm build, andpnpm check:affected --base origin/main --runare green except a preexistinghelp-conformance-topic-coveragefailure for theios-system-uitopic, which reproduces identically on cleanorigin/main(verified with this branch stashed) and is unrelated to this change. Note: the lane requires Node ≥22.18/24 to build (tsdown), matching CI's Node 24.Closes #1416
Link to Devin session: https://app.devin.ai/sessions/b043907331814d8fa203bdbe2f529191
Requested by: @thymikee