@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.2

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 (391) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +38 -2
  3. package/dist/bundling/logic-exports.d.ts +34 -0
  4. package/dist/bundling/node-builtins.d.ts +21 -0
  5. package/dist/bundling/transpiler.d.ts +7 -0
  6. package/dist/cli.js +27404 -0
  7. package/dist/client/dev-server.d.ts +59 -0
  8. package/dist/client/index.d.ts +28 -0
  9. package/dist/client/stub-services.d.ts +32 -0
  10. package/dist/client/test-session.d.ts +195 -0
  11. package/dist/client.js +16920 -0
  12. package/dist/core/asset-reference-translator.d.ts +46 -0
  13. package/dist/core/clock.d.ts +57 -0
  14. package/dist/core/faults.d.ts +65 -0
  15. package/dist/core/game-settings.d.ts +65 -0
  16. package/dist/core/history.d.ts +89 -0
  17. package/dist/core/listen-with-fallback.d.ts +30 -0
  18. package/dist/core/mutation-queue.d.ts +61 -0
  19. package/dist/core/package-scenario.d.ts +89 -0
  20. package/dist/core/participant-registry.d.ts +109 -0
  21. package/dist/core/pending-changes.d.ts +14 -0
  22. package/dist/core/protocol.d.ts +83 -0
  23. package/dist/core/scenario-drafts.d.ts +32 -0
  24. package/dist/core/session-endpoints.d.ts +108 -0
  25. package/dist/core/session-gates.d.ts +75 -0
  26. package/dist/core/session-host.d.ts +94 -0
  27. package/dist/core/session-refusals.d.ts +56 -0
  28. package/dist/core/session-rewind.d.ts +32 -0
  29. package/dist/core/session-roster.d.ts +128 -0
  30. package/dist/core/session-surfaces.d.ts +32 -0
  31. package/dist/core/session-ticks.d.ts +18 -0
  32. package/dist/core/session-timers.d.ts +84 -0
  33. package/dist/core/session-writes.d.ts +40 -0
  34. package/dist/core/session.d.ts +385 -0
  35. package/dist/core/settings-locks.d.ts +22 -0
  36. package/dist/core/team-names.d.ts +52 -0
  37. package/dist/core/team-state-store.d.ts +218 -0
  38. package/dist/core/tick-timer.d.ts +75 -0
  39. package/dist/dev/blob-links.d.ts +10 -0
  40. package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
  41. package/dist/dev/console/index.html +12 -0
  42. package/dist/dev/console-path.d.ts +19 -0
  43. package/dist/dev/lockfile.d.ts +124 -0
  44. package/dist/dev/package-watcher.d.ts +21 -0
  45. package/dist/dev/routes.d.ts +67 -0
  46. package/dist/dev/shell.d.ts +260 -0
  47. package/dist/dev/surfaces.d.ts +41 -0
  48. package/dist/dev/tools-identity.d.ts +23 -0
  49. package/dist/dev/view/batch.d.ts +172 -0
  50. package/dist/dev/view/commands.d.ts +75 -0
  51. package/dist/dev/view/route.d.ts +58 -0
  52. package/dist/dev/view/view.d.ts +78 -0
  53. package/dist/dev/view/viewer.d.ts +32 -0
  54. package/dist/discovery/config.d.ts +9 -0
  55. package/dist/discovery/manifest-paths.d.ts +21 -0
  56. package/dist/discovery/removed-fields.d.ts +90 -0
  57. package/dist/docs/api/deck.md +386 -0
  58. package/dist/docs/api/index.md +67 -0
  59. package/dist/docs/api/sdk-commands.md +286 -0
  60. package/dist/docs/api/sdk-e2e.md +433 -0
  61. package/dist/docs/api/sdk-projection.md +207 -0
  62. package/dist/docs/api/sdk-testing.md +214 -0
  63. package/dist/docs/api/sdk-tools-client.md +560 -0
  64. package/dist/docs/api/sdk-ui-testing.md +231 -0
  65. package/dist/docs/api/sdk-ui.md +1269 -0
  66. package/dist/docs/api/sdk.md +2140 -0
  67. package/dist/docs/changelog-0.x.md +1156 -0
  68. package/dist/docs/changelog.md +241 -0
  69. package/dist/docs/cli-and-dev-server.md +217 -0
  70. package/dist/docs/cli-error-codes.md +156 -0
  71. package/dist/docs/cli-reference.md +2489 -0
  72. package/dist/docs/cli-simulation-testing.md +222 -0
  73. package/dist/docs/concepts/index.md +46 -0
  74. package/dist/docs/concepts/terminology.md +322 -0
  75. package/dist/docs/environment-variables.md +102 -0
  76. package/dist/docs/getting-started.md +145 -0
  77. package/dist/docs/guides/ai-facilitation.md +220 -0
  78. package/dist/docs/guides/capabilities.md +460 -0
  79. package/dist/docs/guides/endpoints.md +335 -0
  80. package/dist/docs/guides/engine-testing.md +477 -0
  81. package/dist/docs/guides/engine.md +527 -0
  82. package/dist/docs/guides/file-handling.md +405 -0
  83. package/dist/docs/guides/games.md +176 -0
  84. package/dist/docs/guides/global-settings.md +94 -0
  85. package/dist/docs/guides/guide.md +123 -0
  86. package/dist/docs/guides/hooks.md +183 -0
  87. package/dist/docs/guides/index.md +51 -0
  88. package/dist/docs/guides/internationalization.md +288 -0
  89. package/dist/docs/guides/llm-integration.md +635 -0
  90. package/dist/docs/guides/multi-team-games.md +586 -0
  91. package/dist/docs/guides/patterns.md +391 -0
  92. package/dist/docs/guides/presentation.md +196 -0
  93. package/dist/docs/guides/production-constraints.md +130 -0
  94. package/dist/docs/guides/scenario-editor.md +143 -0
  95. package/dist/docs/guides/scenarios.md +515 -0
  96. package/dist/docs/guides/session.md +972 -0
  97. package/dist/docs/guides/settings.md +458 -0
  98. package/dist/docs/guides/setup.md +193 -0
  99. package/dist/docs/guides/speech-generation.md +233 -0
  100. package/dist/docs/guides/starter-game.md +164 -0
  101. package/dist/docs/guides/testing.md +577 -0
  102. package/dist/docs/guides/tick-controller.md +569 -0
  103. package/dist/docs/guides/type-safety.md +516 -0
  104. package/dist/docs/guides/ui.md +1389 -0
  105. package/dist/docs/guides/upgrading.md +636 -0
  106. package/dist/docs/guides/validators.md +217 -0
  107. package/dist/docs/guides/waiting-room.md +244 -0
  108. package/dist/docs/index.md +41 -0
  109. package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
  110. package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
  111. package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
  112. package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
  113. package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
  114. package/dist/docs/recipes/create-capability.md +66 -0
  115. package/dist/docs/recipes/create-endpoint.md +127 -0
  116. package/dist/docs/recipes/create-engine.md +111 -0
  117. package/dist/docs/recipes/create-game.md +70 -0
  118. package/dist/docs/recipes/create-guide.md +75 -0
  119. package/dist/docs/recipes/create-hook.md +80 -0
  120. package/dist/docs/recipes/create-presentation.md +142 -0
  121. package/dist/docs/recipes/create-scenario-editor.md +102 -0
  122. package/dist/docs/recipes/create-scenario.md +98 -0
  123. package/dist/docs/recipes/create-session-ui.md +116 -0
  124. package/dist/docs/recipes/create-settings.md +88 -0
  125. package/dist/docs/recipes/create-setup.md +115 -0
  126. package/dist/docs/recipes/create-tick-controller.md +65 -0
  127. package/dist/docs/recipes/create-user-interface.md +96 -0
  128. package/dist/docs/recipes/create-validator.md +67 -0
  129. package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
  130. package/dist/docs/recipes/declare-api-uses.md +67 -0
  131. package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
  132. package/dist/docs/recipes/distribute-package.md +80 -0
  133. package/dist/docs/recipes/e2e-testing.md +116 -0
  134. package/dist/docs/recipes/end-game-on-condition.md +154 -0
  135. package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
  136. package/dist/docs/recipes/index.md +86 -0
  137. package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
  138. package/dist/docs/recipes/negotiate-across-teams.md +164 -0
  139. package/dist/docs/recipes/observe-other-teams-state.md +119 -0
  140. package/dist/docs/recipes/publish-a-package.md +139 -0
  141. package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
  142. package/dist/docs/recipes/review-a-past-day.md +73 -0
  143. package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
  144. package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
  145. package/dist/docs/recipes/show-all-teams-participants.md +109 -0
  146. package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
  147. package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
  148. package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
  149. package/dist/docs/recipes/test-user-interface.md +95 -0
  150. package/dist/docs/recipes/test-with-engine.md +115 -0
  151. package/dist/docs/recipes/upload-and-display-files.md +148 -0
  152. package/dist/docs/recipes/use-request-play.md +65 -0
  153. package/dist/docs/recipes/use-request-tick.md +75 -0
  154. package/dist/docs/troubleshooting.md +816 -0
  155. package/dist/execution/blob-inputs.d.ts +57 -0
  156. package/dist/execution/bundle-reload.d.ts +42 -0
  157. package/dist/execution/config-resolver.d.ts +127 -0
  158. package/dist/execution/configuration-builder.d.ts +66 -0
  159. package/dist/execution/context-composition.d.ts +161 -0
  160. package/dist/execution/endpoint-actions.d.ts +9 -0
  161. package/dist/execution/endpoint-payloads.d.ts +32 -0
  162. package/dist/execution/endpoint-runner.d.ts +129 -0
  163. package/dist/execution/engine-state.d.ts +40 -0
  164. package/dist/execution/executor-callbacks.d.ts +56 -0
  165. package/dist/execution/executor.d.ts +436 -0
  166. package/dist/execution/gates.d.ts +45 -0
  167. package/dist/execution/hook-chain.d.ts +25 -0
  168. package/dist/execution/logic-calls.d.ts +190 -0
  169. package/dist/execution/logic-failure.d.ts +84 -0
  170. package/dist/execution/logic-loader.d.ts +54 -0
  171. package/dist/execution/logic-result.d.ts +76 -0
  172. package/dist/execution/tick-controller-call.d.ts +32 -0
  173. package/dist/execution/types.d.ts +48 -0
  174. package/dist/generation/ts-type.d.ts +39 -0
  175. package/dist/guide/locale.d.ts +12 -0
  176. package/dist/guide/video-registry.d.ts +42 -0
  177. package/dist/help-examples.d.ts +125 -0
  178. package/dist/host/adapter.d.ts +67 -0
  179. package/dist/host/host-channel.d.ts +222 -0
  180. package/dist/host/index.d.ts +20 -0
  181. package/dist/host/protocol.d.ts +783 -0
  182. package/dist/host/surfaces.d.ts +42 -0
  183. package/dist/host/theme.d.ts +55 -0
  184. package/dist/host.js +583 -0
  185. package/dist/prompts/index.d.ts +7 -0
  186. package/dist/prompts/schema-builder.d.ts +29 -0
  187. package/dist/prompts/types.d.ts +108 -0
  188. package/dist/prompts/validators.d.ts +32 -0
  189. package/dist/runtime/capability-scope.d.ts +6 -0
  190. package/dist/runtime/context-merge.d.ts +29 -0
  191. package/dist/runtime/context-projection.d.ts +41 -0
  192. package/dist/runtime/guarded-callbacks.d.ts +15 -0
  193. package/dist/runtime/index.d.ts +27 -0
  194. package/dist/runtime/manifest-version.d.ts +55 -0
  195. package/dist/runtime/tick-validation.d.ts +25 -0
  196. package/dist/runtime.js +346 -0
  197. package/dist/schema.json +1546 -0
  198. package/dist/server/vite-plugin.d.ts +46 -0
  199. package/dist/services/tts/client.d.ts +51 -0
  200. package/dist/services/tts/elevenlabs.d.ts +24 -0
  201. package/dist/services/tts/types.d.ts +44 -0
  202. package/dist/storage/asset-resolver.d.ts +49 -0
  203. package/dist/storage/blob-store.d.ts +41 -0
  204. package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
  205. package/dist/templates/endpoint/logic.ts.liquid +25 -0
  206. package/dist/templates/engine/init.json.liquid +7 -0
  207. package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
  208. package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
  209. package/dist/templates/engine/logic.test.ts.liquid +61 -0
  210. package/dist/templates/engine/logic.ts.liquid +32 -0
  211. package/dist/templates/engine/run.json.liquid +7 -0
  212. package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
  213. package/dist/templates/guide/02-preparation.md.liquid +18 -0
  214. package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
  215. package/dist/templates/hook/logic.test.ts.liquid +46 -0
  216. package/dist/templates/hook/logic.ts.liquid +12 -0
  217. package/dist/templates/package/CLAUDE.md.template +338 -0
  218. package/dist/templates/package/DESIGN.md.liquid +144 -0
  219. package/dist/templates/package/README.md.liquid +143 -0
  220. package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
  221. package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
  222. package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
  223. package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
  224. package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
  225. package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
  226. package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
  227. package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
  228. package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
  229. package/dist/templates/package/claude/settings.json.template +16 -0
  230. package/dist/templates/package/gitignore.template +15 -0
  231. package/dist/templates/package/global-setup.ts.template +21 -0
  232. package/dist/templates/package/globals.d.ts.template +2 -0
  233. package/dist/templates/package/oxfmtrc.json.template +14 -0
  234. package/dist/templates/package/oxlintrc.json.template +238 -0
  235. package/dist/templates/package/package.json.liquid +65 -0
  236. package/dist/templates/package/playwright.config.ts.liquid +19 -0
  237. package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
  238. package/dist/templates/package/test-setup.ts.liquid +7 -0
  239. package/dist/templates/package/tsconfig.json.liquid +15 -0
  240. package/dist/templates/package/vite.config.ts.liquid +19 -0
  241. package/dist/templates/package/vitest.config.ts.liquid +25 -0
  242. package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
  243. package/dist/templates/presentation/index.html.liquid +20 -0
  244. package/dist/templates/presentation/src/App.tsx.liquid +49 -0
  245. package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
  246. package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
  247. package/dist/templates/presentation/src/main.tsx.liquid +14 -0
  248. package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
  249. package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
  250. package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
  251. package/dist/templates/session/src/App.test.tsx.liquid +60 -0
  252. package/dist/templates/session/src/App.tsx.liquid +128 -0
  253. package/dist/templates/session/src/i18n.ts.liquid +53 -0
  254. package/dist/templates/session/src/main.tsx.liquid +48 -0
  255. package/dist/templates/session/src/theme.ts.liquid +46 -0
  256. package/dist/templates/settings-editor/index.html.liquid +12 -0
  257. package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
  258. package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
  259. package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
  260. package/dist/templates/setup/src/App.tsx.liquid +102 -0
  261. package/dist/templates/setup/src/i18n.ts.liquid +47 -0
  262. package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
  263. package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
  264. package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
  265. package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
  266. package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
  267. package/dist/templates/starter-game/engine/defaults.ts +81 -0
  268. package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
  269. package/dist/templates/starter-game/engine/logic.test.ts +221 -0
  270. package/dist/templates/starter-game/engine/logic.ts +149 -0
  271. package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
  272. package/dist/templates/starter-game/engine/types.ts +139 -0
  273. package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
  274. package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
  275. package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
  276. package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
  277. package/dist/templates/starter-game/guide/guide.test.ts +108 -0
  278. package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
  279. package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
  280. package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
  281. package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
  282. package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
  283. package/dist/templates/starter-game/package.json +68 -0
  284. package/dist/templates/starter-game/playwright.config.ts +19 -0
  285. package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
  286. package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
  287. package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
  288. package/dist/templates/starter-game/session/defaults.ts +80 -0
  289. package/dist/templates/starter-game/session/index.html +12 -0
  290. package/dist/templates/starter-game/session/session.sdk.json +11 -0
  291. package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
  292. package/dist/templates/starter-game/session/src/App.tsx +309 -0
  293. package/dist/templates/starter-game/session/src/Section.tsx +37 -0
  294. package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
  295. package/dist/templates/starter-game/session/src/format.ts +8 -0
  296. package/dist/templates/starter-game/session/src/i18n.ts +51 -0
  297. package/dist/templates/starter-game/session/src/main.tsx +48 -0
  298. package/dist/templates/starter-game/session/src/theme.ts +45 -0
  299. package/dist/templates/starter-game/session/types.ts +135 -0
  300. package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
  301. package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
  302. package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
  303. package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
  304. package/dist/templates/starter-game/test-setup.ts +7 -0
  305. package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
  306. package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
  307. package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
  308. package/dist/templates/starter-game/tsconfig.json +19 -0
  309. package/dist/templates/starter-game/ui/defaults.ts +60 -0
  310. package/dist/templates/starter-game/ui/index.html +12 -0
  311. package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
  312. package/dist/templates/starter-game/ui/src/App.tsx +175 -0
  313. package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
  314. package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
  315. package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
  316. package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
  317. package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
  318. package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
  319. package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
  320. package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
  321. package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
  322. package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
  323. package/dist/templates/starter-game/ui/src/format.ts +12 -0
  324. package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
  325. package/dist/templates/starter-game/ui/src/main.tsx +48 -0
  326. package/dist/templates/starter-game/ui/src/menu.ts +18 -0
  327. package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
  328. package/dist/templates/starter-game/ui/src/theme.ts +45 -0
  329. package/dist/templates/starter-game/ui/src/writes.ts +43 -0
  330. package/dist/templates/starter-game/ui/types.ts +137 -0
  331. package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
  332. package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
  333. package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
  334. package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
  335. package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
  336. package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
  337. package/dist/templates/starter-game/vite.config.ts +19 -0
  338. package/dist/templates/starter-game/vitest.config.ts +15 -0
  339. package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
  340. package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
  341. package/dist/templates/starter-game/waiting_room/index.html +12 -0
  342. package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
  343. package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
  344. package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
  345. package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
  346. package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
  347. package/dist/templates/starter-game/waiting_room/types.ts +59 -0
  348. package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
  349. package/dist/templates/tick-controller/logic.ts.liquid +8 -0
  350. package/dist/templates/ui/index.html.liquid +12 -0
  351. package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
  352. package/dist/templates/ui/src/App.tsx.liquid +103 -0
  353. package/dist/templates/ui/src/i18n.ts.liquid +47 -0
  354. package/dist/templates/ui/src/main.tsx.liquid +48 -0
  355. package/dist/templates/ui/src/theme.ts.liquid +46 -0
  356. package/dist/templates/validator/logic.test.ts.liquid +42 -0
  357. package/dist/templates/validator/logic.ts.liquid +21 -0
  358. package/dist/templates/waiting-room/index.html.liquid +12 -0
  359. package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
  360. package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
  361. package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
  362. package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
  363. package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
  364. package/dist/templating/resource-metadata.d.ts +131 -0
  365. package/dist/types.d.ts +14 -0
  366. package/dist/utils/atomic-write.d.ts +40 -0
  367. package/dist/utils/cli-error.d.ts +64 -0
  368. package/dist/utils/document-parser-client.d.ts +20 -0
  369. package/dist/utils/env-loader.d.ts +40 -0
  370. package/dist/utils/error-codes.d.ts +339 -0
  371. package/dist/utils/errors.d.ts +19 -0
  372. package/dist/utils/global-settings.d.ts +65 -0
  373. package/dist/utils/llm-client.d.ts +34 -0
  374. package/dist/utils/logger.d.ts +36 -0
  375. package/dist/utils/output.d.ts +109 -0
  376. package/dist/utils/platform.d.ts +14 -0
  377. package/dist/utils/prompt.d.ts +36 -0
  378. package/dist/utils/real-path.d.ts +6 -0
  379. package/dist/utils/schema-properties.d.ts +35 -0
  380. package/dist/validation/ajv.d.ts +17 -0
  381. package/dist/validation/facilitator-guide-validator.d.ts +27 -0
  382. package/dist/validation/findings.d.ts +40 -0
  383. package/dist/validation/game-constraints.d.ts +10 -0
  384. package/dist/validation/game-validator.d.ts +34 -0
  385. package/dist/validation/package-validation.d.ts +41 -0
  386. package/dist/validation/package-validator.d.ts +19 -0
  387. package/dist/validation/resource-validator.d.ts +79 -0
  388. package/dist/validation/schema-defaults-validator.d.ts +29 -0
  389. package/dist/validation/schema-validation.d.ts +115 -0
  390. package/dist/validation/semver.d.ts +9 -0
  391. package/package.json +123 -3
@@ -0,0 +1,37 @@
1
+ # Running the session
2
+
3
+ One section per phase. Use the say, do, and timing admonitions so the script is easy to follow live.
4
+
5
+ ## Briefing
6
+
7
+ :::say
8
+ Welcome everyone. Today you are running {{ name }}. You will make decisions as a team, see the consequences, and compete with the other teams.
9
+ :::
10
+
11
+ :::do
12
+ Form the teams and hand each team its device.
13
+ :::
14
+
15
+ :::timing
16
+ Keep the briefing under 15 minutes. Energy matters more than completeness; the first tick will teach the mechanics.
17
+ :::
18
+
19
+ ## Play
20
+
21
+ :::do
22
+ Run the first tick once every team has submitted its decisions. Walk the room between ticks and coach teams that are stuck.
23
+ :::
24
+
25
+ :::warning
26
+ Avoid revealing how the underlying model works during play. Save it for the debrief.
27
+ :::
28
+
29
+ ## Debrief
30
+
31
+ :::say
32
+ What surprised you? Where did the result differ from what you expected when you made the decision?
33
+ :::
34
+
35
+ :::tip
36
+ Tie at least one concrete in-game moment back to each learning objective.
37
+ :::
@@ -0,0 +1,46 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Run } from './logic';
3
+ import type { Context, Settings, Callbacks } from '../types';
4
+ import type { LogicMeta } from '@seriousgaming/sdk';
5
+
6
+ // The run-wide facts the host supplies: the session locale, its random seed,
7
+ // whether this is the game being played or a walkthrough beside it, and when
8
+ // the operation started.
9
+ const meta: LogicMeta = { locale: 'en', seed: 'test-seed', runKind: 'session', now: Date.UTC(2026, 0, 1) };
10
+
11
+ const callbacks = {} as Callbacks;
12
+
13
+ // The participant the signal concerns, and the roster the hook receives.
14
+ const participant = {
15
+ id: '1',
16
+ teamId: 'team-1',
17
+ name: 'Participant 1',
18
+ index: 0,
19
+ connected: true,
20
+ };
21
+
22
+ describe('{{ identifier }}', () => {
23
+ describe('Run', () => {
24
+ it('should update context in response to the signal', async () => {
25
+ const context: Context = {
26
+ // TODO: Add context values based on required/provided capabilities
27
+ } as Context;
28
+ const settings: Settings = {
29
+ // TODO: Add values for the settings resources this reads
30
+ } as Settings;
31
+
32
+ // TODO: replace with the payload fields the hook's signal supplies
33
+ const { capabilities } = await Run({
34
+ context,
35
+ settings,
36
+ callbacks,
37
+ meta,
38
+ participant,
39
+ participants: [participant],
40
+ } as Parameters<typeof Run>[0]);
41
+
42
+ // TODO: Add assertions for updated values
43
+ expect(capabilities).toBeDefined();
44
+ });
45
+ });
46
+ });
@@ -0,0 +1,12 @@
1
+ import type { HookFunction } from '../types';
2
+
3
+ export const Run: HookFunction = async ({ context, settings, callbacks }) => {
4
+ // Hooks fire after the runtime applies the lifecycle change.
5
+ // Return the capabilities it writes under `capabilities`; the runtime merges
6
+ // and persists them.
7
+ return {
8
+ capabilities: {
9
+ ...context
10
+ }
11
+ };
12
+ };
@@ -0,0 +1,338 @@
1
+ # CLAUDE.md
2
+
3
+ Guidance for Claude Code working in this Serious Gaming SDK package. A package is a game.
4
+
5
+ ## Non-negotiables
6
+
7
+ - **Logic returns what it writes under `capabilities`**, and removes keys with `deletes` beside it. Never mutate `context`: spread
8
+ - **Declare `reads`, `writes`, `settings` and `uses`.** A resource receives exactly what it declares; a write or a service outside them is refused
9
+ - **Await every `setData`**, through `useMutationReporter`, so a refusal reaches the person. Never retry a write automatically
10
+ - **Never start `pnpm start`, `pnpm run sdk dev` or watch-mode `pnpm test`.** The user starts long-running processes
11
+ - **No host globals in logic.** `meta.now`, not `Date.now()`; `meta.seed` with `deriveSeed` and `seededRandom`, not `Math.random()`; no `fetch`, timers or Node built-ins
12
+ - **Use `update`, not `new`, for a slot that is already filled** (`engine`, `ui`, `session`, …). Change bundles through the CLI, not by hand
13
+ - **Never edit a generated `types.ts`, and never use `any`**
14
+ - **Never publish, and never `pnpm add`, unless the user asks**
15
+
16
+ ## Workflows
17
+
18
+ Before touching a resource, read its rule. A tool that loads only this file still finds them here:
19
+
20
+ - `capabilities/`: `.claude/rules/workflow-create-capability.md`
21
+ - `engine/`: `.claude/rules/workflow-create-engine.md`
22
+ - `ui/`, `session/`, `waiting_room/`, `setup/`, `scenario_editor/`: `.claude/rules/workflow-create-user-interface.md`
23
+ - `endpoints/`: `.claude/rules/workflow-create-endpoint.md`
24
+ - `guide/`: `.claude/rules/workflow-create-guide.md`
25
+ - `presentation/`: `.claude/rules/workflow-create-presentation.md`
26
+ - Any `*.test.ts` or `*.test.tsx`: `.claude/rules/workflow-test-engine-or-ui.md`
27
+ - `package.json` and an SDK upgrade: `.claude/rules/workflow-update-sdk-version.md`
28
+ - Publishing: `.claude/rules/workflow-publish-package.md`
29
+
30
+ ## Documentation
31
+
32
+ The SDK's documentation ships inside it, so it describes the version installed here. **Read the recipe before you start a task; do not guess.**
33
+
34
+ ```bash
35
+ pnpm run --silent sdk docs view # the documentation tree
36
+ pnpm run --silent sdk docs view recipes/index.md # every recipe, one line each
37
+ pnpm run --silent sdk docs view recipes/create-hook.md # one document
38
+ pnpm run --silent sdk docs view cli-reference.md # every command and flag
39
+ ```
40
+
41
+ The same files are on disk in `node_modules/@seriousgaming/sdk-tools/dist/docs/`, so you can search them with `grep -rn`. Start from `recipes/` for a task, `guides/` for a whole subject, and `concepts/` for the model.
42
+
43
+ ## This package
44
+
45
+ Unless it was created with `--empty`, this package holds a complete, playable starter game: a lemonade stand, run for 10 days. Each morning a team decides how many lemonades, iced teas, and cookies to make. Whatever they make they pay for, whatever is left at closing is poured away, and anyone who wanted something that ran out simply leaves.
46
+
47
+ Read it before you change it. It is the reference for four things that are easy to get wrong and expensive to retrofit:
48
+
49
+ - **State keyed so participants cannot overwrite each other.** `plan.Make` is keyed by menu item, and `hooks/assignproducts/` puts each participant in charge of one item, so two people changing two numbers write two different keys
50
+ - **A validator on the one thing they share.** Two changes can each fit while the merged plan exceeds the stall's capacity, so `validators/capacity/` checks the merged result and refuses it out loud
51
+ - **Awaited writes.** Every `setData` in `ui/` goes through the one mutation reporter in `ui/src/writes.ts`
52
+ - **Teams run their own days.** The game is in independent mode; the facilitator watches from `session/` and can undo
53
+
54
+ Replace the model freely. Keep the shape. `pnpm run --silent sdk docs view guides/starter-game.md` walks through every file, and `recipes/start-from-an-empty-package.md` picks up an empty package.
55
+
56
+ ### Mental model
57
+
58
+ A game works like a **reactive spreadsheet**:
59
+
60
+ - **Capabilities**: column headers (named groups of variables, schema only)
61
+ - **Settings**: read-only parameters, each property with a default, overridden by scenarios
62
+ - **The engine**: the formulas (`Init` sets up, `Run` computes one tick)
63
+ - **User interfaces**: charts and input cells (`ui/` for participants, `session/` for the facilitator, `waiting_room/` before play, `setup/` and `scenario_editor/` for settings)
64
+ - **Validators**: data validation on input cells (reject a write, never change it)
65
+ - **Hooks**: handlers that run outside the tick on a signal such as `participantJoin`
66
+ - **Endpoints**: server-side functions a surface, or with `visibility: "external"` a caller outside the game, calls by identifier
67
+ - **Guide and presentation**: the facilitator's run-sheet and slide deck
68
+
69
+ Data flows: surfaces write decisions with `setData`, a tick runs the engine, and surfaces show the results.
70
+
71
+ ### Layout
72
+
73
+ ```
74
+ .
75
+ ├── seriousgaming.sdk.json # The package identifier, locales, maxTicks and teams
76
+ ├── engine/ # THE engine: Init and Run
77
+ ├── ui/ # THE participant interface
78
+ ├── session/ # THE facilitator's screen
79
+ ├── waiting_room/ # THE holding screen
80
+ ├── setup/ # THE settings form at session creation (sdk new setup)
81
+ ├── scenario_editor/ # THE scenario authoring screen (sdk new scenario_editor)
82
+ ├── presentation/ # THE slide deck (sdk new presentation)
83
+ ├── tick_controller/ # THE timer, for timed rounds (sdk new tick_controller)
84
+ ├── guide/ # THE facilitator guide (markdown per locale, plus assets)
85
+ ├── capabilities/<name>/ # Data definitions
86
+ ├── settings/<name>/ # Read-only parameters
87
+ ├── endpoints/<name>/ # Server-side functions
88
+ ├── hooks/<name>/ # Lifecycle handlers
89
+ ├── validators/<name>/ # Reject-only gates on writes and tick requests
90
+ ├── scenarios/<name>/ # Settings overrides, plus assets
91
+ ├── testing/engine/ # Whole-game tests (createTestSession)
92
+ └── testing/e2e/ # Playwright specs, and the global setup that starts their server
93
+ ```
94
+
95
+ A game holds one of each slot resource, so it lives in its slot and carries no name. Everything else sits at `<plural>/<name>/` and is named by its directory: `"identifier": "sales"` makes `capabilities/sales/`, `context.sales`, and `"sales"` in `reads`. A name is lowercase letters, digits, and underscores, starting with a letter. Only the package carries a dotted reverse-DNS identifier, and its game is that identifier with `.game` appended. There is no list to keep in step: a package declares whatever is on disk.
96
+
97
+ ## The loop: edit, validate, test
98
+
99
+ Pass `--json` to any command (or set `SERIOUSGAMING_JSON=1`) and it prints exactly one JSON document on stdout, success or failure, with progress on stderr. Always invoke it as `pnpm run --silent sdk …`, so no pnpm banner precedes the JSON.
100
+
101
+ ```json
102
+ { "ok": true, "command": "tick", "data": { "completedTicks": 1 }, "diagnostics": [], "warnings": [] }
103
+ { "ok": false, "command": "build", "error": { "code": "PACKAGE_INVALID", "message": "…", "hint": "…" }, "diagnostics": [{ "severity": "error", "code": "SETTINGS_UNKNOWN_KEY", "file": "scenarios/hard/scenario.sdk.json", "pointer": "/definition/settings/engine/BaseDemnd", "hint": "Did you mean 'BaseDemand'?", "message": "…" }], "warnings": [] }
104
+ ```
105
+
106
+ After every change, run these in order and stop at the first that fails:
107
+
108
+ ```bash
109
+ pnpm run --silent sdk build --validate-only --json # the package: bundles, references, settings
110
+ pnpm run typecheck # tsc over the whole package
111
+ pnpm run lint # oxlint, warnings included
112
+ pnpm test:run # unit and component tests beside each resource
113
+ pnpm test:engine # whole games, in-process
114
+ pnpm run --silent sdk tick --no-server --json # one tick in the restricted production runtime
115
+ ```
116
+
117
+ Branch on `ok`, the exit code and `error.code`, never on the message:
118
+
119
+ | Exit | Meaning | What to do |
120
+ | ---- | ------------------- | -------------------------------------------------------------------------- |
121
+ | 0 | Success | |
122
+ | 1 | A bug in the CLI | Report it; do not retry blindly |
123
+ | 2 | Usage error | Fix the command: a flag, an argument, or input you did not pass |
124
+ | 3 | Invalid package | Fix the package: `diagnostics` says which file and where |
125
+ | 4 | Refused by the game | The game said no (`REFUSED_VALIDATION`, `TICK_FAILED`, …): read the reason |
126
+ | 5 | Environment | No session, a port in use, the platform unreachable: fix the environment |
127
+
128
+ **On a failure, read `diagnostics[]` first.** `file` is relative to the package root; `pointer` is an RFC 6901 pointer into that JSON file (`/definition/reads/0` is the bundle's first read), and `line` and `column` locate a TypeScript file instead. Open it there, apply the `hint`, and rerun the same command. Branch on each diagnostic's `code` (`SCHEMA_INVALID`, `REFERENCE_UNKNOWN`, `SETTINGS_UNKNOWN_KEY`, `REMOVED_FIELD`, `LOGIC_ERROR`, …). Logic that throws during `init`, `tick` or `endpoint call` exits 4 with a `LOGIC_ERROR` naming the source line, `tick` and `team`. Warnings do not fail a command, but are usually worth fixing.
129
+
130
+ The CLI never prompts without a terminal: a command that would ask fails with `PROMPT_REQUIRED` (exit 2). Pass definitions as an argument, with `--file`, or on stdin, and pass `--yes` to `delete`. `pnpm run --silent sdk docs view recipes/check-a-package-from-a-script.md` has the whole contract.
131
+
132
+ ## Commands
133
+
134
+ ### Resources
135
+
136
+ ```bash
137
+ pnpm run sdk new capability --help # every type prints its JSON format with --help
138
+ pnpm run sdk new capability '{...}' # likewise hook, validator, endpoint, settings, scenario
139
+ pnpm run sdk new setup '{...}' # an empty slot: setup, scenario_editor, presentation, tick_controller
140
+ pnpm run sdk update engine --patch '[{"op":"add","path":"/definition/writes/-","value":"results"}]'
141
+ pnpm run sdk update capability plan '{...}' # full replacement
142
+ pnpm run sdk update package --patch '[{"op":"replace","path":"/ticks","value":12}]'
143
+ pnpm run sdk delete capability plan --yes
144
+ pnpm run generate # regenerate types after a hand edit (new and update already do)
145
+ pnpm run build # validate, generate and bundle into dist/
146
+ ```
147
+
148
+ A slot command takes no identifier. The starter game fills every slot but `setup`, `scenario_editor`, `presentation` and `tick_controller`, and `new` refuses a filled slot. `update package` changes the manifest, which takes no identifier either.
149
+
150
+ ### Playing the game from the CLI
151
+
152
+ ```bash
153
+ pnpm run --silent sdk init # start the session, or rebuild it at tick 0
154
+ pnpm run --silent sdk tick --patch '[{"op":"replace","path":"/plan/Make/lemonade","value":70}]'
155
+ pnpm run --silent sdk patch '[{"op":"replace","path":"/plan/Make/cookies","value":0}]'
156
+ pnpm run --silent sdk view # the current state (--full for the whole projection)
157
+ pnpm run --silent sdk undo # undo the last tick
158
+ pnpm run --silent sdk reset # back to tick 0
159
+ ```
160
+
161
+ Patch paths start at the capability. `--team N` picks the team in a game with several. `pnpm run sdk configure '{"menu":{"Capacity":60}}'` overrides settings for the session, keyed by settings identifier, and restarts a running session on the new values.
162
+
163
+ **Know which session a command touches:**
164
+
165
+ - **If a dev server is running**, session commands act on its session, which is what the user is watching
166
+ - **If none is running**, they start one in the background and say so on stderr
167
+ - **With `--no-server`**, they run in-process against a fresh session and leave nothing behind; each starts again at tick 0
168
+
169
+ Edits to logic and bundles reach a running dev server from its next tick, without a reset. Prefer `--no-server` and `pnpm test:engine` for your own checks. Use the shared session only when the user wants to watch. `pnpm run sdk dev status` says whether a server is running; never stop one you did not start. `pnpm test:e2e` starts an isolated server of its own on free ports, beside the user's.
170
+
171
+ ## How things work
172
+
173
+ ### Reads, writes and what the runtime owns
174
+
175
+ Every resource declares its data flow as arrays of capability identifiers: `reads` for what it consumes, `writes` for what it produces (a capability may appear in both). A capability you forgot to declare is absent from `context` (or a surface's `data`), and the generated `Context` says so. A game holds one engine; a simulation with several steps calls them as plain functions from its `Run`.
176
+
177
+ Do not rebuild runtime concerns in context:
178
+
179
+ - **Ticks**: no `CurrentRound` variable; read `meta.tick`, the tick being run
180
+ - **Time**: `meta.now`, the instant the operation started, which a re-run sees again
181
+ - **Who is on a team**: the read-only `participants` argument, not a roster kept from hooks
182
+ - **Lifecycle**: set `maxTicks` in `seriousgaming.sdk.json`, or return `complete: true` from `Run`. A tick controller is for timed rounds, or ending on a participant's write between ticks
183
+ - **Undo and reset**: built in
184
+ - **Parameters**: a settings resource (`settings/<name>/settings.sdk.json`, a default on every property), read by each resource that lists it in `settings`
185
+
186
+ `setData` updates context immediately, and other surfaces see it at once; the engine's `Run` sees it on the next tick.
187
+
188
+ ### Logic
189
+
190
+ Every logic function is async, takes one args object, and returns what it writes under `capabilities`, always by spreading:
191
+
192
+ ```typescript
193
+ import type { InitializationFunction, RunFunction } from '@seriousgaming/sdk';
194
+ import type { Context, Settings } from './types';
195
+
196
+ export const Init: InitializationFunction<Context, Settings> = async ({ context, settings }) => ({
197
+ capabilities: { ...context },
198
+ });
199
+
200
+ export const Run: RunFunction<Context, Settings> = async ({ context, meta }) => ({
201
+ capabilities: {
202
+ ...context,
203
+ results: { ...context.results, Day: meta.tick }, // never context.results.Day = …
204
+ },
205
+ });
206
+ ```
207
+
208
+ - The args are `{ context, settings, callbacks, meta, participants }`, plus `state` for an engine. `settings` is keyed by settings identifier and already resolved for the session; `guides/settings.md` has the order
209
+ - Hooks, aggregate engines (`{ shared: { capabilities }, teams: [{ capabilities }] }`) and writing endpoints (`{ response, capabilities }`) return the same way. A flat return fails the call
210
+ - **To remove a key**, return its path in `deletes` beside `capabilities` (`deletes: [['orders', 'Open', id]]`), under a capability in `writes`. Leaving a key out never removes it
211
+ - `Init` is optional: without it each team starts from the capabilities' defaults
212
+ - `Run` may return `complete: true` to end the game, and an engine may return `state` beside `capabilities` for private memory between ticks (type it with `RunFunction`'s fourth parameter)
213
+ - A step of a split engine is a `StepFunction<Context, Settings>`: steps pass the context flat, and only the exported function wraps the result
214
+
215
+ ### The logic runtime: ECMAScript intrinsics only
216
+
217
+ Engine, hook, validator, endpoint, and tick controller logic runs in a **bare V8 isolate** in production. `process`, `require`, `fetch`, timers, `structuredClone`, `Buffer`, `TextEncoder`, `URL`, `crypto`, `atob`, `window`, `document` and `localStorage` are absent and throw at tick time. `console.log` is fine.
218
+
219
+ | Instead of | Use |
220
+ | ----------------------------- | -------------------------------------------------------------- |
221
+ | `structuredClone(v)` | `JSON.parse(JSON.stringify(v))` |
222
+ | `TextEncoder`, `TextDecoder` | `encodeUtf8`, `decodeUtf8` from `@seriousgaming/sdk` |
223
+ | `fetch(...)` | The injected `callbacks`, or an endpoint |
224
+ | `setTimeout(...)` | Nothing: ticks are the scheduling primitive |
225
+ | `process.env.X` | The `settings` argument |
226
+ | `Math.random()` for kept data | `deriveSeed` and `seededRandom` from the SDK, from `meta.seed` |
227
+ | `Date.now()`, `new Date()` | `meta.now` |
228
+
229
+ Importing a Node built-in (`node:fs`, `path`, `os`, …), directly or through a library, fails `build`, `dev` and `tick` with `LOGIC_NODE_BUILTIN`. `pnpm run lint` refuses the globals above as errors in every logic file, and fails on any warning, so run it before you call logic done. It cannot see `globalThis.fetch`, and it is off for tests, which run in Node: **a green unit test does not prove logic is portable**, so finish with `tick --no-server`.
230
+
231
+ ### Type safety
232
+
233
+ `variablesSchema` and `settingsSchema` are full JSON Schema 7 objects, and the generated types are only as precise as they are:
234
+
235
+ - **Wrap every schema**: `type: 'object'`, `properties`, `additionalProperties: false` (and `required` in a `variablesSchema`)
236
+ - **Type every field.** Never a string holding JSON; give an object its `properties` and an array its `items`
237
+ - **Key collections by id** where several people write at once (`additionalProperties` with a schema): two writes to two keys cannot collide, and an entry leaves through `deletes`. See `recipes/design-state-for-concurrent-writes.md`
238
+ - **A `default` must satisfy its own schema**: `build --validate-only` fails one that does not
239
+
240
+ ```json
241
+ {
242
+ "definition": {
243
+ "identifier": "market",
244
+ "variablesSchema": {
245
+ "type": "object",
246
+ "properties": {
247
+ "Competitors": {
248
+ "type": "object",
249
+ "properties": {
250
+ "count": { "type": "number", "minimum": 1 },
251
+ "priceList": {
252
+ "type": "array",
253
+ "items": { "type": "number", "minimum": 0 }
254
+ }
255
+ },
256
+ "required": ["count", "priceList"],
257
+ "additionalProperties": false,
258
+ "default": { "count": 5, "priceList": [10, 15, 20] }
259
+ }
260
+ },
261
+ "required": ["Competitors"],
262
+ "additionalProperties": false
263
+ }
264
+ }
265
+ }
266
+ ```
267
+
268
+ Every resource with logic or a surface has a generated `types.ts` beside it: `Context` holds exactly the capabilities it declares, `Settings` the settings resources it lists, and `Callbacks` the services in its `uses`. Import from `./types` (or `../types` from a `logic/` directory), never edit it or `defaults.ts`, and never write your own. `pnpm run --silent sdk docs view guides/type-safety.md` has the rest.
269
+
270
+ ### Testing logic
271
+
272
+ Call a logic function with `logicArgs` from `@seriousgaming/sdk/testing`, which fills `meta`, the roster and offline stub callbacks around what you name; pass the generated `defaultSettings`. A whole game runs in `createTestSession<Context, SharedContext>` (`testing/engine/`), whose `setData` and `requestTick` act as a participant, so validators judge them. Never cast an argument or a context:
273
+
274
+ ```ts
275
+ const result = await Run(
276
+ logicArgs.run({ context: defaultContext, settings: defaultSettings, meta: { tick: 3 } })
277
+ );
278
+
279
+ const session = await createTestSession<Context, SharedContext>({ packagePath });
280
+ await session.setData(0, { plan: { Make: { lemonade: 28 } } });
281
+ await session.tick();
282
+ expect(session.team(0).context.results.Day).toBe(1);
283
+ ```
284
+
285
+ ## User interfaces
286
+
287
+ - **Default to MUI** (`@mui/material`), as the starter game does. Another library must integrate with `useUserInterfaceTheme` so colours and dark mode work, and text on a theme colour uses that colour's `contrastText` (`colors.contrastText.primary`)
288
+ - **Await writes.** `setData` rejects when the host refuses the write. Route writes through `useMutationReporter`, as `ui/src/writes.ts` does; on a `conflict`, offer `failure.retry` behind a 'Try again' button
289
+ - **Endpoint calls fail typed.** `callbacks.endpoints['<name>'](request)` rejects with an `EndpointError` whose `kind` says why; a non-empty `applied` means some effects landed. Never retry one automatically
290
+ - **A viewer's own choices** (a tab, a collapsed panel) go in `useViewerStorage(key, initial)`, never `localStorage`, which collides across sessions
291
+ - **Assets and history through the hooks.** `useAssetUrl(ref)` for a `src`, `useAsset(ref, { as })` to read a file (it finds the locale's copy in `<dir>/<locale>/<file>`), and `usePastState(query)` for past rounds, rather than hand-rolled effects. The wrapper already sets `lang`, `dir` and `color-scheme` on the document
292
+ - **Every string** goes through `src/i18n.ts`
293
+ - **The facilitator's screen** is `session/`: rendered full-bleed beneath a thin band of host chrome, it owns teams, participants and game controls (`guides/session.md`)
294
+
295
+ ### Testing a surface
296
+
297
+ Every surface but the presentation scaffolds an `App.test.tsx`; the starter game's own tests are worked examples, and `pnpm run --silent sdk docs view guides/testing.md` is the reference.
298
+
299
+ - **Render through the helper**: `renderUserInterface()` from `@seriousgaming/sdk/ui/testing` (or `renderSessionUserInterface<SessionData>()`, or `renderSettingsEditor()` for setup and the scenario editor). It supplies every provider the host does; pass `data`, `settings`, `callbacks` and the rest as options
300
+ - **Never `vi.mock` the SDK, and never wire its providers by hand**
301
+ - **Use `fireEvent.change` on controlled inputs**: the mock `setData` does not update the value between `userEvent.type` keystrokes
302
+
303
+ ## Presentations
304
+
305
+ The `presentation/` slot holds the facilitator's slide deck, built with `@seriousgaming/deck` at the SDK's version. It reads every team's state and writes nothing. A facilitator exports it to PDF or PowerPoint and edits the copy, so:
306
+
307
+ - **Use the building blocks** (`Heading`, `Text`, `Bullets`, `Table`) for anything a facilitator might edit; anything else exports as a picture
308
+ - **Render with `session === null`**, showing labelled sample figures where results would go
309
+ - **Branch on settings, not the scenario name**, and draw a custom animation's end state when `useSlide().mode` is `'static'`
310
+ - **Route every word through i18n**, and keep one element per slide
311
+
312
+ ## LLM integration
313
+
314
+ Logic calls `callbacks.generateText()` only if its bundle lists `generateText` in `uses`; once listed it is always present on the generated `Callbacks`, so call it without `?.`. A surface reaches an LLM only through an endpoint. The dev server needs `SERIOUSGAMING_API_KEY` in the environment or `.env`, and `createTestSession` stubs it offline. See `guides/llm-integration.md`.
315
+
316
+ ## Keeping the SDK up to date
317
+
318
+ ```bash
319
+ pnpm run --silent sdk version # the installed version
320
+ pnpm run sdk docs view changelog.md # newest first: read every entry above your version
321
+ pnpm run sdk docs view guides/upgrading.md # a step for each breaking change
322
+ ```
323
+
324
+ Upgrade only when the user asks, or when the changelog has a fix the task needs, and then follow `.claude/rules/workflow-update-sdk-version.md`: it bumps the SDK, its tools and the deck together, and adopts the newer scaffold guidance without overwriting yours.
325
+
326
+ ## Troubleshooting
327
+
328
+ | Problem | Solution |
329
+ | ------------------------------------- | ------------------------------------------------------------------------------------------ |
330
+ | Types not updating | `pnpm run generate` |
331
+ | A capability is missing in code | Add it to the resource's `reads` or `writes`; `update` regenerates the types |
332
+ | A surface's write is refused | Add the capability to that surface's `writes` |
333
+ | Build failing | `pnpm run --silent sdk build --validate-only --json`, then read `diagnostics` |
334
+ | Edit not showing, or `SETTINGS_STALE` | A setting, `teams` or a capability's scope applies only at init: `sdk init` |
335
+ | A hook reads empty data in a surface | Two copies of a package with React context: add it to `resolve.dedupe` in `vite.config.ts` |
336
+ | Anything else | `pnpm run --silent sdk docs view troubleshooting.md` |
337
+
338
+ Keep `DESIGN.md` up to date as the game changes.
@@ -0,0 +1,144 @@
1
+ # DESIGN.md
2
+
3
+ This document describes the design of the **{{ name }}** game. Keep it up to date as you build.
4
+
5
+ Unless it was created with `--empty`, the package was scaffolded with a working game already in it: a lemonade stand, where each team decides every morning how many lemonades, iced teas, and cookies to make, pays for all of it, and pours away what does not sell. The prompts below are written for the game you are going to build instead. Answer them as you replace the model, and delete the examples as you go.
6
+
7
+ ## Overview
8
+
9
+ **Package identifier:** `{{ identifier }}`
10
+
11
+ A package is a game. The package keeps its dotted reverse-DNS identifier, set in `seriousgaming.sdk.json`; the game's is that with `.game` appended, and is never authored.
12
+
13
+ ### Purpose
14
+
15
+ What does this game model, and what decisions does it help players make? Who plays it, and what real-world situation does it stand in for?
16
+
17
+ **Example:** 'Models a retail inventory system, so store managers feel the trade-off between carrying costs and stocking out.'
18
+
19
+ ### Key concepts
20
+
21
+ The business concepts the game is made of.
22
+
23
+ **Example:**
24
+
25
+ - **Demand**: customers wanting to buy, per day
26
+ - **Inventory**: stock on hand
27
+ - **Reorder point**: the level that triggers an order
28
+ - **Lead time**: days between ordering and delivery
29
+
30
+ ### Assumptions and constraints
31
+
32
+ What the model simplifies, and what it deliberately leaves out.
33
+
34
+ **Example:**
35
+
36
+ - Demand is predictable, with no seasonality
37
+ - Lead times are constant
38
+ - Orders arrive whole rather than split across shipments
39
+
40
+ ---
41
+
42
+ ## Session shape
43
+
44
+ - **Ticks:** how many rounds, and what one round represents
45
+ - **Teams:** `independent` (each team on its own clock, and the default when `teams.mode` is omitted) or `shared-state` (every team advances together and can interact through shared capabilities)
46
+ - **Participants per team:** and whether several people write the same context at once, which is the thing that decides how state below has to be shaped
47
+ - **Locales:** which, and which is first (the game's name comes from the first)
48
+
49
+ ---
50
+
51
+ ## Capabilities
52
+
53
+ `capabilities/<name>/`: the game's state, one named group of variables each. A capability is named by its directory, so the identifier is a short name and the bundle authors none.
54
+
55
+ ### `<name>`
56
+
57
+ **Purpose:** why this data exists.
58
+
59
+ **Variables:**
60
+
61
+ - `VariableName` (type): what it means and its units. Default: value
62
+
63
+ **Example:**
64
+
65
+ ```
66
+ - `DailyDemand` (number): customers per day. Default: 50
67
+ - `LeadTime` (number): days to receive an order. Default: 3
68
+ ```
69
+
70
+ **Scope:** `team` (default), `team-readable` (other teams' surfaces may observe it live), `shared` (one value for the whole session, written by an aggregate engine in a shared-state game or by the session interface), or `private` (engine bookkeeping, never delivered to any surface).
71
+
72
+ **Written by:** the engine, a user interface, or both. If several participants on one team write it at the same time, say how it is shaped to survive that: a collection keyed by id rather than an array, and deletion rather than a sentinel.
73
+
74
+ ---
75
+
76
+ ## Engine
77
+
78
+ `engine/`: the code that computes the game. A game holds exactly one.
79
+
80
+ **Reads:** the capabilities it consumes, and why each is needed.
81
+
82
+ **Writes:** the capabilities it produces, and what it computes into them.
83
+
84
+ **Settings:** the settings resources it lists in `settings`: each property, its type, its default, and what it changes.
85
+
86
+ **What `Run` does**, in order. A simulation with several steps calls them as plain functions from its one `Run`:
87
+
88
+ ```
89
+ 1. Read the decisions the team submitted
90
+ 2. Work out demand from settings and the round
91
+ 3. Update inventory
92
+ 4. Compute revenue, costs and profit
93
+ ```
94
+
95
+ **Aggregate?** If a shared-state game's engine needs every team's context at once (a market price, a ranking, anything cross-team), export `InitAggregate` and `RunAggregate` beside `Init`. There is no flag to set: the build reads the exports. A per-team `Run` is optional and runs for every team before `RunAggregate` is called once, so a step that must follow the aggregate is called from inside `RunAggregate` rather than from `Run`. The aggregate functions receive shared capabilities once, as `shared.context`, and each team's own capabilities in `teams`, and write shared ones under `shared` only.
96
+
97
+ **Edge cases:** what happens at the boundaries, and anything that must hold true whatever the input.
98
+
99
+ ---
100
+
101
+ ## User interfaces
102
+
103
+ **`ui/`**: the participant surface. A game holds one, so several views are components inside it rather than separate resources.
104
+
105
+ - **Reads / writes:** the capabilities it renders and the ones players change through it
106
+ - **Views:** the tabs or panels, and what each is for
107
+ - **How a player submits:** what they change, and what tells them a write did not land
108
+
109
+ **`session/`**: the facilitator surface. What the facilitator watches, and what they can change mid-session.
110
+
111
+ **`waiting_room/`**: what players see before the session opens, if the game has one.
112
+
113
+ **`setup/`** and **`scenario_editor/`**: the form a facilitator fills in at session creation, and the screen for authoring lasting scenarios, if the game has them, and which settings resources each `configures`.
114
+
115
+ ---
116
+
117
+ ## The rest
118
+
119
+ Only the ones this game uses.
120
+
121
+ - **`hooks/<name>/`**: one lifecycle signal each (a player joining, for example), run outside the tick. Numbered directories where order matters
122
+ - **`validators/<name>/`**: a read-only gate that rejects a UI write. Reach for one where a contested write cannot be fixed by the shape of the state
123
+ - **`endpoints/<name>/`**: server-side work a UI, or with `visibility: "external"` a caller outside the game, calls by identifier. It is how a surface reaches an LLM or a document parser, and, with `writes`, how it changes state outside the tick
124
+ - **`tick_controller/`**: when a round ends on its own rather than on the facilitator's say-so, or when the game must end on a participant's write between ticks. A game ends by setting `maxTicks`, or by returning `complete: true` from the engine's `Run`
125
+ - **`scenarios/<name>/`**: settings overrides, keyed by settings identifier, plus any assets that change with them
126
+ - **`guide/`**: the facilitator's guide, as numbered markdown chapters under `content/<locale>/`
127
+
128
+ ---
129
+
130
+ ## Design decisions
131
+
132
+ ### Why the state is shaped this way
133
+
134
+ Why these capabilities, and why the boundaries fall where they do.
135
+
136
+ **Example:** 'Pricing and sales are separate capabilities because a later scenario changes how price is set without touching how sales are computed.'
137
+
138
+ ### Decisions taken and not revisited
139
+
140
+ Choices someone will otherwise reopen, with the reason they were made.
141
+
142
+ ### Not built yet
143
+
144
+ What is deliberately out of scope, and what might come later.