codebyplan 1.5.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist/cli.js +4462 -748
  2. package/package.json +5 -1
  3. package/templates/.gitkeep +0 -0
  4. package/templates/README.md +20 -0
  5. package/templates/agents/cbp-cc-executor.md +213 -0
  6. package/templates/agents/cbp-database-agent.md +229 -0
  7. package/templates/agents/cbp-improve-claude.md +245 -0
  8. package/templates/agents/cbp-improve-round.md +284 -0
  9. package/templates/agents/cbp-mechanical-edits.md +111 -0
  10. package/templates/agents/cbp-research.md +282 -0
  11. package/templates/agents/cbp-round-executor.md +604 -0
  12. package/templates/agents/cbp-security-agent.md +134 -0
  13. package/templates/agents/cbp-task-check.md +213 -0
  14. package/templates/agents/cbp-task-planner.md +582 -0
  15. package/templates/agents/cbp-test-e2e-agent.md +363 -0
  16. package/templates/agents/cbp-testing-qa-agent.md +400 -0
  17. package/templates/context/mcp-docs.md +139 -0
  18. package/templates/hooks/README.md +236 -0
  19. package/templates/hooks/cbp-auto-test-hooks.sh +44 -0
  20. package/templates/hooks/cbp-lint-format-on-edit.sh +159 -0
  21. package/templates/hooks/cbp-maestro-yaml-validate.sh +100 -0
  22. package/templates/hooks/cbp-mcp-migration-guard.sh +32 -0
  23. package/templates/hooks/cbp-mcp-round-sync.sh +79 -0
  24. package/templates/hooks/cbp-mcp-worktree-inject.sh +76 -0
  25. package/templates/hooks/cbp-notify.sh +68 -0
  26. package/templates/hooks/cbp-plugin-dispatch.sh +29 -0
  27. package/templates/hooks/cbp-pre-commit-quality-gate.sh +204 -0
  28. package/templates/hooks/cbp-statusline.sh +347 -0
  29. package/templates/hooks/cbp-subagent-statusline.sh +182 -0
  30. package/templates/hooks/cbp-test-coverage-gate.sh +144 -0
  31. package/templates/hooks/cbp-test-hooks.sh +320 -0
  32. package/templates/hooks/hooks.json +85 -0
  33. package/templates/hooks/validate-context-usage.sh +59 -0
  34. package/templates/hooks/validate-git-commit.sh +78 -0
  35. package/templates/hooks/validate-git-stash-deny.sh +32 -0
  36. package/templates/hooks/validate-structure-lengths.sh +57 -0
  37. package/templates/hooks/validate-structure-lib.sh +104 -0
  38. package/templates/hooks/validate-structure-patterns.sh +54 -0
  39. package/templates/hooks/validate-structure-scope.sh +33 -0
  40. package/templates/hooks/validate-structure-smoke.sh +95 -0
  41. package/templates/hooks/validate-structure-templates.sh +34 -0
  42. package/templates/hooks/validate-structure.sh +69 -0
  43. package/templates/rules/.gitkeep +0 -0
  44. package/templates/rules/README.md +47 -0
  45. package/templates/rules/context-file-loading.md +52 -0
  46. package/templates/rules/scope-vocabulary.md +64 -0
  47. package/templates/rules/todo-backend.md +109 -0
  48. package/templates/settings.project.base.json +55 -0
  49. package/templates/settings.user.base.json +25 -0
  50. package/templates/skills/cbp-build-cc-agent/SKILL.md +139 -0
  51. package/templates/skills/cbp-build-cc-agent/examples/read-only-reviewer.md +32 -0
  52. package/templates/skills/cbp-build-cc-agent/examples/with-hooks.md +41 -0
  53. package/templates/skills/cbp-build-cc-agent/examples/with-skills-preload.md +25 -0
  54. package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +153 -0
  55. package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +37 -0
  56. package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +18 -0
  57. package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +67 -0
  58. package/templates/skills/cbp-build-cc-agent/templates/agent.md +66 -0
  59. package/templates/skills/cbp-build-cc-claude-file/SKILL.md +178 -0
  60. package/templates/skills/cbp-build-cc-claude-file/examples/minimal-project.md +33 -0
  61. package/templates/skills/cbp-build-cc-claude-file/examples/monorepo-with-imports.md +39 -0
  62. package/templates/skills/cbp-build-cc-claude-file/reference/imports.md +72 -0
  63. package/templates/skills/cbp-build-cc-claude-file/reference/what-belongs.md +39 -0
  64. package/templates/skills/cbp-build-cc-claude-file/templates/project-claude-md.md +48 -0
  65. package/templates/skills/cbp-build-cc-claude-file/templates/user-claude-md.md +22 -0
  66. package/templates/skills/cbp-build-cc-memory/SKILL.md +201 -0
  67. package/templates/skills/cbp-build-cc-memory/examples/feedback-memory.md +11 -0
  68. package/templates/skills/cbp-build-cc-memory/examples/project-memory.md +11 -0
  69. package/templates/skills/cbp-build-cc-memory/examples/reference-memory.md +13 -0
  70. package/templates/skills/cbp-build-cc-memory/examples/user-memory.md +14 -0
  71. package/templates/skills/cbp-build-cc-memory/reference/memory-types.md +59 -0
  72. package/templates/skills/cbp-build-cc-memory/reference/when-to-save.md +62 -0
  73. package/templates/skills/cbp-build-cc-memory/templates/MEMORY-index.md +4 -0
  74. package/templates/skills/cbp-build-cc-memory/templates/memory-entry.md +15 -0
  75. package/templates/skills/cbp-build-cc-mode/SKILL.md +99 -0
  76. package/templates/skills/cbp-build-cc-rule/SKILL.md +176 -0
  77. package/templates/skills/cbp-build-cc-rule/examples/global-rule.md +19 -0
  78. package/templates/skills/cbp-build-cc-rule/examples/scoped-rule.md +41 -0
  79. package/templates/skills/cbp-build-cc-rule/reference/paths-patterns.md +48 -0
  80. package/templates/skills/cbp-build-cc-rule/templates/rule.md +32 -0
  81. package/templates/skills/cbp-build-cc-settings/SKILL.md +220 -0
  82. package/templates/skills/cbp-build-cc-settings/examples/hooks-config.json +64 -0
  83. package/templates/skills/cbp-build-cc-settings/examples/permissions-config.json +34 -0
  84. package/templates/skills/cbp-build-cc-settings/examples/sandbox-config.json +42 -0
  85. package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +104 -0
  86. package/templates/skills/cbp-build-cc-settings/reference/permission-rules.md +61 -0
  87. package/templates/skills/cbp-build-cc-settings/reference/scope-precedence.md +73 -0
  88. package/templates/skills/cbp-build-cc-settings/reference/settings-fields.md +166 -0
  89. package/templates/skills/cbp-build-cc-settings/templates/settings.json +23 -0
  90. package/templates/skills/cbp-build-cc-settings/templates/settings.local.json +10 -0
  91. package/templates/skills/cbp-build-cc-skill/SKILL.md +154 -0
  92. package/templates/skills/cbp-build-cc-skill/examples/dynamic-context.md +31 -0
  93. package/templates/skills/cbp-build-cc-skill/examples/fork-skill.md +22 -0
  94. package/templates/skills/cbp-build-cc-skill/examples/knowledge-skill.md +25 -0
  95. package/templates/skills/cbp-build-cc-skill/examples/task-skill.md +29 -0
  96. package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +157 -0
  97. package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +35 -0
  98. package/templates/skills/cbp-build-cc-skill/reference/string-substitutions.md +60 -0
  99. package/templates/skills/cbp-build-cc-skill/scripts/validate-skill.sh +90 -0
  100. package/templates/skills/cbp-build-cc-skill/templates/skill.md +51 -0
  101. package/templates/skills/cbp-checkpoint-check/SKILL.md +156 -0
  102. package/templates/skills/cbp-checkpoint-complete/SKILL.md +109 -0
  103. package/templates/skills/cbp-checkpoint-create/SKILL.md +287 -0
  104. package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
  105. package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
  106. package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
  107. package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
  108. package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
  109. package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
  110. package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
  111. package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
  112. package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
  113. package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
  114. package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
  115. package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
  116. package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
  117. package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
  118. package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
  119. package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
  120. package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
  121. package/templates/skills/cbp-git-commit/SKILL.md +278 -0
  122. package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
  123. package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
  124. package/templates/skills/cbp-merge-main/SKILL.md +228 -0
  125. package/templates/skills/cbp-round-check/SKILL.md +104 -0
  126. package/templates/skills/cbp-round-end/SKILL.md +183 -0
  127. package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
  128. package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
  129. package/templates/skills/cbp-round-execute/SKILL.md +211 -0
  130. package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
  131. package/templates/skills/cbp-round-input/SKILL.md +165 -0
  132. package/templates/skills/cbp-round-start/SKILL.md +222 -0
  133. package/templates/skills/cbp-round-update/SKILL.md +163 -0
  134. package/templates/skills/cbp-session-end/SKILL.md +187 -0
  135. package/templates/skills/cbp-session-start/SKILL.md +155 -0
  136. package/templates/skills/cbp-ship/SKILL.md +332 -0
  137. package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
  138. package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
  139. package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
  140. package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
  141. package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
  142. package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
  143. package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
  144. package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
  145. package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
  146. package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
  147. package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
  148. package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
  149. package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
  150. package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
  151. package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
  152. package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
  153. package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
  154. package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
  155. package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
  156. package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
  157. package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
  158. package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
  159. package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
  160. package/templates/skills/cbp-ship/reference/versioning.md +116 -0
  161. package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
  162. package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
  163. package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
  164. package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
  165. package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
  166. package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
  167. package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
  168. package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
  169. package/templates/skills/cbp-ship/templates/eas.json +66 -0
  170. package/templates/skills/cbp-ship/templates/railway.toml +15 -0
  171. package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
  172. package/templates/skills/cbp-ship/templates/vercel.json +19 -0
  173. package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
  174. package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
  175. package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
  176. package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
  177. package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
  178. package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
  179. package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
  180. package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
  181. package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
  182. package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
  183. package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
  184. package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
  185. package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
  186. package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
  187. package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
  188. package/templates/skills/cbp-ship-main/SKILL.md +65 -0
  189. package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
  190. package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
  191. package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
  192. package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
  193. package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
  194. package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
  195. package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
  196. package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
  197. package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
  198. package/templates/skills/cbp-task-check/SKILL.md +166 -0
  199. package/templates/skills/cbp-task-complete/SKILL.md +206 -0
  200. package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
  201. package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
  202. package/templates/skills/cbp-task-create/SKILL.md +167 -0
  203. package/templates/skills/cbp-task-start/SKILL.md +239 -0
  204. package/templates/skills/cbp-task-testing/SKILL.md +277 -0
  205. package/templates/skills/cbp-todo/SKILL.md +97 -0
@@ -0,0 +1,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
@@ -0,0 +1,116 @@
1
+ # Versioning Rules
2
+
3
+ How `/cbp-ship` decides which version a surface ships at.
4
+
5
+ ## Three modes (per surface, configured per-package)
6
+
7
+ | Mode | Trigger | Best for |
8
+ |---|---|---|
9
+ | `manual` | User bumps `package.json` / `tauri.conf.json` / `app.json` version themselves before checkpoint shipment | Apps with infrequent releases; small teams |
10
+ | `release-please` | GH Actions opens version-bump PRs based on conventional commit messages on the watched branch; merge the PR before shipment | npm packages with frequent releases; multi-contributor repos |
11
+ | `changesets` | Devs add `.changeset/*.md` entries in feature branches; an aggregator PR collects them | Monorepos with many independent packages |
12
+
13
+ Mode is set per-surface in `.codebyplan/shipment.json`:
14
+
15
+ ```json
16
+ {
17
+ "surfaces": {
18
+ "npm-package": {
19
+ "packages/codebyplan-package": { "versioning": "release-please" }
20
+ },
21
+ "tauri-desktop": { "versioning": "manual" },
22
+ "expo-mobile": { "versioning": "auto-build-number" }
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## Auto-bumping where possible
28
+
29
+ These bumps happen automatically — never user-prompted:
30
+
31
+ | Surface | Field | Bump rule |
32
+ |---|---|---|
33
+ | `expo-mobile` | `expo.ios.buildNumber` | `eas.json` `autoIncrement: "buildNumber"` — handled by EAS |
34
+ | `expo-mobile` | `expo.android.versionCode` | `eas.json` `autoIncrement: "versionCode"` — handled by EAS |
35
+ | `vercel-web` | n/a | Vercel uses commit SHA; no semver bump needed |
36
+
37
+ ## Semver bump rules (manual or release-please modes)
38
+
39
+ When the orchestrator detects a version change is needed but mode is `manual`, it suggests a bump based on commit messages since the last release:
40
+
41
+ | Commit message pattern | Suggested bump |
42
+ |---|---|
43
+ | Has `BREAKING CHANGE:` in footer OR `feat!:`/`fix!:` prefix | major |
44
+ | Any `feat:` prefix | minor |
45
+ | Otherwise | patch |
46
+
47
+ The user can override the suggestion. The orchestrator NEVER auto-bumps without confirmation in `manual` mode.
48
+
49
+ ## release-please conventions
50
+
51
+ Conventional Commits are the trigger:
52
+
53
+ | Prefix | Action |
54
+ |---|---|
55
+ | `feat:` | Minor bump in next release-please PR |
56
+ | `fix:` | Patch bump |
57
+ | `feat!:` / `fix!:` / `BREAKING CHANGE:` | Major bump |
58
+ | `chore:`, `docs:`, `refactor:`, `test:`, `style:`, `perf:`, `ci:` | No bump |
59
+
60
+ The release-please workflow lives at `.github/workflows/release-please.yml` (scaffolded by `/cbp-ship-configure`).
61
+
62
+ ## changesets conventions
63
+
64
+ A changeset is a markdown file describing the change:
65
+
66
+ ```markdown
67
+ ---
68
+ "@scope/pkg-a": minor
69
+ "@scope/pkg-b": patch
70
+ ---
71
+
72
+ Add new export `foo` and fix bug in `bar`.
73
+ ```
74
+
75
+ Changesets are committed alongside feature work. The aggregator PR (`changeset-release/main`) collects pending changesets and bumps versions when merged.
76
+
77
+ `/cbp-ship-configure` scaffolds `.changeset/config.json` and the GH Actions workflow.
78
+
79
+ ## Pre-release versions
80
+
81
+ When the version contains a pre-release identifier (`-alpha.N`, `-beta.N`, `-rc.N`), the surface deploy uses the appropriate dist-tag:
82
+
83
+ | Surface | Pre-release handling |
84
+ |---|---|
85
+ | `npm-package` | `npm publish --tag <pre-id>` (never promotes to `latest`) |
86
+ | `vscode-ext` | `vsce publish --pre-release` |
87
+ | `tauri-desktop` | Tag pushed normally; users opt into pre-release via Tauri config |
88
+ | `expo-mobile` | TestFlight internal/external groups handle pre-release semantics naturally |
89
+ | `vercel-web` | Use Vercel preview branches for pre-release URLs |
90
+
91
+ ## Coordinated bumps in monorepos
92
+
93
+ When multiple surfaces ship together, bumps may need to coordinate:
94
+
95
+ - **Tauri desktop bump → updater manifest** — desktop bump triggers a `latest.json` update; users get prompted at next launch. Only push tauri tag at planned release boundaries.
96
+ - **npm package bump → consumer apps' `pnpm.overrides`** — if a sibling package consumes the bumped one, decide if you want apps pinned to the prior version (no override change) or rolled forward (override updated).
97
+ - **Mobile build number auto-increment** — EAS bumps independently; the user-facing `expo.version` (semver) is bumped only when the user explicitly changes it.
98
+
99
+ ## What NOT to do
100
+
101
+ - Don't manually edit `package.json` version inside `/cbp-ship` — versioning happens before shipment, not during
102
+ - Don't auto-bump in `manual` mode without confirmation — the user owns the bump
103
+ - Don't squash changeset commits — release-please / changesets need each conventional commit visible
104
+ - Don't tag a Tauri release before the version commit is on PRODUCTION branch — the tag locks the shipped version
105
+
106
+ ## Decision: which mode to pick when configuring a new surface
107
+
108
+ | Question | If yes |
109
+ |---|---|
110
+ | Is this an npm package with multiple authors? | release-please |
111
+ | Is this a monorepo with 3+ published packages? | changesets |
112
+ | Is this a single app with infrequent releases? | manual |
113
+ | Is this a mobile app? | manual for semver + auto for build numbers |
114
+ | Is this Tauri desktop? | manual (tags drive the release) |
115
+ | Is this VS Code extension? | manual |
116
+ | Is this Vercel web? | n/a — Vercel uses SHA not semver |
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env bash
2
+ # Detect shipment surfaces in the current repo.
3
+ # Emits JSON to stdout: { "surfaces": [ { id, instance, configured, reason } ] }
4
+ # Usage: detect-surfaces.sh [--filter=<id>]
5
+
6
+ set -euo pipefail
7
+
8
+ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
9
+ cd "$REPO_ROOT"
10
+
11
+ FILTER=""
12
+ for arg in "$@"; do
13
+ case "$arg" in
14
+ --filter=*) FILTER="${arg#--filter=}" ;;
15
+ esac
16
+ done
17
+
18
+ # Load shipment config from .codebyplan/shipment.json (if present)
19
+ DISABLED_JSON="[]"
20
+ SURFACES_JSON="{}"
21
+ if [ -f .codebyplan/shipment.json ]; then
22
+ DISABLED_JSON=$(jq -r '.disabled // [] | tostring' .codebyplan/shipment.json 2>/dev/null || echo "[]")
23
+ SURFACES_JSON=$(jq -r '.surfaces // {} | tostring' .codebyplan/shipment.json 2>/dev/null || echo "{}")
24
+ fi
25
+
26
+ is_disabled() {
27
+ local id="$1"
28
+ echo "$DISABLED_JSON" | jq -e --arg id "$id" 'index($id)' >/dev/null 2>&1
29
+ }
30
+
31
+ emit() {
32
+ # Emit one JSON line per surface; we'll wrap in array at end
33
+ local id="$1" instance="$2" configured="$3" reason="$4"
34
+ if [ -n "$FILTER" ] && [ "$FILTER" != "$id" ]; then
35
+ return
36
+ fi
37
+ if is_disabled "$id"; then
38
+ return
39
+ fi
40
+ jq -nc \
41
+ --arg id "$id" --arg instance "$instance" \
42
+ --argjson configured "$configured" --arg reason "$reason" \
43
+ '{id: $id, instance: $instance, configured: $configured, reason: $reason}'
44
+ }
45
+
46
+ # ============ vercel-web ============
47
+ detect_vercel() {
48
+ # Look at every package.json and decide if it's a web app
49
+ while IFS= read -r pkg; do
50
+ local app_path
51
+ app_path=$(dirname "$pkg")
52
+ [ "$app_path" = "." ] && continue
53
+ [[ "$app_path" =~ /node_modules/ ]] && continue
54
+
55
+ # Has Next or is explicitly Vercel-flagged?
56
+ local has_next has_vercel_json has_vercel_link
57
+ has_next=$(jq -e '.dependencies.next // .devDependencies.next' "$pkg" >/dev/null 2>&1 && echo y || echo n)
58
+ has_vercel_json=$([ -f "$app_path/vercel.json" ] && echo y || echo n)
59
+ has_vercel_link=$([ -f "$app_path/.vercel/project.json" ] && echo y || echo n)
60
+
61
+ if [ "$has_next" = "y" ] || [ "$has_vercel_json" = "y" ] || [ "$has_vercel_link" = "y" ]; then
62
+ if [ "$has_vercel_link" = "y" ]; then
63
+ emit "vercel-web" "$app_path" true "vercel.json + .vercel/project.json present"
64
+ else
65
+ emit "vercel-web" "$app_path" false "Next.js detected but .vercel/project.json missing — run /cbp-ship-configure"
66
+ fi
67
+ fi
68
+ done < <(find apps -maxdepth 3 -name package.json 2>/dev/null; [ -f package.json ] && echo "./package.json")
69
+ }
70
+
71
+ # ============ expo-mobile ============
72
+ detect_expo() {
73
+ while IFS= read -r app; do
74
+ local app_path
75
+ app_path=$(dirname "$app")
76
+ [[ "$app_path" =~ /node_modules/ ]] && continue
77
+
78
+ local has_eas
79
+ has_eas=$([ -f "$app_path/eas.json" ] && echo y || echo n)
80
+
81
+ if [ "$has_eas" = "y" ]; then
82
+ emit "expo-mobile" "$app_path" true "app.json + eas.json present"
83
+ else
84
+ emit "expo-mobile" "$app_path" false "app.json present but eas.json missing — run /cbp-ship-configure"
85
+ fi
86
+ done < <(find . -maxdepth 4 -name "app.json" -not -path "*/node_modules/*" 2>/dev/null \
87
+ | xargs -I{} sh -c 'jq -e ".expo" "{}" >/dev/null 2>&1 && echo "{}"' 2>/dev/null)
88
+ }
89
+
90
+ # ============ tauri-desktop ============
91
+ detect_tauri() {
92
+ while IFS= read -r conf; do
93
+ local app_path
94
+ app_path=$(dirname "$(dirname "$conf")")
95
+ [[ "$app_path" =~ /node_modules/ ]] && continue
96
+
97
+ local has_workflow
98
+ has_workflow=$(find .github/workflows -maxdepth 1 -name "*.yml" 2>/dev/null \
99
+ | xargs grep -l "tauri-action" 2>/dev/null | head -1)
100
+
101
+ if [ -n "$has_workflow" ]; then
102
+ emit "tauri-desktop" "$app_path" true "src-tauri/ + tauri-action workflow present"
103
+ else
104
+ emit "tauri-desktop" "$app_path" false "src-tauri/ present but no release workflow — run /cbp-ship-configure"
105
+ fi
106
+ done < <(find . -maxdepth 4 -name "tauri.conf.json" -not -path "*/node_modules/*" -not -path "*/target/*" 2>/dev/null)
107
+ }
108
+
109
+ # ============ npm-package ============
110
+ detect_npm() {
111
+ while IFS= read -r pkg; do
112
+ local pkg_path
113
+ pkg_path=$(dirname "$pkg")
114
+ [ "$pkg_path" = "." ] && continue
115
+ [[ "$pkg_path" =~ /node_modules/ ]] && continue
116
+ [[ "$pkg_path" =~ ^./apps/ ]] && continue # apps don't publish
117
+
118
+ local is_private name
119
+ is_private=$(jq -r '.private // false' "$pkg")
120
+ name=$(jq -r '.name // ""' "$pkg")
121
+
122
+ [ "$is_private" = "true" ] && continue
123
+ [ -z "$name" ] && continue
124
+
125
+ emit "npm-package" "$pkg_path" true "publishable package: $name"
126
+ done < <(find packages -maxdepth 2 -name package.json 2>/dev/null)
127
+ }
128
+
129
+ # ============ vscode-ext ============
130
+ detect_vscode() {
131
+ while IFS= read -r pkg; do
132
+ local pkg_path
133
+ pkg_path=$(dirname "$pkg")
134
+ [[ "$pkg_path" =~ /node_modules/ ]] && continue
135
+
136
+ local has_engine has_publisher
137
+ has_engine=$(jq -e '.engines.vscode' "$pkg" >/dev/null 2>&1 && echo y || echo n)
138
+ has_publisher=$(jq -e '.publisher' "$pkg" >/dev/null 2>&1 && echo y || echo n)
139
+
140
+ if [ "$has_engine" = "y" ] && [ "$has_publisher" = "y" ]; then
141
+ emit "vscode-ext" "$pkg_path" true "engines.vscode + publisher set"
142
+ elif [ "$has_engine" = "y" ]; then
143
+ emit "vscode-ext" "$pkg_path" false "engines.vscode set but publisher missing — run /cbp-ship-configure"
144
+ fi
145
+ done < <(find apps packages -maxdepth 3 -name package.json 2>/dev/null)
146
+ }
147
+
148
+ # ============ railway-backend ============
149
+ detect_railway() {
150
+ # Direct signal: railway config
151
+ while IFS= read -r conf; do
152
+ local app_path
153
+ app_path=$(dirname "$conf")
154
+ [[ "$app_path" =~ /node_modules/ ]] && continue
155
+ emit "railway-backend" "$app_path" true "railway.toml/json present"
156
+ done < <(find . -maxdepth 4 \( -name "railway.toml" -o -name "railway.json" \) -not -path "*/node_modules/*" 2>/dev/null)
157
+
158
+ # Indirect: Dockerfile + non-frontend package.json
159
+ while IFS= read -r dockerfile; do
160
+ local app_path
161
+ app_path=$(dirname "$dockerfile")
162
+ [[ "$app_path" =~ /node_modules/ ]] && continue
163
+ [ -f "$app_path/railway.toml" ] || [ -f "$app_path/railway.json" ] && continue # already emitted above
164
+
165
+ local pkg="$app_path/package.json"
166
+ [ -f "$pkg" ] || continue
167
+ local is_frontend
168
+ is_frontend=$(jq -e '.dependencies.next // .dependencies."react-native" // .dependencies.expo' "$pkg" >/dev/null 2>&1 && echo y || echo n)
169
+ [ "$is_frontend" = "y" ] && continue
170
+
171
+ emit "railway-backend" "$app_path" false "Dockerfile present but Railway not linked — run /cbp-ship-configure"
172
+ done < <(find . -maxdepth 3 -name "Dockerfile" -not -path "*/node_modules/*" 2>/dev/null)
173
+ }
174
+
175
+ # ============ supabase ============
176
+ detect_supabase() {
177
+ if [ -d supabase/migrations ] || find apps -maxdepth 3 -type d -name "supabase" 2>/dev/null | head -1 | grep -q .; then
178
+ local migration_dir
179
+ if [ -d supabase/migrations ]; then
180
+ migration_dir="supabase/migrations"
181
+ else
182
+ migration_dir=$(find apps -maxdepth 3 -type d -path "*/supabase/migrations" | head -1)
183
+ fi
184
+
185
+ [ -z "$migration_dir" ] && return
186
+
187
+ # Pending count = files newer than last_shipped_migration_version
188
+ local last_version pending_count
189
+ last_version=$(echo "$SURFACES_JSON" | jq -r '."supabase".last_shipped_migration_version // ""' 2>/dev/null)
190
+ if [ -n "$last_version" ]; then
191
+ pending_count=$(find "$migration_dir" -name "*.sql" -newer "$migration_dir/$last_version" 2>/dev/null | wc -l | tr -d ' ')
192
+ else
193
+ pending_count=$(find "$migration_dir" -name "*.sql" 2>/dev/null | wc -l | tr -d ' ')
194
+ fi
195
+
196
+ if [ "$pending_count" -gt 0 ]; then
197
+ local has_link
198
+ has_link=$([ -f supabase/.temp/project-ref ] && echo y || echo n)
199
+ if [ "$has_link" = "y" ]; then
200
+ emit "supabase" "$migration_dir" true "$pending_count migrations pending"
201
+ else
202
+ emit "supabase" "$migration_dir" false "$pending_count migrations but project not linked — run /cbp-ship-configure"
203
+ fi
204
+ fi
205
+ fi
206
+ }
207
+
208
+ # ============ Run all detectors and wrap in array ============
209
+ {
210
+ detect_vercel
211
+ detect_expo
212
+ detect_tauri
213
+ detect_npm
214
+ detect_vscode
215
+ detect_railway
216
+ detect_supabase
217
+ } | jq -s '{surfaces: .}'
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # Verify an EAS build + submission landed.
3
+ # Usage: verify-expo-eas.sh <build-id> [<submission-platform>]
4
+ # Exit: 0 ok, 1 fail, 2 verification_pending
5
+
6
+ set -euo pipefail
7
+ BUILD_ID="$1"
8
+ PLATFORM="${2:-ios}"
9
+
10
+ STATUS=$(eas build:view "$BUILD_ID" --json 2>/dev/null | jq -r '.status // "UNKNOWN"')
11
+
12
+ case "$STATUS" in
13
+ FINISHED)
14
+ SUBMISSION=$(eas submit:list --status finished --json 2>/dev/null \
15
+ | jq -r --arg b "$BUILD_ID" '.[] | select(.buildId == $b) | .status' | head -1)
16
+ if [ "$SUBMISSION" = "finished" ]; then
17
+ echo "OK build=$BUILD_ID submission=finished"
18
+ exit 0
19
+ fi
20
+ echo "PENDING: build done; submission=$SUBMISSION (TestFlight/Play processing)"
21
+ exit 2
22
+ ;;
23
+ IN_QUEUE|IN_PROGRESS)
24
+ echo "PENDING: $STATUS"
25
+ exit 2
26
+ ;;
27
+ ERRORED|CANCELED)
28
+ echo "FAIL: build $STATUS"
29
+ exit 1
30
+ ;;
31
+ *)
32
+ echo "PENDING: unknown state $STATUS"
33
+ exit 2
34
+ ;;
35
+ esac
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bash
2
+ # Verify an npm package version is published.
3
+ # Usage: verify-npm.sh <package-name> <version>
4
+ # Exit: 0 ok, 1 fail
5
+
6
+ set -euo pipefail
7
+ PKG="$1"
8
+ VERSION="$2"
9
+
10
+ RESOLVED=$(npm view "$PKG@$VERSION" version 2>/dev/null || echo "")
11
+ [ "$RESOLVED" = "$VERSION" ] || { echo "FAIL: $PKG@$VERSION not on registry"; exit 1; }
12
+
13
+ # Provenance attestation (bonus check)
14
+ ATT=$(npm view "$PKG@$VERSION" --json 2>/dev/null | jq -r '.dist.attestations // empty')
15
+ [ -n "$ATT" ] && PROV="yes" || PROV="no"
16
+
17
+ # Tarball SRI
18
+ SRI=$(npm view "$PKG@$VERSION" dist.integrity 2>/dev/null || echo "")
19
+ [ -n "$SRI" ] || echo "WARN: no integrity hash"
20
+
21
+ echo "OK $PKG@$VERSION provenance=$PROV"
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ # Verify a Railway deployment is SUCCESS and the service responds.
3
+ # Usage: verify-railway.sh <deployment-id> [<url>]
4
+ # Exit: 0 ok, 1 fail, 2 pending
5
+
6
+ set -euo pipefail
7
+ DEPLOY_ID="$1"
8
+ URL="${2:-}"
9
+
10
+ STATUS=$(railway deployment status "$DEPLOY_ID" --json 2>/dev/null | jq -r '.status // "UNKNOWN"')
11
+
12
+ case "$STATUS" in
13
+ SUCCESS|DEPLOYED)
14
+ if [ -n "$URL" ]; then
15
+ HEALTH=$(curl -sIm 10 "https://$URL/health" 2>/dev/null | head -1 | awk '{print $2}' || echo "")
16
+ if [ "$HEALTH" = "200" ]; then
17
+ echo "OK deploy=$DEPLOY_ID health=200"
18
+ exit 0
19
+ fi
20
+ ROOT=$(curl -sIm 10 "https://$URL" 2>/dev/null | head -1 | awk '{print $2}' || echo "")
21
+ case "$ROOT" in
22
+ 2*|3*) echo "OK deploy=$DEPLOY_ID root=$ROOT (no /health endpoint)"; exit 0 ;;
23
+ *) echo "FAIL: deploy succeeded but service unreachable ($ROOT)"; exit 1 ;;
24
+ esac
25
+ fi
26
+ echo "OK deploy=$DEPLOY_ID (no URL to probe)"
27
+ exit 0
28
+ ;;
29
+ BUILDING|DEPLOYING|QUEUED|WAITING)
30
+ echo "PENDING: $STATUS"
31
+ exit 2
32
+ ;;
33
+ FAILED|CRASHED)
34
+ echo "FAIL: deploy $STATUS"
35
+ exit 1
36
+ ;;
37
+ *)
38
+ echo "PENDING: unknown state $STATUS"
39
+ exit 2
40
+ ;;
41
+ esac
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Verify Supabase migrations applied successfully.
3
+ # Usage: verify-supabase.sh <expected-last-migration-version>
4
+ # Exit: 0 ok, 1 fail
5
+
6
+ set -euo pipefail
7
+ EXPECTED="$1"
8
+
9
+ # Migration appears in remote-applied list
10
+ APPLIED=$(supabase migration list --linked 2>/dev/null | grep -F "$EXPECTED" | wc -l | tr -d ' ')
11
+ [ "$APPLIED" -gt 0 ] || { echo "FAIL: migration $EXPECTED not found on remote"; exit 1; }
12
+
13
+ # No drift between local migrations and remote schema
14
+ DRIFT_LINES=$(supabase db diff --use-migra 2>/dev/null | grep -v "^--" | grep -v "^$" | wc -l | tr -d ' ')
15
+ if [ "$DRIFT_LINES" -gt 0 ]; then
16
+ echo "WARN: $DRIFT_LINES lines of drift detected (run: supabase db diff --use-migra)"
17
+ fi
18
+
19
+ echo "OK migration=$EXPECTED drift_lines=$DRIFT_LINES"
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ # Verify a Tauri desktop release was tagged + published with assets.
3
+ # Usage: verify-tauri.sh <version-without-v-prefix>
4
+ # Exit: 0 ok, 1 fail
5
+
6
+ set -euo pipefail
7
+ VERSION="$1"
8
+ TAG="v$VERSION"
9
+
10
+ git fetch origin --tags >/dev/null 2>&1
11
+ git rev-parse "$TAG" >/dev/null 2>&1 || { echo "FAIL: tag $TAG missing locally"; exit 1; }
12
+ git ls-remote --tags origin "$TAG" | grep -q . || { echo "FAIL: tag $TAG not pushed"; exit 1; }
13
+
14
+ PUBLISHED=$(gh release view "$TAG" --json publishedAt -q .publishedAt 2>/dev/null || echo "")
15
+ [ -n "$PUBLISHED" ] || { echo "FAIL: release $TAG not published"; exit 1; }
16
+
17
+ ASSETS=$(gh release view "$TAG" --json assets -q '.assets | length' 2>/dev/null)
18
+ [ "${ASSETS:-0}" -ge 1 ] || { echo "FAIL: release $TAG has no assets"; exit 1; }
19
+
20
+ # Tauri auto-update needs latest.json
21
+ HAS_MANIFEST=$(gh release view "$TAG" --json assets -q '.assets[].name' | grep -c "latest.json" || true)
22
+ [ "$HAS_MANIFEST" -gt 0 ] || echo "WARN: latest.json missing — auto-update may not work"
23
+
24
+ echo "OK tag=$TAG assets=$ASSETS published=$PUBLISHED"