Documentation
¶
Overview ¶
Package sessions models a coding agent's Claude Code conversation history as a git repository so it survives VM recreates and follows a project across sandboxes.
The model:
- One bare "history" repo per project at <historyRoot>/<projectID>.git. Its `main` branch is the canonical, accumulated history.
- Each sandbox checks out its own branch (vm/<sandbox>) into the per-sandbox state dir that the provider already mounts at the guest's ~/.claude. The mounted dir simply becomes a git working tree.
- On boot we fold the project's `main` into the sandbox branch, so a fresh sandbox for the same project comes up with prior transcripts and memory in its resume picker. On teardown we merge the sandbox branch back into `main`.
Only conversation transcripts (projects/**, which nests Claude Code's per-project memory/) are tracked — never secrets or host-seeded config. See the .gitignore written by Prepare.
This package is host-side and platform-neutral: it shells out to `git` (matching internal/worktree and internal/config) and is wired into the lifecycle by internal/cli. It is currently used by the vz provider only; firecracker (no virtio-fs live mount) needs a copy-out step before this applies, which is deliberately out of scope here.
Index ¶
- func BranchFor(sandboxName string) string
- func EnsureBare(historyRoot, projectID string) (string, error)
- func Prepare(bare, claudeDir, branch string) error
- func Preserve(bare, claudeDir, branch string) error
- func ProjectID(repoPaths []string) string
- func Publish(bare, branch string) error
- func Refresh(bare, claudeDir, branch string) error
- type BranchInfo
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EnsureBare ¶
EnsureBare creates the bare history repo for a project if it does not yet exist and returns its path. `git init --bare` is idempotent, so this is safe to call on every boot. The repo starts with no refs; `main` is created lazily by the first Publish (a project's very first sandbox has nothing to seed from).
func Prepare ¶
Prepare makes claudeDir a git working tree on the sandbox's branch and folds in the project's accumulated `main` so the sandbox boots with prior history. It is idempotent and runs on every `up`:
- `git init` claudeDir if needed (safe on a dir that already holds a previous sandbox's state — `git init` never deletes files).
- Write the control files (.gitignore/.gitattributes) and point `origin` at the bare repo.
- Check out the sandbox branch, commit whatever transcripts already sit in the tree (the previous session's tail), then merge origin/main in.
The merge — never a checkout — is what brings prior history without ever clobbering files already on disk: new transcript files are added, and the union driver resolves the rare overlapping MEMORY.md.
func Preserve ¶
Preserve commits the live tree and pushes the sandbox's branch to the bare repo. It is lossless and deliberately NEVER touches `main`: a detached or destroyed VM's sessions are kept safe on `vm/<name>`, ready to be published later with Publish. This is the teardown step under the explicit-merge model — destroying a throwaway VM no longer pollutes the canonical history.
It also doubles as in-place migration for a pre-feature sandbox: the existing on-disk transcripts become the branch's first commit. A no-op when the sandbox produced no commits.
func ProjectID ¶
ProjectID returns a stable, filesystem-safe identifier for the set of repos a sandbox works on. Sandboxes that share the same repo set share one history repo (and therefore one `main`); per design §6 this keys history to the workspace, not to any single repo.
The id is "<label>-<hash>": a human-readable label (the sole repo's basename, or "ws" for a multi-repo workspace) for debuggability, plus a hash of the absolute, sorted, de-duplicated repo paths for uniqueness.
FROZEN: ids name history repos on disk (~/.clawk/history/<id>.git) and are recorded on sandbox records. Changing the label rule, the hash algorithm, or the normalization orphans every existing transcript — any change needs a migration that re-keys the history dir.
func Publish ¶
Publish folds a sandbox's preserved branch into the project's canonical `main` — the explicit "this VM's work belongs in the project history" step behind `clawk sessions merge`. It operates on the bare repo alone, so it works even after the VM is destroyed (the branch was pushed during its life). Idempotent: re-publishing an already-merged branch is a no-op.
func Refresh ¶
Refresh is the per-attach sync for a running sandbox. It pulls the curated `main` into the working tree (so the resume list reflects what's been published) and preserves this VM's branch — but, per the explicit-merge model, it does NOT publish to `main`. This is what keeps a long-lived `clawk here` sandbox (which may stay up for weeks and so never re-runs Prepare) current with sessions published elsewhere.
Safe on a live sandbox: a merge from main only ADDS other VMs' transcript files (sessions are single-owner per id) and never rewrites the session being appended; MEMORY.md union-merges.
Types ¶
type BranchInfo ¶
type BranchInfo struct {
Branch string // full ref short name, e.g. "vm/feature-x"
Sandbox string // the sandbox name (branch without the vm/ prefix)
LastSeen string // committer date of the branch tip
Published bool // already merged into main
}
BranchInfo describes one preserved session branch.
func ListBranches ¶
func ListBranches(bare string) ([]BranchInfo, error)
ListBranches returns the preserved per-sandbox branches in the bare repo (the vm/<name> refs), each with its last-activity time and whether it has been published into main. Powers `clawk sessions list`.