Skip to content

Add opt-in bound-intent fence to the ordered-effect engine - #4805

Merged
EHotwagner merged 3 commits into
mainfrom
routine/coord1026-bound-intent-fence
Oct 22, 2026
Merged

EHotwagner merged 3 commits into
mainfrom
routine/coord1026-bound-intent-fence

Conversation

@EHotwagner

@EHotwagner EHotwagner commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Add the shared, opt-in bound-intent fence that the Coordination forward Execution-pair route requires before it may ever be enabled. It lives inside the existing helper scripts/release_successor_execution.py (one of the seven pinned helper files), so Coordination needs no source change, only a re-pin.

Design

BoundIntentFence(invocation) is one object per caller invocation, passed as fence= to advance, advance_effects or full_transition.

  • Arm. Only the intent compare-and-swap this invocation just won arms it, binding (content id, effect, the exact JournalState that write produced). An intent read from the journal at the start of any transition (earlier invocation, or this one in an earlier call) never arms it and is settled by readback only.
  • Release. Immediately before provider.dispatch, after the existing dispatch admission, the engine spends the permit and re-reads the journal; the send proceeds only if the journal equals the state our write produced. A changed, unreadable or merely claimed write (CAS reports a win without writing) refuses before the provider.
  • Guard. The provider the engine calls is wrapped, so dispatch itself refuses unless the release just happened. The enforcement therefore does not depend on every engine code path remembering to call the fence: a regressed engine that resends an inherited intent is refused.
  • Shape. Single-use permit, at most one outstanding, none armed after a refused release, bound to content id and effect, never armed twice for one effect, closed invocation identifier ([A-Za-z0-9][A-Za-z0-9._:-]{0,127}, named in refusals), BOUND_INTENT_FENCE = 1 capability marker. Trust root, stated in the docstring: the release compares journal state, so it relies on a linearizable CAS and a fresh journal.read(). fence.written and fence.sends expose invocation-local evidence only; the fence grants no authority, admission or retry.
  • Compatibility. fence is keyword-only and defaults to None; without it control flow is unchanged (identical journal writes, provider calls and admission calls over all 16 effects, asserted). No journal schema, protocol or consumer changes; the other six pinned helpers are byte-identical.

Evidence

tests/release-successor-execution/run.py (hosted by the existing release-saga-tooling, telemetry-host-package, new-sdd-workspace-selftest and publish workflows): 19 tests, 4 new. fence_controls.py holds 19 causal scenarios run against the shipped engine and against 22 source mutants (each must be killed by its named control, e.g. engine never arms/releases, journal comparison skipped, unreadable journal tolerated, guard bypassed or not checking, permit reusable, content/effect binding dropped, outstanding/repeat intent allowed, identifier unchecked). A separate control shows the defended regression is real: with an engine regressed to resend an inherited intent, the unfenced call dispatches tag, the fenced call is refused, and removing the guard lets it through again.

Also run locally unchanged-green on the committed head: release-successor-recovery, release-successor-journal, release-successor-live, telemetry-host-successor, new-sdd-workspace-successor (211) and release-successor-authentication --current-source-suspension (138).

Re-pin values for Coordination (per-file sha256 at this head; only the first differs from the current pin):
scripts/release_successor_execution.py b5187d0ad7655cf49b5f0458cf65c06e02973a78b8442531f5d8d8254473e697 (was 038661268f3a9339...; previous review head 3af39cd had c031d272...); the other six are unchanged. The re-pin commit is the post-merge main commit and is recorded after merge; Coordination's route may later switch from its invocation-local SendFence to fence=, which is not required by this change.

No release, publication, journal, credential or live effect is changed or implied; Coordination's admission stays disabled until its own re-pin and live-effect admission.

Refs FS-GG/FS.GG.Coordination#1026. architecture-map: unaffected.

🤖 Generated with Claude Code

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