codebyplan 1.13.44 → 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 (41) hide show
  1. package/dist/cli.js +5038 -1551
  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/hooks/cbp-auto-test-hooks.sh +1 -0
  6. package/templates/hooks/cbp-e2e-spec-patterns.sh +100 -0
  7. package/templates/hooks/cbp-lint-format-on-edit.sh +1 -0
  8. package/templates/hooks/cbp-maestro-yaml-validate.sh +1 -0
  9. package/templates/hooks/cbp-pre-commit-quality-gate.sh +1 -0
  10. package/templates/hooks/cbp-statusline.sh +0 -0
  11. package/templates/hooks/cbp-subagent-statusline.sh +0 -0
  12. package/templates/hooks/cbp-test-coverage-gate.sh +1 -0
  13. package/templates/hooks/cbp-test-hooks.sh +1 -0
  14. package/templates/hooks/hooks.json +4 -0
  15. package/templates/hooks/verify-parity.sh +20 -0
  16. package/templates/rules/parallel-waves.md +8 -3
  17. package/templates/rules/scope-vocabulary.md +4 -3
  18. package/templates/settings.project.base.json +22 -0
  19. package/templates/skills/cbp-build-cc-claude-file/SKILL.md +11 -1
  20. package/templates/skills/cbp-build-cc-claude-file/scripts/validate-claude-file.sh +72 -0
  21. package/templates/skills/cbp-build-cc-mode/SKILL.md +12 -16
  22. package/templates/skills/cbp-build-cc-rule/SKILL.md +11 -1
  23. package/templates/skills/cbp-build-cc-rule/scripts/validate-rule.sh +69 -0
  24. package/templates/skills/cbp-build-cc-settings/SKILL.md +2 -2
  25. package/templates/skills/cbp-build-cc-settings/scripts/validate-settings.sh +67 -0
  26. package/templates/skills/cbp-checkpoint-create/SKILL.md +12 -4
  27. package/templates/skills/cbp-checkpoint-end/SKILL.md +19 -11
  28. package/templates/skills/cbp-git-commit/SKILL.md +10 -12
  29. package/templates/skills/cbp-git-worktree-create/SKILL.md +7 -48
  30. package/templates/skills/cbp-git-worktree-remove/SKILL.md +23 -40
  31. package/templates/skills/cbp-map-architecture/SKILL.md +1 -0
  32. package/templates/skills/cbp-merge-main/SKILL.md +21 -26
  33. package/templates/skills/cbp-refresh-arch-map/SKILL.md +1 -0
  34. package/templates/skills/cbp-round-check/SKILL.md +37 -36
  35. package/templates/skills/cbp-round-execute/SKILL.md +9 -3
  36. package/templates/skills/cbp-session-end/SKILL.md +27 -47
  37. package/templates/skills/cbp-session-start/SKILL.md +35 -51
  38. package/templates/skills/cbp-standalone-task-start/SKILL.md +10 -19
  39. package/templates/skills/cbp-supabase-migrate/SKILL.md +24 -27
  40. package/templates/skills/cbp-task-start/SKILL.md +9 -21
  41. package/templates/skills/cbp-task-testing/SKILL.md +18 -10
@@ -88,14 +88,22 @@ This is the first identity-stamping point — when claiming, passing `worktree_i
88
88
 
89
89
  Read `.codebyplan/git.json` `branch_config.production` (default `"main"`) as `BASE`. codebyplan repos are main-only — never create or branch from a `development`/integration branch.
90
90
 
91
+ Compute the slug deterministically:
92
+
93
+ ```bash
94
+ SLUG=$(codebyplan slug "{checkpoint title}")
95
+ ```
96
+
97
+ Then create and push the branch:
98
+
91
99
  ```bash
92
100
  git fetch origin "$BASE" 2>/dev/null || true
93
- git checkout -b "feat/CHK-{NNN}-{slug}" "origin/$BASE" 2>/dev/null \
94
- || git checkout -b "feat/CHK-{NNN}-{slug}" "$BASE"
95
- git push -u origin "feat/CHK-{NNN}-{slug}"
101
+ git checkout -b "feat/CHK-{NNN}-$SLUG" "origin/$BASE" 2>/dev/null \
102
+ || git checkout -b "feat/CHK-{NNN}-$SLUG" "$BASE"
103
+ git push -u origin "feat/CHK-{NNN}-$SLUG"
96
104
  ```
97
105
 
98
- Slug: lowercase, dash-joined, punctuation dropped, ≤40 chars. Persist the branch via `codebyplan checkpoint update --id <checkpoint-id> --branch-name "feat/CHK-{NNN}-{slug}"` (CLI write-through; break-glass: MCP `update_checkpoint`). (The dedicated `/cbp-git-branch-feat-create` skill is the canonical config-driven helper if you prefer to delegate.)
106
+ Persist the branch via `codebyplan checkpoint update --id <checkpoint-id> --branch-name "feat/CHK-{NNN}-$SLUG"` (CLI write-through; break-glass: MCP `update_checkpoint`). (The dedicated `/cbp-git-branch-feat-create` skill is the canonical config-driven helper if you prefer to delegate.)
99
107
 
100
108
  **Note — Supabase preview branch**: no Supabase branch is created here. Creation is lazy — it happens on the first DB change when `/cbp-supabase-migrate` runs on this feat branch, which provisions a Supabase branch named identically to the git branch. See `cbp-supabase-migrate` Step 2.3 for the creation protocol.
101
109
 
@@ -166,12 +166,19 @@ Only after both the local and remote git delete above succeed, run a conditional
166
166
 
167
167
  > Lifecycle contract: see [[supabase-branch-lifecycle]].
168
168
 
169
- - Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
170
- - Scan the returned list for an entry whose `name` exactly equals `$BRANCH`.
171
- - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Record the branch name in `SUPABASE_BRANCHES_DELETED[]`.
172
- - If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
173
- - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
174
- - Never delete the parent project `rrvtrumtkhrsbhcyrwvf` itself or any persistent/production branch.
169
+ - Resolve the parent project ref and apply the lifecycle guard in one deterministic call:
170
+
171
+ ```bash
172
+ codebyplan supabase teardown-preview "$BRANCH"
173
+ ```
174
+
175
+ Parse its JSON `{ status, parent_ref, project_ref, reason }`. The command never deletes anything — it reads the parent ref from `.codebyplan/shipment.json` (`.shipment.surfaces.supabase.project_ref`) and applies the protected / production / parent-ref guard from [[supabase-branch-lifecycle]].
176
+ - If `status === "rejected"`: STOP the teardown for this branch and surface `reason` — never delete a production / protected / integration branch or one whose preview ref equals the parent.
177
+ - Otherwise (`allowed` or `not_found`), use `parent_ref` for the live existence check — `mcp__supabase__list_branches` with `project_id: <parent_ref>`, then scan for an entry whose `name` exactly equals `$BRANCH`:
178
+ - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Record the branch name in `SUPABASE_BRANCHES_DELETED[]`.
179
+ - If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
180
+ - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
181
+ - Never delete the parent project (`parent_ref` from `codebyplan supabase teardown-preview`) itself or any persistent/production branch — the `teardown-preview` guard enforces this.
175
182
 
176
183
  Accumulate all Supabase branch names removed across the loop in `SUPABASE_BRANCHES_DELETED`.
177
184
 
@@ -198,11 +205,12 @@ git push origin --delete "$FEAT_BRANCH"
198
205
 
199
206
  After the feat branch git delete, run the same conditional Supabase teardown for `$FEAT_BRANCH`:
200
207
 
201
- - Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
202
- - Scan for an entry whose `name` exactly equals `$FEAT_BRANCH`.
203
- - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Add `$FEAT_BRANCH` to `SUPABASE_BRANCHES_DELETED[]`.
204
- - If not found: no-op silently idempotent, not-found is success.
205
- - If the `list_branches` call itself fails (network, auth, or a non-success responsedistinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$FEAT_BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
208
+ - Run `codebyplan supabase teardown-preview "$FEAT_BRANCH"` and parse its JSON `{ status, parent_ref, project_ref, reason }` (reads the parent ref from `.codebyplan/shipment.json`, applies the lifecycle guard, never deletes).
209
+ - If `status === "rejected"`: STOP the teardown and surface `reason` never delete a production / protected / integration branch or one whose preview ref equals the parent.
210
+ - Otherwise (`allowed` or `not_found`), use `parent_ref` for the live existence check — `mcp__supabase__list_branches` with `project_id: <parent_ref>`, then scan for an entry whose `name` exactly equals `$FEAT_BRANCH`:
211
+ - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Add `$FEAT_BRANCH` to `SUPABASE_BRANCHES_DELETED[]`.
212
+ - If not found: no-op silentlyidempotent, not-found is success.
213
+ - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$FEAT_BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
206
214
 
207
215
  ### Step 10: Save Shipment Results and Summary
208
216
 
@@ -108,21 +108,19 @@ REPO_PATH="$(git rev-parse --show-toplevel)"
108
108
 
109
109
  **If `--files` provided:** Use the manual file list.
110
110
 
111
- **If `--scope-task`:** Resolve via local state + intersection.
111
+ **If `--scope-task`:** Resolve via the deterministic CLI.
112
+
113
+ First, read `task.files_changed[].path` (local-first from `.codebyplan/state/checkpoints/*/tasks/*.json`; on miss run `npx codebyplan sync` once, then re-read; MCP `get_current_task` as documented break-glass when the state dir is absent and sync fails) and format as a CSV string. Then compute the intersection:
112
114
 
113
115
  ```bash
114
- # 1. Read task.files_changed[] from local state (glob for active task file)
115
- # On miss: npx codebyplan sync once, then re-read.
116
- # MCP get_current_task as documented break-glass when state dir absent + sync fails.
117
- task_paths=$(cat .codebyplan/state/checkpoints/*/tasks/*.json 2>/dev/null \
118
- | jq -r 'select(.status=="in_progress") | .files_changed[].path' | head -n 200)
119
- # 2. Read staged paths
120
- staged_paths=$(git diff --cached --name-only)
121
- # 3. Compute intersection
122
- intersect=$(comm -12 <(echo "$task_paths" | sort) <(echo "$staged_paths" | sort))
116
+ TASK_FILES_CSV="path1,path2,path3" # CSV string of task.files_changed[].path (local-first)
117
+ SCOPE_RESULT=$(codebyplan commit --scope-task --task-files "$TASK_FILES_CSV")
118
+ # Parse JSON: { files: string[], count: number }
119
+ FILES=$(echo "$SCOPE_RESULT" | jq -r '.files[]')
120
+ COUNT=$(echo "$SCOPE_RESULT" | jq -r '.count')
123
121
  ```
124
122
 
125
- If `intersect` is empty: emit error and STOP.
123
+ If `COUNT === 0` (empty `files[]`): emit error and STOP.
126
124
 
127
125
  ```
128
126
  ## Error: No Task Files Staged
@@ -135,7 +133,7 @@ Options:
135
133
  - Or use --all to commit the foreign-staged files instead.
136
134
  ```
137
135
 
138
- If non-empty: use `intersect` as the file list for Step 5.
136
+ If `COUNT > 0`: use `FILES` as the file list for Step 5.
139
137
 
140
138
  **If `--task`, `--all`, or no scope:** No filtering — all staged files committed.
141
139
 
@@ -159,59 +159,18 @@ If the main repo has no `.codebyplan/e2e.env` yet, provision it after setup by r
159
159
  cd "$WORKTREE_PATH" && git push -u origin "$BRANCH_NAME"
160
160
  ```
161
161
 
162
- ### Step 9: Register Worktree in CodeByPlan
162
+ ### Step 9: Register Worktree and Write `.codebyplan/` Config
163
163
 
164
- Get the repo ID from CLAUDE.md (`Repo ID` in Key References table).
164
+ Run `codebyplan worktree create "$BRANCH_NAME" --path "$WORKTREE_PATH"` and parse the JSON output (`{ worktree_files_written: boolean, mcp_registered: boolean, worktree_id?, warn? }`):
165
165
 
166
- Use MCP `create_worktree` to register the worktree in the CodeByPlan database:
166
+ - If `warn` is present: surface it as a non-blocking warning.
167
+ - Save the returned `worktree_id` for reference (if present).
167
168
 
168
- ```
169
- MCP create_worktree:
170
- repo_id: [repo-id from CLAUDE.md]
171
- name: $BRANCH_NAME
172
- path: $WORKTREE_PATH
173
- status: "active"
174
- ```
175
-
176
- Save the returned `worktree_id` for reference.
177
-
178
- ### Step 10: Write `.codebyplan/` directory
179
-
180
- Create the `.codebyplan/` directory in the worktree root and write per-concern config stubs:
181
-
182
- ```bash
183
- mkdir -p "$WORKTREE_PATH/.codebyplan"
184
- ```
185
-
186
- Write `.codebyplan/repo.json` with the correct `repo_id`:
187
-
188
- ```json
189
- {
190
- "repo_id": "[repo-id from CLAUDE.md]"
191
- }
192
- ```
193
-
194
- Write stubs for the other per-concern files (populated by `npx codebyplan sync` on first run):
195
-
196
- ```bash
197
- # .codebyplan/server.json — server port and type config
198
- echo '{}' > "$WORKTREE_PATH/.codebyplan/server.json"
199
-
200
- # .codebyplan/git.json — branch config
201
- echo '{}' > "$WORKTREE_PATH/.codebyplan/git.json"
202
-
203
- # .codebyplan/shipment.json — surface shipment config
204
- echo '{}' > "$WORKTREE_PATH/.codebyplan/shipment.json"
205
-
206
- # .codebyplan/vendor.json — vendor docs path
207
- echo '{}' > "$WORKTREE_PATH/.codebyplan/vendor.json"
208
- ```
209
-
210
- The `.codebyplan/device.local.json` file is created by `npx codebyplan setup` on the device (gitignored). The `worktree_id` is never COMMITTED; it may be cached per-device in the gitignored `.codebyplan/worktree.local.json` (branch-keyed, re-derivable via `codebyplan resolve-worktree --cache`), otherwise resolved at runtime from the `(device_id, repo path, branch)` tuple via `npx codebyplan resolve-worktree`.
169
+ The CLI atomically writes the `.codebyplan/` directory with per-concern config stubs and registers the worktree in the CodeByPlan database. The `.codebyplan/device.local.json` file is created by `npx codebyplan setup` on the device (gitignored). The `worktree_id` is never COMMITTED; it may be cached per-device in the gitignored `.codebyplan/worktree.local.json` (branch-keyed, re-derivable via `codebyplan resolve-worktree --cache`), otherwise resolved at runtime from the `(device_id, repo path, branch)` tuple via `npx codebyplan resolve-worktree`.
211
170
 
212
171
  No need to mark as `skip-worktree` — the committed files are merge-safe per CHK-108 and CHK-120.
213
172
 
214
- ### Step 11: Show Result
173
+ ### Step 10: Show Result
215
174
 
216
175
  ```
217
176
  ## Worktree Created
@@ -238,4 +197,4 @@ No need to mark as `skip-worktree` — the committed files are merge-safe per CH
238
197
  ## Integration
239
198
 
240
199
  - **Related**: `/cbp-git-worktree-remove` (cleanup and deregister)
241
- - **MCP tools**: `create_worktree`
200
+ - **CLI**: `codebyplan worktree create <name> --path <abs>` (Step 9 — writes `.codebyplan/` config and registers worktree)
@@ -49,31 +49,17 @@ Set:
49
49
  - `WORKTREE_PATH` = resolved path
50
50
  - `BRANCH_NAME` = branch checked out in that worktree
51
51
 
52
- ### Step 4: Look Up Worktree in CodeByPlan
52
+ ### Step 4: Look Up and Deregister Worktree in CodeByPlan
53
53
 
54
- Get the repo ID from CLAUDE.md (`Repo ID` in Key References table).
54
+ Run `codebyplan worktree remove "$BRANCH_NAME"` and parse the JSON output (`{ mcp_deregistered: boolean, warn? }`):
55
55
 
56
- Use MCP `get_worktrees` to find the worktree record:
57
-
58
- ```
59
- MCP get_worktrees:
60
- repo_id: [repo-id from CLAUDE.md]
61
- status: "active"
62
- ```
63
-
64
- Match by `name` = `$BRANCH_NAME` or `path` = `$WORKTREE_PATH`.
65
-
66
- Set `WORKTREE_ID` = matched worktree's `id`.
67
-
68
- If not found in CodeByPlan, note it and continue with local removal.
56
+ - If `warn` is present: surface it as a non-blocking warning. The worktree was not found in CodeByPlan or deregistration failed, but local removal will proceed.
57
+ - If `mcp_deregistered === true`: worktree was successfully deregistered.
58
+ - If `mcp_deregistered === false`: worktree was not registered or deregistration failed; continue with local removal.
69
59
 
70
60
  ### Step 5: Check for Assigned Checkpoints
71
61
 
72
- If `WORKTREE_ID` was found, warn if any checkpoints are assigned to this worktree:
73
-
74
- ```
75
- ⚠ This worktree has [N] checkpoint(s) assigned. They will become unassigned.
76
- ```
62
+ If the worktree was successfully deregistered in Step 4 (`mcp_deregistered === true`), any checkpoints that were assigned to it will become unassigned. This is expected behavior and requires no additional action.
77
63
 
78
64
  ### Step 6: Confirm with User
79
65
 
@@ -92,16 +78,7 @@ Ask:
92
78
  2. Remove worktree and delete branch
93
79
  3. Cancel
94
80
 
95
- ### Step 7: Deregister from CodeByPlan
96
-
97
- If `WORKTREE_ID` was found, delete the worktree record:
98
-
99
- ```
100
- MCP delete_worktree:
101
- worktree_id: [worktree-id]
102
- ```
103
-
104
- ### Step 8: Remove Git Worktree
81
+ ### Step 7: Remove Git Worktree
105
82
 
106
83
  ```bash
107
84
  git worktree remove "$WORKTREE_PATH"
@@ -114,7 +91,7 @@ git worktree remove --force "$WORKTREE_PATH"
114
91
 
115
92
  Only use `--force` if the user confirms.
116
93
 
117
- ### Step 9: Delete Branch (if requested)
94
+ ### Step 8: Delete Branch (if requested)
118
95
 
119
96
  **Protected branch check:** Read the protected set from `.codebyplan/git.json`:
120
97
  ```bash
@@ -135,15 +112,21 @@ After the git branch delete succeeds, run a conditional Supabase preview-branch
135
112
 
136
113
  > Lifecycle contract: see [[supabase-branch-lifecycle]].
137
114
 
138
- - Resolve the parent project ref: read `.codebyplan/shipment.json` `.shipment.surfaces.supabase.project_ref`; if absent or empty, read the first line of `supabase/.temp/project-ref`. Use that resolved ref as the `project_id`.
139
- - Call `mcp__supabase__list_branches` with the resolved `project_id`.
140
- - Scan the returned list for an entry whose `name` exactly equals `$BRANCH_NAME`.
141
- - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Report "Supabase preview branch deleted: `$BRANCH_NAME`".
142
- - If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
143
- - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH_NAME` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
144
- - Never delete the branch where `is_default` is true in the `list_branches` response (the production/parent project branch) or any other persistent/long-lived branch.
115
+ - Resolve the parent project ref and apply the lifecycle guard in one deterministic call:
116
+
117
+ ```bash
118
+ codebyplan supabase teardown-preview "$BRANCH_NAME"
119
+ ```
120
+
121
+ Parse its JSON `{ status, parent_ref, project_ref, reason }`. The command never deletes anything — it reads the parent ref from `.codebyplan/shipment.json` (`.shipment.surfaces.supabase.project_ref`) and applies the protected / production / parent-ref guard from [[supabase-branch-lifecycle]].
122
+ - If `status === "rejected"`: STOP the teardown and surface `reason` — never delete a production / protected / integration branch or one whose preview ref equals the parent.
123
+ - Otherwise (`allowed` or `not_found`), use `parent_ref` for the live existence check — `mcp__supabase__list_branches` with `project_id: <parent_ref>`, then scan for an entry whose `name` exactly equals `$BRANCH_NAME`:
124
+ - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Report "Supabase preview branch deleted: `$BRANCH_NAME`".
125
+ - If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
126
+ - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH_NAME` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
127
+ - Never delete the parent project (`parent_ref` from `codebyplan supabase teardown-preview`) itself or any persistent/production branch — the `teardown-preview` guard enforces this.
145
128
 
146
- ### Step 10: Show Result
129
+ ### Step 9: Show Result
147
130
 
148
131
  ```
149
132
  ## Worktree Removed
@@ -159,4 +142,4 @@ After the git branch delete succeeds, run a conditional Supabase preview-branch
159
142
  ## Integration
160
143
 
161
144
  - **Related**: `/cbp-git-worktree-create` (create and register)
162
- - **MCP tools**: `get_worktrees`, `delete_worktree`
145
+ - **CLI**: `codebyplan worktree remove <name>` (Step 4 — deregister from CodeByPlan)
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  scope: org-shared
3
3
  name: cbp-map-architecture
4
+ effort: xhigh
4
5
  description: Orchestrate architecture map generation for one or all modules. Spawns the cbp-map-architecture agent per module, writes per-module .md files to .claude/architecture/, regenerates INDEX.md and graph.md, and stamps each module via the CLI. Idempotent — safe to re-run.
5
6
  argument-hint: '[--module <path>] [--deep <path,...>]'
6
7
  allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task
@@ -69,36 +69,24 @@ Triggered by `/cbp-task-start` (Step 3.6, optional stale-check), `/cbp-task-comp
69
69
 
70
70
  Supabase migrations are version-keyed by their numeric filename prefix. Two files sharing a prefix break `supabase db push`: the schema_migrations table records ONE version per prefix, the second file at the same prefix becomes orphaned, and every subsequent migration stalls — surfacing as the Supabase Preview check failing with `MIGRATIONS_FAILED`. Catch this BEFORE committing the merge, while a clean rollback is one `git merge --abort` away.
71
71
 
72
- 1. Probe both sides of the would-be merge. Use `git ls-files` for the HEAD side so any in-progress `git mv` staged in the index (e.g. a rename produced by step 5 of a prior pass through this section) is reflected — `git ls-tree HEAD` would still see the committed-only state and re-trigger the collision. Use `git ls-tree origin/{BASE}` for the main side since we want the committed remote state, not anything locally staged:
72
+ 1. Probe both sides of the would-be merge via the deterministic CLI:
73
73
 
74
74
  ```bash
75
- git ls-files supabase/migrations/ 2>/dev/null \
76
- | sed 's|.*/||' | sort > /tmp/cbp-merge-our-names.txt
77
- git ls-tree -r --name-only origin/{BASE} supabase/migrations/ 2>/dev/null \
78
- | sed 's|.*/||' | sort > /tmp/cbp-merge-their-names.txt
75
+ PROBE=$(codebyplan migration-collisions --base "$BASE" --json)
76
+ # Parse JSON: { base, collisions: [{ prefix, ours: string[], theirs: string[] }] }
77
+ COLLISIONS=$(echo "$PROBE" | jq '.collisions')
79
78
  ```
80
79
 
81
- 2. A true collision is a numeric prefix that appears on BOTH sides with DIFFERENT filenames. A shared filename (same prefix, same basename i.e. an already-merged migration) is NOT a collision. Compute the unique-to-each-side basenames first, then look for shared prefixes within that unique set:
80
+ 2. If `COLLISIONS` is empty (`[]`), proceed silently to Step 2.
82
81
 
83
- ```bash
84
- # Files unique to each side (same-named files are NOT collisions)
85
- comm -23 /tmp/cbp-merge-our-names.txt /tmp/cbp-merge-their-names.txt > /tmp/cbp-merge-only-ours.txt
86
- comm -13 /tmp/cbp-merge-our-names.txt /tmp/cbp-merge-their-names.txt > /tmp/cbp-merge-only-theirs.txt
87
- # True collision: a prefix in only-ours also appears in only-theirs (same prefix, different basename)
88
- COLLISIONS=$(cat /tmp/cbp-merge-only-ours.txt /tmp/cbp-merge-only-theirs.txt \
89
- | sed 's|_.*||' | sort | uniq -d)
90
- ```
91
-
92
- 3. If `COLLISIONS` is empty, proceed silently to Step 2.
93
-
94
- 4. If `COLLISIONS` is non-empty, for each colliding prefix list both file paths (one from `HEAD`, one from `origin/{BASE}`) and surface via AskUserQuestion:
82
+ 3. If `COLLISIONS` is non-empty, for each colliding prefix in the array (each element has `prefix`, `ours[]`, `theirs[]`) surface via AskUserQuestion:
95
83
 
96
84
  - **Rename HEAD-side (Recommended when a main migration is already applied to a shared remote)** — rename the local file to a fresh, sequential timestamp that respects existing apply-order dependencies (probe `supabase migration list --db-url <preview>` if a preview branch exists, or inspect FK references in surrounding migrations). The orchestrator runs `git mv <old> <new>` itself; the rename lands in the git index and is picked up by the re-probe at step 5.
97
85
  - **Rename main-side (manual, OUT-OF-SKILL)** — only when the main file definitely has not been applied anywhere yet AND the user has write access to `{BASE}`. This skill does NOT touch the main branch: it runs on a feat branch (Step 0 enforces this) and the Key Rules below forbid any push from this skill. The user must, in a separate terminal: `git checkout {BASE} && git mv <old> <new> && git commit -m "fix(migration): rename to resolve collision with feat/..." && git push origin {BASE}`. After that push is confirmed remote-side, re-invoke `/cbp-merge-main` — Step 1 will fetch the updated main tip and Step 1.5 will re-probe with the rename in place.
98
86
  - **Defer to a new task in the active checkpoint** — `git merge --abort` is unnecessary because Step 2 has not started. Create a CHK-bound task per `cbp-round-end` reference `findings-presentation.md` "Infra Issue Absorption Contract — Resolve-in-Current-Scope by Default" and STOP `/cbp-merge-main`. Resume after the task completes.
99
87
  - **Abort merge** — STOP the skill. User decides later.
100
88
 
101
- 5. After any HEAD-side rename action, re-execute Step 1.5 (collisions may chain — fixing one can expose another). The HEAD-side probe at step 1 uses `git ls-files` rather than `git ls-tree HEAD`, so the freshly-staged `git mv` is visible without requiring a commit. Main-side renames require a fresh `/cbp-merge-main` invocation (the user manually fetched and re-ran per option 2 above), not an in-skill loop.
89
+ 4. After any HEAD-side rename action, re-execute Step 1.5 (collisions may chain — fixing one can expose another). The CLI probes the HEAD side via `git ls-files` (so staged renames are visible), matching the documented re-probe behavior. Main-side renames require a fresh `/cbp-merge-main` invocation (the user manually fetched and re-ran per option 2 above), not an in-skill loop.
102
90
 
103
91
  This check is intentionally placed BEFORE Step 2's `git merge`: catching collisions pre-merge means no merge commit to revert, no conflict-resolution work to throw away, no Supabase Preview poll to fail.
104
92
 
@@ -165,27 +153,34 @@ This check is intentionally placed BEFORE Step 2's `git merge`: catching collisi
165
153
 
166
154
  Run a scoped subset of the testing-qa check matrix INLINE (no agent spawn — this skill stays lightweight):
167
155
 
168
- 1. `pnpm -w lint` — always. On non-zero exit, surface stdout/stderr and AskUserQuestion:
156
+ 1. From the repo root, run:
157
+
158
+ ```bash
159
+ codebyplan check --scope merged --json
160
+ ```
161
+
162
+ `--scope merged` runs `gate6`, `lint`, `typecheck`, and `tests` (no `audit` — that is `task` scope only). The runner is **whole-repo + baseline**: it runs `turbo run lint|typecheck|test` across every package and diffs against the committed `.check-baseline.json`, so only NEW per-package failures fail a check (`status: 'fail'` with a non-empty `new_failures`). `gate6` (sibling-identity parity) is ALWAYS hard-fail and never baselined. Capture the JSON result.
163
+
164
+ 2. For each result entry where `status === 'fail'`, surface its `stdout`/`stderr` and present an AskUserQuestion:
169
165
  - **Continue (commit-as-is)** — leave the merge committed; flag QA failure in output.
170
166
  - **Abort merge** — `git reset --hard HEAD~1` to revert just the merge commit. Stop the skill.
171
167
  - **Skip (mark warn)** — leave the merge committed; treat as warn, not fail.
172
168
 
173
- 2. `pnpm exec tsc --noEmit` — only if any merged file matches `*.ts` or `*.tsx`. Same three-option prompt on failure.
174
-
175
- 3. `pnpm test --run` — only if any merged file matches typical source globs (`src/**`, `apps/**/src/**`, `packages/**/src/**`). Same three-option prompt on failure.
176
-
177
- 4. Build a `qa_summary` object:
169
+ 3. Build a `qa_summary` object from the runner's results array:
178
170
 
179
171
  ```
180
172
  {
173
+ "gate6": "pass" | "fail" | "warn" | "skipped",
181
174
  "lint": "pass" | "fail" | "warn" | "skipped",
182
- "tsc": "pass" | "fail" | "warn" | "skipped",
175
+ "typecheck": "pass" | "fail" | "warn" | "skipped",
183
176
  "tests": "pass" | "fail" | "warn" | "skipped",
184
177
  "merged_files_count": N,
185
178
  "user_choice_on_failure": "continue" | "abort" | "skip" | null
186
179
  }
187
180
  ```
188
181
 
182
+ Map runner `status` values: `"pass"` → `"pass"`, `"skipped"` → `"skipped"`, `"fail"` → `"fail"` or `"warn"` per the user's choice above.
183
+
189
184
  Do NOT auto-revert without user consent. User-driven gating is the contract.
190
185
 
191
186
  ### Step 5: Output
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  scope: org-shared
3
3
  name: cbp-refresh-arch-map
4
+ effort: high
4
5
  description: Drift-scoped refresh of the .claude/architecture/ map — re-runs the cbp-map-architecture agent for ONLY the modules whose stamped git SHA has changed, regenerates INDEX.md + graph.md, and re-stamps. Idempotent; no-op when no module has drifted.
5
6
  argument-hint: '[--module <path>]'
6
7
  allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task
@@ -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