Skip to content

feat(durable): durable step, &self context, and replay divergence guard#4970

Merged
bug-ops merged 1 commit into
mainfrom
feat/m28/4947-durable-step-context-replay
Jun 6, 2026
Merged

feat(durable): durable step, &self context, and replay divergence guard#4970
bug-ops merged 1 commit into
mainfrom
feat/m28/4947-durable-step-context-replay

Conversation

@bug-ops

@bug-ops bug-ops commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Part of epic #4707 (native durable execution layer, spec-064), child issue C4. Builds on C1 #4944 (scaffold), C2 #4945 (cipher), C3 #4946 (writer/backend).

What

Implements the execution heart every adapter wraps: the DurableStep typestate, the &self DurableContext, the effect-class ambiguity model, and the fingerprint-guarded replay cursor.

  • step.rsStepDescriptor with the construction-time ambiguity rule (FR-DE-09); StepHandle (exposes the IdempotencyKey for boundary dedup); StepError; the Live/Replayed StepOutcome; the DurableStep record; JSON payload codec.
  • effect.rsEffectIntentSubClass and the OnAmbiguous policy (cost-bearing defaults to Skip; destructive/security/money/custom require an explicit policy).
  • handle.rsDurableContext (&self, AtomicU32 step ids): step/step_recorded, parallel()ParallelScope with eagerly-assigned contiguous ids, the exactly-once intent-before-op / result-after protocol, the BLAKE3 replay-divergence guard, the ambiguous-window resolution with a mandatory audit record, the INV-13 idempotency-key point lookup, and writer-timeout degradation. A sleep_until stub is reserved for the C5 timer layer.
  • replay.rs — segment-buffered ReplayCursor with O(segment) resident memory (range reads, consume-on-lookup, defer-last-step boundary handling).
  • backendDurableBackendEnum::Local now holds Arc<LocalBackend> so the writer and the cursor share one durable.db; lookup_committed_result (idem-key point lookup) added to the sealed ExecutionBackend trait and the enum dispatch.

Acceptance criteria (all covered by unit tests)

Criterion Test
INV-2 — structural step ids, completion-order-independent parallel_step_ids_are_completion_order_independent
INV-3 / FR-DE-03 — fingerprint divergence → abort + restart replay_divergence_on_fingerprint_mismatch
FR-DE-01 — fresh step records before returning fresh_step_runs_op_and_journals_result
INV-10 / FR-DE-02 — replayed idempotent skips op replayed_idempotent_step_skips_op
FR-DE-04 — guarded intent before op, result after guarded_step_commits_intent_before_result
INV-13 — committed guarded effect not re-fired inv13_committed_guarded_result_is_not_refired
FR-DE-09 — guarded destructive without policy rejected at construction guarded_destructive_requires_explicit_policy
FR-DE-10 / FR-DE-14 — ambiguous Fail surfaces, no re-fire ambiguous_window_fail_policy_surfaces_error
§10 — concurrent step() under &self sound concurrent_steps_under_shared_ref_are_sound

Validation

  • cargo nextest run -p zeph-durable — 79 passed.
  • cargo clippy -p zeph-durable --all-targets -- -D warnings — clean under sqlite and postgres.
  • cargo test --doc -p zeph-durable — 30 doc-tests passed.
  • RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc — clean (0 warnings).
  • cargo check --features postgres and RUSTFLAGS="-D warnings" cargo check --all-targets — clean.

Spans durable.step.run / durable.step.replay / durable.replay.cursor.* are present.

Out of scope (follow-ups)

DurablePromise/DurableTimer bodies (C5), retention/compaction (C5), CLI/config/TUI (C6), the agent-loop and other adapters (A1–A4). The live testing playbook (.local/testing/playbooks/durable.md) records scenarios 1–10 as blocked until the C6 CLI + adapters land.

Closes #4947

@github-actions github-actions Bot added documentation Improvements or additions to documentation rust Rust code changes dependencies Dependency updates enhancement New feature or request size/XL Extra large PR (500+ lines) labels Jun 6, 2026
Implement the execution heart of spec-064 (epic #4707, child C4): the
DurableStep typestate, the &self DurableContext, and the fingerprint-guarded
replay cursor that every adapter wraps.

- step.rs: StepDescriptor with the construction-time ambiguity rule (FR-DE-09),
  StepHandle exposing the IdempotencyKey for boundary dedup, StepError, the
  Live/Replayed StepOutcome, and the DurableStep record. JSON payload codec.
- effect.rs: EffectIntentSubClass and the OnAmbiguous policy with the
  cost-bearing default and the explicit-policy requirement.
- handle.rs: DurableContext (&self, AtomicU32 step ids) with step/step_recorded,
  parallel()/ParallelScope for completion-order-independent batches (INV-2), the
  exactly-once intent-before-op / result-after protocol (FR-DE-04), the BLAKE3
  replay-divergence guard that aborts and restarts fresh (INV-3, FR-DE-03), the
  ambiguous-window resolution with a mandatory audit record (FR-DE-10), the
  INV-13 idempotency-key point lookup, and writer-timeout degradation (INV-12).
- replay.rs: segment-buffered ReplayCursor with O(segment) resident memory.
- backend: DurableBackendEnum now holds Arc<LocalBackend> so the writer and the
  cursor share one durable.db; lookup_committed_result added to the sealed
  ExecutionBackend trait and the enum dispatch.

79 zeph-durable tests pass; clippy, rustdoc, and RUSTFLAGS are clean under both
the sqlite and postgres backends.
@bug-ops bug-ops force-pushed the feat/m28/4947-durable-step-context-replay branch from 2121041 to d48533f Compare June 6, 2026 23:10
@bug-ops bug-ops enabled auto-merge (squash) June 6, 2026 23:10
@bug-ops bug-ops merged commit d461b93 into main Jun 6, 2026
36 checks passed
@bug-ops bug-ops deleted the feat/m28/4947-durable-step-context-replay branch June 6, 2026 23:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Dependency updates documentation Improvements or additions to documentation enhancement New feature or request rust Rust code changes size/XL Extra large PR (500+ lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(durable): durable step + context, effect-class policy, replay divergence guard

1 participant