codebyplan 1.13.62 → 1.13.64

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 (47) hide show
  1. package/dist/cli.js +1108 -2061
  2. package/package.json +1 -1
  3. package/templates/agents/cbp-e2e-playwright.md +10 -10
  4. package/templates/hooks/cbp-mcp-round-sync.sh +4 -15
  5. package/templates/hooks/cbp-skill-context-guard.sh +33 -13
  6. package/templates/hooks/cbp-test-hooks.sh +85 -0
  7. package/templates/hooks/hooks.json +0 -9
  8. package/templates/rules/architecture-map.md +6 -0
  9. package/templates/rules/cbp-operating-gotchas.md +13 -1
  10. package/templates/rules/e2e-mandatory.md +5 -0
  11. package/templates/rules/model-invocation-convention.md +1 -1
  12. package/templates/rules/scope-vocabulary.md +5 -0
  13. package/templates/rules/supabase-branch-lifecycle.md +2 -2
  14. package/templates/rules/task-routing-recommendation.md +1 -1
  15. package/templates/rules/todo-backend.md +11 -8
  16. package/templates/settings.project.base.json +9 -12
  17. package/templates/skills/cbp-build-cc-agent/SKILL.md +3 -0
  18. package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +31 -0
  19. package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +6 -0
  20. package/templates/skills/cbp-build-cc-agent/templates/agent.md +1 -0
  21. package/templates/skills/cbp-build-cc-settings/reference/cbp-permission-policy.md +11 -4
  22. package/templates/skills/cbp-build-cc-skill/SKILL.md +1 -0
  23. package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +14 -0
  24. package/templates/skills/cbp-build-cc-skill/templates/skill.md +1 -0
  25. package/templates/skills/cbp-checkpoint-create/SKILL.md +12 -22
  26. package/templates/skills/cbp-checkpoint-end/SKILL.md +37 -0
  27. package/templates/skills/cbp-checkpoint-plan/SKILL.md +10 -8
  28. package/templates/skills/cbp-checkpoint-start/SKILL.md +27 -19
  29. package/templates/skills/cbp-checkpoint-update/SKILL.md +1 -1
  30. package/templates/skills/cbp-clear-prep/SKILL.md +1 -1
  31. package/templates/skills/cbp-finalize/SKILL.md +2 -2
  32. package/templates/skills/cbp-finalize/reference/checkpoint-done-branching.md +1 -1
  33. package/templates/skills/cbp-finalize/reference/next-step-heuristic.md +1 -1
  34. package/templates/skills/cbp-round-complete/SKILL.md +3 -24
  35. package/templates/skills/cbp-round-plan/SKILL.md +1 -1
  36. package/templates/skills/cbp-session-end/SKILL.md +40 -30
  37. package/templates/skills/cbp-session-start/SKILL.md +7 -7
  38. package/templates/skills/cbp-session-start/qa-regression.md +32 -25
  39. package/templates/skills/cbp-standalone-task-complete/SKILL.md +2 -5
  40. package/templates/skills/cbp-standalone-task-create/SKILL.md +5 -13
  41. package/templates/skills/cbp-standalone-task-start/SKILL.md +10 -10
  42. package/templates/skills/cbp-task-create/SKILL.md +1 -1
  43. package/templates/skills/cbp-task-start/SKILL.md +10 -10
  44. package/templates/skills/cbp-todo/SKILL.md +23 -38
  45. package/templates/skills/cbp-todo/qa-regression.md +21 -27
  46. package/templates/skills/cbp-verify/reference/round-scope.md +1 -2
  47. package/templates/hooks/cbp-mcp-caller-worktree-inject.sh +0 -78
@@ -41,7 +41,7 @@ codebyplan session start --json
41
41
  The orchestrator performs (in order, all fail-safe):
42
42
 
43
43
  1. Resolve `repo_id` from `.codebyplan/repo.json`
44
- 2. `codebyplan resolve-worktree --json` → `worktree_id`, `worktree_error_kind`
44
+ 2. `codebyplan whoami --json` → `user_id`, `user_error_kind` (CHK-225 retired worktree identity — concurrency is user-level via `assigned_user_id`; the removed `resolve-worktree` verb no longer exists)
45
45
  3. `codebyplan session freshness-gate --halt-on-update` → update_halt short-circuit (no activate, no create-log when halt)
46
46
  4. Infra-drift nudge (monorepo-only), arch-map drift nudge, LSP binary nudge
47
47
  5. Read previous session log from `.codebyplan/state/session/current.json` (sync on miss)
@@ -49,7 +49,7 @@ The orchestrator performs (in order, all fail-safe):
49
49
  7. `codebyplan session update-state --action activate` (write-through)
50
50
  8. `codebyplan session create-log` → `session_log_id`
51
51
  9. Infra-files set math: active task round files subtracted from `git status --porcelain`
52
- 10. `get_checkpoints({ repo_id, status:'active' })` → ownership partition (owned vs cross)
52
+ 10. `get_checkpoints({ repo_id, status:'active' })` → ownership partition (owned by you vs other users)
53
53
  11. Compute `next_action` + render the Step 6 output block
54
54
 
55
55
  The envelope shape:
@@ -58,8 +58,8 @@ The envelope shape:
58
58
  {
59
59
  status: 'ok' | 'update_halt',
60
60
  session_log_id: string | null,
61
- worktree_id: string | null,
62
- worktree_error_kind: string | null,
61
+ user_id: string | null,
62
+ user_error_kind: string | null,
63
63
  infra_drift_nudge: string | null,
64
64
  arch_map_nudge: string | null,
65
65
  lsp_nudges: string[],
@@ -104,13 +104,13 @@ Route from `envelope.next_action`:
104
104
  - **`resume_handoff`** — trigger `envelope.handoff.command` directly with `envelope.handoff.context` / `envelope.handoff.state`. Skip Step 5.7.
105
105
  - **`commit_then_todo`** — run Step 5.7 (infra commit gate), then trigger `/cbp-todo`.
106
106
  - **`trigger_todo`** — trigger `/cbp-todo` (owns active work, or idle path).
107
- - **`stop`** — do NOT auto-trigger `/cbp-todo`. The Ownership block in `rendered_block` communicates the situation; the user must switch to the owning worktree or start new work explicitly.
107
+ - **`stop`** — do NOT auto-trigger `/cbp-todo`. The Ownership block in `rendered_block` communicates the situation; the user must coordinate with the owning user or start new work explicitly.
108
108
 
109
109
  ## Integration
110
110
 
111
111
  - **Triggered by**: user invocation, `/clear` recovery
112
- - **Reads**: MCP `health_check` (Step 0 hard gate — stays MCP unconditionally); `codebyplan session start --json` (Steps 1–5.8 — the orchestrator reads `.codebyplan/repo.json`, `.codebyplan/git.json`, `.codebyplan/state/session/current.json`, `.codebyplan/state/todos.json`, `.codebyplan/state/checkpoints/` entity files, `scripts/infra-drift.mjs`, `.codebyplan/architecture.json`, `.codebyplan/lsp.json`)
113
- - **Writes**: orchestrator calls `codebyplan session update-state --action activate` (Step 7) and `codebyplan session create-log` (Step 8) — both SKIPPED on Step 0 hard-fail and on `status: update_halt`
112
+ - **Reads**: MCP `health_check` (Step 0 hard gate — stays MCP unconditionally); `codebyplan session start --json` (Steps 1–5.8 — the orchestrator runs `codebyplan whoami --json` for caller identity and reads `.codebyplan/repo.json`, `.codebyplan/git.json`, `.codebyplan/state/session/current.json`, `.codebyplan/state/todos.json`, `.codebyplan/state/checkpoints/` entity files, `scripts/infra-drift.mjs`, `.codebyplan/architecture.json`, `.codebyplan/lsp.json`)
113
+ - **Writes**: orchestrator calls `codebyplan session update-state --action activate` (Step 7) and `codebyplan session create-log` (Step 8 — user-level; no worktree_id in the body) — both SKIPPED on Step 0 hard-fail and on `status: update_halt`
114
114
  - **Spawns**: none
115
115
  - **Triggers**: `/cbp-git-commit` (conditional, on user approval at Step 5.7), `envelope.handoff.command` (on `next_action: resume_handoff`), `/cbp-todo` (on `next_action: trigger_todo` or `commit_then_todo`); STOPS with no trigger on `next_action: stop` or `mcp_update_halt`
116
116
  - **Paired with**: `/cbp-session-end`
@@ -1,52 +1,59 @@
1
1
  ---
2
2
  name: cbp-session-start-qa-regression
3
- description: Manual regression procedure for the cbp-session-start worktree-ownership awareness + resolve-worktree distress channel (CHK-137 TASK-3)
3
+ description: Manual regression procedure for the cbp-session-start user-identity awareness + user-lock routing (CHK-225 TASK-5)
4
4
  ---
5
5
 
6
- # cbp-session-start — Worktree-Ownership Regression
6
+ # cbp-session-start — User-Identity Regression
7
7
 
8
- Manual procedure verifying that `/cbp-session-start` correctly resolves the caller's worktree identity, gates Step 7 auto-trigger on ownership, and surfaces distress signals non-blocking. Run these by hand whenever the SKILL's envelope-consumption logic (Step 0 health gate, the `codebyplan session start` invocation, the Step 5.7 commit gate, or the Step 7 `next_action` routing) changes.
8
+ Manual procedure verifying that `/cbp-session-start` correctly resolves the caller's user identity (via `codebyplan whoami --json`), gates Step 7 auto-trigger on user-level ownership, and surfaces distress signals non-blocking. Run these by hand whenever the SKILL's envelope-consumption logic (Step 0 health gate, the `codebyplan session start` invocation, the Step 5.7 commit gate, or the Step 7 `next_action` routing) changes.
9
9
 
10
- Since the worktree resolution, handoff freshness, ownership partition, and Step 6 render are now computed inside `codebyplan session start` (Steps 1–5.8 collapsed into one CLI call), the deterministic behavior is covered by unit tests (`src/cli/session/start.test.ts`, `src/lib/session.test.ts`). These scenarios are now best verified by inspecting the **envelope** the CLI emits — run `codebyplan session start --json` and read its fields — rather than tracing SKILL prose. The `ownership`, `next_action`, `handoff.fresh`, and `worktree_error_kind` fields below correspond to envelope keys.
10
+ Since the user resolution, handoff freshness, ownership partition, and Step 6 render are now computed inside `codebyplan session start` (Steps 1–5.8 collapsed into one CLI call), the deterministic behavior is covered by unit tests (`src/cli/session/start.test.ts`, `src/lib/session.test.ts`). These scenarios are now best verified by inspecting the **envelope** the CLI emits — run `codebyplan session start --json` and read its fields — rather than tracing SKILL prose. The `ownership`, `next_action`, `handoff.fresh`, and `user_error_kind` fields below correspond to envelope keys.
11
11
 
12
12
  Repo under test: `2ff6d405-39c5-47b8-a6d1-59f998ac0537`.
13
13
 
14
14
  ## Preconditions
15
15
 
16
16
  - The SKILL keeps Step 0 (`health_check`) as a hard gate, then delegates Steps 1–5.8 to `codebyplan session start --json` — confirm with `grep -n 'session start --json' SKILL.md` → at least one hit.
17
- - The orchestrator resolves worktree identity via `resolve-worktree --json` and partitions ownership via `get_checkpoints({ repo_id, status: 'active' })` — confirm the envelope carries `worktree_id` / `worktree_error_kind` / `owned_count` / `total_count`.
17
+ - The orchestrator resolves user identity via `whoami --json` and partitions ownership via `get_checkpoints({ repo_id, status: 'active' })` — confirm the envelope carries `user_id` / `user_error_kind` / `owned_count` / `total_count`.
18
18
  - The Step 6 Ownership block (the envelope's `rendered_block`) is READ-ONLY — confirm no "reassign", "release_assignment", or "transfer" language appears in SKILL.md: `grep -n 'reassign\|release_assignment\|transfer' SKILL.md` → no hits.
19
- - The handoff freshness gate includes the cross-worktree stale rule (checkpoint `worktree_id` non-null and differs from caller) — covered by the `probeHandoff` unit tests; the envelope surfaces the outcome as `handoff.fresh`.
19
+ - The handoff freshness gate includes the cross-user stale rule (checkpoint `assigned_user_id` non-null and differs from caller) — covered by the `probeHandoff` unit tests; the envelope surfaces the outcome as `handoff.fresh`.
20
20
 
21
- ## Scenario A — caller owns an active CHK → auto-trigger
21
+ ## Scenario A — current user owns an active CHK → auto-trigger
22
22
 
23
- 1. Run from a worktree whose `WORKTREE_ID` matches the active checkpoint's `worktree_id` (or both are `null`).
24
- 2. `get_checkpoints({ repo_id, status: 'active' })` returns at least one entry whose `worktree_id === WORKTREE_ID` (or both null).
25
- 3. **Expected**: envelope `owned_count >= 1`, `next_action: 'trigger_todo'`. `rendered_block` shows `Ownership: N active CHK(s), N owned by this worktree`. The SKILL's Step 7 fires `/cbp-todo`.
23
+ 1. Run signed in as a user whose `user_id` matches the active checkpoint's `assigned_user_id` (or the checkpoint's `assigned_user_id` is `null`).
24
+ 2. `get_checkpoints({ repo_id, status: 'active' })` returns at least one entry whose `assigned_user_id === user_id` (or is null).
25
+ 3. **Expected**: envelope `owned_count >= 1`, `next_action: 'trigger_todo'`. `rendered_block` shows `Ownership: N active CHK(s), N owned by you`. The SKILL's Step 7 fires `/cbp-todo`.
26
26
 
27
- ## Scenario B — active CHK(s) exist but none owned by caller → Ownership block + STOP
27
+ ## Scenario B — active CHK(s) exist, all owned by a different user → Ownership block + STOP
28
28
 
29
- Repro: caller worktree is `codebyplan-claude-2` (`38cd7dfa`). The only active checkpoint is CHK-136, assigned to `codebyplan-cli` (`016bd7f2`).
29
+ Repro: current user is `alice@example.com` (`uuid-A`). The only active checkpoint is CHK-136, assigned to `bob@example.com` (`uuid-B`).
30
30
 
31
- 1. `resolve-worktree --json` returns `{"worktree_id":"38cd7dfa-...","error_kind":null}`.
32
- 2. `get_checkpoints({ repo_id, status: 'active' })` returns CHK-136 with `worktree_id = "016bd7f2-..."`.
33
- 3. Step 5.8: `owned_count = 0`, `total_count = 1`, `cross_worktree = [CHK-136]`.
34
- 4. **Expected**: envelope `owned_count = 0`, `total_count = 1`, `next_action: 'stop'`, `cross_checkpoints = [CHK-136]`. `rendered_block` shows `Ownership: 1 active CHK(s), 0 owned by this worktree` and `[Cross-worktree: CHK-136 (…)]`. The SKILL **STOPS** on `next_action: 'stop'` — `/cbp-todo` is NOT auto-triggered. No reassignment language appears.
31
+ 1. `whoami --json` returns `{"user_id":"uuid-A","email":"alice@example.com"}`.
32
+ 2. `get_checkpoints({ repo_id, status: 'active' })` returns CHK-136 with `assigned_user_id = "uuid-B"`.
33
+ 3. Step 5.8: `owned_count = 0`, `total_count = 1`, `cross_checkpoints = [CHK-136]`.
34
+ 4. **Expected**: envelope `owned_count = 0`, `total_count = 1`, `next_action: 'stop'`, `cross_checkpoints = [CHK-136]`. `rendered_block` shows `Ownership: 1 active CHK(s), 0 owned by you` and `[Other users: CHK-136 (…)]`. The SKILL **STOPS** on `next_action: 'stop'` — `/cbp-todo` is NOT auto-triggered. No reassignment language appears.
35
35
 
36
36
  ## Scenario C — no active CHKs anywhere → idle /cbp-todo trigger
37
37
 
38
38
  1. `get_checkpoints({ repo_id, status: 'active' })` returns `[]`.
39
39
  2. Step 5.8: `owned_count = 0`, `total_count = 0`.
40
- 3. **Expected**: envelope `owned_count = 0`, `total_count = 0`, `next_action: 'trigger_todo'`. `rendered_block` shows `Ownership: 0 active CHK(s), 0 owned by this worktree`. The SKILL fires `/cbp-todo` (idle path → checkpoint-create or session-end suggestion).
40
+ 3. **Expected**: envelope `owned_count = 0`, `total_count = 0`, `next_action: 'trigger_todo'`. `rendered_block` shows `Ownership: 0 active CHK(s), 0 owned by you`. The SKILL fires `/cbp-todo` (idle path → checkpoint-create or session-end suggestion).
41
41
 
42
- ## Scenario D — cross-worktree handoff → Step 4.5 marks stale, falls through
42
+ ## Scenario D — stale handoff pointing to a different user's checkpoint handoff marked stale, falls through
43
43
 
44
- 1. The most-recent closed session log contains a handoff payload whose `context.checkpoint_id` resolves to a checkpoint with `worktree_id = "016bd7f2-..."` (a different worktree).
45
- 2. Caller `WORKTREE_ID = "38cd7dfa-..."`.
46
- 3. **Expected**: the orchestrator's handoff freshness gate hits the cross-worktree stale rule (`checkpoint.worktree_id` non-null AND differs from caller) → envelope `handoff.fresh: false`. The mismatched handoff is NOT auto-resumed (`next_action` is never `resume_handoff`); ownership/routing proceed normally per `owned_count`/`total_count`.
44
+ 1. The most-recent closed session log contains a handoff payload whose `context.checkpoint_id` resolves to a checkpoint with `assigned_user_id = "uuid-B"` (a different user).
45
+ 2. Caller `user_id = "uuid-A"`.
46
+ 3. **Expected**: the orchestrator's handoff freshness gate hits the cross-user stale rule (`checkpoint.assigned_user_id` non-null AND differs from caller) → envelope `handoff.fresh: false`. The mismatched handoff is NOT auto-resumed (`next_action` is never `resume_handoff`); ownership/routing proceed normally per `owned_count`/`total_count`.
47
47
 
48
- ## Scenario E — resolver distress (non-tuple-miss) → warning line above Ownership block, session proceeds
48
+ ## Scenario E — whoami distress (identity unresolved) → session proceeds, non-blocking warning
49
49
 
50
- 1. `resolve-worktree --json` returns `{"worktree_id":null,"error_kind":"local_config_read_failed"}` (or any other non-null, non-tuple-miss `error_kind`).
51
- 2. **Expected**: the envelope carries `worktree_error_kind: 'local_config_read_failed'`; `status` stays `'ok'` (non-blocking). `rendered_block` surfaces `⚠ resolve-worktree: local_config_read_failed — local state is broken; routing may be unreliable. Run \`npx codebyplan setup\` to repair.` ABOVE the Ownership block. The orchestrator still activates + creates the session log. `next_action` routes per the `owned_count`/`total_count` values as normal — the skill does NOT hard-stop the way `/cbp-todo` does on the same distress kinds.
52
- 3. **Compound case** (distress typically leaves `worktree_id` null): ownership then classifies every checkpoint with a non-null `worktree_id` as `cross_checkpoints[]` (only truly-null-`worktree_id` checkpoints land in `owned_checkpoints[]`). So if all active checkpoints are assigned, `owned_count = 0` and `next_action: 'stop'` — the distress warning + Ownership block are the only output. The created session log records `worktree_id: null` (the resolver could not read local state); this is expected, not a failure.
50
+ 1. `whoami --json` returns `{"user_id":null,"error_kind":"local_config_read_failed"}` (or any other non-null, non-tuple-miss `error_kind`).
51
+ 2. **Expected**: the envelope carries `user_error_kind: 'local_config_read_failed'`; `status` stays `'ok'` (non-blocking). `rendered_block` surfaces `⚠ whoami: local_config_read_failed — identity could not be resolved; routing may be unreliable. Run \`npx codebyplan whoami\` to check identity.` ABOVE the Ownership block. The orchestrator still activates + creates the session log. `next_action` routes per the `owned_count`/`total_count` values as normal — the skill does NOT hard-stop the way `/cbp-todo` does on the same distress kinds.
52
+ 3. **Compound case** (distress typically leaves `user_id` null): ownership then classifies every checkpoint with a non-null `assigned_user_id` as `cross_checkpoints[]` (only open, null-`assigned_user_id` checkpoints land in `owned_checkpoints[]`). So if all active checkpoints are assigned, `owned_count = 0` and `next_action: 'stop'` — the distress warning + Ownership block are the only output. The created session log records the caller's JWT user server-side; the envelope `user_id: null` (the resolver could not read local identity) is expected, not a failure.
53
+
54
+ ## Scenario F — anonymous session (whoami returns null / signed out) → session proceeds, open checkpoints treated as own
55
+
56
+ 1. `whoami --json` returns `null` (signed out), empty output, or exits non-zero.
57
+ 2. `user_id` is treated as null (anonymous) with `user_error_kind: 'tuple_miss'`.
58
+ 3. `get_checkpoints({ repo_id, status: 'active' })` returns checkpoints with `assigned_user_id` null or set to real user IDs.
59
+ 4. **Expected**: ownership classifies null-`assigned_user_id` checkpoints as `owned_checkpoints[]` (no user to compare against) and non-null-`assigned_user_id` checkpoints as `cross_checkpoints[]` (anonymous caller cannot claim them). `rendered_block` shows the `⚠ Not signed in — run \`npx codebyplan login\` to sign in` line below the User line. `next_action` routes per `owned_count`/`total_count` as normal — the session does NOT hard-stop solely due to anonymous identity. The session log is created in Step 8.
@@ -11,7 +11,7 @@ effort: xhigh
11
11
 
12
12
  Complete a standalone task. Auto-triggered by `/cbp-standalone-task-testing` when all tests pass. Can also be run manually.
13
13
 
14
- This skill is gated by an `ask`-tier `Skill(cbp-standalone-task-complete)` permission rule in `settings.json` (shipped templates). **The permission prompt IS the user confirmation** — there is NO completion-confirmation AskUserQuestion inside this skill (the Step 7.5 `caller_worktree_id` guard is a separate environmental safety prompt, not a flow-control confirmation). A declined permission is a clean no-op (nothing committed, merged, pushed, or completed).
14
+ This skill is gated by an `ask`-tier `Skill(cbp-standalone-task-complete)` permission rule in `settings.json` (shipped templates). **The permission prompt IS the user confirmation** — there is NO completion-confirmation AskUserQuestion inside this skill. A declined permission is a clean no-op (nothing committed, merged, pushed, or completed).
15
15
 
16
16
  ## Instructions
17
17
 
@@ -227,9 +227,7 @@ Complete via the CLI (wraps the `complete_standalone_task` MCP tool):
227
227
  codebyplan standalone-task complete --id <standalone_task.id>
228
228
  ```
229
229
 
230
- The CLI auto-resolves `caller_worktree_id` (override → worktree cache → resolver). `caller_worktree_id` is REQUIRED — the MCP server's pre-guard rejects mutations from non-matching worktrees, and the CLI hard-fails (exit 1) with registration guidance rather than sending an undefined id. The server auto-clears `assigned_worktree_id` on the task on success.
231
-
232
- If the CLI exits 1 with a "could not resolve caller_worktree_id" message, run `npx codebyplan setup` (or `codebyplan resolve-worktree --cache`) from this worktree, then re-run the command.
230
+ The server keys on the JWT user (`ctx.userId`) no worktree param is needed. The server auto-clears `assigned_user_id` on the task on success.
233
231
 
234
232
  ### Step 8: Run Cleanup + Migration (inline)
235
233
 
@@ -266,7 +264,6 @@ Do NOT use AskUserQuestion for routing. Do NOT use the Skill tool to auto-trigge
266
264
 
267
265
  ## Key Rules
268
266
 
269
- - **`caller_worktree_id` is REQUIRED** for `complete_standalone_task`
270
267
  - **Branch shipping lives here** (not in checkpoint-end) — standalone tasks self-ship to production via `codebyplan ship`
271
268
  - **Single-directive routing** — no menus, no auto-triggers via Skill tool
272
269
  - **No checkpoint** — never check `checkpoint_id`, always treat task as standalone
@@ -88,28 +88,20 @@ To create the best task definition, I need to clarify:
88
88
 
89
89
  Skip if requirements are already clear from Step 1.
90
90
 
91
- ### Step 6: Resolve Worktree Assignment
91
+ ### Step 6: Create Standalone Task
92
92
 
93
- Resolve worktree_id via `npx codebyplan resolve-worktree 2>/dev/null`.
94
-
95
- - If non-empty: pass as `assigned_worktree_id` in Step 7. This engages the hard-lock from creation.
96
- - If empty: warn the user that the task will be unassigned and offer to run `npx codebyplan setup` from the current directory first. After setup, re-resolve and proceed. If the user declines, create without `assigned_worktree_id`.
97
-
98
- ### Step 7: Create Standalone Task
99
-
100
- Create via the CLI (wraps the `create_standalone_task` MCP tool; auto-resolves `caller_worktree_id`):
93
+ Create via the CLI (wraps the `create_standalone_task` MCP tool):
101
94
 
102
95
  ```bash
103
96
  codebyplan standalone-task create \
104
97
  --title "<concise task title>" \
105
98
  --number <next number from Step 3> \
106
99
  --requirements "<numbered requirements list>" \
107
- --context '<JSON: decisions from Q&A + source findings>' \
108
- --assigned-worktree-id <from Step 6, if resolved>
100
+ --context '<JSON: decisions from Q&A + source findings>'
109
101
  ```
110
102
 
111
103
  - `--repo-id` is optional — the CLI reads it from `.codebyplan/repo.json`.
112
- - Omit `--assigned-worktree-id` when Step 6 did not resolve a worktree.
104
+ - Task assignment is user-based — the server stamps `assigned_user_id` from the JWT on creation; no worktree param is needed.
113
105
  - On success the CLI prints the created row JSON (including `.id`) to stdout.
114
106
 
115
107
  ```
@@ -127,7 +119,7 @@ codebyplan standalone-task create \
127
119
  - Files likely touched: [relevant paths]
128
120
  ```
129
121
 
130
- ### Step 8: Route
122
+ ### Step 7: Route
131
123
 
132
124
  ```
133
125
  Standalone TASK-[N] created successfully.
@@ -106,20 +106,20 @@ If `current` is still in `branch_config.protected`, block with a hard error citi
106
106
  1. Run `git status --short` to check for uncommitted files.
107
107
  2. If uncommitted files exist → commit them with `chore: clean slate for task start`. Prefer `git add -A && git commit -m "..."`.
108
108
 
109
- ### Step 3.5: Worktree-Match Verification
109
+ ### Step 3.5: User-Match Verification
110
110
 
111
- Before activating, verify the caller's worktree matches the assigned worktree.
111
+ Before activating, verify the current user is allowed to claim/edit the standalone task row.
112
112
 
113
- 1. `CALLER_WT=$(npx codebyplan resolve-worktree 2>/dev/null)`. If empty, omit `caller_worktree_id` from MCP calls.
114
- 2. `TARGET_WT = task.assigned_worktree_id`.
115
- 3. If `TARGET_WT IS NOT NULL AND TARGET_WT != CALLER_WT`, surface error and abort:
113
+ 1. Resolve the current user: `CURRENT_USER=$(npx codebyplan whoami --json 2>/dev/null)` → parse `user_id` and `email` from the JSON. If the command fails or returns null/empty, `CURRENT_USER_ID` is null (anonymous session).
114
+ 2. `TARGET_USER = task.assigned_user_id`.
115
+ 3. If `TARGET_USER IS NOT NULL AND TARGET_USER != CURRENT_USER_ID`, surface error and abort:
116
116
 
117
117
  ```
118
- Standalone TASK-N is assigned to worktree {TARGET_WT_NAME} ({TARGET_WT}); current worktree is {CALLER_WT_NAME} ({CALLER_WT}).
119
- Use the release_assignment MCP tool (requires maintainer role) if {TARGET_WT_NAME} is dead, then re-run /cbp-standalone-task-start.
118
+ Standalone TASK-N is assigned to user {TARGET_EMAIL} ({TARGET_USER}); you are logged in as {CURRENT_EMAIL} ({CURRENT_USER_ID}).
119
+ Use the release_assignment MCP tool (requires maintainer role) if that user is no longer active, then re-run /cbp-standalone-task-start.
120
120
  ```
121
121
 
122
- 4. If `TARGET_WT IS NULL` or matches, proceed.
122
+ 4. If `TARGET_USER IS NULL` or matches `CURRENT_USER_ID`, proceed.
123
123
 
124
124
  ### Step 3.6: Main-Drift Check (optional)
125
125
 
@@ -150,13 +150,13 @@ Load context from DB:
150
150
 
151
151
  ### Step 5: Set Task Status
152
152
 
153
- Set status via the CLI (wraps `update_standalone_task`; auto-resolves `caller_worktree_id`):
153
+ Set status via the CLI (wraps `update_standalone_task`):
154
154
 
155
155
  ```bash
156
156
  codebyplan standalone-task update --id <task.id> --status in_progress
157
157
  ```
158
158
 
159
- `--id` is the standalone task UUID resolved in Step 2. The CLI resolves `caller_worktree_id` itself (override worktree cache resolver), so `CALLER_WT` does not need to be passed.
159
+ `--id` is the standalone task UUID resolved in Step 2. The server keys on the JWT user for authz — no worktree or caller params are needed or accepted; all worktree-identity flags are deprecated and silently ignored server-side.
160
160
 
161
161
  ### Step 6: Auto-trigger Round Start
162
162
 
@@ -146,7 +146,7 @@ Use `codebyplan task create --checkpoint-id <id> ...` (CLI write-through) to cre
146
146
  - **requirements**: Numbered requirements list
147
147
  - **context**: Include decisions from Q&A, dependencies, source findings
148
148
 
149
- **For checkpoint-bound tasks**, an empty `worktree_id` is fine no caller-worktree stamping is needed because the parent checkpoint's `worktree_id` governs.
149
+ **For checkpoint-bound tasks**, assignment is user-based — the task's `assigned_user_id` is stamped server-side from the JWT; no caller-worktree param is needed.
150
150
 
151
151
  ```
152
152
  ## Task Created
@@ -157,21 +157,21 @@ Skip this step if the task title and requirements contain no CVE ID (`CVE-YYYY-N
157
157
 
158
158
  See `dependency-vulnerability-fixes.md` "Audit Sweep at Task Start" for the source rule.
159
159
 
160
- ### Step 3.5: Worktree-Match Verification
160
+ ### Step 3.5: User-Match Verification
161
161
 
162
- Before activating the task, verify the caller's worktree matches the assigned worktree on the target row. (Per CHK-104 — DB-level hard-lock prevents cross-worktree mutations.)
162
+ Before activating the task, verify the current user is allowed to claim/edit the checkpoint row.
163
163
 
164
- 1. Read caller worktree: `CALLER_WT=$(npx codebyplan resolve-worktree 2>/dev/null)`.
165
- 2. Determine target worktree:
166
- - **Checkpoint-bound tasks**: `TARGET_WT = checkpoint.worktree_id` (read from the local checkpoint file already loaded in Step 2). Note: checkpoint-bound tasks may have a NULL `task.assigned_worktree_id` because the lock lives on the parent checkpoint — fall through to `checkpoint.worktree_id`.
167
- 3. If `TARGET_WT IS NOT NULL AND TARGET_WT != CALLER_WT`, surface this error and abort:
164
+ 1. Resolve the current user: `CURRENT_USER=$(npx codebyplan whoami --json 2>/dev/null)` → parse `user_id` and `email` from the JSON. If the command fails or returns null/empty, `CURRENT_USER_ID` is null (anonymous session).
165
+ 2. Determine the target owner:
166
+ - **Checkpoint-bound tasks**: `TARGET_USER = checkpoint.assigned_user_id` (read from the local checkpoint file already loaded in Step 2). Note: checkpoint-bound tasks may have a null `task.assigned_user_id`; the lock lives on the parent checkpoint — fall through to `checkpoint.assigned_user_id`.
167
+ 3. If `TARGET_USER IS NOT NULL AND TARGET_USER != CURRENT_USER_ID`, surface this error and abort:
168
168
 
169
169
  ```
170
- TASK-N is assigned to worktree {TARGET_WT_NAME} ({TARGET_WT}); current worktree is {CALLER_WT_NAME} ({CALLER_WT}).
171
- Use the release_assignment MCP tool (requires maintainer role) if {TARGET_WT_NAME} is dead, then re-run /cbp-task-start.
170
+ TASK-N is assigned to user {TARGET_EMAIL} ({TARGET_USER}); you are logged in as {CURRENT_EMAIL} ({CURRENT_USER_ID}).
171
+ Use the release_assignment MCP tool (requires maintainer role) if that user is no longer active, then re-run /cbp-task-start.
172
172
  ```
173
173
 
174
- 4. If `TARGET_WT IS NULL` or matches, proceed.
174
+ 4. If `TARGET_USER IS NULL` or matches `CURRENT_USER_ID`, proceed.
175
175
 
176
176
  ### Step 3.6: Main-Drift Check (optional)
177
177
 
@@ -218,7 +218,7 @@ Display context summary:
218
218
 
219
219
  ### Step 5: Set Task Status
220
220
 
221
- `codebyplan task update --id <taskId> --checkpoint-id <checkpointId> --status in_progress` (CLI write-through: local state file + REST). If a worktree_id is present, also pass `--claim-worktree-id <worktreeId>` to auto-claim the checkpoint (the CLI passes all flags as snake-case PATCH fields). Break-glass fallback: MCP `update_task(task_id, status: "in_progress", claim_worktree_id: ..., caller_worktree_id: ...)` when the CLI is unavailable — note `caller_worktree_id` is auto-injected by the `cbp-mcp-caller-worktree-inject.sh` PreToolUse hook only on the MCP path.
221
+ `codebyplan task update --id <taskId> --checkpoint-id <checkpointId> --status in_progress` (CLI write-through: local state file + REST). The server keys on the JWT user for authz no worktree or claim params are needed or accepted. Break-glass fallback: MCP `update_task(task_id, status: "in_progress")` when the CLI is unavailable — all worktree-identity params are deprecated and silently ignored server-side; omit them.
222
222
 
223
223
  ### Step 6: Auto-trigger Round Start
224
224
 
@@ -7,27 +7,13 @@ effort: medium
7
7
 
8
8
  # Todo Command
9
9
 
10
- Single entry point after `/clear`: read the pure-read todo queue, gate on worktree ownership + checkpoint planning, load context, then trigger the next command. It ensures Claude always has full context — and never auto-routes into work locked to another worktree — before any command runs.
10
+ Single entry point after `/clear`: read the pure-read todo queue, gate on user ownership + checkpoint planning, load context, then trigger the next command. It ensures Claude always has full context — and never auto-routes into work locked to another user — before any command runs.
11
11
 
12
12
  ## Instructions
13
13
 
14
- ### Step 0: Resolve Caller Identity (worktree + user)
14
+ ### Step 0: Resolve Caller Identity (user)
15
15
 
16
- Before any MCP call, resolve who and where the caller is.
17
-
18
- **Worktree** — derive the caller `worktree_id` and any distress signal in one structured read:
19
-
20
- ```bash
21
- npx codebyplan resolve-worktree --json # → {"worktree_id":"<uuid>|null","error_kind":null|"<kind>"}
22
- ```
23
-
24
- - `error_kind` is `null` or `"tuple_miss"` → healthy. `WORKTREE_ID` = `worktree_id` (may be `null`: a legitimate main-repo or unregistered-worktree case — proceed to read the queue, but downstream hard-locks reject mutations on assigned rows).
25
- - `error_kind` is `local_config_read_failed`, `local_config_write_failed`, `legacy_file_blocks_dir`, `api_failed`, `git_failed`, or `unhandled` → **broken local state**. Surface the distress line and STOP (skip Steps 1–4) — routing is unreliable when the resolver cannot read local state:
26
-
27
- ```
28
- ⚠ resolve-worktree: <error_kind> — local state is broken; routing is unreliable.
29
- Run `npx codebyplan setup` from this directory to register/repair, then re-run /cbp-todo.
30
- ```
16
+ Before any MCP call, resolve who the caller is.
31
17
 
32
18
  **User** — `get_todos` (Step 1) requires a `user_id`:
33
19
 
@@ -36,45 +22,44 @@ npx codebyplan whoami --json # → {"user_id":"<uuid>","email":"…"} or null
36
22
  ```
37
23
 
38
24
  - A `user_id` is returned → use it in Step 1.
39
- - `null` (CLI keychain empty — common in OAuth-MCP-only environments) → the queue cannot be scoped by user; skip Step 1 and use the Step 3 fallback. The Step 1.5 ownership gate still applies.
25
+ - `null` or empty `user_id` (CLI keychain empty — common in OAuth-MCP-only environments) → the queue cannot be scoped by user; skip Step 1 and use the Step 3 fallback. The Step 1.5 ownership gate still applies.
40
26
 
41
27
  ### Step 1: Read the Todo Queue (pure-read)
42
28
 
43
- With `USER_ID` resolved, read `.codebyplan/state/todos.json` (local-first). If missing or stale (`_cursor.json` absent or `sync_status !== "synced"`), run `npx codebyplan sync` once and re-read. Break-glass fallback: MCP `get_todos({ repo_id, user_id, worktree_id })` when the state dir is absent and sync fails. Filter by `user_id` and `worktree_id` (omit `worktree_id` when `WORKTREE_ID` is `null` or unresolved). Take **`rows[0]`** as the queue head (ordered by `sort_order`).
29
+ With `USER_ID` resolved, read `.codebyplan/state/todos.json` (local-first). If missing or stale (`_cursor.json` absent or `sync_status !== "synced"`), run `npx codebyplan sync` once and re-read. Break-glass fallback: MCP `get_todos({ repo_id, user_id })` when the state dir is absent and sync fails. Filter by `user_id`. Take **`rows[0]`** as the queue head (ordered by `sort_order`).
44
30
 
45
- - The head carries `command`, `instructions`, `state`, `metadata`, `worktree_id`, `checkpoint_id`, `task_id`.
31
+ - The head carries `command`, `instructions`, `state`, `metadata`, `checkpoint_id`, `task_id`.
46
32
  - The routing context (checkpoint/task) lives in **`rows[0].metadata`**.
47
33
  - `get_todos` is **pure-read** — `apps/todo-worker` is the sole regen authority. NEVER call `get_next_action` or `regenerate_todos_for_repo`.
48
34
  - Empty array, or `USER_ID` unavailable → go to Step 3 (empty-queue fallback).
49
35
 
50
36
  Queue `command` values may use the `/codebyplan:<name>` plugin-namespace form (e.g. `/codebyplan:round-start`); treat each as the matching `/cbp-<name>` skill for the Step 2 matrix.
51
37
 
52
- ### Step 1.5: Resolve Target + Worktree-Ownership Gate
38
+ ### Step 1.5: Resolve Target + User-Ownership Gate
53
39
 
54
- Resolve the routing target's checkpoint and gate on ownership BEFORE any auto-trigger — including the Step 1.6 planning hand-offs. Refuse to route into, plan, or start work locked to a different worktree.
40
+ Resolve the routing target's checkpoint and gate on ownership BEFORE any auto-trigger — including the Step 1.6 planning hand-offs. Refuse to route into, plan, or start work locked to a different user.
55
41
 
56
- Resolve the checkpoint from `rows[0].metadata` (or read `.codebyplan/state/session/current.json` for the active task, falling back to MCP `get_current_task`), then load its `worktree_id` + `plan` + `status` from `.codebyplan/state/checkpoints/<id>.json` (falling back to MCP `get_checkpoints`) and its task count from `.codebyplan/state/checkpoints/<id>/tasks/` files (falling back to MCP `get_tasks(checkpoint_id)`). This single load is reused by the Step 1.6 planning gate. Skip this gate when the routing target has no checkpoint (idle — see Step 3) or the command is `/cbp-session-start`.
42
+ Resolve the checkpoint from `rows[0].metadata` (or read `.codebyplan/state/session/current.json` for the active task, falling back to MCP `get_current_task`), then load its `assigned_user_id` + `plan` + `status` from `.codebyplan/state/checkpoints/<id>.json` (falling back to MCP `get_checkpoints`) and its task count from `.codebyplan/state/checkpoints/<id>/tasks/` files (falling back to MCP `get_tasks(checkpoint_id)`). This single load is reused by the Step 1.6 planning gate. Skip this gate when the routing target has no checkpoint (idle — see Step 3) or the command is `/cbp-session-start`.
57
43
 
58
44
  Two ownership signals:
59
45
 
60
- 1. **Server-detected conflict** — `rows[0].state === "worktree_conflict"` (command `/codebyplan:release-or-switch`): the todo-worker already detected the lock conflict. The owner is in `rows[0].metadata.conflicting_worktree_name` / `.conflicting_worktree_id`. Surface the mismatch message below and **STOP**. Do NOT auto-trigger the release-or-switch command and do NOT call `release_assignment` — switch worktrees rather than reassigning a lock to the caller.
61
- 2. **Checkpoint-ownership check** — take the target checkpoint's `worktree_id` (loaded above, keyed by `rows[0].checkpoint_id` / `metadata.checkpoint.id`) and compare with caller `WORKTREE_ID`:
62
- - target `worktree_id` is `null` → **allow** (unassigned — any worktree may pick it up; covers the both-null main-repo case).
63
- - target `worktree_id` === caller `WORKTREE_ID` → **allow**.
64
- - target `worktree_id` non-null **AND** caller `WORKTREE_ID` is `null`/unresolved → **block** (deliberate safety: identity cannot be confirmed. This does not contradict Step 0 — reading the queue is fine, auto-triggering INTO assigned work is not. Run `npx codebyplan setup` to register this worktree).
65
- - target `worktree_id` non-null and differs from a non-null caller → **block**.
46
+ 1. **Server-detected conflict** — `rows[0].state === "worktree_conflict"` (command `/codebyplan:release-or-switch`): the todo-worker already detected the lock conflict. (`worktree_conflict` is the worker's current state-value name — it now reflects a user-level lock conflict and is renamed alongside the web-UI/schema work in a later task.) Surface the mismatch message below and **STOP**. Do NOT auto-trigger the release-or-switch command and do NOT call `release_assignment` — the user who owns the lock must release it or a maintainer must use `release_assignment`.
47
+ 2. **Checkpoint-ownership check** — take the target checkpoint's `assigned_user_id` (loaded above) and compare with caller `USER_ID`:
48
+ - target `assigned_user_id` is `null` → **allow** (unassigned — any user may pick it up).
49
+ - target `assigned_user_id` === caller `USER_ID` → **allow**.
50
+ - target `assigned_user_id` non-null AND differs from caller `USER_ID` → **block**.
66
51
 
67
- On block, resolve the owning worktree's `name` + `path` via MCP `get_worktrees({ repo_id })` (display-only ownership-block path no CLI verb exists for worktrees; stays MCP), then emit and STOP:
52
+ On block, emit and STOP (show the owner email if resolvable from the whoami context, otherwise the `assigned_user_id` UUID):
68
53
 
69
54
  ```
70
- ⚠ Work mismatch: CHK-<NNN> TASK-<N> is assigned to worktree <name> (<short-uuid>), not this one (<this-name> / <this-short-uuid>).
55
+ ⚠ Work mismatch: CHK-<NNN> TASK-<N> is assigned to user <email-or-uuid>, not you.
71
56
  Options:
72
- A) Switch to <owning-worktree-path> and resume there
73
- B) Work on something else here run /cbp-checkpoint-create or /cbp-task-create
57
+ A) Work on something else — run /cbp-checkpoint-create or /cbp-task-create
58
+ B) Ask the owner to release the assignment, or a maintainer can use release_assignment, then re-run /cbp-todo
74
59
  C) /cbp-session-end
75
60
  ```
76
61
 
77
- `<short-uuid>` = first 8 chars of the worktree UUID. Caller unresolved → render "this one" as `(unresolved / —)`. Name lookup miss → show the UUID alone. Wait for the user. NEVER propose reassigning the checkpoint to the caller worktree.
62
+ Wait for the user. NEVER propose reassigning the checkpoint to the caller.
78
63
 
79
64
  ### Step 1.55: Stale-Entity Guard
80
65
 
@@ -170,8 +155,8 @@ Display a brief context summary:
170
155
 
171
156
  Reached when `get_todos` returns `[]` or `USER_ID` was unavailable.
172
157
 
173
- 1. **Fallback discovery** (worktree-scoped): read `.codebyplan/state/session/current.json` and scan `.codebyplan/state/checkpoints/` for active checkpoints (fallback: MCP `get_current_task` + `get_checkpoints`) to discover whether actionable work exists for this caller.
174
- 2. **Actionable work found** → treat the discovered checkpoint as the routing target and apply BOTH the Step 1.5 ownership gate and the Step 1.6 planning gate to it, using the `worktree_id` + `plan` + `status` returned by `get_checkpoints` (the fallback has no `rows[0]` — substitute the discovered checkpoint). If both gates pass, route via Step 2 → Step 4.
158
+ 1. **Fallback discovery**: read `.codebyplan/state/session/current.json` and scan `.codebyplan/state/checkpoints/` for active checkpoints (fallback: MCP `get_current_task` + `get_checkpoints`) to discover whether actionable work exists for this caller.
159
+ 2. **Actionable work found** → treat the discovered checkpoint as the routing target and apply BOTH the Step 1.5 ownership gate and the Step 1.6 planning gate to it, using the `assigned_user_id` + `plan` + `status` returned by `get_checkpoints` (the fallback has no `rows[0]` — substitute the discovered checkpoint). If both gates pass, route via Step 2 → Step 4.
175
160
  3. **Nothing actionable** → suggest ending the session:
176
161
 
177
162
  ```
@@ -190,6 +175,6 @@ Reached only when the Step 1.5 ownership gate allowed routing to continue, the S
190
175
  ## Integration
191
176
 
192
177
  - **Called by**: `/cbp-session-start`, `/cbp-finalize`, `/cbp-checkpoint-complete`, manual, after `/clear`
193
- - **Resolves**: `npx codebyplan resolve-worktree --json` (worktree id + distress signal), `npx codebyplan whoami --json` (user id)
194
- - **Reads**: `.codebyplan/state/todos.json`, `session/current.json`, `checkpoints/<id>.json`, `checkpoints/<id>/tasks/<id>.json`, `checkpoints/<id>/tasks/<id>/rounds/<id>.json`, `worktrees.json`. If missing/stale, run `npx codebyplan sync` once and re-read. Break-glass: MCP `get_todos`, `get_current_task`, `get_rounds`, `get_checkpoints`, `get_tasks` when state dir absent and sync fails. `get_worktrees` stays MCP (display-only ownership-block path; no CLI verb).
178
+ - **Resolves**: `npx codebyplan whoami --json` (user id)
179
+ - **Reads**: `.codebyplan/state/todos.json`, `session/current.json`, `checkpoints/<id>.json`, `checkpoints/<id>/tasks/<id>.json`, `checkpoints/<id>/tasks/<id>/rounds/<id>.json`. If missing/stale, run `npx codebyplan sync` once and re-read. Break-glass: MCP `get_todos`, `get_current_task`, `get_rounds`, `get_checkpoints`, `get_tasks` when state dir absent and sync fails.
195
180
  - **Triggers**: `rows[0].command` (auto, after the Step 1.5 ownership gate and Step 1.55 stale-entity guard pass, and the Step 1.6 planning gate falls through); Step 1.55 overrides to STOP (stale completed/cancelled entity); Step 1.6 overrides to `/cbp-checkpoint-plan` (unplanned) or `/cbp-checkpoint-start` (planned-but-pending)
@@ -1,51 +1,45 @@
1
1
  ---
2
2
  name: cbp-todo-qa-regression
3
- description: Manual regression procedure for the cbp-todo worktree-ownership gate + get_todos switch (CHK-137 TASK-2)
3
+ description: Manual regression procedure for the cbp-todo user-ownership gate + get_todos switch (CHK-225 TASK-5)
4
4
  ---
5
5
 
6
- # cbp-todo — Worktree-Ownership Regression
6
+ # cbp-todo — User-Ownership Regression
7
7
 
8
- Manual procedure verifying that `/cbp-todo` reads the pure-read `get_todos` queue and refuses to auto-route into work locked to another worktree. No automated harness exists for markdown skills; run these by hand (or read the queue with the MCP tools) whenever Step 0/1/1.5 of `SKILL.md` changes.
8
+ Manual procedure verifying that `/cbp-todo` reads the pure-read `get_todos` queue and refuses to auto-route into work locked to another user. No automated harness exists for markdown skills; run these by hand (or read the queue with the MCP tools) whenever Step 0/1/1.5 of `SKILL.md` changes.
9
9
 
10
10
  Repo under test: `2ff6d405-39c5-47b8-a6d1-59f998ac0537`. Resolve a real `user_id` with `npx codebyplan whoami --json`, or harvest a non-null `assigned_user_id` from `get_checkpoints`.
11
11
 
12
12
  ## Preconditions
13
13
 
14
14
  - `get_todos` is the only Step 1 read — confirm no `get_next_action` / `regenerate_todos_for_repo` call remains (`grep -n 'get_next_action\|regenerate_todos_for_repo' SKILL.md` → no hits).
15
- - Step 0 uses `resolve-worktree --json` and `whoami --json`.
15
+ - Step 0 uses `whoami --json` only no worktree-resolution CLI verb appears in SKILL.md (verify by grepping the banned token list against SKILL.md; zero hits expected).
16
16
  - Step 1.55 (Stale-Entity Guard) must NOT call `get_next_action` or `regenerate_todos_for_repo` when rejecting a stale head — it reuses the checkpoint `status` + task statuses already loaded in Step 1.5 (the same grep above covers it).
17
17
 
18
- ## Scenario A — caller owns the work → auto-trigger
18
+ ## Scenario A — current user owns the work → auto-trigger
19
19
 
20
- 1. From a worktree that owns the active checkpoint (caller `WORKTREE_ID` === target checkpoint `worktree_id`, or target `worktree_id` is `null`).
21
- 2. `get_todos({ repo_id, user_id, worktree_id })` returns a head whose target checkpoint is owned by the caller (or unscoped).
20
+ 1. `npx codebyplan whoami --json` returns `user_id` matching the target checkpoint's `assigned_user_id` (or `assigned_user_id` is `null` — open queue).
21
+ 2. `get_todos({ repo_id, user_id })` returns a head whose target checkpoint is owned by the caller (or unassigned).
22
22
  3. **Expected**: Step 1.5 ownership gate allows, Step 1.6 planning gate falls through, Step 4 auto-triggers `rows[0].command` (mapped to its `/cbp-<name>` form).
23
23
 
24
- ## Scenario B1server-generated conflict head → halt
24
+ ## Scenario Bcheckpoint assigned to a DIFFERENT user → halt
25
25
 
26
- 1. Caller `codebyplan-claude-2` (`38cd7dfa`). Active work assigned to `codebyplan-cli` (`016bd7f2`).
27
- 2. `get_todos` head is the server conflict todo: `state === "worktree_conflict"`, `command "/codebyplan:release-or-switch"`, `metadata.conflicting_worktree_name === "codebyplan-cli"`.
28
- 3. **MCP calls expected**: `get_todos`; `get_worktrees` (to resolve `016bd7f2` path for the message). NO `get_checkpoints` needed the server already resolved the conflict.
29
- 4. **Expected**: Step 1.5 server-conflict branch blocks and STOPS. The "Work mismatch" message names `codebyplan-cli (016bd7f2)` vs `codebyplan-claude-2 (38cd7dfa)`, offers Switch (`/Users/merilyviks/codebyplan-cli`) / other-work / session-end. NO auto-trigger; NO `release_assignment` call (never reassign the lock to the caller).
26
+ 1. `whoami` returns user A. Target checkpoint `assigned_user_id` is user B (non-null, different).
27
+ 2. `get_todos` head is a normal routing todo whose target checkpoint `assigned_user_id` is user B's UUID.
28
+ 3. **MCP calls expected**: `get_todos`; `get_checkpoints` (Step 1.5 load, reads the target `assigned_user_id`). Owner is identified by `assigned_user_id` directly (email from whoami context if resolvable, else the UUID) — no worktree-lookup MCP call is needed.
29
+ 4. **Expected**: Step 1.5 checkpoint-ownership branch blocks and STOPS with the "Work mismatch" message naming the owner email-or-uuid. NO auto-trigger; the Step 1.6 planning gate never runs (ownership precedes it). NEVER propose reassigning the checkpoint to the caller.
30
30
 
31
- ## Scenario B2normal head, mismatched checkpoint ownerhalt
31
+ ## Scenario Cempty queue / anonymous callerfallback
32
32
 
33
- 1. Caller `codebyplan-claude-2` (`38cd7dfa`). `get_todos` head is a normal routing todo whose target checkpoint `worktree_id` is `016bd7f2`.
34
- 2. **MCP calls expected**: `get_todos`; `get_checkpoints` (Step 1.5 load, reads the target `worktree_id`); `get_worktrees` (resolve `016bd7f2` name + path).
35
- 3. **Expected**: Step 1.5 checkpoint-ownership branch blocks and STOPS with the same "Work mismatch" message. NO auto-trigger; the Step 1.6 planning gate never runs (ownership precedes it).
36
-
37
- ## Scenario C — empty queue + cross-worktree current task → halt
38
-
39
- 1. `get_todos` returns `[]` (or `whoami` returned `null`, so Step 1 was skipped).
40
- 2. Step 3 fallback: `get_current_task({ repo_id, worktree_id })` / `get_checkpoints({ repo_id, worktree_id, status: 'active' })` surface a checkpoint whose `worktree_id` differs from the caller.
41
- 3. **Expected**: the Step 1.5 ownership gate (applied to the fallback target) blocks the discovered work with the same "Work mismatch" message. NO auto-trigger. `regenerate_todos_for_repo` is never called.
33
+ 1. `get_todos` returns `[]` OR `whoami` returned `null`/empty `user_id`, so Step 1 was skipped.
34
+ 2. Step 3 fallback: `get_current_task` / `get_checkpoints` surface a checkpoint whose `assigned_user_id` differs from the caller (or caller is anonymous).
35
+ 3. **Expected**: the Step 1.5 ownership gate (applied to the fallback target) blocks the discovered work with the "Work mismatch" message when the user differs, or routes to the idle suggestion when the caller is anonymous and no open work is found. NO auto-trigger. `regenerate_todos_for_repo` is never called.
42
36
 
43
37
  ## Scenario D — stale queue head for a completed/cancelled entity → halt
44
38
 
45
- 1. Caller owns the active checkpoint (Scenario A ownership holds), but the todo-worker queue is lagging — `health_check.todos_freshness_ok === false`. The `get_todos` head targets a checkpoint (or task) that has since been completed or cancelled.
46
- 2. Step 1.5 loads the target checkpoint `status` + `get_tasks(checkpoint_id)` (already required for the ownership/planning gates — no extra reads).
47
- 3. **Expected**: Step 1.55 rejects the auto-trigger — target checkpoint `status` is `completed`/`cancelled` (or every task is `completed`/`cancelled`) — surfaces the "Stale queue head" message naming the command + `CHK-NNN`[ `TASK-N`] + status, and STOPS. NO auto-trigger. NO `get_next_action` / `regenerate_todos_for_repo` call.
39
+ 1. Caller's `user_id` matches the target checkpoint's `assigned_user_id` (ownership passes), but the todo-worker queue is lagging — `health_check.todos_freshness_ok === false`. The `get_todos` head targets a checkpoint (or task) that has since been completed or cancelled.
40
+ 2. Step 1.5 loads the target checkpoint `status` + task statuses (already required for the ownership/planning gates — no extra reads).
41
+ 3. **Expected**: Step 1.55 rejects the auto-trigger — target checkpoint `status` is `completed`/`cancelled` (or every task is `completed`/`cancelled`) — surfaces the "Stale queue head" message naming the command + `CHK-NNN`[`TASK-N`] + status, and STOPS. NO auto-trigger. NO `get_next_action` / `regenerate_todos_for_repo` call.
48
42
 
49
- ## Edge — both null
43
+ ## Edge — null assigned_user_id (open work)
50
44
 
51
- Caller `WORKTREE_ID` empty AND target checkpoint `worktree_id` `null` → legitimate main-repo / unassigned work → auto-trigger allowed.
45
+ `assigned_user_id` is `null` on the target checkpoint AND caller has a valid `user_id` → open/unassigned work → auto-trigger allowed (proceeds to Step 1.6 planning gate).
@@ -58,5 +58,4 @@ human confirmation — do not add an AskUserQuestion in cbp-verify at round scop
58
58
 
59
59
  `codebyplan round update --id <round_id> --task-id <uuid> --checkpoint-id <uuid> --context '<json>'`
60
60
  (merge `verify_manifest` into existing context; the REPLACE contract requires the full object).
61
- Break-glass: MCP `update_round` (checkpoint KIND) / `update_standalone_round` (standalone KIND)
62
- pass `caller_worktree_id` on locked feat rows.
61
+ Break-glass: MCP `update_round` (checkpoint KIND) / `update_standalone_round` (standalone KIND).