Skip to main content

git-strata

Use when working in a repo managed by git-strata (macOS), or when the user asks you to start, checkpoint, diff, restore, publish or remove a git-strata session. Gives each agent session its own isolated APFS-cloned workspace over a shared base snapshot; covers the CLI workflow, the JSON envelope, error codes, and the commands an agent must hand to a human.

Source facts

Repository
dickwu/git-strata
Last source activity
October 30, 2026 at 19:59
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
git-strata
description
Use when working in a repo managed by git-strata (macOS), or when the user asks you to start, checkpoint, diff, restore, publish or remove a git-strata session. Gives each agent session its own isolated APFS-cloned workspace over a shared base snapshot; covers the CLI workflow, the JSON envelope, error codes, and the commands an agent must hand to a human.
# git-strata git-strata gives every coding session its own workspace: an APFS clone of a shared, sealed base snapshot of each repo (plus its dependency set). One session's edits never change another session's files, but they are readable: `status --all`, `grep`/`files` with `--session S` or `--all`, and `diff --session S` / `--between` show other sessions' content. Edits are recorded as checkpoints, combined and committed by `publish`, and sent to the remote only by `git-strata push` or a publish that is configured to push. macOS only (APFS, FSEvents, launchd). Use it when the user (or the repo's setup) works in git-strata sessions, when several agents edit the same repos in parallel, or when you are asked to checkpoint / restore / publish. `git strata ...` and `git-strata ...` are the same binary. Full command and flag table: [reference.md](reference.md). ## Prerequisites - `git-strata` is on `PATH`. The config lives in `~/Library/Application Support/git-strata/config.toml`; the daemon socket is `~/Library/Application Support/git-strata/daemon.sock`, the pinned safety flags `~/Library/Application Support/git-strata/pins.json` (no env var or flag changes them; only `$HOME` does). Workspaces live under the data root (`[paths] root`, default `~/.strata`) as `<root>/sessions/<session-id>/<repo>/`. - The daemon reads `config.toml` only at start; after a config change, tell the human the daemon needs a restart. Do not restart it yourself. - **Agents never start, stop or restart the daemon** (`git-strata daemon start` / `stop` are human-only; never run them, or `launchctl`, yourself, not even with a temp `HOME`: the LaunchAgent domain is per user, not per HOME). Check it with `git-strata daemon status` (`data.running`). If it is down, keep working in the workspace and tell the human it is down so they can run `git-strata daemon start`. While it is down the hook answers `Busy` `daemon_down` and that checkpoint is not recorded: tell the human which edits were not checkpointed. Do not write a plist yourself. - Without the daemon, only these work (read-only, straight from the store): `status`, `grep`, `files`, `diff`, `deps list`, `daemon status`, and a repeated `checkpoint --tool-call ID` whose checkpoint already exists. Every other command returns `Busy` with `detail.reason = "daemon_down"`. ## Workflow ```sh # 1. New session (ticket is just a label). Name the repos you will touch with # --repo (repeatable): only those get a workspace, all tracked. This is the # fast form; use it. A repo that links a sibling by relative path (../x) # needs that sibling too: pass it, or add it later (step 1b). # (Without --repo, `--repos A,B` gives EVERY configured repo a workspace # and tracks only A,B; do not edit the untracked siblings.) git-strata new PROJ-123 --repo tool --repo core-lib # data.manifest.id -> session id, e.g. "s-k3q7xm2a" # data.manifest.repos.<repo>.path -> that repo's workspace directory # data.warnings -> [] when fine # data.manifest.agent -> the agent recorded for this session # The session records which AI agent created it. Claude Code, Codex and # Cursor are detected from their environment. Any other agent: run # `export STRATA_AGENT=<name>` (e.g. aider; 1-64 of [A-Za-z0-9._-], # starting with a letter or digit) before `new`, or pass --agent <name>. # A warning of kind AgentNotRecorded means the running daemon is older # than this CLI; the session is fine, only the agent is not recorded. # 1b. Need another repo later? Add it to the same session (tracked): git-strata add --repo front # or: --session S --repo front # data.added.<repo>.path; data.already lists repos it had already. # 2. Work inside the workspace. Commands without --session use the cwd. cd "<data.manifest.repos.tool.path>" # ... edit files normally ... # 3. Checkpoint (one of --tool-call / -m, never both) git-strata checkpoint -m "parse config before connect" git-strata checkpoint -m "only these" --path src/a.ts --path ../core-lib/x.php # 4. Inspect git-strata status # this session; --all for every session git-strata diff # this session vs its pinned base (= --base) git-strata diff --between s-aaaa1111 s-bbbb2222 --repo tool # two sessions git-strata grep 'TODO' # this session; --session S or --all git-strata files 'src/**/*.ts' # glob optional # 5. Undo to a checkpoint (all paths, or only the listed ones) git-strata restore 42 git-strata restore 42 src/a.ts # 6. Publish checkpointed content into a commit git-strata publish --from s-k3q7xm2a:src/a.ts --from s-k3q7xm2a:src/b.ts # 7. Push the publish chain (only if the human asked; may need a TTY) git-strata push tool # 8. Remove the session once its work is published git-strata rm --session s-k3q7xm2a ``` Notes: - `--session` takes a session id or a ticket. A ticket shared by several sessions gives `AmbiguousMatch`; use an id from `detail`. `--session` and `--all` together is `InvalidInput` (`both_selectors`). - `checkpoint` may record more than one repo of the session: every path is mapped to the session repo whose workspace contains it; paths outside the session are skipped. With no paths, every tracked repo of the session is scanned. `data.checkpoints` has one row per repo recorded. - `-m` records a new checkpoint only when something changed since the latest one; with no change it returns that latest checkpoint (its id and message, not yours). `--tool-call ID` is idempotent per (session, repo, ID): every new ID records a row (even with no change), and repeating it returns the same rows. - `publish` publishes the selected paths' current content: before sending the publish it records a fresh `BeforePublish` checkpoint of each `--from` batch, so edits made to a selected file up to that moment are included. There is no option to publish an older checkpoint; to keep an edit out, revert it (or `restore` the path) before publishing. Without `--from` it is interactive on a TTY and `InvalidInput` (`no_batch`) otherwise, so agents always pass `--from SESSION:PATH`. PATH is relative to the repo root with no repo prefix (`billing-api/app/x.php` is `--from "${S}:app/x.php"`). Write `"${S}:path"`, never `"$S:path"`: zsh reads `$S:t`, `$S:h`, ... as modifiers. Path mistakes come back before any TTY check: `InvalidInput` `from_repo_prefix` (rerun with `error.detail.expected`), `InvalidInput` `from_path` (no workspace has it), `AmbiguousMatch` `from_repo` (several have it and not exactly one of their checkpoints changed it; rerun with one of `detail.expected`). To pick the repo yourself use `--from "${S}:REPO:PATH"` (e.g. `"${S}:admin:src/a.ts"`); the middle part is REPO only when it is one of the session's repos. - `publish` replies only after checks, commit, push and the rebase of every session. `git push` waits for the remote's own hooks (bounded at 120 s, then `Unknown` `push_unknown`), the rebase is unbounded: a minute or two of silence after the remote branch moved is normal. Wait; do not interrupt or rerun. - Release builds never run in a workspace (no remotes, detached `HEAD`, push guard). Publish (and push), then in the main checkout `git pull --ff-only`, check `HEAD` equals `data.commits.<repo>`, and run the build there; the build script is the human's step unless they asked you to run it. See README "Release builds from a session". - Multi-repo `publish` commits repo by repo. When one repo hits `Conflict`, repos before it have already committed (and pushed, if push is on); the call does not roll them back. See "Publish conflict: fix and re-run" below. - `diff` and `diff --between` preview current workspace content, checkpointed or not. Identical files are not listed; binary / >64 MiB entries carry `code: "Binary"` / `"TooLarge"` instead of a preview and the call stays ok. - `grep` with no hits is `ok` with `"hits": []` (not an error). Skipped binary or oversized files are listed in `data.skipped`. - `deps propose [--session S] [--repo R]` proposes a new dependency set after you change install inputs; `deps list` shows open proposals. Add `--switch` to move your own session onto the proposed set right away (no prompt; other sessions stay pinned). Check `data.switched.kind`: `skipped` with `code: OpenFiles` means a process holds files in your deps dir; stop it (only if you started it) and run again. Approving for other sessions is human-only (below). `data.pending_proposals: [{id, repo}]` in any envelope means a proposal waits for the human. - If you change install inputs (package.json deps, bun.lock, patches, pubspec.yaml / pubspec.lock, ...) and publish without switching, publish still runs the checks with deps that match the staged inputs: it uses a sealed set (warning `DepsSubstituted`) or installs one first (slower; warning `DepsReinstalled`, and the history shows an extra row stopped with `Drift`). Plain drift does not refuse the publish; it can still stop with `Drift` when the inputs move again during that install (`deps_drift`) or when the deps dirs overlap files you publish (`deps_select_failed`, `tracked_overlap`: follow its `fix`/`hint`). Still run the warning's `fix` (`deps propose --switch`) so your workspace matches. `status` shows the same `drift.fix`. If that install fails (`detail.stage: "deps_install"`), hand it to the human with `detail.log_tail`; do not retry in a loop (a retry returns `previous_attempt`). ## Publish conflict: fix and re-run This is the expected loop for agents; the human is not needed unless the conflict is `assigned`. 1. `publish` returns `ok: false`, `error.code = "Conflict"`. - `error.detail.repo` is the repo that conflicted; nothing was committed there. - `error.detail.conflicts` lists its paths (with the hunks). - `data.commits` / `data.pushes` hold repos that already landed in this call (also in `error.detail.published_before`). They stay landed. The same holds for any publish error: a repo whose commit was written before its push timed out (`Unknown`) or was refused (`PushRefused`) is in `data.commits` and `data.pushes` too. 2. Fix the listed paths in that repo's workspace (merge in the upstream change; `git-strata diff` helps). 3. Optionally `git-strata checkpoint -m "resolve conflict" --path P ...` to keep a named point; the re-run snapshots the paths itself. 4. Re-run the exact same `publish --from ...` command. Repos that already landed are not committed again: they appear in `data.already_published` as `{repo, publish, commit, push}`, and only the rest is published (`data.commits`). The check is per `--from` path and by content: a path (or directory) whose content is exactly what landed is skipped; anything new or changed under it since (including new files in a directory you selected) is published as a new commit, and the unchanged paths of the same `--from` list do not cause `NoMatch`. 5. Repeat while another repo conflicts. ## Dev servers and `tauri dev`: per-session ports Every session workspace gets its own block of 4 ports (a lease), so two sessions can run `tauri dev` (or any dev server) at the same time. The main checkout has no lease and keeps the project's default ports. Never change ports by editing committed files (`tauri.conf.json`, `package.json`, `next.config.*`, `.env`); start the server through git-strata instead: ```bash cd <your workspace> # data.manifest.repos.<repo>.path git-strata session ports # read the lease: data.leases[0].ports / .env git-strata session exec -- bun run dev # any server that reads $PORT (Next.js does) git-strata session exec -- sh -c 'vite --port "$PORT" --strictPort' git-strata session exec --tauri -- bun run tauri dev ``` - `exec` runs the command in place of itself (its output, its exit code) in exactly one workspace (`--repo R` when the session has several) with: `PORT` = `STRATA_DEV_PORT` (web), `STRATA_CONNECTOR_PORT`, `STRATA_CONNECTOR_MCP_PORT`, `STRATA_AUX_PORT`, `STRATA_SESSION`, `STRATA_REPO`, `TAURI_CONNECTOR_PROJECT` (the Tauri app dir) and `STRATA_TAURI_DEV_CONFIG`. Child processes (e.g. Tauri's `beforeDevCommand`) inherit them. - `--tauri` adds `--config $STRATA_TAURI_DEV_CONFIG` to `tauri dev`, which points `build.devUrl` at `http://localhost:$PORT`. The frontend dev server must then listen on `$PORT`. It understands `tauri dev`, `cargo tauri dev`, `npx|bunx|pnpx tauri dev`, `pnpm exec tauri dev`, and `bun|pnpm|yarn|npm run <script>` when that script is literally `tauri dev ...` (or the bare `tauri` CLI, with `dev` right after the script name: `bun run tauri dev`, `npm run tauri -- dev`). Always write `run`: `bun <script>` without it is refused, since the package manager may run its own command of that name. Options that pick another package (`--prefix`, `--filter`, `-w`, `--workspace`, `--cwd`, ...) and, for npm, any option before its `--` are refused. Anything else is `InvalidInput` `tauri_launcher`; use the form in `detail.fix`: `git-strata session exec -- sh -c 'tauri dev --config "$STRATA_TAURI_DEV_CONFIG"'`. - `exec` does not set tauri-connector's variables. tauri-connector 0.17.7+ binds exactly `TAURI_CONNECTOR_PORT`, `TAURI_CONNECTOR_MCP_PORT` and `TAURI_CONNECTOR_BRIDGE_PORT`; map the lease onto them: `git-strata session exec -- sh -c 'TAURI_CONNECTOR_PORT=$STRATA_CONNECTOR_PORT TAURI_CONNECTOR_MCP_PORT=$STRATA_CONNECTOR_MCP_PORT TAURI_CONNECTOR_BRIDGE_PORT=$STRATA_AUX_PORT exec tauri dev --config "$STRATA_TAURI_DEV_CONFIG"'`. Precedence: a port in the app's builder code > the exact env variable > `TAURI_CONNECTOR_*_PORT_RANGE` > a builder range > the default. A busy exact port is not replaced: the connector does not start (the app keeps running); free the port or `--reassign`, then restart. The bound ports are in the `.connector.json` under your workspace's `target/` (copies from the main checkout are removed when the session is created). - `exec`'s variables reach only the command it starts and its children, not a later connector command or an MCP server that is already running. When you talk to the app from such a client, select it explicitly: `tauri-connector ... --project <tauri_app_root>` (`tauri_app_root`, else `path`, from `git-strata session ports`), or wrap the client: `git-strata session exec --no-check -- sh -c 'TAURI_CONNECTOR_PORT=$STRATA_CONNECTOR_PORT tauri-connector ...'` (without `--port` the CLI uses `TAURI_CONNECTOR_PORT`). Never connect to the connector's default ports (9555/9556): they may be another session's app. - Before starting, `exec` checks the lease's ports (`--no-check` skips it; `session ports --check` only reports): - `PortInUse` `already_running`: a process of this workspace already listens. Reuse it; stop it only if you started it. - `PortInUse` `foreign`: something else holds a port. Nothing moves on its own: run `detail.fix` (`git-strata session ports --reassign --session S --repo R`) for your own session, then `exec` again. Never kill a process you did not start. - `Busy` `port_unidentified` / `port_probe_error`: retry shortly. - A workspace without a lease yet (e.g. one created before this feature) gets one from the daemon on first `ports` / `exec`; an existing lease works without the daemon. `Busy` `daemon_down` from `ports` / `exec`: report it to the human; do not start the daemon yourself. `Busy` `ports_exhausted` (`ports` shows the workspace with `"ports": null`): no free block; tell the
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub