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,114 @@
1
+ ---
2
+ name: project-structure
3
+ description: "Where a new file goes — the three questions that decide it, and why third-party wrappers have a fixed home. Use whenever creating a module, component, hook or utility rather than editing an existing one."
4
+ ---
5
+
6
+ # Skill: Project Structure
7
+
8
+ ## The fact everything else follows from
9
+
10
+ **"Where does this go?" answered by judgement produces a different answer
11
+ every time.**
12
+
13
+ Nobody is wrong individually. One phase puts the Stripe call in the handler,
14
+ the next puts it in `utils/`, a third writes a `PaymentService`. Six
15
+ milestones later the same concern lives in four places, and no gate noticed —
16
+ because none of it is a defect, it is drift.
17
+
18
+ So the answer is a procedure, not a preference.
19
+
20
+ ## Three questions, in order
21
+
22
+ Take the first one that is true.
23
+
24
+ | Question | Where | What follows |
25
+ |---|---|---|
26
+ | **Does it call a third party's SDK or HTTP API?** | `src/integrations/<provider>/` | Coverage held at **100%** |
27
+ | **Does more than one feature use it?** | `src/lib/` | Needs a doc comment with an example |
28
+ | **Otherwise** | `src/features/<domain>/` | Nothing outside may import its internals |
29
+
30
+ Feature-first, including on the backend. A layer-first tree — `controllers/`,
31
+ `services/`, `models/` — spreads one change across four directories and gives
32
+ you no place to draw a boundary: `services/` has no owner, so nothing stops
33
+ one domain reaching into another. See
34
+ `.claude/skills/architecture/SKILL.md` on what makes a boundary real.
35
+
36
+ **Before creating anything, look in `src/lib/` and the feature you are in.**
37
+ The duplicate that costs is the one written by someone who did not know the
38
+ first existed.
39
+
40
+ ## Why integrations has a fixed home
41
+
42
+ Two reasons, and neither of them is swappability.
43
+
44
+ **The test suite needs something to hold.** The rule is that a function
45
+ calling into a third-party library has a test, so that a dependency bump which
46
+ changes behaviour turns the suite red — that is what makes a green Dependabot
47
+ PR a merge signal. Called inline across a dozen handlers, there is no function
48
+ to test and the bump goes green.
49
+
50
+ **The policy is applied once.** Timeout, bounded retry, circuit breaker,
51
+ mapping the provider's errors into ours, keeping the key out of the logs.
52
+ Spread across a dozen call sites you will miss one, and the one you miss is
53
+ the one that hangs.
54
+
55
+ ### It is a seam, not an abstraction
56
+
57
+ | | Seam wrapper | Abstraction wrapper |
58
+ |---|---|---|
59
+ | Exists for | somewhere to test, one place for policy | pretending the vendor is interchangeable |
60
+ | Shape | thin; this app's types, this app's needs | provider-neutral, lowest common denominator |
61
+ | Justified by | the testing rule, today | a migration that may never happen |
62
+
63
+ A seam wrapper is allowed to look obviously like the provider it wraps. It
64
+ should. The abstraction version is what
65
+ `.claude/rules/code-standards.orchestra.md` forbids — one implementation, and
66
+ a guess about the future.
67
+
68
+ **Migration is the secondary benefit, and it is version upgrades, not vendor
69
+ swaps.** A major with a renamed method touches one file instead of twelve.
70
+ That happens several times a year; a vendor swap mostly never does.
71
+
72
+ ### The 100% asks for exercise, not a test file
73
+
74
+ An integration test that drives the wrapper through a faked HTTP boundary
75
+ satisfies it completely. Do not add a unit test beside the wrapper to repeat
76
+ what a route test already covers — that is writing a test to move a number,
77
+ which the testing rule tells you to refuse rather than comply with.
78
+
79
+ Fake the provider at the **network** boundary, never by mocking the wrapper.
80
+ Mocking the wrapper means the wrapper is never tested, and the wrapper is the
81
+ thing that breaks when the provider changes.
82
+
83
+ ## Components
84
+
85
+ Two levels, not four. Atoms-elements-molecules-organisms sounds precise and
86
+ is not: nobody places `FormField` the same way twice, and the folders drift
87
+ into an argument.
88
+
89
+ | Level | Contains | May import |
90
+ |---|---|---|
91
+ | **Primitives** — `src/design/` | Button, Input, Text. No business logic, no data fetching. | other primitives only |
92
+ | **Compositions** — beside the feature that uses them | Anything that knows what the app does | primitives, and their own feature |
93
+
94
+ The rule worth keeping from atomic design is the **import direction**: a
95
+ primitive never imports a composition. That one is checkable by reading a
96
+ diff. The taxonomy is not.
97
+
98
+ A component used by a second feature moves up to shared — on the second use,
99
+ not in anticipation of it.
100
+
101
+ ## Types live beside what they describe
102
+
103
+ A global `types/` becomes a file everything imports, which is the opposite of
104
+ a boundary. A type that describes a feature's data lives in that feature; a
105
+ type crossing a boundary lives with the interface that defines it.
106
+
107
+ ## Signs the structure has stopped working
108
+
109
+ - A change to one behaviour touches four directories.
110
+ - Two features import each other.
111
+ - `lib/` is the biggest directory in the app.
112
+ - The same provider's SDK is imported in more than one place.
113
+ - A file is named for a technical role — `helpers`, `managers`, `services` —
114
+ rather than for what it does.
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: push-notifications
3
+ description: "Delivering push to iOS and Android — permission timing, token lifecycle, payload choice, deep links, and why none of it can be tested in a JS runner. Use for any phase that sends, receives, or routes a notification."
4
+ ---
5
+
6
+ # Skill: Push Notifications
7
+
8
+ ## The fact everything else follows from
9
+
10
+ **Nothing in the JS test suite can tell you push works.**
11
+
12
+ `jest-expo` runs components in Node against mocked native modules, so the
13
+ permission call, the token callback, the background handler and the deep link
14
+ are all mocked into success. The suite is green and the notification never
15
+ arrives.
16
+
17
+ Push needs a build with your own credentials, on a real device, with the app
18
+ in each of three states — foreground, background, terminated. Anything less
19
+ tests the mock. This is the same reason the device layer is not optional in
20
+ `.claude/skills/mobile-testing/SKILL.md`, on Expo.
21
+
22
+ Resolve the SDK's current API and constraints from its own docs at
23
+ implementation time. This area changes across SDK releases, and a handler
24
+ signature written from memory compiles and silently never fires.
25
+
26
+ ## Permission is a product decision, not a setup step
27
+
28
+ The prompt can be shown **once**. A denial is close to permanent — the system
29
+ stops presenting the dialog, and the only route back is Settings.
30
+
31
+ | When you ask | Roughly what you get |
32
+ |---|---|
33
+ | On first launch, cold | 15–20% |
34
+ | After onboarding | 40–50% |
35
+ | At the moment the user turns on a feature that needs it | 70–80% |
36
+
37
+ So: explain the value, or better, ask at the moment the user does something
38
+ that obviously requires it. Never on launch.
39
+
40
+ - After a denial, do not re-prompt. Offer a route to Settings instead.
41
+ - iOS provisional authorisation delivers quietly with no prompt at all —
42
+ worth considering when the value is not yet obvious to the user.
43
+ - Android 13+ needs a runtime permission of its own. Treat it as a real
44
+ prompt with the same timing rules, not a manifest entry.
45
+
46
+ ## The token is a lifecycle, not a value
47
+
48
+ The single most common defect: a token cached once and never refreshed, so
49
+ delivery quietly stops for users who reinstalled.
50
+
51
+ 1. **Take it from the callback**, every launch. Never from storage as the
52
+ source of truth.
53
+ 2. **Refresh replaces it.** The refresh callback is not optional — the token
54
+ changes on reinstall, restore, and at the platform's discretion.
55
+ 3. **Key it by device, and associate a user.** One user has several devices;
56
+ one device may see several users. A token with no user cannot be targeted;
57
+ a token with no device id produces duplicates.
58
+ 4. **Delete on rejection.** When the provider says the device is not
59
+ registered, that token is dead. Keeping it means a growing share of your
60
+ sends fail, and your delivery rate becomes a number nobody trusts.
61
+ 5. **Treat it as opaque.** No parsing, no format assumptions.
62
+
63
+ A push token identifies a device belonging to a person. It never goes in a
64
+ log line, an error report, or an analytics property — see
65
+ `.claude/rules/security.orchestra.md`.
66
+
67
+ ## Payload: visible or data-only
68
+
69
+ This is the choice that decides whether your handler runs at all.
70
+
71
+ | You want | Payload | Because |
72
+ |---|---|---|
73
+ | The user to see something now | **Visible** | Delivery is prioritised and the system displays it |
74
+ | To sync in the background | **Data-only** | A visible payload is handled by the system, not your code |
75
+ | Your own notification UI | **Data-only** | You build and post it |
76
+
77
+ Two constraints that bite:
78
+
79
+ - **Silent delivery is best-effort.** The OS may delay or drop it — battery,
80
+ Doze, Low Power Mode. Never carry something time-critical this way.
81
+ - **The background handler has seconds, not minutes.** Enqueue the work and
82
+ return. Doing the work inline gets the process killed mid-write.
83
+
84
+ On Android, a notification channel must exist before anything appears at all,
85
+ and a notification icon must be white-on-transparent — a coloured one renders
86
+ as a white square, which reads as a bug in your app.
87
+
88
+ ## Foreground, background, terminated
89
+
90
+ Three states, three code paths, and they are handled in different places.
91
+ A notification that works in the foreground and does nothing from terminated
92
+ is the normal outcome of testing only one.
93
+
94
+ Deep linking is where this shows up first: opening from a terminated state
95
+ means the route is resolved **before** your navigation tree exists. Buffer
96
+ the target and apply it once navigation is ready, rather than dropping it.
97
+
98
+ Every one of the three gets tested by hand on a device before the phase
99
+ closes. There is no substitute.
100
+
101
+ ## The send side
102
+
103
+ Sending is a third-party seam, so it has a test —
104
+ `.claude/rules/testing-standards.orchestra.md`. Fake the provider **at the
105
+ HTTP boundary**, never by mocking your own `sendPush()`, because the wrapper
106
+ is exactly what breaks when the provider changes.
107
+
108
+ Treat it like any other outbound call: timeout, bounded retry with jitter on
109
+ transient failures only, and a reported failure rather than a swallowed one.
110
+ See `.claude/skills/architecture/SKILL.md`.
111
+
112
+ - **A send is not a delivery.** Accepted by the provider means queued. Record
113
+ the two separately or your funnel is fiction.
114
+ - **Batch and rate-limit your own sends.** A fan-out that hits the provider
115
+ as fast as it can is throttled, and the throttling arrives as partial
116
+ failure.
117
+ - **Never send in a loop over users inside a request handler.** Queue it.
118
+
119
+ ## Measuring it
120
+
121
+ Push is a feature with a funnel, so it is instrumented like one —
122
+ `.claude/rules/analytics-kpi.orchestra.md`:
123
+
124
+ - permission asked, granted, denied
125
+ - token registered, refreshed, rejected
126
+ - sent, delivered, opened
127
+ - and the **failure** at each step, with a reason code
128
+
129
+ Without the failure events you will know engagement dropped and never why.
130
+ No PII in any of those properties, and the token is never a property.
131
+
132
+ ## Anti-patterns
133
+
134
+ - **Asking on first launch.** You spend the one prompt you get on a user who
135
+ does not yet know what the app does.
136
+ - **Re-prompting after denial.** The system ignores it; the user does not.
137
+ - **Caching the token long-term.** Delivery stops silently for reinstalls.
138
+ - **A visible payload when you needed the background handler.** The system
139
+ displays it and your code never runs.
140
+ - **Relying on silent push for anything time-critical.** Delivery is not
141
+ guaranteed and you will not be told.
142
+ - **Heavy work in the background handler.** The process is killed partway.
143
+ - **Skipping the notification channel.** Nothing appears at all on modern
144
+ Android, with no error anywhere.
145
+ - **Shipping without testing the terminated state.** It is the state most
146
+ users are actually in.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: react-best-practices
3
+ description: "React and Next.js implementation patterns — data fetching, boundaries, imports, state. Use for any phase writing React components."
4
+ ---
5
+
6
+ # Skill: React Best Practices
7
+
8
+ ## When to Use
9
+ Phase involves React or Next.js frontend development.
10
+
11
+ ## Checklist
12
+ - [ ] **No waterfall fetches:** use `Promise.all` for parallel data, `Suspense` boundaries for streaming
13
+ - [ ] **Direct imports:** never import from barrel files (`index.ts`) — import from exact module path
14
+ - [ ] **Server components by default:** only add `"use client"` when state/effects/browser APIs needed
15
+ - [ ] **Lazy load heavy components:** `next/dynamic` or `React.lazy` for below-the-fold content
16
+ - [ ] **Memoize expensive renders:** `React.memo` for components with stable props, `useMemo` for expensive computations
17
+ - [ ] **Primitive dependencies in hooks:** avoid objects/arrays in `useEffect` deps — extract primitive values
18
+ - [ ] **No duplicate serialization:** don't pass same data as both prop and context
19
+ - [ ] **Parallel route fetching:** use `Promise.all` in `loader`/`getServerSideProps`, not sequential `await`
20
+ - [ ] **SWR/React Query for client data:** deduplication, caching, revalidation built-in
21
+ - [ ] **Passive event listeners:** `{ passive: true }` for scroll/touch handlers
22
+ - [ ] **Hoist static JSX:** constant JSX outside component body to avoid re-creation on render
23
+ - [ ] **Use `Map`/`Set` for lookups:** not `Array.find()` in render paths
24
+
25
+ ## Common Mistakes
26
+ - Sequential `await` in data fetching → blocks waterfall, use `Promise.all`
27
+ - Barrel file imports → prevents tree-shaking, entire module loaded
28
+ - `"use client"` on everything → loses server component benefits (streaming, zero bundle)
29
+ - Complex objects in `useEffect` dependencies → infinite re-render loop
30
+ - Event listeners without cleanup → memory leak in `useEffect`
31
+ - Fetching in `useEffect` without SWR/React Query → no dedup, no cache, race conditions
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: realtime
3
+ description: "Pushing updates to a connected client — choosing between polling, SSE and WebSocket, authenticating the connection, surviving reconnects, and what changes when there is more than one instance. Use for any phase adding live updates, presence, or a subscription."
4
+ ---
5
+
6
+ # Skill: Realtime
7
+
8
+ ## The fact everything else follows from
9
+
10
+ **A connection is state, and state is what makes a service hard to deploy.**
11
+
12
+ A stateless service scales by adding an instance and deploys by replacing
13
+ one. A connection breaks both: the client is pinned to an instance, a deploy
14
+ drops every connection at once, and an event published on instance A never
15
+ reaches a subscriber on instance B.
16
+
17
+ None of that is a reason to avoid realtime. It is a reason to be sure you
18
+ need it, and to decide the multi-instance answer **before** the second
19
+ instance exists — retrofitting it is a rewrite.
20
+
21
+ ## Choose the cheapest thing that works
22
+
23
+ Work down. Take the first row that is genuinely true.
24
+
25
+ | If | Use | Why |
26
+ |---|---|---|
27
+ | Updates are seconds-stale-tolerant, or rare | **Polling** | No connection state, no deploy problem, works through every proxy |
28
+ | Server → client only: feeds, progress, notifications, streamed output | **SSE** | Plain HTTP, reconnects and resumes by itself, no protocol to operate |
29
+ | Genuinely bidirectional and low latency: collaboration, presence, games | **WebSocket** | Only case that pays for the state |
30
+
31
+ Most "realtime" requirements are SSE requirements. A chat that only receives
32
+ is server-to-client. Reaching for WebSocket first buys you connection
33
+ management you did not need.
34
+
35
+ **SSE's resume is the reason to prefer it.** The browser reconnects on its
36
+ own and sends the last event id it saw; give every event an id and a client
37
+ that missed a minute can be caught up. With WebSocket you write that
38
+ yourself, and most implementations do not.
39
+
40
+ ## Authenticate the handshake
41
+
42
+ The upgrade request is the only place authorisation happens. After it, you
43
+ have a socket with no request cycle to check anything against.
44
+
45
+ - Authenticate **during** the handshake, and reject before the upgrade
46
+ completes. Never accept first and check on the first message.
47
+ - **The browser WebSocket API cannot set headers.** Pass a short-lived,
48
+ single-use ticket obtained over normal HTTP — never the session token in
49
+ the query string, which lands in every access log.
50
+ - **A token that expires mid-connection matters.** Decide now: close and let
51
+ the client reconnect, or re-authenticate in-band. Both are fine. Neither
52
+ happening is a connection authorised by a credential that expired hours ago.
53
+ - Authorise **per subscription**, not once at connect. Joining a room is an
54
+ authorisation decision, and it is the one most often forgotten.
55
+
56
+ ## Assume every connection dies
57
+
58
+ Because they do — a tunnel closes, a phone changes network, a proxy times out
59
+ an idle socket, or you deployed.
60
+
61
+ - **Heartbeat both ways.** A TCP connection can be dead for minutes while
62
+ both sides believe it is open. Ping, expect a pong, close when it does not
63
+ arrive.
64
+ - **The client reconnects with exponential backoff and jitter.** Without
65
+ jitter, a deploy disconnects everyone at once and they all return at once —
66
+ you knock over the instance that just came up.
67
+ - **Reconnect is not resume.** State the recovery: replay from the last event
68
+ id, or refetch and replace. "The client will have missed some messages" is
69
+ a bug that reads as flakiness.
70
+ - **Cap the retries, then say so.** A client that reconnects forever in the
71
+ background is a battery complaint. Surface a disconnected state and let the
72
+ user retry.
73
+
74
+ ## Bound everything
75
+
76
+ An open socket is an invitation, and none of these have a safe default.
77
+
78
+ - Maximum message size, enforced on receive.
79
+ - Maximum messages per second, per connection — rate limiting at the socket,
80
+ not just at the HTTP edge.
81
+ - Maximum connections per user, and per instance.
82
+ - Maximum subscriptions per connection.
83
+ - A send queue that **drops or disconnects** when the client stops reading,
84
+ rather than buffering until the process runs out of memory. This is the
85
+ failure that takes the service down, and it looks like a memory leak.
86
+
87
+ Never do synchronous work per message on the event loop. One expensive
88
+ handler stalls every other connection on that instance.
89
+
90
+ ## More than one instance
91
+
92
+ The moment there are two, an event published on one has to reach subscribers
93
+ on the other. Pick the mechanism deliberately:
94
+
95
+ - **A pub/sub backplane** — every instance subscribes, every instance
96
+ forwards to its own connections. Simple, and the usual answer.
97
+ - **A dedicated realtime service** — worth it when the connection count and
98
+ the request load have genuinely different shapes.
99
+
100
+ Either way the backplane is now a dependency the service cannot serve
101
+ without, so **it gets a readiness probe** — see
102
+ `.claude/rules/observability.orchestra.md`. A service accepting connections
103
+ it can never deliver to is worse than one refusing them.
104
+
105
+ Sticky sessions are a workaround, not a design. They break on deploy, they
106
+ unbalance load, and they hide the problem until the day you scale.
107
+
108
+ ## Deploys disconnect everybody
109
+
110
+ This is normal and it is survivable, if it is designed:
111
+
112
+ - Close connections **gracefully** on SIGTERM — send a close frame so clients
113
+ reconnect on purpose instead of timing out. The draining window exists for
114
+ this; see `.claude/skills/deployment/SKILL.md`.
115
+ - Client backoff with jitter spreads the return.
116
+ - The reconnect path is the same one used after a network drop, so it is
117
+ already tested.
118
+
119
+ ## Testing it
120
+
121
+ - The **protocol** is a third-party seam: connect, authenticate, subscribe,
122
+ receive, reconnect — tested against a real server, not a mocked client.
123
+ `.claude/rules/testing-standards.orchestra.md`.
124
+ - The **failure paths** are the point: rejected handshake, expired
125
+ credential, dropped connection mid-stream, a subscription the user is not
126
+ allowed to make.
127
+ - **Never `sleep` and assert.** Wait for the event; a sleep is a flake with a
128
+ delay.
129
+ - Instrument connect, disconnect with reason, reconnect, and messages
130
+ dropped. A realtime feature with no disconnect-reason metric cannot be
131
+ debugged from the outside.
132
+
133
+ ## Anti-patterns
134
+
135
+ - **WebSocket where SSE would do.** You now operate a protocol for a feature
136
+ that only pushes.
137
+ - **Authenticating after the upgrade.** The connection existed before anyone
138
+ checked.
139
+ - **The session token in the query string.** It is in the access log of every
140
+ hop.
141
+ - **No heartbeat.** Dead connections accumulate and are counted as users.
142
+ - **Unbounded server-side buffering.** Reads as a memory leak, is actually a
143
+ slow client.
144
+ - **Broadcasting to everyone and filtering on the client.** Every client
145
+ receives data it is not allowed to see; the filter is decoration.
146
+ - **Assuming one instance.** Works perfectly until the day it matters.
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: review
3
+ description: "Reviewing a diff — as the reviewer role, or self-review before submitting. Use for any phase that reviews code rather than writing it."
4
+ ---
5
+
6
+ # Skill: Review
7
+
8
+ ## Review what the gate cannot
9
+
10
+ By the time a diff exists, a great deal has already been decided
11
+ mechanically. Re-checking any of it spends the only thing a reader has that
12
+ a script does not.
13
+
14
+ **Already enforced. Do not spend attention here:**
15
+
16
+ | Concern | Enforced by |
17
+ |---|---|
18
+ | Secrets, in the diff or anywhere in history | gitleaks |
19
+ | String-built SQL | `scripts/policy-check.mjs` |
20
+ | Empty or unreported `catch` | `scripts/policy-check.mjs` |
21
+ | `getByTestId` in a test | `scripts/policy-check.mjs` |
22
+ | A mocked database | `scripts/policy-check.mjs` |
23
+ | Unused imports, dead code, `console.log` | biome |
24
+ | Contrast, ARIA, missing labels | axe, in component tests and e2e |
25
+ | Coverage of the lines this change touched | the diff gate |
26
+ | Anything wrapping a dependency, at 100% | the seam gate |
27
+ | Bundle growth | the bundle ratchet |
28
+ | It compiles, bundles, boots | typecheck, build, e2e |
29
+ | A known critical vulnerability | `yarn npm audit` in CI |
30
+
31
+ If one of those is wrong the commit does not exist — the gate blocked it.
32
+ Reporting it anyway tells the author you did not know that.
33
+
34
+ ## Yours alone
35
+
36
+ Every item here needs a reader.
37
+
38
+ - [ ] **It does what the phase asked.** Re-read the acceptance criteria
39
+ against the diff. Working code that solves a different problem is the
40
+ most expensive defect there is, and no gate detects it.
41
+ - [ ] **The error handling is meaningful, not merely present.** A check
42
+ proves the `catch` is not empty. Only a reader can tell whether the
43
+ user gets a correct outcome or the failure was made invisible.
44
+ - [ ] **The tests assert behaviour.** Coverage proves a line ran. Read the
45
+ assertions: a test that calls a function and checks nothing about what
46
+ it did passes every gate we have.
47
+ - [ ] **Authorization is on the route**, not in a helper a future route can
48
+ forget to call. Named as unmechanizable in
49
+ `.claude/rules/security.orchestra.md` — so it is yours.
50
+ - [ ] **No PII in logs, errors or analytics.** Redaction covers known keys;
51
+ a new field carrying a person's name is invisible to it.
52
+ - [ ] **Query shape.** N+1, a missing index, a transaction that should exist
53
+ and does not.
54
+ - [ ] **Readiness probes what it claims.** A new dependency without a probe
55
+ is a broken release that gets promoted.
56
+ - [ ] **The abstraction earns itself.** Extracted after the second
57
+ duplication, not before. Was a simpler alternative available?
58
+ - [ ] **Loading, empty and failure states exist** for anything a user waits
59
+ on.
60
+ - [ ] **Anything that will be wrong later.** A pinned version with no reason
61
+ written beside it, a TODO with no owner, a workaround presented as a
62
+ fix.
63
+
64
+ ## Severity
65
+
66
+ Defined once, in `.claude/agents/reviewer.md` under Severity — Blocking,
67
+ Important, Suggestion, and which of them hold the pull request. Read it
68
+ there; a second copy here drifted from it before.
69
+
70
+ ## Common mistakes
71
+
72
+ - Reviewing the diff without opening the file. Context-dependent bugs live
73
+ in the lines that did not change.
74
+ - Re-reporting something a gate already decided.
75
+ - Nitpicking style a formatter owns.
76
+ - A vague finding. Say what, where, and why.
77
+
78
+ ## Never ask for a test to be loosened
79
+
80
+ Nor a threshold lowered, nor an ignore added. If a gate is wrong, that is a
81
+ finding **about the gate** — a change to `.orchestra/stacks/`, not to the
82
+ project under review.
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: testing
3
+ description: "The working checklist for writing tests. Strategy lives in the testing-standards rule; each platform has its own skill. Use for any phase that writes or changes tests."
4
+ ---
5
+
6
+ # Skill: Testing
7
+
8
+ ## When to Use
9
+ Phase involves writing tests or improving coverage. The strategy lives in
10
+ `.claude/rules/testing-standards.orchestra.md`; this is the working checklist.
11
+
12
+ ## Checklist
13
+ - [ ] **Detect framework:** check package.json for vitest, jest, pytest — use the project's
14
+ - [ ] **Read existing tests:** match style, naming, and file organisation
15
+ - [ ] **Pick the right layer:** pure logic → unit. Anything touching I/O → integration through the real entry point
16
+ - [ ] **Real database, never a mocked driver:** a fake repository returns what you told it to and cannot tell you the query is wrong
17
+ - [ ] **Fake external services at the network boundary** (MSW or a stub server), never by mocking our own wrapper — the wrapper is the thing that breaks on a provider change
18
+ - [ ] **Happy path, edge cases, and failures:** empty, null, boundary values, and the error branch with its reason
19
+ - [ ] **Async behaviour:** resolve and reject, timeouts, ordering
20
+ - [ ] **One assertion per concept:** the test name states the scenario
21
+ - [ ] **Run them, then break the code and confirm they fail** — a test that has never failed has not been tested
22
+ - [ ] **Isolation:** each test creates its own data; no shared seed fixture, no ordering dependency
23
+
24
+ ## Common Mistakes
25
+ - Mocking the database → you test the mock. Use a real one in a container.
26
+ - Mocking your own module instead of the network → the seam goes untested.
27
+ - Snapshot tests as coverage → a snapshot passes while a handler stops firing.
28
+ - Writing tests to move the coverage number → the gate has started doing harm; say so.
29
+ - Testing implementation details → assert behaviour, not internal calls.
30
+ - Imposing a new style on an existing project → match what is there.
31
+
32
+ ## Related
33
+
34
+ Each platform has a different shape, and the differences are not stylistic —
35
+ they come from what the test runner can and cannot see. Only the skills for
36
+ this project's `project.platforms` are installed.
37
+
38
+ - Web: `.claude/skills/web-testing/SKILL.md` — jsdom has no layout engine, so
39
+ component tests run in real Chromium.
40
+ - Mobile: `.claude/skills/mobile-testing/SKILL.md` — jest does not run React
41
+ Native; the device layer is not optional.
42
+ - iOS: `.claude/skills/ios-testing/SKILL.md` — a green unit suite does not
43
+ prove the app launches.
44
+ - Android: `.claude/skills/android-testing/SKILL.md` — a passing debug build
45
+ proves little about the release build.
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: web-quality-audit
3
+ description: "End-to-end quality review of a web app — performance, accessibility, SEO, best practices. Use when auditing an existing app rather than building a feature."
4
+ ---
5
+
6
+ # Skill: Web Quality Audit
7
+
8
+ ## When to Use
9
+ Phase involves comprehensive quality review of a web application — performance, accessibility, SEO, and best practices.
10
+
11
+ ## Checklist
12
+
13
+ ### Performance
14
+ - [ ] Core Web Vitals pass (LCP <= 2.5s, INP <= 200ms, CLS <= 0.1)
15
+ - [ ] Images compressed and properly sized (WebP/AVIF preferred)
16
+ - [ ] JavaScript minified and tree-shaken
17
+ - [ ] Critical CSS inlined, non-critical deferred
18
+ - [ ] Lazy loading for below-the-fold images and components
19
+ - [ ] Caching headers set for static assets (Cache-Control, ETag)
20
+
21
+ ### Accessibility
22
+ - [ ] WCAG 2.1 AA compliant (run axe or similar audit)
23
+ - [ ] Keyboard navigation works for all interactive elements
24
+ - [ ] Screen reader tested (proper headings, landmarks, alt text)
25
+ - [ ] Color contrast ratios meet minimums
26
+
27
+ ### SEO
28
+ - [ ] Unique `<title>` and `<meta name="description">` per page
29
+ - [ ] `robots.txt` present and correct
30
+ - [ ] Sitemap.xml generated and submitted
31
+ - [ ] Structured data (JSON-LD) for key content types
32
+ - [ ] Canonical URLs set for duplicate content
33
+ - [ ] Open Graph and Twitter Card meta tags present
34
+
35
+ ### Best Practices
36
+ - [ ] HTTPS everywhere, no mixed content
37
+ - [ ] No vulnerable dependencies (`npm audit`)
38
+ - [ ] Error pages (404, 500) are user-friendly
39
+ - [ ] Console has no errors or warnings in production
40
+ - [ ] No deprecated APIs used
41
+
42
+ ## Common Mistakes
43
+ - Optimizing only one metric → all four areas matter for real quality
44
+ - Ignoring SEO on SPAs → search engines need server-rendered content or prerendering
45
+ - No structured data → misses rich snippets in search results
46
+ - Performance testing only on fast networks → test on 3G/slow 4G too
47
+ - Missing error pages → users see raw error messages