worktree-flow
Isolated git worktree workflow for ai-specs change work. Create a dedicated worktree under .worktrees/ for any change that writes files, keep pure exploration outside a worktree, and clean up merged worktrees safely after topologies via the CLI-owned [project].repo_topology and topology-aware gate scope via recipes.worktree-flow.config.gate_scope.
- License 许可
- MIT
- 最近更新
- 2026-11-08
- Stars
- ★ 0
- Forks
- 2
01 前言 Front Matter
- name
- worktree-flow
- license
- MIT
- metadata
- { "scope": "runtime", "author": "ai-specs", "version": "1.2", "auto_invoke": [ "Starting a change that will write files or modify code", "Cleaning up worktrees after a branch is merged" ] }
- description
- Isolated git worktree workflow for ai-specs change work. Create a dedicated worktree under .worktrees/ for any change that writes files, keep pure exploration outside a worktree, and clean up merged worktrees safely after topologies via the CLI-owned [project].repo_topology and topology-aware gate scope via recipes.worktree-flow.config.gate_scope.
03 SKILL.md 正文
Worktree Flow
Run file-writing change work in an isolated git worktree under .worktrees/,
and remove worktrees safely once their branch is merged. Pure exploration that
writes nothing does not need a worktree.
When to create a worktree
| Situation | Worktree? |
|---|---|
| Exploration / reading / answering a question (no files written) | No |
| Implementing a change, writing artifacts, editing code | Yes |
| Planning or SDD artifact phases that write files | Yes |
| Any phase that produces committed output | Yes |
Topology matrix
| Resolved topology | Create | Clean |
|---|---|---|
standalone |
git worktree add <worktrees_dir>/<slug> -b <branch> <integration_branch> |
Single-repo worktree list + flush (unchanged) |
monorepo-apps |
Same as standalone (naming-only) | Same as standalone |
monorepo-submodules |
git -C <subrepo_path> worktree add <absolute-super>/<worktrees_dir>/<subrepo>-<slug> -b <branch> <integration_branch> |
Enumerate each initialized submodule (git -C / submodule foreach); never superproject worktree list alone |
Topology is CLI-owned: [project].repo_topology = "auto" (default) detects
initialized .gitmodules entries → monorepo-submodules, else standalone.
It never auto-selects monorepo-apps, it resolves even when this recipe is
disabled, and the deprecated recipes.worktree-flow.config.repo_topology alias
is read only for one migration window (the project field wins).
Gate scope and repository ownership
gate_scope is separate from gate_mode and repo_topology. Classification
requires effective repo_topology=monorepo-submodules; explicit
standalone/monorepo-apps never create a topology bypass:
| Value | Protected owner enforced | Runtime behavior |
|---|---|---|
auto |
Proven superrepo and subrepo | Default topology-derived protection; only canonical superrepo planning is excepted. |
superrepo |
Proven superrepo only | Subrepo writes are outside this selected enforcement scope. |
subrepo |
Proven initialized subrepo only | Superrepo writes are outside this explicit Melón scope. |
Before any write-capable delegation in a cross-repository layout, identify the owning repository and branch, not just the current directory:
git rev-parse --show-toplevel
git rev-parse --absolute-git-dir
git rev-parse --git-common-dir
git branch --show-current
Central planning artifacts belong to the proven superrepo canonical subtree
<superrepo>/openspec/changes/**; subrepo code remains subject to its own
protected branch and the separate plan-build-flow authorization. If sync or
doctor reports a stale materialized hook without the scope stamp, refresh it
explicitly with rm <hook-path> && ai-specs sync; customized hook bytes are
never silently overwritten. The runtime hook is a defense in depth, not the
sole guard for delegated or subprocess writes.
Request context
Resolve one ai-specs request context with util.resolve_request_context before
any create or artifact write: owner_root (the repository owning code/VCS for
the request) is distinct from planning_root (the canonical planning tree). A
subrepo request owns the submodule but plans under the proven superproject; a
superrepo request owns and plans under the superproject. Missing, ambiguous,
detached, or uninitialized topology fails safe — no owner inference beyond the
toplevel, no planning-root exception. /worktree-new is generated Markdown
executed by the agent; there is NO executable /worktree-new helper.
Conventions
- Shared layout: worktrees live under the superproject
<worktrees_dir>/<…>(default.worktrees/). Under submodules the directory is<subrepo>-<slug>. - Branch off the integration branch declared in recipe config
(
integration_branch, defaultmain). - Branch name and directory slug match so cleanup can map them 1:1.
- Add
<worktrees_dir>/to the superproject.gitignore. - Before dispatching a write-capable subagent or task, verify which git
repository, worktree, and branch yourself (
git rev-parse --show-toplevel,git branch --show-current,git worktree list). Undermonorepo-submodules, confirming the toplevel (which submodule / linked wt) is mandatory — not only branch + worktree list. Runtime pre-tool-use hooks may not fire for delegated/subprocess tool calls on opencode/pi/omp — do not treat the gate as the sole guard for delegated writes. - If a structured Edit/Write/MultiEdit call is blocked or errors for any
reason while on a protected branch, that is never grounds to retry the
write via bash/shell (heredoc,
python3 -c,cat >,tee,sed -i) — using bash to write bypasses the worktree gate entirely.gate_mode = always: create a dedicated worktree (/worktree-new) and write there instead.gate_mode = ask: ask the user which destination to use and wait — a dedicated worktree (recommended), a feature branch in the current checkout, or an explicit override to write on the protected branch. Do not pick a destination yourself, and do not re-run withWORKTREE_GATE_MODE=offas a self-authorization shortcut.gate_mode = off: the gate does not block; work where the user directs.
Asking the user (per harness)
When gate_mode = ask blocks a write, surface the three destinations with
the harness's native user-question mechanism and stop until the user
answers:
| Harness | Mechanism |
|---|---|
| pi | ask_user_question (structured options) |
| claude | AskUserQuestion (or a direct conversational question) |
| opencode / cursor / omp | the interactive user prompt / inline question |
The user's answer decides the destination; never decide for them, and never treat the block as permission to bypass the guard.
Creating a worktree
Prefer the /worktree-new command.
standalone / monorepo-apps
git worktree add <worktrees_dir>/<slug> -b <branch> <integration_branch>
monorepo-submodules (locked contract)
Destination MUST be absolute. Infer or require <subrepo>; validate path-then
unique name; reject uninitialized (git submodule update --init <path>).
<subrepo> selection and the owner/planning-root split are validated by
util.resolve_request_context / util.resolve_subrepo. A request whose
context is the superrepo MUST NOT infer a subrepo: an explicit, validated
<subrepo> is required, otherwise hard-error before any git worktree add.
super_abs="$(git -C "$super_root" rev-parse --show-toplevel)"
git -C "$super_abs/<subrepo_path>" worktree add \
"$super_abs/<worktrees_dir>/<subrepo>-<slug>" \
-b <branch> <integration_branch>
cwd inference uses git -C/rev-parse --show-toplevel and, for linked
worktrees named <path>-<slug>, the longest matching submodule path
prefix (see util.resolve_subrepo).
Post-merge cleanup
After a branch is merged into the integration branch, reclaim its worktree.
Leave the worktree first (hard rule)
Never run git worktree remove (or the cleanup script) while $PWD is inside
the worktree being removed — that yields fatal: Unable to read current working directory. Always:
cd <main-repo-root>
Script-first (preferred)
Prefer the materialized verified-Go cleanup launcher (or /worktree-clean) over
ad-hoc remove sequences. Run it from the main repository worktree; it owns
post-merge remote deletion and independently verifies remote absence:
bash ai-specs/recipes/worktree-flow/overrides/bin/worktree-cleanup.sh \
--dir <worktrees_dir> --base <integration_branch>
Optional --submodule / --subrepo scopes to one module; default = all
initialized. Run with --dry-run first to preview removals.
The launcher is conservative by design:
- Removes a worktree only when its branch is fully merged into the base — detecting both regular/fast-forward merges (ancestry) and squash/rebase merges (all of the branch's changes already present in base by patch-id).
- Preserves worktrees with uncommitted changes (reported as
dirty). - Preserves worktrees whose branch is not yet merged (
unmerged). - Never touches the main worktree or detached-HEAD worktrees.
- Refuses loudly before every worktree, local-branch, or remote-branch delete
when the branch is protected (
main,master,development,staging, or configured base/integration names). - Deletes the remote branch only after local cleanup and verifies absence with
git ls-remote --heads; failure is not reported as success. - Under submodules, enumerates per-module lists; uninitialized modules are skipped.
Manual fallback
Only if the script is unavailable: from the main repo root, git worktree remove <path>, then git branch -D <branch> after squash/rebase merges.
Stop without deleting if the worktree is dirty.
This honors the project rule: never revert or discard changes you did not make.
Candidate-view store (separate)
This cleanup covers .worktrees/ only. Native review candidate views live under
the shared git dir (.git/gentle-ai/candidate-views) and are controller-owned.
After the merged-worktree pass, load the worktree-candidate-cleanup skill to
audit that store and, only when every eligibility proof holds, reap stale
candidate views and their matching *.owner.json markers. Never delete candidate
views or markers ad hoc.
04 同仓库更多 Skills
test-skill
Test skill for fixture purposes.
obsidian-markdown
Fixture skill for obsidian-markdown.
obsidian-cli
Fixture skill for obsidian-cli.
obsidian-bases
Fixture skill for obsidian-bases.
json-canvas
Fixture skill for json-canvas.
defuddle
Fixture skill for defuddle.