Skip to content

S130 — A game can move onto new content, and replays exactly across it - #588

Merged
The-Running-Dev merged 1 commit into
mainfrom
slice/S130-adopt-content
Oct 9, 2026
Merged

The-Running-Dev merged 1 commit into
mainfrom
slice/S130-adopt-content

Conversation

@The-Running-Dev

Copy link
Copy Markdown
Owner

What

A running game can now move onto another published version of its campaign. Engine.adoptContent(state, to) runs 16 §5.4's steps 0–8. On success it appends a LoggedContent entry, restamps campaignVersion and moves the envelope to formatVersion: 2. On refusal it returns a reason and leaves the state as it was. actionLog is now LoggedEntry[], and an epoch entry consumes a seq. deserialize reads both envelope shapes and refuses a stamp the log contradicts (C26). The §14 harness replays content entries through adoptContent. A game that never adopts still serializes as formatVersion: 1, byte for byte.

Criteria

  • S130.1 — met: engine.test.ts › S130.1: an action after an adoption takes seq 2, and its stream equals the one an uncrossed log of the same length derives. A third test covers startingCampaignVersion.

  • S130.2 — met: engine.test.ts › S130.2. One test per pinned reason: session_ended; unknown_campaign for the target and for the source; content_kind_changed; content_not_adoptable; migration_failed (fails and throws); content_incompatible (refuses and throws); invalid_state. Each asserts no state key and a deep-unchanged input. A further test shows the kind judges the target campaign's migrated state.

  • S130.3 — met: engine.test.ts › S130.3: the entry is appended and the envelope restamped; one core.content.adopted and one core.content.pinned, each with versions and reason; the same version returns the same object and emits nothing; a second adoption chains from the first.

  • S130.4 — met: engine.test.ts › S130.4. 8 negative cases, each invalid_state:

    • v1 with an epoch entry;
    • a last to that is not campaignVersion;
    • formatVersion 3;
    • v2 with no epoch entry;
    • an epoch entry with an actionId;
    • an unknown system;
    • a non-string to;
    • an out-of-place seq.

    2 positive cases: a valid v2 state round-trips byte-identically, and a migration entry whose from no archive holds is accepted without being resolved.

  • S130.5 — met: harness.test.ts › S130.5. A fixture crossing 1 → 2 on a test kind that declares adoptContent produces identical traceFixture output, one serialize() per entry, across two runs. Three fixtures fail: one whose content entry now refuses, one whose entry moves from an epoch the replay is not on, and one carrying a migration entry.

  • S130.6 — met: engine.test.ts › S130.4 › S130.6 (an unepoched state stays at formatVersion: 1). No golden file or replay-corpus fixture changed, and the full suite passes unchanged.

  • S130.7 — met: engine.test.ts imports LoggedEntry, LoggedContent, LoggedMigration, AdoptionResult and AdoptDecision from the package root. S130.7 asserts the three new base reason codes and their messages.

Differs from design

  • branchSession refuses a crossed log, as an interim measure. It refuses any source whose log carries an epoch entry with invalid_state and writes nothing (store.test.ts: refuses a log that crossed an epoch). Branching across epochs is S134's job; without this guard, a branch would silently replay a crossed log onto the default epoch. Recorded in 90-decisions.md.
  • Three interpretations are recorded in 90-decisions.md:
    • step 0 emits no event;
    • a v2 state with no epoch entry is refused;
    • the harness checks a content entry's from before adopting.
  • A missing kind at adoption pins with content_not_adoptable.
  • The internal harness gains traceFixture. runFixture serializes once, because a per-entry trace pushed the long-horizon fixture past its 5 s timeout. The harness is not a package-root export.

Verified

  • npm run typecheck, npm run lint, npm test: 115 files, 2257 tests passing.
  • ./build/ConvertTo-HumanDocumentation.ps1 and ./build/Test-Documentation.ps1 pass. git diff --check is clean.
  • Reverting each guard makes its tests fail:
    • removing the C26 stamp check: 4 S130.4 tests fail;
    • removing the branchSession guard: its test fails;
    • removing the harness from check: the mismatch test fails.
  • Not run locally: ./docs.ps1 -BuildOnly (no installed docs.ps1); the Verify Documentation Build check covers it.

@The-Running-Dev
The-Running-Dev merged commit abfc030 into main Oct 9, 2026
8 checks passed
@The-Running-Dev
The-Running-Dev deleted the slice/S130-adopt-content branch October 9, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant