codebyplan 1.13.62 → 1.13.63

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 (31) 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-test-hooks.sh +0 -81
  6. package/templates/hooks/hooks.json +0 -9
  7. package/templates/rules/cbp-operating-gotchas.md +8 -10
  8. package/templates/rules/supabase-branch-lifecycle.md +2 -2
  9. package/templates/rules/todo-backend.md +11 -8
  10. package/templates/settings.project.base.json +0 -5
  11. package/templates/skills/cbp-build-cc-settings/reference/cbp-permission-policy.md +1 -1
  12. package/templates/skills/cbp-checkpoint-create/SKILL.md +12 -22
  13. package/templates/skills/cbp-checkpoint-end/SKILL.md +37 -0
  14. package/templates/skills/cbp-checkpoint-plan/SKILL.md +10 -8
  15. package/templates/skills/cbp-checkpoint-start/SKILL.md +27 -19
  16. package/templates/skills/cbp-checkpoint-update/SKILL.md +1 -1
  17. package/templates/skills/cbp-finalize/SKILL.md +2 -2
  18. package/templates/skills/cbp-round-complete/SKILL.md +3 -24
  19. package/templates/skills/cbp-round-plan/SKILL.md +1 -1
  20. package/templates/skills/cbp-session-end/SKILL.md +40 -30
  21. package/templates/skills/cbp-session-start/SKILL.md +7 -7
  22. package/templates/skills/cbp-session-start/qa-regression.md +32 -25
  23. package/templates/skills/cbp-standalone-task-complete/SKILL.md +2 -5
  24. package/templates/skills/cbp-standalone-task-create/SKILL.md +5 -13
  25. package/templates/skills/cbp-standalone-task-start/SKILL.md +10 -10
  26. package/templates/skills/cbp-task-create/SKILL.md +1 -1
  27. package/templates/skills/cbp-task-start/SKILL.md +10 -10
  28. package/templates/skills/cbp-todo/SKILL.md +23 -38
  29. package/templates/skills/cbp-todo/qa-regression.md +21 -27
  30. package/templates/skills/cbp-verify/reference/round-scope.md +1 -2
  31. package/templates/hooks/cbp-mcp-caller-worktree-inject.sh +0 -78
@@ -8,7 +8,7 @@ effort: high
8
8
 
9
9
  # Checkpoint Start Command
10
10
 
11
- The activation + claim gate of the checkpoint pipeline. `/cbp-checkpoint-plan` produces tasks but deliberately leaves the checkpoint `pending` and possibly unclaimed so it can sit in a team queue. This skill flips it to `active`, claims it for the caller's worktree if still open, and routes into the first task.
11
+ The activation + claim gate of the checkpoint pipeline. `/cbp-checkpoint-plan` produces tasks but deliberately leaves the checkpoint `pending` and unclaimed so it can sit in a team queue. This skill flips it to `active` the server auto-stamps `assigned_user_id` from the caller's JWT on activation and routes into the first task.
12
12
 
13
13
  ## Pipeline
14
14
 
@@ -20,7 +20,13 @@ The activation + claim gate of the checkpoint pipeline. `/cbp-checkpoint-plan` p
20
20
 
21
21
  ### Step 0: Parse `$ARGUMENTS`
22
22
 
23
- Source `repo_id` from `.codebyplan/repo.json`. Resolve caller worktree once for the whole skill: `CALLER_WT=$(npx codebyplan resolve-worktree 2>/dev/null)`.
23
+ Source `repo_id` from `.codebyplan/repo.json`. Resolve the current user once for the whole skill:
24
+
25
+ ```bash
26
+ npx codebyplan whoami --json # → {"user_id":"<uuid>","email":"…"} or null
27
+ ```
28
+
29
+ `USER_ID` = `user_id` from the result (may be null for anonymous/keychain-empty callers).
24
30
 
25
31
  | Shape | Resolves to |
26
32
  |-------|-------------|
@@ -31,55 +37,57 @@ Malformed (non-numeric, contains `-`): surface `checkpoint-start: invalid argume
31
37
 
32
38
  ### Step 1: Load Checkpoint + Tasks
33
39
 
34
- Read `.codebyplan/state/checkpoints/<id>.json` for `status`, `worktree_id`, `plan` (local-first; if missing/stale run `npx codebyplan sync` once; break-glass: MCP `get_checkpoints`). Read task files under `.codebyplan/state/checkpoints/<id>/tasks/*.json` (fallback: MCP `get_tasks(checkpoint_id)`).
40
+ Read `.codebyplan/state/checkpoints/<id>.json` for `status`, `assigned_user_id`, `plan` (local-first; if missing/stale run `npx codebyplan sync` once; break-glass: MCP `get_checkpoints`). Read task files under `.codebyplan/state/checkpoints/<id>/tasks/*.json` (fallback: MCP `get_tasks(checkpoint_id)`).
35
41
 
36
42
  ### Step 2: Planned-Gate
37
43
 
38
44
  A checkpoint must be planned before it can start.
39
45
 
40
- - **No tasks AND empty `plan.steps[]`** → refuse: surface `CHK-NNN is not planned yet.` and auto-trigger `/cbp-checkpoint-plan {NNN}`. STOP. (An unplanned checkpoint is `worktree_id`-null, so there is nothing to own yet — always kick off planning, matching `/cbp-todo` Rule A.)
46
+ - **No tasks AND empty `plan.steps[]`** → refuse: surface `CHK-NNN is not planned yet.` and auto-trigger `/cbp-checkpoint-plan {NNN}`. STOP. (An unplanned checkpoint is unassigned, so there is nothing to own yet — always kick off planning, matching `/cbp-todo` Rule A.)
41
47
  - **Already `active`** → no activation needed; skip to Step 3 for a claim-check only, then Step 5.
42
48
  - **`pending` with tasks** → proceed.
43
49
 
44
50
  ### Step 3: Claim Logic
45
51
 
46
- Compare the checkpoint's `worktree_id` against `CALLER_WT`:
52
+ Compare the checkpoint's `assigned_user_id` against `USER_ID`:
47
53
 
48
- | Checkpoint `worktree_id` | Action |
49
- |--------------------------|--------|
50
- | null (left open at create) | Claim it: in Step 4 pass `worktree_id: CALLER_WT`. If `CALLER_WT` is empty, warn the checkpoint will stay unclaimed and proceed without it. |
51
- | equals `CALLER_WT` | Already yours — no-op. |
52
- | a DIFFERENT worktree | STOP. Surface: `CHK-NNN is claimed by worktree {other}; current worktree is {CALLER_WT}. If {other} is dead, a maintainer can release it via the release_assignment MCP tool, then re-run /cbp-checkpoint-start.` Do not activate. |
54
+ | Checkpoint `assigned_user_id` | Action |
55
+ |-------------------------------|--------|
56
+ | null (open not yet claimed) | Proceed to Step 4 activation. The server will stamp `assigned_user_id = USER_ID` (from JWT) on `status=active`. If `USER_ID` is null (anonymous caller), warn the checkpoint will stay unclaimed and proceed. |
57
+ | equals `USER_ID` | Already yours — no-op. Proceed to Step 5. |
58
+ | a DIFFERENT user (non-null) | STOP. Surface: `CHK-NNN is assigned to user <assigned_user_id-email-or-uuid>; you are <USER_ID-email-or-uuid>. Ask the owner to release the assignment, or a maintainer can use the release_assignment tool, then re-run /cbp-checkpoint-start.` Do not activate. |
53
59
 
54
- This mirrors the CHK-104 hard-lock model — never wrest a checkpoint from a live worktree.
60
+ Never wrest a checkpoint from another user.
55
61
 
56
62
  ### Step 4: Activate
57
63
 
58
- If the checkpoint is already `active` AND `worktree_id` already equals `CALLER_WT` (the Step 3 no-op row), skip this step entirely and proceed to Step 5 — nothing to write.
64
+ If the checkpoint is already `active` AND `assigned_user_id` already equals `USER_ID` (the Step 3 no-op row), skip this step entirely and proceed to Step 5 — nothing to write.
65
+
66
+ Otherwise set the checkpoint `active` via `codebyplan checkpoint update --id <checkpoint-id> --status active` (CLI write-through; break-glass: MCP `update_checkpoint`). The server auto-stamps `assigned_user_id = ctx.userId` (from the JWT) on activation — no client-side user param required. After the write, read the row back to confirm `assigned_user_id` is non-null.
59
67
 
60
- Otherwise set the checkpoint `active` via `codebyplan checkpoint update --id <checkpoint-id> --status active` (CLI write-through; break-glass: MCP `update_checkpoint`), plus `--worktree-id CALLER_WT` when claiming per Step 3. For MCP break-glass path: `caller_worktree_id` identifies the calling worktree and is auto-injected by the `cbp-mcp-caller-worktree-inject.sh` PreToolUse hook (CHK-198 TASK-2); the server falls back to the repo `main` worktree only when it is absent. If the checkpoint was already `active` but a claim is still needed, skip the status write and only write `worktree_id`.
68
+ If the checkpoint was already `active` but `assigned_user_id` is still null (edge case: prior activation without a valid JWT), re-read and surface a warning rather than silently proceeding.
61
69
 
62
70
  ### Step 5: Route
63
71
 
64
72
  Follow the close-out routing convention — auto-trigger the next same-context step, never an A/B/C menu. `{first-pending-task}` is the lowest-numbered pending task from Step 1 (not necessarily TASK-1, since additive re-planning may have completed earlier ones):
65
73
 
66
- - **Claimed by THIS session** (`CALLER_WT` now owns the checkpoint): auto-trigger `/cbp-task-start {chk}-{first-pending-task}` in the same context.
67
- - **`CALLER_WT` empty / unresolved**: surface a single directive — `Next: /cbp-task-start {chk}-{first-pending-task}` — and let the user proceed.
74
+ - **Claimed by THIS session** (`USER_ID` is non-null and now owns the checkpoint): auto-trigger `/cbp-task-start {chk}-{first-pending-task}` in the same context.
75
+ - **`USER_ID` null / anonymous**: surface a single directive — `Next: /cbp-task-start {chk}-{first-pending-task}` — and let the user proceed.
68
76
 
69
77
  Show a one-line confirmation before routing:
70
78
 
71
79
  ```
72
80
  ## Checkpoint Started
73
81
 
74
- **CHK-NNN**: [title] • **Status**: active • **Claimed by**: [worktree or "open"]
82
+ **CHK-NNN**: [title] • **Status**: active • **Claimed by**: [user email or "open"]
75
83
  **Next task**: TASK-[N] — [title]
76
84
  **Worktree**: `npx codebyplan worktree add CHK-{NNN}`
77
85
  ```
78
86
 
79
87
  ## Integration
80
88
 
81
- - **Reads**: `.codebyplan/state/checkpoints/<id>.json`, `checkpoints/<id>/tasks/*.json` (local-first; `npx codebyplan sync` if stale; break-glass: MCP `get_checkpoints`, `get_tasks`); `npx codebyplan resolve-worktree`
82
- - **Writes**: `codebyplan checkpoint update --status active [--worktree-id <id>]` (CLI write-through; break-glass: MCP `update_checkpoint`; `caller_worktree_id` auto-injected by cbp-mcp-caller-worktree-inject.sh hook on the MCP path)
83
- - **Triggered by**: `/cbp-checkpoint-plan` (auto when claimed at create), `/cbp-todo` (planned-but-pending gate), or user directly
89
+ - **Reads**: `.codebyplan/state/checkpoints/<id>.json`, `checkpoints/<id>/tasks/*.json` (local-first; `npx codebyplan sync` if stale; break-glass: MCP `get_checkpoints`, `get_tasks`); `npx codebyplan whoami --json`
90
+ - **Writes**: `codebyplan checkpoint update --status active` (CLI write-through; break-glass: MCP `update_checkpoint`; server auto-stamps `assigned_user_id` from JWT on activation)
91
+ - **Triggered by**: `/cbp-checkpoint-plan` (auto when open/unclaimed or assigned to the current user), `/cbp-todo` (planned-but-pending gate), or user directly
84
92
  - **Triggers**: `/cbp-task-start` (auto when claimed), or `/cbp-checkpoint-plan` (when the checkpoint is unplanned)
85
93
  - **Never**: plans or creates tasks — that is `/cbp-checkpoint-plan`
@@ -43,7 +43,7 @@ Given the parse from Step 0.5:
43
43
  | Parse | Resolution path |
44
44
  |-------|-----------------|
45
45
  | `{chk}` | Scan `.codebyplan/state/checkpoints/*.json` for `number === {chk}` (local-first; if missing/stale run `npx codebyplan sync` once; break-glass: MCP `get_checkpoints`). |
46
- | _(empty)_ | Read `.codebyplan/state/session/current.json` (with worktree_id from `npx codebyplan resolve-worktree`) to find the active checkpoint (fallback: MCP `get_current_task`). If no active checkpoint, scan local state for `pending` checkpoints (fallback: MCP `get_checkpoints` filtered by `pending`). |
46
+ | _(empty)_ | Read `.codebyplan/state/session/current.json` to find the active checkpoint (fallback: MCP `get_current_task`). If no active checkpoint, scan local state for `pending` checkpoints (fallback: MCP `get_checkpoints` filtered by `pending`). |
47
47
 
48
48
  ### Step 1.5: Detect Entry Context (from `/cbp-finalize` expand path)
49
49
 
@@ -133,7 +133,7 @@ Skip the push only when nothing was committed in Step 5 AND `/cbp-merge-main` re
133
133
 
134
134
  ### Step 7: Complete Task
135
135
 
136
- MCP `complete_task(task_id)` kept on MCP because the CLI `codebyplan task complete` sends an empty POST body and cannot forward `caller_worktree_id`, which the server uses to enforce the mutate-lock. `caller_worktree_id` is auto-injected by the `cbp-mcp-caller-worktree-inject.sh` PreToolUse hook (CHK-198 TASK-2); the server falls back to the repo `main` worktree only when it is absent, then enforces the mutate-lock. The server auto-clears `assigned_user_id` + `assigned_worktree_id` on the task; if this was the last sibling task, it also clears the parent checkpoint's assignment. (Per CHK-104 hard-lock.)
136
+ MCP `complete_task(task_id)`. The server keys on the JWT user (`ctx.userId`) no worktree param is needed. The server auto-clears `assigned_user_id` on the task; if this was the last sibling task, it also clears the parent checkpoint's assignment.
137
137
 
138
138
  ### Step 8: Run Cleanup + Migration (inline)
139
139
 
@@ -190,7 +190,7 @@ direct you to run `/cbp-clear-prep` first; otherwise checkpoint-check starts on
190
190
  - **Triggered by**: `/cbp-verify` (auto, scope=task, when it writes `verify_verdict.verdict === 'READY'`)
191
191
  - **Chain**: `/cbp-verify` (scope=task READY) → `/cbp-finalize`
192
192
  - **Reads**: `.codebyplan/state/checkpoints/*.json`, `checkpoints/<id>/tasks/*.json`, `checkpoints/<id>/tasks/<id>/rounds/*.json`, `todos.json` (local-first; `npx codebyplan sync` on miss; MCP `get_current_task`/`get_rounds`/`get_tasks` break-glass) — including each round's `verify_manifest` and `task.context.verify_verdict`
193
- - **Writes**: `codebyplan task update` for `files_changed` (CLI write-through; MCP `update_task` break-glass); MCP `complete_task` for task completion (kept MCP — CLI cannot forward `caller_worktree_id`)
193
+ - **Writes**: `codebyplan task update` for `files_changed` (CLI write-through; MCP `update_task` break-glass); MCP `complete_task` for task completion
194
194
  - **Uses skills (inline, no sub-agent)**: `cleanup` (if deletions), `migration` (if exports renamed)
195
195
  - **Triggers**: Same-context transitions auto-trigger via the Skill tool (next task in checkpoint → `cbp-task-start {N}`, `allow`-tier, fires silently). Checkpoint-done → auto-triggers `cbp-checkpoint-check` via Skill tool (`ask`-tier, permission prompt IS the human gate). No-task-anywhere fallback → directive `Next: Run /clear, then /cbp-session-end.`
196
196
  - **Checkpoint-bound only** — for standalone tasks use `/cbp-standalone-task-complete`
@@ -25,7 +25,7 @@ Set `KIND` for the rest of this skill. MCP tool names vary by KIND:
25
25
  | Get task | local state (break-glass: `get_current_task`) | `get_current_standalone_task(repo_id)` |
26
26
  | Get rounds | local state (break-glass: `get_rounds`) | `get_standalone_rounds(standalone_task_id)` |
27
27
  | Update round | `update_round(round_id, ...)` | `update_standalone_round(standalone_round_id, ...)` |
28
- | Complete round | `complete_round(round_id, duration_minutes?)` | `complete_standalone_round(standalone_round_id, duration_minutes?, caller_worktree_id)` ⚠️ `caller_worktree_id` is REQUIRED for standalone |
28
+ | Complete round | `complete_round(round_id, duration_minutes?)` | `complete_standalone_round(standalone_round_id, duration_minutes?)` |
29
29
 
30
30
  # Round Complete Command
31
31
 
@@ -84,20 +84,7 @@ Reconcile which files the user has approved by staging them. Run:
84
84
  npx codebyplan round sync-approvals --round-id <round_id> --task-id <task_id>
85
85
  ```
86
86
 
87
- The CLI auto-resolves the caller worktree id with the following precedence:
88
- 1. `--caller-worktree-id <uuid>` override (if passed — skips all resolution)
89
- 2. Per-device branch-keyed cache (`.codebyplan/worktree.local.json`)
90
- 3. In-process tuple API call: `POST /worktrees/resolve` using `(device_id, repo_path, branch)`
91
-
92
- On the write path (non `--dry-run`), if the worktree id cannot be resolved the CLI **hard-fails with exit 1** and prints an actionable message. To pre-populate the cache:
93
-
94
- ```
95
- npx codebyplan resolve-worktree --cache
96
- ```
97
-
98
- If this worktree is not yet registered, run `npx codebyplan setup` first, then re-run `/cbp-round-complete`.
99
-
100
- The CLI parses `git status --short`, merges drift + staging + web-UI flag, and writes both round and task (forwarding `caller_worktree_id` on both writes so the server honors the feat-worktree lock). A **cleanly staged** file (`git add`-ed, no further unstaged changes) becomes `user_approved: true`.
87
+ The CLI parses `git status --short`, merges drift + staging + web-UI flag, and writes both round and task. A **cleanly staged** file (`git add`-ed, no further unstaged changes) becomes `user_approved: true`.
101
88
 
102
89
  Read the stdout JSON: `{ added, stale_marked, reactivated, total_files }`.
103
90
 
@@ -110,14 +97,7 @@ This is the **single** explicit reconcile owned by this skill. (The `cbp-mcp-rou
110
97
  Calculate duration from the round's `started_at` to now in minutes.
111
98
 
112
99
  - **checkpoint KIND**: `codebyplan round complete --id <round_id> --task-id <task_id> --checkpoint-id <checkpoint_id>` (CLI write-through: local state file + REST). Break-glass fallback: MCP `complete_round(round_id, duration_minutes)` when the CLI is unavailable. Note: the CLI does not accept `duration_minutes`; the backend computes duration server-side.
113
- - **standalone KIND**: MCP `complete_standalone_round(standalone_round_id, duration_minutes, caller_worktree_id)`. ⚠️ `caller_worktree_id` is REQUIRED resolve via `CALLER_WT=$(npx codebyplan resolve-worktree 2>/dev/null)`. If `CALLER_WT` is empty, surface this warning and ask the user to confirm before proceeding:
114
-
115
- ```
116
- Warning: could not resolve caller_worktree_id (npx codebyplan resolve-worktree returned empty).
117
- The complete_standalone_round call may be rejected by the pre-guard. Proceed anyway? (yes / no)
118
- ```
119
-
120
- If the user confirms yes, proceed with `caller_worktree_id: ""`. If no, stop.
100
+ - **standalone KIND**: MCP `complete_standalone_round(standalone_round_id, duration_minutes)`. The server keys on the JWT user (`ctx.userId`) no worktree param needed or accepted.
121
101
 
122
102
  `complete_round` / `complete_standalone_round` sets the round `completed`, locks all `file_changes` for the round (`approval_locked: true`), and returns `unapproved_files[]` + `unapproved_count`. Hold those for routing.
123
103
 
@@ -158,7 +138,6 @@ Payload: `round.context.round_complete = { staged_count, unstaged_count, route,
158
138
  - **Permission prompt = confirmation** — gated by `ask`-tier `Skill(cbp-round-complete)`. Because the skill is `disable-model-invocation: true`, this gate applies to the user's **direct** `/cbp-round-complete` invocation. NEVER add an AskUserQuestion to confirm running; the harness prompt is the gate. A declined permission is a clean no-op.
159
139
  - **Step 2 (CLI) must exit 0** — if it fails, STOP before `complete_round`. The merge semantics are enforced by the CLI.
160
140
  - **NEVER ask the user to git add files** — Step 2 only reads staging status. **NEVER stage files** — Claude does not touch the git staging area; the user's `git add` is the approval signal.
161
- - **standalone KIND Step 3**: `caller_worktree_id` is REQUIRED for `complete_standalone_round` — always resolve and pass it.
162
141
 
163
142
  ## Integration
164
143
 
@@ -25,7 +25,7 @@ Set `KIND` for the rest of this skill. Read/write sources vary by KIND:
25
25
  | Get rounds | local state (break-glass: `get_rounds`) | `get_standalone_rounds(standalone_task_id)` |
26
26
  | Add round | `codebyplan round add` (break-glass: `add_round`) | `add_standalone_round(standalone_task_id, ...)` |
27
27
  | Update round | `codebyplan round update` (break-glass: `update_round`) | `update_standalone_round(standalone_round_id, ...)` |
28
- | Complete round | `complete_round(round_id, duration_minutes?)` | `complete_standalone_round(standalone_round_id, duration_minutes?, caller_worktree_id)` ⚠️ `caller_worktree_id` is REQUIRED for standalone |
28
+ | Complete round | `complete_round(round_id, duration_minutes?)` | `complete_standalone_round(standalone_round_id, duration_minutes?)` |
29
29
  | Update task | `codebyplan task update` (break-glass: `update_task`) | `update_standalone_task(standalone_task_id, ...)` |
30
30
 
31
31
  > **Note**: The `standalone` KIND column uses MCP tools unchanged — standalone local-first migration is out of scope and will be addressed in a later task.
@@ -22,35 +22,46 @@ Always write a session log for this session — **even if empty**. `/cbp-session
22
22
  2. Pull facts from local state files rather than narrating from memory:
23
23
  - Rounds added/completed, tasks advanced/completed during this session (read from `.codebyplan/state/checkpoints/` subtree)
24
24
  - Decisions, blockers, or discoveries recorded in checkpoint/task context
25
-
26
- ### Step 1.3: Capture Handoff Snapshot
27
-
28
- Snapshot the current next-action so the next `/cbp-session-start` (Step 4.5) can auto-resume. The handoff write-path + payload shape are specified inline here and in `/cbp-session-start` Step 4.5 (freshness gate).
29
-
30
- 1. Read `.codebyplan/state/todos.json` (local-first) and take the queue head `rows[0]` (rows are ordered by `sort_order`; `rows[0]` is the current next-action). If missing/stale, run `npx codebyplan sync` once and re-read. Break-glass fallback: MCP `get_todos({ repo_id, worktree_id })` when the state dir is absent and sync fails. The worker stamps `command`, `instructions`, `state`, and the entity ids `checkpoint_id` / `task_id` / `round_id` on every row.
31
- 2. If `rows[0]` exists and its `command` is non-empty (active work in flight):
32
- ```yaml
33
- handoff:
34
- command: <rows[0].command> # e.g. "/cbp-verify"
35
- instructions: <rows[0].instructions> # human-readable trigger reason
36
- state: <rows[0].state> # workflow state label
37
- context: # entity ids used by the Step 4.5 freshness probe
38
- checkpoint_id: <rows[0].checkpoint_id>
39
- task_id: <rows[0].task_id>
40
- round_id: <rows[0].round_id>
41
- captured_at: <ISO now> # for entity-drift freshness comparison
42
- captured_session_log_id: <current session log id>
43
- ```
44
- 3. If the queue is empty or `rows[0].command` is empty / idle: set `handoff = null` so the next session's probe falls through to `/cbp-todo`.
45
- 4. Hold `handoff` in context for Step 1's `update_session_log` call below — it ships in the same write as `ended_at` and `summary`.
46
-
47
- Continuing Step 1:
48
-
49
- 3. Run `codebyplan session update-log --id <log-id> --ended-at <now> --summary <text> --pending <text> --handoff <json>` (CLI write-through: updates `.codebyplan/state/session/current.json` + REST). Break-glass fallback: MCP `update_session_log` when the CLI is unavailable. Fields:
25
+ 3. Run `codebyplan session update-log --id <log-id> --ended-at <now> --summary <text> --pending <text> --git-branch <current-branch>` (CLI write-through: updates `.codebyplan/state/session/current.json` + REST). Break-glass fallback: MCP `update_session_log` when the CLI is unavailable. Fields:
50
26
  - `ended_at`: now (maps to the `closed_at` column per TASK-2 alias)
51
- - `handoff`: from Step 1.3 (jsonb or `null`; MCP write surface aliases to the `content` column transparently per CHK-111 Migration A — `handoff` wins over `content` when both are passed)
52
27
  - `summary`: concise — may be empty if nothing happened
53
28
  - `pending`: open items the next session should see first
29
+ - `git_branch`: the current git branch (informational; `git rev-parse --abbrev-ref HEAD`)
30
+ - **Do NOT pass `--handoff` / `handoff` field.** The DB handoff field is no longer written here. Omit entirely — never pass `handoff:null` (it clobbers summary/pending content).
31
+
32
+ ### Step 1.3: Optional Handoff File (mid-work only)
33
+
34
+ When an active in-progress task or round exists, offer to write a handoff file so the next session can auto-resume. Skip entirely when not mid-work.
35
+
36
+ 1. Read `.codebyplan/state/todos.json` (local-first) and check `rows[0]` (queue head, ordered by `sort_order`). If missing/stale, run `npx codebyplan sync` once and re-read. Break-glass fallback: MCP `get_todos({ repo_id })` when the state dir is absent and sync fails. The worker stamps `command`, `instructions`, `state`, and entity ids `checkpoint_id` / `task_id` / `round_id` on every row.
37
+
38
+ 2. **If `rows[0]` exists and its `command` is non-empty** (active work in flight):
39
+ - Determine the handoff file path. The directory is keyed by the human-facing **number** (`<NNN>` checkpoint number / `<N>` standalone task number) — matching the committed `.codebyplan/checkpoint/<NNN>/` plan-artifact convention and the session-start resume probe (which globs by number). Resolve the number from local state (break-glass: MCP):
40
+ - Checkpoint work: read `.codebyplan/state/checkpoints/<rows[0].checkpoint_id>.json` → `number` (break-glass: MCP `get_checkpoints`). Path: `.codebyplan/checkpoint/<NNN>/handoff.md`.
41
+ - Standalone work: read the standalone task's `number` from local standalone state (break-glass: MCP `get_standalone_tasks`). Path: `.codebyplan/standalone/<N>/handoff.md`.
42
+ - Offer to write the file:
43
+ ```
44
+ Write handoff file for next session?
45
+ <path>
46
+
47
+ Reply: yes | no
48
+ ```
49
+ - On `yes`: write the file with this structure:
50
+ ```markdown
51
+ ---
52
+ command: <rows[0].command>
53
+ state: <rows[0].state>
54
+ captured_at: <ISO now>
55
+ checkpoint_id: <rows[0].checkpoint_id or null>
56
+ task_id: <rows[0].task_id or null>
57
+ round_id: <rows[0].round_id or null>
58
+ ---
59
+
60
+ <rows[0].instructions — human-readable trigger reason>
61
+ ```
62
+ - The file is committed with the session's final commit (Step 1.5) or the user can stage it manually.
63
+
64
+ 3. **If the queue is empty or `rows[0].command` is empty/idle**: skip — no handoff file is written.
54
65
 
55
66
  ### Step 1.5: Commit Non-Task Files
56
67
 
@@ -117,7 +128,7 @@ Stop this worktree's Realtime watch daemon, non-blocking and non-fatal:
117
128
  npx codebyplan watch stop 2>/dev/null || true
118
129
  ```
119
130
 
120
- This SIGTERMs the per-worktree pidfile daemon started by `cbp-session-start-hook.sh`. Silently ignored when no daemon is running for this worktree. If there is no daemon to stop (e.g. session-start was skipped), `watch stop` itself exits 0 — the `|| true` guard is defense-in-depth only, so Step 3's session-state write never sees a failure from this step.
131
+ This SIGTERMs the per-worktree pidfile daemon started by `cbp-session-start-hook.sh`. Silently ignored when no daemon is running. If there is no daemon to stop (e.g. session-start was skipped), `watch stop` itself exits 0 — the `|| true` guard is defense-in-depth only, so Step 3's session-state write never sees a failure from this step.
121
132
 
122
133
  ### Step 3: Update Session State
123
134
 
@@ -134,9 +145,8 @@ You can close this window.
134
145
  ## Integration
135
146
 
136
147
  - **Triggered by**: user invocation (prompted by `/cbp-todo` when no work remains)
137
- - **Reads**: `.codebyplan/repo.json`; local-first reads (with `npx codebyplan sync` + MCP break-glass): `.codebyplan/state/session/current.json` (Step 1 resolve log), `.codebyplan/state/todos.json` (Step 1.3 handoff snapshot + Step 1.5 active-task lookup), `.codebyplan/state/checkpoints/<id>/tasks/<id>/rounds/` (Step 1.5 task-file resolution); `codebyplan session freshness-gate` (Step 1.7 package-freshness gate, without --halt-on-update); `codebyplan session infra-files --json --task-files <csv>` (Step 1.5 infra-file set math)
138
- - **Writes**: `codebyplan session update-log --id <id> ...` (Step 1 finalize — CLI write-through to `.codebyplan/state/session/current.json`; break-glass: MCP `update_session_log`), `codebyplan session create-log` (Step 1 fallback when no log exists; break-glass: MCP `create_session_log`), `codebyplan session update-state --action deactivate` (Step 3 — CLI write-through to `.codebyplan/state/session/state.json`; break-glass: MCP `update_session_state`)
148
+ - **Reads**: `.codebyplan/repo.json`; local-first reads (with `npx codebyplan sync` + MCP break-glass): `.codebyplan/state/session/current.json` (Step 1 resolve log), `.codebyplan/state/todos.json` (Step 1.3 mid-work detection + Step 1.5 active-task lookup), `.codebyplan/state/checkpoints/<id>/tasks/<id>/rounds/` (Step 1.5 task-file resolution); `codebyplan session freshness-gate` (Step 1.7 package-freshness gate, without --halt-on-update); `codebyplan session infra-files --json --task-files <csv>` (Step 1.5 infra-file set math)
149
+ - **Writes**: `codebyplan session update-log --id <id> --git-branch <branch> ...` (Step 1 finalize — CLI write-through to `.codebyplan/state/session/current.json`; break-glass: MCP `update_session_log`; handoff field never passed), `codebyplan session create-log` (Step 1 fallback when no log exists; break-glass: MCP `create_session_log`), optional handoff file at `.codebyplan/checkpoint/<NNN>/handoff.md` or `.codebyplan/standalone/<N>/handoff.md` (Step 1.3, mid-work only), `codebyplan session update-state --action deactivate` (Step 3 — CLI write-through to `.codebyplan/state/session/state.json`; break-glass: MCP `update_session_state`)
139
150
  - **Spawns**: none
140
151
  - **Triggers**: none at the skill-contract level. Step 1.5 may invoke `/cbp-git-commit` inline on user approval; Step 1.7 may invoke `/cbp-git-commit` on the `result === 'updated'` path (committing changed `.claude/` and `.codebyplan/` paths).
141
152
  - **Paired with**: `/cbp-session-start`
142
- - **Pairs with**: `/cbp-session-start` Step 4.5 (handoff payload shape + freshness-gate contract; specified inline — no separate rule file)
@@ -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