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

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 +36 -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 +27368 -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 +16884 -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 +209 -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 +236 -0
  69. package/dist/docs/cli-and-dev-server.md +217 -0
  70. package/dist/docs/cli-error-codes.md +154 -0
  71. package/dist/docs/cli-reference.md +2488 -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 +575 -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 +335 -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 +122 -3
@@ -0,0 +1,236 @@
1
+ # Changelog
2
+
3
+ All notable changes to the Serious Gaming SDK are recorded here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and the SDK adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## How to read this file
9
+
10
+ 1. Find your currently installed SDK version in `package.json` (or run
11
+ `cat node_modules/@seriousgaming/sdk/package.json | grep '"version"'`, or
12
+ `node_modules/@businesssimulations/seriousgaming-sdk/package.json` before 1.0).
13
+ 2. Read every entry **above** that version: those are the changes since you
14
+ last updated.
15
+ 3. If any entry is under **Breaking Changes**, address it before rebuilding.
16
+ Coming from 0.6, follow the [upgrading guide](guides/upgrading.md), which
17
+ turns every breaking change below into an ordered step.
18
+
19
+ Entries are grouped into: **Breaking Changes**, **Added**, **Changed**,
20
+ **Deprecated**, **Removed**, **Fixed** and **Security**. Releases before 1.0
21
+ are in the [0.x changelog](changelog-0.x.md).
22
+
23
+ ---
24
+
25
+ ## [Unreleased]
26
+
27
+ ### Breaking Changes
28
+
29
+ #### Packages
30
+
31
+ - **The packages are renamed and move to the public npm registry.** `@businesssimulations/seriousgaming-sdk` is `@seriousgaming/sdk`, `@businesssimulations/seriousgaming-deck` is `@seriousgaming/deck`, and `@businesssimulations/seriousgaming-sdk-tools` is `@seriousgaming/sdk-tools`, each with the same subpaths. They install from npmjs.com with no token. The `seriousgaming-sdk` command keeps its name. `@seriousgaming/sdk-tools` declares `@seriousgaming/sdk` as a peer dependency at the same version. Scaffolded files carry the new names: `package.json.liquid`, `vite.config.ts.liquid`, `vitest.config.ts.liquid`, `oxlintrc.json.template`, `global-setup.ts.template`, `README.md.liquid`, `CLAUDE.md.template` and the `workflow-create-presentation.md`, `workflow-create-user-interface.md`, `workflow-test-engine-or-ui.md` and `workflow-update-sdk-version.md` rules. See [step 1 of the upgrading guide](guides/upgrading.md#step-1-rename-the-packages)
32
+ - **Runtime plumbing leaves the SDK.** The host library moves from the SDK's `./host` to `@seriousgaming/sdk-tools/host`, with the host-side wire types `./ui` used to export, and the runtime helpers (`createGuardedCallbacks`, `deepMergeContext`, `projectContext`, `validateTickControllerResult`, `MANIFEST_VERSION` and others) move from the SDK root to `@seriousgaming/sdk-tools/runtime`. `isApiUseDeclared`, `isDeliverableToSurfaces`, `withheldCapabilityIds`, `resolveManifestVersion`, `removedSharedFlagError` and `tickControllerResultSchema` are removed, and `sdk-tools/client` no longer re-exports `ExecutionHistory`, `JsonPatchOperation`, `LLMGenerateOptions` or `GameContext`. No game needs them; a test harness that merged with `deepMergeContext` uses `mergingSetData` from `./ui/testing`. See [step 9](guides/upgrading.md#step-9-meet-what-the-build-and-runtime-now-enforce)
33
+
34
+ #### Settings
35
+
36
+ - **Settings resources replace `configurationSchema`.** A game's parameters live in `settings/<name>/settings.sdk.json`, holding `{ "definition": { "settingsSchema": {...} } }`, and a package may hold any number. Every property declares a default, and a top-level `required` is refused. The engine, UIs, the tick controller, hooks, validators and endpoints list the settings they read in `"settings": [...]` and receive exactly those, keyed by identifier, as `settings` (where logic received `config`, and `useUserInterfaceData()` and `useSessionUserInterfaceData()` returned `config`). Settings are fixed when play starts. The build refuses a `configurationSchema` on any bundle, an empty one included. Generated `types.ts` exports `Settings` and `defaults.ts` `defaultSettings`, in place of `Config` and `defaultConfig`, and `ModuleFunctionArgs` is `LogicFunctionArgs`. The `CLAUDE.md.template`, `DESIGN.md.liquid`, `workflow-create-engine.md`, `workflow-create-user-interface.md` and `workflow-test-engine-or-ui.md` guidance and every logic and UI scaffold are updated. See [step 2 of the upgrading guide](guides/upgrading.md#step-2-move-configuration-into-settings-resources)
37
+ - **The game-level settings are the built-in `game` settings, and `ticks` is `maxTicks`.** Rename `ticks` in `seriousgaming.sdk.json` to `maxTicks`, and move a scenario's `ticks`, `teams` and `waitingRoom` into its `settings.game` (`ticks` again as `maxTicks`); the build refuses the old fields, naming what replaced each. `teams.min`, `teams.max` and the `perTeam` counts must be whole numbers. A presentation reads `game.maxTicks` (was `game.ticks`). A scenario's `teams.defaultNames` replaces the game's list whole, rather than falling back to the game's name past its end. A settings resource named `game` is refused with `SETTINGS_RESERVED`. In the dev server, an edit to the manifest's `maxTicks` or team bounds waits for the next initialisation, as every settings edit does. The `CLAUDE.md.template`, `DESIGN.md.liquid` and `README.md.liquid` scaffolds say `maxTicks`. See [step 2g](guides/upgrading.md#2g-rename-ticks-to-maxticks-and-move-a-scenarios-game-fields-into-settingsgame)
38
+ - **A single-team game locks `teams.max`.** A game is single-team only when it declares `teams.max: 1` and locks it with `gameSettingsConstraints: { "teams": { "max": { "readOnly": true } } }`. A `teams.max` of 1 left unlocked is treated as possibly multi-team, as an omitted one is, so an independent game whose engine writes a shared capability fails the build until it adds the lock, and its message says so. The rule that a scenario or setup could not widen a `teams.max` of 1 is gone: the lock is what does that now. See [step 2g](guides/upgrading.md#2g-rename-ticks-to-maxticks-and-move-a-scenarios-game-fields-into-settingsgame)
39
+ - **The game-level `configuration` block is removed, and a scenario's `configuration` is `settings`.** A game sets its own values as its settings' defaults, and the build refuses both old fields. Scenario overrides, `sdk configure` overrides and `configures` are keyed by settings identifier. `sdk update package` no longer accepts a `configuration` block
40
+ - **Setup and the scenario editor are slots.** `configuration-user-interfaces/` and `configurationSurfaces` are removed, and the build refuses both: a quick-config UI moves to `setup/setup.sdk.json`, a scenario builder to `scenario_editor/scenario_editor.sdk.json`. Both use `SettingsEditorWrapper` and `useSettingsEditor()` (`{ settings, updateSettings, configures }`), replacing the `useConfigurationUserInterface*` hooks and types, and `renderSettingsEditor` replaces `renderConfigurationUserInterface`. `sdk new setup` and `sdk new scenario_editor` take no name. The configuration step exists exactly when a game has `setup`, and the host refuses `requestPlay` from the scenario editor. `CLAUDE.md.template`, `DESIGN.md.liquid`, `README.md.liquid`, `workflow-create-endpoint.md` and `workflow-test-engine-or-ui.md` are updated. See [step 3](guides/upgrading.md#step-3-move-configuration-uis-into-the-setup-and-scenario-editor-slots)
41
+
42
+ #### Logic
43
+
44
+ - **The engine, hooks and writing endpoints return what they write under `capabilities`.** `Init`, `Run` and a hook's `Run` return `{ capabilities: { ...context, sales } }`, aggregate functions `{ shared: { capabilities }, teams: [{ capabilities }] }`, and a writing endpoint `{ response, capabilities }` rather than `{ response, context }`. A result in the old shape fails the call with a message saying how to wrap it. An engine split into steps types each step as `StepFunction`. `sdk build` emits `manifestVersion: 3`. The `engine`, `hook` and `endpoint` logic templates and their tests, `CLAUDE.md.template` and the `workflow-create-engine.md`, `workflow-create-endpoint.md` and `workflow-test-engine-or-ui.md` rules are updated. See [step 4](guides/upgrading.md#step-4-return-what-logic-writes-under-capabilities)
45
+ - **Shared state has its own channel.** `InitAggregate` and `RunAggregate` receive the shared store once, as `shared.context`, and each `teams[i].context` carries that team's own capabilities only; a shared capability returned in a `teams` entry fails the init or tick. Type aggregates as `RunAggregateFunction<TeamContext, SharedContext, Settings>`. A session UI's `SessionData` is `{ shared, teams }`, and its generated `types.ts` adds `TeamContext`, `SharedWrites` and `SessionData`, with `SharedContext` now covering shared reads too. `createMockSharedState` joins the UI testing helpers. The aggregate engine and session UI templates, `DESIGN.md` and the starter game are updated. See [step 5](guides/upgrading.md#step-5-read-shared-state-from-its-own-channel)
46
+ - **One name for each thing.** `meta.currentTick` is `meta.tick`; every count of completed ticks (`useUserInterfaceData().currentTick`, session and presentation team `tick`, endpoint `session.tick`, CLI JSON `currentTick`) is `completedTicks`; one `TimerState` replaces five timer types, with `enabled`, `paused`, `intervalMs` and `pausedMs`; a tick controller returns `enabled`, `intervalMs` and `complete` and always receives `teams` as `{ teamId, index, context }`; `teamName` is `name`; `Participant` replaces `SessionParticipant`, `UserInterfaceTeamParticipant` and `UserInterfaceParticipant`; `TeamInfo` replaces `EndpointTeam`; and the player-surface hooks are `useUserInterfaceViewerRole`, `useUserInterfaceOwnTeam`, `useUserInterfaceOwnTeamJoinLink` and `useUserInterfaceCrossTeamData`. See [step 6](guides/upgrading.md#step-6-one-name-for-each-thing)
47
+ - **Completion is `complete` and a record's time is `recordedAt`.** `isComplete` is `complete` on an endpoint's `session` and team statuses, past-state entries and the state projection, and a past-state entry's `timestamp` is `recordedAt`. See [step 6d](guides/upgrading.md#6d-completion-record-times-blob-retention-and-the-manifest-types)
48
+ - **The settings-editor blob retention is `settings`.** `writeBlob(data, { retention: 'config' })` is refused; pass `retention: 'settings'`, which is also what a setup or scenario editor upload is stored with
49
+ - **The manifest types take manifest names.** `SDKConfiguration`, the authored `seriousgaming.sdk.json`, is `PackageManifest`; `Configuration`, the built `seriousgaming.json`, is `ResolvedPackageManifest`; and `LogicConfiguration` is `LogicLayout`
50
+ - **Timer durations are milliseconds, which the compiler cannot catch.** `intervalTick(intervalMs)`, a tick controller's `intervalMs`, a settings `intervalMs` default and a session UI's `extendTick(byMs, teamId?)` take milliseconds where they took seconds: `intervalTick(60)` is refused as below the 30-second minimum, and `extendTick(120)` extends by 120 ms. See [step 6c](guides/upgrading.md#6c-timers-in-milliseconds)
51
+ - **A validator names what it gates.** Rename `Run` to `UpdateData`; the build refuses a validator exporting `Run`, or neither gate. The `validator/logic.ts.liquid` scaffold and its test export `UpdateData`. See [step 8](guides/upgrading.md#step-8-rename-each-validators-run-to-updatedata)
52
+ - **Logic may not import Node built-ins.** `sdk build`, `dev` and `tick` refuse one with `LOGIC_NODE_BUILTIN`, and the dev server no longer gives logic `require`, matching production
53
+ - **Logic argument types require `meta.now` and `participants`**, so a test that builds arguments by hand passes both, or uses `logicArgs` (see Added). A test double typed as a generated `Callbacks` must supply every declared service, and `LogicCallbacks` takes `ApiUse` values. See [step 10](guides/upgrading.md#step-10-update-the-tests)
54
+
55
+ #### Endpoints
56
+
57
+ - **`agentCallable` is replaced by `visibility`.** Replace `"agentCallable": true` with `"visibility": "external"`, and delete `"agentCallable": false`; the build refuses the old field. `sdk endpoint list --agent` is `--external`, and `caller.userInterfaceIdentifier` is optional. See [step 7](guides/upgrading.md#step-7-update-endpoints)
58
+ - **An external endpoint's request is an object.** The build refuses an endpoint with `"visibility": "external"` whose `requestSchema` is not `"type": "object"` at the top (`EXTERNAL_REQUEST_NOT_OBJECT`), since an outside caller, such as an AI agent, passes named arguments. Internal endpoints are unaffected. See [step 7a](guides/upgrading.md#7a-replace-agentcallable-with-visibility)
59
+ - **Handlers receive `settings`, `session` and, team-scoped, `team`.** The generated `Handler` type requires them, so a unit test building arguments by hand passes them, or uses `logicArgs.endpoint`. A session-scoped handler's type has no `team`
60
+ - **`caller.teamId` is the caller's own team**, absent for a facilitator or a caller outside the game, where it held the endpoint's target team. Read the target from `team`. See [step 7d](guides/upgrading.md#7d-read-the-target-team-from-team)
61
+ - **The scenario editor calls only endpoints that need no session.** The build refuses a `scenario_editor` listing an endpoint that declares `reads`, `writes` or `actions` (`EDITOR_ENDPOINT_STATEFUL`), and the dev server answers a call from the editor with no session: the endpoint's `settings` resolved from the scenario being edited and the editor's unsaved changes, an empty `context`, no participants, a `session` of nothing played, and a `caller` of the new role `'author'`, not `'facilitator'`, since whoever creates a scenario may not be a facilitator, with no `sessionId`, which is now optional on `EndpointCaller`. The `workflow-create-endpoint.md` rule says so. See [step 7e](guides/upgrading.md#7e-give-the-scenario-editor-endpoints-that-need-no-session)
62
+ - **Endpoint failures are typed.** A surface's endpoint call rejects with `EndpointError` (`kind`, `outcome`, `applied`, `failedStep`), `callEndpoint:response` carries those fields in place of `error`, and `Session.query` and `createTestSession().send` answer `{ type: 'endpointFailure', failure }` instead of throwing
63
+
64
+ #### Surfaces
65
+
66
+ - **A player surface writes only what it declares.** A game UI or waiting room write (or `deletes` path) naming a capability outside its `writes`, or any key that is not a capability, is refused as `denied`
67
+ - **A session UI's team write changes only what it declares.** A write through `setData(teamId, update)` that changes a capability outside the session UI's `writes`, or names a key that is not a capability, is refused as `denied`, where 0.6 applied it. A capability carried back unchanged is still accepted, so a surface that returns a team's whole context with one field edited keeps working. Add the capability to the session UI's `writes`
68
+ - **`uses` on user interfaces is removed**, and the build refuses it. It never had an effect; a surface reaches a service through an endpoint that declares it
69
+ - **Demo mode is removed.** `demo` in the manifest and scenarios, `GameDemo` and `sdk dev --demo` are gone, and the build refuses the field. Use a walkthrough run to show a game
70
+
71
+ #### CLI
72
+
73
+ - **The CLI's results are a contract.** With `--json`, every command writes one envelope: `{ "ok": true, "command", "data", "diagnostics", "warnings" }` on success and `{ "ok": false, "command", "error": { "code", "message", "hint", "details" }, "diagnostics", "warnings" }` on failure. `new`, `update` and `delete` used to print `{ "success", ... }`; `build --validate-only --json` puts its summary in `data` and each finding in `diagnostics`; `package bundle`, `package version`, `package publish`, `configure`, `blob upload`, `endpoint list`, `endpoint audit` and `dev status` put their result in `data`; and a failing `endpoint call` reports in `error.details`. See [step 11](guides/upgrading.md#step-11-update-scripts-and-agents-that-drive-the-cli)
74
+ - **Exit codes say what kind of failure it was.** 1 is a bug in the CLI, 2 a usage error, 3 an invalid package, 4 a refusal by the game (including a failed tick) and 5 the environment, where every failure used to exit 1
75
+ - **`delete --force` is `delete --yes`**, which also answers an interactive `update`'s confirmation
76
+ - **`blob upload --config` is `blob upload --settings`**, and its `retention` reads `settings`. `tick --json` and `view` report `complete` in place of `isComplete`, and `view --tick N` entries carry `recordedAt` in place of `timestamp`
77
+ - **The CLI never waits on a prompt without a terminal.** Without a terminal on both stdin and stdout, with `--json` or `SERIOUSGAMING_JSON`, or with `CI` set, a command that would ask fails with exit 2 and `PROMPT_REQUIRED`. Stdin is read to its end rather than for 100ms
78
+ - **`new package` fails when `pnpm install` fails** in the new package (exit 5, `INSTALL_FAILED`). The files are written; run `pnpm install` to finish
79
+ - **`session.needsConfiguration` is `session.inSetup`** on session state (`createTestSession().state()`, `@seriousgaming/sdk/e2e`, the dev server's `/api/session`) and in `sdk reconfigure --json`, matching the spec's name for the setup state
80
+
81
+ ### Added
82
+
83
+ #### Logic and testing
84
+
85
+ - **`logicArgs` in `@seriousgaming/sdk/testing`.** `logicArgs.run({ context, settings: defaultSettings })` builds the arguments a logic function takes, filling `meta` (a fixed `seed` and `now`), the roster and offline stub callbacks around what the test names, typed from the function it is passed to. There are builders for `init`, `run`, `aggregate`, `hook`, `endpoint`, `tickController`, `validator` and `requestTick`, and the stubs are exported as `createStubCallbacks()`. See [Testing](guides/testing.md#testing-the-run-function)
86
+ - **Typed test sessions.** `createTestSession<Context, SharedContext>(options)` types what a test reads back and writes, and adds `tick({ team })`, `requestTick(team)`, `setData(team, patch, { deletes, as })`, `setSharedData(patch)`, `team(index)` and `shared`, which holds the shared store under `context` as a session surface's `sessionData.shared` does. `createStubServices`, `stubGenerateText` and `TestServices` are exported from `sdk-tools/client`. See [Engine testing](guides/engine-testing.md#injecting-decisions)
87
+ - **`meta.now` on every logic call**, the operation's time in milliseconds, the same for every team in one tick and controlled by the session clock in tests, and **a read-only `participants` roster**: per-team logic sees its team; aggregates, the tick controller and session-scoped endpoints see everyone
88
+ - **Logic can delete keys.** `Init`, `Run`, a hook's `Run`, each level of an aggregate result and a writing endpoint may return `deletes` beside `capabilities`, with the same paths and rules as a UI's `setData` deletes. See [Removing a key](guides/engine.md#removing-a-key)
89
+ - **The engine can keep private memory between ticks.** `Init`, `Run` and the aggregate functions may return `state` beside `capabilities` and receive it back on the next call, typed with the new last type parameter of the engine function types. No other reader sees it, undo and reset rewind it, and it must be JSON-serialisable. New types `EngineResult` and `EngineFunctionArgs`; `createTestSession`'s `state()` reports it, and the `engine/logic.ts.liquid` scaffold mentions it. See [Keeping memory between ticks](guides/engine.md#keeping-memory-between-ticks)
90
+ - **The engine can end the game.** A tick's `Run` result, or a `RunAggregate` result's `shared` or `teams` entries, may carry `complete: true`, working exactly as a tick controller's completion. New types `RunResult` and `RunAggregateTickResult`. See [Game completion](guides/tick-controller.md#game-completion)
91
+ - **`Init` is optional.** An engine without one starts each team from its capabilities' defaults
92
+ - **`StepFunction` and `LogicResult` types**, for an engine that splits `Init` or `Run` into steps and for the `{ capabilities }` result shape
93
+ - **A service a resource declares is always there.** A declared `generateText`, `extractText`, `generateSpeech` or `writeBlob` is typed as present on the resource's generated `Callbacks`, so the `?.` and `!` guards can go, and a runtime that cannot provide a declared service refuses the package at load
94
+ - **`RequestTick` validators** refuse a participant's or observer's request for the next tick, receiving `{ context, requestedTick, actor, settings, meta }`; the facilitator's own ticks and timers are never gated. Generated validator types gain `RequestTickFunction`. See [Gating tick requests](guides/validators.md#gating-tick-requests)
95
+
96
+ #### Settings, capabilities and scenarios
97
+
98
+ - **A game can lock a setting.** `readOnly: true` on any property of a `settingsSchema` keeps the game's own value: the build refuses a scenario that sets it (`SETTINGS_LOCKED`, pointing at the value), and a setup screen or scenario editor that changes it is refused as `denied`, naming the setting. Locking an object locks everything in it. The dev console's Save leaves locked values out of the scenario it writes. See [Lock a setting](guides/settings.md#lock-a-setting)
99
+ - **A game can narrow the built-in `game` settings.** `gameSettingsConstraints` in `seriousgaming.sdk.json` takes `minimum`, `maximum`, `enum` and `readOnly` for each game setting, for example four to ten rounds or a fixed team ceiling. The build refuses a constraint that widens the SDK's schema (`GAME_CONSTRAINT_INVALID`) and a game whose own values break its constraints (`GAME_CONSTRAINT_VIOLATED`), and merges the rest into the `game` settings schema it emits. A bound rules out `null`, so the generated type of a bounded `maxTicks` is `number`. See [Narrow or lock the game settings](guides/settings.md#narrow-or-lock-the-game-settings)
100
+ - **A scenario editor's draft is held to the settings schemas.** An edit outside a setting's range is refused as `denied`, as a setup write already was, and the dev console's Save refuses a scenario whose settings break a schema
101
+ - **`sdk new settings`, `sdk update settings` and `sdk delete settings`.** `new` takes a JSON definition or prompts for each property, and refuses one without a default; `update` renames a settings resource and re-keys every reference; `delete` removes it and every reference. The interactive `new` commands ask which settings a resource reads. See [Create settings](recipes/create-settings.md)
102
+ - **A setup screen can set the number of rounds.** The tick limit, the team bounds, the participant guidance and the default team names are the built-in `game` settings, so a setup UI or scenario editor that lists `game` in `configures` edits them with `updateSettings`, a scenario overrides them under `settings.game`, `sdk configure` sets them under `game`, and any resource can declare `settings: ["game"]` to read them. Generated `types.ts` types them, and `GameSettings` is exported from the SDK. Only `waitingRoom.openOnStart` stays out of setup's reach, and play cannot start while the team count is outside the bounds setup chose. The factory example's setup and scenario editor set the number of rounds. See [The game settings](guides/settings.md#the-game-settings)
103
+ - **Every resource reads settings directly**, so scenarios and setup reach endpoints, hooks and validators, and a surface no longer needs the engine to copy a value into a capability. The starter game reads its menu from a `menu` settings resource. See [Reading settings rather than copying them](guides/settings.md#reading-settings-rather-than-copying-them)
104
+ - **Capabilities that undo and reset leave alone.** `"lifetime": "session"` keeps a value exactly as it was across an undo, a per-team reset and a session reset, for facilitator state such as a pacing gate or an announcement. The build refuses one in the engine's `reads` or `writes`. See [Lifetime](guides/capabilities.md#lifetime)
105
+ - **`tsType` on a capability schema property** names the TypeScript type the generated `types.ts` uses, so reads of a loose object need no cast. See [A TypeScript type of your own](guides/capabilities.md#a-typescript-type-of-your-own)
106
+ - **`scenarioFormat` in `seriousgaming.sdk.json`**, an integer from 1. Bump it when a release would make a saved custom scenario wrong, and a runtime refuses scenarios saved under another format. See [Scenario format: when to bump it](guides/scenarios.md#scenario-format-when-to-bump-it)
107
+ - **`requires` in `seriousgaming.sdk.json`**: an optional list of the runtime features a game depends on. A runtime without one refuses the package at load and names it, and `sdk build` does the same for a feature the installed SDK does not support. No optional features exist yet
108
+
109
+ #### Endpoints
110
+
111
+ - **External endpoints.** `"visibility": "external"` publishes an endpoint to any caller outside the game (a facilitator's AI agent, a platform API or another integration), whose handler sees `caller.role` as `'external'`. `sdk endpoint call --external` makes a call the way an outside caller does
112
+ - **Endpoints know their team and where the session stands.** A team-scoped handler receives `team: { teamId, index, name }`, and every handler `session`: the ticks completed, the tick limit, the waiting room, completion, and each team's standing. Declare `roster: true` to list each team's participants too
113
+ - **Endpoints can act on the session.** An endpoint that declares `actions` may return `actions: [{ type: 'setWaitingRoom', open: false }, { type: 'tick' }]` beside its response, carried out after its write in one step: tick, the timer controls, the waiting room, renaming and adding teams, moving participants and the locale. Export `Plan` and `Respond` in place of `Handle` to answer after the actions land
114
+ - **A session-scoped endpoint can write individual teams**, returning `teams: { 'team-1': { ... } }`: every team's patch lands, or none does
115
+ - **Endpoints can take files.** Mark a request property with `"blob": { "accept": [...], "maxBytes": ... }` and the handler receives a checked `blob:` reference, whether the caller uploaded first or sent the bytes inline as a `data:` URI
116
+ - **`EndpointError`** and the `EffectStep`, `EndpointFailure`, `EndpointFailureOutcome` and `EndpointCallResult` types
117
+ - **The CLI and dev server do what an outside caller does.** `sdk endpoint call` gains `--principal kind:id[:name]`, `--attach field=path` and `--save-blobs dir`, and prints a failure's kind, message and landed steps; `sdk endpoint audit` lists every endpoint call that changed the session; `sdk endpoint list` shows each endpoint's actions. The dev server's `listEndpoints`, `principal` and `getEndpointAudit` serve an outside caller, and an external response carries a URL for every `blob:` reference
118
+ - **The build fails on an endpoint that exports neither `Handle` nor `Plan`**, or whose schema does not compile, where both used to fail only at run time
119
+
120
+ #### User interfaces
121
+
122
+ - **A game can ship a presentation.** A UI bundle in the `presentation/` slot becomes the facilitator's slide deck: read-only, shown during a session with every team's results or with no session at all. Every runtime offers it. Build it with the new `@seriousgaming/deck` package, published in lockstep with the SDK, which gives slides, click-to-reveal steps, transitions, building blocks in the theme's colours, and export to PDF, editable PowerPoint and PNG. `sdk new presentation` scaffolds a starter deck, the dev console shows it at `/presentation`, new packages depend on the deck, and the scaffold gains a `workflow-create-presentation.md` rule and a 'Presentations' section in `CLAUDE.md.template`. See [Building presentations](guides/presentation.md)
123
+ - **UI hooks for assets and past state.** `useAssetUrl(ref)` returns an asset's URL, or `undefined` until the host has sent them; `useAsset(ref, { as, locale? })` fetches and reads an asset, trying the locale's copy first; `usePastState(query)` wraps `getPastState` and drops overtaken answers. See [Reviewing past ticks](guides/ui.md#reviewing-past-ticks)
124
+ - **`useViewerStorage(key, initial)`** on every surface: browser storage namespaced by session, viewer and surface, falling back to memory where storage is blocked. The handshake carries an opaque `sessionKey` for it, and the `render*` test helpers take a `sessionKey` option
125
+ - **The wrappers set `lang`, `dir` and `color-scheme` on the document**, in step with the locale and the theme. The scaffolded `main.tsx` files no longer set `lang` and `dir` themselves
126
+ - **`contrastText` on every theme colour**, meeting WCAG AA against it (`ThemeColorRole`, `ThemeContrastText`). The `theme.ts` templates, `CLAUDE.md.template`, `workflow-create-user-interface.md`, `workflow-create-engine.md` and `workflow-test-engine-or-ui.md` are updated
127
+ - **`failure.retry()` on `useMutationReporter`** after a `conflict`, for a person-initiated 'Try again'
128
+ - **Typed session writes and test fixtures.** A session UI's generated `types.ts` adds `TeamWrites`, the team-scope capabilities in its `writes`, and `useSessionUserInterfaceData<SessionData, Settings, SharedWrites, TeamWrites>()` types the per-team `setData(teamId, update)` with it, so a team write naming a shared capability fails to compile. `createMockTeamState<TeamContext>`, `createMockSharedState<SharedContext>` and `renderSessionUserInterface<SessionData>` type a test's fixtures the same way. Every parameter defaults to today's untyped form, so existing code compiles unchanged. See [Session UIs](guides/testing.md#session-uis)
129
+ - **A session UI can delete keys.** Both forms of a session surface's `setData` take `{ deletes }` last, as a player surface's does: `setData(teamId, {}, { deletes })` removes from one team and `setData({}, { deletes })` from the shared store. Each path must fall under a capability the surface writes, of the form's scope, or the write is refused as `denied`. `mergingSetData` in `@seriousgaming/sdk/ui/testing` takes the same options and applies them after the merge, as the host does. See [Removing a key](guides/session.md#removing-a-key)
130
+ - **A session UI can delete an empty team** with `callbacks.deleteTeam(teamId)` in an independent session, once its participants have been moved out. The index is left as a gap, and undo never brings the team back. The dev console has a Delete team button. See [Deleting a team](guides/session.md#deleting-a-team)
131
+ - **`pauseTimer` and `resumeTimer` are awaitable**, resolving once the host has applied the change and rejecting with a `MutationError` when it refuses
132
+ - **Every past-state entry carries `index` and `name`**, and the mode-colour theme types (`UserInterfaceModeColors` and siblings) are exported
133
+
134
+ #### CLI and dev server
135
+
136
+ - **Stable error codes and `--json` on every command.** Each failure carries an `UPPER_SNAKE` code and an exit code from its category, with a `hint` where there is something to do, and `SERIOUSGAMING_JSON=1` turns `--json` on for a whole job. See [CLI error codes](cli-error-codes.md), and [Check a package from a script](recipes/check-a-package-from-a-script.md) for driving the CLI from CI or an agent
137
+ - **Diagnostics that say where.** A failed `build`, or any command that loads the package, lists every finding at once in `diagnostics`, each with a code, the `file`, an RFC 6901 `pointer`, the `resource` and a `hint` such as `Did you mean 'BaseDemand'?`. A logic error carries `LOGIC_ERROR` with the TypeScript file, line and column and the tick and team it threw for
138
+ - **`SETTINGS_STALE` (exit 4)** for a command that would advance or change play while the session no longer matches the package, and `stale` in `sdk view`'s data
139
+ - **A dev server for tests and recordings, beside your own.** `sdk dev --isolated` keeps no lock file, ignores saved configuration and keeps uploads in a temporary directory; `--port auto` takes free ports; `SERIOUSGAMING_DEV_URL` points session commands at a server; and `startDevServer(options)` from `@seriousgaming/sdk-tools/client` starts one in a test runner. See [Run a second server beside your own](cli-reference.md#run-a-second-server-beside-your-own)
140
+ - **The dev server picks up more without a restart**: edits to `seriousgaming.sdk.json`, a surface added while it runs, and a capability added while it runs, seeded from its defaults. `sdk dev --reinit-on-change` restarts the game at tick 0 on every edit that applies only at initialisation
141
+ - **`SERIOUSGAMING_DEV_STATE_DIR`** keeps the dev server's lock file outside the package
142
+ - **For runtimes: the manifest schema and the logic-result checks.** `@seriousgaming/sdk-tools/schema.json` is the JSON Schema of this release's manifest version, its `ResolvedPackageManifest` the built `seriousgaming.json`, and `@seriousgaming/sdk-tools/runtime` adds `assertJsonSerialisable`, `readEngineResult`, `readAggregateResult` and `readTickControllerResult`. A game needs none of them
143
+ - **The SDK has a README** saying which subpaths carry semver guarantees (the root, `./ui` and `./ui/testing`)
144
+
145
+ ### Changed
146
+
147
+ - **The session scaffold uses the generated types throughout.** `sdk new session` writes an `App.tsx` that passes `SharedWrites` and `TeamWrites` to `useSessionUserInterfaceData`, so both forms of `setData` are typed, and an `App.test.tsx` that renders through `renderSessionUserInterface<SessionData>()` with fixtures from `createMockTeamState<TeamContext>()` and `createMockSharedState<SharedContext>()` in place of a hand-built provider tree, so a shared capability on a team fixture fails to compile. The guidance names the type argument too: merge the changes to `CLAUDE.md.template`, `claude/rules/workflow-create-user-interface.md` and `claude/rules/workflow-test-engine-or-ui.md`, and the session templates `session/src/App.tsx.liquid` and `session/src/App.test.tsx.liquid`, into a game you scaffolded earlier
148
+ - **Docs restructured: one guide per resource kind, steps-only recipes, and generated CLI, API and error-code reference.** Each concept page is folded into its guide, recipes are cut to their steps and link to the guide, the CLI reference, the API reference (`api/`) and [CLI error codes](cli-error-codes.md) are generated from the code, the upgrading guide covers 0.6 to 1.0 only, and releases before 1.0 move to [changelog-0.x.md](changelog-0.x.md). The scaffold's `CLAUDE.md.template` and the `workflow-test-engine-or-ui.md`, `workflow-create-user-interface.md` and `workflow-create-presentation.md` rules now link to the shipped guides for settings resolution, the UI test harness and presentation habits rather than restating them; merge them from `dist/templates/package/`. The same pages are published as a versioned docs site, one directory per release plus `latest/`, with an `llms.txt` index and an `llms-full.txt` holding the whole documentation as one file for agents
149
+ - **The published packages declare Node 24 or later** in `engines`, and link to the docs site at https://sdk.seriousgaming.com/ rather than to the source repository. The scaffold's `workflow-update-sdk-version.md` rule names all three packages it bumps; merge it from `dist/templates/package/`
150
+ - **The packages carry licences.** `@seriousgaming/sdk` and `@seriousgaming/deck` are Apache-2.0. `@seriousgaming/sdk-tools` is under the Serious Gaming SDK Tools Licence in its `LICENSE` file: use it to build, test and publish games for the Serious Gaming platform; files it generates or copies into your project are yours under Apache-2.0. All three were `UNLICENSED`
151
+ - **A settings edit no longer applies mid-session.** At tick 0 the dev server re-initialises on the edit; once a tick is played it keeps the settings the session started with, the console offers a Re-initialise button, and `sdk tick`, `sdk undo`, a context patch and a writing endpoint call are refused with `SETTINGS_STALE` until `sdk init`. The same applies to `teams`, `waitingRoom.openOnStart` and `identifier`, adding or removing `setup`, and removing a capability or changing its `scope`. See [What reloads](cli-reference.md#what-reloads)
152
+ - **The dev server listens on `localhost` only.** It used to accept connections from any machine on the network. To test on a phone or tablet, start it with `pnpm run sdk dev --lan`. See [Start the dev server](cli-reference.md#start-the-dev-server)
153
+ - **Test sessions are offline by default.** `createTestSession` stubs `generateText`, `generateSpeech` and `extractText` with deterministic answers; pass `services: 'platform'` to reach the real services. A hand-built theme object needs `contrastText`
154
+ - **Surfaces never retry a `conflict` themselves**; a runtime with optimistic concurrency retries a losing write before reporting it
155
+ - **A tick whose logic throws is `tick-failed`**, where the dev server reported it as `error`. A surface's `requestTick` rejects with `kind: 'tick-failed'`, and `sdk tick` fails with `TICK_FAILED`
156
+ - **Every runtime offers walkthrough runs**, so `setWalkthroughLink` always carries a link and `useSessionUserInterfaceWalkthroughLink()` is `null` only until the host sends it. `callbacks.resetWalkthrough()` is refused as `denied` while the session has no walkthrough, and for a named team
157
+ - **`sdk team remove` refuses a team with participants, and any team in a shared-state session.** Move participants out with `sdk participant move` first. A team added later at the freed index is a new team
158
+ - **A tick controller's `intervalMs` is optional when its timer is off**, and a game with neither `maxTicks` nor a tick controller builds with a warning, since its engine can end it with `complete: true`
159
+ - **A returned capability merges one level deep.** Each variable an engine, hook or endpoint returns replaces its old value whole, objects included, so `PendingJunction: {}` empties it. The dev server and `createTestSession` already did this; it is now the rule for every runtime. See [How a result folds into the context](guides/engine.md#how-a-result-folds-into-the-context)
160
+ - **A saved custom scenario is validated against the package each time it is used**, and refused with the offending keys named rather than opened on defaults
161
+ - **A CLI command notices a dev server started by other tools.** After an upgrade or rebuild, a command replaces a server it started in the background, and fails with `SESSION_STALE` (exit 5) for one you started with `sdk dev`
162
+ - **Every session command repeats what the dev server could not apply live** (`SETTINGS_STALE`, `RELOAD_FAILED`, `CAPABILITY_SEEDED`) on stderr and in `warnings`, and `GET /api/session` reports them
163
+ - **The dev server runs the checks `sdk build` runs**, at start and after every edit. A package `build` would refuse still runs, so you can fix it while you watch, but each error is printed on stderr, shown in a banner on the console, and listed in `GET /api/session` `warnings` with its diagnostic code (for example `REFERENCE_UNKNOWN`) and `build: true`, so every session command repeats it. See [Where the session lives](cli-reference.md#where-the-session-lives)
164
+ - **A schema error the SDK cannot explain points at the upgrading guide**, and a refused shared write from a session UI says why
165
+ - **The `@seriousgaming/sdk/e2e` helpers find the dev server through `SERIOUSGAMING_DEV_URL`** and take `{ url }` beside `{ port }`
166
+ - **The UI library no longer logs every host ping to the browser console**
167
+ - **The scaffolded `CLAUDE.md` is half the length and leads with what not to do.** It opens with 'Non-negotiables' and an index of `.claude/rules/`, points at the docs shipped inside `@seriousgaming/sdk-tools` (`sdk docs view`), teaches the CLI's result contract (envelope, exit codes, `--yes`, `diagnostics`), no longer runs `init` after `sdk configure`, and bumps `@seriousgaming/deck` with the SDK. `CLAUDE.md.template`, `workflow-create-engine.md`, `workflow-create-endpoint.md` and `workflow-create-user-interface.md` cover declared writes, typed endpoint failures, retries and the Node rule. `workflow-create-endpoint.md`, `workflow-create-engine.md`, `workflow-update-sdk-version.md`, `DESIGN.md.liquid`, `README.md.liquid` and `oxlintrc.json.template` catch up with manifest version 3. `new package --empty` writes `CLAUDE.md` and `.claude/` too. Merge them from `dist/templates/package/`
168
+ - **A missing platform API key is an environment failure.** A tick or endpoint call whose logic called `generateText` with no key configured fails with `LLM_NOT_CONFIGURED` (exit 5), where it was `TICK_FAILED` or `HANDLER_ERROR` (exit 4), with a one-line message and a hint saying how to set the key, in place of the multi-line banner
169
+ - **`sdk init` reports the scenario it started on** as `scenario` in its output, `null` when there is none
170
+ - **`new`, `update` and `delete` report paths relative to the package root** in `filesCreated`, `filesUpdated`, `filesDeleted` and `directoriesRenamed`, where they were absolute
171
+ - **A manifest failure names `seriousgaming.sdk.json`, not its absolute path**, and an unsupported `requires` carries a `FEATURE_UNSUPPORTED` diagnostic pointing at the entry
172
+ - **The scaffold's `pnpm run lint` fails on a warning** (`oxlint --max-warnings=0`)
173
+ - **The starter game declares its scenario format and describes its settings for scenario authors.** `new package` writes `scenarioFormat: 1` into `seriousgaming.sdk.json`, leaves `customScenarios` out so a new game opts into facilitator-saved scenarios deliberately, and every settings property carries a `title`, a `description` with its units, a range and, for `CurrencySymbol`, an `enum`. Whole-number settings are typed `integer`, so a scenario that sets one to a fraction is refused. The generated `types.ts` names `Menu` and `UsualDemand` and narrows `CurrencySymbol` to its three symbols
174
+ - **The scaffold's lint refuses `any` and non-null assertions (`!`)** outside tests, `testing/` and each UI's `src/main.tsx`, and its `.gitignore` ignores Playwright's `test-results/` and `playwright-report/`. Merge `oxlintrc.json.template` and `gitignore.template` from `dist/templates/package/`
175
+ - **The scaffold's dependencies are refreshed**: `@playwright/test` 1.64, `@types/node` 24.19, `@vitejs/plugin-react` 6.1.2, `jsdom` 30.1, `oxlint` 1.87, `react-i18next` 17.0.16, `vite` 8.3.4 and `vitest` 5.0.3. Merge them from `package.json.liquid`, then run `pnpm exec playwright install chromium`
176
+ - **The scaffold's `playwright.config.ts` sets `forbidOnly` on CI**, so a stray `test.only` fails the run rather than skipping every other test. Merge it from `playwright.config.ts.liquid`
177
+ - **The runtime specification settles readings implementers had to choose**: an endpoint's effects take their own `meta.now`; how a cohort tick states the boards it decided on is the runtime's choice; work outside play state may follow an endpoint's effects; a session UI's unchanged capability is compared deeply after the merge; a clamped tick-controller result is reported to operators at every evaluation; and settings the host enforces use the values frozen at play start
178
+ - **The conformance suite reports more of a production host fairly.** Checks that call an endpoint from outside the game require the new `external-callers` driver capability and checks that hold a session at setup require `setup-state`, so a host without either reports them unsupported. REQ-014 and REQ-020 check the outcome rather than a message or the host page's own traffic, each built fixture variant has its own package identifier, a new check holds a participant's `getPastState` to its own surface's rights whatever surface it claims, and the moved-participant check waits for the remount instead of racing it
179
+ - **Three doc pages have new names**: `guides/game-configuration.md` is [`guides/settings.md`](guides/settings.md), `guides/examples.md` is [`guides/patterns.md`](guides/patterns.md), and `recipes/share-logic-between-module-and-ui.md` is [`recipes/share-logic-between-engine-and-ui.md`](recipes/share-logic-between-engine-and-ui.md). The docs no longer point at example packages you cannot see, the publishing and LLM guides say an account needs access Business Simulations grants (as the 403 messages from `package publish` and `generateText` now do, and the scaffold's `workflow-publish-package.md`), and `llms.txt` and `llms-full.txt` on the docs site link by absolute URL
180
+
181
+ ### Removed
182
+
183
+ - **`sdk docs ask` and `sdk docs search`**, with the `@xenova/transformers` dependency behind them (about 230MB less to install) and `HF_HOME` and `HUGGINGFACE_HUB_CACHE`. Read the shipped docs with `sdk docs view <path>`, search `node_modules/@seriousgaming/sdk-tools/dist/docs`, or copy them out with `sdk docs export`. Both commands stay as hidden pointers that say so and fail with `USAGE_REMOVED_COMMAND` (exit 2), so agent guidance that still names them finds its way. Delete the `sharp` and `protobufjs` overrides from your `pnpm-workspace.yaml`
184
+ - **`GameRunner` and `MultiTeamGameRunner`**, deprecated in 0.6.0, with their option and result types. Use `createTestSession`; see [Moving from GameRunner](guides/engine-testing.md#moving-from-gamerunner)
185
+
186
+ ### Fixed
187
+
188
+ - **A write to what the tick controller reads no longer restarts the countdown.** The dev server re-armed the timer every time the controller was re-run after a write, even when it gave the same answer, so a team writing all round pushed its own automatic tick back indefinitely, a facilitator's `extendTick` was undone, and a paused timer lost the time it had left. A re-run that returns the same answer now leaves the countdown alone; only a different answer starts a new window
189
+ - **`sdk build` checks settings defaults against their schema.** A settings default that broke its own schema, such as a string on a number property or an object default missing a key its `required` lists, passed the build and reached logic as the value every session played with. Defaults in capabilities, endpoints and UIs were already checked; settings resources were missed. The build now refuses one, naming the settings resource and the path, and lists settings resources in its report
190
+ - **Team ids are documented as opaque.** The `TeamRunMeta.teamId` and `PastStateQuery.teams` docs said a team id is always `team-${index + 1}`. It is an opaque, stable id: take it from `SessionData` or `setTeamList` and never build one from an index, since a runtime may assign other ids. A runtime never assigns `'own'` or `'all'`, so a `getPastState` query cannot mistake a team for either
191
+ - **A setup UI or scenario editor that spreads an object-valued setting is no longer refused.** A `required` inside an object property was enforced on every override, so `updateSettings({ engine: { ...settings.engine, Execs: { ...settings.engine.Execs, AA: true } } })` was denied for the keys the change left out, and a scenario overriding one key of such an object failed `sdk build`. Overrides are now partial at every depth, as they already were at the top, and a value of the wrong type or an undeclared key at any depth is still refused. An item of an array an override supplies is still held to its `required`, since an array replaces the one beneath whole
192
+ - **The scenario editor no longer changes the session running beside it.** The dev server stored the editor's edits in its session's settings, so editing a scenario reconfigured the session, opening the editor restarted the session on that scenario, and an endpoint the editor called read the setup step's edits as the draft. The editor now edits a draft of its scenario that belongs to no session: its `setSettings` and its endpoints read the draft, **Save** writes it into the scenario, and a session sees the values once it is started on that scenario
193
+ - **A setup UI or scenario editor changes only the settings it lists in `configures`.** The dev server accepted an `updateSettings` naming any settings resource, the built-in `game` settings included, so a settings editor that did not configure `game` could still change the number of rounds. A payload naming a settings resource outside `configures` is now refused as `denied`, naming it, and none of it is applied. List `game` in `configures` to edit the game settings
194
+ - **Saving a scenario keeps every field it does not edit.** The dev server's **Save** rebuilt the scenario's `scenario.sdk.json` from scratch, so saving over one dropped every locale but English and every description, renamed it to its identifier, and dropped `default` and every other field it did not write. It now replaces the settings alone, the number of rounds and team settings under `settings.game` among them. **Add scenario** from a copy now carries the source's locales, descriptions and other fields under the new scenario's name, and is never the default
195
+ - **An engine or hook that changes a capability outside its `writes` fails, aggregate engines included.** The dev server checked only the capabilities a resource declared in `reads`, and only after a per-team `Init` or `Run` or a hook, so `InitAggregate` and `RunAggregate` could change a read-only capability under `shared` or in a `teams` entry, and any of them could write a capability it never declared. A changed capability that is not in `writes` now fails the call, naming it, and nothing of the result is applied. A capability returned unchanged, as the `{ ...context }` spread returns it, still passes. Add a capability to the resource's `writes` if it is meant to write it
196
+ - **A per-team `Init` that changes a shared capability fails, as one from `Run` does.** The dev server checked only `Run`, so a team's `Init` could write a value every team reads. It now fails with _'modified shared capability "market" in Init. Shared capabilities may only be written in InitAggregate'_, and a team added to a shared-state game whose `Init` fails leaves the session as it was
197
+ - **An aggregate engine in an independent game initialises each team on its own.** The dev server handed `InitAggregate` every team at once; it now calls it once per team with a one-element `teams`, as it already called `RunAggregate`
198
+ - **`participantJoin` fires once per participant when play starts.** A game with a setup step ran the hook twice for each participant seated at Play, so a join counter or role assignment ran double. A reset still replays it once
199
+ - **A session reset keeps the settings the setup step chose.** The dev server went back to the settings last saved by `sdk configure`, so a reset session ran on different values from the ones it started with. Going back to the setup step keeps them too, so the form opens on what was chosen
200
+ - **A tick controller result with an unknown key, or a non-boolean `enabled` or `complete`, fails the evaluation.** The dev server ignored the extra key and treated `complete: 'no'` as true, ending the game. It now reports the failure, naming the key, and stops the timer, as for a controller that throws
201
+ - **A surface that replaces its own document after connecting is torn down.** The dev server bounded only a frame's first load, so a UI that navigated itself to another page stayed on screen in the session's chrome. `HostChannel` in `@seriousgaming/sdk-tools/host` now bounds every load of a frame whose element offers a `load` event, and falls back to the platform view when no handshake follows
202
+ - **`-j, --json` and `-w, --working-dir` work before the command**, so `sdk --json view` no longer fails with `USAGE_BAD_FLAG`
203
+ - **A bad number to `--teams`, `--team` or `fault --times` is a usage error (exit 2)**, where `--teams abc` reached the session and failed with `SESSION_ERROR`, and `--team 2abc` was read as team 2
204
+ - **`--scenario` naming a scenario the package does not hold fails with `RESOURCE_NOT_FOUND` (exit 2)**, listing the scenarios it has, on `init`, `tick` and `dev`
205
+ - **`sdk update package` leaves `package.json` alone.** Every update set its `name` to the package identifier and its `description` to the game's name, and reformatted the file, so an npm name such as `@acme/game` was lost. `update package` now writes `seriousgaming.sdk.json` only; `package.json` owns its name, description and version. An update that changes nothing no longer rewrites `seriousgaming.sdk.json` either, so it keeps its formatting
206
+ - **Logic stuck in a loop no longer wedges the command line.** A synchronous loop that never returns blocks the dev server, and its time limit with it. A command that finds the server taking connections but not answering now fails with `DEV_SERVER_UNRESPONSIVE` (exit 5) rather than hanging, and `sdk dev stop` kills a server that does not exit within five seconds and clears its lock. `sdk dev status` reports such a server with `responding: false`, and `sdk dev status` and `sdk dev stop` now honour `-w`
207
+ - **A `title` on a setting, a variable or an endpoint field no longer becomes a type name in the generated `types.ts`.** A title of 'Context' broke the file with a duplicate identifier and one of 'Settings' renamed the settings type `Settings1`. Titles on values are now ignored by type generation and kept everywhere else; a title on an object still names its type. Run `pnpm run generate` to pick it up
208
+ - **A built `seriousgaming.json` now passes the SDK's own manifest schema.** Each user interface's `ui` block recorded a `directory` the schema does not allow, so a platform checking uploads against the schema would refuse every package. The block now carries only `entrypoint`; rebuild to pick it up
209
+ - **`sdk fault --reason` now reaches the outcome for every fault.** Only `llm-failure` used it; `conflict`, `denied`, `tick-failure` and `endpoint-timeout` now carry your `--reason` as the outcome's message, and `hook-throw` adds it to the reported failure. Without `--reason`, the message is unchanged
210
+ - **`sdk fault llm-failure` now fails LLM calls.** It was accepted and reported armed but never fired. While armed, each call logic makes to `generateText`, `extractText` or `generateSpeech` fails as a platform failure would, carrying the `--reason` you gave, and uses up one of its `--times`
211
+ - **A resource copied into a running package is picked up**, and an edit made just as `sdk dev` starts is no longer missed on macOS
212
+ - **An uploaded file's name survives a space**, and uploads from setup and the scenario editor are kept across a reset
213
+ - **The dev server now clears a game's uploads on a reset, as the platform does.** A session reset deletes `game`-retention blobs, including what `writeBlob` and `generateSpeech` stored. `sdk init`, the console's Re-initialise and Reconfigure, and selecting a scenario also delete setup and scenario-editor uploads. The automatic re-initialise after a package edit keeps those, and a per-team reset and the walkthrough clear nothing
214
+ - **`pnpm test:e2e` in a scaffolded package no longer hangs after its last test.** The scaffold starts an isolated server with `startDevServer` in `testing/e2e/global-setup.ts`. To adopt it, copy `global-setup.ts.template`, replace `playwright.config.ts` and drop `{ port }` from your helper calls
215
+ - **A shared package of yours can import the SDK's UI entry.** The scaffold's `vite.config.ts` and `vitest.config.ts` dedupe `@seriousgaming/sdk` and `@seriousgaming/deck`, so a second copy no longer leaves every hook reading empty data
216
+ - **The scaffold's `.claude/settings.json` pre-approves only read and check commands**, never `dev`, watch-mode tests or `package publish`. Replace the file to adopt it
217
+ - **`renderUserInterface`'s `setData` resolves a `MutationCommit`** as the real one does, and its default `uploadBlob` and `readAsset` resolve empty values. CLI messages and the generated `engine/types.ts` header say 'engine' where they said 'module'
218
+ - **`sdk dev` no longer reports itself as already running after a restore** when a lock's pid had been reused
219
+ - **Saving a scenario keeps every settings resource**, not only those named `engine`, `ui` or `session`
220
+ - **A waiting room that declares cross-team reads receives cross-team data**, so `useUserInterfaceCrossTeamData()` is no longer empty there
221
+ - **A session UI's `pauseTimer`, `resumeTimer` and `extendTick` honour their `teamId`**, and a message naming a team the session does not hold is refused rather than acting on another team
222
+ - **Interactive `sdk update engine` works**, and `sdk new settings`, `sdk update settings` and `sdk new endpoint` ask for settings and properties rather than 'configuration fields'
223
+ - **An endpoint's `extendTick` action may pull the deadline in** with a negative `byMs`, as a session UI's can
224
+ - **A `blob:` reference whose id is not a stored blob's is refused as not found wherever it is resolved**
225
+ - **`sdk new` and `sdk delete` tell you to run `pnpm run generate` and `pnpm run build`, not npm**, and the dependency comment in the UI, waiting room, setup and scenario editor templates says `pnpm add`
226
+ - **The dev server's `POST /api/blobs` takes an upload with no `Content-Type`**, and refuses an empty one with a 400 that says so
227
+ - **A team-scoped endpoint called by the CLI or `createTestSession` receives its team**, and a call naming a team id and index that disagree, or no team in a session of several, is refused
228
+ - **An endpoint called by `sdk endpoint call` no longer sees `caller.userInterfaceIdentifier: 'cli'`**; the field is absent. The dev server refuses a past-state read or endpoint call naming a surface the package does not declare
229
+ - **Resetting the walkthrough through the command channel follows the session's current scenario, locale and settings**
230
+ - **pnpm's `--` before a command's flags works in a game.** Given `pnpm run sdk tick` with a `--` ahead of `--no-server`, the CLI read every flag after it as a stray argument and failed with 'too many arguments'. It now drops a `--` that stands before the command's own flags, and the docs no longer show one
231
+ - **`getPastState` from a game UI honours `teams: 'all'` and a team id**, returning another team's rows narrowed to the UI's `crossTeamReads`. It always answered with the caller's own team
232
+ - **A participant moved to another team in the dev server sees their new team's board**, rather than keeping the old team's until the surface showed as disconnected
233
+
234
+ ### Security
235
+
236
+ - **`pnpm audit` is clean.** Six advisories are cleared, one of them critical (`proxy-addr`), with `source-map-js`, `fast-uri` and `sharp`. `image-size`, reached through `@seriousgaming/deck`'s PowerPoint export, is held to `>=2.0.3` by a new override (GHSA-5p2g-fcmc-qvqq, GHSA-w3rx-r6r6-pgpr). A scaffolded package carries the same override in its `pnpm-workspace.yaml`: copy that block into an existing package's file, then run `pnpm update -r` and `pnpm audit`
@@ -0,0 +1,217 @@
1
+ # The CLI and the dev server
2
+
3
+ ## One session, two ways in
4
+
5
+ The dev server owns the session. It holds the teams, the contexts, the roster
6
+ and the tick clock in memory, and it is the only thing that runs your game's
7
+ logic. Everything else (the browser console, the CLI, an agent driving a build)
8
+ is a client of it.
9
+
10
+ That is what makes the two views agree. `sdk tick` advances the session a
11
+ browser is watching, and the browser repaints; a tick taken in the browser is
12
+ what the next `sdk view` reports. There is one session and one answer.
13
+
14
+ ```bash
15
+ pnpm run sdk dev # start it, and leave it running
16
+ pnpm run sdk tick # advance the session it is running
17
+ pnpm run sdk view # read that same session
18
+ ```
19
+
20
+ The dev server is faithful to the platform on how a game behaves, and
21
+ deliberately leaves out what the platform needs to be a product: accounts,
22
+ TLS, session PINs, and state that survives a restart. Check those on the
23
+ platform.
24
+
25
+ ## Surfaces and their URLs
26
+
27
+ Every surface has its own URL, and nothing redirects, so a URL can be typed,
28
+ bookmarked or scripted without asking the server what the link is.
29
+
30
+ | URL | What it shows |
31
+ | ---------------------- | -------------------------------------------------------------------- |
32
+ | `/` | The console: your game's session UI, with the dev drawer |
33
+ | `/team/2/player/1` | The first participant of the second team |
34
+ | `/team/2/observe` | That team's game UI with no participant identity |
35
+ | `/team/2/waiting-room` | That team's waiting room UI |
36
+ | `/team/2/join` | The link a facilitator hands out: it asks which participant you are |
37
+ | `/setup` | The setup screen, for a game with a `setup` slot |
38
+ | `/guide` | The facilitator guide |
39
+ | `/print` | One printable sheet per team |
40
+ | `/presentation` | The facilitator's deck (`?preview` shows it with no session) |
41
+ | `/walkthrough` | The [walkthrough run](./cli-reference.md#walkthrough-runs)'s game UI |
42
+
43
+ `/team/<n>/join` is the one URL that resolves rather than names, because a link
44
+ a facilitator hands out cannot know who will open it. A team holding one
45
+ participant opens straight into their surface. The scenario editor has no URL of
46
+ its own to type: it is always opened on one scenario, from the dev drawer's
47
+ **Scenario** tab.
48
+
49
+ Add `?bare` to any URL to remove the dev chrome entirely. That is what a
50
+ participant sees, what you screenshot, and what a browser test should drive, so
51
+ a change to the console cannot fail a test about your game.
52
+
53
+ While a game with a `setup` slot is at its configuration step, every surface
54
+ except the guide, the print sheets, the presentation and the scenario editor
55
+ shows the setup screen instead, because there is no session to play yet. Once
56
+ play starts, `/setup` says the step has closed, and **Reconfigure** (in the
57
+ drawer, on `/setup`, or `sdk reconfigure`) goes back to it. See
58
+ [Building a setup screen](./guides/setup.md).
59
+
60
+ ## The dev drawer
61
+
62
+ Press the backtick key (`` ` ``) on any surface to open the dev drawer, and
63
+ again, Escape, or its close button to shut it. It is closed by default, so the
64
+ page looks as it will in production. The handle is a floating button whose dot
65
+ shows whether the server is reachable, busy, or has reported an error, and you
66
+ can move it to any corner (remembered per browser).
67
+
68
+ | Tab | What is in it |
69
+ | -------- | ----------------------------------------------------------------------------------------------------------- |
70
+ | Session | Tick, undo, reset, the waiting room, adding a team, Reconfigure, the timer, each team's row, and the locale |
71
+ | Scenario | The scenarios the package declares, which one is active, and adding, editing or deleting one |
72
+ | Surfaces | Every surface you can open, with a link to each |
73
+ | State | The whole state projection, live |
74
+ | Faults | Arm a failure the dev server cannot otherwise produce |
75
+ | Errors | What the session has reported, newest first (shown once there is one) |
76
+
77
+ Everything the drawer does, the CLI does too, because both send the same
78
+ commands.
79
+
80
+ ## How a command finds the session
81
+
82
+ `.seriousgaming-sdk/session.json` is a **lock file**, not state:
83
+
84
+ ```json
85
+ {
86
+ "pid": 48213,
87
+ "port": 3000,
88
+ "startedAt": "2026-09-13T09:14:22.104Z",
89
+ "toolsVersion": "1.0.0",
90
+ "toolsPath": "/work/game/node_modules/@seriousgaming/sdk-tools/dist/cli.js",
91
+ "hostname": "studio-mac",
92
+ "instanceId": "4f1c0a5e-6a43-4c2e-9d0b-1f8f3c2b7e61",
93
+ "background": true
94
+ }
95
+ ```
96
+
97
+ Written once at startup, removed at shutdown, never touched in between, and
98
+ never watched. A command reads it, checks the server is still the one it names,
99
+ connects, sends and prints the answer. A file left behind by a killed server, or
100
+ restored with a snapshot of the project, reads as 'nothing running' rather than
101
+ as an address, and is removed. A server started by other tools than the
102
+ command's is replaced when a command started it, and refused with
103
+ `SESSION_STALE` when you did. See
104
+ [Where the session lives](./cli-reference.md#where-the-session-lives).
105
+
106
+ `SERIOUSGAMING_DEV_URL` sends commands to a server by its address instead, such
107
+ as one started with `sdk dev --isolated` beside your own, and
108
+ `SERIOUSGAMING_DEV_STATE_DIR` keeps the lock outside the project.
109
+
110
+ ```bash
111
+ pnpm run sdk dev status # is one running, and where
112
+ pnpm run sdk dev stop # stop it
113
+ ```
114
+
115
+ **Nothing running?** A session command starts one in the background and carries
116
+ on, and says so, naming the URL. An agent runs `sdk tick` and it simply works,
117
+ and when somebody wants to look, the URL is already in the lock file.
118
+
119
+ The server it starts outlives the command. That is the point, and it is also
120
+ the thing to know before a test fixture calls a session command: whatever runs
121
+ first starts the session, and in Vitest a `describe` body runs at collection
122
+ time, before any `beforeAll`.
123
+
124
+ A server started this way has no flag to take a port from, so it reads
125
+ `SERIOUSGAMING_DEV_PORT` (from the environment or `.env`), and falls back to 3000. Either way it steps up to the first free port from there.
126
+
127
+ ```bash
128
+ SERIOUSGAMING_DEV_PORT=3310 pnpm run sdk tick
129
+ ```
130
+
131
+ **`--no-server`** is the escape hatch: one command, one process, a fresh session
132
+ each time, nothing left behind. That is what you want for a single command in
133
+ CI, and when you need a clean run without disturbing a server you have open.
134
+
135
+ ```bash
136
+ pnpm run sdk tick --no-server
137
+ ```
138
+
139
+ A fresh session each time is the whole of it, so a **sequence** of commands
140
+ cannot be built out of it: `tick --no-server` twice reports `completedTicks: 1`
141
+ both times, because the second one is a different session. A multi-tick
142
+ playthrough, the commonest shape of engine test there is, belongs in
143
+ [`createTestSession`](./guides/engine-testing.md), which runs a whole run in one
144
+ process with no server and no port.
145
+
146
+ ## Driving a session
147
+
148
+ Every session command sends exactly what the console sends, so anything a
149
+ facilitator can do from the browser, an agent can do from a terminal: start and
150
+ tick the session, read and patch it, manage teams, participants and the waiting
151
+ room, switch scenario or locale, and arm faults. Each prints JSON on stdout and
152
+ exits 4 when the session refuses, with the reason the session gave and a code
153
+ naming the refusal (see [Exit codes](./cli-reference.md#exit-codes)). The
154
+ [CLI reference](./cli-reference.md#session-commands) lists them all.
155
+
156
+ ### Reading the whole session
157
+
158
+ `sdk view --full` prints the state projection: one shape carrying the session,
159
+ every team's context and roster, the shared store, the resolved `settings`,
160
+ the timer and any armed faults. The dev server serves the same thing at
161
+ `GET /api/state`, and the console renders from it, so what you read is what the
162
+ browser is showing.
163
+
164
+ ```bash
165
+ pnpm run --silent sdk view --full | jq '.teams[] | {team: .index, completedTicks, context}'
166
+ ```
167
+
168
+ ### Arming a fault
169
+
170
+ The dev server serialises every write against every tick, so it never produces a
171
+ `conflict` outcome, which is the one a production runtime produces most, and
172
+ the 'Try again' path your UI was told to offer. Arming one makes that path reachable
173
+ in development, deliberately and reproducibly:
174
+
175
+ ```bash
176
+ pnpm run sdk fault conflict --reason 'testing the Try again path'
177
+ ```
178
+
179
+ The next UI mutation settles as a conflict, once, and then the fault disarms
180
+ itself. `--times N` fires it more than once; `--disarm` puts it away.
181
+
182
+ The [CLI reference](./cli-reference.md#fault-injection) lists every fault you
183
+ can arm.
184
+
185
+ ## Working alongside an agent
186
+
187
+ The arrangement that makes an agent useful here: you keep the browser open on
188
+ the game, and the agent drives the session from the terminal. It reads state
189
+ with `sdk view --full`, decides, writes with `sdk patch`, advances with
190
+ `sdk tick`, and you watch it happen.
191
+
192
+ ```bash
193
+ # Read where the game is
194
+ pnpm run --silent sdk view --full > session-state.json
195
+
196
+ # Decide, then write and advance
197
+ pnpm run sdk patch '[{"op":"add","path":"/plan/Make/lemonade","value":45}]'
198
+ pnpm run sdk tick
199
+ ```
200
+
201
+ The starter game opens with one team, so neither command names one. In a
202
+ game with several, add `--team N` to both: `patch` requires it, and `tick`
203
+ then advances only that team when the game is independent.
204
+
205
+ Nothing is written to disk by any of that, and nothing needs reconciling: both
206
+ of you are looking at one session.
207
+
208
+ ## What does stay on disk
209
+
210
+ The session overrides, because they are authored content you edit by hand:
211
+ `.seriousgaming-sdk/configuration.json`, written by `sdk configure` and read
212
+ when a session starts. A session already running fixed its settings
213
+ when play started, so `sdk configure` starts it again on the new values, at tick 0,
214
+ keeping its team count, scenario and locale.
215
+
216
+ Session state does not persist, in any form. The session is the process: stop
217
+ the server and it is gone, start it again and you have a new one at tick 0.