@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,149 @@
|
|
|
1
|
+
import { deriveSeed, InitializationFunction, RunFunction, seededRandom } from '@seriousgaming/sdk';
|
|
2
|
+
|
|
3
|
+
import type { Context, Settings } from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A day at the lemonade stand.
|
|
7
|
+
*
|
|
8
|
+
* The team decides how many of each thing to make before the day starts. Then
|
|
9
|
+
* people turn up. Anything they made and did not sell is poured away, and they
|
|
10
|
+
* paid for it either way; anyone who wanted something they had run out of is
|
|
11
|
+
* turned away, and that is a sale they never see again.
|
|
12
|
+
*
|
|
13
|
+
* The whole simulation is that one sentence. Everything below is arithmetic.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** The weather for a given day: the same day always gets the same weather. */
|
|
17
|
+
function weatherFor(seed: string, teamIndex: number, day: number): Context['weather']['Today'] {
|
|
18
|
+
// Derived rather than drawn. An engine function can be invoked more than
|
|
19
|
+
// once for one logical tick and only the last result is kept, so a live
|
|
20
|
+
// `Math.random()` would give two runs of the same day different weather and
|
|
21
|
+
// an author no way to tell which one the team saw.
|
|
22
|
+
const roll = seededRandom(deriveSeed(seed, teamIndex, 'weather', day))();
|
|
23
|
+
if (roll < 0.4) return 'sunny';
|
|
24
|
+
if (roll < 0.75) return 'cloudy';
|
|
25
|
+
return 'rainy';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const pullFor = (weather: Context['weather']['Today'], settings: Settings) =>
|
|
29
|
+
weather === 'sunny'
|
|
30
|
+
? settings.engine.SunnyPull
|
|
31
|
+
: weather === 'rainy'
|
|
32
|
+
? settings.engine.RainyPull
|
|
33
|
+
: 1;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* How many people want an item on a cloudy day. The menu and the demand are
|
|
37
|
+
* separate settings, so a scenario can add an item without saying how popular
|
|
38
|
+
* it is; one it says nothing about has no custom, which shows on the forecast
|
|
39
|
+
* straight away rather than being quietly made up.
|
|
40
|
+
*/
|
|
41
|
+
const usualDemand = (settings: Settings, id: string) => settings.engine.UsualDemand[id] ?? 0;
|
|
42
|
+
|
|
43
|
+
/** The forecast a team is shown before a day is played. */
|
|
44
|
+
function forecastFor(settings: Settings, weather: Context['weather']['Today']) {
|
|
45
|
+
const pull = pullFor(weather, settings);
|
|
46
|
+
const expected: Record<string, number> = {};
|
|
47
|
+
for (const id of Object.keys(settings.menu.Menu)) {
|
|
48
|
+
expected[id] = Math.round(usualDemand(settings, id) * pull);
|
|
49
|
+
}
|
|
50
|
+
return expected;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const Init: InitializationFunction<Context, Settings> = async ({
|
|
54
|
+
context,
|
|
55
|
+
settings,
|
|
56
|
+
meta,
|
|
57
|
+
}) => {
|
|
58
|
+
const make: Record<string, number> = {};
|
|
59
|
+
for (const id of Object.keys(settings.menu.Menu)) {
|
|
60
|
+
// Somewhere to start from, obviously provisional. Starting at nothing
|
|
61
|
+
// reads as a broken screen; starting at the forecast decides day one
|
|
62
|
+
// for them.
|
|
63
|
+
make[id] = Math.round(usualDemand(settings, id) / 2);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const weather = weatherFor(meta.seed, meta.index, 1);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
capabilities: {
|
|
70
|
+
...context,
|
|
71
|
+
plan: { Make: make },
|
|
72
|
+
weather: { Today: weather, Expected: forecastFor(settings, weather) },
|
|
73
|
+
results: {
|
|
74
|
+
Day: 0,
|
|
75
|
+
Weather: weather,
|
|
76
|
+
Lines: {},
|
|
77
|
+
Takings: 0,
|
|
78
|
+
Spent: 0,
|
|
79
|
+
Profit: 0,
|
|
80
|
+
Purse: 0,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, settings, meta }) => {
|
|
87
|
+
// `meta.tick` is the tick being executed, so it is today's number
|
|
88
|
+
// directly. A surface reads `completedTicks` instead, which is the number of
|
|
89
|
+
// days already finished, and adds one to name the day in play.
|
|
90
|
+
const day = meta.tick;
|
|
91
|
+
const weather = weatherFor(meta.seed, meta.index, day);
|
|
92
|
+
const pull = pullFor(weather, settings);
|
|
93
|
+
|
|
94
|
+
// One generator for the day's custom, drawn per item in menu order, so the
|
|
95
|
+
// same day always plays out the same way.
|
|
96
|
+
const random = seededRandom(deriveSeed(meta.seed, meta.index, 'custom', day));
|
|
97
|
+
|
|
98
|
+
const lines: Context['results']['Lines'] = {};
|
|
99
|
+
let takings = 0;
|
|
100
|
+
let spent = 0;
|
|
101
|
+
|
|
102
|
+
for (const [id, entry] of sortedMenu(settings)) {
|
|
103
|
+
const made = Math.max(0, Math.round(context.plan.Make[id] ?? 0));
|
|
104
|
+
const swing = 1 + (random() * 2 - 1) * settings.engine.Variation;
|
|
105
|
+
const wanted = Math.max(0, Math.round(usualDemand(settings, id) * pull * swing));
|
|
106
|
+
|
|
107
|
+
const sold = Math.min(made, wanted);
|
|
108
|
+
lines[id] = {
|
|
109
|
+
Made: made,
|
|
110
|
+
Wanted: wanted,
|
|
111
|
+
Sold: sold,
|
|
112
|
+
PouredAway: made - sold,
|
|
113
|
+
TurnedAway: wanted - sold,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
takings += sold * entry.Price;
|
|
117
|
+
// You paid to make everything, including what you poured away. That is
|
|
118
|
+
// the whole of the lesson about making too much.
|
|
119
|
+
spent += made * entry.Cost;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const profit = takings - spent;
|
|
123
|
+
const purse = context.results.Purse + profit;
|
|
124
|
+
|
|
125
|
+
// Tomorrow's forecast, so the team has something to decide on before the
|
|
126
|
+
// next day runs.
|
|
127
|
+
const tomorrow = weatherFor(meta.seed, meta.index, day + 1);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
capabilities: {
|
|
131
|
+
...context,
|
|
132
|
+
weather: { Today: tomorrow, Expected: forecastFor(settings, tomorrow) },
|
|
133
|
+
results: {
|
|
134
|
+
Day: day,
|
|
135
|
+
Weather: weather,
|
|
136
|
+
Lines: lines,
|
|
137
|
+
Takings: takings,
|
|
138
|
+
Spent: spent,
|
|
139
|
+
Profit: profit,
|
|
140
|
+
Purse: purse,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/** Menu order, so the day is played and reported in the order it is displayed. */
|
|
147
|
+
function sortedMenu(settings: Settings) {
|
|
148
|
+
return Object.entries(settings.menu.Menu).sort(([, a], [, b]) => a.Order - b.Order);
|
|
149
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { TeamRunMeta } from '@seriousgaming/sdk';
|
|
2
|
+
|
|
3
|
+
import type { Context, Settings } from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Starting fixtures for the engine's tests.
|
|
7
|
+
*
|
|
8
|
+
* A test spreads what it cares about over these rather than restating the whole
|
|
9
|
+
* context, so adding a field to a capability does not break every test that was
|
|
10
|
+
* never about that field.
|
|
11
|
+
*/
|
|
12
|
+
export const baseSettings: Settings = {
|
|
13
|
+
menu: {
|
|
14
|
+
Capacity: 120,
|
|
15
|
+
Menu: {
|
|
16
|
+
lemonade: { Name: 'Lemonade', Order: 1, Cost: 15, Price: 50 },
|
|
17
|
+
icedtea: { Name: 'Iced tea', Order: 2, Cost: 20, Price: 60 },
|
|
18
|
+
cookies: { Name: 'Cookies', Order: 3, Cost: 25, Price: 70 },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
engine: {
|
|
22
|
+
UsualDemand: { lemonade: 55, icedtea: 30, cookies: 20 },
|
|
23
|
+
SunnyPull: 1.3,
|
|
24
|
+
RainyPull: 0.65,
|
|
25
|
+
Variation: 0.2,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const baseContext: Context = {
|
|
30
|
+
plan: { Make: { lemonade: 28, icedtea: 15, cookies: 10 } },
|
|
31
|
+
weather: { Today: 'cloudy', Expected: { lemonade: 55, icedtea: 30, cookies: 20 } },
|
|
32
|
+
results: {
|
|
33
|
+
Day: 0,
|
|
34
|
+
Weather: 'cloudy',
|
|
35
|
+
Lines: {},
|
|
36
|
+
Takings: 0,
|
|
37
|
+
Spent: 0,
|
|
38
|
+
Profit: 0,
|
|
39
|
+
Purse: 0,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The `meta` the runtime supplies. `seed` is fixed, so the weather and the
|
|
45
|
+
* day's custom are the same every run: this game derives both from it rather
|
|
46
|
+
* than drawing them, which is what makes a test like this possible at all.
|
|
47
|
+
*/
|
|
48
|
+
export const baseMeta: TeamRunMeta = {
|
|
49
|
+
tick: 1,
|
|
50
|
+
locale: 'en',
|
|
51
|
+
teamId: 'team-1',
|
|
52
|
+
index: 0,
|
|
53
|
+
seed: 'lemonade-test-seed',
|
|
54
|
+
runKind: 'session',
|
|
55
|
+
now: 0,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The engine declares no `uses`, so every gated callback throws rather than
|
|
60
|
+
* returning a benign value: a call that reaches one is a declaration bug, and a
|
|
61
|
+
* test that quietly passes hides it.
|
|
62
|
+
*
|
|
63
|
+
* The ungated ones are a different case. They need no declaration, so reaching
|
|
64
|
+
* one is legitimate, and they answer as a session with nothing in it would.
|
|
65
|
+
*/
|
|
66
|
+
export const callbacks = {
|
|
67
|
+
generateText: async () => {
|
|
68
|
+
throw new Error('generateText is not declared by this engine');
|
|
69
|
+
},
|
|
70
|
+
readAsset: async () => {
|
|
71
|
+
throw new Error('readAsset reached, but this engine references no assets');
|
|
72
|
+
},
|
|
73
|
+
getPastState: async () => [],
|
|
74
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// This file is auto-generated from the engine's capability definitions.
|
|
2
|
+
// Do not edit this file directly. It will be regenerated on build.
|
|
3
|
+
|
|
4
|
+
export interface Context {
|
|
5
|
+
plan: {
|
|
6
|
+
/**
|
|
7
|
+
* How many of each menu item to make today, keyed by item id. Keyed rather than a flat set of fields so that two people changing two different items write different keys, and the host's merge keeps both.
|
|
8
|
+
*/
|
|
9
|
+
Make: {
|
|
10
|
+
[k: string]: number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
weather: {
|
|
14
|
+
/**
|
|
15
|
+
* The forecast for the day about to be played
|
|
16
|
+
*/
|
|
17
|
+
Today: 'sunny' | 'cloudy' | 'rainy';
|
|
18
|
+
/**
|
|
19
|
+
* Roughly how many of each item people will want today, keyed by item id. A forecast, not a promise: the day lands somewhere near it.
|
|
20
|
+
*/
|
|
21
|
+
Expected: {
|
|
22
|
+
[k: string]: number;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
results: {
|
|
26
|
+
/**
|
|
27
|
+
* The day these results are for
|
|
28
|
+
*/
|
|
29
|
+
Day: number;
|
|
30
|
+
/**
|
|
31
|
+
* What the weather actually did
|
|
32
|
+
*/
|
|
33
|
+
Weather: string;
|
|
34
|
+
/**
|
|
35
|
+
* What happened to each menu item, keyed by item id
|
|
36
|
+
*/
|
|
37
|
+
Lines: {
|
|
38
|
+
[k: string]: DayLine;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Money in, in pence
|
|
42
|
+
*/
|
|
43
|
+
Takings: number;
|
|
44
|
+
/**
|
|
45
|
+
* Cost of everything made, in pence
|
|
46
|
+
*/
|
|
47
|
+
Spent: number;
|
|
48
|
+
/**
|
|
49
|
+
* Takings less spend, in pence
|
|
50
|
+
*/
|
|
51
|
+
Profit: number;
|
|
52
|
+
/**
|
|
53
|
+
* Everything made so far, in pence
|
|
54
|
+
*/
|
|
55
|
+
Purse: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export interface DayLine {
|
|
59
|
+
Made: number;
|
|
60
|
+
/**
|
|
61
|
+
* How many people asked for one
|
|
62
|
+
*/
|
|
63
|
+
Wanted: number;
|
|
64
|
+
Sold: number;
|
|
65
|
+
/**
|
|
66
|
+
* Made and not sold
|
|
67
|
+
*/
|
|
68
|
+
PouredAway: number;
|
|
69
|
+
/**
|
|
70
|
+
* Asked for and not available
|
|
71
|
+
*/
|
|
72
|
+
TurnedAway: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type TeamContext = Pick<Context, 'plan' | 'weather' | 'results'>;
|
|
76
|
+
|
|
77
|
+
export type SharedContext = Record<string, never>;
|
|
78
|
+
|
|
79
|
+
export interface Settings {
|
|
80
|
+
menu: {
|
|
81
|
+
/**
|
|
82
|
+
* How many items the stall can make in a day, across the whole menu
|
|
83
|
+
*/
|
|
84
|
+
Capacity: number;
|
|
85
|
+
Menu: Menu;
|
|
86
|
+
};
|
|
87
|
+
engine: {
|
|
88
|
+
UsualDemand: UsualDemand;
|
|
89
|
+
/**
|
|
90
|
+
* How much busier a sunny day is than a cloudy one, as a multiplier on usual demand. 1.3 brings 30% more people
|
|
91
|
+
*/
|
|
92
|
+
SunnyPull: number;
|
|
93
|
+
/**
|
|
94
|
+
* How much quieter a rainy day is than a cloudy one, as a multiplier on usual demand. 0.65 brings 35% fewer people, and above 1 makes rain busier
|
|
95
|
+
*/
|
|
96
|
+
RainyPull: number;
|
|
97
|
+
/**
|
|
98
|
+
* How far a day can land either side of its forecast, as a fraction. 0.2 lands within 20% of it, and 0 makes every forecast exact
|
|
99
|
+
*/
|
|
100
|
+
Variation: number;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* What the stall sells, keyed by item id. Money is in pence, so the sums stay in whole numbers. Read by the engine and every surface, so the price on screen and the price in the sums are the same number.
|
|
105
|
+
*/
|
|
106
|
+
export interface Menu {
|
|
107
|
+
[k: string]: MenuItem;
|
|
108
|
+
}
|
|
109
|
+
export interface MenuItem {
|
|
110
|
+
/**
|
|
111
|
+
* What the item is called on every screen
|
|
112
|
+
*/
|
|
113
|
+
Name: string;
|
|
114
|
+
/**
|
|
115
|
+
* Where it sits on the menu, so every screen lists it in the same order. 1 comes first
|
|
116
|
+
*/
|
|
117
|
+
Order: number;
|
|
118
|
+
/**
|
|
119
|
+
* Cost to make one, in pence
|
|
120
|
+
*/
|
|
121
|
+
Cost: number;
|
|
122
|
+
/**
|
|
123
|
+
* Sale price, in pence
|
|
124
|
+
*/
|
|
125
|
+
Price: number;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* How many people want each menu item on an ordinary cloudy day, keyed by item id. Kept apart from the menu because only the engine reads it: a surface that could see it would be showing the answer. An item on the menu with no entry here has no custom at all, so its forecast reads 0 and nobody buys one.
|
|
129
|
+
*/
|
|
130
|
+
export interface UsualDemand {
|
|
131
|
+
/**
|
|
132
|
+
* People a day who want this item when it is cloudy
|
|
133
|
+
*/
|
|
134
|
+
[k: string]: number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
import type { LogicCallbacks } from '@seriousgaming/sdk';
|
|
138
|
+
|
|
139
|
+
export type Callbacks = LogicCallbacks;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Lemonade Stand at a glance
|
|
2
|
+
|
|
3
|
+
Teams run a lemonade stand for ten days. Each morning they decide how many of each thing to make. Then the day happens.
|
|
4
|
+
|
|
5
|
+
:::timing
|
|
6
|
+
Total session length: about 50 minutes.
|
|
7
|
+
:::
|
|
8
|
+
|
|
9
|
+
| Phase | Duration | Facilitator focus |
|
|
10
|
+
| -------- | ---------- | ------------------------------------------------------- |
|
|
11
|
+
| Briefing | 5 minutes | Hand out the stalls and read the one rule |
|
|
12
|
+
| Play | 30 minutes | Walk the room while stalls run their own days |
|
|
13
|
+
| Debrief | 15 minutes | Connect the two kinds of mistake to the learning points |
|
|
14
|
+
|
|
15
|
+
## The one rule
|
|
16
|
+
|
|
17
|
+
Whatever they make in the morning, they have paid for. Anything left at the end of the day is poured away. Anyone who wanted something they had run out of goes home thirsty, and that is a sale they never get back.
|
|
18
|
+
|
|
19
|
+
That is the whole simulation. There is nothing else to explain.
|
|
20
|
+
|
|
21
|
+
## The menu
|
|
22
|
+
|
|
23
|
+
| Item | Costs to make | Sells for | On an ordinary day |
|
|
24
|
+
| -------- | ------------- | --------- | ------------------------ |
|
|
25
|
+
| Lemonade | 15p | 50p | About 55 people want one |
|
|
26
|
+
| Iced tea | 20p | 60p | About 30 |
|
|
27
|
+
| Cookies | 25p | 70p | About 20 |
|
|
28
|
+
|
|
29
|
+
The stall can make 120 things in a day, all told. On a sunny day people want more than that, so a team has to choose what to fill the stall with. Cookies earn the most per item and the fewest people want one.
|
|
30
|
+
|
|
31
|
+
## What makes it interesting
|
|
32
|
+
|
|
33
|
+
The forecast tells them roughly what to expect, and the day lands somewhere near it rather than on it. So there is no number that is simply right, and a team that makes exactly the forecast every day still has days it gets wrong.
|
|
34
|
+
|
|
35
|
+
:::note
|
|
36
|
+
The two mistakes do not feel the same. Pouring lemonade away is visible and annoying; turning people away costs more and leaves nothing behind to look at. Most teams over-correct towards caution after one bad day of waste, and stop noticing the queue they are losing.
|
|
37
|
+
:::
|
|
38
|
+
|
|
39
|
+
## Two versions
|
|
40
|
+
|
|
41
|
+
| Scenario | When to use it |
|
|
42
|
+
| ---------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| An ordinary week | Ten days of mixed weather. The one to run first |
|
|
44
|
+
| Heatwave | Five hot days. Demand is always past what the stall can make, so every day forces a choice about what to fill it with |
|
|
45
|
+
|
|
46
|
+
Pick one when you create the session.
|
|
47
|
+
|
|
48
|
+
## Materials
|
|
49
|
+
|
|
50
|
+
- One laptop or tablet per stall, plus one for you
|
|
51
|
+
- This guide, printed or on a second screen
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Preparation
|
|
2
|
+
|
|
3
|
+
## Before the room arrives
|
|
4
|
+
|
|
5
|
+
- Create the session and pick a scenario. An ordinary week is ten days of mixed weather; Heatwave is five hot ones, and every day of it forces a choice. The waiting room opens on its own, so the first person through the door lands on a holding screen rather than in day one
|
|
6
|
+
- Add one stall per group of two to five people. Stalls are independent, so you can add one at any point if a late group arrives
|
|
7
|
+
- Open the session surface on your own screen and leave it there. It is where you can see every stall at once, and where the Observe and Join buttons are
|
|
8
|
+
|
|
9
|
+
:::tip
|
|
10
|
+
Groups name their own stall from the waiting room. Ask them to do it while you finish the briefing, and your screen stops being a list of 'Team 1' through 'Team 6'.
|
|
11
|
+
:::
|
|
12
|
+
|
|
13
|
+
## Briefing the room
|
|
14
|
+
|
|
15
|
+
:::say
|
|
16
|
+
You are running a lemonade stand. Every morning you decide how much lemonade, iced tea and cookies to make. You pay for everything you make. Anything you do not sell gets poured away at the end of the day. Anyone who wants something you have run out of just leaves. When your stall is ready, you open it yourselves and the day happens. Ten days. Most money in the jar wins.
|
|
17
|
+
:::
|
|
18
|
+
|
|
19
|
+
:::do
|
|
20
|
+
Do not explain the weather, the capacity, or the margins. All three are on the screen, and a stall that discovers the cookies earn the most has learned something a stall that was told has not.
|
|
21
|
+
:::
|
|
22
|
+
|
|
23
|
+
## Who does what
|
|
24
|
+
|
|
25
|
+
As people join, the game puts each of them in charge of one thing on the menu. A stall of three gets one person each on lemonade, iced tea and cookies; a stall of five gets two people on the lemonade.
|
|
26
|
+
|
|
27
|
+
Nobody is stopped from changing somebody else's item. The point of handing them out is that in ordinary play three people work on three different things at once and nothing gets in anyone's way.
|
|
28
|
+
|
|
29
|
+
:::note
|
|
30
|
+
There is one thing they share: the stall can only make 120 things a day. If two people each add to their own item at the same moment and the total goes over, the second change is refused and says so. It is worth watching for: a team that hits it once usually starts talking to each other properly.
|
|
31
|
+
:::
|
|
32
|
+
|
|
33
|
+
## Opening up
|
|
34
|
+
|
|
35
|
+
Close the waiting room when every stall is named and settled. Every player's screen swaps to the game without a reload, and nothing about the session changes, so you can reopen it mid-session for a discussion and pick up exactly where the stalls were.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Running the day
|
|
2
|
+
|
|
3
|
+
## The shape of a day
|
|
4
|
+
|
|
5
|
+
1. The stall reads the forecast and sets its numbers
|
|
6
|
+
2. The stall presses Open the stall, and the day plays out
|
|
7
|
+
3. The stall reads what happened and changes its mind about tomorrow
|
|
8
|
+
|
|
9
|
+
**The stalls run their own days.** Each one moves at its own pace, and nobody waits for the room. You are not driving the session, which means you are free to walk around it.
|
|
10
|
+
|
|
11
|
+
:::do
|
|
12
|
+
The Today column on your screen says which stalls have changed their numbers since the day they last traded. There is no 'ready' button anywhere, so this is what tells you who is thinking and who has stopped. A stall showing 'Same as yesterday' has either decided to repeat itself or forgotten to look.
|
|
13
|
+
:::
|
|
14
|
+
|
|
15
|
+
:::note
|
|
16
|
+
A fast group will be on day six while a slow one is on day two. That is fine, and it is worth saying so at the briefing, or the fast group will think they have broken something. Pull the room together for the debrief rather than trying to keep it in step.
|
|
17
|
+
:::
|
|
18
|
+
|
|
19
|
+
## Getting into a stall
|
|
20
|
+
|
|
21
|
+
Every row has two ways in.
|
|
22
|
+
|
|
23
|
+
- **Observe** opens that stall's screen exactly as they see it, without joining. Nobody appears on their roster and nothing changes. This is the one to use when you are checking on a quiet group
|
|
24
|
+
- **Join** puts you on the stall as a player, which is what you want when you are showing somebody how something works
|
|
25
|
+
- **Copy link** gives you the join link to hand out, for a latecomer or a slide
|
|
26
|
+
|
|
27
|
+
**Undo** is the one thing that only you can do. If a stall opens up before it meant to, take the day back and let them try again.
|
|
28
|
+
|
|
29
|
+
## What to listen for
|
|
30
|
+
|
|
31
|
+
| Day | What usually happens | Worth asking |
|
|
32
|
+
| ------- | -------------------------------------------------------------- | --------------------------------------------------- |
|
|
33
|
+
| 1 | Stalls make roughly the forecast, or roughly nothing | 'How much of that did you actually sell?' |
|
|
34
|
+
| 2 to 3 | A sunny day catches them out and they hit the capacity limit | 'You could not make everything. What did you drop?' |
|
|
35
|
+
| 4 to 6 | They over-correct after a day of pouring lemonade away | 'How many people went home without one?' |
|
|
36
|
+
| 7 to 10 | The gap between the stalls that kept adjusting becomes obvious | 'When did you last change your numbers, and why?' |
|
|
37
|
+
|
|
38
|
+
## Two things participants ask
|
|
39
|
+
|
|
40
|
+
**'Why did my change get refused?'** Somebody else on the stall was changing their own item at the same moment, and together the two changes asked for more than the stall can make. The screen says so. They take some off something and try again.
|
|
41
|
+
|
|
42
|
+
**'Can I change somebody else's item?'** Yes. The game hands out the menu so that three people can work at once without treading on each other, not to stop anyone.
|
|
43
|
+
|
|
44
|
+
**'Somebody opened the stall before I was ready.'** Anyone on the stall can open it, and their change counts for the day that just ran. If a number was still being moved when it happened, the screen says the change counts for the next day instead. Ask them to sort out who says go.
|
|
45
|
+
|
|
46
|
+
## Debrief
|
|
47
|
+
|
|
48
|
+
The mechanical answer is that you cannot know the day in advance, so you are choosing which mistake to make.
|
|
49
|
+
|
|
50
|
+
The conversation worth having is underneath that.
|
|
51
|
+
|
|
52
|
+
- Which mistake did your stall prefer, and did you ever say so out loud?
|
|
53
|
+
- Pouring lemonade away is right there on the screen. The people who went home are a number you have to go looking for. Which one drove your decisions?
|
|
54
|
+
- What did the capacity limit make you give up, and was it the right thing?
|
|
55
|
+
|
|
56
|
+
:::note
|
|
57
|
+
If nobody raises it, show a stall the Turned away column for a good day and a bad one side by side. Most teams have never added it up.
|
|
58
|
+
:::
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Structural tests for the facilitator guide.
|
|
5
|
+
*
|
|
6
|
+
* A guide is content, not code, so these guard the contract the host relies on
|
|
7
|
+
* rather than anything it says: it sits where the package will find it, every
|
|
8
|
+
* declared locale carries the same chapters, every admonition is one the
|
|
9
|
+
* renderer knows, and every image reference resolves. `pnpm run sdk build --
|
|
10
|
+
* --validate-only` runs the same checks at build time; this keeps them in the
|
|
11
|
+
* package's own test loop.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
15
|
+
import { dirname, join, resolve } from 'path';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
|
|
18
|
+
import { describe, expect, it } from 'vitest';
|
|
19
|
+
|
|
20
|
+
const GUIDE_DIR = fileURLToPath(new URL('.', import.meta.url));
|
|
21
|
+
const PACKAGE_DIR = resolve(GUIDE_DIR, '..');
|
|
22
|
+
|
|
23
|
+
/** The six the renderer defines. Anything else renders as an inert error block. */
|
|
24
|
+
const ADMONITIONS = ['note', 'warning', 'tip', 'say', 'do', 'timing'];
|
|
25
|
+
|
|
26
|
+
const bundle = JSON.parse(readFileSync(join(GUIDE_DIR, 'guide.sdk.json'), 'utf-8'));
|
|
27
|
+
const declaredLocales: string[] = Object.keys(bundle.definition.locales);
|
|
28
|
+
|
|
29
|
+
const chapters = (locale: string) =>
|
|
30
|
+
readdirSync(join(GUIDE_DIR, 'content', locale))
|
|
31
|
+
.filter((name) => name.endsWith('.md'))
|
|
32
|
+
.sort();
|
|
33
|
+
|
|
34
|
+
const chapterFiles = () =>
|
|
35
|
+
declaredLocales.flatMap((locale) =>
|
|
36
|
+
chapters(locale).map((file) => ({
|
|
37
|
+
locale,
|
|
38
|
+
file,
|
|
39
|
+
content: readFileSync(join(GUIDE_DIR, 'content', locale, file), 'utf-8'),
|
|
40
|
+
}))
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
describe('the bundle', () => {
|
|
44
|
+
it('sits in the slot the package scans', () => {
|
|
45
|
+
expect(existsSync(join(PACKAGE_DIR, 'guide', 'guide.sdk.json'))).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('carries no identifier of its own, because the slot names it', () => {
|
|
49
|
+
expect(bundle.definition.identifier).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('names itself in every declared locale', () => {
|
|
53
|
+
expect(declaredLocales.length).toBeGreaterThan(0);
|
|
54
|
+
for (const locale of declaredLocales) {
|
|
55
|
+
expect(bundle.definition.locales[locale].name).toBeTruthy();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('the content', () => {
|
|
61
|
+
it('mirrors the same chapter file set across every declared locale', () => {
|
|
62
|
+
const [reference, ...rest] = declaredLocales;
|
|
63
|
+
const referenceChapters = chapters(reference as string);
|
|
64
|
+
expect(referenceChapters.length).toBeGreaterThan(0);
|
|
65
|
+
for (const locale of rest) {
|
|
66
|
+
expect(chapters(locale)).toEqual(referenceChapters);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('opens every chapter with a heading, which is what the contents list', () => {
|
|
71
|
+
for (const { locale, file, content } of chapterFiles()) {
|
|
72
|
+
expect(content.startsWith('# '), `${locale}/${file}`).toBe(true);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('uses only admonitions the renderer defines', () => {
|
|
77
|
+
for (const { locale, file, content } of chapterFiles()) {
|
|
78
|
+
for (const match of content.matchAll(/^:::(\w+)/gm)) {
|
|
79
|
+
expect(ADMONITIONS, `${locale}/${file}`).toContain(match[1]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('closes every admonition it opens', () => {
|
|
85
|
+
for (const { locale, file, content } of chapterFiles()) {
|
|
86
|
+
const markers = content.match(/^:::/gm) ?? [];
|
|
87
|
+
expect(markers.length % 2, `${locale}/${file}`).toBe(0);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('references only images that exist inside the guide directory', () => {
|
|
92
|
+
for (const { locale, file, content } of chapterFiles()) {
|
|
93
|
+
for (const match of content.matchAll(/!\[[^\]]*\]\(([^()\s]+)\)/g)) {
|
|
94
|
+
const target = match[1] as string;
|
|
95
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(target) || target.startsWith('/')) continue;
|
|
96
|
+
const resolved = resolve(dirname(join(GUIDE_DIR, 'content', locale, file)), target);
|
|
97
|
+
expect(
|
|
98
|
+
resolved.startsWith(resolve(GUIDE_DIR)),
|
|
99
|
+
`${locale}/${file}: ${target} escapes the guide directory`
|
|
100
|
+
).toBe(true);
|
|
101
|
+
expect(
|
|
102
|
+
existsSync(resolved),
|
|
103
|
+
`${locale}/${file}: ${target} does not resolve to a file`
|
|
104
|
+
).toBe(true);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// This file is auto-generated from the schemas the resource declares: the values
|
|
2
|
+
// a session starts with, for use as test fixtures.
|
|
3
|
+
// Do not edit this file directly. It will be regenerated on build.
|
|
4
|
+
|
|
5
|
+
import type { Context, Settings } from './types';
|
|
6
|
+
|
|
7
|
+
export const defaultContext: Context = {
|
|
8
|
+
roles: {
|
|
9
|
+
LooksAfter: {},
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const defaultSettings: Settings = {
|
|
14
|
+
menu: {
|
|
15
|
+
Capacity: 120,
|
|
16
|
+
Menu: {
|
|
17
|
+
lemonade: {
|
|
18
|
+
Name: 'Lemonade',
|
|
19
|
+
Order: 1,
|
|
20
|
+
Cost: 15,
|
|
21
|
+
Price: 50,
|
|
22
|
+
},
|
|
23
|
+
icedtea: {
|
|
24
|
+
Name: 'Iced tea',
|
|
25
|
+
Order: 2,
|
|
26
|
+
Cost: 20,
|
|
27
|
+
Price: 60,
|
|
28
|
+
},
|
|
29
|
+
cookies: {
|
|
30
|
+
Name: 'Cookies',
|
|
31
|
+
Order: 3,
|
|
32
|
+
Cost: 25,
|
|
33
|
+
Price: 70,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|