agentic-team 0.0.0-stage → 6.2.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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +189 -2
  3. package/bin/build-template.js +208 -0
  4. package/bin/index.js +1334 -0
  5. package/bin/platforms.js +510 -0
  6. package/package.json +41 -4
  7. package/template/.claude-plugin/plugin.json +10 -0
  8. package/template/.orchestra/README.md +391 -0
  9. package/template/.orchestra/blueprints/README.md +87 -0
  10. package/template/.orchestra/blueprints/api-only.md +46 -0
  11. package/template/.orchestra/blueprints/component-crud-resource.md +25 -0
  12. package/template/.orchestra/blueprints/saas-starter.md +69 -0
  13. package/template/.orchestra/config.yml +293 -0
  14. package/template/.orchestra/roles/orchestrator.md +105 -0
  15. package/template/.orchestra/roles/product-manager.md +439 -0
  16. package/template/.orchestra/scripts/compact-diff.awk +45 -0
  17. package/template/.orchestra/scripts/context.mjs +373 -0
  18. package/template/.orchestra/stacks/README.md +81 -0
  19. package/template/.orchestra/stacks/_lib/common.sh +3095 -0
  20. package/template/.orchestra/stacks/_repo/setup.sh +1113 -0
  21. package/template/.orchestra/stacks/backend-hono/claude/commands/endpoint.md +13 -0
  22. package/template/.orchestra/stacks/backend-hono/claude/skills/api-endpoint/SKILL.md +111 -0
  23. package/template/.orchestra/stacks/backend-hono/claude/skills/testing/SKILL.md +75 -0
  24. package/template/.orchestra/stacks/backend-hono/setup.sh +5945 -0
  25. package/template/.orchestra/stacks/backend-hono/stack.md +195 -0
  26. package/template/.orchestra/stacks/mobile-android-native/claude/commands/component.md +13 -0
  27. package/template/.orchestra/stacks/mobile-android-native/claude/skills/design-component/SKILL.md +58 -0
  28. package/template/.orchestra/stacks/mobile-android-native/setup.sh +607 -0
  29. package/template/.orchestra/stacks/mobile-android-native/stack.md +138 -0
  30. package/template/.orchestra/stacks/mobile-expo/claude/commands/component.md +12 -0
  31. package/template/.orchestra/stacks/mobile-expo/claude/skills/design-component/SKILL.md +92 -0
  32. package/template/.orchestra/stacks/mobile-expo/setup.sh +470 -0
  33. package/template/.orchestra/stacks/mobile-expo/stack.md +141 -0
  34. package/template/.orchestra/stacks/mobile-ios-native/claude/commands/component.md +12 -0
  35. package/template/.orchestra/stacks/mobile-ios-native/claude/skills/design-component/SKILL.md +58 -0
  36. package/template/.orchestra/stacks/mobile-ios-native/setup.sh +468 -0
  37. package/template/.orchestra/stacks/mobile-ios-native/stack.md +130 -0
  38. package/template/.orchestra/stacks/web-nextjs/claude/commands/component.md +12 -0
  39. package/template/.orchestra/stacks/web-nextjs/claude/skills/design-component/SKILL.md +100 -0
  40. package/template/.orchestra/stacks/web-nextjs/claude/skills/design-tokens/SKILL.md +61 -0
  41. package/template/.orchestra/stacks/web-nextjs/setup.sh +128 -0
  42. package/template/.orchestra/stacks/web-nextjs/stack.md +170 -0
  43. package/template/.orchestra/stacks/web-vite-react/claude/commands/component.md +12 -0
  44. package/template/.orchestra/stacks/web-vite-react/claude/skills/design-component/SKILL.md +100 -0
  45. package/template/.orchestra/stacks/web-vite-react/claude/skills/design-tokens/SKILL.md +61 -0
  46. package/template/.orchestra/stacks/web-vite-react/setup.sh +185 -0
  47. package/template/.orchestra/stacks/web-vite-react/stack.md +177 -0
  48. package/template/.orchestra/trackers/github.sh +467 -0
  49. package/template/CLAUDE.md +54 -0
  50. package/template/agents/architect.md +83 -0
  51. package/template/agents/engineer-high.md +17 -0
  52. package/template/agents/engineer-low.md +17 -0
  53. package/template/agents/engineer-max.md +17 -0
  54. package/template/agents/engineer-medium.md +17 -0
  55. package/template/agents/engineer-xhigh.md +17 -0
  56. package/template/agents/lead.md +925 -0
  57. package/template/agents/milestone-reviewer.md +65 -0
  58. package/template/agents/orchestrator.md +105 -0
  59. package/template/agents/product-manager.md +439 -0
  60. package/template/agents/re-reviewer.md +25 -0
  61. package/template/agents/reviewer.md +303 -0
  62. package/template/agents/scaffolder.md +206 -0
  63. package/template/agents/tracker.md +202 -0
  64. package/template/commands/blueprint.md +23 -0
  65. package/template/commands/create-role.md +64 -0
  66. package/template/commands/export.md +45 -0
  67. package/template/commands/help.md +56 -0
  68. package/template/commands/hotfix.md +14 -0
  69. package/template/commands/orchestrator.md +12 -0
  70. package/template/commands/pm.md +18 -0
  71. package/template/commands/rewind.md +63 -0
  72. package/template/commands/setup.md +15 -0
  73. package/template/commands/start.md +29 -0
  74. package/template/commands/status.md +11 -0
  75. package/template/commands/sync.md +76 -0
  76. package/template/commands/verifier.md +53 -0
  77. package/template/hooks/audit-context.sh +166 -0
  78. package/template/hooks/enforce-role.sh +193 -0
  79. package/template/hooks/protect-system-files.sh +123 -0
  80. package/template/hooks/review-rounds.sh +172 -0
  81. package/template/rules/acceptance-check.orchestra.md +11 -0
  82. package/template/rules/analytics-kpi.orchestra.md +56 -0
  83. package/template/rules/code-standards.orchestra.md +35 -0
  84. package/template/rules/comments.orchestra.md +77 -0
  85. package/template/rules/commit-format.orchestra.md +20 -0
  86. package/template/rules/dependency-upgrade.orchestra.md +112 -0
  87. package/template/rules/design-parity.orchestra.md +181 -0
  88. package/template/rules/observability.orchestra.md +121 -0
  89. package/template/rules/performance.orchestra.md +190 -0
  90. package/template/rules/phase-limits.orchestra.md +3 -0
  91. package/template/rules/pull-request.orchestra.md +218 -0
  92. package/template/rules/role-boundaries.orchestra.md +132 -0
  93. package/template/rules/security.orchestra.md +106 -0
  94. package/template/rules/stuck-detection.orchestra.md +25 -0
  95. package/template/rules/testing-standards.orchestra.md +219 -0
  96. package/template/rules/tracker-authority.orchestra.md +198 -0
  97. package/template/rules/verification-gate.orchestra.md +56 -0
  98. package/template/skills/accessibility/SKILL.md +31 -0
  99. package/template/skills/android-testing/SKILL.md +112 -0
  100. package/template/skills/architecture/SKILL.md +175 -0
  101. package/template/skills/auth-setup/SKILL.md +105 -0
  102. package/template/skills/best-practices/SKILL.md +29 -0
  103. package/template/skills/code-optimizer/SKILL.md +27 -0
  104. package/template/skills/core-web-vitals/SKILL.md +32 -0
  105. package/template/skills/crud-api/SKILL.md +32 -0
  106. package/template/skills/debug/SKILL.md +28 -0
  107. package/template/skills/deployment/SKILL.md +104 -0
  108. package/template/skills/error-handling/SKILL.md +154 -0
  109. package/template/skills/frontend-design/SKILL.md +30 -0
  110. package/template/skills/fullstack-infrastructure/SKILL.md +108 -0
  111. package/template/skills/ios-testing/SKILL.md +100 -0
  112. package/template/skills/mobile-testing/SKILL.md +102 -0
  113. package/template/skills/observability/SKILL.md +91 -0
  114. package/template/skills/project-structure/SKILL.md +114 -0
  115. package/template/skills/push-notifications/SKILL.md +146 -0
  116. package/template/skills/react-best-practices/SKILL.md +31 -0
  117. package/template/skills/realtime/SKILL.md +146 -0
  118. package/template/skills/review/SKILL.md +82 -0
  119. package/template/skills/testing/SKILL.md +45 -0
  120. package/template/skills/web-quality-audit/SKILL.md +47 -0
  121. package/template/skills/web-testing/SKILL.md +120 -0
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: android-testing
3
+ description: "Testing strategy for native Android apps — Kotlin, Compose, JUnit, Robolectric and instrumented tests. Use for any phase that writes or changes tests in a native Android app."
4
+ ---
5
+
6
+ # Skill: Android Testing
7
+
8
+ ## The fact everything else follows from
9
+
10
+ **A debug build passing proves very little about the release build.**
11
+
12
+ R8 shrinks, optimises and obfuscates. Reflection stops resolving, a
13
+ serialisation library loses the field names it needed, a keep rule that was
14
+ never written removes a class the app loads by name. Every test is green,
15
+ `assembleDebug` is fine, and the release APK crashes on launch.
16
+
17
+ So `assembleRelease` belongs in the gate, not in the release checklist.
18
+
19
+ ## The layers
20
+
21
+ | Layer | What belongs here | Runs on |
22
+ |---|---|---|
23
+ | Unit (JUnit) | Pure logic and view models — no Android framework | JVM, fast |
24
+ | Compose UI | Semantics tree, state, interaction | **JVM via Robolectric** |
25
+ | Integration | The real HTTP client against MockWebServer | JVM |
26
+ | Instrumented | 3–5 journeys, one of them launch | Emulator |
27
+ | Build | `assembleDebug` **and** `assembleRelease` | The gate |
28
+
29
+ ## Compose UI tests belong on the JVM
30
+
31
+ `createComposeRule()` with Robolectric runs the real Compose runtime under
32
+ the JVM — no emulator, seconds instead of minutes. That speed is what makes
33
+ it realistic to test every component rather than a token few.
34
+
35
+ Keep `androidTest` for the handful of journeys that genuinely need a device:
36
+ permissions, the real keyboard, process death and restore.
37
+
38
+ ## The semantics tree is the accessibility tree
39
+
40
+ ```kotlin
41
+ onNodeWithText("Add water").performClick() // yes
42
+ onNodeWithContentDescription("Add water").performClick() // yes
43
+ onNodeWithTag("btn_add_water").performClick() // last resort
44
+ ```
45
+
46
+ Compose's semantics tree is what TalkBack reads. Query it and **a control
47
+ with no text or content description fails the test** — accessibility becomes
48
+ enforced rather than reviewed.
49
+
50
+ `testTag` is for something with no user-visible identity at all. Reaching for
51
+ it first hides exactly the bug the query would have found.
52
+
53
+ ## MockWebServer, not a mocked API interface
54
+
55
+ ```kotlin
56
+ val server = MockWebServer()
57
+ server.enqueue(MockResponse().setBody(json).setResponseCode(200))
58
+ ```
59
+
60
+ Real OkHttp, real serialisation, real error mapping, real timeouts. Mocking
61
+ the Retrofit interface instead skips every one of those — and they are where
62
+ a dependency bump breaks you.
63
+
64
+ It also lets you test what a mocked interface cannot: a 500, a truncated
65
+ body, a socket timeout.
66
+
67
+ ## Never
68
+
69
+ - **Screenshot tests as the main assertion.** They change with every
70
+ emulator image and get re-recorded unread.
71
+ - Testing that Compose recomposes, or that Navigation navigates. Assert the
72
+ action fires with the right argument.
73
+ - `Thread.sleep`. Compose has `waitUntil` and an idling clock; a sleep is a
74
+ flake with a delay.
75
+ - Emulator-only verification. Emulators have no thermal throttling, no real
76
+ memory pressure, and an unrealistically fast disk.
77
+
78
+ ## Configuration coverage is a real axis
79
+
80
+ The same code behaves differently across API level, screen size, locale,
81
+ dark mode, and font scale. Robolectric can parameterise `@Config(sdk = [...])`
82
+ cheaply — use it for the minSdk boundary at least, because that is where
83
+ platform behaviour actually diverges.
84
+
85
+ RTL and a large font scale break more layouts than any other setting. Both
86
+ are one line to test and neither is ever tested by hand.
87
+
88
+ ## The JDK is a constraint, not a preference
89
+
90
+ AGP's minimum is 17. Robolectric's stubbed jar for a recent API level
91
+ refuses to start below **21** — `Android SDK 36 requires Java 21 (have Java
92
+ 17)` — and it reports that only after a full compile.
93
+
94
+ So the toolchain is JDK 21, and `verify.sh` checks it up front where the
95
+ message is actionable.
96
+
97
+ Robolectric also lags the platform: its newest stubbed API level is behind
98
+ `compileSdk`, so `robolectric.properties` pins `sdk` explicitly. Letting it
99
+ inherit `targetSdk` fails every test before one runs.
100
+
101
+ ## Versions
102
+
103
+ Gradle forbids a dynamic version, and rightly — a build must resolve the
104
+ same way twice. That is a reason to write the version down, not a reason to
105
+ write it from memory: the version catalog is generated from the repositories
106
+ at scaffold time. See `.claude/rules/dependency-upgrade.orchestra.md`.
107
+
108
+ ## Release risk
109
+
110
+ A Play Store release rolls back only for users who have not yet updated.
111
+ Staged rollout and a server-side kill switch are part of the strategy, not an
112
+ operational afterthought — see `.claude/rules/observability.orchestra.md`.
@@ -0,0 +1,175 @@
1
+ ---
2
+ name: architecture
3
+ description: "Choosing the shape of a system — deployment units, service boundaries, data ownership, consistency and resilience. Use when writing an RFC, structuring a new app, or deciding whether something should be split out."
4
+ ---
5
+
6
+ # Skill: Architecture
7
+
8
+ Depth for the design stage. The artifact is `rfc.md`; see
9
+ `.claude/agents/architect.md` for its shape and
10
+ `.claude/rules/analytics-kpi.orchestra.md` for what has to be settled before
11
+ any of this starts.
12
+
13
+ ## The fact everything else follows from
14
+
15
+ **The default is one deployable unit, and leaving it needs an argument.**
16
+
17
+ Almost every distributed architecture in a young product exists because it
18
+ was chosen at the start, not because a boundary was discovered. The cost
19
+ arrives later and lands on the one person maintaining it: two deploys to
20
+ coordinate, a transaction that no longer commits, a failure mode that cannot
21
+ be reproduced locally.
22
+
23
+ A modular monolith gets you the boundary without the network. Modules with
24
+ explicit interfaces, one process, one database, one deploy. When a module
25
+ genuinely needs to scale or fail on its own, the boundary is already drawn
26
+ and the extraction is mechanical.
27
+
28
+ So: **YAGNI is an architecture rule here**, not a coding one.
29
+
30
+ ## Before the shape: the number
31
+
32
+ A milestone that cannot say which number it should move is not ready to
33
+ design. Read `milestone.md` for the KPI, its baseline, its target, and how it
34
+ is read.
35
+
36
+ If the design cannot say **where that number is produced** — which event,
37
+ which query — the observability section of the RFC will be invented later,
38
+ which is the same as not having one.
39
+
40
+ ## Choosing the shape
41
+
42
+ Work down this list. Take the first row that is actually true, not the first
43
+ that sounds ambitious.
44
+
45
+ | If | Then | Because |
46
+ |---|---|---|
47
+ | You are starting, or the domain is still moving | **Modular monolith** | Boundaries are cheap to move while they are still wrong |
48
+ | One part has a genuinely different scaling or failure profile | **Extract that one part** | A real reason, and only that part |
49
+ | Separate teams must deploy without coordinating | **Services along team lines** | Conway's law is a constraint, not an observation |
50
+ | Work is long-running, retryable, or bursty | **Queue + worker, same repo** | Async is a boundary you can add without splitting the deploy |
51
+ | An audit trail is a product requirement | **Event sourcing for that aggregate** | Not for the whole system |
52
+ | Load is spiky and idle most of the time | **Serverless for that endpoint** | Per-endpoint, not per-application |
53
+
54
+ Two facts worth holding on to:
55
+
56
+ - Below roughly ten engineers, microservices cost more than they return. The
57
+ overhead is operational and it does not shrink with team size.
58
+ - You cannot skip a stage. Monolith → modular monolith → extracted service.
59
+ Each step teaches you where the boundary actually is; skipping straight to
60
+ services means guessing, and a wrong boundary is far more expensive than a
61
+ late one.
62
+
63
+ ## Drawing boundaries
64
+
65
+ A module boundary is worth having when it satisfies all three:
66
+
67
+ 1. **It owns its data.** No other module reads its tables directly.
68
+ 2. **It has an interface you could call over a network** — even though you
69
+ are not going to. If the interface only works in-process, it is not a
70
+ boundary, it is a folder.
71
+ 3. **It can be tested through that interface**, against a real database. That
72
+ is the same seam the testing rule already demands, so drawing it well
73
+ makes the suite easier, not harder.
74
+
75
+ If a proposed boundary fails any of these, it is naming, not structure.
76
+
77
+ ## Data
78
+
79
+ - **One database until a second one is forced.** Two datastores means no
80
+ transaction spans them, and every write that must span them becomes a
81
+ distributed problem you now own forever.
82
+ - **Consistency is a per-flow decision, not a system-wide one.** Money, quota
83
+ and permission changes are transactional. A read model, a feed, a counter
84
+ can lag.
85
+ - When a flow genuinely spans two owners, choose deliberately: a transaction
86
+ if they share a database, an outbox if they do not. **Never a plain HTTP
87
+ call in the middle of a write path** — the failure between "committed" and
88
+ "notified" is the bug you will spend a week on.
89
+ - Every schema change is forward-compatible with the release before it, and
90
+ a destructive change is two deploys. See
91
+ `.claude/skills/deployment/SKILL.md`.
92
+
93
+ ## Resilience
94
+
95
+ Any call leaving the process — another service, a provider, a queue — needs
96
+ all four. They are not optional extras, and they are cheap to add up front
97
+ and painful to retrofit.
98
+
99
+ | Control | Rule |
100
+ |---|---|
101
+ | **Timeout** | Always. A call with no timeout is a resource leak with a delay. Per-attempt timeout below the overall budget. |
102
+ | **Retry** | Idempotent operations only, exponential backoff **with jitter**, bounded attempts. Retrying a non-idempotent write is how you double-charge someone. |
103
+ | **Circuit breaker** | Open after consecutive failures; fail fast instead of queueing. Gives the dependency room to recover instead of holding it down. |
104
+ | **Bulkhead** | Bound the pool — connections, concurrency, queue depth. One slow dependency must not consume every worker. |
105
+
106
+ The fallback is part of the design, not an afterthought: state what the caller
107
+ sees when the dependency is gone. For anything gating a risky path the
108
+ fallback is **false** — the same rule the feature-flag registry follows in
109
+ `.claude/rules/observability.orchestra.md`.
110
+
111
+ Rate limiting is the mirror image, applied at your own edge. Decide the axis
112
+ deliberately — per user, per key, per endpoint — because "requests per
113
+ second" alone protects nothing when one caller owns all of them.
114
+
115
+ ## Operations belong in the design
116
+
117
+ These are architecture, not launch tasks. Each one is already enforced
118
+ somewhere; the design decides what goes in them.
119
+
120
+ - **Every dependency the service cannot serve without gets a readiness
121
+ probe.** Anything missing from that map can be broken while a release is
122
+ promoted anyway.
123
+ - **Every risky path ships behind a flag**, so it is turned off without a
124
+ deploy.
125
+ - **The correlation id crosses every boundary you draw.** A trace that stops
126
+ at a module edge is a trace you cannot follow.
127
+ - **Name what you would roll back to.** If undoing this milestone is not a
128
+ single revert, say so in the RFC's Risks section.
129
+
130
+ ## Say what you are not building
131
+
132
+ The most useful part of a design is usually the exclusions. Write them down —
133
+ the deferred cache, the second datastore, the abstraction over a provider you
134
+ have exactly one of — with the condition that would change the answer.
135
+
136
+ An abstraction with one implementation is a guess about the future, and it is
137
+ usually wrong in a way that costs more than the migration would have. Extract
138
+ after the second real case.
139
+
140
+ ## Decision records
141
+
142
+ The RFC is the record for one milestone. A decision that **outlives** the
143
+ milestone — a datastore, an auth model, a boundary, anything expensive to
144
+ reverse — gets its own file in `docs/adr/`, numbered, with: context, the
145
+ decision, the consequences, and the alternatives that were rejected and why.
146
+
147
+ The rejected alternatives are the point. Six months on, the question is never
148
+ "what did we choose", it is "did we already think about this".
149
+
150
+ ## Depth on the pieces
151
+
152
+ - `.claude/skills/error-handling/SKILL.md` — what a failure returns, and the
153
+ difference between handling one and hiding it.
154
+ - `.claude/skills/realtime/SKILL.md` — when a connection is worth its cost,
155
+ and what it does to deploys and scaling.
156
+ - `.claude/skills/deployment/SKILL.md` — health, promotion, rollback,
157
+ migrations.
158
+ - `.claude/skills/observability/SKILL.md` — wiring the monitoring the design
159
+ assumes.
160
+
161
+ ## Anti-patterns
162
+
163
+ - **Distributed monolith.** Services that must be deployed together. You paid
164
+ the network cost and kept the coupling.
165
+ - **Shared database between services.** The boundary is decorative; the
166
+ schema is the real interface and nobody owns it.
167
+ - **Accidental complexity.** The architecture is more elaborate than the
168
+ domain. Symptom: explaining the system takes longer than explaining the
169
+ product.
170
+ - **Golden hammer.** The same shape as the last project, chosen before the
171
+ problem was read.
172
+ - **Vendor coupling in the core.** The provider belongs behind a module you
173
+ own — which is also the module the testing rule holds at 100%.
174
+ - **Designing for a scale that has no baseline.** If the KPI has no number
175
+ today, the design cannot be sized. Say so rather than guessing.
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: auth-setup
3
+ description: "Authentication, login, signup, session management and user identity. The core decisions are already made and scaffolded — this says what they are, what is still open, and what to check when changing any of it. Use for any phase that establishes or changes who a request belongs to."
4
+ ---
5
+
6
+ # Skill: Auth Setup
7
+
8
+ ## The decisions are made — do not re-open them
9
+
10
+ This used to open with "choose auth strategy: JWT vs session-based vs OAuth"
11
+ and recommend `bcrypt`, `jose` and `arctic`. The backend stack answered that
12
+ question and shipped code, so re-asking it produces an app that disagrees with
13
+ its own scaffold.
14
+
15
+ | Decision | What ships | Where |
16
+ |---|---|---|
17
+ | Strategy | Opaque server-side sessions, not JWT | `src/features/auth/sessions.ts` |
18
+ | Why | A session is revocable with a `DELETE`. A JWT is valid until it expires, which is why JWT designs need refresh rotation to claw revocation back | — |
19
+ | Hashing | `node:crypto` scrypt, zero dependency | `src/lib/password.ts` |
20
+ | Parameters | OWASP's N=2^14, r=8, p=5 set (16 MiB), not their 128 MiB primary — a login route allocating 128 MiB per attempt is the cheapest way to exhaust a small container. Measured, and the reason is in a comment beside it | — |
21
+ | Token at rest | SHA-256 of the token, never the token | `sessions.ts` |
22
+ | Transport | httpOnly cookie, `secure` in production, `SameSite=Lax` | `src/features/auth/routes.ts` |
23
+ | CSRF | `SameSite=Lax` blocks the cross-site POST. Strict was rejected: it drops the cookie when someone arrives from an email link, which reads as a random sign-out | — |
24
+ | Rate limit | 10/min on the auth routes, injectable so a test can drive it to the limit | `routes.ts` |
25
+ | Browser lifetime | One session cookie, sliding: a 30-day **idle** window that rolls forward on use, under a 90-day **absolute** ceiling that never moves. OWASP asks for both, and they catch different things | `sessions.ts` |
26
+ | Why no refresh token in a browser | Rotation exists to claw back revocation for a credential that cannot be revoked. This one is a row we delete. Splitting it would cost every client a 401-and-retry interceptor and buy nothing | — |
27
+ | Native / API clients | `POST /auth/token` returns an access token and a rotating refresh token in the body — no cookies. RFC 9700 §2.2.2 asks for rotation for public clients, which a native app is and a first-party cookie session is not | `routes.ts`, `refresh.ts` |
28
+ | Refresh rotation | On every use. A spent token presented again is theft, so the family and every session descended from it are revoked. The spend is a conditional update, so two racing requests cannot both get a successor | `refresh.ts` |
29
+ | Social | Google and Apple, authorization code, zero dependency. `iss`, `aud`, `exp` and `nonce` are all checked; the signature is not, and the OIDC clause permitting that is cited in the code | `src/integrations/{google,apple}/`, `oauth.ts` |
30
+ | Linking | On the provider's subject, never the email — an address can be reassigned, a subject cannot. An address the provider has not verified is refused rather than linked | `oauth.ts` |
31
+ | Apple secret | An ES256 JWT signed per request with `dsaEncoding: "ieee-p1363"`. Node's default is DER, which Apple rejects as `invalid_client` | `integrations/apple/client.ts` |
32
+ | Enumeration | Login compares against a dummy hash when the user does not exist, so the timing does not answer "does this account exist" | `DUMMY_HASH` |
33
+ | Reset | Token hashed, one hour, and the password update, the token delete and the revocation of every session happen in one transaction | `src/features/auth/reset.ts` |
34
+
35
+ If a phase needs something different, change it in
36
+ `.orchestra/stacks/backend-hono/setup.sh` so the next project gets it too —
37
+ see `.claude/rules/dependency-upgrade.orchestra.md` on fixing the stack rather
38
+ than the project.
39
+
40
+ ## The one that gets re-opened
41
+
42
+ Somebody will propose putting the browser on a short access token with a
43
+ refresh token behind it, because that is what the OAuth material describes.
44
+ It was tried here and reverted. The reasons, so they do not have to be
45
+ rediscovered:
46
+
47
+ - The access token is **opaque and checked against a row**, not a JWT. It is
48
+ revocable now. The short expiry in an OAuth design exists to bound damage
49
+ for tokens that cannot be revoked; that problem does not exist here.
50
+ - A 15-minute cookie means **every client needs a 401-and-retry
51
+ interceptor**. Ship the server half alone and users appear signed out every
52
+ fifteen minutes.
53
+ - Both cookies would be `httpOnly`, so XSS reaches neither, and anything that
54
+ can read the cookie jar reads both. The theft-detection win is much smaller
55
+ than it looks.
56
+
57
+ Rotation is kept where it earns its complexity: a token in device storage on
58
+ a native client, which is RFC 9700's public client.
59
+
60
+ ## What is genuinely still open
61
+
62
+ These are not decided, and a phase that needs one has to design it:
63
+
64
+ - **MFA.** Nothing here is a second factor.
65
+ - **Account lockout** beyond rate limiting. Ten attempts a minute is a brake,
66
+ not a stop.
67
+ - **Unlinking a provider.** Removing the last way into an account is the case
68
+ to get right: an account with no password and no linked provider cannot be
69
+ signed into by anybody.
70
+ - **More providers.** The shape is in place; a third one is a new
71
+ `src/integrations/<provider>/` and a branch in `oauth.ts`. Check its
72
+ discovery document rather than assuming it looks like Google's — Apple
73
+ supports no PKCE and returns its callback as a cross-site POST, and both of
74
+ those had to be discovered rather than recalled.
75
+
76
+ ## Checks for any change to auth
77
+
78
+ - [ ] The route is closed by default — `src/app.routes.test.ts` calls every
79
+ declared route with no cookie and fails on anything that is not 401.
80
+ A new route is caught by that test, not by remembering.
81
+ - [ ] Failure paths return the **same** answer for unknown user, wrong
82
+ password and expired session. Different answers are an enumeration
83
+ oracle.
84
+ - [ ] Anything that changes a credential revokes existing sessions **and
85
+ refresh tokens**, in the same transaction as the change. Sessions alone
86
+ accomplishes nothing: the refresh tokens outlive them and mint new ones.
87
+ - [ ] The new path is rate limited if it accepts a secret.
88
+ - [ ] Email is normalised — lowercased and trimmed — before lookup and before
89
+ storage, or the same person gets two accounts.
90
+ - [ ] No token, hash, or password appears in a log line, an error body, or an
91
+ analytics property.
92
+ - [ ] Tests cover success, failure, expiry, and the limit actually being hit.
93
+
94
+ ## Mistakes still worth naming
95
+
96
+ - A credential comparison that returns early. Use a constant-time compare.
97
+ - Rate limiting keyed only on IP. One NAT is one key; one botnet is thousands.
98
+ - Reporting "user not found" separately from "wrong password".
99
+ - A password change that leaves other sessions alive.
100
+ - Widening a token's lifetime to fix an expiry bug. The bug is the refresh
101
+ path, not the number.
102
+ - Trusting an id_token's `sub` without checking its `aud`. Any application
103
+ registered with the same provider can mint one otherwise.
104
+ - Linking a social account to an existing user by an email address the
105
+ provider has not marked verified.
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: best-practices
3
+ description: "Web application hardening — security headers, CSP, input handling, browser compatibility. Use for any phase preparing a web app for production."
4
+ ---
5
+
6
+ # Skill: Best Practices
7
+
8
+ ## When to Use
9
+ Phase involves web application security, compatibility, or production hardening.
10
+
11
+ ## Checklist
12
+ - [ ] HTTPS everywhere — no mixed HTTP/HTTPS content
13
+ - [ ] Content Security Policy (CSP) header configured
14
+ - [ ] Security headers set: X-Frame-Options, X-Content-Type-Options, Referrer-Policy
15
+ - [ ] No vulnerable dependencies (`npm audit` clean)
16
+ - [ ] Feature detection, not browser detection (`if ('fetch' in window)` not `if (isChrome)`)
17
+ - [ ] Valid HTML5 doctype, charset first in `<head>`
18
+ - [ ] Viewport meta tag present: `<meta name="viewport" content="width=device-width, initial-scale=1">`
19
+ - [ ] Passive event listeners for scroll/touch events
20
+ - [ ] No `innerHTML` with user input — use `textContent` or sanitize
21
+ - [ ] All user input validated + sanitized on both client and server
22
+
23
+ ## Common Mistakes
24
+ - Mixed HTTP/HTTPS content → browsers block mixed requests
25
+ - XSS via `innerHTML` or `dangerouslySetInnerHTML` without sanitization
26
+ - Synchronous XHR blocking main thread → use `fetch` with async/await
27
+ - Browser detection (`navigator.userAgent`) → breaks on new browsers, use feature detection
28
+ - Missing security headers → easy attack vectors
29
+ - Using `eval()` or `new Function()` with user input → code injection
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: code-optimizer
3
+ description: "Performance work — query shape, memory, bundle, hot paths. Use for any phase that optimises existing code rather than adding behaviour."
4
+ ---
5
+
6
+ # Skill: Code Optimizer
7
+
8
+ ## When to Use
9
+ Phase involves performance optimization, refactoring for speed, or audit of existing code performance.
10
+
11
+ ## Checklist
12
+ - [ ] **Database:** N+1 queries eliminated, proper indexes on WHERE/JOIN columns, connection pooling configured
13
+ - [ ] **Memory:** No unbounded caches/arrays, streams used for large data, event listeners cleaned up
14
+ - [ ] **Algorithmic:** No nested loops over large datasets (O(n^2)), use Map/Set for lookups instead of Array.find
15
+ - [ ] **I/O:** Parallel fetch with `Promise.all` where independent, no sequential awaits for unrelated data
16
+ - [ ] **Bundle:** No barrel file imports (`import { x } from './utils'`), use direct imports for tree-shaking
17
+ - [ ] **Dead Code:** Remove unused imports, functions, files — verify with `npx tsc --noEmit` + grep
18
+ - [ ] **Caching:** Expensive computations cached (LRU, memoize), HTTP cache headers set for static assets
19
+ - [ ] **Rendering:** Avoid layout thrashing (batch DOM reads before writes), use `content-visibility: auto` for offscreen content
20
+
21
+ ## Common Mistakes
22
+ - Sequential `await` for independent promises → use `Promise.all([a(), b(), c()])`
23
+ - Importing from barrel files (`index.ts` re-exports) → prevents tree-shaking, bloats bundle
24
+ - Array.find/filter in loops → O(n*m), convert to Map/Set for O(1) lookup
25
+ - Missing database indexes on frequently queried columns → full table scans
26
+ - Unbounded in-memory caches → memory leak over time, use LRU with max size
27
+ - Not cleaning up event listeners, intervals, subscriptions → memory leak
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: core-web-vitals
3
+ description: "Frontend performance against LCP, INP and CLS. Use for any phase targeting page experience metrics or a Lighthouse score."
4
+ ---
5
+
6
+ # Skill: Core Web Vitals
7
+
8
+ ## When to Use
9
+ Phase involves frontend performance optimization targeting Google's page experience metrics.
10
+
11
+ ## Checklist
12
+ - [ ] **LCP (Largest Contentful Paint) <= 2.5s:**
13
+ - Preload LCP image with `<link rel="preload" fetchpriority="high">`
14
+ - Inline critical CSS (< 14KB)
15
+ - No render-blocking JavaScript in `<head>` (defer or async)
16
+ - Server response time (TTFB) < 800ms
17
+ - [ ] **INP (Interaction to Next Paint) <= 200ms:**
18
+ - No long tasks (> 50ms) on main thread — break up with `requestIdleCallback` or `scheduler.yield()`
19
+ - Event handlers return quickly — defer heavy work
20
+ - Avoid forced synchronous layouts (read then write DOM, not interleaved)
21
+ - [ ] **CLS (Cumulative Layout Shift) <= 0.1:**
22
+ - All images/videos have explicit `width` and `height` or `aspect-ratio`
23
+ - No content injected above existing content after load
24
+ - Fonts use `font-display: swap` with `size-adjust` to minimize shift
25
+ - Reserve space for dynamic content (ads, embeds, lazy content)
26
+
27
+ ## Common Mistakes
28
+ - Images without dimensions → causes layout shift on load
29
+ - Web fonts blocking text rendering → use `font-display: swap`
30
+ - Client-side rendering for LCP element → server-render or prerender it
31
+ - Synchronous third-party scripts in `<head>` → blocks rendering
32
+ - Dynamic banners/modals pushing content down → reserve space or use overlay
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: crud-api
3
+ description: "Creating a resource with standard create/read/update/delete routes. Use for any phase adding a new REST or RPC resource."
4
+ ---
5
+
6
+ # Skill: CRUD API
7
+
8
+ ## When to Use
9
+ Phase involves creating a new resource with standard create/read/update/delete operations.
10
+
11
+ ## Checklist
12
+ - [ ] Define resource schema (Zod or equivalent validation)
13
+ - [ ] Create migration for DB table
14
+ - [ ] Implement endpoints: POST (create), GET (list + detail), PUT/PATCH (update), DELETE
15
+ - [ ] Add input validation on all mutation endpoints
16
+ - [ ] Add pagination on list endpoint (cursor-based preferred over offset)
17
+ - [ ] Add filtering and sorting on list endpoint
18
+ - [ ] Return consistent error format (status code + message + details)
19
+ - [ ] Add proper HTTP status codes: 201 (created), 200 (success), 404 (not found), 422 (validation)
20
+ - [ ] Test all CRUD operations + edge cases (not found, duplicate, invalid input)
21
+
22
+ ## Common Mistakes
23
+ - No pagination on list endpoints (will crash with large datasets)
24
+ - Using offset pagination (slow on large tables) → use cursor-based
25
+ - Not validating request body → runtime crashes
26
+ - Returning 200 for everything → use semantic HTTP codes
27
+ - DELETE without checking ownership → authorization bypass
28
+
29
+ ## Patterns
30
+ - Schema → Type → Validation → Migration flow
31
+ - Repository pattern for DB access (separate query logic from handler)
32
+ - Consistent error response: `{ error: { code, message, details } }`
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: debug
3
+ description: "Investigating a bug whose cause is not obvious — unexpected behaviour, a production incident, a flaky test. Use when the fix is not yet known."
4
+ ---
5
+
6
+ # Skill: Debug
7
+
8
+ ## When to Use
9
+ Phase involves investigating a complex bug, unexpected behavior, or production issue that isn't immediately obvious.
10
+
11
+ ## Checklist
12
+ - [ ] **Document current state:** exact error message, steps to reproduce, expected vs actual behavior
13
+ - [ ] **Map the system:** trace the execution path from input to error — which files, functions, APIs are involved?
14
+ - [ ] **Gather evidence BEFORE hypothesizing:** read logs, check git blame, inspect network requests, check env vars
15
+ - [ ] **Form hypotheses:** list 2-3 possible causes ranked by likelihood
16
+ - [ ] **Test each hypothesis minimally:** change ONE thing, observe result, revert if wrong
17
+ - [ ] **Check external factors:** dependency versions, API changes, environment differences (dev vs prod)
18
+ - [ ] **Verify the fix:** reproduce the original bug, apply fix, confirm it's resolved, check for regressions
19
+ - [ ] **Document root cause:** what happened, why, and what prevents it from recurring
20
+
21
+ ## Common Mistakes
22
+ - Assuming the bug is where the error appears → trace upstream, the root cause is often elsewhere
23
+ - Changing multiple things at once → can't tell which change fixed it
24
+ - Not reproducing before fixing → you might fix a different bug
25
+ - Treating your own code with less skepticism → YOUR code is the most likely source
26
+ - Proposing fixes without evidence → "I think it might be X" without testing is guessing
27
+ - Not checking if the bug exists in other environments → might be env-specific
28
+ - Fixing the symptom, not the cause → bug will return in a different form
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: deployment
3
+ description: "Getting a service to production and back again — health, release gating, smoke, rollback, migrations. Use for any phase that touches CI/CD, containers, environment configuration, or a deploy."
4
+ ---
5
+
6
+ # Skill: Deployment
7
+
8
+ Implements `.claude/rules/observability.orchestra.md`. Read it first; this is
9
+ the working checklist.
10
+
11
+ ## The fact everything else follows from
12
+
13
+ **Nothing here protects a release except being able to undo it.**
14
+
15
+ There is no percentage canary on the platforms this stack targets. What
16
+ stands between a broken build and every user is three things, in order: the
17
+ healthcheck that decides whether the new deployment is promoted, a smoke test
18
+ against the real URL afterwards, and putting the last good commit back when
19
+ that smoke fails.
20
+
21
+ Rollback is the same path as a normal deploy, deliberately. An untested
22
+ recovery path is not a recovery path.
23
+
24
+ ## Health is two endpoints
25
+
26
+ | Endpoint | Question | Cost | Fails when |
27
+ |---|---|---|---|
28
+ | `/health` | is the process up? | no dependencies | it doesn't answer |
29
+ | `/ready` | can it serve? | probes every dependency | **503**, naming the one that failed |
30
+
31
+ The platform healthcheck points at **`/ready`**. This is load-bearing: the
32
+ previous deployment keeps serving until the new one answers 200, so a
33
+ readiness probe that returns 200 with an unreachable database does not
34
+ mislead a dashboard — it promotes a broken release.
35
+
36
+ - [ ] `/ready` probes every dependency the service cannot serve without
37
+ - [ ] it answers 503 and names the failed dependency, not a bare status
38
+ - [ ] there is a test that **makes it fail** — a probe that cannot fail is
39
+ worse than none
40
+ - [ ] adding a dependency adds its probe and its failing test in the same
41
+ change
42
+ - [ ] `healthcheckPath` in the platform config points at `/ready`, not
43
+ `/health`
44
+
45
+ ## Releasing
46
+
47
+ - [ ] deploy is gated on the healthcheck, with an overlap window so the old
48
+ instance drains rather than being cut
49
+ - [ ] a smoke test runs against the deployed URL, not against localhost
50
+ - [ ] the smoke failing triggers rollback, and the rollback is exercised
51
+ - [ ] the `last-good-deploy` tag moves **only after the smoke passes** — so
52
+ it always names a release observed working, never one that merely
53
+ deployed
54
+ - [ ] release and commit sha reach the error monitor and every log line
55
+
56
+ ## Environment
57
+
58
+ - [ ] every variable is read through one validating schema, and boot fails
59
+ loudly when a required one is missing
60
+ - [ ] `.env.example` is **generated from that schema**, never written beside
61
+ it — a hand-kept example is wrong the first time somebody forgets, and
62
+ a new engineer trusts it anyway
63
+ - [ ] `.env` gitignored, always
64
+ - [ ] a secret that was ever committed is rotated; deleting the commit is not
65
+ the fix
66
+
67
+ ## Migrations
68
+
69
+ - [ ] run automatically on deploy, never by hand
70
+ - [ ] forward-compatible with the previous release, because both run at once
71
+ during the overlap window
72
+ - [ ] a destructive change is two deploys: stop using the column, then drop
73
+ it
74
+
75
+ ## Containers
76
+
77
+ - [ ] multi-stage build, runtime stage carries no build tooling
78
+ - [ ] non-root user
79
+ - [ ] `.dockerignore` covers `node_modules`, `.git`, test output
80
+ - [ ] SIGTERM drains in-flight requests before exit — the overlap window is
81
+ wasted if the process dies instantly
82
+
83
+ ## Feature flags
84
+
85
+ A risky change ships dark. The lookup must never be able to take the service
86
+ down: bounded by a timeout, failures reported and answered with the flag's
87
+ own fallback rather than thrown into a handler. For a kill switch the
88
+ fallback is **false** — if we cannot ask whether a risky path is allowed, the
89
+ answer is no.
90
+
91
+ Flags live in one typed registry. A string literal at a call site is how they
92
+ rot.
93
+
94
+ ## Common mistakes
95
+
96
+ - **A readiness probe that returns a constant.** It passes review, passes
97
+ CI, and promotes a release with a dead database.
98
+ - Pointing the platform healthcheck at `/health`. Liveness answers yes while
99
+ nothing works.
100
+ - Rolling back by hand through a dashboard. The path nobody scripted is the
101
+ path nobody can run at 3am.
102
+ - Tagging the deploy as good when it deployed, rather than when it was
103
+ observed working.
104
+ - Secrets in the image, or in a build argument, which is the same thing.