You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The sagas scaffold emits sagas/runtime.ts with no KV adapter registration, while the AppHost provisions a Redis/Garnet cache resource by default. The saga background processor therefore crashes on startup in a freshly scaffolded project.
Found by the wave-4 DeepSeek attribution-control run on published 0.0.4 (/home/codex/repos/wave4-deepseek-004), building a deploy queue. This is downstream of #1064/#1065/#1066 — those fixed the saga engine; the generated glue for the durable store is still broken.
Reproduction
Scaffold a project with the sagas plugin and the default cache backend, then start the AppHost. The sagas resource dies immediately:
[sagas] [sys] Starting process...: Cmd = /home/codex/.deno/bin/deno, Args = ["run",
"--minimum-dependency-age=0", "--node-modules-dir=none", "--unstable-worker-options",
"--unstable-kv", "--allow-all", "--watch", "sagas/runtime.ts"]
[sagas] Watcher Process started.
[sagas] error: Uncaught (in promise) KvConnectionError: Redis/Garnet KV provider was
auto-detected but the Redis adapter is not registered. Add `import '@netscript/kv/redis';`
to your service entrypoint to opt-in. Frontend/SSR apps should use Deno KV instead
(set CACHE_PROVIDER=denokv).
throw new KvConnectionError(
^
at initializeKv (https://jsr.io/@netscript/kv/0.0.4/application/shared.ts:221:13)
at getKv (https://jsr.io/@netscript/kv/0.0.4/application/shared.ts:126:17)
at openSagaRuntimeKv (https://jsr.io/@netscript/plugin-sagas-core/0.0.4/src/stores/kv-saga-store.ts:40:31)
at async startSagaRunner (https://jsr.io/@netscript/plugin-sagas/0.0.4/src/runtime/saga-runner.ts:77:3)
at async runSagaRunner (https://jsr.io/@netscript/plugin-sagas/0.0.4/src/runtime/saga-runner.ts:92:3)
at async file:///<project>/sagas/runtime.ts:9:3
The saga API stays up but returns an empty set forever — GET /api/v1/sagas/sagas → {"sagas":[],"total":0,...} — so the failure reads as "no sagas yet" from the outside.
Cause
plugins/sagas/src/adapter/resources/glue/runtime.stub.ts emits a sagas/runtime.ts whose only import is:
No KV adapter is registered. openSagaRuntimeKv then calls getKv(), which auto-detects the Aspire-provisioned Redis/Garnet resource and throws because no adapter was opted in. Nothing under plugins/sagas/src imports @netscript/kv/redis outside tests.
@netscript/kv/redisis a valid export at 0.0.4 (./redis → ./redis.ts), so the fix is available — it is simply never wired into the generated glue.
Why the error message alone is not enough
The message prescribes the correct fix, but sagas/runtime.ts is generator-emitted glue. A hand-added import '@netscript/kv/redis'; is at risk of being overwritten on the next regeneration, so the import belongs in the stub rather than in user edits.
Impact
Any scaffolded project that uses the sagas plugin with the default cache backend has a saga runner that will not start. The observed cost to a fresh agent was roughly 10 minutes of diagnosis plus a behavioural workaround: pinning the saga KV provider to a local Deno KV file, which gets restart-durability but silently gives up the shared durable store across processes.
Acceptance
The emitted sagas runtime glue registers the Redis adapter (or otherwise resolves the provider) so a default scaffold starts cleanly against the Aspire cache resource
A scaffolded project with the sagas plugin and the default cache backend starts its sagas resource without a KvConnectionError
A test that fails without the fix covers the generated-glue path, not only the engine
The CACHE_PROVIDER=denokv path stays working for frontend/SSR projects
A saga is driven through its full lifecycle end to end in a locally scaffolded project — not a unit test — and the run is proven with OTEL evidence
Verification must go further than last time
#1064/#1065/#1066 closed with passing tests and the saga surface was still broken on a real scaffold. Engine-level coverage is what let this through: it never scaffolded a project, never started an AppHost, and never watched a saga actually run. Repeating that shape here would close this issue with the same gap.
Required before this is called done:
Scaffold a fresh project locally with the sagas plugin and the default cache backend. Not a fixture, not a test harness — the thing a user gets.
Start the AppHost and confirm the sagas resource reaches a genuinely healthy state. state: Running with healthStatus: Healthy and an empty healthReports array means nothing was checked — that is not proof.
Prove it with traces and spans, via aspire otel traces / aspire otel spans / aspire otel logs against the failing-then-passing resource. The spans must show the saga steps actually executing, and correlation must hold across them — .correlate() (sagas: engine ignores .correlate() extractor - all workflows collapse onto one shared instance #1066) collapsed every workflow onto one instance and engine tests did not catch it.
Show the RED first. Capture the KvConnectionError on the unfixed scaffold, then the same scenario passing after the fix. A green run alone does not demonstrate the defect was the thing fixed.
Verify the artefact, not the exit code. A piped command reports the last stage's status; confirm the files exist and the boundary actually ran.
Restart durability is part of the lifecycle, not a separate nicety: restart the process and confirm the saga state is still correct afterwards, since the whole reason the durable store exists is that it survives a restart.
The sagas scaffold emits
sagas/runtime.tswith no KV adapter registration, while the AppHost provisions a Redis/Garnet cache resource by default. The saga background processor therefore crashes on startup in a freshly scaffolded project.Found by the wave-4 DeepSeek attribution-control run on published
0.0.4(/home/codex/repos/wave4-deepseek-004), building a deploy queue. This is downstream of #1064/#1065/#1066 — those fixed the saga engine; the generated glue for the durable store is still broken.Reproduction
Scaffold a project with the sagas plugin and the default cache backend, then start the AppHost. The
sagasresource dies immediately:The saga API stays up but returns an empty set forever —
GET /api/v1/sagas/sagas→{"sagas":[],"total":0,...}— so the failure reads as "no sagas yet" from the outside.Cause
plugins/sagas/src/adapter/resources/glue/runtime.stub.tsemits asagas/runtime.tswhose only import is:No KV adapter is registered.
openSagaRuntimeKvthen callsgetKv(), which auto-detects the Aspire-provisioned Redis/Garnet resource and throws because no adapter was opted in. Nothing underplugins/sagas/srcimports@netscript/kv/redisoutside tests.@netscript/kv/redisis a valid export at 0.0.4 (./redis→./redis.ts), so the fix is available — it is simply never wired into the generated glue.Why the error message alone is not enough
The message prescribes the correct fix, but
sagas/runtime.tsis generator-emitted glue. A hand-addedimport '@netscript/kv/redis';is at risk of being overwritten on the next regeneration, so the import belongs in the stub rather than in user edits.Impact
Any scaffolded project that uses the sagas plugin with the default cache backend has a saga runner that will not start. The observed cost to a fresh agent was roughly 10 minutes of diagnosis plus a behavioural workaround: pinning the saga KV provider to a local Deno KV file, which gets restart-durability but silently gives up the shared durable store across processes.
Acceptance
The emitted sagas runtime glue registers the Redis adapter (or otherwise resolves the provider) so a default scaffold starts cleanly against the Aspire cache resource
A scaffolded project with the sagas plugin and the default cache backend starts its
sagasresource without aKvConnectionErrorA test that fails without the fix covers the generated-glue path, not only the engine
The
CACHE_PROVIDER=denokvpath stays working for frontend/SSR projectsA saga is driven through its full lifecycle end to end in a locally scaffolded project — not a unit test — and the run is proven with OTEL evidence
Verification must go further than last time
#1064/#1065/#1066 closed with passing tests and the saga surface was still broken on a real scaffold. Engine-level coverage is what let this through: it never scaffolded a project, never started an AppHost, and never watched a saga actually run. Repeating that shape here would close this issue with the same gap.
Required before this is called done:
sagasresource reaches a genuinely healthy state.state: RunningwithhealthStatus: Healthyand an emptyhealthReportsarray means nothing was checked — that is not proof.sagaCompensate(sagas: sagaCompensate effects are silently dropped - createDurableSagaRuntime wires no compensator by default #1065) is exactly the kind of thing engine tests passed and reality did not.aspire otel traces/aspire otel spans/aspire otel logsagainst the failing-then-passing resource. The spans must show the saga steps actually executing, and correlation must hold across them —.correlate()(sagas: engine ignores .correlate() extractor - all workflows collapse onto one shared instance #1066) collapsed every workflow onto one instance and engine tests did not catch it.KvConnectionErroron the unfixed scaffold, then the same scenario passing after the fix. A green run alone does not demonstrate the defect was the thing fixed.Restart durability is part of the lifecycle, not a separate nicety: restart the process and confirm the saga state is still correct afterwards, since the whole reason the durable store exists is that it survives a restart.