codebyplan 1.13.57 → 1.13.58
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codebyplan",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.58",
|
|
4
4
|
"description": "CLI for CodeByPlan — AI-powered development planning and tracking",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@eslint/js": "^9.18.0",
|
|
54
54
|
"@types/node": "^20",
|
|
55
55
|
"@vitest/eslint-plugin": "^1.1.44",
|
|
56
|
-
"esbuild": "
|
|
56
|
+
"esbuild": ">=0.28.1",
|
|
57
57
|
"eslint": "^9.18.0",
|
|
58
58
|
"eslint-config-prettier": "^10.0.1",
|
|
59
59
|
"eslint-plugin-no-secrets": "^2.2.1",
|
|
@@ -163,7 +163,7 @@ jobs:
|
|
|
163
163
|
|
|
164
164
|
- name: Post release metadata to API
|
|
165
165
|
env:
|
|
166
|
-
|
|
166
|
+
DESKTOP_RELEASE_TOKEN: ${{ secrets.DESKTOP_RELEASE_TOKEN }}
|
|
167
167
|
VERSION: ${{ needs.check-version.outputs.version }}
|
|
168
168
|
run: |
|
|
169
169
|
TAG="desktop-v${VERSION}"
|
|
@@ -205,7 +205,7 @@ jobs:
|
|
|
205
205
|
curl -fL -X POST \
|
|
206
206
|
"https://www.codebyplan.com/api/desktop/releases" \
|
|
207
207
|
-H "Content-Type: application/json" \
|
|
208
|
-
-H "x-
|
|
208
|
+
-H "x-release-token: ${DESKTOP_RELEASE_TOKEN}" \
|
|
209
209
|
-d "$(jq -n \
|
|
210
210
|
--arg version "${VERSION}" \
|
|
211
211
|
--arg notes "${NOTES}" \
|
|
@@ -11,14 +11,14 @@ Activate the session, open a fresh session log, and surface the previous log's p
|
|
|
11
11
|
|
|
12
12
|
## Instructions
|
|
13
13
|
|
|
14
|
-
Run
|
|
14
|
+
Run Step 0 silently (hard gate). Then run `codebyplan session start --json` (Step 1 through Step 5.8 collapsed into one CLI call). Parse the envelope and print the `rendered_block`. Apply Claude-side Step 5.7 (interactive commit gate) and Step 7 (routing) from the envelope. Produce ONE output block, then auto-trigger or stop per Step 7.
|
|
15
15
|
|
|
16
16
|
### Step 0: MCP Health Gate
|
|
17
17
|
|
|
18
18
|
Call MCP `health_check` tool. **The MCP connection is vital — this is a hard gate.**
|
|
19
19
|
|
|
20
|
-
- **If succeeds**: Continue silently to
|
|
21
|
-
- **If fails**: Print the error below and **STOP the entire session-start**. Do NOT continue
|
|
20
|
+
- **If succeeds**: Continue silently to the orchestrator.
|
|
21
|
+
- **If fails**: Print the error below and **STOP the entire session-start**. Do NOT continue — no activate, no create-log, no `/cbp-todo` trigger:
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
✖ MCP connection failed — session-start aborted. Check:
|
|
@@ -29,237 +29,88 @@ Call MCP `health_check` tool. **The MCP connection is vital — this is a hard g
|
|
|
29
29
|
Fix the connection, then run /cbp-session-start again.
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
###
|
|
32
|
+
### Steps 1–5.8: Orchestrator
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- `repo_id` (UUID) — from `.codebyplan/repo.json`, required for all MCP operations
|
|
37
|
-
- `git_branch` — from `.codebyplan/git.json`
|
|
38
|
-
|
|
39
|
-
Resolve `worktree_id` at runtime using the structured JSON form:
|
|
34
|
+
Run the CLI orchestrator and parse the JSON envelope it emits:
|
|
40
35
|
|
|
41
36
|
```bash
|
|
42
|
-
|
|
43
|
-
# → {"worktree_id":"<uuid>|null","error_kind":null|"<kind>"}
|
|
37
|
+
codebyplan session start --json
|
|
44
38
|
```
|
|
45
39
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
npx codebyplan arch-map drift 2>/dev/null | grep -c '^[[:space:]]*DRIFTED' || true
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
If the count is `> 0`, hold this single line for Step 6:
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
⚠ .claude/architecture is N module(s) stale — run /cbp-refresh-arch-map
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
A count of `0` (all maps fresh, no stamped modules, or any probe failure) emits nothing.
|
|
93
|
-
|
|
94
|
-
Fully non-blocking; every failure path falls through with no output. The `arch-map` CLI is
|
|
95
|
-
itself hook-safe (exits 0 on any internal error), so a missing or too-old `codebyplan`
|
|
96
|
-
binary simply yields a zero count and no line.
|
|
97
|
-
|
|
98
|
-
### Step 1.6: Package Freshness Gate
|
|
99
|
-
|
|
100
|
-
Check whether a newer `codebyplan` is published and safe to auto-install on this worktree's current branch. Runs AFTER the architecture-map drift check (Step 1.55) and BEFORE session activation (Step 3).
|
|
101
|
-
|
|
102
|
-
Run `codebyplan session freshness-gate --halt-on-update` and parse the JSON output (`{ result: 'skipped'|'guarded'|'up_to_date'|'updated'|'error', ... }`):
|
|
103
|
-
|
|
104
|
-
- **Probe failed** — the command errored or output cannot be parsed as JSON. → **FAIL-SAFE SKIP**: proceed silently to Step 3. Never disrupt a session over a best-effort freshness probe — the MCP gate (Step 0) is the only vital gate.
|
|
105
|
-
- **`result === 'skipped'` / `'guarded'` / `'up_to_date'`** → skip silently, proceed to Step 3. Gate on the `result` field only.
|
|
106
|
-
- **`result === 'error'`** → fail-safe skip, proceed to Step 3.
|
|
107
|
-
- **`result === 'updated'`** → the CLI already ran the install + `npx codebyplan claude update`. Parse the JSON response:
|
|
108
|
-
- If `changed_files[]` is present and non-empty, offer the same commit gate as Step 5.7:
|
|
109
|
-
|
|
110
|
-
```
|
|
111
|
-
codebyplan updated. Commit the resulting .claude/ and .codebyplan/ changes before exiting?
|
|
112
|
-
[list of changed paths under .claude/ and .codebyplan/]
|
|
113
|
-
|
|
114
|
-
Reply: yes | no | select
|
|
115
|
-
```
|
|
40
|
+
The orchestrator performs (in order, all fail-safe):
|
|
41
|
+
|
|
42
|
+
1. Resolve `repo_id` from `.codebyplan/repo.json`
|
|
43
|
+
2. `codebyplan resolve-worktree --json` → `worktree_id`, `worktree_error_kind`
|
|
44
|
+
3. `codebyplan session freshness-gate --halt-on-update` → update_halt short-circuit (no activate, no create-log when halt)
|
|
45
|
+
4. Infra-drift nudge (monorepo-only), arch-map drift nudge, LSP binary nudge
|
|
46
|
+
5. Read previous session log from `.codebyplan/state/session/current.json` (sync on miss)
|
|
47
|
+
6. Handoff freshness probe (Step 4.5 gate — uses previous row before create-log overwrites it)
|
|
48
|
+
7. `codebyplan session update-state --action activate` (write-through)
|
|
49
|
+
8. `codebyplan session create-log` → `session_log_id`
|
|
50
|
+
9. Infra-files set math: active task round files subtracted from `git status --porcelain`
|
|
51
|
+
10. `get_checkpoints({ repo_id, status:'active' })` → ownership partition (owned vs cross)
|
|
52
|
+
11. Compute `next_action` + render the Step 6 output block
|
|
53
|
+
|
|
54
|
+
The envelope shape:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
{
|
|
58
|
+
status: 'ok' | 'update_halt',
|
|
59
|
+
session_log_id: string | null,
|
|
60
|
+
worktree_id: string | null,
|
|
61
|
+
worktree_error_kind: string | null,
|
|
62
|
+
infra_drift_nudge: string | null,
|
|
63
|
+
arch_map_nudge: string | null,
|
|
64
|
+
lsp_nudges: string[],
|
|
65
|
+
handoff: { fresh: boolean, command?: string, context?: object, state?: unknown },
|
|
66
|
+
infra_files_to_commit: string[],
|
|
67
|
+
owned_checkpoints: Array<{ id: string, title: string }>,
|
|
68
|
+
cross_checkpoints: Array<{ id: string, title: string }>,
|
|
69
|
+
owned_count: number,
|
|
70
|
+
total_count: number,
|
|
71
|
+
next_action: 'mcp_update_halt' | 'resume_handoff' | 'commit_then_todo' | 'trigger_todo' | 'stop',
|
|
72
|
+
rendered_block: string,
|
|
73
|
+
previous_session: { title?: string, summary?: string, pending?: string } | null,
|
|
74
|
+
}
|
|
75
|
+
```
|
|
116
76
|
|
|
117
|
-
|
|
118
|
-
- **HALT** — do NOT proceed to Step 3. Print:
|
|
77
|
+
**Parse and branch**:
|
|
119
78
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
(run /clear or open a new window) so the updated .claude/ takes effect.
|
|
123
|
-
```
|
|
79
|
+
- `status === 'update_halt'` → print `rendered_block` (the update-halt message) and **STOP**. No further writes, no `/cbp-todo` trigger.
|
|
80
|
+
- Otherwise → print `rendered_block` (the Step 6 output block), then proceed to Step 5.7 and Step 7.
|
|
124
81
|
|
|
125
|
-
|
|
82
|
+
### Step 5.7: Commit Non-Task Files (Claude-side)
|
|
126
83
|
|
|
127
|
-
|
|
84
|
+
Driven by `envelope.infra_files_to_commit[]`. If non-empty, present once:
|
|
128
85
|
|
|
129
|
-
```bash
|
|
130
|
-
npx codebyplan claude status --write-cache --quiet 2>/dev/null || true
|
|
131
86
|
```
|
|
87
|
+
Commit these non-task files before starting session?
|
|
88
|
+
[list of infra_files_to_commit]
|
|
132
89
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
Surface — never block — any language-server binaries still missing for this repo's tech stack. Runs after the freshness gate (Step 1.6) and before session activation (Step 3); may add one line per missing binary to the Step 6 output. Fully non-blocking — every failure path falls through with no output.
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
LSP_NUDGE=$(npx codebyplan lsp --check 2>/dev/null || true)
|
|
90
|
+
Reply: yes | no | select
|
|
139
91
|
```
|
|
140
92
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
### Step 3: Update Session State
|
|
144
|
-
|
|
145
|
-
Run `codebyplan session update-state --action activate` (CLI write-through: writes `.codebyplan/state/session/state.json` + REST). This deactivates all other repos automatically. Break-glass fallback: MCP `update_session_state` with action `activate` when the CLI is unavailable. Note: the CLI validates `--action` — only `activate`/`deactivate` are accepted; a missing, valueless, or invalid value exits 1 with a usage message.
|
|
146
|
-
|
|
147
|
-
Note: Step 0 `health_check` stays MCP unconditionally — it tests MCP connectivity itself and must not be replaced.
|
|
148
|
-
|
|
149
|
-
### Step 4: Read Last Session Log
|
|
150
|
-
|
|
151
|
-
Read `.codebyplan/state/session/current.json` (local-first). If missing/stale, run `npx codebyplan sync` once and re-read. Break-glass fallback: MCP `get_session_logs({ repo_id, worktree_id, limit: 1 })` when the state dir is absent and sync fails (daemon-dead + CLI-unavailable).
|
|
152
|
-
|
|
153
|
-
Take the first row — same inclusive-worktree scope as Step 4.5 so the previous-session display and the handoff probe agree on which row is "most recent for this worktree".
|
|
154
|
-
|
|
155
|
-
- If a previous log exists, hold its title/summary/pending items for the Step 6 output so the user sees where they left off.
|
|
156
|
-
- If none exists (first session ever for this worktree), skip silently.
|
|
157
|
-
|
|
158
|
-
### Step 5: Create This Session's Log
|
|
159
|
-
|
|
160
|
-
Run `codebyplan session create-log --started-at <now> --repo-id <repo_id> --worktree-id <WORKTREE_ID>` (CLI write-through: writes `.codebyplan/state/session/current.json` + REST). Create it **even if empty** — this establishes the record for session-end to finalize. Break-glass fallback: MCP `create_session_log` when the CLI is unavailable.
|
|
161
|
-
|
|
162
|
-
Minimal seed content:
|
|
163
|
-
|
|
164
|
-
- `started_at`: now
|
|
165
|
-
- `repo_id` from config; `worktree_id` from `WORKTREE_ID` resolved in Step 1
|
|
166
|
-
- `summary`: empty (session-end fills this in)
|
|
167
|
-
|
|
168
|
-
Hold the new log's ID in context so `/cbp-session-end` can update the same record.
|
|
169
|
-
|
|
170
|
-
### Step 4.5: Handoff Auto-Resume Probe
|
|
171
|
-
|
|
172
|
-
Probe the most-recent closed session log for a structured handoff payload (the handoff freshness-gate contract is specified inline in this step) and auto-resume directly into the captured command when fresh. Additive — placed BEFORE the existing `/cbp-todo` auto-trigger; ALL failure paths fall through silently to Step 7.
|
|
173
|
-
|
|
174
|
-
1. Reuse the row held from Step 4 (held from Step 4 in memory — do NOT re-read from disk here; at this point `session/current.json` still holds the previous session row, which Step 5 will overwrite).
|
|
175
|
-
2. **Defensive gates** (any failure → silent fall-through to Step 7):
|
|
176
|
-
- No row returned → fall through.
|
|
177
|
-
- Row missing `closed_at` (orphan / still-open session) → fall through.
|
|
178
|
-
- `row.content` is `null` → no handoff captured at end-of-session → fall through.
|
|
179
|
-
- `row.content` exists but parse throws or shape mismatch (`command` field absent OR is an empty string) → fall through.
|
|
180
|
-
3. **Freshness gate** — load the row as `handoff = row.content` (per CHK-111 Migration A column alias). Mark stale when ANY of:
|
|
181
|
-
- `(now - row.closed_at) > freshness_window_hours` (read from `.codebyplan/repo.json`, default 24 hours)
|
|
182
|
-
- Referenced entity in `handoff.context` has shifted. For each id present, read the matching local state file and check `updated_at`:
|
|
183
|
-
- `checkpoint_id` → read `.codebyplan/state/checkpoints/<checkpoint_id>.json` (local-first; sync + MCP break-glass if missing)
|
|
184
|
-
- `task_id` → read `.codebyplan/state/checkpoints/<checkpoint_id>/tasks/<task_id>.json` (local-first; sync + MCP break-glass if missing)
|
|
185
|
-
- `round_id` → read `.codebyplan/state/checkpoints/<checkpoint_id>/tasks/<task_id>/rounds/<round_id>.json` (local-first; sync + MCP break-glass if missing)
|
|
186
|
-
Then compare `entry.updated_at > handoff.captured_at` → stale on any inequality.
|
|
187
|
-
- Local file missing after sync attempt → stale (referenced entity gone or moved out of reach).
|
|
188
|
-
- `handoff.context.checkpoint_id` resolves to a checkpoint whose `worktree_id` is non-null AND (caller `WORKTREE_ID` is `null` OR differs from `checkpoint.worktree_id`) → stale (a fresh handoff for another worktree's work — or for assigned work this caller cannot confirm ownership of — must not auto-resume here). Mirrors the cbp-todo Step 1.5 ownership rule.
|
|
189
|
-
4. **On stale OR any defensive gate hit**: fall through silently to Step 7 (existing `/cbp-todo` trigger).
|
|
190
|
-
5. **On fresh hit**: trigger `handoff.command` directly with `handoff.context` / `handoff.state` in the trigger arguments. The downstream skill self-loads its full context — do NOT duplicate `/cbp-todo` Step 2's context-loading matrix here. Skip Step 5.7, Step 6 output, and Step 7.
|
|
191
|
-
|
|
192
|
-
### Step 5.7: Commit Non-Task Files
|
|
193
|
-
|
|
194
|
-
Clean the working tree of leftover infra before the session begins. Only commit files that are **not** part of an unfinished task.
|
|
195
|
-
|
|
196
|
-
1. Resolve the active task's round files (local-first):
|
|
197
|
-
- Read `.codebyplan/state/todos.json` (local-first) to identify the active task id. If missing/stale, run `npx codebyplan sync` once and re-read. Break-glass fallback: MCP `get_current_task(repo_id)` when the state dir is absent and sync fails.
|
|
198
|
-
- If active task exists: read `.codebyplan/state/checkpoints/<checkpoint_id>/tasks/<task_id>/rounds/` and filter to rounds with status not in `completed` / `cancelled`; collect their `files[]` → `task_files` set. Break-glass fallback: MCP `get_rounds(task_id)`.
|
|
199
|
-
- If no active task exists, `task_files` is empty.
|
|
200
|
-
2. Run `codebyplan session infra-files --json --task-files "<csv>"` where `<csv>` is the comma-separated task files from step 1. Parse the JSON response (`{ infra_files: string[], task_files: string[], note?: string }`). The CLI re-runs `git status --porcelain` internally and applies the set-math deterministically — the race-safe recompute, reading the index after Steps 0–5 round-trips complete.
|
|
201
|
-
3. If `infra_files` is empty → skip. Otherwise present once:
|
|
202
|
-
|
|
203
|
-
```
|
|
204
|
-
Commit these non-task files before starting session?
|
|
205
|
-
[list of infra_files]
|
|
206
|
-
|
|
207
|
-
Reply: yes | no | select
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
4. On `yes`: `git add` the listed files, then trigger `/cbp-git-commit` (it handles conventional message + commit).
|
|
211
|
-
On `no`: skip. On `select`: ask which subset.
|
|
93
|
+
On `yes`: `git add` the listed files, then trigger `/cbp-git-commit`.
|
|
94
|
+
On `no`: skip. On `select`: ask which subset.
|
|
212
95
|
|
|
213
96
|
Non-blocking — session start proceeds either way.
|
|
214
97
|
|
|
215
|
-
### Step 5.8: Resolve Ownership
|
|
216
|
-
|
|
217
|
-
Call MCP `get_checkpoints({ repo_id, status: 'active' })` (MCP only — deliberate: the active-filter query has no local-mirror equivalent). Partition results into:
|
|
218
|
-
|
|
219
|
-
- `owned[]` — entries where `checkpoint.worktree_id === WORKTREE_ID`, OR both are `null`
|
|
220
|
-
- `cross_worktree[]` — entries where `checkpoint.worktree_id` is non-null AND differs from `WORKTREE_ID` (includes the case where caller `WORKTREE_ID` is `null` but the target has a non-null `worktree_id`)
|
|
221
|
-
|
|
222
|
-
Hold `owned_count = owned.length`, `total_count = owned.length + cross_worktree.length`, `owned_names` (CHK-NNN + title for each owned entry), and `cross_names` (CHK-NNN + name for each cross-worktree entry). These values are consumed by Step 6 and Step 7 — single MCP call, no duplicate round-trips.
|
|
223
|
-
|
|
224
|
-
### Step 6: Output
|
|
225
|
-
|
|
226
|
-
```
|
|
227
|
-
Session active | Worktree: [worktree_id or "unregistered"]
|
|
228
|
-
|
|
229
|
-
[⚠ resolve-worktree: <error_kind> — local state is broken; routing may be unreliable. Run `npx codebyplan setup` to repair. — only when error_kind is non-null and not tuple_miss]
|
|
230
|
-
|
|
231
|
-
[⚠ .claude/ infra is N behind — run /cbp-refresh-infra — only when Step 1.5 found drift]
|
|
232
|
-
[⚠ .claude/architecture is N module(s) stale — run /cbp-refresh-arch-map — only when Step 1.55 found drift]
|
|
233
|
-
|
|
234
|
-
Previous session: [title or "none"]
|
|
235
|
-
Pending: [pending items from previous log, or "—"]
|
|
236
|
-
|
|
237
|
-
Ownership: [total_count] active CHK(s), [owned_count] owned by this worktree
|
|
238
|
-
[Owned: CHK-NNN (title), … — only when owned_count > 0]
|
|
239
|
-
[Cross-worktree: CHK-ZZZ (name), … — only when total_count > owned_count]
|
|
240
|
-
|
|
241
|
-
[LSP: <install hint> — one line per still-missing LSP binary held from Step 1.7, only when LSP_NUDGE is non-empty]
|
|
242
|
-
|
|
243
|
-
[⚠ Worktree unregistered — run `npx codebyplan setup` to register — only when WORKTREE_ID is null and no resolver distress was already shown]
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
READ-ONLY — this block never proposes reassignment, release, or lock transfer of cross-worktree checkpoints.
|
|
247
|
-
|
|
248
98
|
### Step 7: Auto-trigger
|
|
249
99
|
|
|
250
|
-
|
|
100
|
+
Route from `envelope.next_action`:
|
|
251
101
|
|
|
252
|
-
- **`
|
|
253
|
-
- **`
|
|
254
|
-
- **`
|
|
102
|
+
- **`mcp_update_halt`** — already handled above (STOP after printing rendered_block).
|
|
103
|
+
- **`resume_handoff`** — trigger `envelope.handoff.command` directly with `envelope.handoff.context` / `envelope.handoff.state`. Skip Step 5.7.
|
|
104
|
+
- **`commit_then_todo`** — run Step 5.7 (infra commit gate), then trigger `/cbp-todo`.
|
|
105
|
+
- **`trigger_todo`** — trigger `/cbp-todo` (owns active work, or idle path).
|
|
106
|
+
- **`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.
|
|
255
107
|
|
|
256
108
|
## Integration
|
|
257
109
|
|
|
258
110
|
- **Triggered by**: user invocation, `/clear` recovery
|
|
259
|
-
- **
|
|
260
|
-
- **
|
|
261
|
-
- **Writes**: `codebyplan session create-log` (Step 5 — CLI write-through; break-glass: MCP `create_session_log`), `codebyplan session update-state --action activate` (Step 3 — CLI write-through to `.codebyplan/state/session/state.json`; break-glass: MCP `update_session_state`) — both SKIPPED on a Step 0 MCP hard-fail and on the Step 1.6 update-and-halt path
|
|
111
|
+
- **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`)
|
|
112
|
+
- **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`
|
|
262
113
|
- **Spawns**: none
|
|
263
|
-
- **Triggers**: `/cbp-git-commit` (conditional, on user approval at Step 5.7
|
|
114
|
+
- **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`
|
|
264
115
|
- **Paired with**: `/cbp-session-end`
|
|
265
|
-
- **Pairs with**: `/cbp-session-end` Step 1.3 (handoff write-path; the freshness-gate contract is
|
|
116
|
+
- **Pairs with**: `/cbp-session-end` Step 1.3 (handoff write-path; the freshness-gate contract is implemented inside `codebyplan session start`)
|
|
@@ -5,22 +5,24 @@ description: Manual regression procedure for the cbp-session-start worktree-owne
|
|
|
5
5
|
|
|
6
6
|
# cbp-session-start — Worktree-Ownership 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.
|
|
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.
|
|
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.
|
|
9
11
|
|
|
10
12
|
Repo under test: `2ff6d405-39c5-47b8-a6d1-59f998ac0537`.
|
|
11
13
|
|
|
12
14
|
## Preconditions
|
|
13
15
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- Step 6 Ownership 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.
|
|
17
|
-
-
|
|
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`.
|
|
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`.
|
|
18
20
|
|
|
19
21
|
## Scenario A — caller owns an active CHK → auto-trigger
|
|
20
22
|
|
|
21
23
|
1. Run from a worktree whose `WORKTREE_ID` matches the active checkpoint's `worktree_id` (or both are `null`).
|
|
22
24
|
2. `get_checkpoints({ repo_id, status: 'active' })` returns at least one entry whose `worktree_id === WORKTREE_ID` (or both null).
|
|
23
|
-
3. **Expected**:
|
|
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`.
|
|
24
26
|
|
|
25
27
|
## Scenario B — active CHK(s) exist but none owned by caller → Ownership block + STOP
|
|
26
28
|
|
|
@@ -29,22 +31,22 @@ Repro: caller worktree is `codebyplan-claude-2` (`38cd7dfa`). The only active ch
|
|
|
29
31
|
1. `resolve-worktree --json` returns `{"worktree_id":"38cd7dfa-...","error_kind":null}`.
|
|
30
32
|
2. `get_checkpoints({ repo_id, status: 'active' })` returns CHK-136 with `worktree_id = "016bd7f2-..."`.
|
|
31
33
|
3. Step 5.8: `owned_count = 0`, `total_count = 1`, `cross_worktree = [CHK-136]`.
|
|
32
|
-
4. **Expected**:
|
|
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.
|
|
33
35
|
|
|
34
36
|
## Scenario C — no active CHKs anywhere → idle /cbp-todo trigger
|
|
35
37
|
|
|
36
38
|
1. `get_checkpoints({ repo_id, status: 'active' })` returns `[]`.
|
|
37
39
|
2. Step 5.8: `owned_count = 0`, `total_count = 0`.
|
|
38
|
-
3. **Expected**:
|
|
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).
|
|
39
41
|
|
|
40
42
|
## Scenario D — cross-worktree handoff → Step 4.5 marks stale, falls through
|
|
41
43
|
|
|
42
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).
|
|
43
45
|
2. Caller `WORKTREE_ID = "38cd7dfa-..."`.
|
|
44
|
-
3. **Expected**:
|
|
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`.
|
|
45
47
|
|
|
46
48
|
## Scenario E — resolver distress (non-tuple-miss) → warning line above Ownership block, session proceeds
|
|
47
49
|
|
|
48
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`).
|
|
49
|
-
2. **Expected**:
|
|
50
|
-
3. **Compound case** (distress typically leaves `
|
|
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.
|