Source Branch Policy for Isolated Mounts (Design Proposal)
Design dossier backing Source Branch Policy for Isolated Mounts. Captures the problem framing, product positioning, TUI mockups, data model, and open design questions so the roadmap item itself can stay a lean task list.
Problem
worktree and clone mount isolation let multiple agents work against the same project without sharing one writable checkout, but the branch each isolated checkout starts from is still implicit. Today worktree materialization uses the host repository's current HEAD (a plain git rev-parse HEAD against the mount source), and clone follows the same host-checkout state. That is convenient when the operator is already on the exact base they want, but it breaks the common workflow where the host checkout sits on one branch while a new agent should start from main, a release branch, a specific feature branch, or an operator-selected branch at launch time.
This is especially visible when the operator keeps the host repo on a long-running work branch. Starting a new jackin❯ agent from that branch by accident makes the isolated checkout inherit the wrong context, while switching the host checkout first is exactly the kind of host-side workflow mutation jackin❯ should avoid requiring.
Why It Matters
- Parallel agents need intentional starting points. The operator may want three agents to branch from
main, one fromrelease/2026-06, and one from the host's current exploratory branch without touching the host checkout between launches. - Isolation should remove host choreography. A feature whose promise is "each agent gets its own checkout" should not make the operator manually
git switch mainon the host before launching a clean agent. - The current TUI control is too shallow. Cycling
shared → worktree → cloneon the mount row works while isolation is a single enum, but source branch policy adds nested settings per isolation type. The editor needs a mount-isolation settings dialog rather than one inline toggle. - This is easy to explain as a product advantage. "Launch every agent from the right branch, without moving your host checkout" is a crisp operator-facing promise and a natural upgrade to the per-mount isolation story.
Product Positioning
This feature should be sold as branch-safe parallelism: jackin❯ already lets every agent have its own working copy; source branch policy makes the starting point explicit, repeatable, and visible. The operator should understand the value without reading internals:
Start agents from
main, your current branch, a release branch, or a branch you pick at launch — while your host checkout stays exactly where it is.
The launch summary and editor should use that language. Avoid making the feature sound like a low-level Git ref option. The visible story is: each isolated mount has a source branch policy, and jackin❯ uses that policy to choose the base for the agent's private checkout.
Desired Policy Vocabulary
The policy should live on each isolated mount and apply only when isolation is worktree or clone:
| Policy | Meaning | Recommended label |
|---|---|---|
Host HEAD | Use the exact commit currently checked out in the host repository. This preserves today's behavior and works for detached HEAD. | Host HEAD |
| Host branch | Resolve the host repository's current branch name and start from that branch tip. Fails with a clear error on detached HEAD. | Current branch |
| Default branch | Resolve the repository default branch, preferably from origin/HEAD, then fall back to a configured default or clear error. | Default branch |
| Named branch | Use a branch or remote-tracking ref named in the workspace mount settings. | Named branch |
| Ask every launch | Prompt during launch and persist nothing except the policy itself. The selected branch applies to that launch only. | Ask on launch |
Recommended default for existing configs: Host HEAD. It preserves current behavior exactly, supports detached HEAD, and avoids surprising operators after migration. New UI affordances can steer operators toward Default branch or Ask on launch for fresh isolated mounts without changing existing workspaces.
TUI Direction
The workspace editor should replace the current one-key isolation cycling model with a focused Mount settings dialog. The mount row can still show compact source/destination/mode badges, but editing a mount should open a modal where the operator can change the host source folder, the container destination folder, read-only state, isolation type, and the settings that belong to that isolation type. This should follow the auth source-folder pattern already used in the workspace/settings auth dialogs: one modal owns a staged form, rows appear only when the selected mode supports them, browsing a folder or picking a source opens a sub-modal, returning from that sub-modal updates the staged form, and the outer workspace save flow persists all changes together.
The important design shift is that isolation is not a row toggle anymore. It becomes one field inside the mount editor. When the operator changes the isolation type to worktree or clone, the same dialog immediately reveals the source branch controls; when the operator changes it back to shared, branch controls disappear and the preview explains that the host path is mounted directly. This mirrors the auth dialog where Source folder exists only for sync-capable modes and is hidden for modes that cannot use it.
Mounts tab row
The mounts table should keep one row per mount and make the source policy visible only when it matters. Enter should edit the selected mount, not just add/remove surrounding rows:
Mounts
▸ ~/Projects/api → /workspace/api rw worktree source: default branch
~/.cache/jackin/target → /workspace/api/target rw shared source: host path
~/Projects/docs → /workspace/docs rw clone source: ask on launchFooter hint when a mount row is focused:
Enter edit mount · A add mount · D remove · Esc backI can remain as an optional shortcut, but it should open the same mount settings dialog focused on the Isolation row instead of cycling through every mode. Fast cycling made sense when the only decision was shared | worktree | clone; once each mode has settings, cycling hides important state and makes accidental changes easy.
Mount settings dialog
The dialog should use the existing console modal vocabulary: centered panel, one focused control at a time, footer-only hints, staged edits, and a visible summary of what will happen. Model it as a form, like the auth source-folder dialog, rather than as separate one-off prompts. Source and destination should be normal editable rows; Source folder… opens the existing file-browser/source-folder picker shape, Container destination opens a text input seeded from the current dst, Readonly toggles in place, and Isolation cycles or opens an inline choice inside the form. The source branch group is mode-gated by the staged isolation value.
╭─ jackin❯ · mount settings ───────────────────────────────────────╮
│ Mount │
│ ▸ Source folder ~/Projects/api │
│ Container path /workspace/api │
│ Access read/write │
│ │
│ Isolation │
│ shared Use the host path directly │
│ ▸ worktree Private git worktree; host refs stay visible │
│ clone Private local clone; refs isolated until push │
│ │
│ Source branch │
│ ▸ Default branch Start each agent from origin/HEAD │
│ Current branch Follow the branch checked out on the host │
│ Host HEAD Preserve today's exact-commit behavior │
│ Named branch Choose a specific branch │
│ Ask on launch Prompt before materializing this mount │
│ │
│ Preview │
│ New agents mount /workspace/api from a private worktree based on │
│ the repository default branch. Your host checkout is unchanged. │
╰───────────────────────────────────────────────────────────────────╯
↑/↓ row · Enter edit row · Space toggle · Tab buttons · Esc cancelWhen shared is selected, the Source branch group should disappear or render disabled with not used for shared mounts. When clone is selected, the same policy labels apply, but the preview should say private clone rather than private worktree. If the staged destination changes, the preview should update immediately so the operator sees the real container path before saving.
Source folder and destination editing
The source-folder row should reuse the same interaction shape as auth source folders: pressing Enter opens a folder browser, cancel restores the staged form unchanged, and commit returns to the form with the selected path displayed as the explicit value. For existing mounts, the form starts from the pending workspace mount; for new mounts, the form can start with an empty source and a suggested destination derived from the source basename once a source is chosen.
The destination row should be editable in the same modal instead of forcing remove-and-add. Pressing Enter opens a text input with the current destination. Validation should run before the dialog can apply: destination must be absolute, cannot be empty, cannot duplicate another mount destination unless this edit is replacing that mount, and must still satisfy the existing nested-isolation/collapse rules when combined with the rest of the pending mount list.
╭─ jackin❯ · edit container path ──────────────────────────────────╮
│ Mount source ~/Projects/api │
│ │
│ Container path │
│ /workspace/api │
│ │
│ This is where the agent sees the mounted folder inside the role │
│ container. Changing it may also change the workspace workdir. │
╰───────────────────────────────────────────────────────────────────╯
Enter apply · Esc cancelThe save preview should show source/destination/isolation/source-branch changes as one mount edit, not as an unrelated remove plus add, unless the implementation truly has to model it as remove/add internally. The operator is editing one mount.
Named branch picker
Named branch should be a second-step picker instead of a free-text-only field. The first implementation can allow manual entry, but the target UX should list local branches and remote-tracking branches from the mount's host repo and include a search filter.
╭─ jackin❯ · choose source branch ──────────────────────────────────╮
│ Repo ~/Projects/api │
│ │
│ Filter: release │
│ ▸ origin/release/2026-06 remote │
│ release/2026-06 local │
│ origin/release/2026-05 remote │
│ │
│ Manual ref: │
│ release/2026-06 │
╰───────────────────────────────────────────────────────────────────╯
↑/↓ choose · Enter use branch · Ctrl-E edit manual ref · Esc backThe picker should make remote/local identity visible because feature/foo and origin/feature/foo are not the same starting point. It should not fetch implicitly unless a future implementation adds an explicit opt-in refresh action; reading local refs is safe, while hidden network fetches would be a surprising host-side operation.
Ask-on-launch dialog
When a workspace mount uses Ask on launch, the launch cockpit should stop at the workspace/materialization stage and show a forced-choice dialog before any isolated checkout is created.
╭─ jackin❯ · source branch required ────────────────────────────────╮
│ Workspace api │
│ Mount /workspace/api │
│ Isolation worktree │
│ │
│ Choose the branch this agent should start from. Your host checkout │
│ stays unchanged. │
│ │
│ ▸ Default branch origin/HEAD → main │
│ Current branch feature/search-api │
│ Host HEAD 4f7c2b1 │
│ Named branch… │
╰───────────────────────────────────────────────────────────────────╯
↑/↓ choose · Enter launch from selection · Esc cancel launchIf multiple mounts ask, the launch surface should show one dialog per mount in deterministic mount order. A later enhancement can group same-repo mounts, but V1 should prefer clarity over clever batching.
Data Model
This is a workspace-file schema change because the policy is part of MountConfig. A compact shape keeps the default simple while leaving room for mode-specific fields:
[[mounts]]
src = "~/Projects/api"
dst = "/workspace/api"
isolation = "worktree"
source_branch_policy = "default"
[[mounts]]
src = "~/Projects/docs"
dst = "/workspace/docs"
isolation = "clone"
source_branch_policy = "named"
source_branch = "release/2026-06"Recommended persisted values:
| Value | Meaning |
|---|---|
host_head | Exact commit from host HEAD; default and migration target. |
current_branch | Host current branch name. |
default | Repository default branch from local metadata. |
named | Branch/ref in source_branch. |
ask | Prompt at launch. |
Validation should reject source_branch_policy on shared mounts unless it is absent or defaulted to host_head, reject source_branch unless the policy is named, and reject named without a non-empty source_branch. The editor should prevent invalid combinations before save; the parser still needs hard errors for hand-edited files.
Because this changes WorkspaceConfig, the implementing PR must ship the full versioned-schema set required by the pre-release schema rule: bump CURRENT_WORKSPACE_VERSION, add a WORKSPACE_MIGRATIONS step in crates/jackin-config/src/migrations.rs, add a new workspace migration fixture from the predecessor version, re-bake every existing workspace fixture's after.toml, and add a Schema Versions timeline entry under /reference/runtime/schema-versions/. Existing configs should migrate by stamping no new field and relying on the serde default, or by explicitly writing host_head on the next save if the current writer emits defaults.
Runtime Semantics
Materialization should resolve the source branch policy before creating the worktree or clone:
- Validate the mount source is a git repository root and passes the existing dirty/rebase/merge checks.
- Resolve the policy to a concrete ref or commit without changing the host checkout.
- For
worktree, create the scratch worktree from that ref/commit, then continue to use the scratch branch for agent work. - For
clone, create the local clone, then check out the resolved ref/branch inside the clone without changing the host repository. - Persist the resolved base commit in the isolation record so cleanup/finalization can continue comparing agent work against the actual starting point.
The policy resolver must never run git switch, git checkout, git pull, or git fetch in the host repository implicitly. Reading refs, branch names, origin/HEAD, and commit IDs is fine. Network refresh belongs behind an explicit future action if operators ask for it.
CLI Surface
The CLI should support both workspace creation and editing:
jackin workspace create api \
--workdir /workspace/api \
--mount ~/Projects/api:/workspace/api \
--mount-isolation /workspace/api=worktree \
--mount-source-branch /workspace/api=default
jackin workspace edit api \
--mount-source-branch /workspace/api=named:release/2026-06Candidate grammar:
| Argument | Meaning |
|---|---|
<dst>=host-head | Exact host HEAD. |
<dst>=current-branch | Host current branch. |
<dst>=default | Default branch. |
<dst>=ask | Prompt on launch. |
<dst>=named:<branch> | Explicit branch/ref. |
The CLI should reject source-branch overrides for unknown destinations the same way --mount-isolation does. If the destination is currently shared, the edit can either reject immediately or accept the setting only when the same command also switches the mount to worktree/clone; the latter is friendlier and mirrors existing mount-edit planning.
Open Questions
- Should fresh isolated mounts created in the console default to
Default branchinstead ofHost HEADwhile migrated configs keepHost HEAD? Recommended: yes, if the editor makes the default visible in the preview before save. - Should
current_branchstore the branch name at save time or resolve it at launch time? Recommended: resolve at launch time, because the label means "whatever branch the host checkout currently has"; usenamedwhen the operator wants a pinned branch. - Should
defaultpreferorigin/HEADor ask whenorigin/HEADis missing? Recommended: preferorigin/HEAD, then localinit.defaultBranch/mainonly if the repo actually has that ref, otherwise fail with a dialog that offersHost HEAD,Current branch, andNamed branch. - Should the TUI still support one-key cycling for power users? Recommended: no for V1. Keep
Ias the shortcut into the dialog; add a later quick-cycle only if the dialog proves too slow. - Should mount editing reuse the generic auth/source picker component directly or introduce a mount-specific wrapper around the same row/sub-modal pattern? Recommended: introduce a mount-specific form state that reuses shared primitives (
text_input, file browser/source picker, footer hints) rather than coupling mount semantics to auth-specific types.
Deferred scope and rationale
- Implicit fetch/pull of branch lists. V1 reads local refs only; a later
Refresh branchesaction can be explicit. - Per-role defaults for source branch policy. Workspace mounts are the right first scope.
- Grouped multi-mount ask dialog. V1 prompts one mount at a time.
- Branch policy for
shared,tmpfs, orephemeralmodes. Only git-backed isolated checkouts need a source branch. - Automatic PR branch discovery. A future workflow feature can offer
PR branchas a launch-time option once PR/task orchestration has a stable source model.