Skip to content

sdk: reserve/commit/release journal over the existing store verbs #538

Description

@mfw78

Reframed 2026-07-24 after a multi-lens architecture study. The original premise, that a duplicate-submit window can be closed purely in the SDK, does not hold: a reserve marker is ambiguous between "accepted, commit write faulted" and "crashed or cancelled mid-submit, outcome unknown", so a local-only resolution is always wrong in one of the two. See #572 (held) for the failed local-only attempt.

What

Consolidate the hand-rolled submit-dedup into Journal::{reserve, commit, release, observed} on the existing LocalStoreHost verbs (get/set/delete), in crates/nexum-sdk/src/keeper.rs. No WIT change, no host change, no new interface. The study verdict was decisive: redb is single-process and dispatch is single-actor serialized, so there is no concurrent or cross-process race and atomicity buys nothing the fix needs. The defect is durability ordering, and a plain set() is already fsync-durable per operation.

reserve writes a distinct pre-submit marker durably before the submit await. commit overwrites it with the committed marker on acceptance. release deletes it on a retryable fault. Store distinct RESERVED and COMMITTED marker values and read the value on recovery, so an in-flight reserve is distinguishable from a done commit; treat any legacy empty presence marker as committed for forward compatibility.

Why

This is at-most-once within one process, honestly framed. It closes the window where the venue accepted and a separate record write then faulted while the process lived.

Not guaranteed

Not exactly-once across the HTTP effect. A crash, OutOfFuel trap, or deadline-cancel during the submit await leaves an ambiguous reserve that no local primitive can resolve. Recovery is the sweep reconcile in #573, against the venue idempotency key in #574. This issue is not complete on its own; it is the SDK-shape half.

Scope

  • nexum-sdk Journal: record/contains become reserve/commit/release, plus observed for the witness path and pending over the reserve prefix for reconcile and operator enumerate.
  • Migrate the submit-dedup sites: videre-sdk::Keeper::sweep, composable-cow sweep, ethflow-watcher observed path. The stop-loss module is being deleted (modules: delete the stop-loss example, subsumed by the generic ComposableCoW monitor #568).
  • Do not fold in Gates, the Retrier refused: marker, or the in-memory watch eviction: they are already safe under single-actor serialization and want crash-durability only, which set() already gives.

Acceptance criteria

Journal exposes reserve/commit/release/observed/pending over the existing verbs, the dedup sites use it, the value distinguishes reserved from committed, and the doc states plainly that this is at-most-once and that the reconcile and venue-key issues complete the story.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working.component/local-storeredb per-module state + byte quotascomponent/sdknexum-sdk / shepherd-sdk, proc macros, cargo-nexumeffort/daysFew days. Needs design.p1-brokenActually broken. Fix this week.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions