@moifort/stack 0.0.0-stage → 0.1.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 (263) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +26 -2
  3. package/bin/stack.ts +109 -0
  4. package/hooks/shutdown-simulators-after-push.sh +19 -0
  5. package/package.json +40 -4
  6. package/routines/daily-health.md +78 -0
  7. package/routines/deprecations.md +23 -0
  8. package/routines/sentry-autofix.md +76 -0
  9. package/routines/stack-upgrade.md +45 -0
  10. package/rules/00-collaboration.md +81 -0
  11. package/rules/01-workflow.md +81 -0
  12. package/rules/02-commits.md +71 -0
  13. package/rules/03-code-review.md +25 -0
  14. package/rules/04-push.md +75 -0
  15. package/rules/05-release.md +40 -0
  16. package/rules/06-changelog.md +48 -0
  17. package/rules/07-appstore.md +77 -0
  18. package/rules/08-copy.md +52 -0
  19. package/rules/09-backend.md +132 -0
  20. package/rules/10-ios.md +206 -0
  21. package/rules/11-ops.md +114 -0
  22. package/rules/12-ci.md +32 -0
  23. package/rules/13-ai.md +71 -0
  24. package/rules/14-infra.md +90 -0
  25. package/rules/15-tooling.md +81 -0
  26. package/rules/16-routines.md +69 -0
  27. package/rules/17-landing.md +76 -0
  28. package/skills/appstore/SKILL.md +53 -0
  29. package/skills/domain/SKILL.md +136 -0
  30. package/skills/graphql/SKILL.md +83 -0
  31. package/skills/ios-feature/SKILL.md +94 -0
  32. package/skills/stack-install/SKILL.md +35 -0
  33. package/skills/stack-upgrade/SKILL.md +27 -0
  34. package/src/catalogue.ts +33 -0
  35. package/src/config.ts +20 -0
  36. package/src/init.ts +63 -0
  37. package/src/migrations.ts +72 -0
  38. package/src/migrations.unit.test.ts +44 -0
  39. package/src/package.ts +8 -0
  40. package/src/render.ts +13 -0
  41. package/src/render.unit.test.ts +17 -0
  42. package/src/sync.int.test.ts +103 -0
  43. package/src/sync.ts +161 -0
  44. package/src/templates.ts +121 -0
  45. package/src/validate-content.ts +65 -0
  46. package/templates/backend/.env.example +10 -0
  47. package/templates/backend/.github/workflows/deploy.yml +151 -0
  48. package/templates/backend/.github/workflows/schema.yml +59 -0
  49. package/templates/backend/.github/workflows/test-feat.yml +40 -0
  50. package/templates/backend/.github/workflows/test-int.yml +40 -0
  51. package/templates/backend/.github/workflows/test-unit.yml +64 -0
  52. package/templates/backend/docs/api-evolution.md +67 -0
  53. package/templates/backend/firebase.json +21 -0
  54. package/templates/backend/firestore.indexes.json +4 -0
  55. package/templates/backend/firestore.rules +11 -0
  56. package/templates/backend/infra/auth.tf +54 -0
  57. package/templates/backend/infra/backend.tf +6 -0
  58. package/templates/backend/infra/cicd.tf +63 -0
  59. package/templates/backend/infra/firebase.tf +26 -0
  60. package/templates/backend/infra/firestore.tf +53 -0
  61. package/templates/backend/infra/function.tf +171 -0
  62. package/templates/backend/infra/main.tf +26 -0
  63. package/templates/backend/infra/observer.tf +18 -0
  64. package/templates/backend/infra/outputs.tf +30 -0
  65. package/templates/backend/infra/project.tf +64 -0
  66. package/templates/backend/infra/scheduler.tf +28 -0
  67. package/templates/backend/infra/secrets.tf +44 -0
  68. package/templates/backend/infra/state.tf +13 -0
  69. package/templates/backend/infra/terraform.tfvars.example +16 -0
  70. package/templates/backend/infra/variables.tf +104 -0
  71. package/templates/backend/infra/versions.tf +30 -0
  72. package/templates/backend/nitro.config.ts +40 -0
  73. package/templates/backend/package.json +51 -0
  74. package/templates/backend/scripts/deprecations/inspector-rule.ts +29 -0
  75. package/templates/backend/scripts/deprecations/status.ts +50 -0
  76. package/templates/backend/scripts/deprecations/status.unit.test.ts +57 -0
  77. package/templates/backend/scripts/deprecations/usage.ts +79 -0
  78. package/templates/backend/scripts/deprecations/usage.unit.test.ts +64 -0
  79. package/templates/backend/scripts/deprecations.ts +224 -0
  80. package/templates/backend/scripts/export-graphql-schema.ts +11 -0
  81. package/templates/backend/scripts/generate-changelog-asset.ts +27 -0
  82. package/templates/backend/scripts/install-terraform.sh +61 -0
  83. package/templates/backend/scripts/tf +11 -0
  84. package/templates/backend/server/domain/changelog/business-rules.ts +60 -0
  85. package/templates/backend/server/domain/changelog/business-rules.unit.test.ts +88 -0
  86. package/templates/backend/server/domain/changelog/infrastructure/graphql/queries.ts +17 -0
  87. package/templates/backend/server/domain/changelog/infrastructure/graphql/types.ts +23 -0
  88. package/templates/backend/server/domain/changelog/primitives.ts +19 -0
  89. package/templates/backend/server/domain/changelog/query.ts +13 -0
  90. package/templates/backend/server/domain/changelog/query.unit.test.ts +19 -0
  91. package/templates/backend/server/domain/changelog/types.ts +9 -0
  92. package/templates/backend/server/domain/shared/graphql/builder.ts +52 -0
  93. package/templates/backend/server/domain/shared/graphql/errors.ts +19 -0
  94. package/templates/backend/server/domain/shared/graphql/scalars.ts +51 -0
  95. package/templates/backend/server/domain/shared/graphql/schema.ts +21 -0
  96. package/templates/backend/server/domain/shared/language.ts +26 -0
  97. package/templates/backend/server/domain/shared/page.ts +25 -0
  98. package/templates/backend/server/domain/shared/page.unit.test.ts +51 -0
  99. package/templates/backend/server/domain/shared/primitives.ts +50 -0
  100. package/templates/backend/server/domain/shared/primitives.unit.test.ts +25 -0
  101. package/templates/backend/server/domain/shared/types.ts +14 -0
  102. package/templates/backend/server/domain/user/command.ts +16 -0
  103. package/templates/backend/server/domain/user/infrastructure/graphql/inputs.ts +12 -0
  104. package/templates/backend/server/domain/user/infrastructure/graphql/mutations.ts +40 -0
  105. package/templates/backend/server/domain/user/infrastructure/graphql/queries.ts +13 -0
  106. package/templates/backend/server/domain/user/infrastructure/graphql/types.ts +35 -0
  107. package/templates/backend/server/domain/user/infrastructure/repository.ts +59 -0
  108. package/templates/backend/server/domain/user/mutations.feat.test.ts +42 -0
  109. package/templates/backend/server/domain/user/query.int.test.ts +49 -0
  110. package/templates/backend/server/domain/user/query.ts +34 -0
  111. package/templates/backend/server/domain/user/types.ts +20 -0
  112. package/templates/backend/server/domain/user/use-case.ts +18 -0
  113. package/templates/backend/server/middleware/auth.ts +54 -0
  114. package/templates/backend/server/plugins/00-sentry.ts +74 -0
  115. package/templates/backend/server/plugins/01-request-log.ts +28 -0
  116. package/templates/backend/server/plugins/02-graphql.ts +69 -0
  117. package/templates/backend/server/plugins/03-boot-time.ts +21 -0
  118. package/templates/backend/server/routes/admin/migrate.post.ts +9 -0
  119. package/templates/backend/server/routes/app-config.get.ts +15 -0
  120. package/templates/backend/server/routes/graphql.ts +52 -0
  121. package/templates/backend/server/system/app-support.ts +17 -0
  122. package/templates/backend/server/system/config/index.ts +18 -0
  123. package/templates/backend/server/system/config/primitives.ts +20 -0
  124. package/templates/backend/server/system/config/primitives.unit.test.ts +13 -0
  125. package/templates/backend/server/system/config/types.ts +8 -0
  126. package/templates/backend/server/system/firebase.ts +19 -0
  127. package/templates/backend/server/system/identity.ts +18 -0
  128. package/templates/backend/server/system/logger.ts +119 -0
  129. package/templates/backend/server/system/logger.unit.test.ts +109 -0
  130. package/templates/backend/server/system/migration/migrations/index.ts +6 -0
  131. package/templates/backend/server/system/migration/primitives.ts +18 -0
  132. package/templates/backend/server/system/migration/runner.ts +52 -0
  133. package/templates/backend/server/system/migration/types.ts +16 -0
  134. package/templates/backend/server/system/read-meter.ts +100 -0
  135. package/templates/backend/server/system/read-meter.unit.test.ts +97 -0
  136. package/templates/backend/server/system/request-cache.ts +139 -0
  137. package/templates/backend/server/system/request-cache.unit.test.ts +166 -0
  138. package/templates/backend/server/system/secret-box.ts +49 -0
  139. package/templates/backend/server/system/secret-box.unit.test.ts +46 -0
  140. package/templates/backend/server/test/bdd.ts +12 -0
  141. package/templates/backend/server/test/fake-firestore.ts +483 -0
  142. package/templates/backend/server/utils/apollo.ts +13 -0
  143. package/templates/backend/server/utils/fetch.ts +5 -0
  144. package/templates/backend/server/utils/firestore.ts +91 -0
  145. package/templates/backend/server/utils/input.ts +36 -0
  146. package/templates/backend/server/utils/input.unit.test.ts +43 -0
  147. package/templates/backend/server/utils/slug.ts +21 -0
  148. package/templates/backend/tsconfig.json +16 -0
  149. package/templates/base/.github/workflows/renovate.yml +17 -0
  150. package/templates/base/CHANGELOG.fr.md +3 -0
  151. package/templates/base/CHANGELOG.md +7 -0
  152. package/templates/base/CLAUDE.md +34 -0
  153. package/templates/base/README.md +12 -0
  154. package/templates/base/biome.json +47 -0
  155. package/templates/base/bunfig.toml +2 -0
  156. package/templates/base/docs/adr/0001-built-on-the-polyforms-stack.md +21 -0
  157. package/templates/base/docs/data-recovery.md +116 -0
  158. package/templates/base/docs/ideas.md +4 -0
  159. package/templates/base/project-context.md +43 -0
  160. package/templates/base/renovate.json +37 -0
  161. package/templates/ios/.github/workflows/build-ios.yml +168 -0
  162. package/templates/ios/.github/workflows/ios-upload.yml +308 -0
  163. package/templates/ios/.github/workflows/release-ios.yml +178 -0
  164. package/templates/ios/.github/workflows/testflight-ios.yml +57 -0
  165. package/templates/ios/infra/Gemfile +5 -0
  166. package/templates/ios/infra/fastlane/Appfile +1 -0
  167. package/templates/ios/infra/fastlane/Fastfile +54 -0
  168. package/templates/ios/infra/fastlane/metadata/copyright.txt +1 -0
  169. package/templates/ios/infra/fastlane/metadata/en-US/description.txt +0 -0
  170. package/templates/ios/infra/fastlane/metadata/en-US/keywords.txt +0 -0
  171. package/templates/ios/infra/fastlane/metadata/en-US/name.txt +1 -0
  172. package/templates/ios/infra/fastlane/metadata/en-US/privacy_url.txt +1 -0
  173. package/templates/ios/infra/fastlane/metadata/en-US/promotional_text.txt +0 -0
  174. package/templates/ios/infra/fastlane/metadata/en-US/subtitle.txt +1 -0
  175. package/templates/ios/infra/fastlane/metadata/en-US/support_url.txt +1 -0
  176. package/templates/ios/infra/fastlane/metadata/fr-FR/description.txt +0 -0
  177. package/templates/ios/infra/fastlane/metadata/fr-FR/keywords.txt +0 -0
  178. package/templates/ios/infra/fastlane/metadata/fr-FR/name.txt +1 -0
  179. package/templates/ios/infra/fastlane/metadata/fr-FR/privacy_url.txt +1 -0
  180. package/templates/ios/infra/fastlane/metadata/fr-FR/promotional_text.txt +0 -0
  181. package/templates/ios/infra/fastlane/metadata/fr-FR/subtitle.txt +1 -0
  182. package/templates/ios/infra/fastlane/metadata/fr-FR/support_url.txt +1 -0
  183. package/templates/ios/infra/fastlane/metadata/primary_category.txt +1 -0
  184. package/templates/ios/infra/ios.tf +28 -0
  185. package/templates/ios/ios/ExportOptions.plist +26 -0
  186. package/templates/ios/ios/apollo-codegen-config.json +16 -0
  187. package/templates/ios/ios/project.yml +94 -0
  188. package/templates/ios/ios/{{APP_NAME}}/App.swift +46 -0
  189. package/templates/ios/ios/{{APP_NAME}}/Assets.xcassets/AppIcon.appiconset/AppIcon.png +0 -0
  190. package/templates/ios/ios/{{APP_NAME}}/Assets.xcassets/AppIcon.appiconset/Contents.json +14 -0
  191. package/templates/ios/ios/{{APP_NAME}}/Assets.xcassets/Contents.json +6 -0
  192. package/templates/ios/ios/{{APP_NAME}}/Assets.xcassets/LaunchBackground.colorset/Contents.json +38 -0
  193. package/templates/ios/ios/{{APP_NAME}}/ContentView.swift +17 -0
  194. package/templates/ios/ios/{{APP_NAME}}/Features/Auth/AppleNonce.swift +33 -0
  195. package/templates/ios/ios/{{APP_NAME}}/Features/Auth/AuthRoot.swift +74 -0
  196. package/templates/ios/ios/{{APP_NAME}}/Features/Auth/AuthSession.swift +86 -0
  197. package/templates/ios/ios/{{APP_NAME}}/Features/Auth/LoginScreen.swift +83 -0
  198. package/templates/ios/ios/{{APP_NAME}}/Features/Home/HomeScreen.swift +21 -0
  199. package/templates/ios/ios/{{APP_NAME}}/Features/Home/components/pages/HomePage.swift +40 -0
  200. package/templates/ios/ios/{{APP_NAME}}/Features/Onboarding/AccountEnvironment.swift +7 -0
  201. package/templates/ios/ios/{{APP_NAME}}/Features/Onboarding/GraphQL/Onboarding.graphql +16 -0
  202. package/templates/ios/ios/{{APP_NAME}}/Features/Onboarding/OnboardingAPI.swift +28 -0
  203. package/templates/ios/ios/{{APP_NAME}}/Features/Onboarding/OnboardingClient.swift +26 -0
  204. package/templates/ios/ios/{{APP_NAME}}/Features/Onboarding/OnboardingGate.swift +50 -0
  205. package/templates/ios/ios/{{APP_NAME}}/Features/Onboarding/OnboardingScreen.swift +69 -0
  206. package/templates/ios/ios/{{APP_NAME}}/Features/Onboarding/components/pages/FirstNamePage.swift +66 -0
  207. package/templates/ios/ios/{{APP_NAME}}/Features/Onboarding/components/pages/WelcomePage.swift +37 -0
  208. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/ChangelogScreen.swift +25 -0
  209. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/ChangelogViewModel.swift +27 -0
  210. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/GraphQL/Settings.graphql +11 -0
  211. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/ProfileSettingsScreen.swift +30 -0
  212. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/ProfileSettingsViewModel.swift +51 -0
  213. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/SettingsAPI.swift +37 -0
  214. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/SettingsClient.swift +40 -0
  215. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/SettingsHomeScreen.swift +39 -0
  216. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/components/organisms/DeleteAccountButton.swift +30 -0
  217. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/components/organisms/SettingsRow.swift +56 -0
  218. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/components/organisms/SignOutButton.swift +23 -0
  219. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/components/pages/ChangelogPage.swift +59 -0
  220. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/components/pages/FeedbackSheet.swift +118 -0
  221. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/components/pages/ProfileSettingsPage.swift +90 -0
  222. package/templates/ios/ios/{{APP_NAME}}/Features/Settings/components/pages/SettingsHomePage.swift +70 -0
  223. package/templates/ios/ios/{{APP_NAME}}/Info.plist +14 -0
  224. package/templates/ios/ios/{{APP_NAME}}/Localizable.xcstrings +1030 -0
  225. package/templates/ios/ios/{{APP_NAME}}/PrivacyInfo.xcprivacy +122 -0
  226. package/templates/ios/ios/{{APP_NAME}}/Shared/APIClient.swift +23 -0
  227. package/templates/ios/ios/{{APP_NAME}}/Shared/AcceptLanguageInterceptor.swift +20 -0
  228. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/atoms/AsyncButton.swift +73 -0
  229. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/atoms/AsyncToolbarButton.swift +69 -0
  230. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/atoms/ToolbarIconButton.swift +46 -0
  231. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/molecules/EmptyState.swift +132 -0
  232. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/molecules/LabeledInfoRow.swift +29 -0
  233. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/styles/CaptionLabelStyle.swift +45 -0
  234. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/styles/Copyable.swift +32 -0
  235. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/styles/EdgeToEdgeSeparator.swift +14 -0
  236. package/templates/ios/ios/{{APP_NAME}}/Shared/Components/styles/RowLabelStyle.swift +58 -0
  237. package/templates/ios/ios/{{APP_NAME}}/Shared/CursorPage.swift +26 -0
  238. package/templates/ios/ios/{{APP_NAME}}/Shared/DataChanges.swift +19 -0
  239. package/templates/ios/ios/{{APP_NAME}}/Shared/ErrorPresenter.swift +62 -0
  240. package/templates/ios/ios/{{APP_NAME}}/Shared/ErrorReporting.swift +35 -0
  241. package/templates/ios/ios/{{APP_NAME}}/Shared/FeedbackReporting.swift +42 -0
  242. package/templates/ios/ios/{{APP_NAME}}/Shared/FirebaseTokenInterceptor.swift +49 -0
  243. package/templates/ios/ios/{{APP_NAME}}/Shared/GraphQLClient.swift +54 -0
  244. package/templates/ios/ios/{{APP_NAME}}/Shared/GraphQLHelpers.swift +128 -0
  245. package/templates/ios/ios/{{APP_NAME}}/Shared/GraphQLLoggingInterceptor.swift +28 -0
  246. package/templates/ios/ios/{{APP_NAME}}/Shared/LoadGate.swift +30 -0
  247. package/templates/ios/ios/{{APP_NAME}}/Shared/Secrets.swift.example +9 -0
  248. package/templates/ios/ios/{{APP_NAME}}/Shared/Services/AppSupportGate.swift +50 -0
  249. package/templates/ios/ios/{{APP_NAME}}/Shared/Shell/StartupLoadingView.swift +23 -0
  250. package/templates/ios/ios/{{APP_NAME}}/Shared/Shell/UpdateRequiredView.swift +24 -0
  251. package/templates/ios/ios/{{APP_NAME}}/Shared/Showcase.swift +13 -0
  252. package/templates/ios/ios/{{APP_NAME}}/Shared/SnapshotCache.swift +78 -0
  253. package/templates/ios/ios/{{APP_NAME}}/Shared/UITestEnvironment.swift +70 -0
  254. package/templates/ios/ios/{{APP_NAME}}/{{APP_NAME}}.entitlements +10 -0
  255. package/templates/ios/ios/{{APP_NAME}}Tests/Onboarding/OnboardingGateTests.swift +38 -0
  256. package/templates/ios/ios/{{APP_NAME}}Tests/Settings/SettingsViewModelTests.swift +63 -0
  257. package/templates/ios/ios/{{APP_NAME}}Tests/Support/TestDoubles.swift +35 -0
  258. package/templates/ios/package.merge.json +10 -0
  259. package/templates/ios/scripts/apollo-codegen.ts +31 -0
  260. package/templates/ios/scripts/check-ios-components.ts +72 -0
  261. package/templates/ios/scripts/check-strings.ts +103 -0
  262. package/templates/ios/scripts/install-device.sh +29 -0
  263. package/templates/ios/scripts/testflight.ts +213 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ Every version lists what changed and, when a rule change makes existing code non compliant,
4
+ its migrations with their risk. The upgrade routine reads this first.
5
+
6
+ ## 0.1.0 (2026.10.21)
7
+
8
+ First version, harvested from Shiori.
9
+
10
+ - Rules `00` to `17`: collaboration, workflow, commits, review, push, release, changelog,
11
+ App Store, copy, backend, iOS, operations, CI, AI, infrastructure, tooling, routines,
12
+ landing page.
13
+ - Routines: Sentry autofix, daily health, upgrades, deprecations.
14
+ - Skills: domain, graphql, ios-feature, appstore, stack-install, stack-upgrade.
15
+ - Templates: base, backend (Nitro, Firestore, GraphQL, Terraform, workflows), ios (SwiftUI
16
+ app generated by XcodeGen, workflows, fastlane).
17
+ - CLI: `init`, `sync`, `check`, `migrations`, `placeholders`.
18
+ - Migrations: none.
package/README.md CHANGED
@@ -1,3 +1,27 @@
1
- # Temporary Holding Version
1
+ # @moifort/stack
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ How Polyforms apps are built, as one versioned package: the rules Claude follows on every
4
+ project, the skills and routines it runs, and the templates a new app starts from. A Firebase
5
+ backend in TypeScript, a SwiftUI app, Terraform, GitHub Actions, Sentry.
6
+
7
+ ## Start an app
8
+
9
+ 1. `mkdir <app> && cd <app> && git init`
10
+ 2. `bunx @moifort/stack init --stacks backend,ios`
11
+ 3. `bun install && bun run preflight`
12
+
13
+ ## Keep an app up to date
14
+
15
+ Renovate opens a pull request when a new version is published. The upgrade routine syncs the
16
+ rules into the project's `stack/` folder and applies the version's migrations, listed in
17
+ [CHANGELOG.md](CHANGELOG.md).
18
+
19
+ ## What is inside
20
+
21
+ | Folder | What |
22
+ |---|---|
23
+ | `rules/` | The rules, imported by each project's `CLAUDE.md` through `stack/rules/` |
24
+ | `skills/` | The long forms, copied into `.claude/skills/` |
25
+ | `routines/` | The scheduled Claude tasks |
26
+ | `templates/` | What `init` writes into a new project, per stack |
27
+ | `migrations/` | What to change in a project when a version changes a rule |
package/bin/stack.ts ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env bun
2
+ import { parseArgs } from 'node:util'
3
+ import { readConfig, STACKS, type Stack } from '~/config'
4
+ import { defaultPlaceholders, requiredPlaceholders, writeConfig } from '~/init'
5
+ import { migrationsBetween, readMigrations } from '~/migrations'
6
+ import { packageDir, packageVersion } from '~/package'
7
+ import { applyPlan, applyTemplates, checkPlan, planSync } from '~/sync'
8
+
9
+ const usage = `stack <command>
10
+
11
+ init --stacks backend,ios --set APP_NAME=Name --set MAIN_BRANCH=main write stack.config.json, then sync
12
+ sync render rules, routines, skills, hooks into the project
13
+ check non-zero when the project differs from sync
14
+ migrations <from> <to> [--json] the migrations between two versions, for the project's stacks
15
+ placeholders --stacks backend,ios the placeholders those stacks need
16
+ `
17
+
18
+ const main = async (argv: string[]): Promise<number> => {
19
+ const [command, ...rest] = argv
20
+ const projectDir = process.cwd()
21
+ const pkg = packageDir()
22
+ const version = await packageVersion()
23
+
24
+ switch (command) {
25
+ case 'init': {
26
+ const { values } = parseArgs({
27
+ args: rest,
28
+ options: { stacks: { type: 'string' }, set: { type: 'string', multiple: true } },
29
+ })
30
+ const stacks = (values.stacks ?? '').split(',').filter(Boolean) as Stack[]
31
+ for (const s of stacks)
32
+ if (!STACKS.includes(s)) throw new Error(`unknown stack ${s}; known: ${STACKS.join(', ')}`)
33
+ const placeholders: Record<string, string> = defaultPlaceholders(projectDir)
34
+ for (const pair of values.set ?? []) {
35
+ const i = pair.indexOf('=')
36
+ if (i <= 0) throw new Error(`--set expects NAME=value, got ${pair}`)
37
+ placeholders[pair.slice(0, i)] = pair.slice(i + 1)
38
+ }
39
+ const required = await requiredPlaceholders(pkg, stacks)
40
+ const missing = required.filter((n) => placeholders[n] === undefined)
41
+ if (missing.length > 0) {
42
+ console.error(
43
+ `missing placeholders: ${missing.join(', ')}\n(pass each with --set NAME=value)`,
44
+ )
45
+ return 1
46
+ }
47
+ const used = Object.fromEntries(required.map((n) => [n, placeholders[n] as string]))
48
+ await writeConfig(projectDir, { stacks, placeholders: used, exclude: [] })
49
+ const config = await readConfig(projectDir)
50
+ const today = new Date().toISOString().slice(0, 10)
51
+ const { written, kept } = await applyTemplates(pkg, projectDir, config, {
52
+ ...config.placeholders,
53
+ STACK_VERSION: version,
54
+ TODAY: today,
55
+ })
56
+ const plan = await planSync(pkg, config, version)
57
+ await applyPlan(projectDir, plan)
58
+ console.log(
59
+ `initialised with stacks ${stacks.join(', ') || '(none)'}: ${written.length} files written, ${kept.length} kept, synced to ${version}`,
60
+ )
61
+ return 0
62
+ }
63
+ case 'sync': {
64
+ const plan = await planSync(pkg, await readConfig(projectDir), version)
65
+ await applyPlan(projectDir, plan)
66
+ console.log(`synced ${plan.files.size} files to ${version}`)
67
+ return 0
68
+ }
69
+ case 'check': {
70
+ const plan = await planSync(pkg, await readConfig(projectDir), version)
71
+ const problems = await checkPlan(projectDir, plan)
72
+ for (const p of problems) console.error(p)
73
+ if (problems.length === 0) console.log(`in sync with ${version}`)
74
+ return problems.length === 0 ? 0 : 1
75
+ }
76
+ case 'migrations': {
77
+ const { positionals, values } = parseArgs({
78
+ args: rest,
79
+ options: { json: { type: 'boolean' } },
80
+ allowPositionals: true,
81
+ })
82
+ const [from, to] = positionals
83
+ if (!from || !to) throw new Error('usage: migrations <from> <to>')
84
+ const config = await readConfig(projectDir)
85
+ const list = migrationsBetween(await readMigrations(pkg), from, to, config.stacks)
86
+ if (values.json)
87
+ console.log(
88
+ JSON.stringify(
89
+ list.map(({ body, ...m }) => m),
90
+ null,
91
+ 2,
92
+ ),
93
+ )
94
+ else for (const m of list) console.log(`${m.version} ${m.risk.padEnd(6)} ${m.id}`)
95
+ return 0
96
+ }
97
+ case 'placeholders': {
98
+ const { values } = parseArgs({ args: rest, options: { stacks: { type: 'string' } } })
99
+ const stacks = (values.stacks ?? '').split(',').filter(Boolean) as Stack[]
100
+ for (const name of await requiredPlaceholders(pkg, stacks)) console.log(name)
101
+ return 0
102
+ }
103
+ default:
104
+ console.log(usage)
105
+ return command === undefined || command === 'help' ? 0 : 1
106
+ }
107
+ }
108
+
109
+ process.exit(await main(process.argv.slice(2)))
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # Hook PostToolUse (Bash) : après un `git push` réussi, éteint tous les
3
+ # simulateurs et quitte Simulator.app, qui sinon restent ouverts et occupent
4
+ # 50 à 70 % du processeur.
5
+
6
+ command=$(/usr/bin/python3 -c 'import json, sys; print(json.load(sys.stdin).get("tool_input", {}).get("command", ""))' 2>/dev/null)
7
+
8
+ printf '%s' "$command" | grep -Eq '(^|[;&|[:space:]])git([[:space:]]+-C[[:space:]]+[^[:space:]]+)?[[:space:]]+push([[:space:]]|$)' || exit 0
9
+
10
+ booted=$(xcrun simctl list devices booted 2>/dev/null | grep -c '(Booted)')
11
+ simulator_app=$(pgrep -x Simulator)
12
+ [ "$booted" -eq 0 ] && [ -z "$simulator_app" ] && exit 0
13
+
14
+ xcrun simctl shutdown all >/dev/null 2>&1
15
+ [ -n "$simulator_app" ] && osascript -e 'quit app "Simulator"' >/dev/null 2>&1
16
+
17
+ cat <<EOF
18
+ {"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "Push terminé : $booted simulateur(s) éteint(s) et Simulator.app fermé automatiquement (hook shutdown-simulators-after-push)."}}
19
+ EOF
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@moifort/stack",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "The Polyforms stack: rules, skills, hooks, routines and templates for a Firebase + iOS app, versioned and upgraded through Renovate.",
5
+ "license": "MIT",
6
+ "repository": "github:moifort/polyforms-stack",
7
+ "type": "module",
8
+ "bin": {
9
+ "stack": "bin/stack.ts"
10
+ },
11
+ "files": [
12
+ "bin",
13
+ "src",
14
+ "rules",
15
+ "skills",
16
+ "hooks",
17
+ "routines",
18
+ "templates",
19
+ "migrations",
20
+ "CHANGELOG.md"
21
+ ],
22
+ "scripts": {
23
+ "test": "bun test",
24
+ "test:unit": "bun test .unit.test",
25
+ "test:int": "bun test .int.test",
26
+ "typecheck": "bunx tsc --noEmit",
27
+ "lint": "bunx biome check",
28
+ "lint:fix": "bunx biome check --write",
29
+ "validate": "bun src/validate-content.ts",
30
+ "preflight": "bun run typecheck && bun run lint && bun run test:unit && bun run test:int && bun run validate"
31
+ },
32
+ "dependencies": {
33
+ "semver": "7.7.2",
34
+ "zod": "4.1.12"
35
+ },
36
+ "devDependencies": {
37
+ "@biomejs/biome": "2.5.15",
38
+ "@types/bun": "1.4.3",
39
+ "@types/semver": "7.7.1",
40
+ "typescript": "5.9.3"
41
+ }
42
+ }
@@ -0,0 +1,78 @@
1
+ ---
2
+ id: daily-health
3
+ scope: project
4
+ runtime: cloud
5
+ cadence: "0 12 * * *"
6
+ title: "{{APP_NAME}} health (daily at noon)"
7
+ ---
8
+
9
+ Daily production health pass for {{APP_NAME}} (repository `{{CODE_DIR}}/{{REPO_NAME}}`, GCP project
10
+ `{{APP_SLUG}}-polyforms`, region `europe-west3`, backend Cloud Function gen 2 `{{APP_SLUG}}-server`,
11
+ request logs under `resource.type="cloud_run_revision"`). Analyse the last 24 hours of costs,
12
+ performance and monitoring, fix what is broken in code, push to main, watch CI. Report in the
13
+ person's language.
14
+
15
+ Error logs are out of scope: every `warn` and `error` goes to Sentry and to the `sentry-autofix`
16
+ routine.
17
+
18
+ Read the project's `CLAUDE.md` and the stack rules first and follow them. Work inline, never a
19
+ subagent. Other sessions may be editing the checkout: work in a worktree, or commit only your
20
+ files with an explicit pathspec; never stash, reset, switch branches, amend or force-push.
21
+
22
+ ## 0. Preconditions
23
+ - Every `gcloud` / `bq` command is prefixed with `CLOUDSDK_CONFIG=$HOME/.config/claude-observer/gcloud`,
24
+ the read-only observer account's own configuration. Check it with `gcloud auth
25
+ print-access-token`. If it fails, never fall back to the person's login and never run `gcloud
26
+ auth login`: do the steps that need no GCP and put the failure at the top of the report.
27
+ - The observer account only reads. Never write to GCP with it.
28
+ - `git pull --ff-only` if the tree allows it; otherwise work on what is there and say so.
29
+
30
+ ## 1. Costs
31
+ - Billing export: `bq query --project_id={{APP_SLUG}}-polyforms --use_legacy_sql=false` on
32
+ `{{BILLING_TABLE}}`, filtered on `project.id = "{{APP_SLUG}}-polyforms"`, cost plus credits per day
33
+ (`Europe/Paris`) and per service and SKU for the last 8 days. The export lags hours: yesterday
34
+ may be partial, say so.
35
+ - Flag any service above twice its 7-day daily average, any AI API above `{{AI_DAILY_ALERT}}` a
36
+ day, any new SKU. For Gemini, the grounded-search SKU is the expensive one; trace a spike through
37
+ the structured usage logs grouped by step and hour; `httpRequest.userAgent` and `remoteIp` on the
38
+ API route say whether one device caused it.
39
+ - A prepaid AI account answering 402 is critical: report it first, only the person can top up.
40
+ - Never read or print a key or a secret.
41
+
42
+ ## 2. Performance
43
+ - From the API request logs of the last 24 h: p50, p95, p99 latency, 5xx rate, slowest hours.
44
+ Cloud Monitoring: request latencies, instance count, startup latencies, memory. Compare with
45
+ the previous day. Flag p95 above 2 s not explained by cold starts, a regression above 50 %,
46
+ memory near the limit. Cold starts are a figure, never an action item: scaling to zero is the
47
+ rule.
48
+ - Firestore usage when a cost or latency spike points there. Production data may be read to
49
+ explain a spike; never copy personal data into the report.
50
+
51
+ ## 3. Monitoring
52
+ - CI: `gh run list --repo moifort/{{REPO_NAME}} --limit 30 --json databaseId,name,headSha,status,conclusion,createdAt`:
53
+ any failed run on main in 24 h, Deploy first, including its migrations step.
54
+ - Cloud Scheduler: `gcloud scheduler jobs list --project={{APP_SLUG}}-polyforms --location=europe-west3`,
55
+ state and last attempt of each job; a paused or failed job is a finding.
56
+ - The app configuration endpoint on the production host answers 200.
57
+ - App Store and TestFlight are out of scope.
58
+
59
+ ## 4. Fix
60
+ For each real problem with a clear cause in code: fix it following the rules, verify (typecheck,
61
+ the three test families separately, linter, schema check if the schema changed, an app build for
62
+ app changes), commit without asking (conventional commits, English, explicit pathspec,
63
+ `Co-Authored-By` trailer), push directly to main (authorised for this routine), watch every CI
64
+ run for the commit until green, fix forward on failure. Never touch the changelogs, never deploy or
65
+ terraform from the machine, never run migrations by hand.
66
+
67
+ Only propose, never apply: anything that changes what users see or can do, any infrastructure,
68
+ Terraform or secret change, anything with an unclear cause, and anything that would raise paid API
69
+ calls (state its cost).
70
+
71
+ ## 4b. The other routines
72
+ List the last run of every other routine of this account (Sentry triage, upgrades,
73
+ deprecations). One that missed two cadences is a finding at the top of the report.
74
+
75
+ ## 5. Report (short)
76
+ One-line verdict first (all fine / fixed / action needed from you). Then Costs (yesterday's total
77
+ and per service versus the 7-day average), Performance (p50, p95, 5xx, instances), Monitoring
78
+ (CI, Deploy, scheduler). Then "Fixed" (hashes, CI result per workflow) and "To decide".
@@ -0,0 +1,23 @@
1
+ ---
2
+ id: deprecations
3
+ scope: project
4
+ runtime: cloud
5
+ cadence: "0 9 1 * *"
6
+ title: "{{APP_NAME}} deprecations (monthly)"
7
+ ---
8
+
9
+ Remove from {{APP_NAME}}'s API what no build on sale still asks for (repository `{{CODE_DIR}}/{{REPO_NAME}}`).
10
+ Follow the project's `CLAUDE.md` and the stack rules on API evolution. Report in the person's
11
+ language.
12
+
13
+ 1. In a worktree of `origin/main`, run `bun run deprecations` with the store API key from
14
+ the environment (never printed).
15
+ 2. For every element reported **removable**: remove it from the schema, the resolver, and the
16
+ code and tests only it used. Leave anything in grace, waiting, still asked, or to check by
17
+ hand; list those in the report.
18
+ 3. Set the minimum supported build to the floor the script proposes. Never a higher value.
19
+ 4. Verify as the rules prescribe (typecheck, the three test families separately, linter, schema
20
+ check), commit `refactor(api): remove deprecated fields no build on sale asks for`, push to
21
+ main (authorised for this routine), watch CI and Deploy to the end, fix forward on failure.
22
+ 5. Report: what was removed, the new floor, what stays in grace and until when, anything to
23
+ check by hand.
@@ -0,0 +1,76 @@
1
+ ---
2
+ id: sentry-autofix
3
+ scope: machine
4
+ runtime: cloud
5
+ cadence: "0 */6 * * *"
6
+ title: Sentry autofix (every 6 hours)
7
+ ---
8
+
9
+ Fix the unresolved Sentry errors of every project of the organisation, push each fix to main, check
10
+ CI, and write a report in the person's language. This routine runs every six hours.
11
+
12
+ ## Standing authorisation
13
+ The person authorised this routine to commit AND push its fixes directly to `main` without asking.
14
+ Every other rule of each project's `CLAUDE.md` and of the stack rules still applies: language,
15
+ commit style, changelog never touched, verification before commit, deprecate rather than remove an
16
+ API field, deploy only through CI, never terraform or migrate from the machine.
17
+
18
+ ## Overlap guard (first)
19
+ A run starts on a fresh machine, so there is no lock file: before touching an issue, check that no
20
+ open pull request and no commit of the last 14 days on `main` carries `Fixes <SHORT-ID>`; if one
21
+ does, the issue is being handled, skip it.
22
+
23
+ ## Sentry access
24
+ - Org `polyforms`, API `https://sentry.io/api/0/`.
25
+ - In the cloud, the token is a **network secret** of the cloud environment for the host
26
+ `sentry.io`: the proxy adds the `Authorization` header to every request to that host, so call
27
+ the API with `curl` **without** an authorization header and never look for a token in a file or
28
+ a variable. On a desktop run, the token is `SENTRY_AUTH_TOKEN` in the project's `.env`. Never
29
+ print it, never write it into a file or a commit.
30
+ - Logger extras appear under `.context` in the event JSON.
31
+
32
+ ## Projects
33
+ The task's repositories are cloned at the start of the run: work in them. A Sentry project
34
+ `<name>-<platform>` maps to the repository `moifort/<name>`; a Sentry project whose
35
+ repository is not among the task's repositories is listed as unmapped in the report and skipped.
36
+ Add it to the task to cover it.
37
+
38
+ ## Steps
39
+ 1. Per project: `GET /projects/polyforms/{slug}/issues/?query=is:unresolved&statsPeriod=14d&sort=freq`.
40
+ Keep issues with events in the last 7 days. Skip external or transient causes (timeouts,
41
+ third-party 5xx, user cancellations) unless the code handles them wrongly.
42
+ 2. Skip issues already fixed: after `git fetch origin`,
43
+ `git log origin/main --since="14 days ago" --grep="Fixes <SHORT-ID>"`; if found, retry the
44
+ resolve step only. A regression (`isRegression`) means the fix was wrong: investigate again.
45
+ 3. Fetch the latest event (stack, message, `.context`, tags, release). Find the root cause in the
46
+ code. Without a confident root cause, do not guess: report "to investigate" with findings.
47
+ 4. In the repository's clone (already on `main`), install dependencies, follow that project's
48
+ `CLAUDE.md` and the stack rules.
49
+ 5. Fix with a regression test where the code is testable (test first). One commit per issue or
50
+ shared root cause, minimal.
51
+ 6. Verify as the rules prescribe (typecheck, the three test families separately, linter, schema
52
+ check if the schema changed, an app build for app changes). Never commit a failing fix.
53
+ 7. Commit: conventional commit, English, body with the root cause, trailer `Fixes <SHORT-ID>`,
54
+ then the `Co-Authored-By` trailer. Never touch the changelogs.
55
+ 8. Push **to `main`**, not to a `claude/` branch: `git fetch origin && git rebase origin/main`,
56
+ then `git push origin HEAD:main`. A conflicting rebase is aborted and reported, never forced.
57
+ An **app (iOS) fix cannot be built here** (no Xcode): push it as a branch `claude/sentry-<id>`,
58
+ open a pull request with `gh pr create`, and let CI build it; the next pass merges it when CI
59
+ is green.
60
+ 9. Watch CI for the pushed commit (`gh run list --json databaseId,headSha,name,status,conclusion`
61
+ filtered on `headSha`, then `gh run watch <id> --exit-status`). On failure, fix forward if the
62
+ cause is clear, otherwise revert, push the revert, report.
63
+ 10. Once CI is green, Deploy included: `PUT /organizations/polyforms/issues/{id}/` with
64
+ `{"status":"resolvedInNextRelease"}`, never plain `resolved`.
65
+ 11. Merge the pull requests opened by earlier passes whose CI is green (`gh pr list --author @me
66
+ --label sentry`), then resolve their issues as in step 10.
67
+
68
+ ## Limits
69
+ - At most 3 fixes per pass, prioritised by event count times users affected.
70
+ - Never change infrastructure, secrets, CI workflows or migrations to silence an error; never
71
+ lower a log level to hide a bug; never delete data; never ignore an issue.
72
+
73
+ ## Report (short, scannable)
74
+ Nothing found and nothing fixed: one line, plus any access problem. Otherwise per project: issues
75
+ examined, fixed (short id, root cause in one sentence, SHA, CI status, resolved or not), skipped
76
+ (reason), to investigate (leads). Access problems at the end.
@@ -0,0 +1,45 @@
1
+ ---
2
+ id: stack-upgrade
3
+ scope: machine
4
+ runtime: cloud
5
+ cadence: "0 7 * * *"
6
+ title: Upgrades (daily, after Renovate)
7
+ ---
8
+
9
+ Apply the stack package upgrades and the major dependency upgrades Renovate opened overnight, on
10
+ every project of the organisation. Report in the person's language.
11
+
12
+ ## Discovery
13
+ For every repository under `{{CODE_DIR}}` whose `origin` points at `moifort/*`:
14
+ `gh pr list --repo <owner>/<name> --label stack --state open --json number,title,headRefName`.
15
+ A pull request bumping `@moifort/stack` follows "Stack pull request"; a pull request labelled
16
+ `major` follows "Major dependency"; anything else is reported, not touched.
17
+
18
+ ## Stack pull request
19
+ 1. Check out the pull request's branch in a worktree (`/tmp/stack-upgrade-<name>-<date>`),
20
+ install dependencies, and follow the `stack-upgrade` skill shipped by the package in
21
+ `.claude/skills/`: sync, migrations one by one with their checks, preflight, a comment on the
22
+ pull request with one line per migration, merge only when every migration is `safe`, applied or
23
+ not applicable, every check passes and CI is green. Otherwise label `stack:review` and leave it
24
+ open.
25
+ 2. Remove the worktree.
26
+
27
+ ## Major dependency
28
+ 1. Check out the branch in a worktree, install. Read the dependency's release notes and migration
29
+ guide for every major between the old and the new version (`gh api` on the repository's
30
+ releases, or the package's CHANGELOG).
31
+ 2. Apply the code changes the new major requires, in one commit per dependency, verified as the
32
+ rules prescribe. Respect the project's `allowedVersions` bounds: a pull request Renovate
33
+ should not have opened is closed with the reason.
34
+ 3. Add the `staging` label so the Deploy workflow deploys the branch to staging; wait for it.
35
+ 4. Merge when CI and the staging deploy are green. Otherwise comment with what blocks (the
36
+ failing job, the breaking change, the bound to add) and leave it open.
37
+
38
+ ## Limits
39
+ - Never edit the package's rules from a consumer project; a rule that is wrong is reported.
40
+ - Never force-merge, never force-push, never touch a pull request without the `stack` label.
41
+ - At most one pull request per project per pass.
42
+
43
+ ## Report
44
+ Per project: pull request, version from and to, migrations applied, not applicable, needing
45
+ review, merged or left open with the reason.
@@ -0,0 +1,81 @@
1
+ # Collaboration
2
+
3
+ How Claude behaves on every project, whatever the stack.
4
+
5
+ ## Short answers
6
+
7
+ Lead with the result and what blocks. No "what I built" section, no closing recap, no restating
8
+ the context: the person follows the work as it happens and knows their repository. Details come
9
+ when asked.
10
+
11
+ ## Critical review before execution
12
+
13
+ Every proposed design or architecture choice gets a review as a senior infrastructure engineer
14
+ with a whole-system view: name the failure modes, say plainly when it is wrong or not worth
15
+ building, lead with the verdict. Before agreeing, check:
16
+
17
+ - what fails if an event is lost, a call times out, a process restarts;
18
+ - what the change delivers to the person using the product;
19
+ - whether it is new infrastructure for a single consumer;
20
+ - whether a cheaper design does the job (an idempotent write, a lazy reconciliation).
21
+
22
+ Never side with the person because they pushed back. Change position only on a better argument,
23
+ and say which argument changed it. Once the decision is taken, execute it fully and do not
24
+ relitigate it at the next task.
25
+
26
+ ## Questions sized to the task
27
+
28
+ A simple request (a typo, a label, a one-line fix) is done at once, without a question and
29
+ without turning it into architecture. A request that involves decisions (several readings, a
30
+ trade-off, something that is hard to undo) gets the questions first, before any code. Ask when it
31
+ is needed, never as a reflex.
32
+
33
+ ## Autonomy
34
+
35
+ Never stop to ask permission to continue. Stop for a real blocker or a question that changes a
36
+ lot, not for a checkpoint. A plan runs to its final result in one go; validation screenshots are
37
+ gathered and shown at the end, not used as a pause. Never ask permission to commit: see
38
+ [02-commits.md](02-commits.md).
39
+
40
+ ## Run commands yourself, go to the end
41
+
42
+ A list of manual steps for the person to run is a failure, not a deliverable. Run every command
43
+ line yourself: checks, builds, fixes, verifications; drive the browser when the task is in a
44
+ browser; go to the end of the task. Only a command that needs the person's password, `sudo`, an
45
+ Apple sign-in or a physical action (plugging the phone) is theirs, and then say so plainly. When
46
+ about to end a message with "run this", run it instead and report.
47
+
48
+ Two things wait for a go, asked in one message that says what will be done and why: reading
49
+ production data, and anything that changes production (a deploy, a console setting, a store
50
+ submission). See [15-tooling.md](15-tooling.md#browser-and-doing-things-end-to-end).
51
+
52
+ ## No agent, no plan mode, no spec by default
53
+
54
+ Exploration, review, debugging and planning happen inline, in the main conversation. By default:
55
+ no subagent, no parallel agent, no plan mode, no design document. Each of these happens only when
56
+ the person asks for it explicitly, for that task. A skill that requires dispatching an agent or
57
+ writing a document is applied inline, without them. This rule wins over any external procedure
58
+ that says otherwise.
59
+
60
+ ## Where feedback goes
61
+
62
+ A correction given in conversation is written down in the same task, not remembered privately:
63
+
64
+ | What it is | Where it goes |
65
+ |---|---|
66
+ | A rule that would hold in any project | This package: the rule file, a version bump, a migration if code is affected |
67
+ | A fact of this project (an id, an account, a device, a certificate date) | The project's `project-context.md` |
68
+ | A decision specific to this project's product or data model | The project's `CLAUDE.md` |
69
+
70
+ Claude's private memory keeps neither rules nor project facts. What is not written in a repository
71
+ does not exist at the next session.
72
+
73
+ ## Languages
74
+
75
+ | What is produced | Language |
76
+ |---|---|
77
+ | Code, comments, commit messages, technical documentation, specs, this package | English |
78
+ | Copy seen by the person using the product | `{{SOURCE_LANGUAGE}}`, then `{{SERVED_LANGUAGES}}` |
79
+ | Conversation | The person's language |
80
+
81
+ Never two languages in one commit message or one comment.
@@ -0,0 +1,81 @@
1
+ # Workflow of a task
2
+
3
+ ## The cycle
4
+
5
+ 1. **Framing.** Understand the intent before coding. If two readings of the request lead to two
6
+ different pieces of work, ask now, not at the end.
7
+ 2. **No spec, no plan mode by default.** A design document
8
+ (`docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md`) or a written plan is produced only when
9
+ the person asks for one. Without that, the plain explanation below is given in the conversation
10
+ and the work starts. A paid API call states its cost either way, see [13-ai.md](13-ai.md).
11
+ 3. **Implementation**, inline, to the final result, without checkpoints.
12
+ 4. **Verification**, below.
13
+ 5. **Review** before commit, see [03-code-review.md](03-code-review.md).
14
+ 6. **Commit**, see [02-commits.md](02-commits.md).
15
+ 7. **Screenshot** when a screen changed, below.
16
+
17
+ ## What an explanation or a plan contains
18
+
19
+ Before the work, in the conversation (or in the plan when one was asked), a plain section:
20
+
21
+ - the domains touched: created, modified, removed;
22
+ - how it works today, what it gives afterwards, ideally a table of before and after;
23
+ - the mechanisms at play, without jargon;
24
+ - what does not move;
25
+ - why the tasks are in this order.
26
+
27
+ Code blocks serve execution, the explanation serves the decision. Each task ends with a testable
28
+ deliverable and its verification command with the expected output.
29
+
30
+ ## Verification before a commit
31
+
32
+ A claim of success without the command output behind it has no value. Before every commit:
33
+
34
+ | What was touched | What runs |
35
+ |---|---|
36
+ | Backend | `bun run typecheck` (after `bun run prepare` if routes changed), the three test families **separately** (see below), `bun run lint` |
37
+ | The API schema | `bun run schema:check`: a removal not preceded by a deprecation is refused, see [11-ops.md](11-ops.md#api-evolution) |
38
+ | The app | `bun run ios:build`; and after any on-screen text change, `bun run ios:strings` until it reports every string translated, see [08-copy.md](08-copy.md#localisation) |
39
+
40
+ The three test families run **separately** (`bun run test:unit`, `bun run test:int`,
41
+ `bun run test:feat`): a module mock declared in one test file serves every file of the same
42
+ run, so a single combined run passes while CI, which runs them apart, goes red.
43
+
44
+ ## Mockups before a complex screen
45
+
46
+ A new feature with screens and real complexity (a new flow, several states, a layout nobody has
47
+ seen) starts with mockups, before any real code: quick throwaway renderings of the screens built
48
+ from fixtures (a preview or a harness screenshot, or a drawn mock when that is faster), sent for a
49
+ fast approval of the layout and the flow. Only once the mockups are approved does the real
50
+ implementation start. A small change to an existing screen skips this and goes straight to the
51
+ screenshot below.
52
+
53
+ ## Screenshot after every UI change
54
+
55
+ Every request that changes a screen ends with a screenshot of the result, sent to the person,
56
+ followed by a wait for their approval. Without exception, and whatever the state of the simulator:
57
+
58
+ - "the simulator stops at sign-in" is never a reason to skip it. Feed the page its preview
59
+ fixtures through a temporary launch argument in the app entry point, build into a derived data
60
+ path unique to the session, install and launch, capture, then revert the harness before the
61
+ commit. The fixtures and previews stay.
62
+ - Before sending, check the design conventions of [10-ios.md](10-ios.md#design-conventions).
63
+ - Committing before approval is fine. Installing on the person's device before approval is not.
64
+ - The next task does not start until the screenshot is approved.
65
+
66
+ ## Ideas and decisions
67
+
68
+ - **Ideas** that come up and are not built now go into the project's `docs/ideas.md`, one line
69
+ each with the date, so nothing said in passing is lost. Claude adds to it without being asked.
70
+ - **Architecture choices** get an ADR (architecture decision record) in `docs/adr/`, numbered
71
+ (`0007-flat-collections.md`): context, decision, alternatives rejected, consequences. Written
72
+ when the choice is made, proposed to the person before the code that depends on it.
73
+
74
+ (Shape to refine later; the rule is that both exist in every project.)
75
+
76
+ ## Skills and tooling
77
+
78
+ Before starting, look at what is installed and invoke what matches the task. Never cite, impose
79
+ or document a skill without checking it exists on the machine. The skills this package ships are
80
+ in `.claude/skills/` after `sync`; the ones that matter are `domain`, `graphql`, `ios-feature`,
81
+ `appstore`, `stack-upgrade`.