codebyplan 1.5.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist/cli.js +4462 -748
  2. package/package.json +5 -1
  3. package/templates/.gitkeep +0 -0
  4. package/templates/README.md +20 -0
  5. package/templates/agents/cbp-cc-executor.md +213 -0
  6. package/templates/agents/cbp-database-agent.md +229 -0
  7. package/templates/agents/cbp-improve-claude.md +245 -0
  8. package/templates/agents/cbp-improve-round.md +284 -0
  9. package/templates/agents/cbp-mechanical-edits.md +111 -0
  10. package/templates/agents/cbp-research.md +282 -0
  11. package/templates/agents/cbp-round-executor.md +604 -0
  12. package/templates/agents/cbp-security-agent.md +134 -0
  13. package/templates/agents/cbp-task-check.md +213 -0
  14. package/templates/agents/cbp-task-planner.md +582 -0
  15. package/templates/agents/cbp-test-e2e-agent.md +363 -0
  16. package/templates/agents/cbp-testing-qa-agent.md +400 -0
  17. package/templates/context/mcp-docs.md +139 -0
  18. package/templates/hooks/README.md +236 -0
  19. package/templates/hooks/cbp-auto-test-hooks.sh +44 -0
  20. package/templates/hooks/cbp-lint-format-on-edit.sh +159 -0
  21. package/templates/hooks/cbp-maestro-yaml-validate.sh +100 -0
  22. package/templates/hooks/cbp-mcp-migration-guard.sh +32 -0
  23. package/templates/hooks/cbp-mcp-round-sync.sh +79 -0
  24. package/templates/hooks/cbp-mcp-worktree-inject.sh +76 -0
  25. package/templates/hooks/cbp-notify.sh +68 -0
  26. package/templates/hooks/cbp-plugin-dispatch.sh +29 -0
  27. package/templates/hooks/cbp-pre-commit-quality-gate.sh +204 -0
  28. package/templates/hooks/cbp-statusline.sh +347 -0
  29. package/templates/hooks/cbp-subagent-statusline.sh +182 -0
  30. package/templates/hooks/cbp-test-coverage-gate.sh +144 -0
  31. package/templates/hooks/cbp-test-hooks.sh +320 -0
  32. package/templates/hooks/hooks.json +85 -0
  33. package/templates/hooks/validate-context-usage.sh +59 -0
  34. package/templates/hooks/validate-git-commit.sh +78 -0
  35. package/templates/hooks/validate-git-stash-deny.sh +32 -0
  36. package/templates/hooks/validate-structure-lengths.sh +57 -0
  37. package/templates/hooks/validate-structure-lib.sh +104 -0
  38. package/templates/hooks/validate-structure-patterns.sh +54 -0
  39. package/templates/hooks/validate-structure-scope.sh +33 -0
  40. package/templates/hooks/validate-structure-smoke.sh +95 -0
  41. package/templates/hooks/validate-structure-templates.sh +34 -0
  42. package/templates/hooks/validate-structure.sh +69 -0
  43. package/templates/rules/.gitkeep +0 -0
  44. package/templates/rules/README.md +47 -0
  45. package/templates/rules/context-file-loading.md +52 -0
  46. package/templates/rules/scope-vocabulary.md +64 -0
  47. package/templates/rules/todo-backend.md +109 -0
  48. package/templates/settings.project.base.json +55 -0
  49. package/templates/settings.user.base.json +25 -0
  50. package/templates/skills/cbp-build-cc-agent/SKILL.md +139 -0
  51. package/templates/skills/cbp-build-cc-agent/examples/read-only-reviewer.md +32 -0
  52. package/templates/skills/cbp-build-cc-agent/examples/with-hooks.md +41 -0
  53. package/templates/skills/cbp-build-cc-agent/examples/with-skills-preload.md +25 -0
  54. package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +153 -0
  55. package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +37 -0
  56. package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +18 -0
  57. package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +67 -0
  58. package/templates/skills/cbp-build-cc-agent/templates/agent.md +66 -0
  59. package/templates/skills/cbp-build-cc-claude-file/SKILL.md +178 -0
  60. package/templates/skills/cbp-build-cc-claude-file/examples/minimal-project.md +33 -0
  61. package/templates/skills/cbp-build-cc-claude-file/examples/monorepo-with-imports.md +39 -0
  62. package/templates/skills/cbp-build-cc-claude-file/reference/imports.md +72 -0
  63. package/templates/skills/cbp-build-cc-claude-file/reference/what-belongs.md +39 -0
  64. package/templates/skills/cbp-build-cc-claude-file/templates/project-claude-md.md +48 -0
  65. package/templates/skills/cbp-build-cc-claude-file/templates/user-claude-md.md +22 -0
  66. package/templates/skills/cbp-build-cc-memory/SKILL.md +201 -0
  67. package/templates/skills/cbp-build-cc-memory/examples/feedback-memory.md +11 -0
  68. package/templates/skills/cbp-build-cc-memory/examples/project-memory.md +11 -0
  69. package/templates/skills/cbp-build-cc-memory/examples/reference-memory.md +13 -0
  70. package/templates/skills/cbp-build-cc-memory/examples/user-memory.md +14 -0
  71. package/templates/skills/cbp-build-cc-memory/reference/memory-types.md +59 -0
  72. package/templates/skills/cbp-build-cc-memory/reference/when-to-save.md +62 -0
  73. package/templates/skills/cbp-build-cc-memory/templates/MEMORY-index.md +4 -0
  74. package/templates/skills/cbp-build-cc-memory/templates/memory-entry.md +15 -0
  75. package/templates/skills/cbp-build-cc-mode/SKILL.md +99 -0
  76. package/templates/skills/cbp-build-cc-rule/SKILL.md +176 -0
  77. package/templates/skills/cbp-build-cc-rule/examples/global-rule.md +19 -0
  78. package/templates/skills/cbp-build-cc-rule/examples/scoped-rule.md +41 -0
  79. package/templates/skills/cbp-build-cc-rule/reference/paths-patterns.md +48 -0
  80. package/templates/skills/cbp-build-cc-rule/templates/rule.md +32 -0
  81. package/templates/skills/cbp-build-cc-settings/SKILL.md +220 -0
  82. package/templates/skills/cbp-build-cc-settings/examples/hooks-config.json +64 -0
  83. package/templates/skills/cbp-build-cc-settings/examples/permissions-config.json +34 -0
  84. package/templates/skills/cbp-build-cc-settings/examples/sandbox-config.json +42 -0
  85. package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +104 -0
  86. package/templates/skills/cbp-build-cc-settings/reference/permission-rules.md +61 -0
  87. package/templates/skills/cbp-build-cc-settings/reference/scope-precedence.md +73 -0
  88. package/templates/skills/cbp-build-cc-settings/reference/settings-fields.md +166 -0
  89. package/templates/skills/cbp-build-cc-settings/templates/settings.json +23 -0
  90. package/templates/skills/cbp-build-cc-settings/templates/settings.local.json +10 -0
  91. package/templates/skills/cbp-build-cc-skill/SKILL.md +154 -0
  92. package/templates/skills/cbp-build-cc-skill/examples/dynamic-context.md +31 -0
  93. package/templates/skills/cbp-build-cc-skill/examples/fork-skill.md +22 -0
  94. package/templates/skills/cbp-build-cc-skill/examples/knowledge-skill.md +25 -0
  95. package/templates/skills/cbp-build-cc-skill/examples/task-skill.md +29 -0
  96. package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +157 -0
  97. package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +35 -0
  98. package/templates/skills/cbp-build-cc-skill/reference/string-substitutions.md +60 -0
  99. package/templates/skills/cbp-build-cc-skill/scripts/validate-skill.sh +90 -0
  100. package/templates/skills/cbp-build-cc-skill/templates/skill.md +51 -0
  101. package/templates/skills/cbp-checkpoint-check/SKILL.md +156 -0
  102. package/templates/skills/cbp-checkpoint-complete/SKILL.md +109 -0
  103. package/templates/skills/cbp-checkpoint-create/SKILL.md +287 -0
  104. package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
  105. package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
  106. package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
  107. package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
  108. package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
  109. package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
  110. package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
  111. package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
  112. package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
  113. package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
  114. package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
  115. package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
  116. package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
  117. package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
  118. package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
  119. package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
  120. package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
  121. package/templates/skills/cbp-git-commit/SKILL.md +278 -0
  122. package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
  123. package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
  124. package/templates/skills/cbp-merge-main/SKILL.md +228 -0
  125. package/templates/skills/cbp-round-check/SKILL.md +104 -0
  126. package/templates/skills/cbp-round-end/SKILL.md +183 -0
  127. package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
  128. package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
  129. package/templates/skills/cbp-round-execute/SKILL.md +211 -0
  130. package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
  131. package/templates/skills/cbp-round-input/SKILL.md +165 -0
  132. package/templates/skills/cbp-round-start/SKILL.md +222 -0
  133. package/templates/skills/cbp-round-update/SKILL.md +163 -0
  134. package/templates/skills/cbp-session-end/SKILL.md +187 -0
  135. package/templates/skills/cbp-session-start/SKILL.md +155 -0
  136. package/templates/skills/cbp-ship/SKILL.md +332 -0
  137. package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
  138. package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
  139. package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
  140. package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
  141. package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
  142. package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
  143. package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
  144. package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
  145. package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
  146. package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
  147. package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
  148. package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
  149. package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
  150. package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
  151. package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
  152. package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
  153. package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
  154. package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
  155. package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
  156. package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
  157. package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
  158. package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
  159. package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
  160. package/templates/skills/cbp-ship/reference/versioning.md +116 -0
  161. package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
  162. package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
  163. package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
  164. package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
  165. package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
  166. package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
  167. package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
  168. package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
  169. package/templates/skills/cbp-ship/templates/eas.json +66 -0
  170. package/templates/skills/cbp-ship/templates/railway.toml +15 -0
  171. package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
  172. package/templates/skills/cbp-ship/templates/vercel.json +19 -0
  173. package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
  174. package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
  175. package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
  176. package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
  177. package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
  178. package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
  179. package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
  180. package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
  181. package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
  182. package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
  183. package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
  184. package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
  185. package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
  186. package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
  187. package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
  188. package/templates/skills/cbp-ship-main/SKILL.md +65 -0
  189. package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
  190. package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
  191. package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
  192. package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
  193. package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
  194. package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
  195. package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
  196. package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
  197. package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
  198. package/templates/skills/cbp-task-check/SKILL.md +166 -0
  199. package/templates/skills/cbp-task-complete/SKILL.md +206 -0
  200. package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
  201. package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
  202. package/templates/skills/cbp-task-create/SKILL.md +167 -0
  203. package/templates/skills/cbp-task-start/SKILL.md +239 -0
  204. package/templates/skills/cbp-task-testing/SKILL.md +277 -0
  205. package/templates/skills/cbp-todo/SKILL.md +97 -0
@@ -0,0 +1,138 @@
1
+ # Surface: tauri-desktop
2
+
3
+ Build, sign, notarize, and release a Tauri desktop app via GitHub Actions tag-trigger.
4
+
5
+ This surface file is the orchestrator's interface to the Tauri release pipeline — secrets, CI workflow, signing, notarization, and gotchas are all covered in the sections below.
6
+
7
+ ## Detection
8
+
9
+ Signals:
10
+
11
+ - `src-tauri/tauri.conf.json` present
12
+ - `apps/desktop/src-tauri/` (monorepo) — instance is `apps/desktop`
13
+
14
+ ## Configured indicators
15
+
16
+ `configured: true` requires:
17
+
18
+ 1. `src-tauri/tauri.conf.json` parseable, with `productName` + `version`
19
+ 2. `.github/workflows/release-desktop.yml` (or similar tag-trigger workflow) present
20
+ 3. Required GitHub secrets set (per architecture doc — 9 secrets for the WBP setup)
21
+ 4. `gh auth status` succeeds (CLI authenticated)
22
+
23
+ Probe: `gh secret list -R OWNER/REPO` and check that the 9 secret names from the architecture doc are present.
24
+
25
+ ## Variants
26
+
27
+ | Variant | When |
28
+ |---|---|
29
+ | `tag-and-release` | Version bump detected in `src-tauri/tauri.conf.json` `version` OR root `package.json` |
30
+ | `skip` | No version change since last shipment — desktop releases are tag-driven, not branch-driven |
31
+
32
+ ## STEPS — tag-and-release variant
33
+
34
+ 1. **Read version from tauri config**:
35
+ ```bash
36
+ VERSION=$(jq -r .version "$APP_PATH/src-tauri/tauri.conf.json")
37
+ ```
38
+
39
+ 2. **Verify tag doesn't already exist**:
40
+ ```bash
41
+ git fetch origin --tags
42
+ if git rev-parse "v$VERSION" >/dev/null 2>&1; then
43
+ echo "Tag v$VERSION already exists — skip or bump version"; exit 1
44
+ fi
45
+ ```
46
+
47
+ 3. **Confirm with user** via AskUserQuestion:
48
+ ```
49
+ Push tag v$VERSION? This triggers GH Actions build + sign + notarize + release.
50
+ The build takes ~15 min. You'll receive a GH Releases page when complete.
51
+ A) Push tag — start the release
52
+ B) Skip — bump version in another commit first
53
+ ```
54
+
55
+ 4. **Push the tag**:
56
+ ```bash
57
+ git tag -a "v$VERSION" -m "Release v$VERSION"
58
+ git push origin "v$VERSION"
59
+ ```
60
+
61
+ 5. **Get the workflow run** triggered by the tag:
62
+ ```bash
63
+ sleep 5 # GH Actions registration lag
64
+ RUN_ID=$(gh run list --workflow=release-desktop.yml --limit 1 --json databaseId,headBranch,event \
65
+ | jq -r ".[] | select(.event == \"push\" and .headBranch == \"v$VERSION\") | .databaseId")
66
+ ```
67
+
68
+ 6. **Watch the run** (15 min cap):
69
+ ```bash
70
+ gh run watch "$RUN_ID" --exit-status
71
+ ```
72
+
73
+ 7. **Verify GH Release was created**:
74
+ ```bash
75
+ gh release view "v$VERSION" --json assets,publishedAt | jq
76
+ ```
77
+ Expect: 2 assets minimum (ARM .dmg + Intel .dmg) for macOS-only builds; more for Win/Linux if configured.
78
+
79
+ 8. **Result**:
80
+ ```json
81
+ {
82
+ "status": "verified",
83
+ "version": "v1.4.0",
84
+ "release_url": "https://github.com/OWNER/REPO/releases/tag/v1.4.0",
85
+ "assets": ["WorkByPlan_1.4.0_aarch64.dmg", "WorkByPlan_1.4.0_x64.dmg"],
86
+ "workflow_run_id": "1234567890"
87
+ }
88
+ ```
89
+
90
+ ## Verification
91
+
92
+ `scripts/verify-tauri.sh`:
93
+
94
+ ```bash
95
+ VERSION="$1"
96
+
97
+ # Tag exists locally + remote
98
+ git rev-parse "v$VERSION" >/dev/null 2>&1 || { echo "Tag missing"; exit 1; }
99
+ git ls-remote --tags origin "v$VERSION" | grep -q . || { echo "Tag not pushed"; exit 1; }
100
+
101
+ # GH Release published
102
+ PUBLISHED=$(gh release view "v$VERSION" --json publishedAt -q .publishedAt 2>/dev/null)
103
+ [ -n "$PUBLISHED" ] || { echo "Release not published"; exit 1; }
104
+
105
+ # At least one signed .dmg in assets
106
+ ASSETS=$(gh release view "v$VERSION" --json assets -q '.assets | length')
107
+ [ "$ASSETS" -ge 1 ] || { echo "No release assets"; exit 1; }
108
+
109
+ # Updater manifest (latest.json) updated — Tauri auto-update needs this
110
+ gh release view "v$VERSION" --json assets -q '.assets[].name' | grep -q "latest.json" \
111
+ || echo "WARN: latest.json missing from release — auto-update may not work"
112
+
113
+ echo "OK"
114
+ ```
115
+
116
+ ## Common failures
117
+
118
+ | Symptom | Cause | Fix |
119
+ |---|---|---|
120
+ | Workflow fails at "Notarize" step | Apple API key file path issue | See architecture doc § "Apple Notarization Key" — `.p8` content must be written to a file path, not passed as string |
121
+ | `latest.json` missing | Tauri updater config disabled in tauri.conf.json | Enable `tauri.bundle.updater` |
122
+ | ARM/Intel asset missing | matrix build for one arch failed | Check workflow logs; commonly Rust toolchain issue on the failed runner |
123
+ | "Invalid signing identity" | `APPLE_SIGNING_IDENTITY` secret has wrong format | Must be exact match: `Developer ID Application: Name (TEAMID)` |
124
+ | Public download URL broken | Vercel Blob upload step failed | Check `BLOB_READ_WRITE_TOKEN` secret + Vercel storage quota |
125
+
126
+ ## Rollback
127
+
128
+ Tauri's auto-updater honors the most recent `latest.json` published. To rollback:
129
+
130
+ 1. Delete the bad release: `gh release delete v$VERSION --yes`
131
+ 2. Republish a prior tag: `gh release create v$PRIOR --notes "Republish after rollback" $PRIOR_ASSETS`
132
+ 3. The auto-updater will roll users back at their next launch
133
+
134
+ The orchestrator surfaces this as an option when verification fails.
135
+
136
+ ## Configure-once setup
137
+
138
+ See `${CLAUDE_PLUGIN_ROOT}/skills/ship-configure/reference/tauri-desktop.md` for first-time setup (Apple Developer cert, key generation, secrets upload, workflow scaffolding).
@@ -0,0 +1,124 @@
1
+ # Surface: vercel-web
2
+
3
+ Deploy a Next.js (or static) app to Vercel.
4
+
5
+ ## Detection
6
+
7
+ Signals (any one is sufficient):
8
+
9
+ - `vercel.json` at app root
10
+ - `.vercel/project.json` at app root (links repo to Vercel project)
11
+ - App directory contains `next` in `package.json` dependencies AND no other surface signal claims it
12
+
13
+ A repo can have multiple `vercel-web` instances — one per app folder under `apps/*`.
14
+
15
+ ## Configured indicators
16
+
17
+ `configured: true` requires:
18
+
19
+ 1. `.vercel/project.json` present with `projectId` and `orgId`
20
+ 2. Vercel CLI authenticated (`vercel whoami` succeeds)
21
+ 3. `.codebyplan/shipment.json` `.surfaces.vercel-web.{instance}` block (optional but recommended)
22
+
23
+ If `.vercel/project.json` is missing → `configured: false`, reason "not linked to Vercel project — run /cbp-ship-configure".
24
+
25
+ ## Variant: production
26
+
27
+ Default for shipping to PRODUCTION branch (per `branch_config.production`). Vercel auto-deploys on push to the linked production branch — this variant **observes** the auto-deploy, doesn't trigger it.
28
+
29
+ ### STEPS — production variant
30
+
31
+ 1. **Read project ID** from `.vercel/project.json`:
32
+ ```bash
33
+ PROJECT_ID=$(jq -r .projectId .vercel/project.json)
34
+ ORG_ID=$(jq -r .orgId .vercel/project.json)
35
+ ```
36
+
37
+ 2. **Get the deployment triggered by the merge commit**:
38
+ ```bash
39
+ MERGE_SHA=$(git rev-parse HEAD)
40
+ DEPLOYMENT=$(vercel ls --json 2>/dev/null | jq -r ".[] | select(.meta.githubCommitSha == \"$MERGE_SHA\") | .uid" | head -1)
41
+ ```
42
+
43
+ If no deployment found within 60s, Vercel is slow — wait up to 5min:
44
+ ```bash
45
+ timeout 300 bash -c "while ! vercel ls --json | jq -e '.[] | select(.meta.githubCommitSha == \"$MERGE_SHA\")' > /dev/null; do sleep 10; done"
46
+ ```
47
+
48
+ 3. **Wait for READY state**:
49
+ ```bash
50
+ vercel inspect "$DEPLOYMENT" --wait
51
+ ```
52
+
53
+ 4. **Capture URL + readyState**:
54
+ ```bash
55
+ STATUS=$(vercel inspect "$DEPLOYMENT" --json | jq -r '.readyState')
56
+ URL=$(vercel inspect "$DEPLOYMENT" --json | jq -r '.alias[0] // .url')
57
+ ```
58
+
59
+ 5. **Result**: `{ status: STATUS, url: URL, deployment_id: DEPLOYMENT }` to orchestrator.
60
+
61
+ ## Variant: manual-redeploy
62
+
63
+ For "the auto-deploy failed, force a redeploy" path. User triggers explicitly.
64
+
65
+ ### STEPS — manual-redeploy variant
66
+
67
+ ```bash
68
+ cd "$APP_PATH"
69
+ vercel --prod --yes
70
+ ```
71
+
72
+ Capture deployment ID from output, then run Steps 3–5 from production variant.
73
+
74
+ ## Variant: preview
75
+
76
+ Vercel auto-creates a preview deployment per PR. The orchestrator typically does NOT need to trigger anything for staging shipments — but it CAN verify the preview deployment for the integration branch:
77
+
78
+ ```bash
79
+ INTEGRATION=$(jq -r .branch_config.integration .codebyplan/git.json)
80
+ INTEGRATION_SHA=$(git rev-parse "origin/$INTEGRATION")
81
+ vercel ls --json | jq ".[] | select(.meta.githubCommitSha == \"$INTEGRATION_SHA\" and .target == \"preview\")"
82
+ ```
83
+
84
+ ## Verification
85
+
86
+ `scripts/verify-vercel.sh` does:
87
+
88
+ 1. `vercel inspect "$DEPLOYMENT_ID" --json | jq -r .readyState` returns `READY`
89
+ 2. `curl -sI "$URL" | head -1` returns `HTTP/2 200` (or 401 for password-protected previews — also success)
90
+ 3. If `apps/{instance}/public/health.json` or `/api/health` exists, hit it: expect 200
91
+
92
+ Timeouts:
93
+ - Build: 5 min (Vercel Hobby is slower; allow 8 min)
94
+ - HTTP probe: 30s
95
+
96
+ ## Common failures
97
+
98
+ | Symptom | Likely cause | Fix |
99
+ |---|---|---|
100
+ | `vercel ls` shows no deployment for the merge SHA | GitHub integration not connected, or push didn't trigger | Check Vercel dashboard → Settings → Git; reconnect if needed |
101
+ | Build fails with "Module not found" | Monorepo root not configured | In Vercel project settings: Build Command → `cd ../.. && pnpm turbo build --filter={instance}`, Root Directory → `apps/{instance}` |
102
+ | Preview URL 404s | Project paused or env var missing | Check Vercel logs for the deployment; common: missing `DATABASE_URL` |
103
+ | Build succeeds but page is blank | Production env vars missing | `vercel env ls production` — compare to `.env.example` |
104
+
105
+ ## Rollback
106
+
107
+ ```bash
108
+ vercel rollback # interactive: pick a prior deployment
109
+ vercel promote "$PRIOR_DEPLOYMENT" # promote a specific deployment to production
110
+ ```
111
+
112
+ The orchestrator surfaces this as an option when a verification fails:
113
+
114
+ ```
115
+ Vercel deployment failed verification.
116
+ Options:
117
+ A) Retry — the deploy may complete async
118
+ B) Rollback to prior production deployment
119
+ C) Investigate manually — show me the build logs
120
+ ```
121
+
122
+ ## Configure-once setup
123
+
124
+ See `${CLAUDE_PLUGIN_ROOT}/skills/ship-configure/reference/vercel.md` for first-time setup (vercel link, env var sync, GH integration verification).
@@ -0,0 +1,144 @@
1
+ # Surface: vscode-ext
2
+
3
+ Publish a VS Code extension to the Visual Studio Marketplace.
4
+
5
+ ## Detection
6
+
7
+ Signals:
8
+
9
+ - `package.json` with `engines.vscode` field present
10
+ - `package.json` with `publisher` field present
11
+ - A `.vsixmanifest` template or built `.vsix` may be present (optional)
12
+
13
+ ## Configured indicators
14
+
15
+ `configured: true` requires:
16
+
17
+ 1. `package.json` has `name`, `displayName`, `publisher`, `version`, `engines.vscode`
18
+ 2. `vsce` CLI installed (`npx @vscode/vsce --version` succeeds)
19
+ 3. The user has a Personal Access Token (PAT) for the marketplace publisher
20
+ 4. `vsce verify-pat <publisher>` succeeds
21
+
22
+ PAT scope required: "Marketplace → Manage" on Azure DevOps for the publisher.
23
+
24
+ ## Variants
25
+
26
+ | Variant | When |
27
+ |---|---|
28
+ | `vsce-publish` | `package.json` version > marketplace version |
29
+ | `skip` | No version bump |
30
+
31
+ ## STEPS — vsce-publish variant
32
+
33
+ The `vsce` flow is similar to npm — token auth, the user runs the publish command for security.
34
+
35
+ 1. **Detect extension package**:
36
+ ```bash
37
+ EXT_PATH=$(find apps packages -maxdepth 3 -name package.json -exec sh -c \
38
+ 'jq -e ".engines.vscode and .publisher" "$1" >/dev/null 2>&1 && dirname "$1"' _ {} \; | head -1)
39
+ ```
40
+
41
+ 2. **Check version vs marketplace**:
42
+ ```bash
43
+ PUBLISHER=$(jq -r .publisher "$EXT_PATH/package.json")
44
+ NAME=$(jq -r .name "$EXT_PATH/package.json")
45
+ LOCAL=$(jq -r .version "$EXT_PATH/package.json")
46
+ MARKET=$(npx @vscode/vsce show "$PUBLISHER.$NAME" --json 2>/dev/null | jq -r '.versions[0].version' || echo "")
47
+
48
+ [ "$LOCAL" = "$MARKET" ] && { echo "Already published"; exit 0; }
49
+ ```
50
+
51
+ 3. **Build the extension** (orchestrator-side):
52
+ ```bash
53
+ cd "$EXT_PATH"
54
+ pnpm run build # if defined
55
+ ```
56
+
57
+ 4. **Package locally to verify** (no publish):
58
+ ```bash
59
+ cd "$EXT_PATH"
60
+ npx @vscode/vsce package --no-yarn -o "/tmp/$NAME-$LOCAL.vsix"
61
+ ```
62
+
63
+ If packaging fails (typical: missing icon, README too short), HALT — surface error.
64
+
65
+ 5. **Display publish command** for the user:
66
+ ```
67
+ ## VS Code Marketplace publish — manual step
68
+
69
+ Extension: $PUBLISHER.$NAME @ $LOCAL
70
+ Directory: $EXT_PATH
71
+
72
+ Run in your terminal:
73
+ cd $EXT_PATH
74
+ npx @vscode/vsce publish
75
+
76
+ You'll be prompted for the PAT if not cached. Reply 'done' when published, 'fail' if it errored.
77
+ ```
78
+
79
+ 6. **Verify publication**:
80
+ ```bash
81
+ sleep 30 # marketplace propagation
82
+ RESOLVED=$(npx @vscode/vsce show "$PUBLISHER.$NAME" --json | jq -r '.versions[0].version')
83
+ [ "$RESOLVED" = "$LOCAL" ] || { echo "Not yet visible — Marketplace can lag 1-5min"; exit 2; }
84
+ ```
85
+
86
+ 7. **Result**:
87
+ ```json
88
+ {
89
+ "status": "verified",
90
+ "extension": "publisher.name",
91
+ "version": "1.4.0",
92
+ "marketplace_url": "https://marketplace.visualstudio.com/items?itemName=publisher.name"
93
+ }
94
+ ```
95
+
96
+ ## Pre-release channel
97
+
98
+ VS Code supports a "pre-release" channel via `vsce publish --pre-release`. Useful for staged rollouts:
99
+
100
+ ```bash
101
+ npx @vscode/vsce publish --pre-release # version must already include preid (e.g., 1.4.0-rc.1)
102
+ ```
103
+
104
+ If the extension supports pre-release, the orchestrator asks the user which channel:
105
+
106
+ ```
107
+ A) Stable channel — 1.4.0 → all users
108
+ B) Pre-release channel — 1.4.0-rc.1 → users opted into pre-release
109
+ ```
110
+
111
+ ## Verification
112
+
113
+ `scripts/verify-vscode-ext.sh`:
114
+
115
+ ```bash
116
+ PUBLISHER_NAME="$1" # e.g., publisher.name
117
+ VERSION="$2"
118
+
119
+ RESOLVED=$(npx @vscode/vsce show "$PUBLISHER_NAME" --json 2>/dev/null | jq -r '.versions[0].version')
120
+ [ "$RESOLVED" = "$VERSION" ] || { echo "Not on marketplace: $VERSION (latest: $RESOLVED)"; exit 1; }
121
+
122
+ # Activation events resolvable
123
+ EVENTS=$(npx @vscode/vsce show "$PUBLISHER_NAME" --json | jq -r '.activationEvents | length // 0')
124
+ [ "$EVENTS" -gt 0 ] || echo "WARN: no activation events declared — extension won't load"
125
+
126
+ echo "OK"
127
+ ```
128
+
129
+ ## Common failures
130
+
131
+ | Symptom | Cause | Fix |
132
+ |---|---|---|
133
+ | `vsce publish` fails with 401 | PAT expired or wrong scope | Regenerate PAT with "Marketplace → Manage" scope |
134
+ | Package size > 50MB | `.vscodeignore` missing critical entries | Add `node_modules/**`, `*.test.*`, `.git/**` to `.vscodeignore` |
135
+ | Icon validation fails | PNG must be ≥128×128 | Replace icon, ensure PNG format |
136
+ | Marketplace 5xx during publish | Azure DevOps infra | Wait 5-10 min, retry |
137
+
138
+ ## Rollback
139
+
140
+ `vsce unpublish <publisher>.<name> --force` removes the entire extension. To unpublish just one version, use the marketplace web UI → Manage Extension → Versions → remove. Surface as manual step.
141
+
142
+ ## Configure-once setup
143
+
144
+ See `${CLAUDE_PLUGIN_ROOT}/skills/ship-configure/reference/vscode-ext.md` for first-time setup (publisher account creation, PAT generation, vsce login).
@@ -0,0 +1,60 @@
1
+ # Shipment Surface Catalog
2
+
3
+ Authoritative list of every surface `/cbp-ship` knows how to deploy. The detection script (`scripts/detect-surfaces.sh`) maps repo signals to one of these IDs.
4
+
5
+ ## Surfaces
6
+
7
+ | ID | Runtime | Detection signal | Deploy mechanism | Reference |
8
+ | ----------------- | ------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------- |
9
+ | `vercel-web` | Web (Next.js, static) | `vercel.json` OR `.vercel/project.json` OR Next.js app under `apps/*` with `next` in deps | Auto-deploy on git push to linked branch + optional `vercel --prod` for force redeploy | [surface-vercel.md](surface-vercel.md) |
10
+ | `expo-mobile` | Mobile (iOS/Android) | `app.json` / `app.config.{ts,js}` AND `expo` in deps | EAS Build → EAS Submit → TestFlight (iOS) / Play Console (Android) | [surface-expo-eas.md](surface-expo-eas.md) |
11
+ | `tauri-desktop` | Desktop (macOS/Windows/Linux) | `src-tauri/tauri.conf.json` | Push `v*.*.*` tag → GH Actions builds + signs + releases | [surface-tauri.md](surface-tauri.md) |
12
+ | `npm-package` | Package distribution | `package.json` with `private: false` OR `publishConfig` AND not under `apps/` | `npm publish` from local (2FA) or GH Actions (OIDC trusted publishing) | [surface-npm.md](surface-npm.md) |
13
+ | `vscode-ext` | VS Code Marketplace | `package.json` with `engines.vscode` AND `publisher` field | `vsce publish` from local OR GH Actions workflow | [surface-vscode-ext.md](surface-vscode-ext.md) |
14
+ | `railway-backend` | Backend (NestJS / Node service) | `railway.toml` OR `railway.json` OR `.railway/` AND non-frontend `package.json` | `railway up` from local OR auto-deploy on git push to linked branch | [surface-railway.md](surface-railway.md) |
15
+ | `supabase` | Database migrations | `supabase/migrations/` with at least one .sql newer than last shipment | `supabase db push` (interactive review of diff) | [surface-supabase.md](surface-supabase.md) |
16
+
17
+ ## Variant rules
18
+
19
+ Each surface has variant choices that the orchestrator picks per-checkpoint:
20
+
21
+ | Surface | Variants | Default rule |
22
+ | ----------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
23
+ | `vercel-web` | `production`, `preview`, `manual-redeploy` | `production` if shipped to PRODUCTION branch this checkpoint, else skip (preview already auto-created on PR) |
24
+ | `expo-mobile` | `expo-go-only`, `eas-internal`, `eas-external` | Always ASK — never default; mobile shipment is opt-in per checkpoint |
25
+ | `tauri-desktop` | `tag-and-release`, `skip` | `tag-and-release` if version bump detected in `src-tauri/tauri.conf.json` or root, else `skip` |
26
+ | `npm-package` | `publish`, `skip` | `publish` if `package.json` version is unpublished on registry, else `skip` |
27
+ | `vscode-ext` | `vsce-publish`, `skip` | `vsce-publish` if version bump, else `skip` |
28
+ | `railway-backend` | `deploy`, `skip` | `deploy` if linked AND any service code changed this checkpoint, else `skip` |
29
+ | `supabase` | `push-migrations`, `skip` | `push-migrations` if pending migration count > 0, else `skip` |
30
+
31
+ ## Detection precedence
32
+
33
+ When a surface signal is ambiguous (e.g., `apps/web` has both `vercel.json` and a `Dockerfile`), use this precedence:
34
+
35
+ 1. Explicit `.codebyplan/shipment.json` `surfaces.{id}` block — definitive
36
+ 2. Explicit `.codebyplan/shipment.json` `disabled[]` list — surface is hidden
37
+ 3. Filesystem signal in priority order (above table)
38
+ 4. Fallback — emit with `configured: false` and surface to user
39
+
40
+ ## Multiple instances of one surface
41
+
42
+ A monorepo can have multiple instances of the same surface — e.g., `tarkur` ships 3 Vercel apps (`apps/homepage`, `apps/creator`, `apps/tenant`). Detection emits each as a separate entry:
43
+
44
+ ```json
45
+ { "id": "vercel-web", "instance": "apps/homepage", "configured": true }
46
+ { "id": "vercel-web", "instance": "apps/creator", "configured": true }
47
+ { "id": "vercel-web", "instance": "apps/tenant", "configured": true }
48
+ ```
49
+
50
+ `/cbp-ship` deploys each instance independently and reports each separately.
51
+
52
+ ## Excluded by design
53
+
54
+ These don't have surfaces because shipment doesn't apply:
55
+
56
+ - `apps/cli` — bundled inside an npm package; ships via `npm-package`
57
+ - `packages/auth`, `packages/design-tokens`, etc. — internal workspace packages, never published
58
+ - Storybook builds — typically deployed as part of `vercel-web` (Storybook on Vercel) or skipped
59
+
60
+ If a project genuinely needs a missing surface (Cloudflare Pages, Fly.io, Heroku, AWS Amplify), open a feedback report — adding a surface is a structured change to this catalog plus a new reference file plus detection signal.
@@ -0,0 +1,215 @@
1
+ # TestFlight Automation
2
+
3
+ Automation contract for shipping to TestFlight from `/cbp-ship` (expo-mobile surface): App Store Connect API key setup, EAS Submit wiring, the common ITMS-90xxx error catalogue with remediations, build-expiration handling, and beta-group management via API.
4
+
5
+ ## App Store Connect API Key
6
+
7
+ The API key replaces interactive Apple ID + 2FA prompts. It is the **only** sane authentication mechanism for CI / agent-driven submission.
8
+
9
+ ### Generation
10
+
11
+ 1. Sign in to [App Store Connect](https://appstoreconnect.apple.com/) as the Account Holder (or an Admin with the right permission).
12
+ 2. Navigate to **Users and Access → Integrations → App Store Connect API → Team Keys**.
13
+ 3. Click **Generate API Key**, give it a name (e.g. `cbp-ship-ci`), and pick an access role:
14
+ - **Developer** — sufficient for build upload + TestFlight distribution.
15
+ - **App Manager** — needed to create beta groups, manage testers, edit metadata.
16
+ - **Admin** — only if you also need account-level operations (rarely needed for `/cbp-ship`).
17
+ 4. Download the `.p8` private key file. **Apple shows this exactly once** — store it immediately in the secret manager.
18
+ 5. Note the **Key ID** (10-char alphanumeric, shown next to the key) and the **Issuer ID** (UUID, shown at the top of the API page — same for all keys on the team).
19
+
20
+ You now have three secrets:
21
+
22
+ | Field | Format | Where it lives |
23
+ |---|---|---|
24
+ | `ASC_API_KEY_ID` | 10-char string | Plain env var |
25
+ | `ASC_API_ISSUER_ID` | UUID | Plain env var |
26
+ | `ASC_API_KEY` (the `.p8` content) | PEM-encoded private key | Secret manager |
27
+
28
+ Reference: [App Store Connect API — Get started](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api/).
29
+
30
+ ### Rotation
31
+
32
+ Keys do not expire automatically, but Apple recommends rotating annually. Revoke via the same Integrations page; no grace period — revoked keys 401 immediately. Plan rotations during low-ship windows.
33
+
34
+ ## EAS Submit Wiring
35
+
36
+ EAS Submit is the canonical upload path for Expo apps. Configuration lives in `eas.json` under the `submit` profile.
37
+
38
+ ### Minimal `eas.json`
39
+
40
+ ```jsonc
41
+ {
42
+ "submit": {
43
+ "production": {
44
+ "ios": {
45
+ "ascAppId": "1234567890",
46
+ "appleTeamId": "ABCDE12345",
47
+ "ascApiKeyPath": "./secrets/AuthKey_ABC1234567.p8",
48
+ "ascApiKeyId": "ABC1234567",
49
+ "ascApiKeyIssuerId": "11111111-2222-3333-4444-555555555555"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ - `ascAppId` — the numeric App Store Connect app ID (visible in the URL when viewing the app in ASC, NOT the bundle identifier).
57
+ - `appleTeamId` — 10-char Team ID from the Apple Developer Portal **Membership** page.
58
+ - `ascApiKey*` fields — the three values from key generation above. The `.p8` path is relative to the repo root.
59
+
60
+ For CI, prefer the env-var form so the `.p8` doesn't get checked in:
61
+
62
+ ```jsonc
63
+ {
64
+ "submit": {
65
+ "production": {
66
+ "ios": {
67
+ "ascAppId": "$ASC_APP_ID",
68
+ "ascApiKeyPath": "$ASC_API_KEY_PATH",
69
+ "ascApiKeyId": "$ASC_API_KEY_ID",
70
+ "ascApiKeyIssuerId": "$ASC_API_ISSUER_ID"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ The CI step writes `$ASC_API_KEY` (the PEM content) to a temp file and points `$ASC_API_KEY_PATH` at it.
78
+
79
+ Reference: [EAS Submit — Submit to the Apple App Store](https://docs.expo.dev/submit/ios/).
80
+
81
+ ### Invocation
82
+
83
+ ```bash
84
+ eas submit --platform ios --profile production --non-interactive
85
+ ```
86
+
87
+ `--non-interactive` is required for agent-driven runs; it fails fast instead of prompting for missing credentials. Exit code 0 on successful upload (NOT on processing completion — see polling below).
88
+
89
+ ### Post-Upload Polling
90
+
91
+ `eas submit` returns once the binary is handed off to App Store Connect. Apple's processing takes 10–15 minutes typically. To wait for `VALID` state, query `/v1/builds`:
92
+
93
+ ```
94
+ GET /v1/builds?filter[app]={ascAppId}&filter[preReleaseVersion.version]={version}&sort=-uploadedDate&limit=1
95
+ ```
96
+
97
+ Poll every 30 seconds. The `processingState` attribute transitions `PROCESSING` → `VALID` or `INVALID`. On `INVALID`, fetch the build's `buildBetaDetails` for the rejection reason.
98
+
99
+ ## Common ITMS-90xxx Errors
100
+
101
+ These come back as email from Apple AND on the build record after upload. Capture and surface them in `/cbp-ship` failure output.
102
+
103
+ | Code | Meaning | Remediation |
104
+ |---|---|---|
105
+ | ITMS-90034 | Missing or invalid signature | EAS credentials drift — run `eas credentials` and re-sync. Often caused by an expired distribution certificate. |
106
+ | ITMS-90078 | Missing Push Notification Entitlement | `app.config` declares APNs capability but provisioning profile lacks it. Regenerate profile via `eas credentials`. |
107
+ | ITMS-90161 | Invalid provisioning profile | Profile mismatch with bundle ID or team. Re-fetch via EAS. |
108
+ | ITMS-90164 | Invalid code-signing entitlements | Entitlements in build don't match what the provisioning profile authorises. Common after adding capabilities (HealthKit, Background Modes) without regenerating the profile. |
109
+ | ITMS-90189 | Redundant binary upload | Build number already used for this version. Increment `CFBundleVersion` and re-upload. Apple never lets a build number be reused. |
110
+ | ITMS-90338 | Non-public API usage | A dependency uses a private symbol. Identify via the error's symbol list; usually a stale native module. Update or remove. |
111
+ | ITMS-90683 | Missing Purpose String | Info.plist lacks a `NSXxxUsageDescription` for a permission the app declares. Add the string in `app.config` under `ios.infoPlist`. |
112
+ | ITMS-90685 | CFBundleIdentifier collision | Two frameworks share a bundle ID. Usually a duplicated CocoaPod. Clean derived data and rebuild. |
113
+ | ITMS-90713 | Missing Info.plist value | A required key (e.g. `CFBundleIconName`) is missing. Check `app.config` and `eas build` output. |
114
+ | ITMS-91053 | Missing API declaration | New as of iOS 17 — privacy manifest required for certain "required reason" APIs (e.g. `UserDefaults`, `fileTimestamp`). Add `PrivacyInfo.xcprivacy` to the iOS bundle. |
115
+ | ITMS-91061 | Missing privacy manifest | Same family as 91053 — the manifest file itself is absent. |
116
+ | ITMS-4088 | Beta upload missing build | EAS submit uploaded but ASC didn't register the build. Often a transient ASC issue; retry. See [eas-cli#824](https://github.com/expo/eas-cli/issues/824). |
117
+
118
+ When `/cbp-ship` sees an ITMS-90xxx error, it should:
119
+
120
+ 1. Capture the full error text from the ASC API response or email.
121
+ 2. Match against the table above for a known remediation.
122
+ 3. If unknown, surface to the user verbatim — do not guess.
123
+
124
+ ## Build Expiration Handling
125
+
126
+ The 90-day rule (see [testflight-overview.md](./testflight-overview.md)) means long-running checkpoints risk silent expiry. Detection:
127
+
128
+ ```
129
+ GET /v1/builds/{id}?fields[builds]=expirationDate,expired
130
+ ```
131
+
132
+ `/cbp-ship` should:
133
+
134
+ - On every ship, query the previous build's `expirationDate`. If `< now + 14 days`, log a warning so the user knows a refresh is imminent.
135
+ - On `expired = true`, treat the previous TestFlight as gone — new testers will not be able to install. The next ship is no longer additive; it's a replacement.
136
+
137
+ There is no API to extend expiry. The only remedy is a fresh upload.
138
+
139
+ ## Beta Groups via API
140
+
141
+ Group management is necessary when a checkpoint introduces a new tester segment.
142
+
143
+ ### Create an internal group
144
+
145
+ ```
146
+ POST /v1/betaGroups
147
+ {
148
+ "data": {
149
+ "type": "betaGroups",
150
+ "attributes": {
151
+ "name": "Engineering",
152
+ "isInternalGroup": true,
153
+ "publicLinkEnabled": false
154
+ },
155
+ "relationships": {
156
+ "app": { "data": { "type": "apps", "id": "{ascAppId}" } }
157
+ }
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### Create an external group with public link
163
+
164
+ ```
165
+ POST /v1/betaGroups
166
+ {
167
+ "data": {
168
+ "type": "betaGroups",
169
+ "attributes": {
170
+ "name": "Public Beta",
171
+ "isInternalGroup": false,
172
+ "publicLinkEnabled": true,
173
+ "publicLinkLimitEnabled": true,
174
+ "publicLinkLimit": 1000
175
+ },
176
+ "relationships": {
177
+ "app": { "data": { "type": "apps", "id": "{ascAppId}" } }
178
+ }
179
+ }
180
+ }
181
+ ```
182
+
183
+ `publicLink` is returned in the response — that's the URL to share.
184
+
185
+ ### Assign a build to a group
186
+
187
+ ```
188
+ POST /v1/betaGroups/{groupId}/relationships/builds
189
+ { "data": [{ "type": "builds", "id": "{buildId}" }] }
190
+ ```
191
+
192
+ For external groups, the build must be `VALID` AND have an approved Beta App Review submission before assignment results in distribution.
193
+
194
+ ### Submit for Beta App Review
195
+
196
+ ```
197
+ POST /v1/betaAppReviewSubmissions
198
+ {
199
+ "data": {
200
+ "type": "betaAppReviewSubmissions",
201
+ "relationships": {
202
+ "build": { "data": { "type": "builds", "id": "{buildId}" } }
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ Poll `/v1/betaAppReviewSubmissions/{id}` for `betaReviewState`: `WAITING_FOR_REVIEW` → `IN_REVIEW` → `APPROVED` / `REJECTED`. On rejection, fetch the rejection reason from the related `betaAppReviewDetails`.
209
+
210
+ ## References
211
+
212
+ - [App Store Connect API — Get started](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api/)
213
+ - [EAS Submit — Submit to the Apple App Store](https://docs.expo.dev/submit/ios/)
214
+ - [EAS Submit — Introduction](https://docs.expo.dev/submit/introduction/)
215
+ - [TestFlight overview](https://developer.apple.com/help/app-store-connect/test-a-beta-version/testflight-overview/)