Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/implementation/pre-merge-rebase.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Pre-merge rebase and foreign-conflict boundary
# Pre-merge rebase and sandboxed conflict boundary

**Issue and lane:** #22, lanes F3-F4. This is the trusted local rewrite, recovery, and foreign-conflict engine boundary. The `rebase-fix` child adapter is assembled inside the production rebaser through lane D with durable resource ownership and bounded exact-path copy-in/copy-out. No production request starts that rebaser yet: F5-F6 must own admission plus the current base/head refresh before invoking it, then push the rewritten head, run plan `cmd:` checks, wait for GitHub checks, and hand the validated pair to the merge coordinator. Exposing the raw rebaser earlier would bypass those required guards.
**Issue and lane:** #22, lanes F3-F4. This is the trusted local rewrite, recovery, and sandboxed conflict engine boundary. The `rebase-fix` child adapter is assembled inside the production rebaser through lane D with durable resource ownership and bounded exact-path copy-in/copy-out. No production request starts that rebaser yet: F5-F6 must own admission plus the current base/head refresh before invoking it, then push the rewritten head, run plan `cmd:` checks, wait for GitHub checks, and hand the validated pair to the merge coordinator. Exposing the raw rebaser earlier would bypass those required guards.

## Ownership and lifecycle

Expand All @@ -18,11 +18,11 @@ Admission checks the current plan revision, snapshot, review version, task state

After replay settles and before its first result-ref write, the rebaser supplies the Store's preparation hook only the ordered rewritten history. The Store zips that history by position with the independently captured `oldHistory`, then durably binds the resulting complete mapping and exact intended ref value as `prepared`. The atomic create outcome advances it to `ready`, `uncertain`, or `refused`; an already-current no-op uses `prepared` then `ready` without a ref write. `Store.finishRebase` accepts only a `ready`, byte-for-byte mapping from the same attempt while the captured plan, snapshot, review version, task version, old base, old head, and target base are unchanged. A non-empty mapping must end at both the captured old head and the committed rewritten head; an empty mapping is valid only when the captured history is empty and the result equals the new base. The snapshot, rewrite map, and owned/foreign ledger provenance commit atomically, and the marker clears in that transaction. A stale result changes nothing and leaves its marker for exact-attempt cleanup.

For each conflict, classification uses only the supplied trusted ledger. A missing entry and an explicit `{ owner: null, origin: "foreign" }` entry take the foreign branch; trailers are never read. An owned entry is refused until the separate owned-commit resolver exists. A gitlink conflict is also refused for manual resolution before the file resolver runs; the boundary never enters a nested repository. The injected foreign resolver receives the source commit and exact non-gitlink conflicting path set as a read-only string array. Raw Git path streams use the same explicit 32 MiB and 262,144-entry bounds as checkout verification, travel between processes as base64, and undergo fatal UTF-8 decoding so malformed names fail closed without rejecting a valid literal replacement character. Before and after the resolver runs, one killable helper batches the index scan and hashes every non-conflict changed path against the same pinned `HEAD`; separate bounded helpers hash every index-only gitlink entry and the byte-exact rebase control directory without following links. These helpers run inside the operation deadline instead of blocking the server event loop or spawning once per path. After the resolver settles, the rebaser proves both `HEAD` and `REBASE_HEAD` are unchanged, rejects every tracked or untracked edit outside the conflict set, refuses any change to the remaining rebase operation, stages only literal NUL-delimited path data on stdin, then rechecks the gitlink index so an allowed file cannot become an embedded repository. It also refuses any remaining unmerged entry. A rejection or invocation failure aborts and removes the partial rewrite.
For each conflict, classification uses only the supplied trusted ledger. A missing entry and an explicit `{ owner: null, origin: "foreign" }` entry take the foreign branch; trailers are never read. An `{ owner: Px, origin: "owned" }` entry takes the owned branch and binds the invocation to the exact Px definition from one captured plan revision. That complete invocation context is rechecked immediately before launch and again after the final asynchronous export, before any output is copied back, so a concurrent plan, snapshot, assignment, referenced-code, or state-generation change cannot run or apply stale instructions. A gitlink conflict is refused for manual resolution before either file branch runs; the boundary never enters a nested repository. The injected resolver receives the source commit, trusted owner or null, the pinned pre-conflict head, and the exact non-gitlink conflicting path set as a read-only string array. Its bounded prompt names both commit identities; for owned work the existing captured `/run/codeboost-input/schema.json` envelope carries the exact plan item in a `planItem` property, while foreign work is explicitly told not to infer ownership from commit content. Raw Git path streams use the same explicit 32 MiB and 262,144-entry bounds as checkout verification, travel between processes as base64, and undergo fatal UTF-8 decoding so malformed names fail closed without rejecting a valid literal replacement character. Before and after the resolver runs, one killable helper batches the index scan and hashes every non-conflict changed path against the same pinned `HEAD`; separate bounded helpers hash every index-only gitlink entry and the byte-exact rebase control directory without following links. These helpers run inside the operation deadline instead of blocking the server event loop or spawning once per path. After the resolver settles, the rebaser proves both `HEAD` and `REBASE_HEAD` are unchanged, rejects every tracked or untracked edit outside the conflict set, refuses any change to the remaining rebase operation, stages only literal NUL-delimited path data on stdin, then rechecks the gitlink index so an allowed file cannot become an embedded repository. It also refuses any remaining unmerged entry. A rejection or invocation failure aborts and removes the partial rewrite.

Before any clone or Docker operation, the production adapter records a unique child attempt plus distinct storage and network allocation IDs in `conflict`. It clones the pinned pre-conflict `HEAD`, snapshots only the exact conflict leaves without following ancestor links, and allocates ordinary bounded task storage from that still-clean clone. Only after seeding succeeds does a no-network stdin-backed importer write the bounded snapshot into the work volume; metadata stays read-only. The importer owns a monotonic deadline before base64/JSON serialization and rechecks it before any Docker probe. The path set is capped at 32 KiB before ownership is claimed, keeping the provider prompt below the operating system's single-argument limit. Every individual Git or Docker client—validation, execution, and cleanup included—moves the durable marker through `spawning`, its exact process group, and settled or retained ownership; a retained client prevents every later subprocess from starting. Each child subprocess budget leaves 17 seconds for the default 5-second stop grace, 10-second group drain, 1-second pipe drain, and durable settlement, and every asynchronous child stage shares the parent's rebase-work boundary. The resolver carries that same monotonic budget through vendor-network creation, profile validation, and supervisor launch; those stages do not re-read the transported wall deadline. If a stage cannot settle by that boundary, the resolver returns with durable ownership retained instead of consuming the parent's 30-second cleanup reserve. A specialized pre-launch tree check allows the expected dirty conflict paths but rejects repository metadata, commits, outside paths, unsafe types, links, populated gitlinks, and unmountable gitlink layouts. The ordinary `fix` profile then runs with no command capability. Its host-side input snapshot is created beneath the canonical spelling of the durably known conflict staging directory, so retaining the parent workspace also retains every reported directory resource for restart cleanup. On success, the adapter audits the complete manifest, exports only the exact approved leaves with a 32 MiB raw-content budget, and copies them back without exposing the host worktree or shared Git metadata to the agent. Storage and staging cleanup complete before the marker clears; an unreleased resource keeps the marker and parent workspace durable. Startup recovery removes agent/network leftovers through lane D, aborts the parent worktree (including profile snapshots), removes an exactly matching child storage handle, and only then clears child and parent ownership.

The Store records each resolved source SHA before the result ref write. Preparation refuses duplicates, sources outside the captured history, owned sources, and sources that did not actually rewrite. On publication, the mapped commit retains `owner: null`, `origin: foreign`, and `sourceSha`, plus `conflictResolved: true`. Linking therefore keeps its lines in Unplanned while the review UI adds the text label “conflict resolved by agent.” The commit author is preserved by Git's replay and no Plan-Item trailer is added.
The Store records each resolved foreign source SHA before the result ref write. Preparation refuses duplicates, sources outside the captured history, owned sources, and sources that did not actually rewrite. On publication, a foreign mapped commit retains `owner: null`, `origin: foreign`, and `sourceSha`, plus `conflictResolved: true`. Linking therefore keeps its lines in Unplanned while the review UI adds the text label “conflict resolved by agent.” An owned mapped commit instead retains its Px owner and source SHA without foreign-resolution provenance; its preserved message keeps the original plan trailers, and the rewritten mapping lets review recompute whether Px's approval became stale. Every replay preserves the original commit author.

All other conflicts are a review outcome (`RebaseConflict`). Cancellation and failures settle the Git process group before cleanup. Cleanup removes only the validated UUID workspace. A partially created but unregistered worktree is removed only after Git's own worktree list proves it is not registered. Result refs are created atomically from the absent state; a reused attempt ID cannot overwrite or delete a previously retained result, while an ambiguous create is reconciled against the exact attempted value.

Expand All @@ -34,4 +34,4 @@ This ordering is intentionally local-only. The later push integration must exten

## Regression evidence

`test/runner-rebase.test.ts` covers clean one-to-one replay, the already-current no-op, process-group ownership, the whole-operation deadline, foreign conflict resolution, clean sibling files, gitlink-pointer auditing and manual gitlink-conflict routing, embedded-repository refusal, literal replacement-character paths, invalid ledger classification, outside-file, resolver-commit and rebase-control refusal, owned-conflict refusal, cancellation before and during Git, exact cleanup, retained refs, and a read-only source checkout. `test/runner-rebase-conflict.test.ts` covers claim-before-prepare ordering, distinct allocation identities, bounded no-follow host snapshots, nested modify/delete copy-in, copy-back audit, child settlement deadlines, cancellation settlement, cleanup ordering, and retained ownership for unreleased resources. `test/runner-lifecycle-store.test.ts` covers admission, durable process and child ownership, compare-and-swap races, resolved-source validation, and owned/foreign provenance. `test/runner-recovery.test.ts` covers marker validation, live-group fail-closed recovery, exact child-storage recovery, partial-match refusal, and restart ordering. `test/agent-process-group.test.ts` covers delayed-signal suppression after exit and retained ownership for an orphaned group. The real-container suite covers dirty-conflict admission and bounded exact-path export. `test/history.test.ts` and `test/browser/review.spec.ts` cover Unplanned attribution and the visible conflict-resolution label. `test/runner-production.test.ts` covers exact-plan recovery routing and adapter construction.
`test/runner-rebase.test.ts` covers clean one-to-one replay, the already-current no-op, process-group ownership, the whole-operation deadline, owned and foreign conflict resolution, clean sibling files, gitlink-pointer auditing and manual gitlink-conflict routing, embedded-repository refusal, literal replacement-character paths, invalid ledger classification, outside-file, resolver-commit and rebase-control refusal, cancellation before and during Git, exact cleanup, retained refs, and a read-only source checkout. `test/runner-rebase-conflict.test.ts` covers owned-item prompt binding, plan-revision races before launch and copy-back, claim-before-prepare ordering, distinct allocation identities, bounded no-follow host snapshots, nested modify/delete copy-in, copy-back audit, child settlement deadlines, cancellation settlement, cleanup ordering, and retained ownership for unreleased resources. `test/runner-lifecycle-store.test.ts` covers admission, durable process and child ownership, compare-and-swap races, resolved-source validation, and owned/foreign provenance. `test/runner-recovery.test.ts` covers marker validation, live-group fail-closed recovery, exact child-storage recovery, partial-match refusal, and restart ordering. `test/agent-process-group.test.ts` covers delayed-signal suppression after exit and retained ownership for an orphaned group. The real-container suite covers dirty-conflict admission and bounded exact-path export. `test/history.test.ts` and `test/browser/review.spec.ts` cover Unplanned attribution and the visible conflict-resolution label. `test/runner-production.test.ts` covers exact-plan recovery routing and adapter construction.
Loading
Loading