From 019604f6e8134bcd55c7668dd5770b792f3e1225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Pe=C3=B1a?= Date: Tue, 5 May 2026 04:33:05 -0600 Subject: [PATCH 1/4] feat(implement): make OpenSpec mandatory when configured (Phase 0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces a new Phase 0 in /make-no-mistakes:implement that runs BEFORE Phase 1 (Setup). When linear-setup.json has openspec.changesPath configured, every issue MUST have a corresponding OpenSpec change (proposal.md + design.md + tasks.md) before implementation begins. Previously, step 4b in Phase 2 read OpenSpec context only "if found", which made the spec-first discipline silently skippable. Since OpenSpec was officially adopted at Dojo Coding on 2026-03-30 (Slack thread C0AE5MKAX7B), that gap re-introduces exactly the failure mode adoption was meant to prevent: implementations diverging from intent because nobody wrote the intent down. Behavior change: - When openspec.changesPath is set AND the issue is non-trivial (>2 files, architectural decisions, or reviewer-flagged risk), the implementer STOPS and creates the OpenSpec change as the first commit on the implementation branch. - Trivial issues (typo fixes, dependency bumps, single-line changes) may opt out with a one-line PR note. - Repos without openspec.changesPath are unaffected — Phase 0 is skipped entirely, preserving back-compat. Bumps version to 1.4.1 across plugin.json, marketplace.json, and package.json. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude-plugin/marketplace.json | 4 +-- .claude-plugin/plugin.json | 2 +- commands/implement.md | 48 ++++++++++++++++++++++++++++----- package.json | 2 +- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0ff88c1..a36d1bf 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,7 +1,7 @@ { "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", "name": "make-no-mistakes", - "version": "1.4.0", + "version": "1.6.0", "description": "The disciplined dev lifecycle — implement issues, review PRs, sync releases, test E2E, manage sessions, and stash secrets via OS-native prompts. One plugin to make no mistakes.", "owner": { "name": "Luis Andres Pena Castillo", @@ -11,7 +11,7 @@ { "name": "make-no-mistakes", "description": "Dev lifecycle orchestrator: disciplined Linear issue execution with worktree isolation, PR review with Greptile gating, team release sync, E2E test generation and execution, test suite previewer, security pentesting, MoSCoW + RICE prioritization, cross-platform secret stash via OS-native GUI prompts (zenity / kdialog / osascript / Get-Credential), and session management. 18 commands, 6 auto-activating skills, 2 specialized agents.", - "version": "1.4.0", + "version": "1.6.0", "author": { "name": "Luis Andres Pena Castillo", "email": "lapc506@users.noreply.github.com" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index bf417bf..731facd 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "make-no-mistakes", - "version": "1.5.0", + "version": "1.6.0", "description": "The disciplined dev lifecycle — implement issues, review PRs, sync releases, test E2E, manage sessions, stash secrets, and enforce manifest-driven tool-call hooks. One plugin to make no mistakes.", "author": { "name": "Luis Andres Pena Castillo", diff --git a/commands/implement.md b/commands/implement.md index e296c0e..887a56c 100644 --- a/commands/implement.md +++ b/commands/implement.md @@ -181,6 +181,36 @@ If any check fails, STOP and resolve before proceeding. For EACH issue in the sequence, follow this exact workflow. No shortcuts. No exceptions. +### Phase 0: OpenSpec Context (MANDATORY when configured) + +Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPath`: + +1. **Resolve the configured changes directory** and check for an existing OpenSpec change that references this issue. The grep MUST use the configured path — projects may set `openspec.changesPath` to anything (e.g., `specs/changes/`) and a hardcoded `openspec/changes/` would silently miss every existing spec there: + ```bash + CHANGES_PATH=$(jq -r '.openspec.changesPath' linear-setup.json) + grep -r "{issue-id}" "$CHANGES_PATH"/*/proposal.md 2>/dev/null + ``` + In all references below, `` denotes that resolved `$CHANGES_PATH` value, NOT the literal string `openspec/changes/`. + +2. **If found**: Read ALL three artifacts as primary implementation context: + - `/{change-slug}/proposal.md` — intent, domain, scope + - `/{change-slug}/design.md` — architectural decisions, rejected alternatives, pre-launch checklist + - `/{change-slug}/tasks.md` — atomic task list with file paths and commit messages + + These three files are the CONTRACT. The Linear issue is the WHAT; OpenSpec is the HOW. Do not deviate from the spec without going back to it first. + +3. **If MISSING and the issue is non-trivial** (touches >2 files OR involves architectural decisions OR has reviewer-flagged risk): STOP and prepare the OpenSpec change BEFORE proceeding. + - Use `/superpowers:brainstorming` if the design needs more thinking + - Use `/make-no-mistakes:premortem` if the change is load-bearing in production + - **Draft the three artifacts** in `/{change-slug}/`: proposal.md (intent + scope + out-of-scope), design.md (decisions + rationale), tasks.md (atomic checklist with commit messages). Leave them uncommitted on disk — the implementation branch does not exist yet. + - **Defer the commit to Phase 1 step 4a** (below). Phase 1 creates the branch and worktree; the OpenSpec files are then committed as the very first commit on that branch. + +4. **If MISSING but the issue is trivial** (typo fix, dependency bump, single-line change): proceed to Phase 1 without OpenSpec. Add a one-line note to the PR description explaining why OpenSpec was skipped. + +5. **If `openspec.changesPath` is not configured** in `linear-setup.json`: skip this phase entirely. The project hasn't adopted OpenSpec yet. + +**Why mandatory**: per the adoption decision (Slack 2026-03-30, channel C0AE5MKAX7B), OpenSpec is the durable persistence of design decisions. A skill that makes it optional re-introduces the failure mode it was adopted to prevent — implementations diverging from intent because nobody wrote the intent down. + ### Phase 1: Setup 1. **Claim the issue in Linear:** @@ -209,15 +239,19 @@ For EACH issue in the sequence, follow this exact workflow. No shortcuts. No exc - Create sub-issues in Linear for each PR if decomposing. - Comment the decomposition plan on the parent issue. -### Phase 2: Implement +4a. **Commit the OpenSpec change as the first commit** (only if Phase 0 step 3 drafted artifacts because none existed): + - Move/copy the `/{change-slug}/` directory drafted in Phase 0 into the worktree if it isn't already there. + - Commit it before any implementation work: + ```bash + git add "{changes-path}/{change-slug}/" + git commit -m "docs(openspec): {change-slug}" + ``` + - The `docs(openspec)` commit MUST be commit #1 on the branch — reviewers and future agents read the spec before the diff. + - If Phase 0 found an existing change (step 2), skip this — the spec is already on `{baseBranch}`. -4. **Implement in the worktree.** Follow all project conventions from CLAUDE.md. +### Phase 2: Implement -4b. **Check for OpenSpec context:** - - If `linear-setup.json` has `openspec.changesPath`, check if an OpenSpec change exists that references this issue - - Look in `openspec/changes/*/proposal.md` for the issue ID - - If found, read ALL artifacts (proposal.md, design.md, tasks.md) as implementation context - - This provides richer context than the Linear issue description alone +4. **Implement in the worktree.** Follow all project conventions from CLAUDE.md. If Phase 0 produced an OpenSpec change, treat its `tasks.md` as the authoritative checklist — work through it in order and do not improvise file paths or commit messages outside the spec. 5. **If multiple approaches exist:** - Dispatch one sub-agent per approach via Mode A (each gets its own worktree automatically) diff --git a/package.json b/package.json index b9db4a2..a8a7ffe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lapc506/make-no-mistakes", - "version": "1.5.0", + "version": "1.6.0", "description": "The disciplined dev lifecycle — implement issues, review PRs, sync releases, test E2E, manage sessions, stash secrets, and enforce manifest-driven tool-call hooks (no SSH+DB, no manual prod, no minified build, no secret leaks, Slack format). OpenCode + Claude Code plugin.", "type": "module", "main": "./dist/index.js", From 60e2cbb69b2a3a69aefe45467e08d82cd8d7fd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Pe=C3=B1a?= Date: Sat, 9 May 2026 18:47:05 -0600 Subject: [PATCH 2/4] fix(implement): use \$CHANGES_PATH in step 4a + renumber phases sequentially MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Greptile re-review on PR #13 caught two real defects in the new Phase 0/Phase 1 text: 1. Phase 1 step 4a's git add command used an undefined placeholder "{changes-path}" — the variable established in Phase 0 step 1 is "\$CHANGES_PATH" (set via jq from linear-setup.json). An agent running the step verbatim would stage nothing, producing an empty first commit and silently breaking the spec-first guarantee the entire phase enforces. Step 4a now re-exports CHANGES_PATH inside the worktree (the parent shell's variable doesn't survive the cd into a fresh worktree) and uses "\$CHANGES_PATH/{change-slug}/" in the git add. 2. The original document restarted step numbering at 4 in Phase 2, colliding with Phase 1's existing step 4 + 4a. Cross-phase references like "step 4" became ambiguous. Renumbered the entire chain so steps run sequentially across all four phases: - Phase 1: 1, 2, 3, 4, 4a - Phase 2: 5, 6, 7 - Phase 3: 8, 9, 10, 11, 12 - Phase 4: 13, 14, 15, 16, 17 Co-Authored-By: Claude Opus 4.7 (1M context) --- commands/implement.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/commands/implement.md b/commands/implement.md index 887a56c..dd5bdca 100644 --- a/commands/implement.md +++ b/commands/implement.md @@ -241,9 +241,10 @@ Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPat 4a. **Commit the OpenSpec change as the first commit** (only if Phase 0 step 3 drafted artifacts because none existed): - Move/copy the `/{change-slug}/` directory drafted in Phase 0 into the worktree if it isn't already there. - - Commit it before any implementation work: + - Re-export `CHANGES_PATH` inside the worktree (the variable from Phase 0 was set in the parent shell), then commit before any implementation work: ```bash - git add "{changes-path}/{change-slug}/" + CHANGES_PATH=$(jq -r '.openspec.changesPath' linear-setup.json) + git add "$CHANGES_PATH/{change-slug}/" git commit -m "docs(openspec): {change-slug}" ``` - The `docs(openspec)` commit MUST be commit #1 on the branch — reviewers and future agents read the spec before the diff. @@ -251,16 +252,16 @@ Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPat ### Phase 2: Implement -4. **Implement in the worktree.** Follow all project conventions from CLAUDE.md. If Phase 0 produced an OpenSpec change, treat its `tasks.md` as the authoritative checklist — work through it in order and do not improvise file paths or commit messages outside the spec. +5. **Implement in the worktree.** Follow all project conventions from CLAUDE.md. If Phase 0 produced an OpenSpec change, treat its `tasks.md` as the authoritative checklist — work through it in order and do not improvise file paths or commit messages outside the spec. -5. **If multiple approaches exist:** +6. **If multiple approaches exist:** - Dispatch one sub-agent per approach via Mode A (each gets its own worktree automatically) - Run all approaches in parallel with `run_in_background: true` - Each approach may discover new issues — document them in its final report - Synthesize the best parts of multiple solutions if needed - Close losing sub-agent branches/PRs after synthesis -6. **Write tests:** +7. **Write tests:** - Model E2E test cases with Slack MCP first (plan them in a test channel or thread) - Split test cases: some for **Playwright**, others for **Chrome DevTools MCP** - Browser ALWAYS in focus. **NEVER headless.** Both Playwright and Chrome DevTools MCP. @@ -272,33 +273,33 @@ Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPat ### Phase 3: PR + Review Loop -7. **Create the PR:** +8. **Create the PR:** ```bash gh pr create --base {baseBranch} --title "{issue-id}: {concise title}" --body "..." ``` - Link the Linear issue in the PR body - Add "Created by Claude Code on behalf of @{user}" -8. **Tag ALL reviewers:** +9. **Tag ALL reviewers:** - Comment `@greptile review` on the PR - Wait for automated reviews from **Greptile**, **CodeRabbit**, and **Graphite** - All three reviewers are configured in the project — check all of them -9. **Fix reviewer feedback:** - - Address ALL insights from Greptile, CodeRabbit, AND Graphite - - Commit fixes to the same branch - - Re-tag: `@greptile review` again if needed - - Target: Greptile confidence **≥ 3/5**, CodeRabbit no critical issues, Graphite no blockers - - If a reviewer doesn't respond within 5 minutes, proceed but note it to the user +10. **Fix reviewer feedback:** + - Address ALL insights from Greptile, CodeRabbit, AND Graphite + - Commit fixes to the same branch + - Re-tag: `@greptile review` again if needed + - Target: Greptile confidence **≥ 3/5**, CodeRabbit no critical issues, Graphite no blockers + - If a reviewer doesn't respond within 5 minutes, proceed but note it to the user -10. **Verify CI:** +11. **Verify CI:** ```bash gh pr checks {pr-number} --watch ``` - ALL checks must pass before proceeding - If CI fails, fix and push — do not skip -11. **Check merge conflicts:** +12. **Check merge conflicts:** ```bash gh pr view {pr-number} --json mergeable ``` @@ -307,16 +308,16 @@ Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPat ### Phase 4: Merge + Cleanup -12. **Merge the PR:** +13. **Merge the PR:** ```bash gh pr merge {pr-number} --squash --delete-branch ``` -13. **Update Linear:** +14. **Update Linear:** - Set status to **Done** - Comment: "Merged via PR #{pr-number}" -14. **Clean up worktrees:** +15. **Clean up worktrees:** ```bash git worktree remove .claude/worktrees/{issue-id} --force # Verify ALL worktrees for this issue are removed @@ -324,13 +325,13 @@ Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPat git worktree prune ``` -15. **Sync before next issue:** +16. **Sync before next issue:** ```bash git checkout {baseBranch} git pull origin {baseBranch} --rebase ``` -16. **Repeat** from Phase 1 for the next issue. +17. **Repeat** from Phase 1 for the next issue. ## Chain Merge Strategy From 553d81a8757dfaa25226b02bd434dcd1d127965e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Pe=C3=B1a?= Date: Sat, 9 May 2026 19:03:28 -0600 Subject: [PATCH 3/4] fix(implement): make step 4a's source path explicit via git worktree list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Greptile re-review on PR #13 caught a P1 ambiguity in step 4a: Phase 0 drafts spec files in the main working tree at {main-tree}/\$CHANGES_PATH/ {change-slug}/, but Phase 1 step 3 cd's into a freshly created worktree that does NOT carry over uncommitted files. The previous wording said "move/copy if not already there" without naming the source path — an agent inside the worktree could resolve it incorrectly and end up running git add on a non-existent path, producing a silently empty docs(openspec) commit and defeating the spec-first guarantee. Step 4a now: 1. Re-exports \$CHANGES_PATH inside the worktree (the parent shell's variable doesn't survive the cd). 2. Computes \$MAIN_TREE via git worktree list --porcelain (first entry is always the primary tree, regardless of which worktree we're currently in). 3. Skips the copy when the directory already exists in the worktree (idempotent on retry). 4. Copies "\$MAIN_TREE/\$CHANGES_PATH/{change-slug}" into the worktree before git add — explicit, unambiguous, agent-runnable verbatim. Co-Authored-By: Claude Opus 4.7 (1M context) --- commands/implement.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/commands/implement.md b/commands/implement.md index dd5bdca..85834a0 100644 --- a/commands/implement.md +++ b/commands/implement.md @@ -240,15 +240,25 @@ Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPat - Comment the decomposition plan on the parent issue. 4a. **Commit the OpenSpec change as the first commit** (only if Phase 0 step 3 drafted artifacts because none existed): - - Move/copy the `/{change-slug}/` directory drafted in Phase 0 into the worktree if it isn't already there. - - Re-export `CHANGES_PATH` inside the worktree (the variable from Phase 0 was set in the parent shell), then commit before any implementation work: + - Phase 0 wrote the artifacts to the **main working tree** at `{main-tree}/$CHANGES_PATH/{change-slug}/`. Phase 1 step 3 created a fresh worktree from `{baseBranch}`, which does NOT carry over those uncommitted files. Copy them into the current worktree explicitly: ```bash + # Inside the new worktree (Phase 1 step 3 cd'd here). CHANGES_PATH=$(jq -r '.openspec.changesPath' linear-setup.json) + # Resolve the main working tree's filesystem path from git's worktree + # registry — first row of `git worktree list --porcelain` is always the + # primary tree, regardless of which worktree we're currently in. + MAIN_TREE=$(git worktree list --porcelain | awk '/^worktree/ {print $2; exit}') + # Skip the copy if the worktree already has the directory (e.g. an + # earlier run already staged it, or the spec was committed previously). + if [ ! -d "$CHANGES_PATH/{change-slug}" ]; then + mkdir -p "$CHANGES_PATH" + cp -r "$MAIN_TREE/$CHANGES_PATH/{change-slug}" "$CHANGES_PATH/" + fi git add "$CHANGES_PATH/{change-slug}/" git commit -m "docs(openspec): {change-slug}" ``` - The `docs(openspec)` commit MUST be commit #1 on the branch — reviewers and future agents read the spec before the diff. - - If Phase 0 found an existing change (step 2), skip this — the spec is already on `{baseBranch}`. + - If Phase 0 found an existing change (step 2), skip this entire step — the spec is already on `{baseBranch}` and inherited by the new worktree. ### Phase 2: Implement From d7ea9c93fe5723cd76160dca7a49e53a231272a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Pe=C3=B1a?= Date: Sat, 9 May 2026 19:10:27 -0600 Subject: [PATCH 4/4] fix(implement): bind {change-slug} as $CHANGE_SLUG + define naming rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Greptile round-4 caught the same class of defect as round 2 but for a different placeholder: {change-slug} was a bare template literal in the step 4a bash block, sitting next to a properly bound \$CHANGES_PATH. An agent running the block verbatim would execute git add "\$CHANGES_PATH/{change-slug}/" which stages a literal path containing the brace string — staging nothing — and produces a silently empty docs(openspec) commit. Two paired fixes: 1. Phase 0 step 3 now states the slug naming convention explicitly: {issue-id-lowercase}-{short-kebab-description} (lowercase ASCII + hyphens, e.g. doj-3946-atomic-primitives-sprint). Same shape as the implementation branch so agents and humans converge on the same directory name across runs. 2. Step 4a bash block now binds CHANGE_SLUG="" at the top (placeholder agents/humans MUST replace before running) and uses "\$CHANGE_SLUG" in every file path expression. mkdir/cp/git add/git commit all reference the bound variable, so verbatim execution produces a non-empty commit even if the surrounding text is unread. Documentation-template uses of {change-slug} elsewhere in the file are left intact — they refer to the literal directory name in prose, not shell variables, and agents reading prose interpret them correctly. Co-Authored-By: Claude Opus 4.7 (1M context) --- commands/implement.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/commands/implement.md b/commands/implement.md index 85834a0..fa46dd9 100644 --- a/commands/implement.md +++ b/commands/implement.md @@ -202,7 +202,8 @@ Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPat 3. **If MISSING and the issue is non-trivial** (touches >2 files OR involves architectural decisions OR has reviewer-flagged risk): STOP and prepare the OpenSpec change BEFORE proceeding. - Use `/superpowers:brainstorming` if the design needs more thinking - Use `/make-no-mistakes:premortem` if the change is load-bearing in production - - **Draft the three artifacts** in `/{change-slug}/`: proposal.md (intent + scope + out-of-scope), design.md (decisions + rationale), tasks.md (atomic checklist with commit messages). Leave them uncommitted on disk — the implementation branch does not exist yet. + - **Compute the change slug deterministically** so every later step (and any restart) targets the same directory. The slug MUST follow the same shape as the implementation branch: `{issue-id-lowercase}-{short-kebab-description}` (e.g., `doj-3946-atomic-primitives-sprint`). Lowercase only, ASCII alphanumerics + hyphens, no leading/trailing hyphens, no other separators. + - **Draft the three artifacts** in `//`: proposal.md (intent + scope + out-of-scope), design.md (decisions + rationale), tasks.md (atomic checklist with commit messages). Leave them uncommitted on disk — the implementation branch does not exist yet. - **Defer the commit to Phase 1 step 4a** (below). Phase 1 creates the branch and worktree; the OpenSpec files are then committed as the very first commit on that branch. 4. **If MISSING but the issue is trivial** (typo fix, dependency bump, single-line change): proceed to Phase 1 without OpenSpec. Add a one-line note to the PR description explaining why OpenSpec was skipped. @@ -240,22 +241,25 @@ Run this BEFORE Phase 1 (Setup). If `linear-setup.json` has `openspec.changesPat - Comment the decomposition plan on the parent issue. 4a. **Commit the OpenSpec change as the first commit** (only if Phase 0 step 3 drafted artifacts because none existed): - - Phase 0 wrote the artifacts to the **main working tree** at `{main-tree}/$CHANGES_PATH/{change-slug}/`. Phase 1 step 3 created a fresh worktree from `{baseBranch}`, which does NOT carry over those uncommitted files. Copy them into the current worktree explicitly: + - Phase 0 wrote the artifacts to the **main working tree** at `{main-tree}/$CHANGES_PATH//`. Phase 1 step 3 created a fresh worktree from `{baseBranch}`, which does NOT carry over those uncommitted files. Copy them into the current worktree explicitly. The `CHANGE_SLUG` value MUST match the slug Phase 0 step 3 chose (same `{issue-id-lowercase}-{short-kebab-description}` rule): ```bash # Inside the new worktree (Phase 1 step 3 cd'd here). CHANGES_PATH=$(jq -r '.openspec.changesPath' linear-setup.json) + # Bind the slug Phase 0 step 3 produced. Replace the placeholder before + # running — never leave it as a literal "" string. + CHANGE_SLUG="" # e.g. doj-3946-atomic-primitives-sprint # Resolve the main working tree's filesystem path from git's worktree # registry — first row of `git worktree list --porcelain` is always the # primary tree, regardless of which worktree we're currently in. MAIN_TREE=$(git worktree list --porcelain | awk '/^worktree/ {print $2; exit}') # Skip the copy if the worktree already has the directory (e.g. an # earlier run already staged it, or the spec was committed previously). - if [ ! -d "$CHANGES_PATH/{change-slug}" ]; then + if [ ! -d "$CHANGES_PATH/$CHANGE_SLUG" ]; then mkdir -p "$CHANGES_PATH" - cp -r "$MAIN_TREE/$CHANGES_PATH/{change-slug}" "$CHANGES_PATH/" + cp -r "$MAIN_TREE/$CHANGES_PATH/$CHANGE_SLUG" "$CHANGES_PATH/" fi - git add "$CHANGES_PATH/{change-slug}/" - git commit -m "docs(openspec): {change-slug}" + git add "$CHANGES_PATH/$CHANGE_SLUG/" + git commit -m "docs(openspec): $CHANGE_SLUG" ``` - The `docs(openspec)` commit MUST be commit #1 on the branch — reviewers and future agents read the spec before the diff. - If Phase 0 found an existing change (step 2), skip this entire step — the spec is already on `{baseBranch}` and inherited by the new worktree.