Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ they are outputs, not editing surfaces.
| `11-content-packs.md` | Post-MVP: resolving an ordered pack set into the frozen registry. Campaigns replace wholesale, strings replace per key. The load-bearing part is **identity** — `campaignVersion` becomes a digest of the resolution, because two players on the same campaign version with different packs are playing different games and the envelope had no way to say so |
| `10-simulation-kind.md` | The second kind against the Kind seam — **the seam only**, not a port. Reconciles the upstream model with the envelope (seven fields it must not duplicate, and no persisted `RngState`), maps its richer verbs onto the one-action model (`plan.add`/`remove`/`clear`, `end_week`), and fixes projection, reason codes, events and terminal identity. §14 states what is still upstream and why |
| `12-world-graph-kind.md` | The third kind: a navigable world with autonomous inhabitants. A tick batch is the turn, and **batch invariance** — `advance_ticks n` reaches the same `kindState` as any split of it — is the load-bearing property, the one that forced `KindContext.derive` and the `tick` stream into 04. Win/loss is `Kind.outcome`, not a `GameStatus`. Not related to `story-graph` despite the suffix: a story graph is *authored*, a world graph is *navigated* |
| `09-clients.md` | The **client contract**, MVP scope: a client is a projection of the session store, never a participant — made testable as *two clients, same inputs, byte-identical `serialize()`*. Defines the **API coverage checklist** that `MVP.md` §5 and W16 both required and neither specified: nine store operations, nine MCP tools, one-to-one |
| `09-clients.md` | The **client contract**, MVP scope: a client is a projection of the session store, never a participant — made testable as *two clients, same inputs, byte-identical `serialize()`*. Defines the **API coverage checklist** that `MVP.md` §5 and W16 both required and neither specified: ten store operations, ten MCP tools, one-to-one |
| `13-playable-web-demo.md` | The first public browser client: a static `/play/` route over the Bureaucracy MVP, the browser-portability boundary, same-page checkpoint lifetime, client-parity proof, responsive/accessibility rules, and the explicit line between an engine demo and a finished game |
| `14-platform-static-host.md` | The first Platform consumer: a product-owned ASP.NET static host and immutable container image for the existing combined artifact, with the package-release gate, CI smoke contract, GHCR publication boundary, and the explicit line before a hosted engine |
| `08-session-capture.md` | Turning a played session into a `ReplayFixture` — post-MVP, gated on hosting. Mostly a **privacy contract**: no identity, only kind-declared params (`ActionParams` is arbitrary caller input), no timing; the seed is the sharp edge; promotion to the committed corpus is a reviewed one-way door |
| `07-replay.md` | The **regression oracle**, post-MVP: replaying `{config, actionLog}` fixtures across *engine versions* and comparing an `Outcome` built only from cross-version-stable vocabulary (`GameStatus`, `ReasonCode`, achievement ids). Distinct from 04 §14, which compares a build against itself. Fixtures are inputs, not state, so this sidesteps save migration |
| `06-extensibility.md` | Where the engine can be extended: the **ports** a host supplies (`IdSource`, stores, `Emitter`, `Clock`, `ExperimentSource`), the two composition roots, and the rule that decides — *a host may supply anything that cannot change `serialize()` output*, which makes the determinism boundary the trust boundary. Kinds stay engine-owned per architecture N2 |
Expand Down
456 changes: 436 additions & 20 deletions design/10-design.md

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions design/13-playable-web-demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- Generated compatibility pointer. Do not edit directly. -->

> Canonical content is in [10-design.md](10-design.md). This file preserves pre-migration relative links and section anchors only.

# Playable Web Demo — Browser Client and Static Delivery

## 1. Outcome and Boundary

## 2. Player Flow

## 3. Composition and Dependency Direction

## 4. Browser Portability Is an Engine Property

## 5. Checkpoints and Lifetime

## 6. Route, Visual System, and Delivery

## 7. Client Proof and Tests

## 8. Accessibility and Responsive Behaviour

## 9. Failure Behaviour

## 10. Explicit Non-Goals

## 11. Decision Summary
23 changes: 23 additions & 0 deletions design/14-platform-static-host.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- Generated compatibility pointer. Do not edit directly. -->

> Canonical content is in [10-design.md](10-design.md). This file preserves pre-migration relative links and section anchors only.

# Platform Static Host — Container Delivery without a Hosted Engine

## 1. Outcome and Boundary

## 2. Ownership and Dependency Direction

## 3. Platform Package Gate

## 4. Artifact Assembly and Routes

## 5. Runtime Contract

## 6. CI, Publication, and Deployment Boundary

## 7. Failure Behaviour

## 8. Explicit Non-Goals

## 9. Decision Summary
143 changes: 140 additions & 3 deletions design/30-slices.md
Original file line number Diff line number Diff line change
Expand Up @@ -1651,14 +1651,151 @@ client's rendering and the MCP surface all sit on the path.
test. Manual play-testing tooling, not a shipped client surface — distinct from "more
clients" below, no projection or store change, no contract change. Not required by any
Definition-of-Done item; exists purely so the engine can be seen running.
- [ ] More clients (web, Discord).

### [ ] W61 — Public Playable Web Demo {#w61}

**Delivers:** Turns the completed Bureaucracy MVP into a public `/play/` route a visitor can
finish without cloning the repository. The engine runs locally in the browser behind a real
client over `SessionStore`; the page renders scenes, shown choices, disabled reasons, visible
state and achievements, offers non-committing previews and same-page checkpoints, and reaches
the existing ending with no React-owned game rule.

This is deliberately one campaign and one route. The five story campaigns, Stable Life, and
the world-graph MVP already prove useful engine breadth, but putting all of them in a picker
would turn the first browser boundary into three rendering problems and still leave the
load-bearing question unanswered: can the package, session store, save envelope, registry and
client contract run together in a production browser bundle? [`13-playable-web-demo.md`](13-playable-web-demo.md)
fixes that product and architecture boundary.

The browser currently exposes three real portability defects hidden by the Node.js CLI:
`version.ts` reads `node:fs`, `envelope.ts` uses `node:crypto`, and `emitter.ts` reads an
unguarded `process.env`. Close those in the shared runtime, not with a reduced browser fork.
The checksum stays SHA-256 over the same canonical bytes, and the pure engine remains
synchronous; only the already-async store boundary may await platform crypto.

- **Spec:** [`13-playable-web-demo.md`](13-playable-web-demo.md);
[09 §1](09-clients.md#1-the-rule-made-testable),
[§2](09-clients.md#2-the-only-surface),
[§4](09-clients.md#4-the-api-coverage-checklist),
[§6](09-clients.md#6-projection-is-not-optional);
[04 §7](04-core.md#7-the-session-store-and-the-platform-api),
[§9](04-core.md#9-projection), [§10.2](04-core.md#102-save-envelope-and-migration).
- **Touches:** `src/engine/src/version.ts`, `core/persistence/envelope.ts`,
`core/observability/emitter.ts`, the package root and browser-bundle smoke test;
`site/` — a new play entry, composition root, browser adapter, React page, shared
navigation, styles and tests; the static-build/merge verification; factual status copy
in `README.md`, `src/engine/README.md`, and the landing page.
- **Depends on:** [W19](#x-w19--mvp-acceptance), [W31](#x-w31--save-migration), and W41.
Implement against whichever
landing build/merge mechanism is on `main` when the unit starts; issue #179's package
migration is not a semantic dependency and must not be reimplemented here.
- **Status:** Not started.
- **Done when:**
- W61.1 The supported engine entry graph used by the site produces a real browser bundle
with no `node:` import, unguarded Node.js global, runtime filesystem read, or second
browser-only engine path; Node.js typecheck, lint, tests and package build remain green.
- W61.2 `ENGINE_VERSION` still has package metadata as its single owner, and save/load under
Node.js and a browser produce the same lowercase SHA-256 checksum for the same
`{ state, replayCompatible }` canonical bytes without changing any committed replay or
serialization fixture.
- W61.3 The package root exports the committed Bureaucracy builder needed by the site
composition root; React and the browser adapter import only `SessionStore` types and
call no engine, kind, registry, validation, projection, or persistence helper.
- W61.4 A direct static request to `/play/` succeeds; the production artifact contains
`/`, `/roadmap/`, `/play/`, and `/docs/`, and the protected merge proves the docs
subtree byte-identical before and after overlay.
- W61.5 A visitor can start Bureaucracy, traverse the `office_visits >= 3` loop, see the
gated choice with its reason, exercise the seeded transition, reach the existing
ending, see `it_builds_character`, and start again. No raw `LocKey` appears in any
ready, playing, rejected, preview, or ended state.
- W61.6 Previewing an enabled choice shows the labelled prospective scene without changing
the committed scene, view, action sequence or checkpoint; committing it afterwards
reaches the same result as choosing it without a preview.
- W61.7 Save/load is presented honestly as a same-page checkpoint. Restoring it loses no
state; refreshing starts a new demo and the UI says so. No component writes raw state
or a save envelope to browser storage.
- W61.8 [09 §4](09-clients.md#4-the-api-coverage-checklist)'s browser column is checked
against ten named adapter tests. The full Bureaucracy path through the browser adapter
and text client, under the same seed and counting `IdSource`, produces identical
`Scene`/`PlayerView` steps and byte-identical final `serialize()` output.
- W61.9 The page is keyboard-complete and usable at 320 px, 390 px, 768 px and 1280 px:
native action controls, adjacent disabled reasons, visible focus, announced committed
scene changes, no colour-only state, no horizontal overflow, and complete reduced-motion
behavior.
- W61.10 The public header and landing page expose `Play`; stale claims that nothing is
playable are corrected without calling the demo a finished game. Site checks,
documentation checks, engine gates, `git diff --check`, and the exact-merge deployment
verification all pass before the route is announced.
- **Out of scope:** additional campaigns or kinds; durable browser storage; profiles across
reloads; accounts, cloud sync or any backend; new gameplay; art, audio, analytics,
session capture, service workers, a PWA, or a generic reusable web-client package.

### [ ] W62 — Platform Static Host Image {#w62}

**Delivers:** Adds a product-owned ASP.NET Core host under `src/host/`, composed with
`SubZeroDev.Platform.Hosting`, and packages W61's verified combined static artifact into a
stateless container. Pull requests build, run, and smoke the image. Merges to `main` publish a
new immutable GHCR image when relevant hosting inputs change, but do not deploy it; GitHub Pages
remains the public host.

This is the first Platform consumer and deliberately the smaller half of hosting. The engine
continues to execute in the browser. A later W63 owns the `.NET Platform edge → Node engine
workload`, JSON/HTTP boundary, MCP projection, and remote session semantics.

- **Spec:** [`14-platform-static-host.md`](14-platform-static-host.md);
[`13-playable-web-demo.md`](13-playable-web-demo.md) §6;
the Platform repository's `platform-identity.md`, `engine-hosting-contract.md`, ADR-002,
ADR-005, and D3 implementation plan.
- **Touches:** new `src/host/` web project and tests; the multi-stage container definition and
build context; static artifact/route smoke scripts; CI and GHCR publication workflows;
package-source configuration without credentials; hosting documentation.
- **Depends on:** [W61](#w61) and SubZeroDev.Platform S9 package publication. A temporary sibling
`ProjectReference` may unblock local development, but W62 cannot merge until the project
uses one exact released `SubZeroDev.Platform.Hosting` package version and a clean CI clone
restores without `../SubZeroDev.Platform`.
- **Status:** Not started.
- **Done when:**
- W62.1 `src/host/` is the product composition root. It calls `AddPlatformWebHost()` and maps
Platform probes; Platform gains no GameEngine dependency, and the host adds no worker,
persistence, migration, outbox, account, or session service.
- W62.2 The committed project pins an exact released `SubZeroDev.Platform.Hosting` NuGet
version. CI restores it with a short-lived secret that is absent from repository files,
Docker arguments, environment layers, runtime image history, and build output.
- W62.3 One multi-stage build constructs the site and documentation from the same commit, runs
the protected merge, proves the docs subtree byte-identical, publishes the host, and
copies only the verified combined artifact into `wwwroot` in the runtime stage.
- W62.4 Direct container requests to `/`, `/roadmap/`, `/play/`, and `/docs/` return the
expected documents; Platform liveness and readiness succeed; a named unknown route
returns `404` with no SPA fallback.
- W62.5 The browser demo remains W61's local `SessionStore` client: the container exposes no
engine API, game action, or runtime content endpoint, and a production browser smoke
observes no such network request.
- W62.6 The runtime image contains no Node.js, package-manager cache, source tree, build tools,
or registry credential; it runs non-root, supports a read-only root filesystem, writes no
product data, performs no normal outbound request, and stops gracefully on `SIGTERM`.
- W62.7 PR CI builds and starts the exact image, runs positive route/probe/browser checks, and
contains a deliberate missing-or-corrupt-artifact case that proves the gate fails red.
- W62.8 A path-filtered `main` workflow publishes a new GHCR image only when host, site,
documentation-build, merge, container, or locked dependency inputs change. It records an
immutable full-commit tag and digest, creates no `latest` tag, and performs no deployment.
- W62.9 The existing GitHub Pages exact-merge workflow and public routes remain unchanged and
green; an image publication failure cannot alter the live site or an earlier image.
- **Out of scope:** public deployment, DNS/TLS/custom domain, traffic cutover or rollback;
hosted Node engine/API/MCP/session behavior (W63); persistence, auth, accounts, databases,
worker processes; gameplay, campaign, browser-client, or serialization changes; a generic
static-site facility in Platform.

- [ ] More clients (Discord; the first web client is [W61](#w61)).
- [ ] **Additional locales — sliced as [W60](#w60).** The MVP ships English only; the
authoring→registry types already support more
([04 §10.1](04-core.md#101-content-registry)), so this is string tables plus tooling,
no type change.
- [ ] AI-assisted authoring (content only; engine validates).
- [ ] The hosted service — only once all of the above works
([`neaas-platform-vision.md`](https://github.com/The-Running-Dev/SubZeroDev.Platform)).
- [ ] **W63 proposed — Hosted engine edge.** Follow W62 with the real
`.NET Platform edge → Node engine workload` process boundary: generated JSON/HTTP service
contract first, MCP as a projection, one in-memory remote session before persistence,
accounts, catalogue, or metering. Slice it only after W62 and the Platform package gate are
proven ([`neaas-platform-vision.md`](https://github.com/The-Running-Dev/SubZeroDev.Platform)).
- [ ] Content packs — **sliced as [W58](#w58) and [W59](#w59)** — per
[`11-content-packs.md`](11-content-packs.md): `resolvePacks` as a
pure ordered fold; campaigns replace wholesale, strings per key; exact-version
Expand Down
6 changes: 6 additions & 0 deletions design/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@

### Breadth: The Platform

### [ ] W61 — Public Playable Web Demo {#w61}

### [ ] W62 — Platform Static Host Image {#w62}

### Content Tooling — A First-Class Workstream, Not an Afterthought

## Known Open Items Carried In
Expand All @@ -163,3 +167,5 @@
### [x] W48 — preview/client parity: `previewAction` across Engine, session, text and MCP {#w48}

### [x] W49 — validation, scenario and replay guard: the canonical engine-owned MVP {#w49}

### [ ] W63 — Hosted engine edge. Follow W62 with the real {#w63}
Loading