feat(orchestrate): C5a — resumable saga engine (spec 09) - #8
Merged
Conversation
The load-bearing spine of the up saga: an ordered Phase driver that is
resumable, crash-safe, compensating, and observable. Owns no domain logic —
it sequences Phases under the lock+ledger spine.
- Phase: name/scope, Mutating, AlwaysRun, Fingerprint, Run, Compensate.
- Saga.Run: per phase — compute fingerprint, SKIP iff saga_phase is satisfied
for that exact fingerprint (a changed fingerprint or a started-but-not-
satisfied row re-arms it → crash mid-phase re-runs), StartPhase under the
flock, run the body lock-free (the phase owns its own short mutating critical
sections), then Satisfy/Fail under the flock. A panic in Run becomes a
failure, never a CLI crash.
- Compensation: on a downstream failure the succeeded mutating phases unwind in
REVERSE (best-effort; a comp error is logged, not fatal), and each unwound
phase row is cleared so a re-run redoes it. Non-mutating phases and
intentionally-kept mutations (the shared network, provisioned data) declare no
Compensate — a failed up never destroys data (spec 09 hard rule).
- Output contract: a Record per phase ({phase,scope,status,durationMs,error,
detail}, error null on success) for --json; FormatPlain for non-TTY; an Emit
callback for live streaming; AnyFailed for the exit code. Every transition
also writes an event_log row.
Unit-tested against a REAL temp ledger + fake phases: happy path, skip-on-
satisfied, fingerprint re-arm, AlwaysRun (secrets), failure→reverse
compensation, resume-after-failure (crash recovery), panic→failure, the JSON
error:null contract, plain rendering, fingerprint stability, Emit streaming.
This is C5a (the engine). The concrete daemon phases (clone/network/shared/
provision/generate/compose-up/hooks wiring + the shared-postgres host-port
coupling + the integration test) are C5b.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Chunk C5a (M2-remainder) — the saga engine
The load-bearing spine of the
upsaga: an ordered Phase driver that is resumable, crash-safe, compensating, and observable. It owns no domain logic — it sequences Phases under the lock+ledger spine. Per spec 09.What
Phase—Name/Scope,Mutating,AlwaysRun,Fingerprint,Run,Compensate.Saga.Run— per phase: compute fingerprint → skip iffsaga_phaseis satisfied for that exact fingerprint (a changed fingerprint, or astarted-but-not-satisfiedrow, re-arms it → crash mid-phase re-runs) →StartPhaseunder the flock → run the body lock-free (the phase owns its short mutating critical sections) →Satisfy/Failunder the flock. A panic inRunbecomes a failure, never a CLI crash.Compensate— a failedupnever destroys data (spec 09 hard rule).Recordper phase ({phase,scope,status,durationMs,error,detail},errornull on success) for--json;FormatPlainfor non-TTY; anEmitcallback for live streaming;AnyFailedfor the exit code. Every transition also writes anevent_logrow.Tests
Against a real temp ledger + fake phases: happy path, skip-on-satisfied, fingerprint re-arm, AlwaysRun (secrets), failure→reverse compensation, resume-after-failure (crash recovery), panic→failure, the JSON
error:nullcontract, plain rendering, fingerprint stability, Emit streaming.Gate
make cigreen (race included).🤖 Generated with Claude Code