Skip to content

fix(sync-service): don't suspend a consumer mid-transaction (#4501)#4503

Draft
erik-the-implementer wants to merge 1 commit into
mainfrom
alco/fix-suspend-mid-txn
Draft

fix(sync-service): don't suspend a consumer mid-transaction (#4501)#4503
erik-the-implementer wants to merge 1 commit into
mainfrom
alco/fix-suspend-mid-txn

Conversation

@erik-the-implementer
Copy link
Copy Markdown
Contributor

Summary

Fixes #4501KeyError: key :consider_flushed? not found in: nil in Electric.Shapes.Consumer.process_txn_fragment/2.

A shape consumer could suspend (terminate to reclaim memory) on its idle timeout while still holding a pending_txn for an in-flight multi-fragment transaction. The producer's EventRouter tracks "this shape already saw the begin for the current xid" keyed by shape_handle, independently of consumer liveness — so when a later fragment of that transaction arrived, ConsumerRegistry started a fresh consumer and delivered a has_begin?: false fragment to it. The fresh consumer has pending_txn: nil, so process_txn_fragment/2 dereferenced nil at txn.consider_flushed?.

Fix

consumer_can_suspend?/1 now also requires is_nil(state.pending_txn). A consumer that is mid-transaction hibernates instead of suspending, and only suspends once the transaction completes and it goes idle again.

Why it happened in production

It needs the gap between two fragments of one transaction, as delivered to a single shape's consumer, to exceed hibernate_after (10 min default). That lines up with a connection-scaledown backlog being replayed slowly: I correlated this to a specific source whose largest multi-fragment transaction (21 fragments) was smeared across ~10 minutes ending exactly at the crash. (Source id shared internally.)

Test

test/electric/shapes/consumer_test.exs — "should hibernate not suspend while a multi-fragment transaction is pending": sends a begin fragment, asserts the consumer hibernates (does not emit {:shutdown, :suspend}) while pending, then sends the commit fragment and asserts it suspends. Verified to fail without the guard and pass with it.

🤖 Generated with Claude Code

A consumer could suspend on its idle timeout while holding a pending_txn
for an in-flight multi-fragment transaction, dropping that state. When a
later fragment of the transaction arrived, a fresh consumer received a
has_begin?: false fragment with pending_txn=nil and crashed in
process_txn_fragment/2 (KeyError on :consider_flushed?).

Guard consumer_can_suspend?/1 on is_nil(pending_txn) so the consumer
hibernates instead and suspends only once the transaction completes.

Fixes #4501

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 4, 2026

❌ 108 Tests Failed:

Tests completed Failed Passed Skipped
4343 108 4235 17
View the top 3 failed test(s) by shortest run time
test/runtime-dsl.test.ts > K: wiki coordination > K9: idempotent wiki recreation does not duplicate shared article rows
Stack Traces | 0.00207s run time
Error: spawn wiki-parent-k1/wiki-9 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5762:20
test/runtime-dsl.test.ts > D: shared state > D9: a setup-registered shared-state effect fires on the first wake write and survives a later wake
Stack Traces | 0.00212s run time
Error: spawn ss-effect-d9/sse-9 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-effect-d9"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3400:20
test/runtime-dsl.test.ts > D: shared state > D7: multiple entities can contribute durable rows to the same shared collection
Stack Traces | 0.00214s run time
Error: spawn ss-creator-d4/ssw-7a failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-creator-d4"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3310:21
test/runtime-dsl.test.ts > D: shared state > D11: adjacent writers contending on the same shared key preserve full history and last-write-wins
Stack Traces | 0.00217s run time
Error: spawn ss-creator-d4/ssw-11a failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-creator-d4"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3490:21
test/runtime-dsl.test.ts > K: wiki coordination > K7: get_wiki_status before creating a wiki reports the empty state
Stack Traces | 0.0022s run time
Error: spawn wiki-parent-k1/wiki-7 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5670:20
test/runtime-dsl.test.ts > D: shared state > D8: a later writer can overwrite a shared row and a new reader sees the latest value
Stack Traces | 0.00224s run time
Error: spawn ss-creator-d4/ssw-8a failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-creator-d4"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3346:21
test/runtime-dsl.test.ts > D: shared state > D2: shared state stream exists even before any writes
Stack Traces | 0.00225s run time
Error: spawn ss-creator2-d2/ssc-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-creator2-d2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3134:20
test/runtime-dsl.test.ts > D: shared state > D1: mkdb produces entity history with a manifest entry
Stack Traces | 0.00225s run time
Error: spawn ss-creator-d1/ssc-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-creator-d1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3124:20
test/runtime-dsl.test.ts > F: coordination orchestration > F11: dispatcher preserves counters and child rows when a specialist fails
Stack Traces | 0.00228s run time
Error: spawn dispatcher-f1/dispatch-11 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn dispatcher-f1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4317:20
test/runtime-dsl.test.ts > K: wiki coordination > K10: same-topic wiki expansion adds only the missing article and updates later query coverage
Stack Traces | 0.0023s run time
Error: spawn wiki-parent-k1/wiki-10 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5793:20
test/runtime-dsl.test.ts > K: wiki coordination > K6: repeating create_wiki with the same topic and subtopics is idempotent
Stack Traces | 0.00232s run time
Error: spawn wiki-parent-k1/wiki-6 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5632:20
test/runtime-dsl.test.ts > F: coordination orchestration > F10: manager-worker can retry after a targeted failure and later collect full results
Stack Traces | 0.00234s run time
Error: spawn manager-f2/manager-6 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn manager-f2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4223:20
test/runtime-dsl.test.ts > K: wiki coordination > K2: repeating create_wiki reuses existing specialists and only spawns missing subtopics
Stack Traces | 0.00234s run time
Error: spawn wiki-parent-k1/wiki-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5436:20
test/runtime-dsl.test.ts > H: pipeline sequencing > H4: pipeline persists stage-by-stage currentInput updates through the run
Stack Traces | 0.00234s run time
Error: spawn pipeline-h1/pipeline-4 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn pipeline-h1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4778:20
test/runtime-dsl.test.ts > D: shared state > D3: writes to shared state are reflected in both histories
Stack Traces | 0.00235s run time
Error: spawn ss-writer-d3/ssw-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-writer-d3"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3145:20
test/runtime-dsl.test.ts > K: wiki coordination > K8: wiki keeps durable child metadata and shared articles carry topic and author details
Stack Traces | 0.00236s run time
Error: spawn wiki-parent-k1/wiki-8 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5690:20
test/runtime-dsl.test.ts > D: shared state > D12: mutating one shared collection does not disturb reads from another collection
Stack Traces | 0.00237s run time
Error: spawn ss-writer-d6/ssw-12a failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-writer-d6"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3542:27
test/runtime-dsl.test.ts > D: shared state > D4: a second entity can connect to existing shared state and read prior rows
Stack Traces | 0.00237s run time
Error: spawn ss-creator-d4/ssc-4 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-creator-d4"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3157:20
test/runtime-dsl.test.ts > I: peer review coordination > I1: peer review aggregates three reviewer writes through shared state
Stack Traces | 0.00237s run time
Error: spawn peer-review-i1/review-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn peer-review-i1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5051:20
test/runtime-dsl.test.ts > F: coordination orchestration > F7: manager-worker uses placeholders when every perspective child is silent
Stack Traces | 0.00238s run time
Error: spawn manager-f2/manager-3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn manager-f2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4064:20
test/runtime-dsl.test.ts > H: pipeline sequencing > H3: pipeline status caps at stage_5 while longer pipelines still complete
Stack Traces | 0.00238s run time
Error: spawn pipeline-h1/pipeline-3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn pipeline-h1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4742:20
test/runtime-dsl.test.ts > D: shared state > D6: multi-collection shared state stays consistent across writer and reader entities
Stack Traces | 0.0024s run time
Error: spawn ss-writer-d6/ssw-6 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-writer-d6"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3272:20
test/runtime-dsl.test.ts > D: shared state > D5: shared state update and delete events remain durable across wakes
Stack Traces | 0.0024s run time
Error: spawn ss-creator-d4/ssc-5 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-creator-d4"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3197:20
test/runtime-dsl.test.ts > K: wiki coordination > K3: get_wiki_status reports complete coverage after specialist articles land
Stack Traces | 0.00241s run time
Error: spawn wiki-parent-k1/wiki-3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5523:20
test/runtime-dsl.test.ts > K: wiki coordination > K5: query_wiki before any specialist articles exist returns the empty-state message
Stack Traces | 0.00242s run time
Error: spawn wiki-parent-k1/wiki-5 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5597:20
test/runtime-dsl.test.ts > H: pipeline sequencing > H5: pipeline later runs reuse stage children but reset to the latest input chain
Stack Traces | 0.00243s run time
Error: spawn pipeline-h1/pipeline-5 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn pipeline-h1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4818:20
test/runtime-dsl.test.ts > F: coordination orchestration > F6: wait_for_all before spawning perspectives returns the documented error path
Stack Traces | 0.00243s run time
Error: spawn manager-f2/manager-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn manager-f2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4029:20
test/runtime-dsl.test.ts > F: coordination orchestration > F8: repeated spawn_perspectives reuses the same child streams for later questions
Stack Traces | 0.00244s run time
Error: spawn manager-f2/manager-4 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn manager-f2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4098:20
test/runtime-dsl.test.ts > C: state collections > C3: self-authored state writes do not trigger a second run
Stack Traces | 0.00244s run time
Error: spawn state-loop-c3/loop-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn state-loop-c3"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3105:20
test/runtime-dsl.test.ts > D: shared state > D10: separate entities can contribute to different collections in one shared state
Stack Traces | 0.00245s run time
Error: spawn ss-writer-d6/ssw-10a failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-writer-d6"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3447:21
test/runtime-dsl.test.ts > H: pipeline sequencing > H2: pipeline feeds each stage the previous stage output and persists final state
Stack Traces | 0.00248s run time
Error: spawn pipeline-h1/pipeline-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn pipeline-h1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4712:20
test/runtime-dsl.test.ts > F: coordination orchestration > F12: dispatcher preserves counters and child rows across repeated failing dispatches
Stack Traces | 0.00249s run time
Error: spawn dispatcher-f1/dispatch-12 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn dispatcher-f1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4391:20
test/runtime-dsl.test.ts > C: state collections > C2: setup-initialized state remains visible in final history
Stack Traces | 0.00249s run time
Error: spawn status-entity-c2/se-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn status-entity-c2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3097:20
test/runtime-dsl.test.ts > G: map-reduce ordering > G3: map-reduce reuses chunk children across later wakes and returns only the latest chunk outputs
Stack Traces | 0.0025s run time
Error: spawn map-reduce-g1/map-3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn map-reduce-g1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4602:20
test/runtime-dsl.test.ts > K: wiki coordination > K1: wiki specialists accumulate shared articles that a later query can read
Stack Traces | 0.00255s run time
Error: spawn wiki-parent-k1/wiki-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5387:20
test/runtime-dsl.test.ts > L: reactive observation flows > L2: re-waking the watcher without new child changes does not duplicate prior observation notices
Stack Traces | 0.00259s run time
Error: spawn observed-child-e1/child-l2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5921:19
test/runtime-dsl.test.ts > A: basic entity lifecycle > A13: failing tools close the run cleanly with durable failure history
Stack Traces | 0.00261s run time
Error: spawn toolful-a9/tool-fail-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn toolful-a9"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3005:20
test/runtime-dsl.test.ts > F: coordination orchestration > F9: manager-worker records a targeted child failure and uses a placeholder only for that perspective
Stack Traces | 0.00262s run time
Error: spawn manager-f2/manager-5 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn manager-f2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4155:20
test/runtime-dsl.test.ts > F: coordination orchestration > F2: manager-worker spawns, observes, and later collects all perspectives in a stable order
Stack Traces | 0.00264s run time
Error: spawn manager-f2/manager-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn manager-f2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3818:20
test/runtime-dsl.test.ts > M: deep researcher coordination > M3: separate researcher entities keep child results isolated across later wakes
Stack Traces | 0.00265s run time
Error: spawn deep-researcher-m1/research-3a failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn deep-researcher-m1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4970:19
test/runtime-dsl.test.ts > H: pipeline sequencing > H1: pipeline writes its state row during the first wake before stage execution
Stack Traces | 0.00267s run time
Error: spawn pipeline-h1/pipeline-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn pipeline-h1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4690:20
test/runtime-dsl.test.ts > F: coordination orchestration > F1: dispatcher routes to the requested specialist type and records the child
Stack Traces | 0.00269s run time
Error: spawn dispatcher-f1/dispatch-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn dispatcher-f1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3795:20
test/runtime-dsl.test.ts > I: peer review coordination > I2: summarize_reviews before any reviews exist returns the empty-state error path
Stack Traces | 0.0027s run time
Error: spawn peer-review-i1/review-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn peer-review-i1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5112:20
test/runtime-dsl.test.ts > C: state collections > C1: ctx.state inserts are reflected in full stream history
Stack Traces | 0.00274s run time
Error: spawn state-writer-c1/sw-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn state-writer-c1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3089:20
test/runtime-dsl.test.ts > M: deep researcher coordination > M1: researcher workers start from spawn initialMessage without an extra send
Stack Traces | 0.00279s run time
Error: spawn deep-researcher-m1/research-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn deep-researcher-m1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4917:20
test/runtime-dsl.test.ts > L: reactive observation flows > L1: explicit observe plus createEffect forwards insert, update, and delete notices
Stack Traces | 0.00285s run time
Error: spawn observed-child-e1/child-l1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5860:19
test/runtime-dsl.test.ts > J: debate coordination > J1: debate parent reads both sides from shared state before issuing a ruling
Stack Traces | 0.0029s run time
Error: spawn debate-parent-j1/debate-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn debate-parent-j1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5223:20
test/runtime-dsl.test.ts > F: coordination orchestration > F3: dispatcher increments dispatch count and keeps both child rows across wakes
Stack Traces | 0.00291s run time
Error: spawn dispatcher-f1/dispatch-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn dispatcher-f1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3921:20
test/runtime-dsl.test.ts > B: spawn mechanics > B4: spawn marks the child manifest row as observed
Stack Traces | 0.00292s run time
Error: spawn obs-parent-b4/op-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn obs-parent-b4"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3079:20
test/runtime-dsl.test.ts > J: debate coordination > J2: end_debate before any arguments exist returns the empty-state error path
Stack Traces | 0.00293s run time
Error: spawn debate-parent-j1/debate-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn debate-parent-j1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5295:20
test/runtime-dsl.test.ts > G: map-reduce ordering > G4: map-reduce uses a placeholder only for the failed chunk while keeping the others
Stack Traces | 0.00298s run time
Error: Expected 1 additional wake error(s), but they did not occur
 ❯ drainRuntimeWakes test/runtime-dsl.ts:589:15
 ❯ waitForRuntimeSettled test/runtime-dsl.ts:619:5
 ❯ Object.waitForSettled test/runtime-dsl.ts:929:7
 ❯ test/runtime-dsl.test.ts:2806:3
test/runtime-dsl.test.ts > J: debate coordination > J3: debate with only one durable side stays partial until the missing side arrives
Stack Traces | 0.00304s run time
Error: spawn debate-parent-j1/debate-3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn debate-parent-j1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5330:20
test/runtime-dsl.test.ts > G: map-reduce ordering > G2: map-reduce with one chunk still uses the orchestration path
Stack Traces | 0.00307s run time
Error: spawn map-reduce-g1/map-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn map-reduce-g1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4561:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A12: stateful note writes persist across wakes and can be read later
Stack Traces | 0.00308s run time
Error: spawn toolful-a9/tool-note-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn toolful-a9"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2976:20
test/runtime-dsl.test.ts > L: reactive observation flows > L3: a child delete while the watcher is asleep replays as one delete notice
Stack Traces | 0.0031s run time
Error: spawn observed-child-e1/child-l3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5980:19
test/runtime-dsl.test.ts > K: wiki coordination > K4: create_wiki rejects switching the topic on an existing wiki
Stack Traces | 0.0031s run time
Error: spawn wiki-parent-k1/wiki-4 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wiki-parent-k1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5548:20
test/runtime-dsl.test.ts > E: observation replay > E3: an observed row update is replayed as an update, not a second insert
Stack Traces | 0.00313s run time
Error: spawn observed-child-e1/child-3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3738:19
test/runtime-dsl.test.ts > A: basic entity lifecycle > A6: agent-less entity records only inbound messages
Stack Traces | 0.00314s run time
Error: spawn noagent-a6/na-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn noagent-a6"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2898:20
test/runtime-dsl.test.ts > H: pipeline sequencing > H6: pipeline carries a failed stage forward as placeholder input for later stages
Stack Traces | 0.00318s run time
Error: spawn pipeline-h1/pipeline-6 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn pipeline-h1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4862:20
test/runtime-dsl.test.ts > L: reactive observation flows > L4: watching the same child twice stays deduped
Stack Traces | 0.00321s run time
Error: spawn observed-child-e1/child-l4 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:6043:19
test/runtime-dsl.test.ts > M: deep researcher coordination > M2: wait_for_results before spawning researchers returns the empty-state error path
Stack Traces | 0.00325s run time
Error: spawn deep-researcher-m1/research-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn deep-researcher-m1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4944:20
test/runtime-dsl.test.ts > N: wake primitives verification > N1: WakeEvent type is "wake" when parent is re-woken by child completion
Stack Traces | 0.00325s run time
Error: spawn wake-type-parent-n1/wake-type-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wake-type-parent-n1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:6142:20
test/runtime-dsl.test.ts > L: reactive observation flows > L5: one watcher can observe multiple children and preserve source attribution
Stack Traces | 0.00332s run time
Error: spawn observed-child-e1/child-l5-a failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:6093:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A11: repeated tool calls keep ordering stable and use the last result
Stack Traces | 0.00333s run time
Error: spawn toolful-a9/tool-double-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn toolful-a9"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2959:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A14: an entity can recover from a failed tool call in a later run
Stack Traces | 0.00341s run time
Error: spawn toolful-a9/tool-recover-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn toolful-a9"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3019:20
test/runtime-dsl.test.ts > F: coordination orchestration > F5: dispatcher returns the documented placeholder when a child produces no text
Stack Traces | 0.00348s run time
Error: spawn dispatcher-f1/dispatch-4 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn dispatcher-f1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4005:20
test/runtime-dsl.test.ts > E: observation replay > E0: observe without wake does not re-wake on later child writes
Stack Traces | 0.00348s run time
Error: spawn observed-child-e1/child-0 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3610:19
test/runtime-dsl.test.ts > A: basic entity lifecycle > A8: manifest history includes the configured agent
Stack Traces | 0.00349s run time
Error: spawn manifested-a8/mf-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn manifested-a8"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2915:20
test/runtime-dsl.test.ts > B: spawn mechanics > B1: spawn creates a child entity that can receive messages
Stack Traces | 0.0035s run time
Error: spawn parent-b1/p-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn parent-b1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3046:20
test/runtime-dsl.test.ts > B: spawn mechanics > B3: spawn manifest history includes the resolved entityUrl
Stack Traces | 0.00359s run time
Error: spawn spawner-b3/s-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn spawner-b3"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3071:20
test/runtime-dsl.test.ts > G: map-reduce ordering > G1: map-reduce returns results in chunk order even when completions differ
Stack Traces | 0.00364s run time
Error: spawn map-reduce-g1/map-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn map-reduce-g1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:4507:20
test/runtime-dsl.test.ts > B: spawn mechanics > B2: spawn with initial message writes the child history
Stack Traces | 0.00371s run time
Error: spawn parent2-b2/p-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn parent2-b2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3059:20
test/runtime-dsl.test.ts > F: coordination orchestration > F4: dispatcher records the expected status progression during a dispatch
Stack Traces | 0.00377s run time
Error: spawn dispatcher-f1/dispatch-3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn dispatcher-f1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3975:20
test/runtime-dsl.test.ts > I: peer review coordination > I4: peer review with two configured reviewers summarizes only those durable rows
Stack Traces | 0.00377s run time
Error: spawn peer-review-i1/review-4 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn peer-review-i1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5183:20
test/runtime-dsl.test.ts > E: observation replay > E1: observed effects do not duplicate old child rows after parent re-wake
Stack Traces | 0.00384s run time
Error: spawn observed-child-e1/child-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3637:19
test/runtime-dsl.test.ts > A: basic entity lifecycle > A7: setup state writes appear before the run history
Stack Traces | 0.00392s run time
Error: spawn stateful-a7/s-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn stateful-a7"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2906:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A5c: t.waitForSettled waits for runtime quiescence
Stack Traces | 0.00409s run time
Error: spawn multi-a5/m-settled-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn multi-a5"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2888:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A10: async tool completion preserves a single clean run history
Stack Traces | 0.00419s run time
Error: spawn toolful-a9/tool-async-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn toolful-a9"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2944:20
test/runtime-dsl.test.ts > N: wake primitives verification > N3: wake events are delivered as wake when the parent is re-woken
Stack Traces | 0.00423s run time
Error: spawn wake-type-parent-n1/idle-test-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wake-type-parent-n1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:6346:20
test/runtime-dsl.test.ts > N: wake primitives verification > N5: runFinished wake records the finished child on the parent stream
Stack Traces | 0.00435s run time
Error: spawn wake-summary-parent-n4/wake-summary-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn wake-summary-parent-n4"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:6253:20
test/runtime-dsl.test.ts > N: wake primitives verification > N4: ctx.agent.run receives the wake payload and performs a second run on child completion
Stack Traces | 0.0046s run time
Error: spawn idle-wake-parent-n3/wake-agent-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn idle-wake-parent-n3"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:6226:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A3: single message produces a full run history
Stack Traces | 0.00476s run time
Error: spawn runner-a3/r-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn runner-a3"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2849:20
test/runtime-dsl.test.ts > I: peer review coordination > I3: peer review with one configured reviewer summarizes only that durable row
Stack Traces | 0.00498s run time
Error: spawn peer-review-i1/review-3 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn peer-review-i1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:5145:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A5b: entity.waitForSettled returns the settled history without run-count polling
Stack Traces | 0.00531s run time
Error: spawn multi-a5/m-settled-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn multi-a5"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2877:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A4: agent text output is reflected in the final history
Stack Traces | 0.00536s run time
Error: spawn texter-a4/t-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn texter-a4"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2857:20
test/runtime-dsl.test.ts > E: observation replay > E2: updating an observed row preserves a single derived row key
Stack Traces | 0.00541s run time
Error: spawn observed-child-e1/child-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn observed-child-e1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:3683:19
test/runtime-dsl.test.ts > A: basic entity lifecycle > A2: spawn with initial message writes inbox history before any run
Stack Traces | 0.00554s run time
Error: spawn basic-a2/b-2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn basic-a2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2828:20
test/runtime-dsl.test.ts > N: wake primitives verification > N2: observe(db(...)) with wake option triggers re-wake on shared state write
Stack Traces | 0.00575s run time
Error: spawn ss-wake-writer-n2/ss-writer-n2 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn ss-wake-writer-n2"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:6188:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A9: sync tool calls appear in-order within one completed run
Stack Traces | 0.00708s run time
Error: spawn toolful-a9/tool-sync-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn toolful-a9"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2923:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A5: multiple messages produce two completed runs
Stack Traces | 0.00811s run time
Error: spawn multi-a5/m-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn multi-a5"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2866:20
test/runtime-dsl.test.ts > A: basic entity lifecycle > A1: spawn writes entity_created immediately with spawn args
Stack Traces | 0.0347s run time
Error: spawn basic-a1/b-1 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn basic-a1"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:2811:20
Elixir.Electric.Postgres.ReplicationClientTest::test ReplicationClient against real db flushes are advanced based on standby messages when publication is otherwise silent
Stack Traces | 0.158s run time
23) test ReplicationClient against real db flushes are advanced based on standby messages when publication is otherwise silent (Electric.Postgres.ReplicationClientTest)
     .../electric/postgres/replication_client_test.exs:493
     Assertion with == failed
     code:  assert Lsn.compare(confirmed_flush_lsn, new_confirmed_flush_lsn) == :lt
     left:  :eq
     right: :lt
     stacktrace:
       .../electric/postgres/replication_client_test.exs:511: (test)
View the full list of 16 ❄️ flaky test(s)
test/dispatch-policy-routing.test.ts > dispatch policy routing > creates pull-wake subscriptions for runner-targeted spawns

Flake rate in main: 100.00% (Passed 0 times, Failed 10 times)

Stack Traces | 0.038s run time
AssertionError: expected 500 to be 201 // Object.is equality

- Expected
+ Received

- 201
+ 500

 ❯ test/dispatch-policy-routing.test.ts:115:29
test/dispatch-policy-routing.test.ts > dispatch policy routing > creates webhook subscriptions and stores the original target

Flake rate in main: 100.00% (Passed 0 times, Failed 10 times)

Stack Traces | 0.000793s run time
AssertionError: expected 500 to be 201 // Object.is equality

- Expected
+ Received

- 201
+ 500

 ❯ test/dispatch-policy-routing.test.ts:173:29
test/dispatch-policy-routing.test.ts > dispatch policy routing > does not re-add already linked runner streams before sending

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.00084s run time
AssertionError: expected 500 to be 204 // Object.is equality

- Expected
+ Received

- 204
+ 500

 ❯ test/dispatch-policy-routing.test.ts:360:29
test/dispatch-policy-routing.test.ts > dispatch policy routing > links legacy entities through the type default before sending

Flake rate in main: 100.00% (Passed 0 times, Failed 10 times)

Stack Traces | 0.0022s run time
AssertionError: expected 500 to be 204 // Object.is equality

- Expected
+ Received

- 204
+ 500

 ❯ test/dispatch-policy-routing.test.ts:286:29
test/dispatch-policy-routing.test.ts > dispatch policy routing > links pull-wake dispatch before sending spawn initialMessage

Flake rate in main: 100.00% (Passed 0 times, Failed 10 times)

Stack Traces | 0.000777s run time
AssertionError: expected 500 to be 201 // Object.is equality

- Expected
+ Received

- 201
+ 500

 ❯ test/dispatch-policy-routing.test.ts:204:29
test/dispatch-policy-routing.test.ts > dispatch policy routing > links webhook dispatch before sending spawn initialMessage

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.000704s run time
AssertionError: expected 500 to be 201 // Object.is equality

- Expected
+ Received

- 201
+ 500

 ❯ test/dispatch-policy-routing.test.ts:244:29
test/dispatch-policy-routing.test.ts > dispatch policy routing > recreates missing runner dispatch subscriptions before sending

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.000778s run time
AssertionError: expected 500 to be 204 // Object.is equality

- Expected
+ Received

- 204
+ 500

 ❯ test/dispatch-policy-routing.test.ts:321:29
test/dispatch-policy-routing.test.ts > dispatch policy routing > treats runner subscription create conflicts as an idempotent spawn link

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.000837s run time
AssertionError: expected 500 to be 201 // Object.is equality

- Expected
+ Received

- 201
+ 500

 ❯ test/dispatch-policy-routing.test.ts:419:29
test/dispatch-policy-routing.test.ts > dispatch policy routing > uses separate pull-wake subscriptions for separate runner-targeted entities

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.00187s run time
AssertionError: expected 500 to be 201 // Object.is equality

- Expected
+ Received

- 201
+ 500

 ❯ test/dispatch-policy-routing.test.ts:149:26
test/electric-agents-manager-write-validation.test.ts > ElectricAgentsManager event source subscriptions > persists subscription deletion before unregistering wake side effects

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.000586s run time
TypeError: this.registry.replaceSharedStateLink is not a function
 ❯ EntityManager.syncManifestLinks src/entity-manager.ts:3052:25
 ❯ EntityManager.writeManifestEntry src/entity-manager.ts:2660:5
 ❯ EntityManager.deleteEventSourceSubscription src/entity-manager.ts:2898:5
 ❯ test/electric-agents-manager-write-validation.test.ts:250:5
test/electric-agents-manager-write-validation.test.ts > ElectricAgentsManager event source subscriptions > persists the manifest before registering wake side effects

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.00555s run time
TypeError: this.registry.replaceSharedStateLink is not a function
 ❯ EntityManager.syncManifestLinks src/entity-manager.ts:3052:25
 ❯ EntityManager.writeManifestEntry src/entity-manager.ts:2660:5
 ❯ EntityManager.upsertEventSourceSubscription src/entity-manager.ts:2859:5
 ❯ test/electric-agents-manager-write-validation.test.ts:210:5
test/electric-agents-status.test.ts > ElectricAgentsManager.forkSubtree > forks durable streams before registering remapped entity rows

Flake rate in main: 100.00% (Passed 0 times, Failed 10 times)

Stack Traces | 0.00761s run time
TypeError: this.registry.replaceSharedStateLink is not a function
 ❯ EntityManager.syncManifestLinks src/entity-manager.ts:3052:25
 ❯ EntityManager.materializeForkManifestSideEffects src/entity-manager.ts:2050:7
 ❯ EntityManager.forkSubtreeInner src/entity-manager.ts:1141:11
 ❯ src/entity-manager.ts:876:22
 ❯ src/tracing.ts:32:14
 ❯ withSpan src/tracing.ts:30:10
 ❯ EntityManager.forkSubtree src/entity-manager.ts:874:12
 ❯ test/electric-agents-status.test.ts:478:20
test/event-source-subscriptions-route.test.ts > event source subscription routes > creates a manifest-backed webhook wake subscription

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.0263s run time
TypeError: ctx.entityManager.registry.hasEntityPermission is not a function
 ❯ canAccessEntity src/permissions.ts:40:39
 ❯ src/routing/entities-router.ts:713:9
 ❯ fetch ../../node_modules/.pnpm/itty-router@5.0..../node_modules/itty-router/index.mjs:1:1266
 ❯ Object.fetch ../../node_modules/.pnpm/itty-router@5.0..../node_modules/itty-router/index.mjs:1:1266
 ❯ test/event-source-subscriptions-route.test.ts:29:22
test/event-source-subscriptions-route.test.ts > event source subscription routes > rejects subscriptions whose params do not match the bucket schema

Flake rate in main: 100.00% (Passed 0 times, Failed 4 times)

Stack Traces | 0.000516s run time
TypeError: ctx.entityManager.registry.hasEntityPermission is not a function
 ❯ canAccessEntity src/permissions.ts:40:39
 ❯ src/routing/entities-router.ts:713:9
 ❯ fetch ../../node_modules/.pnpm/itty-router@5.0..../node_modules/itty-router/index.mjs:1:1266
 ❯ Object.fetch ../../node_modules/.pnpm/itty-router@5.0..../node_modules/itty-router/index.mjs:1:1266
 ❯ test/event-source-subscriptions-route.test.ts:135:22
test/runtime-dsl.test.ts > N: wake primitives verification > N3b: spawn wake and child manifest share one runFinished registration

Flake rate in main: 100.00% (Passed 0 times, Failed 5 times)

Stack Traces | 0.00444s run time
Error: spawn idle-wake-parent-n3/idle-test-dedupe-1780574880746 failed (401): {"error":{"code":"UNAUTHORIZED","message":"Principal is not allowed to spawn idle-wake-parent-n3"}}
 ❯ Object.spawnEntity src/runtime-server-client.ts:444:13
 ❯ timeStep test/runtime-dsl.ts:236:12
 ❯ Object.spawn test/runtime-dsl.ts:894:22
 ❯ test/runtime-dsl.test.ts:6302:20
test/server-start.test.ts > ElectricAgentsServer.start > rehydrates pending future_send manifest schedules on startup

Flake rate in main: 100.00% (Passed 0 times, Failed 10 times)

Stack Traces | 0.00194s run time
AssertionError: expected "vi.fn()" to be called with arguments: [ '/chat/test', …(3) ]

Number of calls: 0

 ❯ test/server-start.test.ts:356:50

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@claude
Copy link
Copy Markdown

claude Bot commented Jun 4, 2026

Claude Code Review

Summary

This PR fixes the production crash KeyError: key :consider_flushed? not found in: nil in Electric.Shapes.Consumer.process_txn_fragment/2 (#4501) by preventing a consumer from suspending while it holds a pending_txn for an in-flight multi-fragment transaction. The change is minimal, correct, well-targeted, and properly tested.

What's Working Well

  • Accurate root-cause analysis. The PR description correctly identifies the failure mode: EventRouter.shapes_seen_begin (in the single ShapeLogCollector process) tracks per-shape_handle begin state independently of consumer liveness (event_router.ex:25,187-194), so a fresh consumer started after a suspend receives a has_begin?: false fragment with pending_txn: nil and dereferences nil at consumer.ex:568 (txn.consider_flushed?).
  • The guard is in exactly the right place. pending_txn is the canonical "mid-transaction" indicator: it's set on any begin-without-commit (consumer.ex:522-529) regardless of write_unit mode, and cleared on every completion path (consumer.ex:586,602,751,760). So is_nil(state.pending_txn) also transparently protects the write_unit=txn buffering case (transaction_builder), not just the streaming case.
  • Correct hibernate-instead-of-suspend trade-off. Falling through to {:noreply, state, :hibernate} (consumer.ex:408-410) keeps the process alive with a compacted heap, and the next handle_event re-arms the idle timer via {:reply, :ok, state, state.hibernate_after} (consumer.ex:201), so the consumer suspends naturally once the txn completes. No risk of a permanently un-reclaimable process.
  • Solid, targeted test. "should hibernate not suspend while a multi-fragment transaction is pending" asserts both the negative (refute_receive {:DOWN, ... {:shutdown, :suspend}}) and the positive recovery (suspends after the commit fragment), and pins :current_function to :gen_server.loop_hibernate to prove hibernation actually occurred. The PR notes it fails without the guard and passes with it.
  • Changeset present and correctly scoped (@core/sync-service, patch).

Issues Found

Critical (Must Fix)

None.

Important (Should Fix)

Defense in depth: the crash in process_txn_fragment/2 is still reachable on any other mid-transaction consumer death.

File: packages/sync-service/lib/electric/shapes/consumer.ex:562-568

This PR removes the most common trigger (idle-timeout suspend), but the underlying fragility remains. pending_txn is purely in-memory (consumer/state.ex:40, defaulted to nil, never recovered on init). Since EventRouter.shapes_seen_begin survives a consumer restart, any consumer that dies mid-multi-fragment-transaction — a crash from a transient storage/error path, a supervisor restart, etc. — comes back with pending_txn: nil and will hit the exact same nil.consider_flushed? dereference on the next fragment (which carries has_begin?: false). Worse, the commit fragment also carries has_begin?: false, so the restarted consumer would crash on every remaining fragment of that transaction until it completes — a potential crash loop.

Consider adding a defensive clause so the consumer degrades gracefully regardless of why it lost its pending_txn, e.g. a process_txn_fragment/2 head matching %State{pending_txn: nil} with a non-begin fragment that logs a warning and skips/recovers rather than crashing the supervision tree. That closes the whole class of bug rather than the single trigger.

I haven't fully traced whether a single consumer crash triggers any ShapeLogCollector/EventRouter reset that would re-emit the begin — if it does, the scenario above is unreachable and this can be downgraded to a suggestion. Worth confirming either way, since the targeted fix's correctness depends on suspend being the only path to a pending_txn-less restart mid-transaction.

Suggestions (Nice to Have)

  • The test exercises the streaming path (default write_unit). Since the guard now also protects the write_unit=txn buffering path, a one-line note in the test (or a sibling assertion) that the same protection applies there would document the broader intent. Optional.

Issue Conformance

The implementation directly addresses #4501. The linked issue is a Sentry-sourced crash report with a clear stacktrace pointing at consumer.ex:567/196; the fix targets exactly that dereference path. The PR description goes beyond the issue with a credible production correlation (a 21-fragment transaction smeared across ~10 min against the default hibernate_after), which explains the timing precondition well. No scope creep. The issue is adequately specified for a crash fix; no improvements needed there.

Previous Review Status

N/A — first review of this PR.


Review iteration: 1 | 2026-06-04

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KeyError: key :consider_flushed? not found in: nil

2 participants