codebyplan 1.5.1 → 1.9.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 (211) 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 +116 -0
  104. package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
  105. package/templates/skills/cbp-checkpoint-plan/SKILL.md +137 -0
  106. package/templates/skills/cbp-checkpoint-plan/reference/alternative-comparison-template.md +54 -0
  107. package/templates/skills/cbp-checkpoint-plan/reference/dep-decision-rubric.md +50 -0
  108. package/templates/skills/cbp-checkpoint-plan/reference/e2e-discovery-probe.md +57 -0
  109. package/templates/skills/cbp-checkpoint-plan/reference/gap-analysis-playbook.md +47 -0
  110. package/templates/skills/cbp-checkpoint-start/SKILL.md +84 -0
  111. package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
  112. package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
  113. package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
  114. package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
  115. package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
  116. package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
  117. package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
  118. package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
  119. package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
  120. package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
  121. package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
  122. package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
  123. package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
  124. package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
  125. package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
  126. package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
  127. package/templates/skills/cbp-git-commit/SKILL.md +278 -0
  128. package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
  129. package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
  130. package/templates/skills/cbp-merge-main/SKILL.md +228 -0
  131. package/templates/skills/cbp-round-check/SKILL.md +104 -0
  132. package/templates/skills/cbp-round-end/SKILL.md +183 -0
  133. package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
  134. package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
  135. package/templates/skills/cbp-round-execute/SKILL.md +211 -0
  136. package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
  137. package/templates/skills/cbp-round-input/SKILL.md +165 -0
  138. package/templates/skills/cbp-round-start/SKILL.md +222 -0
  139. package/templates/skills/cbp-round-update/SKILL.md +163 -0
  140. package/templates/skills/cbp-session-end/SKILL.md +187 -0
  141. package/templates/skills/cbp-session-start/SKILL.md +155 -0
  142. package/templates/skills/cbp-ship/SKILL.md +332 -0
  143. package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
  144. package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
  145. package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
  146. package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
  147. package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
  148. package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
  149. package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
  150. package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
  151. package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
  152. package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
  153. package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
  154. package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
  155. package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
  156. package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
  157. package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
  158. package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
  159. package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
  160. package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
  161. package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
  162. package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
  163. package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
  164. package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
  165. package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
  166. package/templates/skills/cbp-ship/reference/versioning.md +116 -0
  167. package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
  168. package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
  169. package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
  170. package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
  171. package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
  172. package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
  173. package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
  174. package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
  175. package/templates/skills/cbp-ship/templates/eas.json +66 -0
  176. package/templates/skills/cbp-ship/templates/railway.toml +15 -0
  177. package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
  178. package/templates/skills/cbp-ship/templates/vercel.json +19 -0
  179. package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
  180. package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
  181. package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
  182. package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
  183. package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
  184. package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
  185. package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
  186. package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
  187. package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
  188. package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
  189. package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
  190. package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
  191. package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
  192. package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
  193. package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
  194. package/templates/skills/cbp-ship-main/SKILL.md +65 -0
  195. package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
  196. package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
  197. package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
  198. package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
  199. package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
  200. package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
  201. package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
  202. package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
  203. package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
  204. package/templates/skills/cbp-task-check/SKILL.md +166 -0
  205. package/templates/skills/cbp-task-complete/SKILL.md +206 -0
  206. package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
  207. package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
  208. package/templates/skills/cbp-task-create/SKILL.md +167 -0
  209. package/templates/skills/cbp-task-start/SKILL.md +239 -0
  210. package/templates/skills/cbp-task-testing/SKILL.md +277 -0
  211. package/templates/skills/cbp-todo/SKILL.md +111 -0
@@ -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/)
@@ -0,0 +1,69 @@
1
+ # TestFlight — Internal vs External Testers
2
+
3
+ When `/cbp-ship` lands a build on TestFlight, the orchestrator must decide which tester population to target. The two paths have different latency, audit, and access semantics. Pick the wrong one and either (a) the right people don't get the build, or (b) you wait 24+ hours on a Beta Review you didn't need.
4
+
5
+ ## The Core Trade-off
6
+
7
+ | Dimension | Internal | External |
8
+ |---|---|---|
9
+ | Tester ceiling | 100 | 10,000 |
10
+ | Identity | Apple ID on your App Store Connect team | Any email address |
11
+ | Beta App Review | **Not required** | **Required** for first build of each version |
12
+ | Time to availability after `VALID` | Minutes (push notification) | 24–48 hours typical (review queue) |
13
+ | Public link | No | Yes (self-enrolment up to group cap) |
14
+ | Tester role required | App Store Connect role: Account Holder, Admin, App Manager, Developer, or Marketing | None (no ASC account needed) |
15
+ | Devices per tester | 30 | 30 |
16
+ | Build expiry | 90 days | 90 days |
17
+ | Auto-distribute on new build | Yes (per-group toggle) | Yes (per-group toggle), but each build still passes through review unless minor-change exemption applies |
18
+ | Crash + feedback collection | Yes | Yes |
19
+ | Cost | Free (within team) | Free |
20
+
21
+ ## Decision Table
22
+
23
+ | Checkpoint scenario | Choose | Why |
24
+ |---|---|---|
25
+ | Engineering smoke-test before any external eyes | Internal | No review wait; team already has ASC accounts |
26
+ | Daily / per-PR builds during active development | Internal | Review queue would block iteration speed |
27
+ | Designer or PM previewing a feature pre-merge | Internal (add them as App Manager / Marketing role) | Avoids review delay for trusted reviewers |
28
+ | Closed beta with a named friends-and-family list (≤100 people, all willing to be added to ASC) | Internal | Faster, simpler, no review |
29
+ | Closed beta with >100 testers OR testers unwilling/unable to join ASC | External | Internal cap exceeded; ASC role not appropriate |
30
+ | Public beta with a shareable signup link | External | Only external supports public links |
31
+ | Marketing-driven preview to press / influencers | External | Don't grant ASC roles to non-employees |
32
+ | Final pre-release rehearsal mirroring real-world install | External | Mirrors the post-launch update mechanic; flushes out review-time issues before App Store submission |
33
+ | Region-restricted or audience-segmented rollouts | External (multiple groups) | Group-scoped distribution is the segmentation primitive |
34
+
35
+ ## Operational Notes
36
+
37
+ ### Internal-only fast-path
38
+
39
+ If a checkpoint declares `testflight.audience: internal`, `/cbp-ship` should:
40
+
41
+ 1. `eas submit --platform ios` and wait for `VALID`.
42
+ 2. Verify the build is auto-assigned to the internal group(s) configured on the app (or assign explicitly via App Store Connect API `POST /v1/betaGroups/{id}/relationships/builds`).
43
+ 3. Stop. Skip the Beta Review submission step entirely. Surface the TestFlight install link to the user.
44
+
45
+ ### External path
46
+
47
+ If `testflight.audience: external`:
48
+
49
+ 1. Run the internal-fast-path first (so the team gets the build immediately).
50
+ 2. Then submit for Beta App Review: `POST /v1/betaAppReviewSubmissions` with the build ID.
51
+ 3. Poll `betaAppReviewSubmissions/{id}` for state transitions (`WAITING_FOR_REVIEW` → `IN_REVIEW` → `APPROVED` / `REJECTED`).
52
+ 4. On approval, the build auto-distributes to assigned external groups (if the group's auto-notify is on).
53
+
54
+ ### Hybrid
55
+
56
+ Most production checkpoints want both: internal testers get the build immediately for smoke-testing, then the same build goes to external review for the broader beta. This is the default unless the checkpoint explicitly opts out.
57
+
58
+ ## Common Mistakes
59
+
60
+ - **Adding non-employees as internal testers.** They get an ASC role, which exposes more than intended. Use external groups instead.
61
+ - **Submitting for Beta Review when only internal distribution is needed.** Wastes 24–48 hours and an Apple review slot.
62
+ - **Assuming "external review approved" persists across versions.** It is per-version (technically per-`CFBundleShortVersionString`). A new marketing version requires a fresh review.
63
+ - **Missing the encryption-export declaration.** External review will reject builds without `ITSAppUsesNonExemptEncryption` resolved. Internal builds are also blocked from distribution until resolved, even though no review is required.
64
+
65
+ ## References
66
+
67
+ - [Add internal testers — App Store Connect Help](https://developer.apple.com/help/app-store-connect/test-a-beta-version/add-internal-testers/)
68
+ - [Invite external testers — App Store Connect Help](https://developer.apple.com/help/app-store-connect/test-a-beta-version/invite-external-testers/)
69
+ - [TestFlight overview — App Store Connect Help](https://developer.apple.com/help/app-store-connect/test-a-beta-version/testflight-overview/)
@@ -0,0 +1,98 @@
1
+ # TestFlight Overview
2
+
3
+ TestFlight is Apple's first-party beta-distribution service for iOS, iPadOS, macOS, tvOS, visionOS, and watchOS apps. It is the **only** sanctioned path for distributing pre-release builds of an Expo / React Native iOS app to testers without going through the public App Store. For the `expo-mobile` surface of `/cbp-ship`, TestFlight is the terminal hop after `eas build --platform ios` produces a signed `.ipa`.
4
+
5
+ ## What TestFlight Is
6
+
7
+ - A subsystem of App Store Connect — every TestFlight build is a real App Store Connect build record, with the same processing pipeline, export-compliance metadata, and version/build-number rules.
8
+ - A separate iOS app (`TestFlight.app`) that testers install from the App Store; they receive a public link or email invitation to redeem.
9
+ - The pre-flight gate for App Store review: the same binary uploaded to TestFlight is the one promoted to production. There is no separate "production-only" upload step — you promote the existing TestFlight build.
10
+
11
+ Reference: [TestFlight overview — App Store Connect Help](https://developer.apple.com/help/app-store-connect/test-a-beta-version/testflight-overview/) and [developer.apple.com/testflight](https://developer.apple.com/testflight/).
12
+
13
+ ## Tester Categories
14
+
15
+ TestFlight has two distinct tester populations with very different rules:
16
+
17
+ ### Internal Testers
18
+
19
+ - Up to **100 internal testers** per app.
20
+ - Must be App Store Connect users on your team with one of these roles: Account Holder, Admin, App Manager, Developer, or Marketing.
21
+ - **No Beta App Review required** — builds are available to internal testers as soon as Apple finishes processing the upload (typically 10–15 minutes).
22
+ - Identified by Apple ID (the address tied to their App Store Connect account).
23
+
24
+ Reference: [Add internal testers](https://developer.apple.com/help/app-store-connect/test-a-beta-version/add-internal-testers/).
25
+
26
+ ### External Testers
27
+
28
+ - Up to **10,000 external testers** per app, organised into groups (max 100 groups, up to 10,000 testers per group, with the 10K cap shared across the app).
29
+ - Anyone with a valid email address — no App Store Connect account required.
30
+ - **First build per version requires Beta App Review** by Apple (typically 24–48 hours, sometimes same-day, occasionally several days).
31
+ - Subsequent builds with the same version may auto-pass review if the changes are minor (Apple's discretion).
32
+ - Public link distribution is supported — anyone with the link can self-enrol up to the group cap.
33
+
34
+ Reference: [Invite external testers](https://developer.apple.com/help/app-store-connect/test-a-beta-version/invite-external-testers/).
35
+
36
+ ## Processing Pipeline
37
+
38
+ After `eas submit --platform ios` (or any upload tool) hands the `.ipa` to App Store Connect:
39
+
40
+ 1. **Upload** — Transporter / `altool` / EAS streams the `.ipa` to App Store Connect. Returns immediately with a build record in `PROCESSING` state.
41
+ 2. **Apple processing** — Apple validates the binary, extracts dSYMs, scans for export-compliance and missing-Info.plist keys, and re-signs as needed. Usually 10–15 minutes; can stretch to an hour during peak windows. State transitions: `PROCESSING` → `VALID` (or `INVALID` with ITMS error).
42
+ 3. **Compliance gate** — Build must declare encryption-export compliance. Set `ITSAppUsesNonExemptEncryption = false` in `Info.plist` (or `app.config` `ios.config.usesNonExemptEncryption`) for the auto-pass on standard HTTPS-only apps; otherwise App Store Connect will block distribution until you answer the encryption questionnaire.
43
+ 4. **Internal availability** — Once `VALID`, the build appears in TestFlight for any internal-tester group it's been assigned to. Manual assignment is required unless auto-distribute is enabled on the group.
44
+ 5. **External submission** — To distribute externally, submit the build for Beta App Review (separate from internal availability). On approval, the build is pushed to assigned external groups.
45
+
46
+ ## Build Expiration — the 90-Day Rule
47
+
48
+ Every TestFlight build expires **90 days after upload**, regardless of tester activity. Expired builds:
49
+
50
+ - Cannot be installed by new testers.
51
+ - Existing installs continue to work but receive no updates.
52
+ - Must be replaced by uploading a fresh build (incremented build number).
53
+
54
+ Implication for `/cbp-ship`: a checkpoint that ships to TestFlight on day 0 will be unavailable to new testers on day 91. If the cadence between TestFlight ship and App Store promotion exceeds 90 days, plan for a refresh build.
55
+
56
+ ## Version and Build Number Rules
57
+
58
+ App Store Connect enforces:
59
+
60
+ - `CFBundleShortVersionString` (marketing version, e.g. `1.4.2`) — must match or exceed the latest version of this app.
61
+ - `CFBundleVersion` (build number, e.g. `42`) — must be **strictly greater** than every previous build for the same `CFBundleShortVersionString`. Once a build number is uploaded, it can never be reused for that version, even if the upload was rejected.
62
+ - EAS auto-increments build numbers when `cli.appVersionSource = remote` in `eas.json` and `autoIncrement = true` in the build profile. Verify before assuming.
63
+
64
+ ## Device Limits per Tester
65
+
66
+ A single tester (Apple ID) can install TestFlight builds on up to **30 devices**. This is per-tester, not per-app. Not to be confused with the 100-device limit on the Apple Developer Program account (which applies to ad-hoc / development provisioning, NOT TestFlight).
67
+
68
+ ## Relationship to App Store Connect API
69
+
70
+ The App Store Connect API exposes TestFlight as a set of REST resources:
71
+
72
+ - `/v1/builds` — list / query builds, including processing state and expiry date.
73
+ - `/v1/betaGroups` — internal and external group CRUD.
74
+ - `/v1/betaTesters` — invite / remove individual testers.
75
+ - `/v1/buildBetaDetails` and `/v1/betaAppReviewSubmissions` — submit a build for external review.
76
+ - `/v1/preReleaseVersions` — query version groupings.
77
+
78
+ Authentication uses an issuer-scoped JWT signed with a `.p8` private key. See [testflight-automation.md](./testflight-automation.md) for the full automation contract.
79
+
80
+ Reference: [App Store Connect API — Get started](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api/).
81
+
82
+ ## Where TestFlight Fits in `/cbp-ship` (expo-mobile)
83
+
84
+ ```
85
+ eas build --platform ios → produces signed .ipa
86
+ eas submit --platform ios → uploads to App Store Connect (TestFlight)
87
+ [10–15 min processing]
88
+ internal group auto-distribute → testers get push notification
89
+ [optional] beta review submission → 24–48h, then external groups receive build
90
+ ```
91
+
92
+ The `/cbp-ship` orchestrator owns the trigger (`eas submit`) and the wait-loop (poll build state until `VALID`). It does NOT own the Beta Review submission decision — that is gated on whether the checkpoint targets external testers, which is metadata on the checkpoint itself.
93
+
94
+ ## See also
95
+
96
+ - [testflight-internal-vs-external.md](./testflight-internal-vs-external.md) — picking the right tester population per checkpoint
97
+ - [testflight-automation.md](./testflight-automation.md) — ASC API key + EAS Submit + ITMS error catalogue + beta groups via API
98
+ - [surface-expo-eas.md](./surface-expo-eas.md) — the `/cbp-ship` deploy steps that consume this content