codebyplan 1.10.2 → 1.11.0

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.
@@ -7,47 +7,95 @@ effort: low
7
7
 
8
8
  # Todo Command
9
9
 
10
- Single MCP call to determine next action, then load appropriate context before triggering the command. This is the **sole entry point** after `/clear` it ensures Claude always has full context before any command runs.
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.
11
11
 
12
12
  ## Instructions
13
13
 
14
- ### Step 0: Resolve Caller Worktree
14
+ ### Step 0: Resolve Caller Identity (worktree + user)
15
15
 
16
- Before any MCP call, derive the caller's `worktree_id` at runtime via `npx codebyplan resolve-worktree` so downstream calls can identify this worktree for hard-lock enforcement (CHK-104 TASK-2). The CLI resolves the tuple (device_id, repo path, current branch) against the worktrees table; CHK-108 removed the legacy `worktree_id` field from `.codebyplan/repo.json`.
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:
17
19
 
18
20
  ```bash
19
- WORKTREE_ID=$(npx codebyplan resolve-worktree 2>/dev/null)
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.
20
30
  ```
21
31
 
22
- - **`WORKTREE_ID` non-empty** forward as `worktree_id` to the `get_next_action` call in Step 1.
23
- - **`WORKTREE_ID` empty** → the current repo path + branch did not match any worktree row. The MCP server's hard-lock will reject mutations on assigned rows; surface a one-line note to the user that the worktree may need registration via `npx codebyplan setup` from this directory.
32
+ **User** `get_todos` (Step 1) requires a `user_id`:
33
+
34
+ ```bash
35
+ npx codebyplan whoami --json # → {"user_id":"<uuid>","email":"…"} or null
36
+ ```
37
+
38
+ - 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.
40
+
41
+ ### Step 1: Read the Todo Queue (pure-read)
42
+
43
+ With `USER_ID` resolved, call MCP `get_todos({ repo_id, user_id, worktree_id })` (omit `worktree_id` when `WORKTREE_ID` is `null` or unresolved — that returns only unscoped rows). Take **`rows[0]`** as the queue head (ordered by `sort_order`).
24
44
 
25
- ### Step 1: Get Next Action
45
+ - The head carries `command`, `instructions`, `state`, `metadata`, `worktree_id`, `checkpoint_id`, `task_id`.
46
+ - The routing context (checkpoint/task) lives in **`rows[0].metadata`**.
47
+ - `get_todos` is **pure-read** — `apps/todo-worker` is the sole regen authority. NEVER call `get_next_action` or `regenerate_todos_for_repo`.
48
+ - Empty array, or `USER_ID` unavailable → go to Step 3 (empty-queue fallback).
26
49
 
27
- Use MCP `get_next_action` with `repo_id` and `worktree_id` (if present from Step 0).
50
+ 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.
28
51
 
29
- ### Step 1.5: Checkpoint Planning Gate
52
+ ### Step 1.5: Resolve Target + Worktree-Ownership Gate
30
53
 
31
- Before honoring the command from Step 1, gate on the resolved active/next checkpoint's planning + activation state. This keeps work from starting on a half-baked or un-activated checkpoint. Resolve the checkpoint from the `get_next_action` response context (or MCP `get_current_task`), then load its `plan` + `status` via MCP `get_checkpoints` and its task count via MCP `get_tasks(checkpoint_id)`.
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.
32
55
 
33
- Evaluate two rules in order (Rule A wins if both could match):
56
+ Resolve the checkpoint from `rows[0].metadata` (or MCP `get_current_task`), then load its `worktree_id` + `plan` + `status` via MCP `get_checkpoints` and its task count via 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`.
34
57
 
35
- - **RULE A — unplanned**: empty `plan.steps[]` **AND** zero tasks → the checkpoint has not been planned. Suppress the Step-1 command; surface `Now planning CHK-NNN… handing off to /cbp-checkpoint-plan` and auto-trigger `/cbp-checkpoint-plan {NNN}`.
36
- - **RULE B — planned-but-pending**: has tasks (or non-empty `plan.steps[]`) **BUT** `status === "pending"` (not yet activated) → the checkpoint is planned but not started. Suppress the Step-1 command; surface `Now starting CHK-NNN… handing off to /cbp-checkpoint-start` and auto-trigger `/cbp-checkpoint-start {NNN}` (a planned checkpoint must be started + claimed before task work).
37
- - **Neither** (planned AND `active`) → fall through to Step 2 unchanged. No regression to the existing flow.
58
+ Two ownership signals:
38
59
 
39
- Skip this gate when `get_next_action` returns no checkpoint (idle see Step 3) or the command is `/cbp-session-start`.
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**.
66
+
67
+ On block, resolve the owning worktree's `name` + `path` via MCP `get_worktrees({ repo_id })` (match by id), then emit and STOP:
68
+
69
+ ```
70
+ ⚠ Work mismatch: CHK-<NNN> TASK-<N> is assigned to worktree <name> (<short-uuid>), not this one (<this-name> / <this-short-uuid>).
71
+ 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
74
+ C) /cbp-session-end
75
+ ```
76
+
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.
78
+
79
+ ### Step 1.6: Checkpoint Planning Gate
80
+
81
+ Ownership passed (Step 1.5). Now gate on the checkpoint's planning + activation state — reusing the `plan` + `status` + task count loaded in Step 1.5 — so work never starts on a half-baked or un-activated checkpoint. Evaluate two rules in order (Rule A wins if both could match):
82
+
83
+ - **RULE A — unplanned**: empty `plan.steps[]` **AND** zero tasks → not planned. Suppress the head command; surface `Now planning CHK-NNN… handing off to /cbp-checkpoint-plan` and auto-trigger `/cbp-checkpoint-plan {NNN}`.
84
+ - **RULE B — planned-but-pending**: has tasks (or non-empty `plan.steps[]`) **BUT** `status === "pending"` (not yet activated) → planned, not started. Suppress the head command; surface `Now starting CHK-NNN… handing off to /cbp-checkpoint-start` and auto-trigger `/cbp-checkpoint-start {NNN}` (a planned checkpoint must be started + claimed before task work).
85
+ - **Neither** (planned AND `active`) → fall through to Step 2.
86
+
87
+ Skip this gate when the routing target has no checkpoint (idle — see Step 3) or the command is `/cbp-session-start`.
40
88
 
41
89
  ### Step 2: Load Context Based on Command
42
90
 
43
- Before triggering the command, load the context it needs. This ensures `/clear` + `/cbp-todo` reliably restores full working context.
91
+ Once the gates pass, load the context the head command needs. This ensures `/clear` + `/cbp-todo` reliably restores full working context.
44
92
 
45
- **Use the context loading matrix below.** Match the `command` from the response to determine what to load.
93
+ **Use the context loading matrix below.** Match the `command` (in its `/cbp-<name>` form) to determine what to load.
46
94
 
47
95
  | Command Pattern | Context to Load |
48
96
  |----------------|-----------------|
49
97
  | `/cbp-session-start` | None — `/cbp-session-start` handles its own loading |
50
- | `/cbp-checkpoint-create` | If checkpoint exists in response context: load checkpoint via MCP `get_checkpoints` (filter by number). Display checkpoint title, goal, ideas summary |
98
+ | `/cbp-checkpoint-create` | If checkpoint exists in `rows[0].metadata`: load checkpoint via MCP `get_checkpoints` (filter by number). Display checkpoint title, goal, ideas summary |
51
99
  | `/cbp-checkpoint-plan` | Load checkpoint via MCP `get_checkpoints` (filter by number) + `get_tasks(checkpoint_id)`. Display checkpoint title, goal, ideas, existing task count |
52
100
  | `/cbp-checkpoint-start` | Load checkpoint via MCP `get_checkpoints` + `get_tasks(checkpoint_id)`. Display checkpoint title, status, claim state, first pending task |
53
101
  | `/cbp-task-start [N]` | Load via MCP `get_current_task`. Display checkpoint title + task title/requirements summary |
@@ -87,9 +135,13 @@ Display a brief context summary:
87
135
  [Brief: what was done, what passed/failed]
88
136
  ```
89
137
 
90
- ### Step 3: Suggest Session End When Idle
138
+ ### Step 3: Empty Queue Fallback, then Idle
139
+
140
+ Reached when `get_todos` returns `[]` or `USER_ID` was unavailable.
91
141
 
92
- If `get_next_action` returns no actionable `command` (null, empty, or explicit idle/done signal) i.e. no active checkpoint with pending work **do not auto-trigger anything**. Instead, suggest ending the session:
142
+ 1. **Fallback discovery** (worktree-scoped): MCP `get_current_task({ repo_id, worktree_id })` and `get_checkpoints({ repo_id, worktree_id, status: 'active' })` to discover whether actionable work exists for this caller.
143
+ 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.
144
+ 3. **Nothing actionable** → suggest ending the session:
93
145
 
94
146
  ```
95
147
  No work remaining in the current checkpoint.
@@ -98,14 +150,15 @@ Run `/cbp-session-end` to finalize the session log and close out.
98
150
  Or run `/cbp-checkpoint-create` to start new work.
99
151
  ```
100
152
 
101
- Wait for the user. Do not auto-trigger `/cbp-session-end` — session wrap-up is a user-driven decision.
153
+ Wait for the user. Do not auto-trigger `/cbp-session-end` — session wrap-up is a user-driven decision. NEVER call `regenerate_todos_for_repo`; an empty queue is a legitimate "nothing scheduled" state (the todo-worker has not regenerated yet, or there is no actionable work).
102
154
 
103
155
  ### Step 4: Display and Auto-trigger
104
156
 
105
- If Step 3 found actionable work, show `instructions` from the get_next_action response (gives user a chance to see what's happening), then auto-trigger the `command`.
157
+ Reached only when the Step 1.5 ownership gate allowed routing to continue and the Step 1.6 planning gate fell through (no hand-off). Show `rows[0].instructions` (so the user sees what is happening), then auto-trigger `rows[0].command` (its `/cbp-<name>` form).
106
158
 
107
159
  ## Integration
108
160
 
109
161
  - **Called by**: `/cbp-session-start`, `/cbp-task-complete`, `/cbp-checkpoint-complete`, manual, after `/clear`
110
- - **Reads**: MCP `get_next_action`, `get_current_task`, `get_rounds`, `get_checkpoints`, `get_tasks`
111
- - **Triggers**: `command` from response (auto); Step 1.5 gate overrides to `/cbp-checkpoint-plan` (unplanned) or `/cbp-checkpoint-start` (planned-but-pending)
162
+ - **Resolves**: `npx codebyplan resolve-worktree --json` (worktree id + distress signal), `npx codebyplan whoami --json` (user id)
163
+ - **Reads**: MCP `get_todos`, `get_current_task`, `get_rounds`, `get_checkpoints`, `get_tasks`, `get_worktrees`
164
+ - **Triggers**: `rows[0].command` (auto, after the Step 1.5 ownership gate); Step 1.6 overrides to `/cbp-checkpoint-plan` (unplanned) or `/cbp-checkpoint-start` (planned-but-pending)
@@ -0,0 +1,45 @@
1
+ ---
2
+ scope: org-shared
3
+ name: cbp-todo-qa-regression
4
+ description: Manual regression procedure for the cbp-todo worktree-ownership gate + get_todos switch (CHK-137 TASK-2)
5
+ ---
6
+
7
+ # cbp-todo — Worktree-Ownership Regression
8
+
9
+ 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.
10
+
11
+ 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`.
12
+
13
+ ## Preconditions
14
+
15
+ - `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).
16
+ - Step 0 uses `resolve-worktree --json` and `whoami --json`.
17
+
18
+ ## Scenario A — caller owns the work → auto-trigger
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).
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
+
24
+ ## Scenario B1 — server-generated conflict head → halt
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).
30
+
31
+ ## Scenario B2 — normal head, mismatched checkpoint owner → halt
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.
42
+
43
+ ## Edge — both null
44
+
45
+ Caller `WORKTREE_ID` empty AND target checkpoint `worktree_id` `null` → legitimate main-repo / unassigned work → auto-trigger allowed.