diff --git a/.claude/skills/specsync/SKILL.md b/.claude/skills/specsync/SKILL.md index dfcc353..d9702ae 100644 --- a/.claude/skills/specsync/SKILL.md +++ b/.claude/skills/specsync/SKILL.md @@ -27,7 +27,9 @@ specsync -change # push to GitHub **Pull issues into specs:** ```bash specsync pull -issue N [-change ] +specsync pull -issue N -worktree # also creates/reuses a dedicated git worktree + branch ``` +One issue, one branch, one worktree — don't stack unrelated changes on whatever branch is currently checked out. `-worktree` sets both up for the issue-first path; see `specsync agent-help` for the general convention (also applies spec-first, where you branch/worktree by hand: `git worktree add ../worktrees/ -b feat/-`). **Bind an already-existing change to an already-existing issue:** ```bash diff --git a/cmd/specsync/SKILL.md b/cmd/specsync/SKILL.md index dfcc353..d9702ae 100644 --- a/cmd/specsync/SKILL.md +++ b/cmd/specsync/SKILL.md @@ -27,7 +27,9 @@ specsync -change # push to GitHub **Pull issues into specs:** ```bash specsync pull -issue N [-change ] +specsync pull -issue N -worktree # also creates/reuses a dedicated git worktree + branch ``` +One issue, one branch, one worktree — don't stack unrelated changes on whatever branch is currently checked out. `-worktree` sets both up for the issue-first path; see `specsync agent-help` for the general convention (also applies spec-first, where you branch/worktree by hand: `git worktree add ../worktrees/ -b feat/-`). **Bind an already-existing change to an already-existing issue:** ```bash diff --git a/cmd/specsync/agent_help.go b/cmd/specsync/agent_help.go index ff92408..ec60829 100644 --- a/cmd/specsync/agent_help.go +++ b/cmd/specsync/agent_help.go @@ -131,10 +131,25 @@ var commandMetadata = map[string]AgentCommandHelp{ Default: "auto-detect", Description: "Target repo as owner/name", }, + { + Name: "worktree", + Type: "boolean", + Required: false, + Default: false, + Description: "Create (or reuse) a git worktree for this issue and run the pull inside it, instead of the current directory — one issue, one branch, one worktree", + }, + { + Name: "worktree-dir", + Type: "string", + Required: false, + Default: "../worktrees (or $SPECSYNC_WORKTREE_DIR)", + Description: "Base directory for -worktree", + }, }, SafetyRules: []string{ "Use -dry-run to preview before pulling", "Verify the issue number is correct", + "Work each issue in its own branch (feat/-) and, ideally, its own worktree — pass -worktree to have pull set both up automatically, rather than accumulating unrelated changes on whatever branch happens to be checked out", }, Examples: []string{ "specsync pull -issue 42 -dry-run", @@ -935,6 +950,7 @@ func renderAgentHelpOverview(asJSON bool) { "Use `specsync agent-help -json` for machine-readable output", "All output formats support -json for automation", "Always use -dry-run before making changes", + "One issue, one branch, one worktree: before starting work on a change, create a branch named after its issue (e.g. feat/-) — a dedicated worktree (`git worktree add ../worktrees/ -b feat/-`) keeps concurrent changes from colliding in the same working directory. `specsync pull -issue -worktree` does this for you on the issue-first path.", }, } data, _ := json.MarshalIndent(overview, "", " ") @@ -964,7 +980,20 @@ Available commands:`) - Always use -dry-run before making changes - Many commands support -json for machine-readable output - See agent-help for full details and flags -- Read AGENTS.md for workflow patterns +- If the project has an AGENTS.md, read it for local workflow patterns + +## Branch & worktree per issue + +Before starting work on a change, create a branch named after its issue +(e.g. feat/42-my-change) — do not commit directly to the main branch or mix +two issues' work in one branch. A dedicated worktree keeps concurrent +changes from colliding in the same working directory: + + git worktree add ../worktrees/my-change -b feat/42-my-change + +On the issue-first path, 'specsync pull -issue 42 -worktree' does this for +you (see 'specsync agent-help pull'). Clean up when merged: +'git worktree remove ../worktrees/my-change'. `) } } diff --git a/npm/skills/specsync/SKILL.md b/npm/skills/specsync/SKILL.md index dfcc353..d9702ae 100644 --- a/npm/skills/specsync/SKILL.md +++ b/npm/skills/specsync/SKILL.md @@ -27,7 +27,9 @@ specsync -change # push to GitHub **Pull issues into specs:** ```bash specsync pull -issue N [-change ] +specsync pull -issue N -worktree # also creates/reuses a dedicated git worktree + branch ``` +One issue, one branch, one worktree — don't stack unrelated changes on whatever branch is currently checked out. `-worktree` sets both up for the issue-first path; see `specsync agent-help` for the general convention (also applies spec-first, where you branch/worktree by hand: `git worktree add ../worktrees/ -b feat/-`). **Bind an already-existing change to an already-existing issue:** ```bash diff --git a/openspec/changes/guide-agents-on-branch-per-issue/proposal.md b/openspec/changes/guide-agents-on-branch-per-issue/proposal.md new file mode 100644 index 0000000..220af6a --- /dev/null +++ b/openspec/changes/guide-agents-on-branch-per-issue/proposal.md @@ -0,0 +1,40 @@ +# Guide agents toward one branch, one worktree per issue + +## Why + +Agents working with specsync-managed projects are often unclear on basic +git hygiene: which branch to work on, whether to reuse one across unrelated +issues, and when a worktree is warranted. specsync already has real support +for this — `pull -worktree` creates a dedicated branch and worktree per +issue — but nothing an agent actually reads mentions it. + +The gap is specific: this repo's own `AGENTS.md` documents the convention in +detail, but `AGENTS.md` is a dogfooding doc for *this* repo — it is never +installed into a project that adopts specsync. `specsync install-skill` only +ever ships `SKILL.md`; `skills/specsync/docs/*.md` never leaves this repo +either. So an agent working in an arbitrary consuming project, or reading +`specsync agent-help` (the one thing guaranteed to be reachable everywhere, +since it ships with the binary), gets no guidance on this at all — and +`agent-help`'s own overview previously just said "read AGENTS.md," which is +silently false for most projects. + +## What + +- `specsync agent-help` (overview, both text and `-json`) now states the + one-issue/one-branch/one-worktree convention directly, and softens the + AGENTS.md pointer to "if the project has one." +- `specsync agent-help pull` documents the existing `-worktree`/ + `-worktree-dir` flags (previously undocumented anywhere) and adds a safety + rule pointing at them. +- The skill (`SKILL.md`, `docs/reference.md`, `docs/workflow.md`) mentions + the same convention and the `-worktree` flag, including the honest caveat + that there's no equivalent automation for the spec-first path yet — branch + and worktree by hand there. + +No new command or flag: `pull -worktree` already existed and did the right +thing. This only makes it discoverable. + +## Release note + +`specsync agent-help` now guides agents toward one branch and worktree per +issue, and documents `pull`'s existing `-worktree`/`-worktree-dir` flags. diff --git a/openspec/changes/guide-agents-on-branch-per-issue/tasks.md b/openspec/changes/guide-agents-on-branch-per-issue/tasks.md new file mode 100644 index 0000000..775398d --- /dev/null +++ b/openspec/changes/guide-agents-on-branch-per-issue/tasks.md @@ -0,0 +1,6 @@ +# Tasks: Guide agents toward one branch, one worktree per issue + +- [x] `agent-help` overview (text + JSON) states the branch/worktree-per-issue convention directly, not just "read AGENTS.md" +- [x] `agent-help pull` documents `-worktree`/`-worktree-dir` and adds a matching safety rule +- [x] Skill (`SKILL.md`, `docs/reference.md`, `docs/workflow.md`) mentions the convention and the flag, including the spec-first gap (no automation there yet) +- [x] `make sync-skill`; `go build`/`go vet`/`go test` clean diff --git a/skills/specsync/SKILL.md b/skills/specsync/SKILL.md index dfcc353..d9702ae 100644 --- a/skills/specsync/SKILL.md +++ b/skills/specsync/SKILL.md @@ -27,7 +27,9 @@ specsync -change # push to GitHub **Pull issues into specs:** ```bash specsync pull -issue N [-change ] +specsync pull -issue N -worktree # also creates/reuses a dedicated git worktree + branch ``` +One issue, one branch, one worktree — don't stack unrelated changes on whatever branch is currently checked out. `-worktree` sets both up for the issue-first path; see `specsync agent-help` for the general convention (also applies spec-first, where you branch/worktree by hand: `git worktree add ../worktrees/ -b feat/-`). **Bind an already-existing change to an already-existing issue:** ```bash diff --git a/skills/specsync/docs/reference.md b/skills/specsync/docs/reference.md index 07b1714..1267d07 100644 --- a/skills/specsync/docs/reference.md +++ b/skills/specsync/docs/reference.md @@ -22,7 +22,7 @@ specsync [-dry-run] [-change ] [-reconcile] [-close-completed] [-repo owne Pull an issue into a local OpenSpec change. ``` -specsync pull -issue [-change ] [-dry-run] [-repo owner/name] +specsync pull -issue [-change ] [-dry-run] [-repo owner/name] [-worktree] [-worktree-dir ] ``` **Flags:** @@ -30,6 +30,8 @@ specsync pull -issue [-change ] [-dry-run] [-repo owner/name] - `-change ` — Override auto-derived change name - `-dry-run` — Preview without writing files - `-repo owner/name` — Override auto-detected repo +- `-worktree` — Create (or reuse) a dedicated git worktree and branch (`feat/-`) for this issue, and run the pull inside it, instead of pulling into whatever's currently checked out — one issue, one branch, one worktree +- `-worktree-dir ` — Base directory for `-worktree` (default: `$SPECSYNC_WORKTREE_DIR` or `../worktrees`) ## adopt diff --git a/skills/specsync/docs/workflow.md b/skills/specsync/docs/workflow.md index 285f30d..c493909 100644 --- a/skills/specsync/docs/workflow.md +++ b/skills/specsync/docs/workflow.md @@ -1,5 +1,24 @@ # Workflow Patterns +## One Issue, One Branch, One Worktree + +Before starting *any* change below — spec-first or issue-first — put it on +its own branch, named after the issue once one exists (`feat/-`; use +a working name and rename once `sync`/`pull` mints the issue if starting +spec-first). Don't commit two unrelated changes to the same branch, and don't +work directly on the main branch. A dedicated worktree avoids collisions when +more than one change is in flight at once: + +```bash +git worktree add ../worktrees/my-change -b feat/42-my-change +# ... implement here ... +git worktree remove ../worktrees/my-change # once merged +``` + +On the issue-first path, `specsync pull -issue 42 -worktree` sets both up for +you in one step (see `pull` in [reference.md](reference.md)). There's no +equivalent flag for spec-first yet — branch/worktree by hand as above. + ## Spec-First (Plan → Issue) When starting from a spec idea: