@soulcraft/sdk 1.0.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 (149) hide show
  1. package/dist/client/index.d.ts +62 -0
  2. package/dist/client/index.d.ts.map +1 -0
  3. package/dist/client/index.js +60 -0
  4. package/dist/client/index.js.map +1 -0
  5. package/dist/index.d.ts +33 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +17 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/modules/ai/index.d.ts +55 -0
  10. package/dist/modules/ai/index.d.ts.map +1 -0
  11. package/dist/modules/ai/index.js +263 -0
  12. package/dist/modules/ai/index.js.map +1 -0
  13. package/dist/modules/ai/types.d.ts +216 -0
  14. package/dist/modules/ai/types.d.ts.map +1 -0
  15. package/dist/modules/ai/types.js +30 -0
  16. package/dist/modules/ai/types.js.map +1 -0
  17. package/dist/modules/auth/backchannel.d.ts +85 -0
  18. package/dist/modules/auth/backchannel.d.ts.map +1 -0
  19. package/dist/modules/auth/backchannel.js +168 -0
  20. package/dist/modules/auth/backchannel.js.map +1 -0
  21. package/dist/modules/auth/config.d.ts +122 -0
  22. package/dist/modules/auth/config.d.ts.map +1 -0
  23. package/dist/modules/auth/config.js +158 -0
  24. package/dist/modules/auth/config.js.map +1 -0
  25. package/dist/modules/auth/middleware.d.ts +146 -0
  26. package/dist/modules/auth/middleware.d.ts.map +1 -0
  27. package/dist/modules/auth/middleware.js +204 -0
  28. package/dist/modules/auth/middleware.js.map +1 -0
  29. package/dist/modules/auth/types.d.ts +162 -0
  30. package/dist/modules/auth/types.d.ts.map +1 -0
  31. package/dist/modules/auth/types.js +14 -0
  32. package/dist/modules/auth/types.js.map +1 -0
  33. package/dist/modules/billing/types.d.ts +7 -0
  34. package/dist/modules/billing/types.d.ts.map +1 -0
  35. package/dist/modules/billing/types.js +7 -0
  36. package/dist/modules/billing/types.js.map +1 -0
  37. package/dist/modules/brainy/auth.d.ts +104 -0
  38. package/dist/modules/brainy/auth.d.ts.map +1 -0
  39. package/dist/modules/brainy/auth.js +144 -0
  40. package/dist/modules/brainy/auth.js.map +1 -0
  41. package/dist/modules/brainy/errors.d.ts +118 -0
  42. package/dist/modules/brainy/errors.d.ts.map +1 -0
  43. package/dist/modules/brainy/errors.js +142 -0
  44. package/dist/modules/brainy/errors.js.map +1 -0
  45. package/dist/modules/brainy/events.d.ts +63 -0
  46. package/dist/modules/brainy/events.d.ts.map +1 -0
  47. package/dist/modules/brainy/events.js +14 -0
  48. package/dist/modules/brainy/events.js.map +1 -0
  49. package/dist/modules/brainy/proxy.d.ts +48 -0
  50. package/dist/modules/brainy/proxy.d.ts.map +1 -0
  51. package/dist/modules/brainy/proxy.js +95 -0
  52. package/dist/modules/brainy/proxy.js.map +1 -0
  53. package/dist/modules/brainy/types.d.ts +83 -0
  54. package/dist/modules/brainy/types.d.ts.map +1 -0
  55. package/dist/modules/brainy/types.js +21 -0
  56. package/dist/modules/brainy/types.js.map +1 -0
  57. package/dist/modules/events/index.d.ts +41 -0
  58. package/dist/modules/events/index.d.ts.map +1 -0
  59. package/dist/modules/events/index.js +53 -0
  60. package/dist/modules/events/index.js.map +1 -0
  61. package/dist/modules/events/types.d.ts +129 -0
  62. package/dist/modules/events/types.d.ts.map +1 -0
  63. package/dist/modules/events/types.js +32 -0
  64. package/dist/modules/events/types.js.map +1 -0
  65. package/dist/modules/formats/types.d.ts +7 -0
  66. package/dist/modules/formats/types.d.ts.map +1 -0
  67. package/dist/modules/formats/types.js +7 -0
  68. package/dist/modules/formats/types.js.map +1 -0
  69. package/dist/modules/hall/types.d.ts +56 -0
  70. package/dist/modules/hall/types.d.ts.map +1 -0
  71. package/dist/modules/hall/types.js +16 -0
  72. package/dist/modules/hall/types.js.map +1 -0
  73. package/dist/modules/kits/types.d.ts +7 -0
  74. package/dist/modules/kits/types.d.ts.map +1 -0
  75. package/dist/modules/kits/types.js +7 -0
  76. package/dist/modules/kits/types.js.map +1 -0
  77. package/dist/modules/license/types.d.ts +7 -0
  78. package/dist/modules/license/types.d.ts.map +1 -0
  79. package/dist/modules/license/types.js +7 -0
  80. package/dist/modules/license/types.js.map +1 -0
  81. package/dist/modules/notifications/types.d.ts +7 -0
  82. package/dist/modules/notifications/types.d.ts.map +1 -0
  83. package/dist/modules/notifications/types.js +7 -0
  84. package/dist/modules/notifications/types.js.map +1 -0
  85. package/dist/modules/skills/index.d.ts +60 -0
  86. package/dist/modules/skills/index.d.ts.map +1 -0
  87. package/dist/modules/skills/index.js +253 -0
  88. package/dist/modules/skills/index.js.map +1 -0
  89. package/dist/modules/skills/types.d.ts +127 -0
  90. package/dist/modules/skills/types.d.ts.map +1 -0
  91. package/dist/modules/skills/types.js +23 -0
  92. package/dist/modules/skills/types.js.map +1 -0
  93. package/dist/modules/versions/types.d.ts +31 -0
  94. package/dist/modules/versions/types.d.ts.map +1 -0
  95. package/dist/modules/versions/types.js +9 -0
  96. package/dist/modules/versions/types.js.map +1 -0
  97. package/dist/modules/vfs/types.d.ts +26 -0
  98. package/dist/modules/vfs/types.d.ts.map +1 -0
  99. package/dist/modules/vfs/types.js +11 -0
  100. package/dist/modules/vfs/types.js.map +1 -0
  101. package/dist/server/create-sdk.d.ts +70 -0
  102. package/dist/server/create-sdk.d.ts.map +1 -0
  103. package/dist/server/create-sdk.js +125 -0
  104. package/dist/server/create-sdk.js.map +1 -0
  105. package/dist/server/hall-handlers.d.ts +195 -0
  106. package/dist/server/hall-handlers.d.ts.map +1 -0
  107. package/dist/server/hall-handlers.js +239 -0
  108. package/dist/server/hall-handlers.js.map +1 -0
  109. package/dist/server/handlers.d.ts +216 -0
  110. package/dist/server/handlers.d.ts.map +1 -0
  111. package/dist/server/handlers.js +214 -0
  112. package/dist/server/handlers.js.map +1 -0
  113. package/dist/server/index.d.ts +52 -0
  114. package/dist/server/index.d.ts.map +1 -0
  115. package/dist/server/index.js +50 -0
  116. package/dist/server/index.js.map +1 -0
  117. package/dist/server/instance-pool.d.ts +299 -0
  118. package/dist/server/instance-pool.d.ts.map +1 -0
  119. package/dist/server/instance-pool.js +359 -0
  120. package/dist/server/instance-pool.js.map +1 -0
  121. package/dist/transports/http.d.ts +86 -0
  122. package/dist/transports/http.d.ts.map +1 -0
  123. package/dist/transports/http.js +134 -0
  124. package/dist/transports/http.js.map +1 -0
  125. package/dist/transports/local.d.ts +76 -0
  126. package/dist/transports/local.d.ts.map +1 -0
  127. package/dist/transports/local.js +101 -0
  128. package/dist/transports/local.js.map +1 -0
  129. package/dist/transports/sse.d.ts +99 -0
  130. package/dist/transports/sse.d.ts.map +1 -0
  131. package/dist/transports/sse.js +192 -0
  132. package/dist/transports/sse.js.map +1 -0
  133. package/dist/transports/transport.d.ts +68 -0
  134. package/dist/transports/transport.d.ts.map +1 -0
  135. package/dist/transports/transport.js +14 -0
  136. package/dist/transports/transport.js.map +1 -0
  137. package/dist/transports/ws.d.ts +135 -0
  138. package/dist/transports/ws.d.ts.map +1 -0
  139. package/dist/transports/ws.js +331 -0
  140. package/dist/transports/ws.js.map +1 -0
  141. package/dist/types.d.ts +152 -0
  142. package/dist/types.d.ts.map +1 -0
  143. package/dist/types.js +8 -0
  144. package/dist/types.js.map +1 -0
  145. package/docs/ADR-001-sdk-design.md +282 -0
  146. package/docs/IMPLEMENTATION-PLAN.md +708 -0
  147. package/docs/USAGE.md +646 -0
  148. package/docs/kit-sdk-guide.md +474 -0
  149. package/package.json +61 -0
@@ -0,0 +1,282 @@
1
+ # ADR-001: @soulcraft/sdk — Architecture and Design Decisions
2
+
3
+ **Status:** Accepted
4
+ **Date:** 2026-02-27
5
+ **Decision session:** Workshop session, informed by cross-project research across
6
+ Workshop, Venue, Academy, and the existing `@soulcraft/brainy-client` and
7
+ `@soulcraft/auth` packages.
8
+
9
+ ---
10
+
11
+ ## Context
12
+
13
+ The Soulcraft platform consists of four products (Workshop, Venue, Academy, Portal) and
14
+ a growing ecosystem of kit apps deployed on those products. As of 2026-02-27:
15
+
16
+ - `@soulcraft/brainy-client` provides transport-level Brainy access (HTTP/WS/Local)
17
+ - `@soulcraft/auth` provides shared session types and auth utilities
18
+ - Each product duplicates significant auth, licensing, billing, and AI-integration logic
19
+ - Kit apps injected into Venue or Workshop lack a standard API surface
20
+
21
+ The proliferation of per-product implementations of the same concerns (auth middleware,
22
+ Cortex activation, usage metering, AI model selection) creates drift and bugs. A unified
23
+ SDK is the correct architectural response.
24
+
25
+ ---
26
+
27
+ ## Decision 1: Scope — Absorb, Not Wrap
28
+
29
+ **Decision:** `@soulcraft/sdk` absorbs all code from `@soulcraft/brainy-client` and
30
+ `@soulcraft/auth`. Both packages are deprecated immediately. No backward-compat re-export
31
+ shims. All imports in all products are updated in the same session that ships the SDK.
32
+
33
+ **Rejected alternative:** SDK wraps brainy-client + auth internally, keeping them as
34
+ independent packages. Rejected because it creates three packages to maintain and the
35
+ abstraction boundary adds no value — the SDK is the new contract.
36
+
37
+ ---
38
+
39
+ ## Decision 2: Uniform API Surface — Server and Client Modes
40
+
41
+ **Decision:** Both server and client modes expose identical `sdk.*` method namespaces.
42
+ Server mode runs Brainy in-process. Client mode serializes calls over a transport.
43
+ App code written against the SDK works in both contexts without modification.
44
+
45
+ ```typescript
46
+ // Same code works in server mode (in-process) and client mode (over HTTP):
47
+ async function listConcepts(sdk: SoulcraftSDK) {
48
+ return sdk.brainy.find({ where: { type: NounType.Concept } })
49
+ }
50
+ ```
51
+
52
+ **Rejected alternative:** Separate entry points with different API shapes. Rejected
53
+ because it forces app authors to branch on context — the entire value of the SDK is
54
+ that they don't have to.
55
+
56
+ ---
57
+
58
+ ## Decision 3: Entry Points — Conditional Exports
59
+
60
+ **Decision:** Three conditional exports for proper tree-shaking and bundle safety:
61
+
62
+ | Export | Who uses it |
63
+ |--------|-------------|
64
+ | `@soulcraft/sdk` | Shared types + `createSDK` factory |
65
+ | `@soulcraft/sdk/server` | Product backends — Brainy lifecycle, Cortex, middleware |
66
+ | `@soulcraft/sdk/client` | Browser kit apps — transports, SSE, cookie auth |
67
+
68
+ **Rationale:** Server-only code (Brainy core, Cortex, better-auth) must never land in
69
+ browser bundles. Conditional exports enforce this at the package level. Bundlers tree-shake
70
+ anything not reachable from the imported entry point.
71
+
72
+ ---
73
+
74
+ ## Decision 4: Server Mode — Full Instance Lifecycle Management
75
+
76
+ **Decision:** SDK server mode creates and manages Brainy instances, replacing
77
+ per-product patterns (Workshop's `brainy-singleton.ts`, Venue's `tenantCache`, etc.).
78
+ Three built-in instance strategies:
79
+
80
+ | Strategy | Key function | Use case |
81
+ |----------|-------------|----------|
82
+ | `per-user` | `userId` | Workshop — one Brainy per authenticated user |
83
+ | `per-tenant` | `tenantSlug` | Venue — LRU cache max 50, flush-on-evict |
84
+ | `per-scope` | Custom function | Academy or bespoke scenarios |
85
+
86
+ All strategies include: LRU cache, configurable max instances, flush-on-evict,
87
+ graceful shutdown with `sdk.shutdown()`.
88
+
89
+ ---
90
+
91
+ ## Decision 5: Transports — Four Protocols, Fixed Serialization
92
+
93
+ **Decision:** Four transports are supported:
94
+
95
+ | Transport | Serialization | Direction | Use case |
96
+ |-----------|--------------|-----------|---------|
97
+ | `local` | None (in-process) | — | Server mode, zero overhead |
98
+ | `http` | JSON | Request/response | Stateless RPC — kit apps, simple clients |
99
+ | `ws` | MessagePack binary | Bidirectional | Real-time — RPC + change push events |
100
+ | `sse` | text/event-stream | Server → Client | Live updates — VFS/entity events |
101
+
102
+ Serialization is fixed per transport and never configurable. HTTP=JSON because it is
103
+ universally debuggable. WebSocket=MessagePack because it is ~40% smaller for
104
+ high-frequency bidirectional traffic and the existing protocol is already MessagePack.
105
+
106
+ **Rejected alternatives:**
107
+ - MessagePack over HTTP: non-standard, harder to debug, no significant benefit at typical HTTP RPC volumes.
108
+ - Configurable serializer: unnecessary complexity.
109
+ - gRPC/Protobuf: over-engineered for the current scale.
110
+
111
+ ---
112
+
113
+ ## Decision 6: Full Brainy API — No Curation
114
+
115
+ **Decision:** `sdk.brainy.*` exposes every public method on the Brainy class. No method
116
+ blocklist at the SDK level. Permission scoping (blocking `clear`, `migrate` for kit apps)
117
+ is a server-side concern configured at instance creation, not baked into the SDK types.
118
+
119
+ **Rationale:** Curating a subset means every new Brainy feature requires an SDK update
120
+ before it can be used. The SDK is the platform — it should never be the bottleneck.
121
+
122
+ ---
123
+
124
+ ## Decision 7: VFS as First-Class Namespace
125
+
126
+ **Decision:** Virtual filesystem operations are exposed as `sdk.vfs.*`, not nested under
127
+ `sdk.brainy.vfs.*`. VFS is a first-class concern — it stores documents, slides,
128
+ visualizations, and all Soulcraft content formats. Hiding it under brainy implies it is
129
+ an implementation detail, which is wrong.
130
+
131
+ ```typescript
132
+ sdk.brainy.find(...) // Entity/graph operations
133
+ sdk.vfs.readFile(...) // File operations — same SDK, different namespace
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Decision 8: Auth — OIDC-Centralized in Production, Standalone for Dev
139
+
140
+ **Decision:** `auth.soulcraft.com` is the single auth source for all production deployments
141
+ of Workshop, Venue, Academy, and Portal. Products are pre-registered OIDC clients.
142
+ Cross-subdomain cookies (`domain=.soulcraft.com`) provide seamless SSO.
143
+
144
+ In local development (when `SOULCRAFT_IDP_URL` is unset), better-auth runs standalone
145
+ with a local SQLite database. This is the intended dev default.
146
+
147
+ **Rationale:** `auth.soulcraft.com` is live and running in production. All three products
148
+ switch to OIDC when `SOULCRAFT_IDP_URL=https://auth.soulcraft.com` is set in the
149
+ environment. Standalone mode is kept for two reasons:
150
+ 1. Local dev requires no internet connection, no OAuth credentials, and no prior setup.
151
+ 2. `auth.soulcraft.com` itself needs to be testable without self-dependency.
152
+
153
+ The `SOULCRAFT_IDP_URL` env var is the clean, zero-ambiguity boundary. It is always
154
+ set in all production and staging environments. It is never set in local dev.
155
+
156
+ **SDK provides:**
157
+ - `getAuthMode()` — returns `'standalone'` or `'oidc-client'` based on env
158
+ - `getOIDCClientConfig()` — reads OIDC config from env, validates required vars
159
+ - `createAuthMiddleware(auth)` — Hono `requireAuth` / `optionalAuth` middleware
160
+ - `createBackchannelLogoutHandler(config)` — OIDC backchannel logout endpoint factory
161
+ - `SOULCRAFT_USER_FIELDS`, `SOULCRAFT_SESSION_CONFIG` — shared better-auth config
162
+ - `computeEmailHash(email)` — SHA-256 digest for Brainy path isolation
163
+ - Capability tokens for cross-product server-to-server RPC auth
164
+
165
+ ---
166
+
167
+ ## Decision 9: Module Scope — Core Platform + Billing + Notifications
168
+
169
+ **Decision:** v1.0 includes:
170
+
171
+ **In SDK:**
172
+ - `sdk.brainy.*` — data graph
173
+ - `sdk.vfs.*` — filesystem
174
+ - `sdk.versions.*` — version history
175
+ - `sdk.auth.*` — auth + sessions + capability tokens
176
+ - `sdk.license.*` — Cortex activation + plans + credits + BYOK
177
+ - `sdk.ai.*` — Claude model tiers, delegator, tool definitions, Briggy companion API
178
+ - `sdk.events.*` — platform event bus (30+ event types)
179
+ - `sdk.skills.*` — VFS-backed skill system
180
+ - `sdk.kits.*` — kit loader and initialization
181
+ - `sdk.formats.*` — Soulcraft format types (WVIZ, WDOC, WSLIDE, WQUIZ)
182
+ - `sdk.billing.*` — usage metering, quotas, top-ups, Stripe subscriptions
183
+ - `sdk.notifications.*` — Postmark email + Twilio SMS + dev console
184
+
185
+ **Stays in product (not in SDK):**
186
+ - Academy pedagogical engine (enrollment, certification, socratic tutor, gamification)
187
+ - Venue POS/booking/calendar/gift card flows
188
+ - Workshop publish/deploy flows
189
+ - Product-specific AI tool definitions (beyond shared types)
190
+
191
+ **Rationale:** Billing and notifications have patterns shared across products (usage
192
+ metering is structurally identical across Workshop, Venue, and Academy; the
193
+ NotificationSender interface is already provider-agnostic). Academy's pedagogical engine
194
+ is deeply domain-specific and has no cross-product value.
195
+
196
+ ---
197
+
198
+ ## Decision 10: Licensing — Full Pipeline in SDK
199
+
200
+ **Decision:** `sdk.license.*` handles the complete Cortex + plan lifecycle:
201
+ 1. Read activation code from `.soulcraft.json`
202
+ 2. Exchange with Portal's license server for JWT
203
+ 3. Activate Cortex native SIMD/mmap providers
204
+ 4. Cache JWT locally in `.soulcraft/` directory
205
+ 5. Background heartbeat every 4 hours
206
+ 6. Expose plan info: `sdk.license.plan`, `sdk.license.isActive`, `sdk.license.features`
207
+ 7. AI usage: `sdk.license.checkAICredits(userId)`, `sdk.license.consumeAICredit(userId)`
208
+ 8. BYOK: `sdk.license.validateBYOK(key)`, `sdk.license.getAIProvider(userId)`
209
+
210
+ **Rationale:** Today every product duplicates the `.soulcraft.json` → Cortex activation
211
+ flow. Centralizing it eliminates drift and ensures the heartbeat + JWT cache pattern is
212
+ consistent everywhere.
213
+
214
+ ---
215
+
216
+ ## Implementation Plan
217
+
218
+ ### Phase 1 — Foundation (new repo + package scaffold)
219
+ - [x] Create `soulcraftlabs/sdk` GitHub repo
220
+ - [x] Initialize TypeScript + Bun package with conditional exports
221
+ - [x] Write this ADR and CLAUDE.md
222
+ - [ ] Scaffold `src/` directory structure (empty modules with JSDoc stubs)
223
+ - [ ] Write `tsconfig.json`, `vitest.config.ts`, `.env.example`
224
+
225
+ ### Phase 2 — Core Data Layer
226
+ - [ ] `src/transports/` — migrate Local, HTTP, WS from brainy-client; add SSE
227
+ - [ ] `src/modules/brainy/` — full Brainy API proxy (all methods, all sub-APIs)
228
+ - [ ] `src/modules/vfs/` — VFS namespace (delegates to `brain.vfs.*`)
229
+ - [ ] `src/modules/versions/` — versions namespace (delegates to `brain.versions.*`)
230
+ - [ ] `src/types.ts` — core shared types (SoulcraftSDK, SDKOptions, etc.)
231
+ - [ ] Server mode instance pool (`per-user`, `per-tenant`, `per-scope`)
232
+ - [ ] Tests: all transport modes, all brainy methods via proxy
233
+
234
+ ### Phase 3 — Auth + License
235
+ - [ ] `src/modules/auth/` — OIDC client config, session cache, capability tokens,
236
+ Hono middleware, SvelteKit handle, backchannel logout
237
+ - [ ] `src/modules/license/` — Cortex activation, plan verification, credit metering,
238
+ BYOK validation, heartbeat background task
239
+ - [ ] Absorb `@soulcraft/auth` package entirely
240
+ - [ ] Tests: auth middleware, capability token create/verify, license exchange
241
+
242
+ ### Phase 4 — AI + Events + Skills + Kits + Formats
243
+ - [ ] `src/modules/ai/` — model tiers, delegator, Briggy API, tool definition types,
244
+ KitAIContext, UI action registry
245
+ - [ ] `src/modules/events/` — platform event bus (DataChangeEmitter, all 30+ event types)
246
+ - [ ] `src/modules/skills/` — skill loading, bundled skills, user-invocable registry
247
+ - [ ] `src/modules/kits/` — kit loader, initialization, template injection
248
+ - [ ] `src/modules/formats/` — WVIZ, WDOC, WSLIDE, WQUIZ types (migrate from @soulcraft/views + richDocument.ts)
249
+
250
+ ### Phase 5 — Billing + Notifications
251
+ - [ ] `src/modules/billing/` — usage tracking, quota checks, top-up credits,
252
+ Stripe subscription management, billing provider (Firestore vs local)
253
+ - [ ] `src/modules/notifications/` — NotificationSender interface, Postmark, Twilio,
254
+ dev-mode console sender, notification templates
255
+
256
+ ### Phase 6 — Publish + First npm Release
257
+ - [ ] Publish `@soulcraft/sdk@1.0.0` to npmjs.com (private/restricted)
258
+ - [ ] Update Workshop: replace all `@soulcraft/brainy-client` + `@soulcraft/auth` imports
259
+ - [ ] Update Venue: same
260
+ - [ ] Update Academy: same
261
+ - [ ] Deprecate (do not unpublish) `@soulcraft/brainy-client` and `@soulcraft/auth`
262
+ - [ ] Add `@soulcraft/sdk` to bundle loop in all three `build.sh` files
263
+
264
+ ---
265
+
266
+ ## Packages Deprecated by This SDK
267
+
268
+ | Package | Status after SDK ships |
269
+ |---------|----------------------|
270
+ | `@soulcraft/brainy-client` | Deprecated — all code moved into SDK |
271
+ | `@soulcraft/auth` | Deprecated — all code moved into SDK |
272
+
273
+ Both packages remain published on npmjs.com (no unpublish) but get a deprecation
274
+ notice in their README and `package.json` `"deprecated"` field pointing to `@soulcraft/sdk`.
275
+
276
+ ---
277
+
278
+ ## Related Documents
279
+
280
+ - `docs/ADR-002-transport-protocol.md` — Wire protocol specification (to be written in Phase 2)
281
+ - `docs/ADR-003-instance-strategies.md` — Brainy instance pooling spec (to be written in Phase 2)
282
+ - `/home/dpsifr/.strategy/PLATFORM-HANDOFF.md` — Cross-project coordination thread