@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.
- package/LICENSE +93 -0
- package/README.md +36 -2
- package/dist/bundling/logic-exports.d.ts +34 -0
- package/dist/bundling/node-builtins.d.ts +21 -0
- package/dist/bundling/transpiler.d.ts +7 -0
- package/dist/cli.js +27368 -0
- package/dist/client/dev-server.d.ts +59 -0
- package/dist/client/index.d.ts +28 -0
- package/dist/client/stub-services.d.ts +32 -0
- package/dist/client/test-session.d.ts +195 -0
- package/dist/client.js +16884 -0
- package/dist/core/asset-reference-translator.d.ts +46 -0
- package/dist/core/clock.d.ts +57 -0
- package/dist/core/faults.d.ts +65 -0
- package/dist/core/game-settings.d.ts +65 -0
- package/dist/core/history.d.ts +89 -0
- package/dist/core/listen-with-fallback.d.ts +30 -0
- package/dist/core/mutation-queue.d.ts +61 -0
- package/dist/core/package-scenario.d.ts +89 -0
- package/dist/core/participant-registry.d.ts +109 -0
- package/dist/core/pending-changes.d.ts +14 -0
- package/dist/core/protocol.d.ts +83 -0
- package/dist/core/scenario-drafts.d.ts +32 -0
- package/dist/core/session-endpoints.d.ts +108 -0
- package/dist/core/session-gates.d.ts +75 -0
- package/dist/core/session-host.d.ts +94 -0
- package/dist/core/session-refusals.d.ts +56 -0
- package/dist/core/session-rewind.d.ts +32 -0
- package/dist/core/session-roster.d.ts +128 -0
- package/dist/core/session-surfaces.d.ts +32 -0
- package/dist/core/session-ticks.d.ts +18 -0
- package/dist/core/session-timers.d.ts +84 -0
- package/dist/core/session-writes.d.ts +40 -0
- package/dist/core/session.d.ts +385 -0
- package/dist/core/settings-locks.d.ts +22 -0
- package/dist/core/team-names.d.ts +52 -0
- package/dist/core/team-state-store.d.ts +209 -0
- package/dist/core/tick-timer.d.ts +75 -0
- package/dist/dev/blob-links.d.ts +10 -0
- package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
- package/dist/dev/console/index.html +12 -0
- package/dist/dev/console-path.d.ts +19 -0
- package/dist/dev/lockfile.d.ts +124 -0
- package/dist/dev/package-watcher.d.ts +21 -0
- package/dist/dev/routes.d.ts +67 -0
- package/dist/dev/shell.d.ts +260 -0
- package/dist/dev/surfaces.d.ts +41 -0
- package/dist/dev/tools-identity.d.ts +23 -0
- package/dist/dev/view/batch.d.ts +172 -0
- package/dist/dev/view/commands.d.ts +75 -0
- package/dist/dev/view/route.d.ts +58 -0
- package/dist/dev/view/view.d.ts +78 -0
- package/dist/dev/view/viewer.d.ts +32 -0
- package/dist/discovery/config.d.ts +9 -0
- package/dist/discovery/manifest-paths.d.ts +21 -0
- package/dist/discovery/removed-fields.d.ts +90 -0
- package/dist/docs/api/deck.md +386 -0
- package/dist/docs/api/index.md +67 -0
- package/dist/docs/api/sdk-commands.md +286 -0
- package/dist/docs/api/sdk-e2e.md +433 -0
- package/dist/docs/api/sdk-projection.md +207 -0
- package/dist/docs/api/sdk-testing.md +214 -0
- package/dist/docs/api/sdk-tools-client.md +560 -0
- package/dist/docs/api/sdk-ui-testing.md +231 -0
- package/dist/docs/api/sdk-ui.md +1269 -0
- package/dist/docs/api/sdk.md +2140 -0
- package/dist/docs/changelog-0.x.md +1156 -0
- package/dist/docs/changelog.md +236 -0
- package/dist/docs/cli-and-dev-server.md +217 -0
- package/dist/docs/cli-error-codes.md +154 -0
- package/dist/docs/cli-reference.md +2488 -0
- package/dist/docs/cli-simulation-testing.md +222 -0
- package/dist/docs/concepts/index.md +46 -0
- package/dist/docs/concepts/terminology.md +322 -0
- package/dist/docs/environment-variables.md +102 -0
- package/dist/docs/getting-started.md +145 -0
- package/dist/docs/guides/ai-facilitation.md +220 -0
- package/dist/docs/guides/capabilities.md +460 -0
- package/dist/docs/guides/endpoints.md +335 -0
- package/dist/docs/guides/engine-testing.md +477 -0
- package/dist/docs/guides/engine.md +527 -0
- package/dist/docs/guides/file-handling.md +405 -0
- package/dist/docs/guides/games.md +176 -0
- package/dist/docs/guides/global-settings.md +94 -0
- package/dist/docs/guides/guide.md +123 -0
- package/dist/docs/guides/hooks.md +183 -0
- package/dist/docs/guides/index.md +51 -0
- package/dist/docs/guides/internationalization.md +288 -0
- package/dist/docs/guides/llm-integration.md +635 -0
- package/dist/docs/guides/multi-team-games.md +586 -0
- package/dist/docs/guides/patterns.md +391 -0
- package/dist/docs/guides/presentation.md +196 -0
- package/dist/docs/guides/production-constraints.md +130 -0
- package/dist/docs/guides/scenario-editor.md +143 -0
- package/dist/docs/guides/scenarios.md +515 -0
- package/dist/docs/guides/session.md +972 -0
- package/dist/docs/guides/settings.md +458 -0
- package/dist/docs/guides/setup.md +193 -0
- package/dist/docs/guides/speech-generation.md +233 -0
- package/dist/docs/guides/starter-game.md +164 -0
- package/dist/docs/guides/testing.md +575 -0
- package/dist/docs/guides/tick-controller.md +569 -0
- package/dist/docs/guides/type-safety.md +516 -0
- package/dist/docs/guides/ui.md +1389 -0
- package/dist/docs/guides/upgrading.md +636 -0
- package/dist/docs/guides/validators.md +217 -0
- package/dist/docs/guides/waiting-room.md +244 -0
- package/dist/docs/index.md +41 -0
- package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
- package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
- package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
- package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
- package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
- package/dist/docs/recipes/create-capability.md +66 -0
- package/dist/docs/recipes/create-endpoint.md +127 -0
- package/dist/docs/recipes/create-engine.md +111 -0
- package/dist/docs/recipes/create-game.md +70 -0
- package/dist/docs/recipes/create-guide.md +75 -0
- package/dist/docs/recipes/create-hook.md +80 -0
- package/dist/docs/recipes/create-presentation.md +142 -0
- package/dist/docs/recipes/create-scenario-editor.md +102 -0
- package/dist/docs/recipes/create-scenario.md +98 -0
- package/dist/docs/recipes/create-session-ui.md +116 -0
- package/dist/docs/recipes/create-settings.md +88 -0
- package/dist/docs/recipes/create-setup.md +115 -0
- package/dist/docs/recipes/create-tick-controller.md +65 -0
- package/dist/docs/recipes/create-user-interface.md +96 -0
- package/dist/docs/recipes/create-validator.md +67 -0
- package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
- package/dist/docs/recipes/declare-api-uses.md +67 -0
- package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
- package/dist/docs/recipes/distribute-package.md +80 -0
- package/dist/docs/recipes/e2e-testing.md +116 -0
- package/dist/docs/recipes/end-game-on-condition.md +154 -0
- package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
- package/dist/docs/recipes/index.md +86 -0
- package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
- package/dist/docs/recipes/negotiate-across-teams.md +164 -0
- package/dist/docs/recipes/observe-other-teams-state.md +119 -0
- package/dist/docs/recipes/publish-a-package.md +139 -0
- package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
- package/dist/docs/recipes/review-a-past-day.md +73 -0
- package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
- package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
- package/dist/docs/recipes/show-all-teams-participants.md +109 -0
- package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
- package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
- package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
- package/dist/docs/recipes/test-user-interface.md +95 -0
- package/dist/docs/recipes/test-with-engine.md +115 -0
- package/dist/docs/recipes/upload-and-display-files.md +148 -0
- package/dist/docs/recipes/use-request-play.md +65 -0
- package/dist/docs/recipes/use-request-tick.md +75 -0
- package/dist/docs/troubleshooting.md +816 -0
- package/dist/execution/blob-inputs.d.ts +57 -0
- package/dist/execution/bundle-reload.d.ts +42 -0
- package/dist/execution/config-resolver.d.ts +127 -0
- package/dist/execution/configuration-builder.d.ts +66 -0
- package/dist/execution/context-composition.d.ts +161 -0
- package/dist/execution/endpoint-actions.d.ts +9 -0
- package/dist/execution/endpoint-payloads.d.ts +32 -0
- package/dist/execution/endpoint-runner.d.ts +129 -0
- package/dist/execution/engine-state.d.ts +40 -0
- package/dist/execution/executor-callbacks.d.ts +56 -0
- package/dist/execution/executor.d.ts +436 -0
- package/dist/execution/gates.d.ts +45 -0
- package/dist/execution/hook-chain.d.ts +25 -0
- package/dist/execution/logic-calls.d.ts +190 -0
- package/dist/execution/logic-failure.d.ts +84 -0
- package/dist/execution/logic-loader.d.ts +54 -0
- package/dist/execution/logic-result.d.ts +76 -0
- package/dist/execution/tick-controller-call.d.ts +32 -0
- package/dist/execution/types.d.ts +48 -0
- package/dist/generation/ts-type.d.ts +39 -0
- package/dist/guide/locale.d.ts +12 -0
- package/dist/guide/video-registry.d.ts +42 -0
- package/dist/help-examples.d.ts +125 -0
- package/dist/host/adapter.d.ts +67 -0
- package/dist/host/host-channel.d.ts +222 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/protocol.d.ts +783 -0
- package/dist/host/surfaces.d.ts +42 -0
- package/dist/host/theme.d.ts +55 -0
- package/dist/host.js +583 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/schema-builder.d.ts +29 -0
- package/dist/prompts/types.d.ts +108 -0
- package/dist/prompts/validators.d.ts +32 -0
- package/dist/runtime/capability-scope.d.ts +6 -0
- package/dist/runtime/context-merge.d.ts +29 -0
- package/dist/runtime/context-projection.d.ts +41 -0
- package/dist/runtime/guarded-callbacks.d.ts +15 -0
- package/dist/runtime/index.d.ts +27 -0
- package/dist/runtime/manifest-version.d.ts +55 -0
- package/dist/runtime/tick-validation.d.ts +25 -0
- package/dist/runtime.js +346 -0
- package/dist/schema.json +1546 -0
- package/dist/server/vite-plugin.d.ts +46 -0
- package/dist/services/tts/client.d.ts +51 -0
- package/dist/services/tts/elevenlabs.d.ts +24 -0
- package/dist/services/tts/types.d.ts +44 -0
- package/dist/storage/asset-resolver.d.ts +49 -0
- package/dist/storage/blob-store.d.ts +41 -0
- package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
- package/dist/templates/endpoint/logic.ts.liquid +25 -0
- package/dist/templates/engine/init.json.liquid +7 -0
- package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
- package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
- package/dist/templates/engine/logic.test.ts.liquid +61 -0
- package/dist/templates/engine/logic.ts.liquid +32 -0
- package/dist/templates/engine/run.json.liquid +7 -0
- package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
- package/dist/templates/guide/02-preparation.md.liquid +18 -0
- package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
- package/dist/templates/hook/logic.test.ts.liquid +46 -0
- package/dist/templates/hook/logic.ts.liquid +12 -0
- package/dist/templates/package/CLAUDE.md.template +338 -0
- package/dist/templates/package/DESIGN.md.liquid +144 -0
- package/dist/templates/package/README.md.liquid +143 -0
- package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
- package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
- package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
- package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
- package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
- package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
- package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
- package/dist/templates/package/claude/settings.json.template +16 -0
- package/dist/templates/package/gitignore.template +15 -0
- package/dist/templates/package/global-setup.ts.template +21 -0
- package/dist/templates/package/globals.d.ts.template +2 -0
- package/dist/templates/package/oxfmtrc.json.template +14 -0
- package/dist/templates/package/oxlintrc.json.template +238 -0
- package/dist/templates/package/package.json.liquid +65 -0
- package/dist/templates/package/playwright.config.ts.liquid +19 -0
- package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
- package/dist/templates/package/test-setup.ts.liquid +7 -0
- package/dist/templates/package/tsconfig.json.liquid +15 -0
- package/dist/templates/package/vite.config.ts.liquid +19 -0
- package/dist/templates/package/vitest.config.ts.liquid +25 -0
- package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
- package/dist/templates/presentation/index.html.liquid +20 -0
- package/dist/templates/presentation/src/App.tsx.liquid +49 -0
- package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
- package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
- package/dist/templates/presentation/src/main.tsx.liquid +14 -0
- package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
- package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
- package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
- package/dist/templates/session/src/App.test.tsx.liquid +60 -0
- package/dist/templates/session/src/App.tsx.liquid +128 -0
- package/dist/templates/session/src/i18n.ts.liquid +53 -0
- package/dist/templates/session/src/main.tsx.liquid +48 -0
- package/dist/templates/session/src/theme.ts.liquid +46 -0
- package/dist/templates/settings-editor/index.html.liquid +12 -0
- package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
- package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
- package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
- package/dist/templates/setup/src/App.tsx.liquid +102 -0
- package/dist/templates/setup/src/i18n.ts.liquid +47 -0
- package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
- package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
- package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
- package/dist/templates/starter-game/engine/defaults.ts +81 -0
- package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
- package/dist/templates/starter-game/engine/logic.test.ts +221 -0
- package/dist/templates/starter-game/engine/logic.ts +149 -0
- package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
- package/dist/templates/starter-game/engine/types.ts +139 -0
- package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
- package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
- package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
- package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
- package/dist/templates/starter-game/guide/guide.test.ts +108 -0
- package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
- package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
- package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
- package/dist/templates/starter-game/package.json +68 -0
- package/dist/templates/starter-game/playwright.config.ts +19 -0
- package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
- package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
- package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
- package/dist/templates/starter-game/session/defaults.ts +80 -0
- package/dist/templates/starter-game/session/index.html +12 -0
- package/dist/templates/starter-game/session/session.sdk.json +11 -0
- package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
- package/dist/templates/starter-game/session/src/App.tsx +309 -0
- package/dist/templates/starter-game/session/src/Section.tsx +37 -0
- package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
- package/dist/templates/starter-game/session/src/format.ts +8 -0
- package/dist/templates/starter-game/session/src/i18n.ts +51 -0
- package/dist/templates/starter-game/session/src/main.tsx +48 -0
- package/dist/templates/starter-game/session/src/theme.ts +45 -0
- package/dist/templates/starter-game/session/types.ts +135 -0
- package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
- package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
- package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
- package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
- package/dist/templates/starter-game/test-setup.ts +7 -0
- package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
- package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
- package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
- package/dist/templates/starter-game/tsconfig.json +19 -0
- package/dist/templates/starter-game/ui/defaults.ts +60 -0
- package/dist/templates/starter-game/ui/index.html +12 -0
- package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
- package/dist/templates/starter-game/ui/src/App.tsx +175 -0
- package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
- package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
- package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
- package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
- package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
- package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
- package/dist/templates/starter-game/ui/src/format.ts +12 -0
- package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
- package/dist/templates/starter-game/ui/src/main.tsx +48 -0
- package/dist/templates/starter-game/ui/src/menu.ts +18 -0
- package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
- package/dist/templates/starter-game/ui/src/theme.ts +45 -0
- package/dist/templates/starter-game/ui/src/writes.ts +43 -0
- package/dist/templates/starter-game/ui/types.ts +137 -0
- package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
- package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
- package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
- package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
- package/dist/templates/starter-game/vite.config.ts +19 -0
- package/dist/templates/starter-game/vitest.config.ts +15 -0
- package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
- package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
- package/dist/templates/starter-game/waiting_room/index.html +12 -0
- package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
- package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
- package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
- package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
- package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
- package/dist/templates/starter-game/waiting_room/types.ts +59 -0
- package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
- package/dist/templates/tick-controller/logic.ts.liquid +8 -0
- package/dist/templates/ui/index.html.liquid +12 -0
- package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
- package/dist/templates/ui/src/App.tsx.liquid +103 -0
- package/dist/templates/ui/src/i18n.ts.liquid +47 -0
- package/dist/templates/ui/src/main.tsx.liquid +48 -0
- package/dist/templates/ui/src/theme.ts.liquid +46 -0
- package/dist/templates/validator/logic.test.ts.liquid +42 -0
- package/dist/templates/validator/logic.ts.liquid +21 -0
- package/dist/templates/waiting-room/index.html.liquid +12 -0
- package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
- package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
- package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
- package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
- package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
- package/dist/templating/resource-metadata.d.ts +131 -0
- package/dist/types.d.ts +14 -0
- package/dist/utils/atomic-write.d.ts +40 -0
- package/dist/utils/cli-error.d.ts +64 -0
- package/dist/utils/document-parser-client.d.ts +20 -0
- package/dist/utils/env-loader.d.ts +40 -0
- package/dist/utils/error-codes.d.ts +335 -0
- package/dist/utils/errors.d.ts +19 -0
- package/dist/utils/global-settings.d.ts +65 -0
- package/dist/utils/llm-client.d.ts +34 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/output.d.ts +109 -0
- package/dist/utils/platform.d.ts +14 -0
- package/dist/utils/prompt.d.ts +36 -0
- package/dist/utils/real-path.d.ts +6 -0
- package/dist/utils/schema-properties.d.ts +35 -0
- package/dist/validation/ajv.d.ts +17 -0
- package/dist/validation/facilitator-guide-validator.d.ts +27 -0
- package/dist/validation/findings.d.ts +40 -0
- package/dist/validation/game-constraints.d.ts +10 -0
- package/dist/validation/game-validator.d.ts +34 -0
- package/dist/validation/package-validation.d.ts +41 -0
- package/dist/validation/package-validator.d.ts +19 -0
- package/dist/validation/resource-validator.d.ts +79 -0
- package/dist/validation/schema-defaults-validator.d.ts +29 -0
- package/dist/validation/schema-validation.d.ts +115 -0
- package/dist/validation/semver.d.ts +9 -0
- package/package.json +122 -3
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# Capabilities
|
|
2
|
+
|
|
3
|
+
Capabilities hold your game's state. This guide explains the state model, who sees and writes each capability, and how long its values last, then covers creating capabilities and every kind of variable they hold.
|
|
4
|
+
|
|
5
|
+
## The state model
|
|
6
|
+
|
|
7
|
+
A capability is a named collection of related variables, declared by a schema. For example:
|
|
8
|
+
|
|
9
|
+
- `inventory` holds `Stock` and `Reorder`
|
|
10
|
+
- `sales` holds `Revenue` and `UnitsSold`
|
|
11
|
+
- `pricing` holds `Price` and `PricingStrategy`
|
|
12
|
+
|
|
13
|
+
Each capability declares its variables in a `variablesSchema`: a full JSON Schema 7 object whose properties are the variables, each with a type, a `default`, and any constraints. Every capability starts from those defaults, so its variables exist from the first tick for anything that declares it.
|
|
14
|
+
|
|
15
|
+
### Context is scoped by capability
|
|
16
|
+
|
|
17
|
+
A team's state, the context, is one object keyed by capability identifier, each value holding that capability's variables:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
{
|
|
21
|
+
inventory: { Stock: 100, Reorder: false },
|
|
22
|
+
sales: { Revenue: 5000, UnitsSold: 50 },
|
|
23
|
+
production: { Output: 75 },
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Logic reads a variable through its capability: `context.inventory.Stock`. Scoping variables under their capability prevents name clashes between capabilities and keeps related variables together, and it is what lets every resource declare, by identifier, exactly which slices of state it touches.
|
|
28
|
+
|
|
29
|
+
### Immutable updates
|
|
30
|
+
|
|
31
|
+
Never assign to `context`. Build a new object with spreads and return what you write under `capabilities`:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// ❌ Don't
|
|
35
|
+
context.inventory.Stock = 50;
|
|
36
|
+
|
|
37
|
+
// ✅ Do
|
|
38
|
+
return {
|
|
39
|
+
capabilities: {
|
|
40
|
+
...context,
|
|
41
|
+
inventory: {
|
|
42
|
+
...context.inventory,
|
|
43
|
+
Stock: 50,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The runtime folds the result into the context one level deep, and removes keys only through `deletes`. See [How a result folds into the context](./engine.md#how-a-result-folds-into-the-context).
|
|
50
|
+
|
|
51
|
+
### How state moves through a tick
|
|
52
|
+
|
|
53
|
+
1. **Initialisation**: every capability starts from its defaults, then the engine's `Init`, if it has one, sets up each team's starting state
|
|
54
|
+
2. **Each tick**: the engine's `Run` plays the round, calling the simulation's steps in the order it writes them, each step's output becoming the next step's input
|
|
55
|
+
3. **Between ticks**: surfaces, endpoints and hooks change the capabilities they declare in `writes`, and the next tick reads the result
|
|
56
|
+
|
|
57
|
+
See [Ordering the simulation](./engine.md#ordering-the-simulation).
|
|
58
|
+
|
|
59
|
+
## Declaring reads and writes
|
|
60
|
+
|
|
61
|
+
Every resource that touches state declares the capabilities it reads and writes, by identifier:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"reads": ["inventory", "discounts"],
|
|
66
|
+
"writes": ["revenue"]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- **`reads`**: the capabilities the resource consumes. Every declared capability is present from the first tick, holding its schema defaults until something writes it, so a read is never conditional. The build fails if an identifier names no capability in the package
|
|
71
|
+
- **`writes`**: the capabilities the resource produces values for, or removes keys from
|
|
72
|
+
|
|
73
|
+
### A declaration is what gets delivered
|
|
74
|
+
|
|
75
|
+
A resource receives exactly the capabilities it declares, in `reads` or in `writes`, and nothing else.
|
|
76
|
+
|
|
77
|
+
On a user interface (game, session or waiting room) two things follow:
|
|
78
|
+
|
|
79
|
+
- A capability you forgot to declare is **absent** from `data`, not just undocumented. Your generated `Context` already says so, so TypeScript catches it before you run
|
|
80
|
+
- A capability you declare but never render is payload you pay for on every update, to every participant
|
|
81
|
+
|
|
82
|
+
Writes count because a screen that sets a value almost always shows it: a decisions panel writes `price` and displays the current price in the same field.
|
|
83
|
+
|
|
84
|
+
`writes` is also what a game or waiting room UI is allowed to change. The runtime refuses a `setData` naming a capability the UI only `reads`, or anything that is not a capability, as `denied`. A session UI's team writes are the facilitator's and are not held to it.
|
|
85
|
+
|
|
86
|
+
The engine, hooks, validators, tick controllers, and endpoints follow the same rule: their logic is handed exactly the capabilities in `reads` and `writes`. What a resource returns under `capabilities` is folded over the wider context as a patch, so a resource cannot drop a capability it was never shown. A result that changes a capability not in its `writes`, whether it only reads it or never declared it, fails the call, naming it; this holds an aggregate engine's `shared` slice and each `teams` entry too. A capability returned unchanged, as `{ ...context }` returns it, is not a change. A result removes a key by naming its path in `deletes`, and only under a capability in its `writes`: a delete under one it only reads fails the call.
|
|
87
|
+
|
|
88
|
+
## Scope
|
|
89
|
+
|
|
90
|
+
A capability's `scope` controls who may write it and who may see it:
|
|
91
|
+
|
|
92
|
+
| Scope | Meaning |
|
|
93
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `team` (default) | One value per team, delivered to that team's surfaces |
|
|
95
|
+
| `team-readable` | Each team writes only its own copy, but user interfaces on other teams can observe every team's value live (shared-state games only) |
|
|
96
|
+
| `shared` | One value for all teams, written by an aggregate engine, by the facilitator (a session UI that declares it in `writes`), or by a session-scoped endpoint |
|
|
97
|
+
| `private` | Per team like `team`, but never delivered to any user interface. For engine bookkeeping |
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"definition": {
|
|
102
|
+
"scope": "team-readable",
|
|
103
|
+
"variablesSchema": { "...": "..." }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
See [Multi-team games](./multi-team-games.md) for the full rules and the `useUserInterfaceCrossTeamData` hook.
|
|
109
|
+
|
|
110
|
+
### Private capabilities
|
|
111
|
+
|
|
112
|
+
Simulations accumulate state that exists only to get from one tick to the next: a serialised engine, a smoothing accumulator, a seed, a ledger the next tick consumes. Nothing on screen needs it, and under team scope all of it is sent to every screen on every update.
|
|
113
|
+
|
|
114
|
+
`scope: 'private'` says so out loud. A private capability reaches your engine, hooks, validators, tick controller, and endpoints exactly as a team-scoped one does, and reaches no interface at all. Declaring one on an interface fails the build, naming the interface and the capability.
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"definition": {
|
|
119
|
+
"scope": "private",
|
|
120
|
+
"variablesSchema": {
|
|
121
|
+
"type": "object",
|
|
122
|
+
"properties": {
|
|
123
|
+
"PreviousProfit": { "type": "number", "default": 0 }
|
|
124
|
+
},
|
|
125
|
+
"required": ["PreviousProfit"],
|
|
126
|
+
"additionalProperties": false
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
A `trend` capability like this one carries last tick's profit, so the engine's report can say whether things are improving, and no UI can read it.
|
|
133
|
+
|
|
134
|
+
Reach for it whenever a screen would have no reason to render the value. When nothing but the engine reads it either, the engine's own `state` is simpler still: no capability, no schema, typed by TypeScript (see [Keeping memory between ticks](./engine.md#keeping-memory-between-ticks)). It costs nothing to change your mind later: widening `private` to `team` is a one-word edit, where narrowing `team` to `private` means finding out which screens had come to depend on it.
|
|
135
|
+
|
|
136
|
+
## Lifetime
|
|
137
|
+
|
|
138
|
+
Most state belongs to the round it was written in: undo a tick and it goes back, reset the session and it starts again from its defaults. Some state does not. That setup is complete, the round a facilitator has opened, the announcement on everyone's screen: undoing a mistaken tick should not wipe any of those.
|
|
139
|
+
|
|
140
|
+
Set `lifetime` to `session` for that kind of value:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"definition": {
|
|
145
|
+
"scope": "shared",
|
|
146
|
+
"lifetime": "session",
|
|
147
|
+
"variablesSchema": {
|
|
148
|
+
"type": "object",
|
|
149
|
+
"properties": {
|
|
150
|
+
"Message": { "type": "string", "default": "" }
|
|
151
|
+
},
|
|
152
|
+
"required": ["Message"],
|
|
153
|
+
"additionalProperties": false
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
A session-lifetime value:
|
|
160
|
+
|
|
161
|
+
- **Is left alone by undo and reset.** Per team for a team-scoped capability, in the one store for a shared one
|
|
162
|
+
- **Is not part of any round.** `getPastState` never reports it, because a past round did not hold it
|
|
163
|
+
- **Starts from its defaults once,** when the session is created or a team is added
|
|
164
|
+
- **Is not the engine's.** The build refuses one in the engine's `reads` or `writes`. A tick has to be replayable from its snapshot, and a value that undo leaves alone would change under it. Surfaces, endpoints, hooks, validators and the tick controller use it as its `scope` allows
|
|
165
|
+
|
|
166
|
+
`lifetime` is independent of `scope`: `scope` says who sees a value, `lifetime` how long it lasts. The default, `tick`, keeps a value in each tick snapshot. The starter game makes its facilitator `briefing` session-lifetime, so an undo never takes a note back off the teams' screens.
|
|
167
|
+
|
|
168
|
+
## Creating a capability
|
|
169
|
+
|
|
170
|
+
Create a capability with the CLI. The identifier becomes the directory name:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pnpm run sdk new capability '{
|
|
174
|
+
"definition": {
|
|
175
|
+
"identifier": "inventory",
|
|
176
|
+
"variablesSchema": {
|
|
177
|
+
"type": "object",
|
|
178
|
+
"properties": {
|
|
179
|
+
"Stock": { "type": "number", "default": 100 }
|
|
180
|
+
},
|
|
181
|
+
"required": ["Stock"],
|
|
182
|
+
"additionalProperties": false
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
This writes `capabilities/inventory/capability.sdk.json`, a capability with one numeric variable that starts at 100. A capability directory may also hold TypeScript files its schema names through [`tsType`](#a-typescript-type-of-your-own), such as `capabilities/chathistory/message.ts`.
|
|
189
|
+
|
|
190
|
+
A capability can hold several variables of different types:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
pnpm run sdk new capability '{
|
|
194
|
+
"definition": {
|
|
195
|
+
"identifier": "warehouse",
|
|
196
|
+
"variablesSchema": {
|
|
197
|
+
"type": "object",
|
|
198
|
+
"properties": {
|
|
199
|
+
"CurrentStock": {
|
|
200
|
+
"type": "number",
|
|
201
|
+
"default": 500,
|
|
202
|
+
"minimum": 0,
|
|
203
|
+
"description": "Current stock level"
|
|
204
|
+
},
|
|
205
|
+
"LastRestockDate": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"default": "2024-01-01",
|
|
208
|
+
"description": "Date of last restock"
|
|
209
|
+
},
|
|
210
|
+
"IsOperating": {
|
|
211
|
+
"type": "boolean",
|
|
212
|
+
"default": true,
|
|
213
|
+
"description": "Whether warehouse is open"
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"required": ["CurrentStock", "LastRestockDate", "IsOperating"],
|
|
217
|
+
"additionalProperties": false
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}'
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Then list it in the `reads` or `writes` of each resource that uses it.
|
|
224
|
+
|
|
225
|
+
### Defaults
|
|
226
|
+
|
|
227
|
+
A variable's `default` is its tick-0 value, so it must itself satisfy the subschema that declares it. `sdk build` fails on a default that does not, so an object default cannot carry an undeclared field under `additionalProperties: false`, and a value your logic only produces later (a status that starts as `'Unknown'`) still has to be admitted by the `enum`. See [Schema defaults](../cli-reference.md#schema-defaults).
|
|
228
|
+
|
|
229
|
+
A default seeds the value and nothing else: the generated type comes from the schema's `type`, `properties`, `items`, `enum` and `const`. See [Where a precise type comes from](./type-safety.md#where-a-precise-type-comes-from).
|
|
230
|
+
|
|
231
|
+
## Variable types
|
|
232
|
+
|
|
233
|
+
### Number
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"StockLevel": {
|
|
238
|
+
"type": "number",
|
|
239
|
+
"default": 0,
|
|
240
|
+
"minimum": 0,
|
|
241
|
+
"maximum": 1000,
|
|
242
|
+
"description": "Current stock"
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### String
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"Status": {
|
|
252
|
+
"type": "string",
|
|
253
|
+
"default": "pending",
|
|
254
|
+
"description": "Current status"
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Boolean
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"IsActive": {
|
|
264
|
+
"type": "boolean",
|
|
265
|
+
"default": true,
|
|
266
|
+
"description": "Is capability active"
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Object
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{
|
|
275
|
+
"Preferences": {
|
|
276
|
+
"type": "object",
|
|
277
|
+
"properties": {
|
|
278
|
+
"Theme": { "type": "string" }
|
|
279
|
+
},
|
|
280
|
+
"required": ["Theme"],
|
|
281
|
+
"additionalProperties": false,
|
|
282
|
+
"default": {
|
|
283
|
+
"Theme": "light"
|
|
284
|
+
},
|
|
285
|
+
"description": "Preferences object"
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Declare the object's `properties`: an object with only a `default` generates `{ [k: string]: unknown }`.
|
|
291
|
+
|
|
292
|
+
### Array
|
|
293
|
+
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"Items": {
|
|
297
|
+
"type": "array",
|
|
298
|
+
"default": [],
|
|
299
|
+
"description": "List of items"
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
An array replaces whole when a write merges, so it is the wrong shape for anything several participants add to at once: two people each sending a list is two lists, and the last one wins. Use a keyed collection instead.
|
|
305
|
+
|
|
306
|
+
### Keyed collection
|
|
307
|
+
|
|
308
|
+
A collection several participants write at the same time is an object keyed by a stable id, not an array. Concurrent writes then touch different keys and the merge unions them, rather than replacing each other's list.
|
|
309
|
+
|
|
310
|
+
Describe the item under `additionalProperties`, and give it a `title`:
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"Notes": {
|
|
315
|
+
"type": "object",
|
|
316
|
+
"default": {},
|
|
317
|
+
"description": "One sticky note per participant, keyed by participant id",
|
|
318
|
+
"additionalProperties": {
|
|
319
|
+
"title": "PinboardNote",
|
|
320
|
+
"type": "object",
|
|
321
|
+
"properties": {
|
|
322
|
+
"Author": { "type": "string" },
|
|
323
|
+
"Text": { "type": "string" }
|
|
324
|
+
},
|
|
325
|
+
"required": ["Author", "Text"],
|
|
326
|
+
"additionalProperties": false
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The `title` is what gives the item a name in the generated types, so surfaces and engine logic can refer to it:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import type { Context, PinboardNote } from '../types';
|
|
336
|
+
|
|
337
|
+
const render = (note: PinboardNote) => note.Text;
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Without one you still get the right shape, `{ [k: string]: { Author: string; Text: string } }`, but nothing to name it, and every function taking an item has to index back into the map that holds it.
|
|
341
|
+
|
|
342
|
+
Where the order of the collection matters, carry it on the entry (a rank, or when it was added) and sort on read. A separate ordered array is an array again, with the same problem.
|
|
343
|
+
|
|
344
|
+
See [Design state for concurrent writes](../recipes/design-state-for-concurrent-writes.md) for the full set of rules this belongs to.
|
|
345
|
+
|
|
346
|
+
### A TypeScript type of your own
|
|
347
|
+
|
|
348
|
+
The generated types are only as precise as the schema. A schema that says `{ "type": "object" }` for a deep structure, such as a model's output, generates `{ [k: string]: unknown }`, and every read of it needs a cast. Where you already have the type in TypeScript, name it with `tsType` and the generated `types.ts` files use it instead:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"Messages": {
|
|
353
|
+
"type": "array",
|
|
354
|
+
"default": [],
|
|
355
|
+
"items": {
|
|
356
|
+
"type": "object",
|
|
357
|
+
"tsType": "import('./message').ChatMessage",
|
|
358
|
+
"properties": {
|
|
359
|
+
"role": { "type": "string", "enum": ["user", "assistant"] },
|
|
360
|
+
"content": { "type": "string" }
|
|
361
|
+
},
|
|
362
|
+
"required": ["role", "content"],
|
|
363
|
+
"additionalProperties": false
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
- **The path is relative to the capability's own directory,** as an import in a file beside `capability.sdk.json` would be. Each generated `types.ts` sits somewhere different, so the generator rewrites the path for each one. A bare module name (`import('some-package').Thing`) is left alone, and so is a `tsType` with no import (`"'low' | 'high'"`)
|
|
370
|
+
- **The build checks the reference.** A path that resolves to no module, or a name the module does not export, fails the build, naming the capability
|
|
371
|
+
- **The schema still does the rest.** `default` still seeds the value, and the runtime still validates against `properties`. `tsType` changes only what TypeScript sees, so keep the two describing the same thing
|
|
372
|
+
|
|
373
|
+
The type lives in a file beside the bundle, here `capabilities/chathistory/message.ts`:
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
export interface ChatMessage {
|
|
377
|
+
role: 'user' | 'assistant';
|
|
378
|
+
content: string;
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
For engine bookkeeping nothing else reads, the engine's own [state](./engine.md#keeping-memory-between-ticks) is simpler still: no capability and no schema at all.
|
|
383
|
+
|
|
384
|
+
## Updating capabilities
|
|
385
|
+
|
|
386
|
+
Update an existing capability:
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
pnpm run sdk update capability inventory '{
|
|
390
|
+
"definition": {
|
|
391
|
+
"variablesSchema": {
|
|
392
|
+
"type": "object",
|
|
393
|
+
"properties": {
|
|
394
|
+
"Stock": { "type": "number", "default": 100 },
|
|
395
|
+
"MinimumStock": { "type": "number", "default": 20 }
|
|
396
|
+
},
|
|
397
|
+
"required": ["Stock", "MinimumStock"],
|
|
398
|
+
"additionalProperties": false
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}'
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Deleting capabilities
|
|
405
|
+
|
|
406
|
+
Remove a capability:
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
pnpm run sdk delete capability inventory
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Deleting a capability also removes every read and write of it, from whichever resources declared them.
|
|
413
|
+
|
|
414
|
+
## Best practices
|
|
415
|
+
|
|
416
|
+
1. **Use meaningful identifiers**: a short directory name that says what the variables are
|
|
417
|
+
- ✅ `inventory`
|
|
418
|
+
- ❌ `data1`
|
|
419
|
+
|
|
420
|
+
2. **Keep variables related**: group related variables in one capability
|
|
421
|
+
- ✅ One capability for inventory variables
|
|
422
|
+
- ❌ Mix inventory and sales in one capability
|
|
423
|
+
|
|
424
|
+
3. **Provide descriptions**: document what each variable means
|
|
425
|
+
- ✅ `"description": "Current stock level in units"`
|
|
426
|
+
- ❌ `"description": "stock"`
|
|
427
|
+
|
|
428
|
+
4. **Set sensible defaults**: initialise variables to realistic values
|
|
429
|
+
- ✅ `"default": 100`
|
|
430
|
+
- ❌ `"default": 0` for a stock level that shouldn't be zero
|
|
431
|
+
|
|
432
|
+
5. **Use constraints**: apply minimum/maximum to numeric values
|
|
433
|
+
- ✅ `"minimum": 0` for quantities that can't be negative
|
|
434
|
+
- ❌ No constraints on bounded values
|
|
435
|
+
|
|
436
|
+
6. **Use proper types**: never use a string to hold JSON or complex data
|
|
437
|
+
- ✅ Use an `object` type with `properties`
|
|
438
|
+
- ❌ Use a string type with JSON: `{ "type": "string", "default": "{\"key\": \"value\"}" }`
|
|
439
|
+
- See the [type safety guide](./type-safety.md)
|
|
440
|
+
|
|
441
|
+
7. **Define array elements**: use an `items` schema with property definitions
|
|
442
|
+
- ✅ `{ "type": "array", "items": { "type": "object", "properties": {...} }, "default": [...] }`
|
|
443
|
+
- ❌ `{ "type": "array", "default": [] }` (no type info)
|
|
444
|
+
- See the [type safety guide](./type-safety.md)
|
|
445
|
+
|
|
446
|
+
8. **Mark engine bookkeeping private**: state that no screen reads it
|
|
447
|
+
- ✅ `"scope": "private"` for a serialised engine, an accumulator, a seed, a ledger the next tick consumes
|
|
448
|
+
- ❌ Leaving it team-scoped, where it is delivered to every screen on every update and a screen can quietly start depending on it
|
|
449
|
+
- See [Private capabilities](#private-capabilities)
|
|
450
|
+
|
|
451
|
+
9. **Declare only what a screen renders**: a user interface receives exactly its declared capabilities
|
|
452
|
+
- ✅ A `reads` list that matches what the screen actually displays
|
|
453
|
+
- ❌ A long `reads` list kept 'just in case', which is payload sent to every participant on every update
|
|
454
|
+
- Logic resources follow the same rule: the engine, hooks, validators, tick controllers, and endpoints receive only what they declare
|
|
455
|
+
|
|
456
|
+
## Next steps
|
|
457
|
+
|
|
458
|
+
- [Engines](./engine.md): read and write these capabilities each tick
|
|
459
|
+
- [Multi-team games](./multi-team-games.md): shared and team-readable capabilities in practice
|
|
460
|
+
- [Design state for concurrent writes](../recipes/design-state-for-concurrent-writes.md): shaping state several participants write at once
|