Summary
A saga handler that returns sagaCompensate(...) produces no error, no log, and no
compensation. The effect is silently discarded by the runtime that createDurableSagaRuntime
builds.
Compensation is the mechanism the "a multi-step operation must not leave the world half-changed"
guarantee rests on, and the sagas capability page presents .compensate() + sagaCompensate() as
a live, first-class path with no warning that it requires additional wiring.
Found by a wave-four demo agent with no prior exposure to NetScript. Related: #1064 (same surface,
same session).
Reproduction
Saga definition registers both the failure handler and a compensation branch:
.on('PageDeliveryFailed', (state, message) => [
sagaCompensate({ level: state.level, pages: state.pages }, 'delivery failed mid-level'),
])
.compensate('PageDeliveryFailed', (state) => /* void the half-sent pages, escalate */)
Then signal it:
curl -X POST .../escalation/signal -d '{"type":"PageDeliveryFailed", ...}'
Observed
job result: {"delivered": false, "voided": 1, "reroutedToLevel": 2} # decision.level stayed 2
saga history: [triggered, level-paged, deadline-elapsed, escalated] # no level-voided, no escalation
The compensation branch never ran. Nothing was logged. Nothing errored. The sagaCompensate
effect simply evaporated between the handler returning it and the runtime processing the effect
ledger.
Root cause
The runtime built by createDurableSagaRuntime wires no compensator by default —
SagaBusBridgeOptions.compensator is optional, and when it is absent a returned sagaCompensate
effect is dropped rather than rejected.
So the default runtime silently does not support a documented first-class effect.
Why this is worse than a normal missing feature
The failure mode is invisible in every direction:
- The handler returns successfully.
- The effect ledger is accepted.
- No warning is emitted that an effect kind is unhandled.
- Saga history simply lacks the compensation entries, which reads as "the branch didn't trigger"
rather than "the runtime ignored it".
An author following the capability page has no signal that their compensation logic is dead code.
Workaround used
Moved the undo logic into a plain .on('PageDeliveryFailed') failure branch. Note the storefront
tutorial does recommend that shape — so the docs contain two paths, one of which works and one of
which silently does not, with nothing marking the difference.
Acceptance
Provenance
Wave-four demo run 1 (Claude Fable 5, high effort), 2026-08-03. ~20 min lost (combined with the
.correlate() finding filed separately).
Full drift log: /home/codex/repos/wave4-fable/.llm/run/drift.md, finding #3 of 9.
Product repo: https://github.com/rickylabs/vigil
Summary
A saga handler that returns
sagaCompensate(...)produces no error, no log, and nocompensation. The effect is silently discarded by the runtime that
createDurableSagaRuntimebuilds.
Compensation is the mechanism the "a multi-step operation must not leave the world half-changed"
guarantee rests on, and the sagas capability page presents
.compensate()+sagaCompensate()asa live, first-class path with no warning that it requires additional wiring.
Found by a wave-four demo agent with no prior exposure to NetScript. Related: #1064 (same surface,
same session).
Reproduction
Saga definition registers both the failure handler and a compensation branch:
Then signal it:
curl -X POST .../escalation/signal -d '{"type":"PageDeliveryFailed", ...}'Observed
The compensation branch never ran. Nothing was logged. Nothing errored. The
sagaCompensateeffect simply evaporated between the handler returning it and the runtime processing the effect
ledger.
Root cause
The runtime built by
createDurableSagaRuntimewires no compensator by default —SagaBusBridgeOptions.compensatoris optional, and when it is absent a returnedsagaCompensateeffect is dropped rather than rejected.
So the default runtime silently does not support a documented first-class effect.
Why this is worse than a normal missing feature
The failure mode is invisible in every direction:
rather than "the runtime ignored it".
An author following the capability page has no signal that their compensation logic is dead code.
Workaround used
Moved the undo logic into a plain
.on('PageDeliveryFailed')failure branch. Note the storefronttutorial does recommend that shape — so the docs contain two paths, one of which works and one of
which silently does not, with nothing marking the difference.
Acceptance
createDurableSagaRuntimewires a working compensator by default, so a returnedsagaCompensate(...)runs the registered.compensate()branchloudly (throw or error-log naming the effect and the missing option) rather than being
dropped, and the capability page documents the requirement at the point
.compensate()isintroduced
sagaCompensateeffect from a handler actually invokes the registeredcompensation branch through the default runtime
Provenance
Wave-four demo run 1 (Claude Fable 5, high effort), 2026-08-03. ~20 min lost (combined with the
.correlate()finding filed separately).Full drift log:
/home/codex/repos/wave4-fable/.llm/run/drift.md, finding #3 of 9.Product repo: https://github.com/rickylabs/vigil