kvs-RFC-001 — Worktree-per-session isolation for high-concurrency monorepo work

ratified

Status: ratified 2026-06-20 (owner via /k-go arch — forced the Rule-13 long-term tiebreaker over the pragmatic "hold"). Worktree-per-session is the Stack's chosen isolation model. The build + flip are now authorized; tracked in KVS-251. The structural target and migration path below stand.

1. Problem

Multiple AI sessions act on the single shared physical working tree at ~/dev/koder in parallel. That sharing is the root of three distinct hazards:

Hazard Mechanism Status
H1 — shared .git/index git add/commit -o of one session contaminates another's index Solved at the client by KVS#188 (no-index, per-op temp GIT_INDEX_FILE)
H2 — shared working-tree files one session's reset --hardcheckoutstash/stale commit -o clobbers another's uncommitted edits (the reported "version bump reverted mid-flow") Open
H3 — ref lost-update two concurrent finalizes; the second update-ref silently overwrites the first → orphaned commit Closed by KVS#208 (CAS + fail-safe remerge)

The entire compensating regime — per-component serialization locks, lock-carve-out.kmd, the "commit early before VM round-trips" memory, the -- <paths> scoping in KVS#188 — exists only because sessions share one physical tree. It treats the symptom (survive the shared tree) instead of the cause (the tree is shared).

2. Proposal

Give each session its own git worktree (separate working directory + separate index, shared object store), materialized with sparse-checkout to the component(s) the session touches. Keep the KVS no-index model (#188) inside each worktree. Sessions converge only at the ref, where KVS#208's CAS+remerge already reconciles concurrent commits semantically.

~/dev/koder                      # primary worktree (object store owner)
~/dev/koder.wt/<session-id>/     # per-session worktree, sparse to its component
  └─ products/dev/kvs/...        # only the session's component is materialized

New surface (sketch):

  • kvs session start [<component>...] → allocate a worktree from a pool, sparse

    to the given components, print its path; the session cds there.

  • kvs session end → finalize/abandon, return the worktree to the pool (git

    worktree auto-prunes if unchanged).

  • kvs session list → cross-session view (replaces ad-hoc lock notices).

3. Why this is the long-term-correct shape (Architectural Fitness)

Scored against architecture-quality.kmd:

  • D2 (coupling): no shared mutable state between sessions — the working tree

    is no longer a global coupling point. Best possible.

  • D3 (evolvability): removes a class of complexity (the whole lock /

    carve-out / commit-early regime) instead of adding to it.

  • D6 (consistency): H1 + H2 become impossible by construction; the reported

    clobber cannot happen.

  • D9 (reversibility): git worktree is native git — fully reversible; a

    session can always fall back to the shared tree. No lock-in.

  • D12 (root-cause > symptom): this is the root-cause fix for H1+H2. Per

    the rule, a root fix outranks a palliative even at higher effort — effort, duration, gating are explicitly not tiebreakers; only known-vs-hypothetical need is, and the need here is known (lock frequency kdb 453 / ai 410 / koda 294 + reported clobbers), not hypothetical, so YAGNI does not down-rank it.

Alternatives rejected:

  • Protect the shared tree (leases + continuous snapshot): palliative against

    H2 — adds a lease subsystem + snapshot daemon and keeps the lock regime. Continuous snapshot (KVS#210) is still worth doing as a cheap safety net, but as a net under this RFC, not as the H2 fix.

  • Status quo (git commit -o + locks, fix only H3): leaves the shared tree

    as a global mutable coupling point (D2 anti-pattern). Recurring debt.

4. Relationship to KVS#188 / #202 — layered, not competing

kvs wc (#188/#202) and kvs session (#211) are different layers and compose; they do not compete for "the default":

  • kvs wc (no-index) = commit-mechanism layer — no staging, temp index.

    Low-friction (same physical dir). Valid inside a worktree too (the no-index ergonomics don't disappear under isolation).

  • kvs session (worktree) = isolation layer — own physical tree + index.

    Removes H1+H2 by construction; changes the workflow (cd in, integrate at end).

So the #202 flip is not wasted by the eventual worktree default. What the worktree default obsoletes is only the path-scoped-on-a-shared-tree mode of

188 (the -- <paths> H2 palliative) and the per-component lock regime — not the

no-index commit mechanism itself.

Coordination dependency (do NOT treat as independent flips). The #202 flip (per-component lock → advisory) and this RFC's step 4 (lock → moot) edit the same surfaces: CLAUDE.md "Regras de fluxo" + the lock policy. They must be sequenced by one owner: #202 first (advisory on the shared tree), then step 4 (worktree default supersedes the advisory state). The 2026-06-05 #202 gate evaluation should record this so the two are not flipped in conflict.

5. Migration (phased, owner-gated like #202)

  1. Floor (no flip): DONE (2026-06-02). KVS#208 (CAS+remerge) + KVS#209

    (orphan telemetry) landed (released v1.1.1) — correctness for both the shared-tree present and the worktree future.

  2. Mechanism: DONE (2026-06-02, KVS#211). kvs session {start,end,list} over

    git worktree + sparse-checkout, integrating via the #208 semantic CAS merge. Additive, opt-in; the shared tree keeps working. The flip below stays gated.

  3. Tooling awareness — KVS#212: DONE (2026-06-02). The flow tooling now

    resolves two roots (KODERWT current worktree for code/commit; KODERMAIN main worktree for shared coordination) via tasks/resolve-roots.md; the executable commitpushcleanlock includes are worktree-safe and locknotice behavior under a worktree is defined. Backward-compatible (both roots = ~/dev/koder in the main tree). The step-4 blocker is cleared.

  4. *lip — OWNER PRE-APPROVED 2026-06-02, BUNDLED with #202; held on the #202

    evidence gate.*Make per-session worktrees the default for concurrent AI sessions; the per-component lock becomes moot (superseding #202's advisory state); update CLAUDE.md "Regras de fluxo"; retire the lock-carve-out crutches.

    212 (step 3) is done, so the blocker is clear and the owner approved flipping

    this together with #202 as one atomic change. NOT executed yet: kvs wc stats on 2026-06-02 shows the #202 gate at ~16 (050 adoption commits) — per Regra 13 the flip is held on the GREEN evidence gate, not the approval or the calendar. The exact bundled-flip checklist lives in the #202 ticket ("OWNER PRE-APPROVAL" section). Owner can force-flip-now by saying so explicitly.

5. Migration (phased, owner-gated like #202)

  1. Floor (no flip): DONE (2026-06-02). KVS#208 (CAS+remerge) + KVS#209

    (orphan telemetry) landed (released v1.1.1) — correctness for both the shared-tree present and the worktree future.

  2. Mechanism: DONE (2026-06-02, KVS#211). kvs session {start,end,list} over

    git worktree + sparse-checkout, integrating via the #208 semantic CAS merge. Additive, opt-in; the shared tree keeps working. The flip below stays gated.

  3. Tooling awareness: teach /k-go, /k-save, /k-commit, /k-ship and

    the build/VM-sync flows that the working dir may be a per-session worktree (resolve via git rev-parse --show-toplevel, never hardcode ~/dev/koder).

  4. Flip (owner-gated): make per-session worktrees the default for concurrent

    AI sessions; relax per-component locks to advisory; update CLAUDE.md "Regras de fluxo". Retire the lock-carve-out crutches.

6. Costs / open questions

  • Disk: N worktrees duplicate checked-out files (object store shared). Sparse-

    checkout per component bounds this; the monorepo-wide checkout is never duplicated. Quantify before the flip.

  • Cross-session visibility: sessions lose the (rare) ability to see another's

    uncommitted in-flight edits. Almost always a feature, not a loss.

  • Tooling that hardcodes ~/dev/koder: must be audited (step 3). This is the

    bulk of the migration effort.

  • Build hosts already sparse-checkout (per existing session memories) — this

    RFC aligns the laptop workflow with that established grain.

7. Acceptance

  • kvs session start/end/list implemented over git worktree + sparse.
  • Two concurrent sessions on the same hot component, each in its own worktree,

    both commit clean; convergence handled by #208; zero H1/H2 incidents.

  • Migration step 3 audit complete (no hardcoded monorepo path in flow tooling).
  • Owner approves the flip → CLAUDE.md + lock policy updated.