codebyplan 1.13.34 → 1.13.36
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 +87 -27
- package/package.json +1 -1
- package/templates/rules/supabase-branch-lifecycle.md +3 -1
- package/templates/skills/cbp-ship-main/SKILL.md +19 -1
- package/templates/skills/cbp-standalone-task-complete/SKILL.md +53 -13
- package/templates/skills/cbp-supabase-branch-check/SKILL.md +3 -2
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ var VERSION, PACKAGE_NAME;
|
|
|
14
14
|
var init_version = __esm({
|
|
15
15
|
"src/lib/version.ts"() {
|
|
16
16
|
"use strict";
|
|
17
|
-
VERSION = "1.13.
|
|
17
|
+
VERSION = "1.13.36";
|
|
18
18
|
PACKAGE_NAME = "codebyplan";
|
|
19
19
|
}
|
|
20
20
|
});
|
|
@@ -1606,22 +1606,16 @@ function mergeEnabledPluginsIntoSettings(settings, plugins) {
|
|
|
1606
1606
|
}
|
|
1607
1607
|
return settings;
|
|
1608
1608
|
}
|
|
1609
|
-
function
|
|
1609
|
+
function collapseEmptyHookBlocks(settings) {
|
|
1610
1610
|
if (!settings.hooks) {
|
|
1611
|
-
return
|
|
1611
|
+
return;
|
|
1612
1612
|
}
|
|
1613
1613
|
for (const event of Object.keys(settings.hooks)) {
|
|
1614
1614
|
const eventBlocks = settings.hooks[event];
|
|
1615
1615
|
if (!eventBlocks) {
|
|
1616
1616
|
continue;
|
|
1617
1617
|
}
|
|
1618
|
-
const survivingBlocks =
|
|
1619
|
-
for (const block of eventBlocks) {
|
|
1620
|
-
block.hooks = block.hooks.filter((e) => e._owner !== OWNER);
|
|
1621
|
-
if (block.hooks.length > 0) {
|
|
1622
|
-
survivingBlocks.push(block);
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1618
|
+
const survivingBlocks = eventBlocks.filter((b) => b.hooks.length > 0);
|
|
1625
1619
|
if (survivingBlocks.length > 0) {
|
|
1626
1620
|
settings.hooks[event] = survivingBlocks;
|
|
1627
1621
|
} else {
|
|
@@ -1631,6 +1625,54 @@ function stripOwnedHooksFromSettings(settings) {
|
|
|
1631
1625
|
if (Object.keys(settings.hooks).length === 0) {
|
|
1632
1626
|
delete settings.hooks;
|
|
1633
1627
|
}
|
|
1628
|
+
}
|
|
1629
|
+
function stripOwnedHooksFromSettings(settings) {
|
|
1630
|
+
if (!settings.hooks) {
|
|
1631
|
+
return settings;
|
|
1632
|
+
}
|
|
1633
|
+
for (const event of Object.keys(settings.hooks)) {
|
|
1634
|
+
const eventBlocks = settings.hooks[event];
|
|
1635
|
+
if (!eventBlocks) {
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
for (const block of eventBlocks) {
|
|
1639
|
+
block.hooks = block.hooks.filter((e) => e._owner !== OWNER);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
collapseEmptyHookBlocks(settings);
|
|
1643
|
+
return settings;
|
|
1644
|
+
}
|
|
1645
|
+
function pruneMissingManagedHooks(settings, hooksJson, onPruned) {
|
|
1646
|
+
if (!settings.hooks) {
|
|
1647
|
+
return settings;
|
|
1648
|
+
}
|
|
1649
|
+
const liveIdsByEvent = /* @__PURE__ */ new Map();
|
|
1650
|
+
for (const [event, matchers] of Object.entries(hooksJson.hooks)) {
|
|
1651
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1652
|
+
for (const block of matchers) {
|
|
1653
|
+
for (const cmd of block.hooks) {
|
|
1654
|
+
ids.add(extractHookId(cmd.command));
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
liveIdsByEvent.set(event, ids);
|
|
1658
|
+
}
|
|
1659
|
+
for (const event of Object.keys(settings.hooks)) {
|
|
1660
|
+
const eventBlocks = settings.hooks[event];
|
|
1661
|
+
if (!eventBlocks) {
|
|
1662
|
+
continue;
|
|
1663
|
+
}
|
|
1664
|
+
const liveIds = liveIdsByEvent.get(event) ?? /* @__PURE__ */ new Set();
|
|
1665
|
+
for (const block of eventBlocks) {
|
|
1666
|
+
block.hooks = block.hooks.filter((e) => {
|
|
1667
|
+
const shouldPrune = e._owner === OWNER && e._hook_id !== void 0 && !liveIds.has(e._hook_id);
|
|
1668
|
+
if (shouldPrune && onPruned) {
|
|
1669
|
+
onPruned(e._hook_id);
|
|
1670
|
+
}
|
|
1671
|
+
return !shouldPrune;
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
collapseEmptyHookBlocks(settings);
|
|
1634
1676
|
return settings;
|
|
1635
1677
|
}
|
|
1636
1678
|
var OWNER, PLACEHOLDER_RE, REPLACEMENT, SCALAR_BASE_KEYS, DEPRECATED_BASE_KEYS;
|
|
@@ -2558,9 +2600,9 @@ async function runLspFull(projectPath, opts = {}) {
|
|
|
2558
2600
|
` Detected LSP servers: ${servers.map((s) => s.plugin).join(", ")}
|
|
2559
2601
|
`
|
|
2560
2602
|
);
|
|
2561
|
-
const
|
|
2603
|
+
const settingsLocalPath = join10(projectPath, ".claude", "settings.local.json");
|
|
2562
2604
|
let settings = {};
|
|
2563
|
-
const existingSettingsRaw = await readJsonFile(
|
|
2605
|
+
const existingSettingsRaw = await readJsonFile(settingsLocalPath);
|
|
2564
2606
|
if (existingSettingsRaw) {
|
|
2565
2607
|
settings = existingSettingsRaw;
|
|
2566
2608
|
}
|
|
@@ -2569,15 +2611,15 @@ async function runLspFull(projectPath, opts = {}) {
|
|
|
2569
2611
|
servers.map((s) => s.plugin)
|
|
2570
2612
|
);
|
|
2571
2613
|
if (dryRun) {
|
|
2572
|
-
console.log(` [dry-run] would update ${
|
|
2614
|
+
console.log(` [dry-run] would update ${settingsLocalPath}`);
|
|
2573
2615
|
} else {
|
|
2574
2616
|
await mkdir4(join10(projectPath, ".claude"), { recursive: true });
|
|
2575
2617
|
await writeFile6(
|
|
2576
|
-
|
|
2618
|
+
settingsLocalPath,
|
|
2577
2619
|
JSON.stringify(settings, null, 2) + "\n",
|
|
2578
2620
|
"utf-8"
|
|
2579
2621
|
);
|
|
2580
|
-
console.log(` Updated ${
|
|
2622
|
+
console.log(` Updated ${settingsLocalPath}`);
|
|
2581
2623
|
}
|
|
2582
2624
|
const lspJsonPath = join10(projectPath, ".codebyplan", "lsp.json");
|
|
2583
2625
|
const lspJsonContent = {
|
|
@@ -2648,7 +2690,7 @@ async function runLspFull(projectPath, opts = {}) {
|
|
|
2648
2690
|
` Enabled plugins: ${pluginNames.length > 0 ? pluginNames.join(", ") : "none"}`
|
|
2649
2691
|
);
|
|
2650
2692
|
console.log(
|
|
2651
|
-
` (settings.json key format: <plugin>@claude-plugins-official)`
|
|
2693
|
+
` (settings.local.json key format: <plugin>@claude-plugins-official)`
|
|
2652
2694
|
);
|
|
2653
2695
|
if (autoInstalled.length > 0) {
|
|
2654
2696
|
console.log(
|
|
@@ -2811,6 +2853,13 @@ async function runInstall(opts, deps = {}) {
|
|
|
2811
2853
|
fs3.readFileSync(hooksJsonPath, "utf8")
|
|
2812
2854
|
);
|
|
2813
2855
|
mergeHooksIntoSettings(existingSettings, hooksJson);
|
|
2856
|
+
pruneMissingManagedHooks(
|
|
2857
|
+
existingSettings,
|
|
2858
|
+
hooksJson,
|
|
2859
|
+
opts.verbose ? (id) => console.log(
|
|
2860
|
+
`${opts.dryRun ? "[dry-run] would prune" : "pruned"} removed managed hook: ${id}`
|
|
2861
|
+
) : void 0
|
|
2862
|
+
);
|
|
2814
2863
|
}
|
|
2815
2864
|
if (!opts.dryRun) {
|
|
2816
2865
|
fs3.mkdirSync(path4.dirname(settingsPath), { recursive: true });
|
|
@@ -5628,6 +5677,13 @@ function defaultPrBody(feat, base) {
|
|
|
5628
5677
|
Merge \`${feat}\` \u2192 \`${base}\`.
|
|
5629
5678
|
`;
|
|
5630
5679
|
}
|
|
5680
|
+
function evaluateChecks(checks) {
|
|
5681
|
+
const failed = checks.filter(
|
|
5682
|
+
(c) => c.bucket !== "pass" && c.bucket !== "skipping" && c.bucket !== "pending"
|
|
5683
|
+
).map((c) => c.name);
|
|
5684
|
+
const pending = checks.filter((c) => c.bucket === "pending").length;
|
|
5685
|
+
return { passed: failed.length === 0 && pending === 0, failed, pending };
|
|
5686
|
+
}
|
|
5631
5687
|
async function pollChecks(feat, timeoutSeconds, cwd, _sleepMs = (ms) => new Promise((r) => setTimeout(r, ms))) {
|
|
5632
5688
|
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
5633
5689
|
const POLL_INTERVAL_MS = 15e3;
|
|
@@ -5640,7 +5696,7 @@ async function pollChecks(feat, timeoutSeconds, cwd, _sleepMs = (ms) => new Prom
|
|
|
5640
5696
|
while (Date.now() < deadline) {
|
|
5641
5697
|
const ghResult = spawnSync6(
|
|
5642
5698
|
"gh",
|
|
5643
|
-
["pr", "checks", feat, "--json", "name,state,
|
|
5699
|
+
["pr", "checks", feat, "--json", "name,state,bucket"],
|
|
5644
5700
|
{
|
|
5645
5701
|
cwd,
|
|
5646
5702
|
encoding: "utf-8",
|
|
@@ -5678,17 +5734,14 @@ async function pollChecks(feat, timeoutSeconds, cwd, _sleepMs = (ms) => new Prom
|
|
|
5678
5734
|
return { passed: true };
|
|
5679
5735
|
}
|
|
5680
5736
|
iteration++;
|
|
5681
|
-
const
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
if (failed.length > 0) {
|
|
5688
|
-
const names = failed.map((c) => c.name).join(", ");
|
|
5689
|
-
return { passed: false, reason: `Checks failed: ${names}` };
|
|
5737
|
+
const result = evaluateChecks(checks);
|
|
5738
|
+
if (result.failed.length > 0) {
|
|
5739
|
+
return {
|
|
5740
|
+
passed: false,
|
|
5741
|
+
reason: `Checks failed: ${result.failed.join(", ")}`
|
|
5742
|
+
};
|
|
5690
5743
|
}
|
|
5691
|
-
if (pending
|
|
5744
|
+
if (result.pending === 0) {
|
|
5692
5745
|
return { passed: true };
|
|
5693
5746
|
}
|
|
5694
5747
|
await _sleepMs(POLL_INTERVAL_MS);
|
|
@@ -8806,6 +8859,13 @@ async function runUpdate(opts, deps = {}) {
|
|
|
8806
8859
|
fs7.readFileSync(hooksJsonPath, "utf8")
|
|
8807
8860
|
);
|
|
8808
8861
|
mergeHooksIntoSettings(existingSettings, hooksJson);
|
|
8862
|
+
pruneMissingManagedHooks(
|
|
8863
|
+
existingSettings,
|
|
8864
|
+
hooksJson,
|
|
8865
|
+
opts.verbose ? (id) => console.log(
|
|
8866
|
+
`${opts.dryRun ? "[dry-run] would prune" : "pruned"} removed managed hook: ${id}`
|
|
8867
|
+
) : void 0
|
|
8868
|
+
);
|
|
8809
8869
|
}
|
|
8810
8870
|
if (!opts.dryRun) {
|
|
8811
8871
|
fs7.mkdirSync(path8.dirname(settingsPath), { recursive: true });
|
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ paths:
|
|
|
10
10
|
- ".claude/skills/cbp-checkpoint-end/**"
|
|
11
11
|
- ".claude/skills/cbp-git-worktree-remove/**"
|
|
12
12
|
- ".claude/skills/cbp-ship-main/**"
|
|
13
|
+
- ".claude/skills/cbp-standalone-task-complete/**"
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
# Supabase Branch Lifecycle
|
|
@@ -50,6 +51,7 @@ The Supabase branch is removed wherever the git branch is deleted:
|
|
|
50
51
|
| `cbp-checkpoint-end` | stale-branch cleanup + current feat-branch delete on ship |
|
|
51
52
|
| `cbp-git-worktree-remove` | worktree teardown removes the coupled Supabase branch |
|
|
52
53
|
| `cbp-ship-main` | `branch_deleted` event after PR merge |
|
|
54
|
+
| `cbp-standalone-task-complete` | `branch_deleted` event after standalone PR merge (Step 7.3) |
|
|
53
55
|
|
|
54
56
|
Deletion is **existence-checked and idempotent** — a not-found response is treated as
|
|
55
57
|
success. This tolerates the GitHub integration auto-deleting the preview branch on PR
|
|
@@ -93,7 +95,7 @@ or auto-created by the GitHub integration — both paths use the same branch nam
|
|
|
93
95
|
| Role | Skill |
|
|
94
96
|
|---|---|
|
|
95
97
|
| Create (lazy) | `cbp-supabase-migrate` (Step 2.3) |
|
|
96
|
-
| Delete | `cbp-checkpoint-end`, `cbp-git-worktree-remove`, `cbp-ship-main` |
|
|
98
|
+
| Delete | `cbp-checkpoint-end`, `cbp-standalone-task-complete`, `cbp-git-worktree-remove`, `cbp-ship-main` |
|
|
97
99
|
| PR gate | `cbp-supabase-branch-check` |
|
|
98
100
|
|
|
99
101
|
Each skill in the Skill Map above carries an inline back-reference to this rule at its create or teardown step.
|
|
@@ -38,6 +38,24 @@ Write to `/tmp/cbp-ship-main-body.md`:
|
|
|
38
38
|
- /cbp-checkpoint-check passed
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
### Step 2.5: Supabase Branch Pre-Merge Gate
|
|
42
|
+
|
|
43
|
+
**PR-existence pre-check** — `codebyplan ship` (Step 3) is what pushes the branch and creates the PR, so on the FIRST ship of a branch no PR exists yet and a `pre_merge` gate would self-defeatingly block the very step that creates it. Probe first:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
PR_EXISTS=$(gh pr list --head "$(git branch --show-current)" --json number --jq 'length' 2>/dev/null || echo 0)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- `PR_EXISTS` is `0` → skip this gate and proceed directly to Step 3 with a one-line note: `Supabase pre-merge gate skipped (no PR yet) — the generic check-poll inside codebyplan ship gates Supabase Preview as a required check.`
|
|
50
|
+
- `PR_EXISTS` ≥ `1` → run the gate: invoke the `cbp-supabase-branch-check` skill with `--mode pre_merge --target production` and parse the fenced JSON block it emits (`status`, `reason`, optional `failed_step` / `log_excerpt`).
|
|
51
|
+
|
|
52
|
+
Gate dispositions:
|
|
53
|
+
|
|
54
|
+
- `status: "passed"` or `status: "skipped"` → proceed to Step 3.
|
|
55
|
+
- `status: "blocked"` or `status: "pending_pr"` → surface `reason` (plus `failed_step` and `log_excerpt` when present) and STOP — do NOT invoke `codebyplan ship`.
|
|
56
|
+
|
|
57
|
+
**Dual-gate note**: this dedicated gate adds DB-failure diagnostics (`failed_step`, `log_excerpt` pulled from Supabase logs) that the generic check-poll inside `codebyplan ship` cannot produce. The generic poll (`pollChecks` → `evaluateChecks` in `src/lib/ship.ts`, gh bucket model) still runs after PR creation and gates Supabase Preview as one required check among all checks — the two gates are complementary, not redundant.
|
|
58
|
+
|
|
41
59
|
### Step 3: Invoke `codebyplan ship`
|
|
42
60
|
|
|
43
61
|
```bash
|
|
@@ -53,7 +71,7 @@ Pass `--dry-run` through if the skill was invoked with a dry-run arg.
|
|
|
53
71
|
|
|
54
72
|
Parse JSON from Step 3. Report `pr_url`, `merge_commit`, `branch_deleted`. If `checks_failed: true`, surface `checks_failure_reason` and stop.
|
|
55
73
|
|
|
56
|
-
> **gh false-negative workaround.** `codebyplan ship` can report `checks_failed: true` when the underlying `gh` query reads a stale/mismatched check field (
|
|
74
|
+
> **gh false-negative workaround.** `codebyplan ship` can report `checks_failed: true` when the underlying `gh` query reads a stale/mismatched check field (the poller queries the gh `bucket` field — `pass|fail|pending|skipping|cancel` — and fail-safe-blocks anything outside `pass`/`skipping`, so an API lag or an unrecognized bucket reads as a failure). Before treating the stop as final, verify the real status: `gh pr checks <PR> --watch`. If every required check is green, merge manually with `gh pr merge <PR> --merge` — add `--admin` ONLY to escape a transient secondary-rate-limit loop, never to bypass a genuinely-failing gate. Never auto-merge silently on a `checks_failed` report; this verification is a manual decision. (A `checks_failed` at this stage most likely pertains to a non-Supabase-Preview check — Supabase Preview was already gated in Step 2.5 when a PR pre-existed. If `gh pr checks <PR> --watch` shows Supabase Preview itself failing, investigate via `cbp-supabase-branch-check`'s `failed_step`/`log_excerpt` diagnostics before treating it as a false negative.)
|
|
57
75
|
|
|
58
76
|
If `bumps[]` is present with any non-skipped entry, surface a **Version bumps** line per package — `<name>: <currentVersion> → <nextVersion>` — so the user sees what this PR will publish on merge.
|
|
59
77
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
scope: repo-only:codebyplan
|
|
3
3
|
name: cbp-standalone-task-complete
|
|
4
|
-
description: Complete a standalone task —
|
|
4
|
+
description: Complete a standalone task — ship feat branch to production via PR and mark done
|
|
5
5
|
argument-hint: [task] # e.g. `45` (standalone TASK-45)
|
|
6
6
|
effort: xhigh
|
|
7
7
|
---
|
|
@@ -125,24 +125,60 @@ Skip only when nothing was committed AND `/cbp-merge-main` reported already-up-t
|
|
|
125
125
|
|
|
126
126
|
`update_standalone_task(task_id, context: { ...existing, files_changed: aggregated_files })`.
|
|
127
127
|
|
|
128
|
-
### Step 7: Standalone Task Branch
|
|
128
|
+
### Step 7: Standalone Task Branch Ship
|
|
129
129
|
|
|
130
130
|
Only when current branch matches `feat/standalone-TASK-*`:
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
2. `git checkout {PRODUCTION}`
|
|
134
|
-
3. `git merge {feat-branch} --no-ff -m "Merge {feat-branch}: {task title}"`
|
|
135
|
-
4. `git push origin {PRODUCTION}`
|
|
136
|
-
5. Delete feat branch local: `git branch -d {feat-branch}`
|
|
137
|
-
6. Delete feat branch remote: `git push origin --delete {feat-branch}`
|
|
132
|
+
#### Step 7.1 — Compose PR body
|
|
138
133
|
|
|
139
|
-
|
|
134
|
+
Write `/tmp/cbp-ship-body-standalone-{N}.md`:
|
|
140
135
|
|
|
141
|
-
|
|
136
|
+
```markdown
|
|
137
|
+
## Summary
|
|
138
|
+
|
|
139
|
+
{standalone_task.title}
|
|
140
|
+
|
|
141
|
+
Standalone TASK-{N}
|
|
142
|
+
|
|
143
|
+
## Test Plan
|
|
144
|
+
|
|
145
|
+
- All rounds passed testing-qa
|
|
146
|
+
- /cbp-standalone-task-check passed
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### Step 7.2 — Invoke `codebyplan ship`
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
codebyplan ship --body-file /tmp/cbp-ship-body-standalone-{N}.md --json
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`codebyplan ship` patch-bumps every changed workspace package and commits `chore(release): bump versions` on the feat branch BEFORE creating the PR, so the bump rides this same feat→main PR. Pass `--no-bump` for infra/doc-only standalone work where a version bump is not appropriate.
|
|
156
|
+
|
|
157
|
+
`codebyplan ship` pushes the feat branch, gets-or-creates the PR, polls required checks (including Supabase Preview) via the gh bucket model (`pollChecks` → `evaluateChecks` — pass|fail|pending|skipping|cancel; only pass/skipping are non-blocking), merges with `gh pr merge --merge`, checks out the base branch, and deletes the feat branch.
|
|
158
|
+
|
|
159
|
+
**On `checks_failed: true`**: surface `checks_failure_reason` and STOP — do NOT proceed to Step 7.5 (`complete_standalone_task` is never called on a failed ship).
|
|
160
|
+
|
|
161
|
+
> **gh false-negative workaround.** `codebyplan ship` can report `checks_failed: true` when the underlying `gh` query reads a stale/mismatched check field. Before treating the stop as final, verify with `gh pr checks <PR> --watch`. If every required check is genuinely green, merge manually with `gh pr merge <PR> --merge`. Never auto-merge silently on a `checks_failed` report — this verification is a manual decision.
|
|
162
|
+
|
|
163
|
+
Parse the JSON output and store: `pr_url`, `merge_commit`, `branch_deleted`, `feat_branch`, `checks_failed`, `checks_failure_reason`, `bumps[]`.
|
|
164
|
+
|
|
165
|
+
#### Step 7.3 — Supabase preview-branch teardown
|
|
166
|
+
|
|
167
|
+
> Lifecycle contract: see [[supabase-branch-lifecycle]].
|
|
168
|
+
|
|
169
|
+
When `branch_deleted === true` in the ship JSON:
|
|
170
|
+
|
|
171
|
+
- Read `FEAT_BRANCH` from the `feat_branch` field in the ship JSON — NOT from `git branch --show-current`. By the time Step 7.3 runs, `codebyplan ship` has already checked out the base branch, so the live branch is the base, not the feat branch.
|
|
172
|
+
- Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
|
|
173
|
+
- Scan the returned list for an entry whose `name` exactly equals `FEAT_BRANCH`.
|
|
174
|
+
- If found: call `mcp__supabase__delete_branch` with its `branch_id`. Report the outcome.
|
|
175
|
+
- If not found: no-op silently — the GitHub integration may have already removed the preview branch on PR close; not-found is success, NOT an error.
|
|
176
|
+
- If the `list_branches` call itself fails (network, auth, or non-success response): emit a non-blocking warning that the Supabase preview branch for `FEAT_BRANCH` may still exist and should be verified in the dashboard. Never treat an API failure as a not-found success.
|
|
177
|
+
- Never delete the parent project `rrvtrumtkhrsbhcyrwvf` itself or any persistent/production branch.
|
|
142
178
|
|
|
143
179
|
### Step 7.5: Complete Standalone Task
|
|
144
180
|
|
|
145
|
-
Note: `complete_standalone_task` is called only after
|
|
181
|
+
Note: `complete_standalone_task` is called only after `codebyplan ship` succeeds (no `checks_failed`) — the DB completion record reflects work that has landed in production.
|
|
146
182
|
|
|
147
183
|
Resolve caller worktree: `CALLER_WT=$(npx codebyplan resolve-worktree 2>/dev/null)`.
|
|
148
184
|
|
|
@@ -168,7 +204,10 @@ Apply the `cleanup` skill inline to remove orphan references to deleted/modified
|
|
|
168
204
|
**Rounds**: [N] completed
|
|
169
205
|
**Files**: [N] changed
|
|
170
206
|
**Commit**: [hash]
|
|
171
|
-
**
|
|
207
|
+
**PR**: [pr_url]
|
|
208
|
+
**Merge commit**: [merge_commit]
|
|
209
|
+
**Branch deleted**: [branch_deleted]
|
|
210
|
+
**Version bumps**: [<name>: <current> → <next> per package, or "none"]
|
|
172
211
|
**Warnings**: [any QA / file-approval warnings from Step 3, or "none"]
|
|
173
212
|
```
|
|
174
213
|
|
|
@@ -187,7 +226,7 @@ Do NOT use AskUserQuestion for routing. Do NOT use the Skill tool to auto-trigge
|
|
|
187
226
|
## Key Rules
|
|
188
227
|
|
|
189
228
|
- **`caller_worktree_id` is REQUIRED** for `complete_standalone_task`
|
|
190
|
-
- **Branch
|
|
229
|
+
- **Branch shipping lives here** (not in checkpoint-end) — standalone tasks self-ship to production via `codebyplan ship`
|
|
191
230
|
- **Single-directive routing** — no menus, no auto-triggers via Skill tool
|
|
192
231
|
- **No checkpoint** — never check `checkpoint_id`, always treat task as standalone
|
|
193
232
|
- **Never skippable prerequisites** — check_verdict READY + task_testing_output all_passed required
|
|
@@ -196,6 +235,7 @@ Do NOT use AskUserQuestion for routing. Do NOT use the Skill tool to auto-trigge
|
|
|
196
235
|
|
|
197
236
|
- **Triggered by**: `/cbp-standalone-task-testing` emits directive (user runs it manually after seeing directive)
|
|
198
237
|
- **Chain**: `/cbp-standalone-task-check` → `/cbp-standalone-task-testing` → `/cbp-standalone-task-complete`
|
|
238
|
+
- **Delegates to**: `codebyplan ship` CLI (Step 7 — PR creation, check polling, merge, branch cleanup)
|
|
199
239
|
- **Reads**: MCP `get_current_standalone_task`, `get_standalone_tasks`, `get_standalone_rounds`
|
|
200
240
|
- **Writes**: MCP `update_standalone_task`, `complete_standalone_task`
|
|
201
241
|
- **Uses skills (inline, no sub-agent)**: `cleanup` (if deletions), `migration` (if exports renamed)
|
|
@@ -57,7 +57,7 @@ Invalid --mode value: <value>. Valid values: pre_pr_create, pre_merge.
|
|
|
57
57
|
|
|
58
58
|
Infer `TARGET` when absent:
|
|
59
59
|
- `pre_pr_create` → `integration`
|
|
60
|
-
- `pre_merge` → detect via `gh pr view --json baseRefName --jq '.baseRefName'`; fallback to `integration` if no PR is open.
|
|
60
|
+
- `pre_merge` → detect via `gh pr view --json baseRefName --jq '.baseRefName'`; fallback to `integration` if no PR is open. Production-ship callers (e.g. `cbp-ship-main` Step 2.5) should pass `--target production` explicitly rather than rely on auto-detect.
|
|
61
61
|
|
|
62
62
|
## Step 1 — Read DB Paths Config
|
|
63
63
|
|
|
@@ -339,5 +339,6 @@ Both emit `status: passed` so callers proceed; the `reason` differs for traceabi
|
|
|
339
339
|
|
|
340
340
|
## Integration
|
|
341
341
|
|
|
342
|
-
**Callers**:
|
|
342
|
+
**Callers**: `cbp-ship-main` Step 2.5 invokes this skill pre-merge (`--mode pre_merge --target production`, only when a PR already exists for the branch); the caller proceeds on `passed` (Supabase Preview check green — including the CI-level `skipping` bucket, which Step 8 maps to `passed`) and `skipped` (no DB-path changes detected — the gate did not apply), and stops on `blocked`/`pending_pr`. Note: `status: "skipped"` is NOT the CI `skipping` bucket — the two are distinct layers.
|
|
343
|
+
**Canonical bucket contract**: the gh-checks bucket vocabulary used in Step 6 — `pass|fail|pending|skipping|cancel` — is the single canonical contract shared with `evaluateChecks()` in `packages/codebyplan-package/src/lib/ship.ts` (the generic poller inside `codebyplan ship`). Only `pass`/`skipping` are non-blocking, `pending` waits, and every other disposition blocks (fail-safe). The two pollers MUST NOT drift — any change to this enum or its dispositions must land in both places.
|
|
343
344
|
**Tools used**: `mcp__supabase__list_branches` for project_ref resolution; `mcp__supabase__get_logs` for failure diagnostics; `gh pr checks` for status polling; `supabase --experimental branches get` as CLI fallback.
|