Persistent project workspace for Koder Kli (Flow-backed, ephemeral-default)

shipped

1. Decision

Koder Kli sessions stay ephemeral by default (throwaway sandbox), and gain an opt-in persistent project workspace whose durability comes from a Koder Flow git repository — NOT from persisting the container or inventing a workspace store. The sandbox clones the repo at session start; the AI works and commits + pushes back to Flow; the diff/PR surface (#011) reads git in the session.

This is the /k-arch option (c) hybrid Flow-backed (Quality > Speed, Rule 13): covers both the scratch and the real-project use-cases, distributes complexity only where there's value, and reuses Flow as the repo SSOT (self-hosted-first, reuse-first) instead of a new store. The container is still ephemeral (cheap, secure); persistence = git.

2. Why not the alternatives

  • Ephemeral-only (status quo): caps kli as a scratch runner; the diff viewer

    is cosmetic; no project continuity. Low ceiling.

  • Persistent-always (durable volume per session): pays storageGCquota on

    every run, including throwaway smoke-tests, and reinvents a project store that Flow already is. Correct-but-redundant and mal-distributed.

3. Model

A session is created with an optional project:

POST /v1/sessions
{ "adapter_id": "claude",
  "project": { "repo": "Koder/koder", "branch": "main", "base": "main" } }   // optional
  • No project → today's ephemeral session (unchanged).
  • With project → klid resolves a per-tenant git credential (§5), the

    sandbox clones https://flow.koder.dev/{repo}.git (branch) into the session working dir BEFORE the adapter is attached, and the adapter runs *inside the checkout. The AI's edits are durable the moment they are *committed + pushed*

Durability boundary: the git repo on Flow, not the container. A session that ends with un-pushed work loses only the un-pushed delta — surfaced honestly in the UI ("N uncommitted changes"). A work branch (kli/<session> or user-named) keeps the AI's commits off main until reviewed.

4. Surfaces (slices)

Slice Ticket What
1 kli#017 Backend workspace mountSpec.Workspace; klid clones the Flow repo into the session before spawn; project on session-create + on the Session model.
2 kli#018 Commit / push — klid endpoints to git add/commit + push the work branch (per-tenant cred); surfaced in the UI.
3 kli#019 (= #011) Diff / changes viewergit status/git diff via session exec (or fswatch), per-file diff side panel. Now real (a repo exists).
4 kli#020 Project picker UI — choose repo+branch when launching an AI; show the active project + branch + dirty state in the workspace.
5 kli#021 Open PR — push branch + create a Flow pull request from the session.

Slices ship independently; 1 is the foundation. #011 is reframed as slice 3.

5. Security & tenancy

  • Per-tenant git credential. Extends the existing internal/credential.Resolver

    (already resolves per-tenant provider secrets, KLI-001 1f) with a git token role. For the owner/v1: a configured Flow PAT (gitignored, --provider-secrets-style). For multi-tenant prod: the user's Koder ID → a scoped Flow token (future, ties to Flow's token API). The credential never rides the shared image (SANDBOX-028 per-session injection only).

  • Tenant isolation unchanged (multi-tenancy/contract.kmd): a session can only

    clone repos the session's tenant/user may read on Flow — enforced by Flow's own authz on the clone, not re-implemented in kli.

  • Branch hygiene: AI commits land on a work branch, never directly on base;

    merge to base is an explicit PR (slice 5).

6. Non-goals

  • Persisting the container or a raw durable volume (rejected — §2).
  • A kli-native VCS or merge engine (Flow/git owns that).
  • Real-time multi-writer on one workspace (one session = one checkout; collaboration

    is via branches/PRs, the git model).

7. Status

Shipped 2026-06-24 — all slices (#017018011020021) DONE + proven on prod. The loop "AI works in a Flow repo → diff → commit/push → open PR" is live on kli.koder.dev. Slice 1 (kli#017) is the foundation and starts immediately.