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.
- package/dist/cli.js +1108 -2061
- package/package.json +1 -1
- package/templates/agents/cbp-e2e-playwright.md +10 -10
- package/templates/hooks/cbp-mcp-round-sync.sh +4 -15
- package/templates/hooks/cbp-test-hooks.sh +0 -81
- package/templates/hooks/hooks.json +0 -9
- package/templates/rules/cbp-operating-gotchas.md +8 -10
- package/templates/rules/supabase-branch-lifecycle.md +2 -2
- package/templates/rules/todo-backend.md +11 -8
- package/templates/settings.project.base.json +0 -5
- package/templates/skills/cbp-build-cc-settings/reference/cbp-permission-policy.md +1 -1
- package/templates/skills/cbp-checkpoint-create/SKILL.md +12 -22
- package/templates/skills/cbp-checkpoint-end/SKILL.md +37 -0
- package/templates/skills/cbp-checkpoint-plan/SKILL.md +10 -8
- package/templates/skills/cbp-checkpoint-start/SKILL.md +27 -19
- package/templates/skills/cbp-checkpoint-update/SKILL.md +1 -1
- package/templates/skills/cbp-finalize/SKILL.md +2 -2
- package/templates/skills/cbp-round-complete/SKILL.md +3 -24
- package/templates/skills/cbp-round-plan/SKILL.md +1 -1
- package/templates/skills/cbp-session-end/SKILL.md +40 -30
- package/templates/skills/cbp-session-start/SKILL.md +7 -7
- package/templates/skills/cbp-session-start/qa-regression.md +32 -25
- package/templates/skills/cbp-standalone-task-complete/SKILL.md +2 -5
- package/templates/skills/cbp-standalone-task-create/SKILL.md +5 -13
- package/templates/skills/cbp-standalone-task-start/SKILL.md +10 -10
- package/templates/skills/cbp-task-create/SKILL.md +1 -1
- package/templates/skills/cbp-task-start/SKILL.md +10 -10
- package/templates/skills/cbp-todo/SKILL.md +23 -38
- package/templates/skills/cbp-todo/qa-regression.md +21 -27
- package/templates/skills/cbp-verify/reference/round-scope.md +1 -2
- package/templates/hooks/cbp-mcp-caller-worktree-inject.sh +0 -78
package/package.json
CHANGED
|
@@ -31,20 +31,20 @@ pnpm exec playwright install --with-deps chromium
|
|
|
31
31
|
|
|
32
32
|
Resolve the apps/{app} dev-server port at config-read time via the shared resolver
|
|
33
33
|
`apps/{app}/e2e/resolve-web-dev-port.ts` — imported by BOTH `playwright.config.ts` and
|
|
34
|
-
`e2e/auth.setup.ts` (single source of truth). It reads the per-
|
|
34
|
+
`e2e/auth.setup.ts` (single source of truth). It reads the per-checkout, branch-keyed
|
|
35
35
|
`.codebyplan/server.local.json` overlay first, then the committed `.codebyplan/server.json`.
|
|
36
36
|
Match by label rather than array position — a monorepo can have several Next.js allocations
|
|
37
37
|
with similar label prefixes.
|
|
38
38
|
|
|
39
39
|
**Label-matching rules** (`findWebDevPort`):
|
|
40
40
|
|
|
41
|
-
- `server.local.json` overlay: each label has
|
|
42
|
-
parenthetical group (e.g. `"Web Dev (<
|
|
41
|
+
- `server.local.json` overlay: each label has a branch-keyed suffix appended as the last
|
|
42
|
+
parenthetical group (e.g. `"Web Dev (<branch-keyed-suffix>)"`). Strip exactly ONE trailing
|
|
43
43
|
`" (…)"` group, then require the result `=== "Web Dev"`.
|
|
44
|
-
- `"Web Dev (<
|
|
45
|
-
- `"Web Dev (<other-
|
|
44
|
+
- `"Web Dev (<branch-keyed-suffix>)"` → strip → `"Web Dev"` ✓
|
|
45
|
+
- `"Web Dev (<other-suffix>) (<branch-keyed-suffix>)"` → strip → `"Web Dev (<other-suffix>)"` ✗
|
|
46
46
|
- `server.json` committed base: require `label === "Web Dev"` exactly (do NOT strip —
|
|
47
|
-
`"Web Dev (<other-
|
|
47
|
+
`"Web Dev (<other-suffix>)"` must not match).
|
|
48
48
|
|
|
49
49
|
**Resolution order** (first hit wins):
|
|
50
50
|
|
|
@@ -108,7 +108,7 @@ import { resolveWebDevPort } from "./e2e/resolve-web-dev-port";
|
|
|
108
108
|
// findWebDevPort, parsePortFromUrl, and resolveWebDevPort live in the shared
|
|
109
109
|
// module ./e2e/resolve-web-dev-port.ts (imported above) — single source of
|
|
110
110
|
// truth, also consumed by e2e/auth.setup.ts. Resolution order:
|
|
111
|
-
// 0. PLAYWRIGHT_BASE_URL → 1. server.local.json → 2. server.json
|
|
111
|
+
// 0. PLAYWRIGHT_BASE_URL → 1. server.local.json (branch-keyed overlay) → 2. server.json
|
|
112
112
|
// → 3. E2E_BASE_URL → 4. 3010.
|
|
113
113
|
const port = resolveWebDevPort();
|
|
114
114
|
|
|
@@ -198,7 +198,7 @@ timing**: it loads creds from `.env.local` + `.codebyplan/e2e.env`, calls
|
|
|
198
198
|
the project ref from `NEXT_PUBLIC_SUPABASE_URL`, and writes a `sb-<projectref>-auth-token`
|
|
199
199
|
cookie (domain `localhost`) into `state.json` using the same `encodeAuthCookie` from
|
|
200
200
|
`e2e/auth-cookie.ts` that global-setup consumes. This makes seeding deterministic in any
|
|
201
|
-
|
|
201
|
+
checkout — run `pnpm e2e:auth-setup` (optionally `--port N`) when `state.json` is missing or
|
|
202
202
|
its refresh token has expired. Do NOT reintroduce a browser-login flow (the `(auth)/login`
|
|
203
203
|
page is a client component whose `onSubmit` only attaches after hydration — clicking submit
|
|
204
204
|
pre-hydration falls through to a native GET and never authenticates).
|
|
@@ -261,8 +261,8 @@ any already-running process, so the dev-server readiness probe is the active gua
|
|
|
261
261
|
path.
|
|
262
262
|
|
|
263
263
|
**Port alignment**: parse `playwright.config.ts` `baseURL` port; compare to the resolved
|
|
264
|
-
port from `.codebyplan/server.local.json` (
|
|
265
|
-
`.codebyplan/server.json` (committed base). On mismatch ask which is correct, then propose
|
|
264
|
+
port from `.codebyplan/server.local.json` (per-checkout branch-keyed overlay, checked first)
|
|
265
|
+
then `.codebyplan/server.json` (committed base). On mismatch ask which is correct, then propose
|
|
266
266
|
an Edit to align them.
|
|
267
267
|
|
|
268
268
|
## Quality Fixture (MANDATORY)
|
|
@@ -20,13 +20,10 @@
|
|
|
20
20
|
# - Web-UI flag (app_file_approval_by_user) consumption + reset
|
|
21
21
|
# - Writes both PATCH /api/rounds/${ROUND_ID} and PATCH /api/tasks/${TASK_ID}
|
|
22
22
|
#
|
|
23
|
-
# Caller
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# CLI and passes it via --caller-worktree-id so the server can honor the
|
|
28
|
-
# feat-worktree lock. The hook itself stays non-fatal (exits 0) and surfaces
|
|
29
|
-
# the CLI's stderr output to the user.
|
|
23
|
+
# Caller identity:
|
|
24
|
+
# Worktree-based caller injection was retired in CHK-225. The hook no longer
|
|
25
|
+
# resolves or threads any worktree id; user identity travels via the MCP JWT.
|
|
26
|
+
# The hook stays fail-open and exits 0.
|
|
30
27
|
#
|
|
31
28
|
# Flags:
|
|
32
29
|
# --dry-run Pass through to CLI (prints merged payload, no API writes).
|
|
@@ -83,16 +80,8 @@ if [ -z "$TASK_ID" ]; then
|
|
|
83
80
|
exit 0
|
|
84
81
|
fi
|
|
85
82
|
|
|
86
|
-
# Resolve worktree id before invoking the CLI so the server can honor the
|
|
87
|
-
# feat-worktree lock. On miss (unregistered worktree) the CLI falls back to
|
|
88
|
-
# its in-process resolve and hard-fails with guidance if still unresolved.
|
|
89
|
-
WORKTREE_ID=$(npx codebyplan resolve-worktree 2>/dev/null)
|
|
90
|
-
|
|
91
83
|
# Delegate to the codebyplan CLI (single source of truth for merge semantics)
|
|
92
84
|
CMD_ARGS=("round" "sync-approvals" "--round-id" "$ROUND_ID" "--task-id" "$TASK_ID")
|
|
93
|
-
if [ -n "$WORKTREE_ID" ]; then
|
|
94
|
-
CMD_ARGS+=("--caller-worktree-id" "$WORKTREE_ID")
|
|
95
|
-
fi
|
|
96
85
|
[ "$DRY_RUN" = "true" ] && CMD_ARGS+=("--dry-run")
|
|
97
86
|
|
|
98
87
|
if npx codebyplan "${CMD_ARGS[@]}"; then
|
|
@@ -374,87 +374,6 @@ fi
|
|
|
374
374
|
|
|
375
375
|
echo ""
|
|
376
376
|
|
|
377
|
-
# ===== HOOK SMOKE TESTS — cbp-mcp-caller-worktree-inject =====
|
|
378
|
-
echo "## Hook Smoke Tests — cbp-mcp-caller-worktree-inject (CHK-198)"
|
|
379
|
-
|
|
380
|
-
INJECT_HOOK="$HOOKS_DIR/cbp-mcp-caller-worktree-inject.sh"
|
|
381
|
-
# Absolute path — the fail-open test runs the hook from a temp cwd (to isolate it
|
|
382
|
-
# from this repo's git context), where the relative "$HOOKS_DIR" no longer resolves.
|
|
383
|
-
INJECT_HOOK_ABS="$(cd "$HOOKS_DIR" 2>/dev/null && pwd)/cbp-mcp-caller-worktree-inject.sh"
|
|
384
|
-
|
|
385
|
-
if [ ! -f "$INJECT_HOOK" ]; then
|
|
386
|
-
test_result "cbp-mcp-caller-worktree-inject.sh present" "passed" "missing"
|
|
387
|
-
else
|
|
388
|
-
test_result "cbp-mcp-caller-worktree-inject.sh present" "passed" "passed"
|
|
389
|
-
|
|
390
|
-
FIRST_LINE=$(head -1 "$INJECT_HOOK")
|
|
391
|
-
if echo "$FIRST_LINE" | grep -q '^#!/'; then
|
|
392
|
-
test_result "cbp-mcp-caller-worktree-inject.sh has shebang" "passed" "passed"
|
|
393
|
-
else
|
|
394
|
-
test_result "cbp-mcp-caller-worktree-inject.sh has shebang" "passed" "missing"
|
|
395
|
-
fi
|
|
396
|
-
|
|
397
|
-
if grep -q '@scope: org-shared' "$INJECT_HOOK"; then
|
|
398
|
-
test_result "cbp-mcp-caller-worktree-inject.sh has @scope: org-shared" "passed" "passed"
|
|
399
|
-
else
|
|
400
|
-
test_result "cbp-mcp-caller-worktree-inject.sh has @scope: org-shared" "passed" "missing"
|
|
401
|
-
fi
|
|
402
|
-
|
|
403
|
-
# Fail-open: run from a non-repo temp dir with no worktree cache and no
|
|
404
|
-
# CLAUDE_PROJECT_DIR — neither the cache nor the CLI fallback can resolve a
|
|
405
|
-
# worktree, so the hook must exit 0 with empty stdout (no updatedInput).
|
|
406
|
-
ISO=$(mktemp -d)
|
|
407
|
-
OUTPUT=$( (cd "$ISO" && env -u CLAUDE_PROJECT_DIR bash "$INJECT_HOOK_ABS" <<< '{"tool_input":{"task_id":"x"}}') 2>/dev/null )
|
|
408
|
-
EXIT_CODE=$?
|
|
409
|
-
if [ "$EXIT_CODE" = "0" ] && [ -z "$OUTPUT" ]; then
|
|
410
|
-
test_result "cbp-mcp-caller-worktree-inject.sh fail-open (unresolvable) exits 0 + empty stdout" "passed" "passed"
|
|
411
|
-
else
|
|
412
|
-
test_result "cbp-mcp-caller-worktree-inject.sh fail-open (unresolvable) exits 0 + empty stdout" "passed" "failed (exit=$EXIT_CODE)"
|
|
413
|
-
fi
|
|
414
|
-
rm -rf "$ISO"
|
|
415
|
-
|
|
416
|
-
# C6 — input already carries a non-empty caller_worktree_id → never overwrite;
|
|
417
|
-
# early-return with exit 0 and empty stdout (no resolution attempted).
|
|
418
|
-
OUTPUT=$(echo '{"tool_input":{"caller_worktree_id":"11111111-1111-1111-1111-111111111111"}}' | bash "$INJECT_HOOK" 2>/dev/null)
|
|
419
|
-
EXIT_CODE=$?
|
|
420
|
-
if [ "$EXIT_CODE" = "0" ] && [ -z "$OUTPUT" ]; then
|
|
421
|
-
test_result "cbp-mcp-caller-worktree-inject.sh C6 keeps existing caller_worktree_id (exit 0 + empty stdout)" "passed" "passed"
|
|
422
|
-
else
|
|
423
|
-
test_result "cbp-mcp-caller-worktree-inject.sh C6 keeps existing caller_worktree_id (exit 0 + empty stdout)" "passed" "failed (exit=$EXIT_CODE)"
|
|
424
|
-
fi
|
|
425
|
-
|
|
426
|
-
# Injection — a worktree.local.json whose .branch matches the current git branch
|
|
427
|
-
# makes the cache fast-path resolve. Use a synthetic UUID so the assertion proves
|
|
428
|
-
# the cache value (not the live CLI) was injected. Skipped when no concrete git
|
|
429
|
-
# branch resolves (detached HEAD / non-git checkout) or jq is unavailable.
|
|
430
|
-
CUR_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
|
431
|
-
if [ -n "$CUR_BRANCH" ] && [ "$CUR_BRANCH" != "HEAD" ] && command -v jq >/dev/null 2>&1; then
|
|
432
|
-
ISO=$(mktemp -d)
|
|
433
|
-
mkdir -p "$ISO/.codebyplan"
|
|
434
|
-
FAKE_WT="abcdef01-2345-6789-abcd-ef0123456789"
|
|
435
|
-
jq -n --arg b "$CUR_BRANCH" --arg w "$FAKE_WT" \
|
|
436
|
-
'{worktree_id:$w, branch:$b}' > "$ISO/.codebyplan/worktree.local.json"
|
|
437
|
-
OUTPUT=$(CLAUDE_PROJECT_DIR="$ISO" bash "$INJECT_HOOK" <<< '{"tool_input":{"task_id":"x"}}' 2>/dev/null)
|
|
438
|
-
EXIT_CODE=$?
|
|
439
|
-
INJECTED=$(echo "$OUTPUT" | jq -r '.hookSpecificOutput.updatedInput.caller_worktree_id // empty' 2>/dev/null)
|
|
440
|
-
# Sibling-key survival — CC's updatedInput REPLACES tool_input wholesale (it is
|
|
441
|
-
# not a partial merge), so the hook must echo back every original field merged
|
|
442
|
-
# with caller_worktree_id. Assert the non-target sibling key (task_id) survives;
|
|
443
|
-
# this is the assertion gap that let the replace-vs-merge bug ship in round 2.
|
|
444
|
-
PRESERVED=$(echo "$OUTPUT" | jq -r '.hookSpecificOutput.updatedInput.task_id // empty' 2>/dev/null)
|
|
445
|
-
if [ "$EXIT_CODE" = "0" ] && [ "$INJECTED" = "$FAKE_WT" ] && [ "$PRESERVED" = "x" ]; then
|
|
446
|
-
test_result "cbp-mcp-caller-worktree-inject.sh injects caller_worktree_id AND preserves sibling keys" "passed" "passed"
|
|
447
|
-
else
|
|
448
|
-
test_result "cbp-mcp-caller-worktree-inject.sh injects caller_worktree_id AND preserves sibling keys" "passed" "failed (exit=$EXIT_CODE injected=$INJECTED preserved=$PRESERVED)"
|
|
449
|
-
fi
|
|
450
|
-
rm -rf "$ISO"
|
|
451
|
-
else
|
|
452
|
-
test_result "cbp-mcp-caller-worktree-inject.sh injection test (no branch resolvable — skipped)" "passed" "passed"
|
|
453
|
-
fi
|
|
454
|
-
fi
|
|
455
|
-
|
|
456
|
-
echo ""
|
|
457
|
-
|
|
458
377
|
# ===== HOOK SMOKE TESTS — cbp-session-start-hook =====
|
|
459
378
|
echo "## Hook Smoke Tests — cbp-session-start-hook (CHK-178)"
|
|
460
379
|
|
|
@@ -69,15 +69,6 @@
|
|
|
69
69
|
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/cbp-mcp-migration-guard.sh"
|
|
70
70
|
}
|
|
71
71
|
]
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
"matcher": "mcp__codebyplan__(update_checkpoint|complete_checkpoint|update_task|complete_task|add_round|update_round|complete_round|create_standalone_task|update_standalone_task|complete_standalone_task|add_standalone_round|update_standalone_round|complete_standalone_round|update_standalone_file_change)",
|
|
75
|
-
"hooks": [
|
|
76
|
-
{
|
|
77
|
-
"type": "command",
|
|
78
|
-
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/cbp-mcp-caller-worktree-inject.sh"
|
|
79
|
-
}
|
|
80
|
-
]
|
|
81
72
|
}
|
|
82
73
|
],
|
|
83
74
|
"PostToolUse": [
|
|
@@ -25,11 +25,13 @@ SHARED tooling behavior only — repo-specific gotchas belong in that repo's own
|
|
|
25
25
|
clobbers existing `decisions` / `discoveries` / `check_results`. Always read the current row,
|
|
26
26
|
merge your change into the full object/array, then write the whole thing back.
|
|
27
27
|
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
- **User-level locks are invisible until a mutation they block.** `get_checkpoints` /
|
|
29
|
+
`get_tasks` succeed even when another user holds the assignment; the 403 fires only on
|
|
30
|
+
`update_*` / `complete_*`. The lock keys on the JWT user (`ctx.userId`) vs the row's
|
|
31
|
+
`assigned_user_id` (null = open). `caller_worktree_id` / `worktree_id` params are
|
|
32
|
+
accepted-and-ignored — do not thread them. Verify `assigned_user_id` matches
|
|
33
|
+
`npx codebyplan whoami` before mutating; recover a stale assignment with
|
|
34
|
+
`release_assignment` (maintainer).
|
|
33
35
|
|
|
34
36
|
- **Full-repo lint/type baselines are often pre-existing red.** A round must gate on the files
|
|
35
37
|
it changed, not the whole-repo baseline — scope lint/tsc checks to the round's changed set so a
|
|
@@ -40,14 +42,10 @@ SHARED tooling behavior only — repo-specific gotchas belong in that repo's own
|
|
|
40
42
|
`update_task` alone — updating only the task leaves the round entries unapproved and
|
|
41
43
|
`complete_task` rejects with "files are not approved".
|
|
42
44
|
|
|
43
|
-
- **CLI transport uses REST (reads) and OAuth+MCP (writes) — a 502 from `codebyplan round sync-approvals` is transient MCP churn, not an outage.** The CLI exits 0 with a warning and MCP tools still work. A missing `CODEBYPLAN_API_KEY` surfaces as an `ApiError`, not a 502. `sync-approvals` can also drag untracked per-device dirs into `files_changed` — run it from the repo root
|
|
45
|
+
- **CLI transport uses REST (reads) and OAuth+MCP (writes) — a 502 from `codebyplan round sync-approvals` is transient MCP churn, not an outage.** The CLI exits 0 with a warning and MCP tools still work. A missing `CODEBYPLAN_API_KEY` surfaces as an `ApiError`, not a 502. `sync-approvals` can also drag untracked per-device dirs into `files_changed` — run it from the repo root.
|
|
44
46
|
|
|
45
47
|
- **`codebyplan claude update` requires a TTY.** On non-TTY stdin (CI, piped) it half-applies then errors. Re-run with `--yes` to accept defaults non-interactively.
|
|
46
48
|
|
|
47
|
-
- **Checkpoint locks are invisible until a mutation they block.** `get_checkpoints` / `get_tasks` succeed even when another worktree holds the lock; the 403 fires only on `update_*` / `complete_*`. Verify the row's `worktree_id` matches the caller before mutating. A null-`worktree_id` checkpoint can still be actively shipped by whichever worktree physically holds its feat branch — check `git worktree list` first.
|
|
48
|
-
|
|
49
|
-
- **`update_task` accepts `caller_worktree_id` for lock-verify only — it does NOT assign ownership.** Ownership assignment goes through the web UI or the dedicated assignment path. Don't conflate `caller_worktree_id` with `assigned_worktree_id`.
|
|
50
|
-
|
|
51
49
|
- **Re-run config-driven gates after merging main into a feat branch.** A merge can add or change `.codebyplan/shipment.json`, ports, branch config, `e2e.json`, and `eslint.json` — treat the post-merge state as a fresh baseline before continuing.
|
|
52
50
|
|
|
53
51
|
## Behavioral Preferences
|
|
@@ -47,7 +47,7 @@ The Supabase branch is removed wherever the git branch is deleted:
|
|
|
47
47
|
| Skill | Trigger |
|
|
48
48
|
|---|---|
|
|
49
49
|
| `cbp-checkpoint-end` | stale-branch cleanup + current feat-branch delete on ship |
|
|
50
|
-
| `codebyplan worktree remove
|
|
50
|
+
| `codebyplan worktree remove <path>` | git-worktree teardown removes the coupled Supabase branch (branch-keyed, not identity-keyed) |
|
|
51
51
|
| `cbp-ship-main` | `branch_deleted` event after PR merge |
|
|
52
52
|
| `cbp-standalone-task-complete` | `branch_deleted` event after standalone PR merge (Step 7.3) |
|
|
53
53
|
|
|
@@ -93,7 +93,7 @@ or auto-created by the GitHub integration — both paths use the same branch nam
|
|
|
93
93
|
| Role | Skill |
|
|
94
94
|
|---|---|
|
|
95
95
|
| Create (lazy) | `cbp-supabase-migrate` (Step 2.3) |
|
|
96
|
-
| Delete | `cbp-checkpoint-end`, `cbp-standalone-task-complete`, `codebyplan worktree remove
|
|
96
|
+
| Delete | `cbp-checkpoint-end`, `cbp-standalone-task-complete`, `codebyplan worktree remove <path>`, `cbp-ship-main` |
|
|
97
97
|
| PR gate | `cbp-supabase-branch-check` |
|
|
98
98
|
|
|
99
99
|
Each skill in the Skill Map above carries an inline back-reference to this rule at its create or teardown step.
|
|
@@ -12,16 +12,16 @@ The todos queue is materialised by `apps/todo-worker` (CHK-122) and consumed by
|
|
|
12
12
|
|
|
13
13
|
## 1. Six workflow invariants — DB-layer guards, never bypassable
|
|
14
14
|
|
|
15
|
-
Defined in `supabase/migrations/20260511211900_chk111_workflow_invariants.sql
|
|
15
|
+
Defined in `supabase/migrations/20260511211900_chk111_workflow_invariants.sql` (updated by `supabase/migrations/20260612000000_chk225_task1_user_locks.sql`). These are `BEFORE UPDATE` triggers — they refuse invalid state transitions and produce structured `RAISE EXCEPTION` errors with `HINT` pointing at the offending row. **Do NOT port these to TS.** The DB layer is the bypass-proof contract.
|
|
16
16
|
|
|
17
17
|
| # | Trigger | What it enforces |
|
|
18
18
|
|---|---------|------------------|
|
|
19
|
-
| 1 | `trg_enforce_checkpoint_activation_worktree` | A checkpoint cannot be activated without `
|
|
20
|
-
| 2 | `
|
|
19
|
+
| 1 | `trg_enforce_checkpoint_activation_worktree` | A checkpoint cannot be activated without `assigned_user_id` set (CHK-225: was `worktree_id`) |
|
|
20
|
+
| 2 | `trg_enforce_standalone_task_workflow_invariants` | A standalone task cannot be moved to `in_progress` without `assigned_user_id` (CHK-225: was `assigned_worktree_id`) |
|
|
21
21
|
| 3 | `trg_enforce_task_workflow_invariants` | ≤ 1 `in_progress` task per checkpoint |
|
|
22
22
|
| 4 | `trg_enforce_single_in_progress_round_per_task` | ≤ 1 `in_progress` round per task |
|
|
23
|
-
| 5 | `trg_enforce_single_active_scope_per_worktree` | ≤ 1 active (checkpoint OR standalone task) per
|
|
24
|
-
| 6 | `trg_enforce_standalone_task_scope_per_worktree` | ≤ 1 `in_progress` standalone task per
|
|
23
|
+
| 5 | `trg_enforce_single_active_scope_per_worktree` | ≤ 1 active (checkpoint OR standalone task) per `assigned_user_id` (CHK-225: was per `worktree_id`) |
|
|
24
|
+
| 6 | `trg_enforce_standalone_task_scope_per_worktree` | ≤ 1 `in_progress` standalone task per `assigned_user_id` (CHK-225: was per `assigned_worktree_id`) |
|
|
25
25
|
|
|
26
26
|
The worker is a passive cross-checker (`apps/todo-worker/src/invariants/check.ts`) — if its check disagrees with the DB, the DB wins.
|
|
27
27
|
|
|
@@ -34,7 +34,7 @@ MCP write → enqueueTodosJob → todos_jobs (status='pending')
|
|
|
34
34
|
↓
|
|
35
35
|
worker claim_todos_job (SELECT … FOR UPDATE SKIP LOCKED)
|
|
36
36
|
↓
|
|
37
|
-
computeTodos(repo,
|
|
37
|
+
computeTodos(repo, user) → desired rows
|
|
38
38
|
↓
|
|
39
39
|
apply_todos RPC → todos table (status='current' / 'pending')
|
|
40
40
|
↓
|
|
@@ -71,7 +71,7 @@ The queue head (`get_todos` `rows[0]`) maps to one of these slash commands. The
|
|
|
71
71
|
|
|
72
72
|
## 5. Heartbeat policy
|
|
73
73
|
|
|
74
|
-
The worker's `node-cron` heartbeat runs at `0 0 * * *` (UTC midnight). It enumerates every `(repo,
|
|
74
|
+
The worker's `node-cron` heartbeat runs at `0 0 * * *` (UTC midnight). It enumerates every `(repo, user)` pair with an active checkpoint OR in-progress standalone task (via `assigned_user_id`) and enqueues a `HEARTBEAT_SWEEP` todos_jobs row for each. This catches drift from missed `enqueueTodosJob` calls in MCP writers.
|
|
75
75
|
|
|
76
76
|
Backoff: a failed job retries at `now + 2^attempts minutes` (cap 60min). After 3 attempts, the job stays `failed` and the heartbeat picks it up again at the next sweep.
|
|
77
77
|
|
|
@@ -83,7 +83,6 @@ The shared enqueue helper lives at `packages/mcp-tools/src/tools/enqueue-todos.t
|
|
|
83
83
|
enqueueTodosJob(
|
|
84
84
|
client: SupabaseClient,
|
|
85
85
|
repoId: string,
|
|
86
|
-
callerWorktreeId: string | undefined,
|
|
87
86
|
userId: string | null,
|
|
88
87
|
reason: string
|
|
89
88
|
): Promise<void>
|
|
@@ -108,6 +107,8 @@ Every workflow mutator MUST call `void enqueueTodosJob(...)` after the mutation
|
|
|
108
107
|
|
|
109
108
|
CHK-111 shipped the original todos queue as Postgres triggers + a 583-LOC `regenerate_todos_for_repo` PL/pgSQL function. CHK-122 ported the regen to `apps/todo-worker` (Node) for shared infrastructure with `apps/docs-ingest` (CHK-116), easier testing, and per-user fanout. The 10 `trg_*_todos` triggers and the 4 `wrap_*` wrappers were dropped in migration `20260521000000_chk122_drop_legacy_todos_regen.sql`. The 6 BEFORE-UPDATE invariant triggers stayed.
|
|
110
109
|
|
|
110
|
+
CHK-225 updated the invariant triggers from worktree-scoped to user-scoped (`assigned_user_id`). The trigger names were preserved for continuity; only the function bodies changed. Migration: `20260612000000_chk225_task1_user_locks.sql`.
|
|
111
|
+
|
|
111
112
|
## 8. Deployment — Railway
|
|
112
113
|
|
|
113
114
|
`apps/todo-worker` runs as a Railway service alongside `apps/backend`. Setup:
|
|
@@ -119,3 +120,5 @@ CHK-111 shipped the original todos queue as Postgres triggers + a 583-LOC `regen
|
|
|
119
120
|
5. Save the resulting `project_ref` to `.codebyplan.json` `shipment.surfaces.railway-todo-worker.project_ref`.
|
|
120
121
|
|
|
121
122
|
Smoke after deploy: run `/cbp-finalize` in any worktree → tail Railway logs → expect a `claim → apply` cycle within `WORKER_POLL_MS`.
|
|
123
|
+
|
|
124
|
+
**CHK-225 deploy note**: apply migration `20260612100000` (drops `worktree_id` from `todos` + `todos_jobs`, dedup todos rows, updates `apply_todos` RPC) BEFORE running `railway up` for the worker. Until the migration lands, the worker's 3-arg `apply_todos` call fails with a function-not-found error.
|
|
@@ -66,7 +66,6 @@
|
|
|
66
66
|
"mcp__codebyplan__create_project",
|
|
67
67
|
"mcp__codebyplan__create_repo",
|
|
68
68
|
"mcp__codebyplan__delete_session_log",
|
|
69
|
-
"mcp__codebyplan__delete_worktree",
|
|
70
69
|
"mcp__codebyplan__release_assignment",
|
|
71
70
|
"mcp__stripe__create_customer",
|
|
72
71
|
"mcp__stripe__create_product",
|
|
@@ -161,7 +160,6 @@
|
|
|
161
160
|
"mcp__codebyplan__get_task_templates",
|
|
162
161
|
"mcp__codebyplan__get_tasks",
|
|
163
162
|
"mcp__codebyplan__get_todos",
|
|
164
|
-
"mcp__codebyplan__get_worktrees",
|
|
165
163
|
"mcp__codebyplan__list_tech_stack_sync_sessions",
|
|
166
164
|
"mcp__codebyplan__get_chunk",
|
|
167
165
|
"mcp__codebyplan__get_library_toc",
|
|
@@ -183,7 +181,6 @@
|
|
|
183
181
|
"mcp__codebyplan__create_session_log",
|
|
184
182
|
"mcp__codebyplan__update_session_log",
|
|
185
183
|
"mcp__codebyplan__update_session_state",
|
|
186
|
-
"mcp__codebyplan__create_worktree",
|
|
187
184
|
"mcp__codebyplan__flag_stale_chunk",
|
|
188
185
|
"mcp__codebyplan__update_eslint_repo_config",
|
|
189
186
|
"mcp__codebyplan__update_server_config",
|
|
@@ -196,8 +193,6 @@
|
|
|
196
193
|
"Bash(npx codebyplan supabase:*)",
|
|
197
194
|
"Bash(codebyplan whoami:*)",
|
|
198
195
|
"Bash(npx codebyplan whoami:*)",
|
|
199
|
-
"Bash(codebyplan resolve-worktree:*)",
|
|
200
|
-
"Bash(npx codebyplan resolve-worktree:*)",
|
|
201
196
|
"Bash(codebyplan version-status:*)",
|
|
202
197
|
"Bash(npx codebyplan version-status:*)",
|
|
203
198
|
"Bash(codebyplan worktree:*)",
|
|
@@ -25,7 +25,7 @@ Precedence is `deny > ask > allow`; arrays union across scopes (managed/user/pro
|
|
|
25
25
|
- **Non-lifecycle, non-shipment `/cbp-*` skills** — authoring (`cbp-build-cc-*`), frontend (`cbp-frontend-*`), git (`cbp-git-*`, `cbp-merge-main`, `cbp-refresh-infra`), round work (`cbp-round-plan`, `cbp-verify` — `cbp-verify` is the autonomous verify stage that runs deterministic gates, proves execution, spawns the fresh-context reviewer, and routes to `cbp-round-complete` or `cbp-round-plan`, so it runs without a prompt), setup/configure (`cbp-setup-*`, `cbp-ship-configure`, `cbp-supabase-*`), task prep (`cbp-task-create`/`-start`, `cbp-standalone-task-check`/`-testing`), planning (`cbp-checkpoint-plan`/`-update`), plus `cbp-session-start` and `cbp-todo`. Invoking a skill is the intended mode of operation; the gated side effects happen inside via the Bash/MCP tools the skill calls, which carry their own tiering. The lifecycle/state-transition and plan-approval skills are the exception — they live in `ask` (next section).
|
|
26
26
|
- **All `mcp__codebyplan__*` reads** (`get_*`, `list_*`, `search_*`, `health_check`, `lookup_symbol`, `resolve_library_id`, `get_chunk`).
|
|
27
27
|
- **Routine workflow-write MCP tools** the pipeline calls many times per task: create/update/complete checkpoint, task, and round; session log + session-state writes; `create_worktree`, `add_library`, `flag_stale_chunk`, `update_server_config`, `update_eslint_repo_config`, `update_task_template`. Gating these with `ask` would make the autonomous workflow unusable.
|
|
28
|
-
- **Read/safe CLI commands** (both `codebyplan X` and `npx codebyplan X`): `whoami`, `
|
|
28
|
+
- **Read/safe CLI commands** (both `codebyplan X` and `npx codebyplan X`): `whoami`, `statusline`, `ports`, `tech-stack`, `eslint`, `round`, `help`, `--version`.
|
|
29
29
|
|
|
30
30
|
### `ask` — the deliberate confirm-gate
|
|
31
31
|
|
|
@@ -60,35 +60,25 @@ Skip when the description has zero domain matches OR the user already named a ta
|
|
|
60
60
|
|
|
61
61
|
Lightweight inference from the description — no deep analysis. **Title**: concise, ≤80 chars. **Goal**: ≤300 chars, a faithful restatement of intent (not a plan).
|
|
62
62
|
|
|
63
|
-
### Step 6:
|
|
64
|
-
|
|
65
|
-
Ask the user via AskUserQuestion whether to claim this checkpoint now:
|
|
66
|
-
|
|
67
|
-
- **Claim for me + this worktree** (default) — resolve `npx codebyplan resolve-worktree 2>/dev/null` and set it as the checkpoint `worktree_id` at create. The creator carries momentum straight through plan → start.
|
|
68
|
-
- **Leave it open** — create with `worktree_id` null so anyone free can claim it later via `/cbp-checkpoint-start`.
|
|
69
|
-
|
|
70
|
-
Record the choice; it drives both the create call (Step 7) and the plan→start routing in `/cbp-checkpoint-plan`.
|
|
71
|
-
|
|
72
|
-
### Step 7: Create Checkpoint Row
|
|
63
|
+
### Step 6: Create Checkpoint Row
|
|
73
64
|
|
|
74
65
|
`codebyplan checkpoint create` (CLI write-through: writes `.codebyplan/state/checkpoints/<id>.json` + REST). Pass flags:
|
|
75
66
|
- `--repo-id` (from `.codebyplan/repo.json`), `--title`, `--goal`, `--deadline`, `--status pending`
|
|
76
67
|
- `--ideas` JSON `[{ description: <raw user text> }]`
|
|
77
|
-
- `--worktree-id` the resolved worktree **only if the user chose "claim"**; omit when "leave open"
|
|
78
68
|
|
|
79
|
-
Do **not** pass `--number` — the database auto-assigns the next per-repo checkpoint number via a `BEFORE INSERT` trigger (advisory-locked `MAX(number)+1` scoped to `repo_id`). The DB-assigned number comes back on the created row (and is written into `.codebyplan/state/checkpoints/<id>.json`); read it for the branch slug (Step
|
|
69
|
+
Do **not** pass `--number` — the database auto-assigns the next per-repo checkpoint number via a `BEFORE INSERT` trigger (advisory-locked `MAX(number)+1` scoped to `repo_id`). The DB-assigned number comes back on the created row (and is written into `.codebyplan/state/checkpoints/<id>.json`); read it for the branch slug (Step 7) and the result display (Step 8).
|
|
80
70
|
|
|
81
71
|
Break-glass fallback: MCP `create_checkpoint` (also omit `number`) when the CLI is unavailable.
|
|
82
72
|
|
|
83
|
-
|
|
73
|
+
Assignment (`assigned_user_id`) is stamped server-side from the caller's JWT when the checkpoint is activated via `/cbp-checkpoint-start` — it is not set at create time. No `context`, `research`, `plan`, or tasks are written here.
|
|
84
74
|
|
|
85
|
-
### Step
|
|
75
|
+
### Step 7: Create + Switch to Feat Branch
|
|
86
76
|
|
|
87
|
-
`{NNN}` below is the DB-assigned checkpoint number read back from the Step
|
|
77
|
+
`{NNN}` below is the DB-assigned checkpoint number read back from the Step 6 `codebyplan checkpoint create` response.
|
|
88
78
|
|
|
89
79
|
Read `.codebyplan/git.json` `branch_config.production` (default `"main"`) as `BASE`. codebyplan repos are main-only — never create or branch from a `development`/integration branch.
|
|
90
80
|
|
|
91
|
-
**
|
|
81
|
+
**7.1 — Reuse the cloud-created branch when present.** When the repo is GitHub-connected, the CHK-207 `create-feat-branch` Edge Function fires on the Step 6 row INSERT, creates `feat/CHK-{NNN}-<slug>` on origin, and writes `branch_name` back to the checkpoint row. Creating a second, differently-slugged branch here orphans the cloud one — so re-read the row first:
|
|
92
82
|
|
|
93
83
|
```bash
|
|
94
84
|
sleep 5 # give the INSERT webhook a moment to write branch_name back
|
|
@@ -96,14 +86,14 @@ npx codebyplan sync 2>/dev/null || true
|
|
|
96
86
|
BRANCH=$(jq -r '.branch_name // empty' ".codebyplan/state/checkpoints/{checkpoint-id}.json" 2>/dev/null)
|
|
97
87
|
```
|
|
98
88
|
|
|
99
|
-
(Break-glass: MCP `get_checkpoints` and read the row's `branch_name`.) If `BRANCH` is non-empty, check out the existing remote branch and skip
|
|
89
|
+
(Break-glass: MCP `get_checkpoints` and read the row's `branch_name`.) If `BRANCH` is non-empty, check out the existing remote branch and skip 7.2 entirely — do NOT push (it already exists on origin) and do NOT persist `--branch-name` (the Edge Function already recorded it):
|
|
100
90
|
|
|
101
91
|
```bash
|
|
102
92
|
git fetch origin "$BRANCH"
|
|
103
93
|
git checkout -b "$BRANCH" --track "origin/$BRANCH"
|
|
104
94
|
```
|
|
105
95
|
|
|
106
|
-
**
|
|
96
|
+
**7.2 — Fallback: create the branch locally.** Only when `BRANCH` is empty (repo not GitHub-connected, or the webhook hasn't landed). Compute the slug deterministically:
|
|
107
97
|
|
|
108
98
|
```bash
|
|
109
99
|
SLUG=$(codebyplan slug "{checkpoint title}")
|
|
@@ -122,13 +112,13 @@ Persist the branch via `codebyplan checkpoint update --id <checkpoint-id> --bran
|
|
|
122
112
|
|
|
123
113
|
**Note — Supabase preview branch**: no Supabase branch is created here. Creation is lazy — it happens on the first DB change when `/cbp-supabase-migrate` runs on this feat branch, which provisions a Supabase branch named identically to the git branch. See `cbp-supabase-migrate` Step 2.3 for the creation protocol.
|
|
124
114
|
|
|
125
|
-
### Step
|
|
115
|
+
### Step 8: Show Result + Auto-Trigger Plan
|
|
126
116
|
|
|
127
117
|
```
|
|
128
118
|
## Checkpoint Created
|
|
129
119
|
|
|
130
120
|
**CHK-NNN**: [title] • **Deadline**: [date] • **Branch**: feat/CHK-NNN-slug
|
|
131
|
-
**
|
|
121
|
+
**Assignment**: stamped on activation via /cbp-checkpoint-start
|
|
132
122
|
**Worktree**: `npx codebyplan worktree add CHK-{NNN}`
|
|
133
123
|
|
|
134
124
|
Now planning CHK-NNN… handing off to /cbp-checkpoint-plan.
|
|
@@ -139,6 +129,6 @@ Auto-trigger `/cbp-checkpoint-plan {NNN}` in the same context. This skill create
|
|
|
139
129
|
## Integration
|
|
140
130
|
|
|
141
131
|
- **Runs inline**: mechanical setup only — no assessment, research, Q&A, plan, or tasks
|
|
142
|
-
- **Reads**: `.codebyplan/state/checkpoints/*.json` (local-first; `npx codebyplan sync` if stale; MCP `get_checkpoints` break-glass); `.codebyplan/repo.json`, `.codebyplan/git.json
|
|
143
|
-
- **Writes**: `codebyplan checkpoint create` (description-only ideas + deadline
|
|
132
|
+
- **Reads**: `.codebyplan/state/checkpoints/*.json` (local-first; `npx codebyplan sync` if stale; MCP `get_checkpoints` break-glass); `.codebyplan/repo.json`, `.codebyplan/git.json`
|
|
133
|
+
- **Writes**: `codebyplan checkpoint create` (description-only ideas + deadline), `codebyplan checkpoint update --branch-name` (break-glass: MCP `create_checkpoint` / `update_checkpoint`)
|
|
144
134
|
- **Triggers**: `/cbp-checkpoint-plan` (auto)
|
|
@@ -265,6 +265,41 @@ After the feat branch git delete, run the same conditional Supabase teardown for
|
|
|
265
265
|
- If not found: no-op silently — idempotent, not-found is success.
|
|
266
266
|
- If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$FEAT_BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
|
|
267
267
|
|
|
268
|
+
### Step 9.5: Physical Git-Worktree Cleanup
|
|
269
|
+
|
|
270
|
+
After the feat branch has been git-deleted (Step 9), check whether a git worktree is still
|
|
271
|
+
checked out to that now-deleted branch and clean it up if safe.
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
git worktree list --porcelain
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Scan the output for any `worktree` entry whose `branch` field matches `refs/heads/$FEAT_BRANCH`
|
|
278
|
+
(i.e. the feat branch just deleted in Step 9). For each matching worktree path:
|
|
279
|
+
|
|
280
|
+
**Case A — the current session is NOT inside that worktree path:**
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
codebyplan worktree remove <path>
|
|
284
|
+
git worktree prune
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Record the removed path in `WORKTREES_REMOVED[]`. If `codebyplan worktree remove` exits
|
|
288
|
+
non-zero, emit a non-blocking warning and continue — a failed removal does not halt shipment.
|
|
289
|
+
|
|
290
|
+
**Case B — the current session IS inside that worktree path (i.e. `$PWD` starts with
|
|
291
|
+
`<path>`):**
|
|
292
|
+
|
|
293
|
+
Do NOT self-remove. Surface a single directive (no A/B/C menu):
|
|
294
|
+
|
|
295
|
+
> "This session is inside the worktree at `<path>`. After switching to your main checkout,
|
|
296
|
+
> run `codebyplan worktree remove <path>` to clean it up."
|
|
297
|
+
|
|
298
|
+
Record the path in `WORKTREES_REMOVED[]` as `{ path, status: 'pending_manual_cleanup' }`.
|
|
299
|
+
|
|
300
|
+
If `git worktree list --porcelain` finds no worktree checked out to the deleted feat branch,
|
|
301
|
+
skip this step silently — `WORKTREES_REMOVED` stays empty.
|
|
302
|
+
|
|
268
303
|
### Step 10: Save Shipment Results and Summary
|
|
269
304
|
|
|
270
305
|
Update checkpoint context via `codebyplan checkpoint update <id> --context '{"shipment": {...}}'` (CLI write-through); use MCP `update_checkpoint` as documented break-glass when the CLI is unavailable. The `shipment` block contains both branch promotion AND runtime surface results (from `/cbp-ship` Step 7):
|
|
@@ -280,6 +315,7 @@ context.shipment: {
|
|
|
280
315
|
stale_branches_cleaned: [list of deleted git branches],
|
|
281
316
|
feat_branch_deleted: true/false,
|
|
282
317
|
supabase_branches_deleted: [list of Supabase preview branch names removed in Steps 8–9],
|
|
318
|
+
worktrees_removed: WORKTREES_REMOVED, // from Step 9.5 — paths removed or pending manual cleanup
|
|
283
319
|
e2e_images_uploaded: E2E_IMAGES_UPLOADED // from Step 7.5 — { count, stored_paths, skipped, error? } (CHK-171)
|
|
284
320
|
}
|
|
285
321
|
```
|
|
@@ -310,6 +346,7 @@ Present summary:
|
|
|
310
346
|
- Stale branches deleted: [N] ([list])
|
|
311
347
|
- Feat branch: [deleted/kept]
|
|
312
348
|
- Supabase preview branches deleted: [N] ([list from supabase_branches_deleted], or "none")
|
|
349
|
+
- Git worktrees cleaned: [N] ([paths from worktrees_removed], or "none")
|
|
313
350
|
|
|
314
351
|
### Before/After Branch State
|
|
315
352
|
- Before: [list of feat/* branches]
|
|
@@ -8,7 +8,7 @@ effort: xhigh
|
|
|
8
8
|
|
|
9
9
|
# Checkpoint Plan Command
|
|
10
10
|
|
|
11
|
-
Runs INLINE (no subagent) — all analysis and Q&A stay in the main session. This is the rigour stage that prevents half-baked plans: it discovers shortcomings, decides whether existing dependencies suffice or a new one is warranted, compares competing approaches, and only THEN creates tasks. It produces `plan.steps[]` + tasks but **never activates the checkpoint and never claims a user
|
|
11
|
+
Runs INLINE (no subagent) — all analysis and Q&A stay in the main session. This is the rigour stage that prevents half-baked plans: it discovers shortcomings, decides whether existing dependencies suffice or a new one is warranted, compares competing approaches, and only THEN creates tasks. It produces `plan.steps[]` + tasks but **never activates the checkpoint and never claims a user** — that is `/cbp-checkpoint-start`.
|
|
12
12
|
|
|
13
13
|
## Pipeline
|
|
14
14
|
|
|
@@ -37,7 +37,7 @@ Malformed (non-numeric, contains `-`): surface `checkpoint-plan: invalid argumen
|
|
|
37
37
|
2. Read task files under `.codebyplan/state/checkpoints/<id>/tasks/*.json` (fallback: MCP `get_tasks(checkpoint_id)`) — load existing tasks. This sets the mode:
|
|
38
38
|
- **fresh** — zero tasks: full plan + create all tasks.
|
|
39
39
|
- **additive re-plan** — tasks exist: gap-analyse against them; only ADD new tasks or refine requirements for gaps. NEVER delete or overwrite an in-flight task.
|
|
40
|
-
3. Note whether `
|
|
40
|
+
3. Note whether `assigned_user_id` is set (claimed at activation) — drives routing in Step 11.
|
|
41
41
|
|
|
42
42
|
### Step 2: Assess Ideas + Codebase
|
|
43
43
|
|
|
@@ -134,16 +134,18 @@ Final write of the complete `checkpoint.context` JSONB via `codebyplan checkpoin
|
|
|
134
134
|
...
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
This skill does **NOT** activate the checkpoint and does **NOT** claim a user
|
|
137
|
+
This skill does **NOT** activate the checkpoint and does **NOT** claim a user.
|
|
138
138
|
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
Resolve the current user: `npx codebyplan whoami --json` → `user_id`.
|
|
140
|
+
|
|
141
|
+
- **Open or yours** — `whoami` returns a `user_id` AND `assigned_user_id` is either null (unclaimed) or equals caller `user_id`: auto-trigger `/cbp-checkpoint-start` in the same context. `assigned_user_id` is stamped on activation, so a freshly-created checkpoint (null) is claimed by `/cbp-checkpoint-start` itself — the creator carries momentum into activation.
|
|
142
|
+
- **Owned by another, or no identity** — `assigned_user_id` is non-null and differs from caller `user_id`, or `whoami` returns no `user_id`: surface a single directive — `Next: /cbp-checkpoint-start` — so the owning session (or anyone free, if open) claims and starts it. Never auto-activate a checkpoint owned by a different user.
|
|
141
143
|
|
|
142
144
|
## Integration
|
|
143
145
|
|
|
144
|
-
- **Reads**: `.codebyplan/state/checkpoints/<id>.json`, `checkpoints/<id>/tasks/*.json`, `session/current.json` (local-first; `npx codebyplan sync` if stale; break-glass: MCP `get_current_task`, `get_checkpoints`, `get_tasks`)
|
|
146
|
+
- **Reads**: `.codebyplan/state/checkpoints/<id>.json`, `checkpoints/<id>/tasks/*.json`, `session/current.json` (local-first; `npx codebyplan sync` if stale; break-glass: MCP `get_current_task`, `get_checkpoints`, `get_tasks`); `npx codebyplan whoami --json`
|
|
145
147
|
- **Writes**: `codebyplan checkpoint update` (ideas assessment, context, plan, research), `codebyplan task create` (break-glass: MCP `update_checkpoint`, `create_task`)
|
|
146
148
|
- **Spawns**: `cbp-research` (level 2+ only), config-matched `cbp-e2e-*` specialist (opt-in discovery probe, `whole_checkpoint_mode` — see `context/testing/e2e.md` dispatch contract)
|
|
147
149
|
- **Triggered by**: `/cbp-checkpoint-create` (auto), or user directly
|
|
148
|
-
- **Triggers**: `/cbp-checkpoint-start` (auto when
|
|
149
|
-
- **Never**: activates the checkpoint or claims a user
|
|
150
|
+
- **Triggers**: `/cbp-checkpoint-start` (auto when open/unclaimed or assigned to the current user; directive when owned by another or no identity)
|
|
151
|
+
- **Never**: activates the checkpoint or claims a user — that is `/cbp-checkpoint-start`
|