Skip to content

feat(orchestrate): C5a — resumable saga engine (spec 09) - #8

Merged
gustavobertoi merged 1 commit into
mainfrom
nightly/C5a
Jun 29, 2026
Merged

feat(orchestrate): C5a — resumable saga engine (spec 09)#8
gustavobertoi merged 1 commit into
mainfrom
nightly/C5a

Conversation

@gustavobertoi

Copy link
Copy Markdown
Contributor

Chunk C5a (M2-remainder) — the saga engine

The load-bearing spine of the up saga: 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.

The spec calls C5 the largest chunk and recommends splitting it. This PR is C5a (the engine); the concrete daemon-phase wiring (clone/network/shared/provision/generate/compose-up/hooks) + the shared-postgres host-port coupling (the flagged design item) + the daemon integration test are C5b.

What

  • PhaseName/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 short mutating critical sections) → 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. 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.

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:null contract, plain rendering, fingerprint stability, Emit streaming.

Gate

make ci green (race included).

🤖 Generated with Claude Code

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>
@gustavobertoi
gustavobertoi merged commit cb128b4 into main Jun 29, 2026
9 checks passed
@gustavobertoi
gustavobertoi deleted the nightly/C5a branch June 29, 2026 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant