Skip to content

Commit 9da79de

Browse files
Reconcile — W74b browser-column drift, W88 export-split decision (#372)
**What changed, and why.** `09-clients.md`'s API-coverage table cited a browser-client test file W74b deleted four days before this pass was written; repoints the column at `SubZeroDev.Adventures`, with no gate in this repo re-running it. `20-contract.md` §19 gains a third sanctioned package-root export category — a kind's own reference campaign (`buildWorldGraphMvpCampaign`) — closing the gap between what W88 shipped and what §19 said, and gets its own decision-log entry rather than leaving the reasoning in a slice that will eventually be renumbered out. Regenerates the affected `docs/docs/engine/` pages and fixes a stale `guide.md` claim (frozen-campaign removal already shipped in 0.9.0, not upcoming). **Note for the reviewer:** this branch also removes the open-register item on per-kind event-severity literals from `90-decisions.md` §2 and `OPEN-QUESTIONS.md`, with no corresponding decision entry explaining the removal — none of the three new 2026-08-23 entries address it, and I found no code change implementing the structural fix it called for. GitHub issue #371 tracks this exact item and is still open. Flagging rather than silently dropping — worth confirming whether the removal was intentional (issue resolved elsewhere) or should be restored. ### Verified **Ran and passed:** - Parse-check PowerShell scripts — ran locally: `[System.Management.Automation.Language.Parser]::ParseFile` against every `*.ps1` in the repo. No parse errors found. - Run Pester tests — CI `powershell` check passed in 1m4s on this exact head commit (run 32647649865, job 97214271251). A local reproduction (`Invoke-Pester -Path tools`) diverged with 10 failures, all inside `Test-DesignState`/`Read-DesignState`/`Update-DesignProjection` tests that shell out to `gh` to read dozens of tracked issues (e.g. `TrackerUnavailable: could not read issue #142...#371`) against a local `design/state/` cache — that step's own test (#79) asserts CI carries a `GH_TOKEN` env var this local session's `gh` auth does not replicate the same way for project/board-scoped lookups. Treated as environment divergence, not a code defect in this diff: CI ran the same commit and passed. - Typecheck, Lint, Test, Pack package, Inspect tarball, Consumer smoke — CI `engine` check passed in 38s (run 32647649885, job 97214271430). This PR changes no files under `src/engine/`, so the job's own change-detection step skips these and the job still reports success. - Validate Markdown links, terminology, and generated files — initially failed both locally and in CI (`Documentation links and terminology`, fail, 4m3s, run 32647649890, job 97214271493): `build/Test-Documentation.ps1:773` threw *"Generated guide is stale: docs/docs/guide.md (run /make-human-docs, then stamp the guide)"* because the earlier `/make-human-docs` pass this session edited `docs/docs/guide.md` without running the follow-up `./build/ConvertTo-HumanDocumentation.ps1 -StampGuide` step. Fixed (`7191435`) by running that stamp step and re-running locally: *"Human-documentation drift check passed across 18 generated engine page(s), compatibility pointers, and the guide. Documentation checks passed across 173 Markdown file(s)."* - Build documentation, Build and verify landing page, Merge landing page into documentation build — CI `Verify Documentation Build` passed in 2m11s (run 32647649890, job 97214271395). Not reproducible locally: these steps run inside the `ghcr.io/the-running-dev/docs-template` Docker image via the installed `docs.ps1` wrapper, and `docs.ps1` is generated by `Invoke-SetupDocs` rather than committed to this repo — it is absent from this checkout. - Test the host, Positive route and probe smoke, Negative fixture -- corrupted artifact must fail to start — CI `Build, run, and smoke the image` passed in 2m6s (run 32647649863, job 97214271317). Not attempted locally: the dotnet test step needs a `NUGET_GITHUB_TOKEN` secret unavailable locally, and the smoke steps need a full Docker image build/run this session didn't reproduce. **Ran and failed:** none, after the guide-digest fix above. **Did not run:** none — every discovered gate (`# verification: true` in `.github/workflows/{ci,verify,docs-ci,host-image}.yml`) produced a result, either from a local run or from CI's own execution of this exact commit. --- <details><summary><b>Agent detail</b></summary> <!-- agent:start --> - **Kind:** Reconciliation pass (`design/90-decisions.md`, three 2026-08-23 entries) + `/make-human-docs` regeneration - **Files:** `design/10-design.md`, `design/20-contract.md`, `design/90-decisions.md`, `docs/docs/engine/02-architecture.md`, `docs/docs/engine/04-core.md`, `docs/docs/engine/09-clients.md`, `docs/docs/engine/13-playable-web-demo.md`, `docs/docs/engine/OPEN-QUESTIONS.md`, `docs/docs/guide.md` - **Note:** the reconciliation content (design/decisions edits) predates this session's `/pr` invocation; `/pr` packaged it to a branch, committed, and pushed. `docs/docs/guide.md`'s content change and digest stamp were made in this session via `/make-human-docs` and `/verify`. <!-- agent:end --> </details>
2 parents 9c84f0d + 7191435 commit 9da79de

9 files changed

Lines changed: 157 additions & 84 deletions

File tree

design/10-design.md

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,9 @@ inside the engine as story-graph regression evidence only; it is not a publicati
605605

606606
The former in-repository `/play/` route and its campaign artifact directory are superseded:
607607
Adventures is the browser host now, consuming the deployed Content feed rather than
608-
Engine-generated campaign files. `/play/` itself stays in the repository through 0.8.0 and is
609-
removed with the breaking ownership release, 0.9.0 (design/30-slices.md, W74c).
608+
Engine-generated campaign files. `/play/` and its campaign artifact directory were removed from
609+
the repository by the breaking ownership release, 0.9.0 (design/30-slices.md, W74c); the package
610+
is at 0.10.0 and neither remains.
610611
<!-- human-doc:end -->
611612

612613
<!-- human-doc:start path="engine/05-observability.md" -->
@@ -2393,7 +2394,7 @@ checklist."* This is that checklist.
23932394
real client**, not by inspection and not by a unit test of the store. One row per operation,
23942395
one column per MVP client:
23952396

2396-
| # | Operation | Text client (W16) | MCP tool (W17) | Simulation kind (W50) | Browser demo (W61) | Hosted transport (Platform G1/S5) |
2397+
| # | Operation | Text client (W16) | MCP tool (W17) | Simulation kind (W50) | Browser client (Adventures) | Hosted transport (Platform G1/S5) |
23972398
|---|---|---|---|---|---|---|
23982399
| 1 | `listCampaigns` || `list_campaigns`||||
23992400
| 2 | `createSession` || `start_game`||||
@@ -2462,13 +2463,17 @@ through MCP independently — asserting identical `Scene`/`PlayerView` at every
24622463
client-free replay of the identical action log reaching the identical, golden-filed
24632464
`serialize()` output on repeat.
24642465

2465-
**Browser-demo evidence** lives in `site/src/play/browser-client.test.ts`. Its ten numbered
2466-
`it` blocks drive the real browser adapter against Bureaucracy, one per operation; they do not
2467-
call the store directly from a component test. The same file then drives the full committed
2468-
path through that adapter and the text client with the same seed and counting `IdSource`,
2469-
asserting identical `Scene`/`PlayerView` steps and byte-identical final `serialize()` output.
2470-
How the demo presents save/load — a same-page checkpoint, or the locally durable one
2471-
`13-playable-web-demo.md` §5 now specifies — does not weaken the adapter proof either way.
2466+
**The "Browser client" column is `SubZeroDev.Adventures`', and its evidence lives there** — the
2467+
same arrangement as the Hosted transport column below, and for the same reason: this repository
2468+
ships no browser client. W61 proved the column here, in `site/src/play/browser-client.test.ts`
2469+
ten numbered `it` blocks driving the real browser adapter against Bureaucracy one per operation,
2470+
then the full committed path through that adapter and the text client with the same seed and
2471+
counting `IdSource`, asserting identical `Scene`/`PlayerView` steps and byte-identical final
2472+
`serialize()` output. W74b deleted that file with the rest of `site/src/play/`
2473+
(`13-playable-web-demo.md`, *Succeeded by SubZeroDev.Adventures*), so **the ticks in this column
2474+
record what W61 demonstrated and what Adventures is obliged to keep demonstrating; no gate in this
2475+
repository re-runs them.** That is the honest cost of the extraction, stated rather than left to
2476+
be discovered — the same treatment §4's browser-portability gate already took.
24722477

24732478
**The mapping is one-to-one, and that is the point.** Every store operation has exactly one
24742479
MCP tool, and there is no tool that is not an operation. That is what *"no AI-specific path"*
@@ -2576,12 +2581,14 @@ bundle gate and §5's checkpoint lifetime against what W61 built, and §1's open
25762581
§10's non-goals and §11's first row against the multi-campaign shelf W63 and W64 shipped.
25772582
§§2–3 and §§6–9 are unchanged except where they cited §5's same-page limit.
25782583

2579-
> **`site/src/play/` is still in the tree, and still runs.** The route is gone from the build;
2580-
> the browser adapter, its ten-operation coverage suite and its parity proof are not.
2581-
> [`09-clients.md`](09-clients.md) §4's browser-demo column cites
2582-
> `site/src/play/browser-client.test.ts`, which still executes under both site test
2583-
> configurations. The directory is live evidence for a live claim, not dead code awaiting
2584-
> deletion.
2584+
> **`site/src/play/` is gone, and Revision 4 said otherwise for four days.** This blockquote
2585+
> read "still in the tree, and still runs" — true when Revision 4 was written, and false from
2586+
> W74b (`cf2d9eb`, 2026-08-19), which deleted the directory, the browser adapter, its
2587+
> ten-operation coverage suite and its parity proof together. Corrected rather than removed,
2588+
> because the claim was load-bearing: [`09-clients.md`](09-clients.md) §4's browser column cited
2589+
> `site/src/play/browser-client.test.ts` as its evidence, and that column is now Adventures' to
2590+
> prove (§4). The engine keeps its landing page and roadmap at `site/`; nothing else of the play
2591+
> surface remains here.
25852592
25862593
**Reading order:** after [`09-clients.md`](09-clients.md). That document owns what every
25872594
client may do; this one owns the first public browser client's product boundary, composition,
@@ -2611,9 +2618,10 @@ it, and Adventures is now the only play surface either repository ships.
26112618
- **§§1–2, §§6–9 are historical.** They describe a route, its states, its delivery and its
26122619
failure behaviour, and the route is gone. They are retained as the record of what the first
26132620
public browser client was, not as a target to build against. Two consequences of the removal
2614-
are worth naming rather than leaving to be discovered: §6's same-origin `campaigns/` fetch
2615-
has no caller, so the nine campaign JSON files and their manifest still ship in `site/public/`
2616-
and are copied into every artifact with nothing reading them; and §7's acceptance that "a
2621+
are worth naming rather than leaving to be discovered: §6's same-origin `campaigns/` fetch has
2622+
no caller, and W74b deleted `site/public/campaigns/` along with it, so the nine campaign JSON
2623+
files and their manifest no longer ship — `site/public/` now holds icons only; and §7's
2624+
acceptance that "a
26172625
direct static request to `/play/` succeeds" is not something any gate now checks, because
26182626
there is no such route to request.
26192627
- **§5's checkpoint lifetime is superseded by a stronger implementation.** Adventures has

design/20-contract.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,11 +1554,20 @@ and is an exception noted here rather than moved.
15541554
`SubZeroDev.Adventures.Content` owns the canonical source and publication of narrative
15551555
campaigns. GameEngine owns kinds, validation, portable hydration and authoring primitives.
15561556
GameEngine may retain a frozen campaign solely as a regression fixture; such a fixture is not
1557-
published and not listed in a manifest. Existing frozen campaigns stay package-root exports
1558-
through 0.8.0 for compatibility; the breaking 0.9.0 release removes them from the root
1559-
(design/30-slices.md, W74c). The peg moved once already: 0.8.0 was originally named here
1560-
and was then spent on an additive release, so a version reserved by this section is a name
1561-
to check against `src/engine/package.json` before a bump, not after.
1557+
published and not listed in a manifest. Frozen fixtures left the package root in the breaking
1558+
0.9.0 release (design/30-slices.md, W74c), which has shipped — `src/engine/package.json` reads
1559+
`0.10.0`. That peg moved once before it landed: this section originally named 0.8.0 as the
1560+
breaking release, and 0.8.0 was then spent on an additive one. A version reserved by this
1561+
section is a name to check against `src/engine/package.json` before a bump, not after.
1562+
1563+
A third category is sanctioned and is neither of the two above: **a kind's own reference
1564+
campaign** — engine-owned content that exists to make the kind registrable and exercisable,
1565+
never authored by Content and never a frozen fixture — is a package-root export. The instance is
1566+
`buildWorldGraphMvpCampaign` / `WORLD_GRAPH_MVP_CAMPAIGN_ID`, the only content a host can
1567+
register the `world-graph` kind against. The line that keeps this from swallowing the rule: the
1568+
root publishes no *narrative* campaign, which is the claim `src/engine/src/authoring.test.ts`
1569+
enforces in both directions. A second kind adding a reference campaign to the root follows this
1570+
sentence; a second reference campaign for one kind does not, and wants its own decision.
15621571
15631572
Portable campaign documents remain format version 2. `toPortable` and
15641573
`digestManifestResolution` are public only through `/authoring`; `fromPortable` remains a

design/90-decisions.md

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -310,21 +310,6 @@ fully correct fix, but it touches the shared `applyWorldEffects` interpreter sea
310310
all six call sites). **Accepted as-is for this MVP slice** — the event is debug severity
311311
and `scenario` is the only caller reading `.applied` today. Revisit if a second caller
312312
starts reading `.applied`, or as part of whatever unit closes #349.
313-
- **A kind's event severities are literals at each `emit` call; only the core fixes them in
314-
one table.** `05-observability.md` §7 requires severity to be fixed per event *name*, and
315-
`src/engine/src/core/observability/events.ts` holds a `CORE_EVENTS` map that makes the rule
316-
structural — all nine core events match §8 exactly. Every kind instead writes the severity
317-
inline, so nothing stops two call sites for one name from disagreeing, and nothing compares
318-
the emitted set against the contract's table at all. That is how four `world-graph`
319-
severities drifted for three weeks across four reconciliations (2026-08-23 decision above),
320-
and `incident.resolved`'s two call sites agree only by luck. The fix is a per-kind
321-
name→severity table mirroring `CORE_EVENTS`, plus a check that diffs `Kind.eventNames`, the
322-
emitted severities, and `20-contract.md`'s tables — comparing three lists by hand is the
323-
arithmetic-over-files work `CLAUDE.md` classifies as belonging in code rather than in a
324-
model. **Not started here**, because it touches three kinds' emit sites and their tests, and
325-
is its own unit under "one unit at a time". **Revisit when** that unit is sliced, or sooner
326-
if a fifth severity divergence appears.
327-
328313
---
329314

330315
## 3. Judgement Calls to Revisit (Settled for the MVP)
@@ -1072,3 +1057,62 @@ resolve**, mirroring 05 §8 exactly — the most internally consistent option an
10721057
first if a host ever runs `debug` in production; declined for now because the pair is the diagnostic
10731058
and no host runs kind-level `debug` today.
10741059
Reversibility: cheap — one table cell and a callout; the alternative is a three-line branch.
1060+
1061+
### 2026-08-23 — `09-clients.md` §4's browser column is Adventures', because its evidence left with W74b
1062+
Context: §4's whole stated value is that the ten-operation mapping is "checkable by counting, not by
1063+
reading intent", and one of its five columns — *Browser demo (W61)* — carried ten ticks resting on
1064+
`site/src/play/browser-client.test.ts`. W74b (`cf2d9eb`, 2026-08-19) deleted that file with the rest
1065+
of `site/src/play/`. The blockquote in `13-playable-web-demo.md`'s retirement narrative still read
1066+
"`site/src/play/` is still in the tree, and still runs", and cited the column as the live claim the
1067+
directory was evidence for — true when Revision 4 was written, false for four days and seven commits
1068+
to `10-design.md` after. The surrounding prose was already correct: §§1–2 and §§6–9 are marked
1069+
historical and §4's browser-portability gate is deleted. The column is what that pass missed.
1070+
Chosen: Repoint the column at `SubZeroDev.Adventures`, in the shape the *Hosted transport* column
1071+
already uses for Platform — name the owning repository, say the evidence lives there, and state
1072+
plainly that **no gate in this repository re-runs it**. The ticks now record what W61 demonstrated
1073+
and what Adventures is obliged to keep demonstrating. §13's peg sentence and the `site/public/`
1074+
campaign-file claim are corrected in the same pass; both were stale from the same commit.
1075+
Rejected: **Strike the column** — leaves §4 with three checkable columns and Platform's, and is the
1076+
tidier table, but it erases a real W61 result and quietly narrows what "no AI-specific path" (04 §13)
1077+
was ever demonstrated against. **Restore the evidence** — reverses W74b to satisfy a table, re-adds a
1078+
browser client to the repository that just retired one, and is a work unit with its own criteria
1079+
rather than a reconciliation edit.
1080+
Reversibility: cheap — one column header, one paragraph, one blockquote. Note the residual honestly:
1081+
this repository can no longer fail a build when the column stops being true.
1082+
1083+
### 2026-08-23 — A kind's own reference campaign is a sanctioned package-root export, and §19 now says so
1084+
Context: §19 sanctioned two categories of campaign — published narrative ones, which W74c removed
1085+
from the root, and frozen regression fixtures, which the same release removed. `src/engine/src/index.ts`
1086+
root-exports a third at `0.10.0`: `buildWorldGraphMvpCampaign` and `WORLD_GRAPH_MVP_CAMPAIGN_ID`. The
1087+
only statement that this is legitimate was a comment in `authoring.test.ts` — "content the engine
1088+
legitimately still ships" — while W88's own *Out of scope* read §19 as forbidding "exporting any
1089+
campaign". Two defensible readings of one section, with the deciding sentence in a test file.
1090+
Chosen: Name the category in §19. A kind's **reference campaign** — engine-owned, never Content's,
1091+
never a frozen fixture — is a root export, because it is what makes a registrable kind exercisable;
1092+
`world-graph` has no other content a host could register it against. The rule is bounded by the claim
1093+
`authoring.test.ts` actually enforces in both directions: the root publishes no *narrative* campaign.
1094+
A second kind adding one reference campaign follows this sentence; a second reference campaign for one
1095+
kind does not.
1096+
Rejected: **Remove both exports from the root** — the strictest reading, but a breaking change to a
1097+
published surface at 0.10.0 needs a version decision and is a work unit, and it would leave the
1098+
`world-graph` kind registrable with nothing to register. **Record it as known-and-retained** — leaves
1099+
§19 stating a rule the shipped surface breaks, which is the failure this pass exists to catch, and
1100+
leaves the sanction in a test comment where the next reader will not look.
1101+
Reversibility: cheap — one paragraph; the code is unchanged.
1102+
1103+
### 2026-08-23 — W88's export-split rule gets a decision entry, because a slice ledger is not the register
1104+
Context: W88 amended §19 with the rule that decides a kind's export split — the builder and its source
1105+
types are author-time and belong on `/authoring`; the campaign, state, view and outcome types are what
1106+
a runtime host compiles against and belong at the root — citing `design/30-slices.md, W88`. §19 states
1107+
the rule where a reader needs it, but the fork it resolved lives only in the slice: the two existing
1108+
kinds straddled the question (`world-graph` put all five at the root, `story-graph` put its campaign
1109+
and state types on `/authoring`), and `buildWorldGraphCampaign`'s root placement predates the subpath
1110+
and was noted rather than moved.
1111+
Chosen: Record it here as well, with the entry citing §19 rather than restating the rule — single
1112+
ownership keeps the rule in one place, and puts the *reasoning* where reasoning is indexed. The
1113+
trigger is that slices are retired: `W74.2``W74.5` already were, and this repository keeps the gap in
1114+
that numbering precisely as the record of a split. A rule whose only rationale lives in a retirable
1115+
ledger is one renumbering away from being unexplained.
1116+
Rejected: **Leave it in §19 alone** — the rule is stated and the reasoning is one link away, which is
1117+
defensible today and gets weaker every time the slice ledger is reorganised.
1118+
Reversibility: cheap — documentation only; no code, contract or behaviour change.

docs/docs/engine/02-architecture.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,5 +561,6 @@ inside the engine as story-graph regression evidence only; it is not a publicati
561561

562562
The former in-repository `/play/` route and its campaign artifact directory are superseded:
563563
Adventures is the browser host now, consuming the deployed Content feed rather than
564-
Engine-generated campaign files. `/play/` itself stays in the repository through 0.8.0 and is
565-
removed with the breaking ownership release, 0.9.0 (design/30-slices.md, W74c).
564+
Engine-generated campaign files. `/play/` and its campaign artifact directory were removed from
565+
the repository by the breaking ownership release, 0.9.0 (design/30-slices.md, W74c); the package
566+
is at 0.10.0 and neither remains.

docs/docs/engine/04-core.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,11 +1526,20 @@ and is an exception noted here rather than moved.
15261526
`SubZeroDev.Adventures.Content` owns the canonical source and publication of narrative
15271527
campaigns. GameEngine owns kinds, validation, portable hydration and authoring primitives.
15281528
GameEngine may retain a frozen campaign solely as a regression fixture; such a fixture is not
1529-
published and not listed in a manifest. Existing frozen campaigns stay package-root exports
1530-
through 0.8.0 for compatibility; the breaking 0.9.0 release removes them from the root
1531-
(design/30-slices.md, W74c). The peg moved once already: 0.8.0 was originally named here
1532-
and was then spent on an additive release, so a version reserved by this section is a name
1533-
to check against `src/engine/package.json` before a bump, not after.
1529+
published and not listed in a manifest. Frozen fixtures left the package root in the breaking
1530+
0.9.0 release (design/30-slices.md, W74c), which has shipped — `src/engine/package.json` reads
1531+
`0.10.0`. That peg moved once before it landed: this section originally named 0.8.0 as the
1532+
breaking release, and 0.8.0 was then spent on an additive one. A version reserved by this
1533+
section is a name to check against `src/engine/package.json` before a bump, not after.
1534+
1535+
A third category is sanctioned and is neither of the two above: **a kind's own reference
1536+
campaign** — engine-owned content that exists to make the kind registrable and exercisable,
1537+
never authored by Content and never a frozen fixture — is a package-root export. The instance is
1538+
`buildWorldGraphMvpCampaign` / `WORLD_GRAPH_MVP_CAMPAIGN_ID`, the only content a host can
1539+
register the `world-graph` kind against. The line that keeps this from swallowing the rule: the
1540+
root publishes no *narrative* campaign, which is the claim `src/engine/src/authoring.test.ts`
1541+
enforces in both directions. A second kind adding a reference campaign to the root follows this
1542+
sentence; a second reference campaign for one kind does not, and wants its own decision.
15341543
15351544
Portable campaign documents remain format version 2. `toPortable` and
15361545
`digestManifestResolution` are public only through `/authoring`; `fromPortable` remains a

0 commit comments

Comments
 (0)