Workspace projects: design risks before implementation #161
Replies: 6 comments
-
Proposed resolution for the root-file model: version the parent (root-as-repo)Picking up risk #1 (root symlinks are not a write barrier). I think the cleanest answer is to stop treating root files as a special case and make the parent folder a git repo (gitignoring the child-repo paths). A workspace session then becomes uniformly worktrees: a parent worktree holding the root files plus one child worktree per registered repo, all on The reframe that drives thisThe current doc frames root files as "shared read context." For the files this feature actually names —
So this isn't a "how do we expose read-only context" problem. It's a "what happens when the normal toolchain writes a root path" problem. The symlink model turns the common, benign event (a lockfile write) into silent cross-session corruption of canonical, then patches it with detection the doc itself admits isn't a real barrier. Why root-as-repo beats the alternatives
Nesting behavior — verified, not theoreticalThe obvious worry is "a child git repo living inside a parent git repo's worktree." I ran this end to end with live git. It's clean on one condition: the parent must gitignore each child path before its first commit.
The one hard requirement / trap: if a child dir is Implementation implicationsRegistration (
Spawn: create the parent worktree at the session root, then each child worktree inside it. The parent worktree materializes only tracked root files; ignored child dirs are absent, leaving the paths free for the child worktree commands. One Edits / cleanup: root changes are ordinary commits on Schema impact — net simpler than the current plan:
This also dissolves most of risk #2's matrix for the root: the root is no longer an un-versioned blob that cleanup might silently delete — it's a worktree with the same dirty/refuse/prune semantics as every child. Accepted tradeoffs
NetVersioning the parent deletes the symlink enforcement layer, the root-write lock, and the |
Beta Was this translation helpful? Give feedback.
-
Follow-up: how root changes actually travel (the N+1 fan-out, no parent remote)The root-as-repo proposal above answers "where do root edits live" (a parent worktree on A workspace session produces N+1 independent deliverablesA session that touches
So right after the cli PR merges, the Detection is a local git check, not a remote webhookThis is the key difference from child repos. The SCM observer learns about child changes by watching the remote (PR/CI/merge events). The parent has no remote, so there's nothing to watch. Instead AO asks git directly — and it can, because all worktrees of the parent share one object store, so the session branch is already present in the canonical parent with no push: git -C <canonical>/<parent> rev-list --count main..ao/<session-id> # >0 ⇒ root changed
git -C <canonical>/<parent> diff main..ao/<session-id> # the root diff to reviewCheap, offline, always available. AO runs it (at session completion, or while reconciling the session's child PRs); if non-empty, it raises a "session has unlanded root changes" signal — a new, local notification to the orchestrator that sits alongside the existing PR-based signals for children. Landing is a gated local merge into the parent's
|
Beta Was this translation helpful? Give feedback.
-
Resolved: branch / base / collision / registration policy (risk #3)Decisions for the branch and registration questions from this thread, so the adapter is unblocked. Base ref per childEach child worktree branches off its local default branch tip — no fetch at spawn.
Branch name (the one-shared-name invariant)Default name is Collision rule: if Pseudocode: compute the name once at spawn by probing existence across the full repo set, bump the suffix until it's free everywhere, then create that one name in each repo. Still store the (identical) name per child in Registration validity — strict, with an auto-fix offerEach child repo must:
If a child fails these (e.g. freshly Parent (root-as-repo) specifics
|
Beta Was this translation helpful? Give feedback.
-
Resolved: composite lifecycle matrix (risk #2)This is the second artifact this thread asked for. The key enabler is a primitive that turns the worst row — "dirty worktree refuses removal forever" — into a solved case. Primitive: preserve uncommitted state to a ref, per repo
# on destroy, per repo, if dirty:
git -C <wt> stash push -u -q -m "ao-preserve <session-id>"
git -C <wt> update-ref refs/ao/preserved/<session-id> "$(git -C <wt> rev-parse refs/stash)"
git -C <wt> stash drop -q
# worktree is now clean -> remove --force is safe
# on restore, per repo, if a preserved ref exists:
git -C <repo> worktree add <wt> ao/<session-id>
git -C <wt> stash apply --index refs/ao/preserved/<session-id> # restores staged/unstaged/untrackedEach repo has its own ref store, so the ref lives in that repo ( The matrix
Snapshot lifetimeEvery session is restorable — there is no "permanently killed" session. "Kill" = suspend. Preserved refs therefore persist for the session's whole lifetime and are removed only on:
Net effect: cleanup always succeeds (no zombie worktrees, no disk litter, no "retry forever" state), and uncommitted work is never lost — it's either reapplied on restore or recoverable from the ref until the session is explicitly deleted. |
Beta Was this translation helpful? Give feedback.
-
Resolved: status aggregation + remote-aware root delivery (risk #4, and the tail of #1)Session status aggregation (risk #4)List view = worst-of-N. A workspace session's single glanceable status is the least-done state across all its child PRs, with unlanded root changes counted as a pending item. Rough ordering (worst wins): So a session shows "done" only when every child PR is merged and the root changes are landed (or there were none). One failing child, or an un-landed root diff, keeps the whole session visibly not-done — which is the safe bias. Detail view shows the breakdown: a per-child PR list (each repo's PR + CI/review state) plus a root row — the Cross-repo bulk operations (cancel-all / rebase-all / merge-all) are out of V1, explicitly deferred. V1 keeps existing per-PR actions plus the single root land/PR action. Remote-aware root delivery (tail of #1)Root-change detection is unchanged and remote-independent: a local
This makes the root symmetric with child repos when the infra allows it, and degrades to a local merge when it doesn't — without ever forcing the user to add a remote. The status model above already accounts for both: "root-unlanded" covers an un-merged local diff or an open root PR. Net for the threadWith this + the branch policy and lifecycle matrix comments above, all five risks raised here have a decided answer:
The remaining task is the doc PR to replace the symlink section of |
Beta Was this translation helpful? Give feedback.
-
|
The proposals in this thread have been consolidated — and audited/corrected (a stash-on-conflict hole in the lifecycle matrix, a two-dot vs three-dot diff bug in the root-review surface, land mechanics that previously could trample the user's checkout, plus detection of uncommitted root changes) — into a single final design: #164. Treat #164 as the implementable spec; the comments above are its derivation history. Note the original post's "settled" list still mentions read-only root symlinks — that direction is superseded by root-as-repo in #164, and |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Workspace project Option 1: design risks to resolve before implementation
Follow-up to #155.
The provisioning direction from #155 is now settled:
--targetsfor V1;This thread is for the problems that remain in that design before we start coding past the first schema/registration slice.
1. Root symlinks are not a write barrier
The current doc says normal workers see root files through read-only symlinks. That is useful for workspace shape, but it is not enforcement by itself.
Likely day-one failure modes:
pnpm install,npm install, oryarn installwrites a lockfile next to the root manifest and can mutate canonical root state through the symlinked path..cache/,dist/, stamp files, generated config, or lockfiles;sed -i,tee, shell redirection, Pythonopen(..., "w"), etc.) follow symlinks;scripts/ortools/are recursively write-through;Questions to decide:
2. Composite cleanup and restore need a state matrix
Single-repo cleanup already has an important invariant: do not force-delete dirty registered worktrees. With all child repos provisioned for every workspace session, cleanup now has N opportunities to partially fail.
Scenarios to enumerate:
git worktree removerefuses forever;cli/successfully, thenapi/refuses, leaving a partially cleaned composite root;Questions to decide:
3. Branch/base/collision policy across all child repos
V1 likely uses one shared branch name,
ao/<session-id>, in every child repo. That still leaves several policy choices.Questions to decide:
ao/<session-id>already exists in one child repo but not the others?4. PR fan-out and session status aggregation
One workspace session can produce one PR per child repo. PR ownership can still point to the session, but the product read model needs an aggregation rule.
Questions to decide:
Likely V1 answer: worst-of-N status in list views, per-child PR list in detail views, and no cross-repo bulk operations beyond existing per-PR actions.
5. Platform and registration edge cases
These are probably lower priority than the first four, but should be named before implementation:
clivsCLI);frontend/uiandmobile/uiif we ever support more than direct children;api -> /opt/work/api) and whether registration follows, refuses, or warns;.gitignore,.envrc,.npmrc,.tool-versions, etc. as read-through context with side effects;workspace_repos.repo_origin_urlbecoming stale);CHECKenum future-proofing forprojects.kind.Proposed next step
Before coding past project detection + registration, write two small design artifacts:
Those two are operationally more important than revisiting the provisioning choice itself. Option 1 remains the right provisioning model; this discussion is about making it safe enough to ship.
Beta Was this translation helpful? Give feedback.
All reactions