@patterkit/runtime 0.12.1 → 0.14.0

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/CHANGELOG.md CHANGED
@@ -33,6 +33,54 @@ version number always means the same runtime behaviour. This package is versione
33
33
 
34
34
  ## [Unreleased]
35
35
 
36
+ ## [0.14.0] - 2026-09-24
37
+
38
+ ### Added
39
+
40
+ - **Other engines' scopes, with no setting.** A Patter line can name another engine's game-wide scope from the family's shared list (`@story.act` in a condition, an effect, or a `{@story.act}` slot) in any project. It is opaque to the compiler, recorded in the bundle (`externalScopes`), and never self-backed. Content that names one runs only where that engine is on the same registry: without it, `openFlow` and `loadGame` refuse before anything changes (`this content names @story, which no engine on this registry registered: give every engine the game's one registry`), and a write to a scope another engine has since removed fails naming the scope instead of landing in `@patter`.
41
+
42
+ ### Changed
43
+
44
+ - **One registry per game.** Every property bag the engine holds now lives in a `ScopeRegistry`
45
+ (`@wildwinter/scoperegistry` 0.7.0): `@patter` under `patter`, and each flow's and scene's bag
46
+ under a key starting `patter/`. A new `registry` option takes the game's own registry, shared
47
+ with any other engine in the game; without one the engine makes its own and acts as its own game,
48
+ so a single-engine game needs no change.
49
+ - **The save is version 3.** `saveGame()` holds what is not a property (cursors, PRNGs, visit
50
+ counts, selector cursors). An engine built without a registry also carries that registry's values
51
+ under `registry`, so one call is still the whole game; an engine given the game's registry leaves
52
+ them to the game, which saves the registry once. Version 2 saves still load, their values moving
53
+ into the registry.
54
+ - **A self-backed `@world` is saved.** When the game binds no resolver, `@world` is a property the
55
+ engine's registry stores, so it rides in the save. A resolver the game binds is still external and
56
+ never saved. Given the game's registry, the engine self-backs nothing: `@world` is the game's to
57
+ register there.
58
+ - **Every expression reads every registered scope**, so a condition can test another engine's
59
+ `@story.act` in a combined game. A token two engines both want fails as the second is built, naming
60
+ the first.
61
+ - `hotSwap` hands every bag to the replacement engine on the same registry. The engine it replaces is
62
+ released and its flows are closed. If the restore fails, the fallback engine keeps the shared
63
+ properties and restarts each flow, where it used to start everything cold.
64
+
65
+ ### Deprecated
66
+
67
+ - `Engine.save()` / `Engine.load()`, the shared `@patter` values alone. Save the registry instead.
68
+
69
+ ## [0.13.0] - 2026-09-05
70
+
71
+ ### Changed
72
+
73
+ - **A `writable: false` host declaration is the STORY's promise, and only the story's: the game
74
+ writes it.** `Engine.setProperty` / `Flow.setProperty` are the game's surfaces, so they now write a
75
+ read-only `@world` property whether it is self-backed or bound to a resolver; an effect that writes
76
+ one is refused exactly as before, with the same sentence. Until now the runtime refused every
77
+ caller alike, so a game could not advance its own clock through the engine and a Patterplay
78
+ coverage driver on such a property killed the run - which is how both this project and the
79
+ Storylet Engine found it, on the same day, in different content
80
+ (from-storylets/host-writes-to-read-only-world). The rule now lives in the shared kernel
81
+ (`@wildwinter/scoperegistry` ^0.6.0, an explicit host authority on `set`), so both products mean
82
+ the same thing by the flag.
83
+
36
84
  ## [0.12.1] - 2026-09-04
37
85
 
38
86
  ### Changed
package/README.md CHANGED
@@ -40,9 +40,11 @@ for (;;) {
40
40
  }
41
41
  ```
42
42
 
43
- `new Engine(bundle, options)` takes `{ rng?, seed?, locale?, world?, replayPromptOnChoose?, closedCaptions? }`:
43
+ `new Engine(bundle, options)` takes `{ rng?, seed?, locale?, world?, registry?, replayPromptOnChoose?, closedCaptions? }`:
44
44
  - `world` is the host's resolver for `@world` properties (World Properties): `{ get(name), set?(name, value) }`.
45
- Omit it and the runtime self-backs `@world` from the declared defaults.
45
+ Omit it and the runtime self-backs `@world` from the declared defaults, as a property it saves.
46
+ - `registry` is the game's one `ScopeRegistry` (from `@wildwinter/scoperegistry`), holding every
47
+ engine's properties. Omit it and the engine makes its own. See *One registry per game* below.
46
48
  - `locale` plays a non-default language (embedded localisation; an IDs-only bundle ignores it).
47
49
  - A string the active locale is missing falls back to the default-locale source,
48
50
  flagged `<Untranslated: {id}> {source}` so a partial translation is impossible to miss.
@@ -90,7 +92,24 @@ The release zip's `demos/` folder holds two working references (in the repo:
90
92
 
91
93
  ## Save / load
92
94
 
93
- `engine.saveGame()` returns a JSON-serialisable snapshot of the whole game (shared
94
- state, visit counts, every live flow); `engine.loadGame(blob)` restores it.
95
- [@patterkit/play-helpers](../play-helpers) wraps these as `serializeState` /
96
- `deserializeState` with a tagged envelope.
95
+ `engine.saveGame()` returns a JSON-serialisable snapshot of the whole game (visit counts,
96
+ selector cursors, every live flow, and, for an engine built without a registry, every property
97
+ value); `engine.loadGame(blob)` restores it. Saves from before property values moved into the
98
+ registry (version 2) still load. [@patterkit/play-helpers](../play-helpers) wraps these as
99
+ `serializeState` / `deserializeState` with a tagged envelope.
100
+
101
+ ## One registry per game
102
+
103
+ A game has one `ScopeRegistry` holding every property from every engine, except values the game
104
+ keeps itself and lends through a resolver, and saves it once. Pass it as `registry`:
105
+
106
+ ```ts
107
+ import { ScopeRegistry } from "@wildwinter/scoperegistry";
108
+
109
+ const registry = new ScopeRegistry().defineOwned("world", worldDeclarations, { owner: "Game" });
110
+ const engine = new Engine(bundle, { registry });
111
+ const save = { registry: registry.save(), patter: engine.saveGame() }; // saveGame() holds no values now
112
+ ```
113
+
114
+ The engine registers `@patter` under `patter` and every per-flow and per-scene bag under a key
115
+ starting `patter/`; `@world` is the game's to register. Loading works in either order.