codebyplan 1.13.43 → 1.13.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/cli.js +5079 -1556
  2. package/package.json +1 -1
  3. package/templates/agents/cbp-task-check.md +1 -3
  4. package/templates/agents/cbp-task-planner.md +8 -6
  5. package/templates/github-workflows/publish.yml +93 -21
  6. package/templates/hooks/cbp-auto-test-hooks.sh +1 -0
  7. package/templates/hooks/cbp-e2e-spec-patterns.sh +100 -0
  8. package/templates/hooks/cbp-lint-format-on-edit.sh +1 -0
  9. package/templates/hooks/cbp-maestro-yaml-validate.sh +1 -0
  10. package/templates/hooks/cbp-pre-commit-quality-gate.sh +1 -0
  11. package/templates/hooks/cbp-statusline.sh +0 -0
  12. package/templates/hooks/cbp-subagent-statusline.sh +0 -0
  13. package/templates/hooks/cbp-test-coverage-gate.sh +1 -0
  14. package/templates/hooks/cbp-test-hooks.sh +1 -0
  15. package/templates/hooks/hooks.json +4 -0
  16. package/templates/hooks/verify-parity.sh +20 -0
  17. package/templates/rules/parallel-waves.md +8 -3
  18. package/templates/rules/scope-vocabulary.md +4 -3
  19. package/templates/settings.project.base.json +22 -0
  20. package/templates/skills/cbp-build-cc-claude-file/SKILL.md +11 -1
  21. package/templates/skills/cbp-build-cc-claude-file/scripts/validate-claude-file.sh +72 -0
  22. package/templates/skills/cbp-build-cc-mode/SKILL.md +12 -16
  23. package/templates/skills/cbp-build-cc-rule/SKILL.md +11 -1
  24. package/templates/skills/cbp-build-cc-rule/scripts/validate-rule.sh +69 -0
  25. package/templates/skills/cbp-build-cc-settings/SKILL.md +2 -2
  26. package/templates/skills/cbp-build-cc-settings/scripts/validate-settings.sh +67 -0
  27. package/templates/skills/cbp-checkpoint-create/SKILL.md +12 -4
  28. package/templates/skills/cbp-checkpoint-end/SKILL.md +19 -11
  29. package/templates/skills/cbp-git-commit/SKILL.md +10 -12
  30. package/templates/skills/cbp-git-worktree-create/SKILL.md +7 -48
  31. package/templates/skills/cbp-git-worktree-remove/SKILL.md +23 -40
  32. package/templates/skills/cbp-map-architecture/SKILL.md +1 -0
  33. package/templates/skills/cbp-merge-main/SKILL.md +21 -26
  34. package/templates/skills/cbp-refresh-arch-map/SKILL.md +1 -0
  35. package/templates/skills/cbp-round-check/SKILL.md +37 -36
  36. package/templates/skills/cbp-round-execute/SKILL.md +9 -3
  37. package/templates/skills/cbp-session-end/SKILL.md +27 -47
  38. package/templates/skills/cbp-session-start/SKILL.md +35 -51
  39. package/templates/skills/cbp-standalone-task-start/SKILL.md +10 -19
  40. package/templates/skills/cbp-supabase-migrate/SKILL.md +24 -27
  41. package/templates/skills/cbp-task-start/SKILL.md +9 -21
  42. package/templates/skills/cbp-task-testing/SKILL.md +18 -10
@@ -30,7 +30,7 @@ Set `KIND` for the rest of this skill. MCP tool names vary by KIND:
30
30
 
31
31
  # Round Check Command
32
32
 
33
- Run automated checks independently with mandatory execution. Updates round QA. Hard fails if mandatory checks (build/lint/types) fail.
33
+ Run automated checks independently with mandatory execution. Updates round QA. Hard fails if mandatory checks (gate6/lint/typecheck/tests) fail.
34
34
 
35
35
  ## Instructions
36
36
 
@@ -41,80 +41,80 @@ Use Kind Detection above to set KIND. Then:
41
41
  - **checkpoint KIND**: Read `.codebyplan/state/checkpoints/<checkpointId>/tasks/<taskId>.json` (local-first) to find active task, then read `.codebyplan/state/checkpoints/<checkpointId>/tasks/<taskId>/rounds/<roundId>.json` to find the in-progress round. If missing/stale, run `npx codebyplan sync` once and re-read. Break-glass fallback: MCP `get_current_task(repo_id)` + `get_rounds(task_id)` when state dir is absent and sync fails.
42
42
  - **standalone KIND**: MCP `get_current_standalone_task(repo_id)` to find active task, then `get_standalone_rounds(standalone_task_id)` to find the in-progress round. (Standalone KIND still uses MCP until a later task.)
43
43
 
44
- ### Step 2: Determine Project Root
44
+ ### Step 2: Run Core Check Matrix
45
+
46
+ From the repo root, run:
45
47
 
46
- Find the correct app directory:
47
48
  ```bash
48
- REPO_ROOT="$(git rev-parse --show-toplevel)"
49
+ codebyplan check --scope round --json
49
50
  ```
50
- Identify app dir from project structure (e.g., `apps/web/` for Next.js).
51
51
 
52
- ### Step 3: Execute Mandatory Checks (Hard Fail)
52
+ Capture the JSON output. The runner is **whole-repo + baseline**: it runs `turbo run lint|typecheck|test` across every package and diffs each per-package result against the committed `.check-baseline.json`, so a pre-existing failure in an unrelated package does NOT fail the check — only a NEW failure does. The result shape is:
53
53
 
54
- For each check, EXECUTE the command and capture stdout + stderr. Log execution status.
54
+ ```json
55
+ {
56
+ "results": [
57
+ {"check": "gate6"|"lint"|"typecheck"|"tests"|"audit", "status": "pass"|"fail"|"skipped",
58
+ "exit_code": number|null, "command": string, "stdout": string, "stderr": string,
59
+ "executed": boolean, "new_failures"?: string[]}
60
+ ],
61
+ "any_failed": boolean,
62
+ "hard_fail_checks": [ ...names of checks that FAILED ]
63
+ }
64
+ ```
55
65
 
56
- | Check | Command | Hard Fail |
57
- |-------|---------|-----------|
58
- | **Build** | `cd {app_dir} && npm run build 2>&1` | YES |
59
- | **Lint** | `cd {app_dir} && npm run lint 2>&1` | YES |
60
- | **Types** | `cd {app_dir} && npx tsc --noEmit 2>&1` | YES |
66
+ Five checks run in order: `gate6` (sibling-identity parity — `node scripts/check-sibling-identity.mjs`), `lint`, `typecheck`, `tests`, `audit`. For the baselined checks (`lint`/`typecheck`/`tests`) `new_failures` lists the packages that newly fail (not in the baseline); `status` is `pass` when `new_failures` is empty **even if the underlying command exited non-zero** (those failures are pre-existing/baselined). `audit.new_failures` lists new GHSA advisory ids not in the allowlist. **`gate6` is ALWAYS hard-fail — it is never baselined**; its `new_failures` field is omitted (absent/`undefined` in the JSON, not `null`), and a sibling-parity divergence fails the round regardless of the baseline.
61
67
 
62
- For each:
63
- - Run the command via Bash tool
64
- - Log `EXECUTED: <command>` or `FAILED: <command> (exit code: N)`
65
- - If skipping (infrastructure-only changes): log `SKIPPED: <command> (reason: no app code changed)`
68
+ `hard_fail_checks` is dynamic — it lists only the checks that failed (`[]` when all pass; e.g. `["gate6"]` or `["typecheck","tests"]`), drawn from `results[].check`. The hard-fail checks for `--scope round` are `gate6`, `lint`, `typecheck`, and `tests` (`audit` is `--scope task` only). If `any_failed === true` (equivalently, `hard_fail_checks` is non-empty), this is a **hard fail** — surface each failing result's `stdout`/`stderr` (and `new_failures`) and stop.
66
69
 
67
- ### Step 4: Execute Conditional Checks
70
+ ### Step 3: Execute Conditional Checks
68
71
 
69
72
  | Check | Command | Condition |
70
73
  |-------|---------|-----------|
71
- | **Tests** | `cd {app_dir} && npx vitest --run 2>&1` | Test files exist |
72
74
  | **A11y** | Static check (aria, alt, focus) | UI files changed |
73
75
  | **API Health** | `curl -s -o /dev/null -w "%{http_code}" http://localhost:{PORT}/` | API routes changed |
74
76
  | **Visual** | Visual check flow (page-map + visual-check) | UI work + dev server running |
75
77
 
76
- ### Step 5: Analyze Build Output
78
+ ### Step 4: Analyze Output
77
79
 
78
- Scan all captured output for:
80
+ Scan each runner result's `stdout`/`stderr` for:
79
81
  - **Warnings** (not just errors)
80
82
  - **Deprecation notices** (`grep -i "deprecat"` in output)
81
83
  - **Console.log in changed files**: `grep -rn "console\.\(log\|debug\|info\)" {changed_files}` (exclude tests)
82
84
  - **Bundle size warnings**
83
85
 
84
- ### Step 6: Save QA Results
86
+ ### Step 5: Save QA Results
85
87
 
86
88
  Update round QA:
87
89
  - **checkpoint KIND**: `codebyplan round update --id <round_id> --task-id <task_id> --checkpoint-id <checkpoint_id> --qa '<json>'` (CLI write-through: local state file + REST). Break-glass fallback: MCP `update_round(round_id, qa: ...)` when the CLI is unavailable.
88
90
  - **standalone KIND**: MCP `update_standalone_round(standalone_round_id, qa: ...)`. (Standalone KIND still uses MCP until a later task.)
89
91
 
92
+ Map each runner result entry to a QA item:
93
+
90
94
  ```json
91
95
  {
92
96
  "items": [
93
- {"type": "auto", "check": "build", "status": "pass", "ran_at": "...", "notes": null, "executed": true},
94
- {"type": "auto", "check": "lint", "status": "fail", "ran_at": "...", "notes": "3 errors", "executed": true},
95
- {"type": "auto", "check": "types", "status": "pass", "ran_at": "...", "notes": null, "executed": true},
96
- {"type": "auto", "check": "tests", "status": "skipped", "ran_at": "...", "notes": "no test files", "executed": false}
97
+ {"type": "auto", "check": "gate6", "status": "pass", "ran_at": "...", "notes": null, "executed": true},
98
+ {"type": "auto", "check": "lint", "status": "pass", "ran_at": "...", "notes": null, "executed": true},
99
+ {"type": "auto", "check": "typecheck", "status": "fail", "ran_at": "...", "notes": "1 new failing package", "executed": true},
100
+ {"type": "auto", "check": "tests", "status": "pass", "ran_at": "...", "notes": "no new failures (baselined)", "executed": true}
97
101
  ]
98
102
  }
99
103
  ```
100
104
 
101
- ### Step 7: Show Results
105
+ ### Step 6: Show Results
102
106
 
103
107
  ```
104
108
  ## Round Check Results
105
109
 
106
110
  | Check | Status | Executed | Notes |
107
111
  |-------|--------|----------|-------|
108
- | Build | pass | yes | - |
109
- | Lint | fail | yes | 3 errors |
110
- | Types | pass | yes | - |
111
- | Tests | skipped | no | no test files |
112
- | Visual | pass | yes | screenshots saved |
113
-
114
- ### Build Analysis
115
- - Warnings: [N]
116
- - Deprecations: [N]
117
- - Console.logs in code: [N]
112
+ | gate6 | pass | yes | sibling-identity OK |
113
+ | lint | pass | yes | - |
114
+ | typecheck | fail | yes | 1 new failing package |
115
+ | tests | pass | yes | no new failures (baselined) |
116
+ | A11y | pass | yes | - |
117
+ | Visual| pass | yes | screenshots saved |
118
118
 
119
119
  **Result**: [N] passed, [N] failed, [N] skipped
120
120
  **Hard fail**: [yes/no]
@@ -129,4 +129,5 @@ If soft failures only: `Run /cbp-round-start to trigger auto-fix, or fix manuall
129
129
  - **Reads (standalone KIND)**: MCP `get_current_standalone_task` / `get_standalone_rounds` (standalone KIND still uses MCP until a later task)
130
130
  - **Writes (checkpoint KIND)**: `codebyplan round update` (qa field). Break-glass: MCP `update_round`.
131
131
  - **Writes (standalone KIND)**: MCP `update_standalone_round` (qa field). (Standalone KIND still uses MCP until a later task.)
132
+ - **Runner**: `codebyplan check --scope round --json` (whole-repo + baseline via `turbo run`; runs gate6 + lint + typecheck + tests; `--files` is accepted but ignored in whole-repo mode)
132
133
  - **Standalone**: Can be run independently at any time
@@ -178,7 +178,13 @@ Per-wave hard-fail signal — true when ANY hold:
178
178
 
179
179
  - `testing_qa_output.totals.hard_fail === true`.
180
180
  - For any framework `f` in `round.context.e2e_outputs`: `e2e_outputs[f].status === 'failed'` OR `e2e_outputs[f].test_results?.failed > 0`.
181
- - **`e2e_eligible_skipped`**: any framework in `round.context.e2e_eligible[]` for which no specialist output exists in `round.context.e2e_outputs` AND no valid skip reason is recorded (per the `rules/e2e-mandatory.md` valid-skip list). A silently-skipped eligible framework is a hard-fail.
181
+ - **E2E deterministic gate** (replaces the former judgment-based `e2e_eligible_skipped` evaluation): when `round.context.e2e_eligible[]` is non-empty, first persist `e2e_eligible` / `e2e_outputs` to round context via MCP `update_round` (the Step 7 write, pulled forward the CLI reads the round row from the DB), then run:
182
+
183
+ ```bash
184
+ codebyplan e2e verify-round --round-id <round_id> --task-id <task_id>
185
+ ```
186
+
187
+ Exit 0 = e2e pass. Exit 1 = one or more deterministic hard-fails — the stdout JSON's `failed_checks[]` identifies which (`e2e_eligible_skipped`, `zero_assertion_run`, `empty_gallery`); the `rules/e2e-mandatory.md` valid-skip list and the vscode-test empty-gallery exception are honored by the CLI. When `e2e_eligible[]` is empty, skip the CLI call — nothing to verify.
182
188
 
183
189
  **All waves hard_fail: false** → proceed to Step 7. **Any wave hard_fail: true**:
184
190
 
@@ -197,9 +203,9 @@ When `cbp-testing-qa-agent` spawn fails OR the resolved `testing_profile` is `cl
197
203
 
198
204
  `codebyplan round update --id <round-id> --task-id <uuid> --checkpoint-id <uuid> --context <json>` (CLI write-through: local state at `.codebyplan/state/checkpoints/<checkpointId>/tasks/<taskId>/rounds/<roundId>.json` + REST). Break-glass fallback: MCP `update_round` when the CLI is unavailable.
199
205
 
200
- - `context`: { ...existing, executor_output, testing_qa_output, e2e_eligible, e2e_outputs, frontend_ui_review }
206
+ - `context`: { ...existing, executor_output, testing_qa_output, e2e_eligible, e2e_outputs, frontend_ui_review } — when e2e ran, `e2e_eligible` / `e2e_outputs` were already persisted by the Step 6 pull-forward write; re-include them in this merge payload (the `update_round` REPLACE contract requires re-sending every field that should remain — this is a consolidating merge, not a second write of new data).
201
207
 
202
- `e2e_outputs` (a framework-keyed map of specialist outputs, e.g. `{ playwright: {...}, maestro: {...} }`) and `frontend_ui_review` are present only when the gates above admitted them (≥1 eligible framework ran AND Step 5b ran). `e2e_eligible[]` records which frameworks were eligible this round and drives the Step 6 `e2e_eligible_skipped` check.
208
+ `e2e_outputs` (a framework-keyed map of specialist outputs, e.g. `{ playwright: {...}, maestro: {...} }`) is present when ≥1 eligible framework ran. `frontend_ui_review` is present only when ≥1 eligible framework ran AND Step 5b ran (non-empty screenshots). `e2e_eligible[]` records which frameworks were eligible this round and drives the Step 6 E2E deterministic gate.
203
209
 
204
210
  ### Step 8: Auto-trigger Round End
205
211
 
@@ -56,15 +56,12 @@ Continuing Step 1:
56
56
 
57
57
  Same rule as `/cbp-session-start` Step 5.7 — only commit files that are **not** part of an unfinished task.
58
58
 
59
- 1. `git status --porcelain` list all modified/untracked files. If empty → skip this step.
60
- 2. Resolve **task-related files** (leave these uncommitted):
61
- - Read `.codebyplan/state/todos.json` (local-first; reuse result from Step 1.3 if already fetched) to identify the active task id. Break-glass fallback: MCP `get_current_task(repo_id)` when the state dir is absent and sync fails.
62
- - If active task exists: read `.codebyplan/state/checkpoints/<checkpoint_id>/tasks/<task_id>/rounds/` directory and filter to rounds with status not in `completed` / `cancelled`. Break-glass fallback: MCP `get_rounds(task_id)`.
63
- - Collect `files[]` from those rounds `task_files` set
64
- - If no active task exists, `task_files` is empty
65
- 3. `infra_files = changed_files − task_files`
66
- 4. Re-run `git status --porcelain` immediately before showing the commit-prompt (after Steps 1–1.3 have completed their round-trips). Recompute `infra_files` from the fresh listing — eliminates the race where files appear in the index only after network round-trips complete.
67
- 5. If `infra_files` is empty → skip. Otherwise present once:
59
+ 1. Resolve the active task's round files (local-first):
60
+ - Read `.codebyplan/state/todos.json` (local-first; reuse the Step 1.3 result if already fetched) to identify the active task id. Break-glass fallback: MCP `get_current_task(repo_id)` when the state dir is absent and sync fails.
61
+ - If active task exists: read `.codebyplan/state/checkpoints/<checkpoint_id>/tasks/<task_id>/rounds/` and filter to rounds with status not in `completed` / `cancelled`; collect their `files[]` → `task_files` set. Break-glass fallback: MCP `get_rounds(task_id)`.
62
+ - If no active task exists, `task_files` is empty.
63
+ 2. Run `codebyplan session infra-files --json --task-files "<csv>"` where `<csv>` is the comma-separated task files from step 1. Parse the JSON response (`{ infra_files: string[], task_files: string[], note?: string }`). The CLI re-runs `git status --porcelain` internally and applies the set-math deterministically — the race-safe recompute, reading the index after Steps 1–1.3 round-trips complete.
64
+ 3. If `infra_files` is empty skip. Otherwise present once:
68
65
 
69
66
  ```
70
67
  Commit these non-task files before ending session?
@@ -73,7 +70,7 @@ Same rule as `/cbp-session-start` Step 5.7 — only commit files that are **not*
73
70
  Reply: yes | no | select
74
71
  ```
75
72
 
76
- 6. On `yes`: `git add` the listed files, then trigger `/cbp-git-commit`.
73
+ 4. On `yes`: `git add` the listed files, then trigger `/cbp-git-commit`.
77
74
  On `no`: skip. On `select`: ask which subset.
78
75
 
79
76
  Non-blocking — session end proceeds either way.
@@ -84,22 +81,11 @@ Keep this worktree's **home branch** rooted at the freshest production tip — n
84
81
 
85
82
  Runs after Step 1.5 so any infra commits land first, and before Step 1.7 so the freshness gate's asset update operates on the freshest tree.
86
83
 
87
- Resolve the production branch: read `.codebyplan/git.json` and take `branch_config.production` (fall back to `main` if the file or field is absent). Call this `$PRODUCTION`. Then compare the current branch against the worktree's folder name:
84
+ Run `codebyplan session home-ff` and parse the JSON output (`{ result: 'skipped'|'warn'|'fast_forwarded', reason?, warn? }`):
88
85
 
89
- ```bash
90
- FOLDER="$(basename "$(git rev-parse --show-toplevel)")"
91
- CURRENT="$(git rev-parse --abbrev-ref HEAD)"
92
- ```
93
-
94
- - **`$CURRENT` != `$FOLDER`** (a `feat/*` or any non-home branch): skip silently — no fetch, no output.
95
- - **`$CURRENT` == `$FOLDER`** (home branch): fast-forward to `origin/$PRODUCTION`, warning once per failure and continuing:
96
-
97
- ```bash
98
- git fetch origin "$PRODUCTION" 2>/dev/null \
99
- || echo "⚠ home-branch ff: fetch failed — staying on local $CURRENT"
100
- git merge --ff-only "origin/$PRODUCTION" 2>/dev/null \
101
- || echo "⚠ home-branch ff: $CURRENT not fast-forwardable (ahead/diverged) — left untouched"
102
- ```
86
+ - **`result === 'skipped'`** (non-home branch or production mismatch): proceed silently.
87
+ - **`result === 'warn'`** (fast-forward attempt failed): surface the `warn` field as one line, then proceed silently.
88
+ - **`result === 'fast_forwarded'`** (home branch updated): proceed silently.
103
89
 
104
90
  Never rebase, reset, force-push, or stash. A non-fast-forwardable home branch is a signal to reconcile manually, not to overwrite.
105
91
 
@@ -107,31 +93,25 @@ Never rebase, reset, force-push, or stash. A non-fast-forwardable home branch is
107
93
 
108
94
  Check whether a newer `codebyplan` is published and safe to auto-install on this worktree's current branch, then run the local install + asset update. Runs after the Step 1.6 home-branch fast-forward (so any update lands on the freshest tree) and before Step 2. Fully non-blocking — the guard and skip branches proceed silently; any failure in the install/update commands warns once and continues to Step 2. session-end never halts.
109
95
 
110
- ```bash
111
- VERSION_JSON=$(npx codebyplan version-status 2>/dev/null)
112
- ```
113
-
114
- Parse `$VERSION_JSON` as JSON and branch on the result:
96
+ Run `codebyplan session freshness-gate` (WITHOUT `--halt-on-update`; session-end is continue-only) and parse the JSON output (`{ result: 'skipped'|'guarded'|'up_to_date'|'updated'|'error', ... }`):
115
97
 
116
- - **Probe failed / unparseable** — the command errored, produced no output, or the output cannot be parsed as JSON carrying the required keys (`newer`, `guarded`, `installCommand`). This includes an installed `codebyplan` too old to have the `version-status` subcommand (which prints `Unknown command`). → **FAIL-SAFE SKIP**: proceed silently to Step 2. A best-effort freshness probe must never disrupt session-end.
117
- - **`guarded: true`** (protected/main branch OR the canonical `codebyplan` source monorepo — any `guardReason`) → skip silently, proceed to Step 2. Gate on the `guarded` boolean only; never branch on the specific `guardReason` string. This is the guard that prevents clobbering the canonical monorepo's ahead-of-published `.claude/` and blocks any update or commit on a protected branch.
118
- - **`newer: false`** (already up to date) → skip silently, proceed to Step 2.
119
- - **`newer: true` AND `guarded: false`** → run the update path:
120
- 1. Run the JSON's `installCommand` (e.g. `pnpm add codebyplan@latest`) to LOCALLY install `codebyplan@latest` via the repo's package manager. Never a global `-g` install.
121
- 2. Run `npx codebyplan claude update` to refresh the worktree's `.claude/` assets on the current branch. If `installCommand` (step 1) or this command exits non-zero, warn once and skip to Step 2 — this path is non-blocking.
122
- 3. Detect changes across BOTH asset directories in one pass: `git status --porcelain -- .claude/ .codebyplan/`. If non-empty, present a single combined offer (same pattern as Step 1.5):
98
+ - **Probe failed** — the command errored or output cannot be parsed as JSON. → **FAIL-SAFE SKIP**: proceed silently to Step 2. A best-effort freshness probe must never disrupt session-end.
99
+ - **`result === 'skipped'` / `'guarded'` / `'up_to_date'`** → skip silently, proceed to Step 2. Gate on the `result` field only. This guard prevents clobbering the canonical monorepo's ahead-of-published `.claude/` and blocks any update or commit on a protected branch.
100
+ - **`result === 'error'`** fail-safe skip, proceed to Step 2.
101
+ - **`result === 'updated'`** → the CLI already ran the install + `npx codebyplan claude update`. Parse the JSON response:
102
+ - If `changed_files[]` is present and non-empty, present a single combined offer (same pattern as Step 1.5):
123
103
 
124
- ```
125
- codebyplan updated. Commit these changes before ending the session?
126
- [list of changed paths under .claude/ and .codebyplan/]
104
+ ```
105
+ codebyplan updated. Commit these changes before ending the session?
106
+ [list of changed paths under .claude/ and .codebyplan/]
127
107
 
128
- Reply: yes | no | select
129
- ```
108
+ Reply: yes | no | select
109
+ ```
130
110
 
131
- On `yes`: `git add` the listed paths only (not the whole directories), then trigger `/cbp-git-commit`. On `no`: skip. On `select`: ask which subset.
132
- 4. Continue to Step 2 — session-end does NOT halt. The update lands in the current worktree on its current branch; main/protected branches and the canonical source repo are already excluded by the `guarded` check above.
111
+ On `yes`: `git add` the listed paths only (not the whole directories), then trigger `/cbp-git-commit`. On `no`: skip. On `select`: ask which subset.
112
+ - Continue to Step 2 — session-end does NOT halt. The update lands in the current worktree on its current branch; main/protected branches and the canonical source repo are already excluded by the `result !== 'guarded'` check above.
133
113
 
134
- **Home branches are intentionally not guarded.** A worktree's folder-named home branch (e.g. `codebyplan-web`) is neither protected/main nor the canonical source, so `version-status` returns `guarded:false` there and this gate runs the update exactly as on a feat branch — landing it on whichever branch the worktree currently rests on (the deliberate "update the branch they're on, except main/canonical" behavior).
114
+ **Home branches are intentionally not guarded.** A worktree's folder-named home branch (e.g. `codebyplan-web`) is neither protected/main nor the canonical source, so the freshness gate returns `result !== 'guarded'` there and this gate runs the update exactly as on a feat branch — landing it on whichever branch the worktree currently rests on (the deliberate "update the branch they're on, except main/canonical" behavior).
135
115
 
136
116
  Non-blocking — session end proceeds regardless of outcome.
137
117
 
@@ -170,9 +150,9 @@ You can close this window.
170
150
  ## Integration
171
151
 
172
152
  - **Triggered by**: user invocation (prompted by `/cbp-todo` when no work remains)
173
- - **Reads**: `.codebyplan/repo.json`, `.codebyplan/git.json` (`branch_config.production` for the Step 1.6 home-branch fast-forward); local-first reads (with `npx codebyplan sync` + MCP break-glass): `.codebyplan/state/session/current.json` (Step 1 resolve log), `.codebyplan/state/todos.json` (Step 1.3 handoff snapshot + Step 1.5 active-task lookup), `.codebyplan/state/checkpoints/<id>/tasks/<id>/rounds/` (Step 1.5 task-file resolution); `npx codebyplan version-status` (Step 1.7 package-freshness gate)
153
+ - **Reads**: `.codebyplan/repo.json`, `.codebyplan/git.json` (`branch_config.production` for the Step 1.6 home-branch fast-forward); local-first reads (with `npx codebyplan sync` + MCP break-glass): `.codebyplan/state/session/current.json` (Step 1 resolve log), `.codebyplan/state/todos.json` (Step 1.3 handoff snapshot + Step 1.5 active-task lookup), `.codebyplan/state/checkpoints/<id>/tasks/<id>/rounds/` (Step 1.5 task-file resolution); `codebyplan session home-ff` (Step 1.6 home-branch fast-forward); `codebyplan session freshness-gate` (Step 1.7 package-freshness gate, without --halt-on-update); `codebyplan session infra-files --json --task-files <csv>` (Step 1.5 infra-file set math)
174
154
  - **Writes**: `codebyplan session update-log --id <id> ...` (Step 1 finalize — CLI write-through to `.codebyplan/state/session/current.json`; break-glass: MCP `update_session_log`), `codebyplan session create-log` (Step 1 fallback when no log exists; break-glass: MCP `create_session_log`), `codebyplan session update-state --action deactivate` (Step 3 — CLI write-through to `.codebyplan/state/session/state.json`; break-glass: MCP `update_session_state`)
175
155
  - **Spawns**: none
176
- - **Triggers**: none at the skill-contract level. Step 1.5 may invoke `/cbp-git-commit` inline on user approval; Step 1.7 may invoke `/cbp-git-commit` on the `newer:true AND guarded:false` update path (committing changed `.claude/` and `.codebyplan/` paths).
156
+ - **Triggers**: none at the skill-contract level. Step 1.5 may invoke `/cbp-git-commit` inline on user approval; Step 1.7 may invoke `/cbp-git-commit` on the `result === 'updated'` path (committing changed `.claude/` and `.codebyplan/` paths).
177
157
  - **Paired with**: `/cbp-session-start`
178
158
  - **Pairs with**: `/cbp-session-start` Step 4.5 (handoff payload shape + freshness-gate contract; specified inline — no separate rule file)
@@ -55,22 +55,11 @@ Pass `WORKTREE_ID` to MCP tools that support it. Null `WORKTREE_ID` means the (d
55
55
 
56
56
  Keep this worktree's **home branch** rooted at the freshest production tip — no prompt, fully non-blocking. Home branches are the folder-named placeholder branches each worktree rests on (e.g. `codebyplan-web`); `feat/*` branches are deliberately skipped — they integrate via `/cbp-merge-main` with QA, never an auto fast-forward.
57
57
 
58
- Resolve the production branch: read `.codebyplan/git.json` and take `branch_config.production` (fall back to `main` if the file or field is absent). Call this `$PRODUCTION`. Then compare the current branch against the worktree's folder name:
58
+ Run `codebyplan session home-ff` and parse the JSON output (`{ result: 'skipped'|'warn'|'fast_forwarded', reason?, warn? }`):
59
59
 
60
- ```bash
61
- FOLDER="$(basename "$(git rev-parse --show-toplevel)")"
62
- CURRENT="$(git rev-parse --abbrev-ref HEAD)"
63
- ```
64
-
65
- - **`$CURRENT` != `$FOLDER`** (a `feat/*` or any non-home branch): skip silently — no fetch, no output.
66
- - **`$CURRENT` == `$FOLDER`** (home branch): fast-forward to `origin/$PRODUCTION`, warning once per failure and continuing:
67
-
68
- ```bash
69
- git fetch origin "$PRODUCTION" 2>/dev/null \
70
- || echo "⚠ home-branch ff: fetch failed — staying on local $CURRENT"
71
- git merge --ff-only "origin/$PRODUCTION" 2>/dev/null \
72
- || echo "⚠ home-branch ff: $CURRENT not fast-forwardable (ahead/diverged) — left untouched"
73
- ```
60
+ - **`result === 'skipped'`** (non-home branch or production mismatch): proceed silently.
61
+ - **`result === 'warn'`** (fast-forward attempt failed): surface the `warn` field as one line, then proceed silently.
62
+ - **`result === 'fast_forwarded'`** (home branch updated): proceed silently.
74
63
 
75
64
  Never rebase, reset, force-push, or stash. A non-fast-forwardable home branch is a signal to reconcile manually, not to overwrite.
76
65
 
@@ -123,40 +112,38 @@ binary simply yields a zero count and no line.
123
112
 
124
113
  Check whether a newer `codebyplan` is published and safe to auto-install on this worktree's current branch. Runs AFTER the architecture-map drift check (Step 1.55) and BEFORE session activation (Step 3).
125
114
 
126
- ```bash
127
- VERSION_JSON=$(npx codebyplan version-status 2>/dev/null)
128
- # Populate the claude-status cache best-effort (pure cache population never gates session-start).
129
- npx codebyplan claude status --write-cache --quiet 2>/dev/null || true
130
- ```
115
+ Run `codebyplan session freshness-gate --halt-on-update` and parse the JSON output (`{ result: 'skipped'|'guarded'|'up_to_date'|'updated'|'error', ... }`):
116
+
117
+ - **Probe failed** — the command errored or output cannot be parsed as JSON. → **FAIL-SAFE SKIP**: proceed silently to Step 3. Never disrupt a session over a best-effort freshness probe — the MCP gate (Step 0) is the only vital gate.
118
+ - **`result === 'skipped'` / `'guarded'` / `'up_to_date'`** → skip silently, proceed to Step 3. Gate on the `result` field only.
119
+ - **`result === 'error'`** → fail-safe skip, proceed to Step 3.
120
+ - **`result === 'updated'`** → the CLI already ran the install + `npx codebyplan claude update`. Parse the JSON response:
121
+ - If `changed_files[]` is present and non-empty, offer the same commit gate as Step 5.7:
131
122
 
132
- Parse `$VERSION_JSON` as JSON and branch on the result:
123
+ ```
124
+ codebyplan updated. Commit the resulting .claude/ and .codebyplan/ changes before exiting?
125
+ [list of changed paths under .claude/ and .codebyplan/]
133
126
 
134
- - **Probe failed / unparseable** — the command errored, produced no output, or the output cannot be parsed as JSON carrying the required keys (`newer`, `guarded`, `installCommand`). This includes an installed `codebyplan` too old to have the `version-status` subcommand (which prints `Unknown command`). → **FAIL-SAFE SKIP**: proceed silently to Step 3. Never disrupt a session over a best-effort freshness probe — the MCP gate (Step 0) is the only vital gate.
135
- - **`guarded: true`** (protected/main branch OR the canonical `codebyplan` source monorepo — any `guardReason`) → skip silently, proceed to Step 3. Gate on the `guarded` boolean only; never branch on the specific `guardReason` string.
136
- - **`newer: false`** (already up to date) → skip silently, proceed to Step 3.
137
- - **`newer: true` AND `guarded: false`** → run the update path:
138
- 1. Run the JSON's `installCommand` (e.g. `pnpm add codebyplan@latest`) to LOCALLY install `codebyplan@latest` via the repo's package manager. Never a global `-g` install.
139
- 2. Run `npx codebyplan claude update` to refresh the worktree's `.claude/` assets on the current branch.
140
- 3. Detect changes across BOTH asset directories in one pass: `git status --porcelain -- .claude/ .codebyplan/`. If non-empty, offer the same commit gate as Step 5.7:
127
+ Reply: yes | no | select
128
+ ```
141
129
 
142
- ```
143
- codebyplan updated. Commit the resulting .claude/ and .codebyplan/ changes before exiting?
144
- [list of changed paths under .claude/ and .codebyplan/]
130
+ On `yes`: `git add` the listed paths only, then trigger `/cbp-git-commit`. On `no`: skip. On `select`: ask which subset.
131
+ - **HALT** do NOT proceed to Step 3. Print:
145
132
 
146
- Reply: yes | no | select
147
- ```
133
+ ```
134
+ ✓ codebyplan updated. Start a FRESH Claude Code session
135
+ (run /clear or open a new window) so the updated .claude/ takes effect.
136
+ ```
148
137
 
149
- On `yes`: `git add` the listed paths only, then trigger `/cbp-git-commit`. On `no`: skip. On `select`: ask which subset.
150
- 4. **HALT** — do NOT proceed to Step 3. Print:
138
+ On this update-and-halt path the session is NOT continued: `update_session_state(activate)` is NOT called, `create_session_log` is NOT called, and `/cbp-todo` is NOT triggered.
151
139
 
152
- ```
153
- ✓ codebyplan updated to {latest}. Start a FRESH Claude Code session
154
- (run /clear or open a new window) so the updated .claude/ takes effect.
155
- ```
140
+ **Home branches are intentionally not guarded.** A worktree's folder-named home branch (e.g. `codebyplan-web`) is neither protected/main nor the canonical source, so the freshness gate returns `result !== 'guarded'` there and this gate fires exactly as on a feat branch. That is deliberate — the update lands on whatever branch the worktree is currently resting on (the "update the branch they're on, except main/canonical" decision), not an accidental halt.
156
141
 
157
- On this update-and-halt path the session is NOT continued: `update_session_state(activate)` is NOT called, `create_session_log` is NOT called, and `/cbp-todo` is NOT triggered.
142
+ Populate the claude-status cache best-effort (pure cache population never gates session-start):
158
143
 
159
- **Home branches are intentionally not guarded.** A worktree's folder-named home branch (e.g. `codebyplan-web`) is neither protected/main nor the canonical source, so `version-status` returns `guarded:false` there and this gate fires exactly as on a feat branch. That is deliberate — the update lands on whatever branch the worktree is currently resting on (the "update the branch they're on, except main/canonical" decision), not an accidental halt.
144
+ ```bash
145
+ npx codebyplan claude status --write-cache --quiet 2>/dev/null || true
146
+ ```
160
147
 
161
148
  ### Step 1.7: LSP Binary Nudge
162
149
 
@@ -221,15 +208,12 @@ Probe the most-recent closed session log for a structured handoff payload (the h
221
208
 
222
209
  Clean the working tree of leftover infra before the session begins. Only commit files that are **not** part of an unfinished task.
223
210
 
224
- 1. `git status --porcelain` list all modified/untracked files. If empty → skip this step.
225
- 2. Resolve **task-related files** (leave these uncommitted):
211
+ 1. Resolve the active task's round files (local-first):
226
212
  - Read `.codebyplan/state/todos.json` (local-first) to identify the active task id. If missing/stale, run `npx codebyplan sync` once and re-read. Break-glass fallback: MCP `get_current_task(repo_id)` when the state dir is absent and sync fails.
227
- - If active task exists: read `.codebyplan/state/checkpoints/<checkpoint_id>/tasks/<task_id>/rounds/` directory and filter to rounds with status not in `completed` / `cancelled`. Break-glass fallback: MCP `get_rounds(task_id)`.
228
- - Collect `files[]` from those rounds `task_files` set
229
- - If no active task exists, `task_files` is empty
230
- 3. `infra_files = changed_files task_files`
231
- 4. **Re-run `git status --porcelain` immediately before showing the commit-prompt** (after Steps 0–5 have completed all their round-trips). Eliminates the race where prior-session staged files appear in the index only after network round-trips complete. Recompute `infra_files` from the fresh listing.
232
- 5. If `infra_files` is empty → skip. Otherwise present once:
213
+ - If active task exists: read `.codebyplan/state/checkpoints/<checkpoint_id>/tasks/<task_id>/rounds/` and filter to rounds with status not in `completed` / `cancelled`; collect their `files[]` → `task_files` set. Break-glass fallback: MCP `get_rounds(task_id)`.
214
+ - If no active task exists, `task_files` is empty.
215
+ 2. Run `codebyplan session infra-files --json --task-files "<csv>"` where `<csv>` is the comma-separated task files from step 1. Parse the JSON response (`{ infra_files: string[], task_files: string[], note?: string }`). The CLI re-runs `git status --porcelain` internally and applies the set-math deterministically — the race-safe recompute, reading the index after Steps 0–5 round-trips complete.
216
+ 3. If `infra_files` is empty skip. Otherwise present once:
233
217
 
234
218
  ```
235
219
  Commit these non-task files before starting session?
@@ -238,7 +222,7 @@ Clean the working tree of leftover infra before the session begins. Only commit
238
222
  Reply: yes | no | select
239
223
  ```
240
224
 
241
- 6. On `yes`: `git add` the listed files, then trigger `/cbp-git-commit` (it handles conventional message + commit).
225
+ 4. On `yes`: `git add` the listed files, then trigger `/cbp-git-commit` (it handles conventional message + commit).
242
226
  On `no`: skip. On `select`: ask which subset.
243
227
 
244
228
  Non-blocking — session start proceeds either way.
@@ -288,7 +272,7 @@ Three-branch gate using `owned_count` and `total_count` from Step 5.8:
288
272
 
289
273
  - **Triggered by**: user invocation, `/clear` recovery
290
274
  - **Resolves**: `npx codebyplan resolve-worktree --json` (worktree id + distress signal; non-tuple-miss distress is non-blocking at session-start)
291
- - **Reads**: `.codebyplan/repo.json`, `.codebyplan/git.json` (`branch_config.production` for the Step 1.4 home-branch fast-forward), MCP `health_check` (Step 0 hard gate — stays MCP unconditionally); local-first reads (with `npx codebyplan sync` + MCP break-glass): `.codebyplan/state/session/current.json` (Step 4 previous log + Step 4.5 handoff probe), `.codebyplan/state/checkpoints/<id>.json` / `tasks/<id>.json` / `rounds/<id>.json` (Step 4.5 freshness probe), `.codebyplan/state/todos.json` (Step 5.7 active-task lookup); MCP `get_checkpoints({ repo_id, status: 'active' })` (Step 5.8 ownership partition — MCP only, no local mirror for active-filter query); `scripts/infra-drift.mjs` + a best-effort `git fetch` (Step 1.5 monorepo drift); `npx codebyplan arch-map drift` + `.codebyplan/architecture.json` presence (Step 1.55 architecture-map drift nudge, non-blocking); `npx codebyplan version-status` (Step 1.6 package-freshness gate); `npx codebyplan lsp --check` (Step 1.7 LSP binary nudge — reads `.codebyplan/lsp.json`, non-blocking). Reads at Step 3 and later do NOT fire on a Step 0 MCP hard-fail or the Step 1.6 update-and-halt path
275
+ - **Reads**: `.codebyplan/repo.json`, `.codebyplan/git.json` (`branch_config.production` for the Step 1.4 home-branch fast-forward), MCP `health_check` (Step 0 hard gate — stays MCP unconditionally); local-first reads (with `npx codebyplan sync` + MCP break-glass): `.codebyplan/state/session/current.json` (Step 4 previous log + Step 4.5 handoff probe), `.codebyplan/state/checkpoints/<id>.json` / `tasks/<id>.json` / `rounds/<id>.json` (Step 4.5 freshness probe), `.codebyplan/state/todos.json` (Step 5.7 active-task lookup); MCP `get_checkpoints({ repo_id, status: 'active' })` (Step 5.8 ownership partition — MCP only, no local mirror for active-filter query); `scripts/infra-drift.mjs` + a best-effort `git fetch` (Step 1.5 monorepo drift); `npx codebyplan arch-map drift` + `.codebyplan/architecture.json` presence (Step 1.55 architecture-map drift nudge, non-blocking); `codebyplan session home-ff` (Step 1.4 home-branch fast-forward); `codebyplan session freshness-gate --halt-on-update` (Step 1.6 package-freshness gate); `codebyplan session infra-files --json --task-files <csv>` (Step 5.7 infra-file set math); `npx codebyplan lsp --check` (Step 1.7 LSP binary nudge — reads `.codebyplan/lsp.json`, non-blocking). Reads at Step 3 and later do NOT fire on a Step 0 MCP hard-fail or the Step 1.6 update-and-halt path
292
276
  - **Writes**: `codebyplan session create-log` (Step 5 — CLI write-through; break-glass: MCP `create_session_log`), `codebyplan session update-state --action activate` (Step 3 — CLI write-through to `.codebyplan/state/session/state.json`; break-glass: MCP `update_session_state`) — both SKIPPED on a Step 0 MCP hard-fail and on the Step 1.6 update-and-halt path
293
277
  - **Spawns**: none
294
278
  - **Triggers**: `/cbp-git-commit` (conditional, on user approval at Step 5.7 or the Step 1.6 update path), `handoff.command` (on fresh handoff hit at Step 4.5), `/cbp-todo` (auto fall-through when owned_count >= 1 or total_count === 0; STOPS with no trigger when total_count >= 1 AND owned_count === 0; NOT triggered on a Step 0 hard-fail or the Step 1.6 update-and-halt path)
@@ -66,9 +66,7 @@ Compute `TARGET`:
66
66
  | Task type | TARGET |
67
67
  |-----------|--------|
68
68
  | Standalone, `task.branch_name` set | `task.branch_name` |
69
- | Standalone, `branch_name` null | `feat/standalone-TASK-{N}-{kebab-slug-from-task-title}` |
70
-
71
- Slug rules: lowercase, words joined by `-`, drop punctuation, truncate to 40 chars.
69
+ | Standalone, `branch_name` null | `feat/standalone-TASK-{N}-{slug}` where slug is computed via `codebyplan slug "{task title}"` |
72
70
 
73
71
  #### 3.2 — Compare current branch
74
72
 
@@ -76,26 +74,19 @@ Run `git branch --show-current`. If `current == TARGET` → continue to Step 3b.
76
74
 
77
75
  #### 3.3 — Switch automatically (no AskUserQuestion, no blocking)
78
76
 
77
+ Run the branch checkout via the deterministic CLI:
78
+
79
79
  ```bash
80
- # (a) target exists locally
81
- if git rev-parse --verify "$TARGET" >/dev/null 2>&1; then
82
- git checkout "$TARGET"
83
-
84
- # (b) target exists on origin (track it)
85
- elif git rev-parse --verify "origin/$TARGET" >/dev/null 2>&1; then
86
- git checkout -t "origin/$TARGET"
87
-
88
- # (c) target doesn't exist — create from production branch
89
- else
90
- git fetch origin "$PRODUCTION" 2>/dev/null || true
91
- git checkout -b "$TARGET" "origin/$PRODUCTION" 2>/dev/null \
92
- || git checkout -b "$TARGET" "$PRODUCTION"
93
- fi
80
+ RESULT=$(codebyplan branch checkout --target "$TARGET" --production "$PRODUCTION")
81
+ # Parse JSON: { action: "checked_out_local" | "checked_out_tracking" | "created_from_production" | "error", branch, previous?, error? }
82
+ ACTION=$(echo "$RESULT" | jq -r '.action')
94
83
  ```
95
84
 
96
- No `git stash`, ever (per `git-safety.md`). No `git add`, ever (per `git-workflow.md`).
85
+ **Interpreting the result:**
86
+ - `action === "error"`: surface the raw `error` field verbatim and stop.
87
+ - Otherwise (all success actions): branch is now `TARGET`. Continue to Step 3.4.
97
88
 
98
- If `git checkout` exits non-zero (would clobber), surface the raw git error verbatim and stop.
89
+ No `git stash`, ever (per `git-safety.md`). No `git add`, ever (per `git-workflow.md`).
99
90
 
100
91
  #### 3.4 — Persist + verify
101
92
 
@@ -3,7 +3,7 @@ scope: org-shared
3
3
  name: cbp-supabase-migrate
4
4
  description: Scaffold or adopt a Supabase migration for the current PR branch, apply to the branch's preview DB, run advisor checks, regenerate TypeScript types. Includes a fresh-branch dry-run pre-flight that uses one persistent dry-run ephemeral branch per feature branch.
5
5
  argument-hint: "[--new <name> | <path-to-sql>]"
6
- allowed-tools: Read, Edit, Write, Bash(git *), Bash(supabase *), Bash(jq *), Bash(date *), Bash(which *), Bash(cp *), Bash(mv *), Bash(test *), Bash(ls *), Bash(npx codebyplan checkpoint update *), Bash(npx codebyplan task update *), mcp__supabase__apply_migration, mcp__supabase__list_migrations, mcp__supabase__get_advisors, mcp__supabase__generate_typescript_types, mcp__supabase__reset_branch, mcp__supabase__list_branches, mcp__supabase__create_branch, mcp__supabase__get_cost, mcp__supabase__confirm_cost
6
+ allowed-tools: Read, Edit, Write, Bash(codebyplan supabase *), Bash(npx codebyplan supabase *), Bash(npx codebyplan checkpoint update *), Bash(npx codebyplan task update *), Bash(git *), Bash(supabase *), Bash(jq *), Bash(date *), Bash(which *), Bash(cp *), Bash(mv *), Bash(test *), Bash(ls *), mcp__supabase__apply_migration, mcp__supabase__list_migrations, mcp__supabase__get_advisors, mcp__supabase__generate_typescript_types, mcp__supabase__reset_branch, mcp__supabase__list_branches, mcp__supabase__create_branch, mcp__supabase__get_cost, mcp__supabase__confirm_cost, mcp__codebyplan__update_checkpoint, mcp__codebyplan__update_task
7
7
  effort: xhigh
8
8
  ---
9
9
 
@@ -99,16 +99,21 @@ skip.
99
99
 
100
100
  ### Guard 2 — DB-path / migration intent
101
101
 
102
- Reuse the db_paths detection so the step proceeds only when this invocation actually touches
103
- the database. Read the globs (default `supabase/**`, `apps/backend/**`, `packages/**/db/**`):
102
+ Delegate the db_paths config + branch diff to the CLI (replaces the former `db_paths` jq +
103
+ `git diff` bash), and capture the parent project ref for the MCP calls below:
104
104
 
105
105
  ```bash
106
- DB_PATHS=$(jq -r '.shipment.surfaces.supabase.db_paths[]? // empty' .codebyplan/shipment.json 2>/dev/null)
107
- [ -z "$DB_PATHS" ] && DB_PATHS=$(printf '%s\n' 'supabase/**' 'apps/backend/**' 'packages/**/db/**')
106
+ codebyplan supabase resolve-preview
108
107
  ```
109
108
 
109
+ Parse its JSON `{ parent_ref, project_ref, db_changed, base }`. Capture `PARENT_REF=parent_ref`
110
+ (read from `.codebyplan/shipment.json` `.shipment.surfaces.supabase.project_ref` — used by the
111
+ list/create calls below, never a hardcoded literal). `db_changed` reflects the branch diff
112
+ against `origin/<base>` matched against the configured db_paths globs (default `supabase/**`,
113
+ `apps/backend/**`, `packages/**/db/**`).
114
+
110
115
  Proceed when **either**:
111
- - the branch diff against `origin/$PRODUCTION` touches any `DB_PATHS` glob, **or**
116
+ - `db_changed === true`, **or**
112
117
  - this invocation will create/adopt a migration — `$ARGUMENTS` is `--new <name>` or a `.sql`
113
118
  path (the migration file may not exist on disk yet, so explicit migrate intent counts as a
114
119
  DB change). The bare-picker case (Step 5 Case C) is treated as migrate intent.
@@ -125,11 +130,12 @@ already provisioned the branch. Capture it as `PREVIEW_PROJECT_REF` and jump str
125
130
 
126
131
  ### Idempotency check (list before create)
127
132
 
128
- Call `mcp__supabase__list_branches` with the parent `project_id` `rrvtrumtkhrsbhcyrwvf`. The
129
- parent `project_id` for MCP calls is the same ref string stored in `.codebyplan/shipment.json`
130
- `surfaces.supabase.project_ref` — Supabase uses that one ref as both the project identifier
131
- and the branch target. Scan the returned list for an entry whose `name` exactly equals
132
- `$BRANCH` (slashes included — `feat/CHK-144-...` is a valid Supabase branch name).
133
+ Call `mcp__supabase__list_branches` with the parent `project_id` `$PARENT_REF` (resolved by
134
+ `codebyplan supabase resolve-preview` in Guard 2 from `.codebyplan/shipment.json`
135
+ `.shipment.surfaces.supabase.project_ref` — never a hardcoded literal). Supabase uses that one
136
+ ref as both the project identifier and the branch target. Scan the returned list for an entry
137
+ whose `name` exactly equals `$BRANCH` (slashes included — `feat/CHK-144-...` is a valid
138
+ Supabase branch name).
133
139
 
134
140
  - **Match found**: capture its `project_ref` as `PREVIEW_PROJECT_REF`. Skip creation; proceed
135
141
  to "Record connection". This is the idempotent reuse path (covers a branch the GitHub
@@ -148,7 +154,7 @@ Cost confirmation is mandatory before creating a branch — never bypass it:
148
154
  stays empty.
149
155
 
150
156
  After cost is confirmed, call `mcp__supabase__create_branch`:
151
- - `project_id`: `rrvtrumtkhrsbhcyrwvf` (parent project ref)
157
+ - `project_id`: `$PARENT_REF` (parent project ref from `codebyplan supabase resolve-preview`)
152
158
  - `name`: `$BRANCH` (verbatim — slashes included)
153
159
  - `confirm_cost_id`: the same id from the `get_cost` response that was passed to `confirm_cost`
154
160
 
@@ -276,25 +282,16 @@ Parse `$ARGUMENTS`:
276
282
 
277
283
  ### Case A: `--new <name>`
278
284
 
279
- Generate a timestamp strictly greater than the latest filename in `supabase/migrations/`:
280
-
281
- ```bash
282
- ls supabase/migrations/ | sort | tail -1
283
- ```
284
-
285
- Extract the numeric prefix from the `tail -1` output (e.g. `20260520000003` from `20260520000003_chk116_security_and_logging_fixes.sql`). Your `date -u +%Y%m%d%H%M%S` output must be strictly greater than that prefix. If two scaffolds happen in the same UTC second, append `_01`, `_02` to maintain monotonicity. Use the current wall-clock UTC time
286
- via:
285
+ Scaffold the migration file with a guaranteed-monotonic timestamp via the CLI (replaces the
286
+ former `ls | tail` + `date` + `touch` bash):
287
287
 
288
288
  ```bash
289
- date -u +%Y%m%d%H%M%S
289
+ codebyplan supabase new-migration "<name>"
290
290
  ```
291
291
 
292
- Create the file:
293
-
294
- ```bash
295
- TIMESTAMP=$(date -u +%Y%m%d%H%M%S)
296
- touch "supabase/migrations/${TIMESTAMP}_<name>.sql"
297
- ```
292
+ Parse its JSON `{ path }`. The CLI creates an empty `supabase/migrations/<timestamp>_<slug>.sql`
293
+ whose timestamp is strictly greater than the latest existing migration's, appending `_01`/`_02`
294
+ on a same-second collision. Record the returned path as `MIGRATION_PATH`.
298
295
 
299
296
  Open the file for editing — write the migration SQL now. Include both the intended change
300
297
  and a comment block at the top: