clone-topology¶
ADDED Requirements¶
Requirement: Lane isolation by construction¶
Each lane seat SHALL operate in a full, independent local clone of its project’s primary repository. A lane clone SHALL share no mutable git state with the primary or with any other lane: refs, branches, index, stash, worktree metadata, hooks, local configuration, and reflogs are all clone-private. The topology SHALL NOT rely on git worktrees for seat isolation.
Scenario: Stash is lane-private¶
WHEN two lanes each run
git stash pushconcurrentlyTHEN each entry lands on its own clone’s
refs/stashAND THEN neither lane can observe, clobber, or reorder the other’s entries
Scenario: Hooks and config are lane-private¶
WHEN a lane modifies its clone’s hooks or local git configuration
THEN no other lane’s git behavior changes
Requirement: Object storage via plain local clones¶
Lane clones SHALL be created as plain local-path clones of the primary
(git clone <primary-path> <lane-path>), yielding object-complete
repositories whose initial packs are hardlinked by git’s local-clone
optimization where the filesystem supports it. Alternates, --shared, and
--reference SHALL NOT be used. Each clone SHALL be safe to
garbage-collect, repack, or delete independently of every other clone.
(Hardlinked packs are immutable files: repack in either repository writes
new files and unlinks old ones rather than mutating shared content, so a
hardlink can never propagate changes; deleting either repository only
decrements link counts.)
Local-path cloning can race with concurrent mutation of the source, so
provisioning SHALL obtain a consistent snapshot: a local-path clone SHALL
be created only while the primary is quiescent, under a provisioning lock
that excludes commits, ref updates, garbage collection, and repack for the
clone’s duration. Alternatively, provisioning MAY use a transport mode
with snapshot semantics (the git transport via a file:// URL) at the
cost of hardlink savings. Multiple lanes MAY be provisioned concurrently
against the same quiescent primary: the lock excludes primary mutation,
not sibling clone operations. After cloning, provisioning SHALL validate
branch tips by hash against the primary before the lane begins work.
Scenario: Independent garbage collection is safe¶
WHEN
git gcor a repack runs in any clone after stable creationTHEN it considers only that clone’s refs and reflogs
AND THEN no object reachable in any other clone is affected
Scenario: Clone deletion is safe¶
WHEN a lane clone is deleted at seat retirement
THEN the primary and all other lane clones are unaffected
Scenario: Provisioning excludes source mutation¶
WHEN a lane clone is provisioned over a local path
THEN the provisioning lock holds the primary quiescent for the duration
AND THEN a primary mutation attempted during provisioning waits or fails; it can never yield a torn clone
Scenario: Concurrent provisioning from one quiescent primary¶
WHEN two lanes are provisioned concurrently from the same quiescent primary
THEN both obtain consistent clones
AND THEN both validate their branch tips by hash before their lanes begin work
Requirement: Lineage and remote naming¶
The canonical lineage SHALL be: durable origin (bare repository in synced
storage, or a hosted remote) → primary clone (~/src/<project>) → lane
clones (~/src/CLONES/<project>/<lane>). In every clone, the remote named
origin SHALL point at that clone’s immediate parent. Routine fetches flow
strictly downward (child fetches parent); parents read children only
through the review and integration flow.
If the primary is lost, recovery SHALL prefer recreating the primary from
the durable origin and then restoring each lane’s parent URL. Primary
state the durable origin never received is recoverable only to the extent
surviving lanes fetched it: recovery SHALL treat surviving lanes’
remote-tracking refs as best-effort sources, inventory their union, and
verify candidate tips by hash and ancestry before adopting them into the
recreated primary. When no surviving lane fetched the newest primary
commits, recovery SHALL explicitly declare and accept the loss window
rather than assume completeness. A lane MAY re-point origin at the
grandparent temporarily, and that is an explicitly DEGRADED state: the
disabled push configuration SHALL be preserved, and the lane SHALL return
to normal lineage once the primary is restored.
Scenario: Primary loss and best-effort recovery¶
WHEN the primary clone is lost
THEN a replacement primary is recreated from the durable origin
AND THEN the union of surviving lanes’ remote-tracking refs is inventoried and candidate tips are verified by hash and ancestry before adoption, and lanes resume normal lineage
Scenario: Unfetched primary state is declared lost¶
WHEN the lost primary held commits that no surviving lane and no durable origin ever received
THEN recovery explicitly declares the loss window covering those commits
AND THEN the recreated primary proceeds without silently claiming completeness
Scenario: Degraded grandparent operation is temporary¶
WHEN a lane must continue work before the primary is restored
THEN it re-points
originat the durable origin as a named degraded state, preserving its disabled push configurationAND THEN it returns to normal lineage once the primary exists again
Requirement: Branch ownership and naming¶
Within a lane clone, working branches SHALL be named for the change they
carry (existing convention, e.g. add-clone-topology); no seat prefix is
required because the integrator’s remote-tracking namespace
(refs/remotes/<lane>/...) already disambiguates seats. New branches
SHALL base on origin/<integration-branch> as seen from the lane.
History rewriting SHALL follow review state. A lane MAY rewrite branches freely while they are private and unreviewed. During review, findings are addressed with targeted fixup commits that extend the stack; consolidating them (autosquash) rewrites hashes and therefore stales any prior hash-based review, requiring an updated review packet for the new hashes. After final approval, the approved stack SHALL NOT be rewritten or rebased; base advances are handled by the integration requirement below.
WIP commits (the stash replacement) SHALL carry intentional untracked work by explicit staging; ephemeral and debug files SHALL live in ignored scratch locations and are never carried by WIP commits. The owning lane SHALL delete a working branch after observing, via fetch, that the integrator has merged it. Force pushes require no separate policy: a force push is a push, governed by the write-authority requirement’s behavioral rule, fail-fast guardrail, and eventual capability boundary like any other push.
Scenario: Cross-seat name collision is impossible¶
WHEN two lanes independently use the branch name
add-fooTHEN the integrator sees them as distinct remote-tracking refs under each lane’s named remote
Scenario: Consolidation during review stales the packet¶
WHEN a lane autosquashes review fixups into their targets before final approval
THEN the rewritten hashes invalidate the prior review packet
AND THEN the lane sends an updated packet for the new hashes
Scenario: Branch deleted after integration is observed¶
WHEN the owning lane fetches and observes its branch merged into the integration branch
THEN the lane deletes its local working branch
Requirement: Review and integration flow¶
The integrator SHALL fetch lane refs through a named per-lane remote,
created at lane registration and removed — with its remote-tracking refs
pruned — at lane retirement. Review packets SHALL be hash-stable: they
identify the repository, the base branch and commit, the reviewed ref, and
the complete commit list by hash; nbspec-managed changes additionally bind
verdicts to the rendered-content aggregate hash. Integration merges SHALL
be performed --no-ff by the integrator into the integration branch.
When the integration base advances after final approval, the integrator SHALL merge the approved tip into the current base when it merges cleanly; the approved stack is NOT rebased merely because the base advanced. A textually clean merge is not sufficient by itself: the combined result SHALL pass the project’s validation suite against the advanced base before integration completes or publishes. Failed combined validation BLOCKS integration; corrective commits carry new hashes and follow normal review. If conflict resolution or policy requires rewriting the approved stack, the approval becomes STALE and the rewritten range requires renewed review through an updated packet. Continuous-integration gates run where they run today — on hosted remotes after human publication — and intra-fleet gating remains commit hooks in each clone plus validation evidence carried in review packets.
Scenario: Fetch-based review¶
WHEN the integrator fetches a lane’s refs
THEN review proceeds against immutable commit hashes
AND THEN the lane pushed nothing to make that possible
Scenario: Base advance with clean merge preserves approved hashes¶
WHEN the integration base advances after final approval and the approved tip merges cleanly
THEN the integrator merges the approved tip as-is and every approved commit hash remains exactly as reviewed
AND THEN the combined result passes validation against the advanced base before integration completes
Scenario: Combined-validation failure blocks integration¶
WHEN the approved tip merges cleanly but the combined result fails validation against the advanced base
THEN integration is blocked before completion or publication
AND THEN corrective commits carry new hashes and follow normal review
Scenario: Required rewrite stales approval¶
WHEN merging the approved tip requires rewriting or rebasing the approved stack
THEN the prior approval is stale
AND THEN the rewritten range is re-reviewed via an updated packet before integration
Scenario: Named remote lifecycle¶
WHEN a lane is registered and later retired
THEN the integrator’s named remote for that lane is created at registration and removed at retirement
AND THEN its remote-tracking refs are pruned at removal
Requirement: Single-writer concurrency¶
Every repository in the topology SHALL have exactly one writing owner: the seat that owns the clone, or the integrator for the primary. Until the capability-enforcement layer exists, single-writer is a POLICY rule — same-OS-user seats retain filesystem capability to write peer paths — and the capability invariant to enforce when that layer lands is: each seat holds write permission only on its own clone, and read permission on its parent and peers. Peer-to-peer lane reads (fetch for cross-review) are PERMITTED, read-only, under the same review-without-execution rules as integrator review. Cross-seat reads are safe against concurrent writes because git ref updates are atomic: a fetch observes pre-update or post-update state, never partial state.
Scenario: Fetch during a lane commit¶
WHEN the integrator fetches while the lane is mid-commit
THEN the fetch returns a consistent snapshot of the lane’s refs
AND THEN neither repository is corrupted or left in partial state
Scenario: Crossed review requests¶
WHEN two review requests race in the message queue
THEN each is evaluated against the explicit commit hashes it names
AND THEN message ordering cannot change what was reviewed
Scenario: Peer lane read for cross-review¶
WHEN lane A fetches lane B’s refs for a peer review
THEN the fetch is read-only and lane B is never written
AND THEN review-without-execution rules apply to the fetched refs
Requirement: Lane lifecycle¶
Lane clones SHALL be created at seat provisioning by bootstrap tooling: clone per the object-storage requirement (quiescent primary or snapshot transport), disable the push URL per the write-authority requirement, install hooks and project-local tool configuration from the project template rather than inheriting any shared state, and create the lane’s local working branches at verified tips (a fresh clone carries source branches primarily as remote-tracking refs). The canonical lane identifier SHALL be the agentmux bundle lane name, normalized to lowercase at provisioning; provisioning SHALL reject names that collide after normalization. The agentmux bundle SHALL record the session-to-clone mapping (lane name and clone path).
Template hook updates do not propagate to existing clones automatically; refresh mechanics are DEFERRED to provisioning tooling, but doctor checks SHALL report installed-versus-template hook drift so staleness is visible, and nothing outside the lane mutates a lane’s hooks mid-session.
Retirement SHALL verify both that no unmerged work exists — the
integrator fetches the lane with --prune and confirms every lane branch
is merged or explicitly abandoned — AND that the lane working tree holds
no undispositioned dirty or untracked content, before the clone directory
is deleted.
Doctor checks SHALL verify, per clone: the origin URL matches the
recorded lineage (from the bundle mapping; validation against a
repository policy file is DEFERRED and doctor SHALL NOT require that file
until its owning proposal lands), the push URL is disabled, the
integration base resolves, and hook drift per above. Disk-use checks are
DEFERRED until a threshold policy exists.
Scenario: Provisioning yields a compliant clone¶
WHEN bootstrap tooling provisions a lane
THEN the resulting clone has a parent-pointing
origin, a disabled push URL, template-installed hooks and tool configuration, a lowercased lane identifier, and local working branches at verified tips
Scenario: Retirement refuses while work is unmerged¶
WHEN a seat is retired while its clone holds an unmerged branch
THEN cleanup refuses until the branch is merged or explicitly abandoned by the integrator
Scenario: Retirement refuses undispositioned content¶
WHEN a seat is retired while its working tree holds dirty or untracked content
THEN cleanup refuses until that content is committed, migrated, or explicitly discarded
Requirement: Review without execution¶
Fetching refs SHALL execute no fetched code: git transports objects and
refs only — hooks, local configuration, and their execution context are
never transported. The integrator SHALL review fetched refs from the
object store (git show, git diff, rendered artifacts) and SHALL NOT
check out a fetched review ref into a working tree whose hook path
resolves inside that tree, since checkout would then swap hook content
under control of the reviewed branch. Building or testing a fetched ref
is code execution and SHALL occur only in the authoring lane or in a
sandbox, never implicitly as part of review.
Scenario: Fetch is inert¶
WHEN the integrator fetches a lane ref that modifies hook sources
THEN nothing executes and the integrator’s active hooks are unchanged
Scenario: File-level inspection avoids checkout¶
WHEN review requires reading files from the fetched ref
THEN inspection uses object-store reads rather than checking the ref out in the integration clone
Requirement: Portability¶
The topology SHALL rely only on git behaviors portable across Linux,
macOS, and Windows: local-path clones (the hardlink optimization degrades
to plain copy where unsupported), no symlinks, no socket dependencies in
the git layer, and lane names lowercased at provisioning (per the
lifecycle requirement) to avoid case-only collisions on case-insensitive
filesystems. The path roots (~/src, ~/src/CLONES) SHALL be
convention defaults resolved against the platform home directory, not
hardcoded absolute paths.
Scenario: Filesystem without hardlink support¶
WHEN a lane clone is provisioned on a filesystem that does not support hardlinks
THEN the clone falls back to copying objects
AND THEN every other property of this specification holds unchanged
Requirement: Migration and rollback¶
Migration SHALL proceed seat by seat with no loss of work, in this order:
Inventory the worktree: tracked modifications, untracked and ignored files, detached HEAD, and in-progress operations. Migration SHALL NOT proceed while a rebase, merge, bisect, or similar operation is in progress.
Commit tracked work (WIP commits permitted). Stage intentional untracked work into WIP commits explicitly; remaining untracked and ignored content SHALL be explicitly dispositioned — carried over, relocated to scratch, or intentionally discarded — never silently dropped.
If the worktree is at a detached HEAD, create a named recovery branch at that commit before proceeding.
Provision the lane clone. A clone of the primary carries source branches primarily as remote-tracking refs: migration SHALL create the lane’s local working branches at the verified source tips.
Compare ALL migrated refs by hash between worktree and clone.
Retain both the worktree and the clone until acceptance completes; only then remove the worktree and repoint the seat’s working directory and bundle mapping at the clone.
Rollback SHALL be symmetric: worktrees can be recreated from the primary at any time, and a lane clone SHALL NOT be deleted until its branches are merged or migrated. Review context (notebooks, verdicts, review packets) is topology-independent and unaffected in both directions.
Scenario: Migration preserves work by hash¶
WHEN a seat migrates from worktree to lane clone
THEN every migrated ref in the clone matches the worktree’s tip hash, with local working branches created from remote-tracking refs
AND THEN the worktree is removed only after acceptance completes
Scenario: Dirty, detached, and untracked state migrates explicitly¶
WHEN a migrating worktree holds uncommitted changes, untracked files, and a detached HEAD
THEN migration creates a named recovery branch for the detached commit, commits or explicitly dispositions all content, and refuses while any operation is in progress
Scenario: Rollback to worktrees¶
WHEN migration must be reversed for a seat
THEN a worktree is recreated from the primary and the lane clone is retained until its unmerged branches are recovered