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.
- package/dist/cli.js +4462 -748
- package/package.json +5 -1
- package/templates/.gitkeep +0 -0
- package/templates/README.md +20 -0
- package/templates/agents/cbp-cc-executor.md +213 -0
- package/templates/agents/cbp-database-agent.md +229 -0
- package/templates/agents/cbp-improve-claude.md +245 -0
- package/templates/agents/cbp-improve-round.md +284 -0
- package/templates/agents/cbp-mechanical-edits.md +111 -0
- package/templates/agents/cbp-research.md +282 -0
- package/templates/agents/cbp-round-executor.md +604 -0
- package/templates/agents/cbp-security-agent.md +134 -0
- package/templates/agents/cbp-task-check.md +213 -0
- package/templates/agents/cbp-task-planner.md +582 -0
- package/templates/agents/cbp-test-e2e-agent.md +363 -0
- package/templates/agents/cbp-testing-qa-agent.md +400 -0
- package/templates/context/mcp-docs.md +139 -0
- package/templates/hooks/README.md +236 -0
- package/templates/hooks/cbp-auto-test-hooks.sh +44 -0
- package/templates/hooks/cbp-lint-format-on-edit.sh +159 -0
- package/templates/hooks/cbp-maestro-yaml-validate.sh +100 -0
- package/templates/hooks/cbp-mcp-migration-guard.sh +32 -0
- package/templates/hooks/cbp-mcp-round-sync.sh +79 -0
- package/templates/hooks/cbp-mcp-worktree-inject.sh +76 -0
- package/templates/hooks/cbp-notify.sh +68 -0
- package/templates/hooks/cbp-plugin-dispatch.sh +29 -0
- package/templates/hooks/cbp-pre-commit-quality-gate.sh +204 -0
- package/templates/hooks/cbp-statusline.sh +347 -0
- package/templates/hooks/cbp-subagent-statusline.sh +182 -0
- package/templates/hooks/cbp-test-coverage-gate.sh +144 -0
- package/templates/hooks/cbp-test-hooks.sh +320 -0
- package/templates/hooks/hooks.json +85 -0
- package/templates/hooks/validate-context-usage.sh +59 -0
- package/templates/hooks/validate-git-commit.sh +78 -0
- package/templates/hooks/validate-git-stash-deny.sh +32 -0
- package/templates/hooks/validate-structure-lengths.sh +57 -0
- package/templates/hooks/validate-structure-lib.sh +104 -0
- package/templates/hooks/validate-structure-patterns.sh +54 -0
- package/templates/hooks/validate-structure-scope.sh +33 -0
- package/templates/hooks/validate-structure-smoke.sh +95 -0
- package/templates/hooks/validate-structure-templates.sh +34 -0
- package/templates/hooks/validate-structure.sh +69 -0
- package/templates/rules/.gitkeep +0 -0
- package/templates/rules/README.md +47 -0
- package/templates/rules/context-file-loading.md +52 -0
- package/templates/rules/scope-vocabulary.md +64 -0
- package/templates/rules/todo-backend.md +109 -0
- package/templates/settings.project.base.json +55 -0
- package/templates/settings.user.base.json +25 -0
- package/templates/skills/cbp-build-cc-agent/SKILL.md +139 -0
- package/templates/skills/cbp-build-cc-agent/examples/read-only-reviewer.md +32 -0
- package/templates/skills/cbp-build-cc-agent/examples/with-hooks.md +41 -0
- package/templates/skills/cbp-build-cc-agent/examples/with-skills-preload.md +25 -0
- package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +153 -0
- package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +37 -0
- package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +18 -0
- package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +67 -0
- package/templates/skills/cbp-build-cc-agent/templates/agent.md +66 -0
- package/templates/skills/cbp-build-cc-claude-file/SKILL.md +178 -0
- package/templates/skills/cbp-build-cc-claude-file/examples/minimal-project.md +33 -0
- package/templates/skills/cbp-build-cc-claude-file/examples/monorepo-with-imports.md +39 -0
- package/templates/skills/cbp-build-cc-claude-file/reference/imports.md +72 -0
- package/templates/skills/cbp-build-cc-claude-file/reference/what-belongs.md +39 -0
- package/templates/skills/cbp-build-cc-claude-file/templates/project-claude-md.md +48 -0
- package/templates/skills/cbp-build-cc-claude-file/templates/user-claude-md.md +22 -0
- package/templates/skills/cbp-build-cc-memory/SKILL.md +201 -0
- package/templates/skills/cbp-build-cc-memory/examples/feedback-memory.md +11 -0
- package/templates/skills/cbp-build-cc-memory/examples/project-memory.md +11 -0
- package/templates/skills/cbp-build-cc-memory/examples/reference-memory.md +13 -0
- package/templates/skills/cbp-build-cc-memory/examples/user-memory.md +14 -0
- package/templates/skills/cbp-build-cc-memory/reference/memory-types.md +59 -0
- package/templates/skills/cbp-build-cc-memory/reference/when-to-save.md +62 -0
- package/templates/skills/cbp-build-cc-memory/templates/MEMORY-index.md +4 -0
- package/templates/skills/cbp-build-cc-memory/templates/memory-entry.md +15 -0
- package/templates/skills/cbp-build-cc-mode/SKILL.md +99 -0
- package/templates/skills/cbp-build-cc-rule/SKILL.md +176 -0
- package/templates/skills/cbp-build-cc-rule/examples/global-rule.md +19 -0
- package/templates/skills/cbp-build-cc-rule/examples/scoped-rule.md +41 -0
- package/templates/skills/cbp-build-cc-rule/reference/paths-patterns.md +48 -0
- package/templates/skills/cbp-build-cc-rule/templates/rule.md +32 -0
- package/templates/skills/cbp-build-cc-settings/SKILL.md +220 -0
- package/templates/skills/cbp-build-cc-settings/examples/hooks-config.json +64 -0
- package/templates/skills/cbp-build-cc-settings/examples/permissions-config.json +34 -0
- package/templates/skills/cbp-build-cc-settings/examples/sandbox-config.json +42 -0
- package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +104 -0
- package/templates/skills/cbp-build-cc-settings/reference/permission-rules.md +61 -0
- package/templates/skills/cbp-build-cc-settings/reference/scope-precedence.md +73 -0
- package/templates/skills/cbp-build-cc-settings/reference/settings-fields.md +166 -0
- package/templates/skills/cbp-build-cc-settings/templates/settings.json +23 -0
- package/templates/skills/cbp-build-cc-settings/templates/settings.local.json +10 -0
- package/templates/skills/cbp-build-cc-skill/SKILL.md +154 -0
- package/templates/skills/cbp-build-cc-skill/examples/dynamic-context.md +31 -0
- package/templates/skills/cbp-build-cc-skill/examples/fork-skill.md +22 -0
- package/templates/skills/cbp-build-cc-skill/examples/knowledge-skill.md +25 -0
- package/templates/skills/cbp-build-cc-skill/examples/task-skill.md +29 -0
- package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +157 -0
- package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +35 -0
- package/templates/skills/cbp-build-cc-skill/reference/string-substitutions.md +60 -0
- package/templates/skills/cbp-build-cc-skill/scripts/validate-skill.sh +90 -0
- package/templates/skills/cbp-build-cc-skill/templates/skill.md +51 -0
- package/templates/skills/cbp-checkpoint-check/SKILL.md +156 -0
- package/templates/skills/cbp-checkpoint-complete/SKILL.md +109 -0
- package/templates/skills/cbp-checkpoint-create/SKILL.md +116 -0
- package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
- package/templates/skills/cbp-checkpoint-plan/SKILL.md +137 -0
- package/templates/skills/cbp-checkpoint-plan/reference/alternative-comparison-template.md +54 -0
- package/templates/skills/cbp-checkpoint-plan/reference/dep-decision-rubric.md +50 -0
- package/templates/skills/cbp-checkpoint-plan/reference/e2e-discovery-probe.md +57 -0
- package/templates/skills/cbp-checkpoint-plan/reference/gap-analysis-playbook.md +47 -0
- package/templates/skills/cbp-checkpoint-start/SKILL.md +84 -0
- package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
- package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
- package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
- package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
- package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
- package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
- package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
- package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
- package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
- package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
- package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
- package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
- package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
- package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
- package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
- package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
- package/templates/skills/cbp-git-commit/SKILL.md +278 -0
- package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
- package/templates/skills/cbp-merge-main/SKILL.md +228 -0
- package/templates/skills/cbp-round-check/SKILL.md +104 -0
- package/templates/skills/cbp-round-end/SKILL.md +183 -0
- package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
- package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
- package/templates/skills/cbp-round-execute/SKILL.md +211 -0
- package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
- package/templates/skills/cbp-round-input/SKILL.md +165 -0
- package/templates/skills/cbp-round-start/SKILL.md +222 -0
- package/templates/skills/cbp-round-update/SKILL.md +163 -0
- package/templates/skills/cbp-session-end/SKILL.md +187 -0
- package/templates/skills/cbp-session-start/SKILL.md +155 -0
- package/templates/skills/cbp-ship/SKILL.md +332 -0
- package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
- package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
- package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
- package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
- package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
- package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
- package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
- package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
- package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
- package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
- package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
- package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
- package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
- package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
- package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
- package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
- package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
- package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
- package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
- package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
- package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
- package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
- package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
- package/templates/skills/cbp-ship/reference/versioning.md +116 -0
- package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
- package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
- package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
- package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
- package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
- package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
- package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
- package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
- package/templates/skills/cbp-ship/templates/eas.json +66 -0
- package/templates/skills/cbp-ship/templates/railway.toml +15 -0
- package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
- package/templates/skills/cbp-ship/templates/vercel.json +19 -0
- package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
- package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
- package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
- package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
- package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
- package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
- package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
- package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
- package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
- package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
- package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
- package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
- package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
- package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
- package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
- package/templates/skills/cbp-ship-main/SKILL.md +65 -0
- package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
- package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
- package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
- package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
- package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
- package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
- package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
- package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
- package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
- package/templates/skills/cbp-task-check/SKILL.md +166 -0
- package/templates/skills/cbp-task-complete/SKILL.md +206 -0
- package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
- package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
- package/templates/skills/cbp-task-create/SKILL.md +167 -0
- package/templates/skills/cbp-task-start/SKILL.md +239 -0
- package/templates/skills/cbp-task-testing/SKILL.md +277 -0
- 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
|