0%

Mutation-ownership boundary primitive (completion-gated; blocks all downstream sections)

Goal

Establish, by a FALSIFIED PROTOTYPE (not a design paragraph), the single linearizable, client-portable, in-shared-tree ownership/mutation boundary that records a per-session path -> last-authored blob/mode/tombstone set covering EVERY mutation channel and failing closed on a missing channel. This section gates the whole plan: the downstream cutover sections (scope-from-record, live-vs-record preflight, candidate-derived projection, freeze-all + per-repo CAS publish, stage-contract + failure-envelope parity) cannot be authored until the primitive is chosen, because their shape depends on it.

Constraints the primitive MUST satisfy

  • Linearizable: a post-write live READ is provably inadequate (the validate-to-capture window moves, it does not close).
  • Client-portable: hook_bridge.SUPPORTED_EVENTS = {Stop, PreToolUse}; Codex has no PostToolUse event, so a Claude-only hook cannot own a tree Codex writes.
  • Full channel coverage: Claude Edit/Write, Codex apply_patch, Bash/formatter, in-process runtime writers (e.g. refresh_bug_tracker_views).
  • Fail closed: a mutation through an un-covered channel refuses BEFORE it mutates.
  • Shared-tree only: session worktrees are excluded per the owner-CONFIRMED requirement (mission.md ## Constraint); the owner confirmed one shared worktree directly. This is NOT a proven technical incompatibility — the graph MECHANISM is explicitly unverified (linked worktrees are supported; the graph resolves to MAIN_ROOT). The requirement stands on the owner’s confirmation.

Falsifier matrix (Section 1 acceptance — the section-owned criterion binds to a probe covering EVERY row)

Probe: scripts/commit_push/tests/test_mutation_boundary_primitive.py. The chosen primitive is not selected until it passes all four:

  1. RACE: a peer mutates the same path BETWEEN intent capture and tool execution -> reconciliation hits case (3) and publication REFUSES; it NEVER journals the peer’s bytes, and the session’s intended bytes are retained.
  2. CHANNEL COMPLETENESS: exercise each supported mutation channel (Write, Edit, apply_patch) -> assert EXACTLY ONE finalized session record carries the intended identity.
  3. MEDIATION: attempt an un-wrapped repository-mutating Bash form, a formatter, a rename/delete/mode/symlink, and an in-process runtime write -> each is DENIED before mutation OR mediated into an intent; none silently mutates the tree un-recorded.
  4. CRASH RECOVERY: crash after intent / after physical write / after finalization -> recovery classifies each into finalize/discard/conflict WITHOUT reading peer bytes into ownership.

Work Items

  • Prototype candidate B (LEADING) — the tentative-intent protocol. At PreToolUse (the one event Claude Code + Codex both expose per hook_bridge.SUPPORTED_EVENTS) record an IMMUTABLE tentative mutation intent per owned path: {expected preimage identity (blob/mode/tombstone), computed desired postimage identity}. For a full Write the postimage is direct; for Edit/apply_patch the adapter applies the delta to the expected preimage IN MEMORY to compute the postimage — never reading live post-write bytes. Reconciliation (at the next mediated event, Stop, or commit-push PreToolUse) is a THREE-way exhaustive identity compare on the live path: (1) desired postimage present -> finalize the intent as owned; (2) expected preimage still present -> the tool no-op’d, discard the tentative intent; (3) any THIRD identity -> same-path conflict, REFUSE (never journal peer bytes). Deletes/renames/modes/symlinks use the same preimage/postimage transaction; crash recovery processes abandoned tentative records.
  • The enforcement teeth for candidate B (recording an intent is NOT mediating the channel). A Python helper cannot interpose native Edit/Write/apply_patch, and a PreToolUse classifier cannot prove an arbitrary Bash subprocess is non-mutating. So: mutation-aware wrappers emit intents for the sanctioned paths, AND every un-wrapped repository-mutating Bash/formatter form is mechanically DENIED before mutation (raw mutation authority removed). Deterministic runtime outputs (refresh_bug_tracker_views) are candidate-derived PROJECTION rendered from candidate inputs into immutable entries, NOT physical-write journaling. The prototype must demonstrate ENFORCEMENT, not merely the journal API.
  • Prototype candidate A (COMPETITOR) — generic write-interposition content-addressing bytes AS they write. Retained as the alternative, but REJECTED unless complete mediation is mechanically proven: absent an OS/filesystem enforcement boundary or removal of raw mutation authority, a helper cannot interpose native tools nor prove a Bash subprocess non-mutating. Select the surviving primitive against: linearizable (post-write read provably inadequate), client-portable, all-channel, fail-closed, ONE shared tree (session worktrees excluded per the owner constraint). Record the falsified-loser candidate(s) so they are never retried.

Downstream (deferred until the primitive lands — the plan grows on close)

scope-from-record through PREPARE; live-vs-record preflight (mismatch REFUSES); candidate-derived projection for refresh_bug_tracker_views; capture-all + freeze every repo; per-repo CAS publish with existing overlap/re-gate; no live-capture fallback (absent record HALTS); multi-repo parity; bypass + hook-failure envelope parity.