@its-not-rocket-science/ananke 0.1.68 → 0.5.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.
Files changed (165) hide show
  1. package/CHANGELOG.md +274 -0
  2. package/README.md +150 -431
  3. package/STABLE_API.md +113 -490
  4. package/conformance/README.md +46 -44
  5. package/conformance/lockstep-sequence.json +395 -395
  6. package/conformance/phase-order.json +21 -0
  7. package/conformance/replay-parity.json +65 -65
  8. package/conformance/state-hash-regression.json +39 -0
  9. package/conformance/state-hash.json +23 -22
  10. package/dist/src/conformance.d.ts +8 -2
  11. package/dist/src/content/index.d.ts +3 -0
  12. package/dist/src/content/index.js +2 -0
  13. package/dist/src/content/injector.d.ts +19 -0
  14. package/dist/src/content/injector.js +61 -0
  15. package/dist/src/content/loader.d.ts +3 -0
  16. package/dist/src/content/loader.js +170 -0
  17. package/dist/src/content/types.d.ts +56 -0
  18. package/dist/src/content/types.js +1 -0
  19. package/dist/src/content/validator.d.ts +6 -0
  20. package/dist/src/content/validator.js +14 -0
  21. package/dist/src/content-pack.d.ts +3 -2
  22. package/dist/src/content-pack.js +205 -10
  23. package/dist/src/data-governance.d.ts +73 -0
  24. package/dist/src/data-governance.js +188 -0
  25. package/dist/src/debug.d.ts +6 -0
  26. package/dist/src/debug.js +9 -0
  27. package/dist/src/determinism.d.ts +12 -0
  28. package/dist/src/determinism.js +93 -0
  29. package/dist/src/dialogue.d.ts +41 -0
  30. package/dist/src/dialogue.js +113 -2
  31. package/dist/src/economy.d.ts +97 -3
  32. package/dist/src/economy.js +191 -37
  33. package/dist/src/history/autosave.d.ts +19 -0
  34. package/dist/src/history/autosave.js +98 -0
  35. package/dist/src/history/timetravel.d.ts +28 -0
  36. package/dist/src/history/timetravel.js +61 -0
  37. package/dist/src/index.d.ts +19 -31
  38. package/dist/src/index.js +20 -38
  39. package/dist/src/model3d.js +63 -2
  40. package/dist/src/narrative/combat-logger.d.ts +25 -0
  41. package/dist/src/narrative/combat-logger.js +97 -0
  42. package/dist/src/narrative/plausibility.d.ts +30 -0
  43. package/dist/src/narrative/plausibility.js +70 -0
  44. package/dist/src/narrative-layer.d.ts +2 -0
  45. package/dist/src/narrative-layer.js +2 -0
  46. package/dist/src/navigation/causal-chain.d.ts +26 -0
  47. package/dist/src/navigation/causal-chain.js +74 -0
  48. package/dist/src/netcode.d.ts +1 -1
  49. package/dist/src/netcode.js +1 -1
  50. package/dist/src/performance/adaptive-tick.d.ts +23 -0
  51. package/dist/src/performance/adaptive-tick.js +50 -0
  52. package/dist/src/plugins/loader.d.ts +23 -0
  53. package/dist/src/plugins/loader.js +86 -0
  54. package/dist/src/plugins/registry.d.ts +11 -0
  55. package/dist/src/plugins/registry.js +28 -0
  56. package/dist/src/plugins/types.d.ts +31 -0
  57. package/dist/src/plugins/types.js +7 -0
  58. package/dist/src/replay.js +5 -2
  59. package/dist/src/scenario.js +2 -1
  60. package/dist/src/serialization/binary.d.ts +5 -0
  61. package/dist/src/serialization/binary.js +193 -0
  62. package/dist/src/session.d.ts +115 -0
  63. package/dist/src/session.js +228 -0
  64. package/dist/src/sim/ai/decide.js +1 -1
  65. package/dist/src/sim/ai/personality.js +2 -2
  66. package/dist/src/sim/bodyplan.d.ts +30 -0
  67. package/dist/src/sim/context.d.ts +2 -0
  68. package/dist/src/sim/kernel.js +80 -1294
  69. package/dist/src/sim/normalization.d.ts +12 -0
  70. package/dist/src/sim/normalization.js +92 -0
  71. package/dist/src/sim/resolvers/attack-resolver.d.ts +30 -0
  72. package/dist/src/sim/resolvers/attack-resolver.js +310 -0
  73. package/dist/src/sim/resolvers/capability-resolver.d.ts +15 -0
  74. package/dist/src/sim/resolvers/capability-resolver.js +270 -0
  75. package/dist/src/sim/resolvers/grapple-resolver.d.ts +28 -0
  76. package/dist/src/sim/resolvers/grapple-resolver.js +70 -0
  77. package/dist/src/sim/resolvers/shoot-resolver.d.ts +27 -0
  78. package/dist/src/sim/resolvers/shoot-resolver.js +212 -0
  79. package/dist/src/sim/resolvers/treat-resolver.d.ts +16 -0
  80. package/dist/src/sim/resolvers/treat-resolver.js +77 -0
  81. package/dist/src/sim/step/apply/intents.d.ts +6 -0
  82. package/dist/src/sim/step/apply/intents.js +83 -0
  83. package/dist/src/sim/step/kernel-pipeline.d.ts +9 -0
  84. package/dist/src/sim/step/kernel-pipeline.js +15 -0
  85. package/dist/src/sim/step/phases/capability-phase.d.ts +8 -0
  86. package/dist/src/sim/step/phases/capability-phase.js +65 -0
  87. package/dist/src/sim/step/phases/cooldowns-phase.d.ts +3 -0
  88. package/dist/src/sim/step/phases/cooldowns-phase.js +37 -0
  89. package/dist/src/sim/step/phases/prepare-phase.d.ts +10 -0
  90. package/dist/src/sim/step/phases/prepare-phase.js +33 -0
  91. package/dist/src/sim/step/pipeline.d.ts +2 -0
  92. package/dist/src/sim/step/pipeline.js +15 -0
  93. package/dist/src/sim/step/resolvers/impact-resolver.d.ts +9 -0
  94. package/dist/src/sim/step/resolvers/impact-resolver.js +55 -0
  95. package/dist/src/sim/step/world-phases.d.ts +7 -0
  96. package/dist/src/sim/step/world-phases.js +15 -0
  97. package/dist/src/sim/step/world-step-context.d.ts +23 -0
  98. package/dist/src/sim/step/world-step-context.js +1 -0
  99. package/dist/src/sim/systemic-toxicology.js +1 -1
  100. package/dist/src/sim/team.js +2 -2
  101. package/dist/src/sim/testing.js +4 -3
  102. package/dist/src/sim/toxicology.d.ts +1 -1
  103. package/dist/src/sim/toxicology.js +1 -1
  104. package/dist/src/sim/world.d.ts +17 -24
  105. package/dist/src/snapshot.js +3 -6
  106. package/dist/src/tier2.d.ts +19 -0
  107. package/dist/src/tier2.js +21 -0
  108. package/dist/src/tier3.d.ts +10 -0
  109. package/dist/src/tier3.js +12 -0
  110. package/dist/src/version.d.ts +6 -0
  111. package/dist/src/version.js +8 -0
  112. package/dist/src/wasm/bridge.d.ts +14 -0
  113. package/dist/src/wasm/bridge.js +84 -0
  114. package/dist/src/wasm-kernel.d.ts +2 -0
  115. package/dist/src/wasm-kernel.js +1 -0
  116. package/dist/src/world-evolution-backend/engine.d.ts +4 -0
  117. package/dist/src/world-evolution-backend/engine.js +347 -0
  118. package/dist/src/world-evolution-backend/host-schema.d.ts +145 -0
  119. package/dist/src/world-evolution-backend/host-schema.js +375 -0
  120. package/dist/src/world-evolution-backend/index.d.ts +1 -0
  121. package/dist/src/world-evolution-backend/index.js +1 -0
  122. package/dist/src/world-evolution-backend/open-world-host-adapter.d.ts +96 -0
  123. package/dist/src/world-evolution-backend/open-world-host-adapter.js +208 -0
  124. package/dist/src/world-evolution-backend/profiles.d.ts +4 -0
  125. package/dist/src/world-evolution-backend/profiles.js +136 -0
  126. package/dist/src/world-evolution-backend/public.d.ts +11 -0
  127. package/dist/src/world-evolution-backend/public.js +7 -0
  128. package/dist/src/world-evolution-backend/reproducibility.d.ts +8 -0
  129. package/dist/src/world-evolution-backend/reproducibility.js +35 -0
  130. package/dist/src/world-evolution-backend/timeline.d.ts +6 -0
  131. package/dist/src/world-evolution-backend/timeline.js +337 -0
  132. package/dist/src/world-evolution-backend/types.d.ts +138 -0
  133. package/dist/src/world-evolution-backend/types.js +1 -0
  134. package/dist/src/world-evolution-host-backend.d.ts +51 -0
  135. package/dist/src/world-evolution-host-backend.js +97 -0
  136. package/dist/src/world-evolution.d.ts +201 -0
  137. package/dist/src/world-evolution.js +543 -0
  138. package/dist/src/world-factory.js +2 -1
  139. package/dist/tools/pack-cli.js +55 -2
  140. package/docs/bridge-contract.md +140 -332
  141. package/docs/completion-evidence-dashboard.md +63 -0
  142. package/docs/host-contract.md +86 -310
  143. package/docs/host-world-evolution-schema.md +155 -0
  144. package/docs/integration-primer.md +29 -22
  145. package/docs/migration-monolith-to-modular.md +102 -120
  146. package/docs/package-architecture.md +116 -323
  147. package/docs/plugins/README.md +42 -0
  148. package/docs/plugins/hooks.md +22 -0
  149. package/docs/plugins/publishing.md +29 -0
  150. package/docs/plugins/security-model.md +273 -0
  151. package/docs/project-overview.md +30 -30
  152. package/docs/release-dashboard.md +47 -0
  153. package/docs/session-api.md +127 -0
  154. package/docs/stable-api-manifest.json +52 -0
  155. package/docs/trust-dashboard.md +37 -0
  156. package/docs/versioning.md +256 -245
  157. package/docs/wire-protocol.md +140 -209
  158. package/docs/world-evolution-backend.md +200 -0
  159. package/docs/world-evolution-engine.md +150 -0
  160. package/docs/world-evolution-host-backend.md +70 -0
  161. package/docs/world-evolution-orchestration.md +248 -0
  162. package/package.json +125 -6
  163. package/schema/content-pack.schema.json +85 -0
  164. package/schema/plugin.schema.json +57 -0
  165. package/schema/world-evolution-input.schema.json +196 -0
@@ -1,209 +1,140 @@
1
- # Ananke Wire Protocol & Save Format
2
-
3
- This document specifies how Ananke state is serialised for persistence, replay, and
4
- network transport. All formats are deterministic: the same simulation state always
5
- produces the same bytes.
6
-
7
- ---
8
-
9
- ## 1. Concepts
10
-
11
- | Term | Meaning |
12
- |------|---------|
13
- | **Snapshot** | A serialised `WorldState` — complete enough to resume simulation |
14
- | **Replay** | An initial snapshot + a sequence of command frames |
15
- | **Diff** | A compact binary diff between two consecutive snapshots (CE-9) |
16
- | **Wire message** | A single unit transmitted between host and client over the network |
17
- | **Q value** | A fixed-point integer scaled by `SCALE.Q = 10 000` (e.g. `q(0.75) = 7500`) |
18
-
19
- ---
20
-
21
- ## 2. JSON Snapshot Format
22
-
23
- JSON is the recommended format for long-term save files and editor tooling.
24
-
25
- ### 2.1 Deterministic key ordering
26
-
27
- When computing hash-checks across clients, keys must appear in insertion order.
28
- The canonical TypeScript implementation (`JSON.stringify`) preserves insertion
29
- order for string keys. Third-party deserializers must preserve or sort keys
30
- identically.
31
-
32
- ### 2.2 Q values
33
-
34
- All `Q`-typed fields are serialised as plain integers. Do **not** divide by
35
- `SCALE.Q` before saving — the raw integer is the canonical representation.
36
-
37
- ```json
38
- { "fearQ": 7500 } // correct — q(0.75)
39
- { "fearQ": 0.75 } // WRONG — will cause precision loss and replay divergence
40
- ```
41
-
42
- ### 2.3 Maps
43
-
44
- JavaScript `Map` instances do not serialise to JSON automatically. Ananke
45
- serialises `Map<K, V>` as an array of `[K, V]` pairs:
46
-
47
- ```json
48
- { "__nutritionAccum": 0 }
49
- ```
50
-
51
- > Note: `__nutritionAccum` was simplified to a scalar in v0.1. If a `Map`
52
- > field is added in a future version, its pairs will use the array format above.
53
-
54
- ### 2.4 Version stamping
55
-
56
- Always call `stampSnapshot(world, "world")` before persisting. This adds
57
- `_ananke_version` and `_schema` fields that enable forward migration:
58
-
59
- ```typescript
60
- import { stampSnapshot } from "@its-not-rocket-science/ananke/schema";
61
- // or: import { stampSnapshot } from "@ananke/core"; (when published)
62
-
63
- const save = JSON.stringify(stampSnapshot(world, "world"), null, 2);
64
- ```
65
-
66
- ### 2.5 JSON Schema files
67
-
68
- Canonical schemas ship with the package:
69
-
70
- | File | Validates |
71
- |------|-----------|
72
- | `schema/world.schema.json` | `WorldState` snapshots |
73
- | `schema/replay.schema.json` | `Replay` objects |
74
-
75
- Use `validateSnapshot(raw)` from `@its-not-rocket-science/ananke/schema` to
76
- check conformance programmatically before calling `stepWorld`.
77
-
78
- ---
79
-
80
- ## 3. Binary Diff Format
81
-
82
- For tick-to-tick state synchronisation (multiplayer, streaming), use the binary
83
- diff format implemented in `src/snapshot.ts`.
84
-
85
- ### 3.1 Encoding
86
-
87
- ```
88
- [magic: "ANKD" (4 bytes)] [version: 1 (u8)] [payload: tag-value stream]
89
- ```
90
-
91
- Tag values:
92
-
93
- | Tag | Byte | Encodes |
94
- |-----|------|---------|
95
- | NULL | 0x00 | `null` |
96
- | TRUE | 0x01 | `true` |
97
- | FALSE | 0x02 | `false` |
98
- | UINT8 | 0x10 | Unsigned integer 0–255 |
99
- | INT32 | 0x11 | Signed 32-bit integer (big-endian) |
100
- | FLOAT64 | 0x12 | IEEE 754 double (big-endian) — use only for non-Q floats |
101
- | STRING | 0x20 | Length-prefixed UTF-8 |
102
- | ARRAY | 0x30 | Length-prefixed sequence of tag-value items |
103
- | OBJECT | 0x40 | Length-prefixed sequence of (string key, tag-value) pairs |
104
-
105
- ### 3.2 Usage
106
-
107
- ```typescript
108
- import { diffWorldState, packDiff, unpackDiff, applyDiff } from "@its-not-rocket-science/ananke";
109
-
110
- // Sender
111
- const diff = diffWorldState(prevState, nextState);
112
- const bytes = packDiff(diff);
113
- socket.send(bytes);
114
-
115
- // Receiver
116
- const diff2 = unpackDiff(bytes);
117
- const state2 = applyDiff(prevState, diff2);
118
- ```
119
-
120
- ### 3.3 Determinism guarantee
121
-
122
- A diff produced from identical states must produce identical bytes. Do not
123
- include wall-clock timestamps or random nonces in diff payloads.
124
-
125
- ---
126
-
127
- ## 4. Multiplayer Message Protocol
128
-
129
- For lockstep multiplayer, hosts exchange command frames rather than full state.
130
-
131
- ### 4.1 Message types
132
-
133
- | `kind` | Direction | Payload |
134
- |--------|-----------|---------|
135
- | `"cmd"` | Client → Server | `{ tick, commands: Command[] }` |
136
- | `"ack"` | Server → Client | `{ tick, stateHash: number }` |
137
- | `"resync"` | Server Client | `{ tick, snapshot: WorldState }` |
138
- | `"hash_mismatch"` | Server Client | `{ tick, expected: number, got: number }` |
139
-
140
- ### 4.2 State hash
141
-
142
- Use the built-in tick counter and entity count as a cheap hash for divergence
143
- detection:
144
-
145
- ```typescript
146
- function stateHash(world: WorldState): number {
147
- return world.tick * 0x10000 + (world.entities.length & 0xFFFF);
148
- }
149
- ```
150
-
151
- A full structural hash is more robust but expensive; use it only on resync.
152
-
153
- ### 4.3 Lockstep loop
154
-
155
- ```
156
- ┌──────────────────────────────────────────────────────────┐
157
- │ Client Server │
158
- │ │
159
- │ collect commands ──── cmd ──► apply to authoritative │
160
- │ state │
161
- │ ◄── ack ─── broadcast stateHash │
162
- │ verify hash │
163
- │ if mismatch ─── resync req ─► send full snapshot │
164
- │ ◄── resync ── │
165
- │ restore snapshot │
166
- └──────────────────────────────────────────────────────────┘
167
- ```
168
-
169
- ### 4.4 Transport encoding
170
-
171
- Use JSON for development and debugging. For production, encode wire messages
172
- as CBOR (RFC 8949) or MessagePack for ~30% size reduction. The message
173
- structure is identical; only the outer encoding changes.
174
-
175
- ---
176
-
177
- ## 5. Save File Recommendations
178
-
179
- | Scenario | Format | Compression |
180
- |----------|--------|-------------|
181
- | Development / debugging | JSON (pretty-printed) | none |
182
- | Production saves | JSON (compact) | gzip or zstd |
183
- | Network sync (full state) | JSON or CBOR | none (already compact) |
184
- | Network sync (incremental) | Binary diff (`packDiff`) | none |
185
- | Replay archives | JSON replay schema | zstd |
186
-
187
- ---
188
-
189
- ## 6. Migration
190
-
191
- Load a save and bring it to the current schema version before simulating:
192
-
193
- ```typescript
194
- import {
195
- migrateWorld, validateSnapshot, stampSnapshot,
196
- } from "@its-not-rocket-science/ananke/schema";
197
-
198
- function loadSave(json: string): WorldState {
199
- const raw = JSON.parse(json) as Record<string, unknown>;
200
- const migrated = migrateWorld(raw); // no-op until 0.2 is released
201
- const errors = validateSnapshot(migrated);
202
- if (errors.length > 0) {
203
- throw new Error(`Invalid save: ${errors.map(e => `${e.path}: ${e.message}`).join("; ")}`);
204
- }
205
- return migrated as WorldState;
206
- }
207
- ```
208
-
209
- See `docs/migration-monolith-to-modular.md` for package-level migration guidance.
1
+ # Ananke Wire Protocol & Schema Formats
2
+
3
+ This page documents only what is currently shipped in code. It avoids treating roadmap ideas as current protocol guarantees.
4
+
5
+ For maintainer promises/non-promises and pinning recommendations, see `docs/support-boundaries.md`.
6
+
7
+ Status badge legend:
8
+ - 🟢 **Implemented + public**: implemented and exported on documented package entrypoints.
9
+ - 🟡 **Partial**: implemented API exists, but completeness/coverage is intentionally limited.
10
+ - 🟠 **Implemented + internal**: implemented and exported only on internal/advanced surfaces.
11
+ - 🔵 **Planned**: not shipped as a canonical helper/module.
12
+
13
+ ## Compatibility table (current)
14
+
15
+ | Surface | Status | Current contract |
16
+ |---|---|---|
17
+ | Save format | 🟢 Implemented + public | JSON snapshots stamped with `_ananke_version`/`_schema` via `stampSnapshot` on `@its-not-rocket-science/ananke/schema` (and `./schema-migration` alias). |
18
+ | Replay format | 🟢 Implemented + public | `Replay` JSON via `serializeReplay` / `deserializeReplay` on root entrypoint `@its-not-rocket-science/ananke`. |
19
+ | Schema migration | 🟡 Partial | `migrateWorld` + `registerMigration` are shipped; only explicitly registered migration edges run (no built-in universal chain). |
20
+ | Binary diff | 🟠 Implemented + internal | `diffWorldState` / `packDiff` / `unpackDiff` / `applyDiff` are shipped on `@its-not-rocket-science/ananke/tier3` (advanced/internal surface). |
21
+ | Lockstep messages | 🔵 Planned | No canonical exported lockstep message envelope/type module. Hosts define their own message schema today. |
22
+
23
+ Versioning note: production hosts should pin exact patch versions for schema migration and any non-root protocol helpers.
24
+
25
+ ## 1) Save snapshots (JSON)
26
+
27
+ **Status:** 🟢 Implemented + public
28
+
29
+ Shipped helpers:
30
+ - `stampSnapshot(snapshot, schemaKind)`
31
+ - `validateSnapshot(snapshot)`
32
+ - `detectVersion(snapshot)`
33
+ - `isValidSnapshot(snapshot)`
34
+
35
+ Entrypoints:
36
+ - `@its-not-rocket-science/ananke/schema`
37
+ - `@its-not-rocket-science/ananke/schema-migration` (alias)
38
+
39
+ Example:
40
+
41
+ ```ts
42
+ import { stampSnapshot, validateSnapshot } from "@its-not-rocket-science/ananke/schema";
43
+
44
+ const world = {} as Parameters<typeof stampSnapshot>[0];
45
+ const stamped = stampSnapshot(world, "world");
46
+ const errors = validateSnapshot(stamped);
47
+ if (errors.length > 0) throw new Error("invalid snapshot");
48
+ const json = JSON.stringify(stamped);
49
+ ```
50
+
51
+ Notes:
52
+ - `validateSnapshot` validates core structural fields required by simulation, and permits extra host fields.
53
+ - Canonical schema files are shipped in `schema/world.schema.json` and `schema/replay.schema.json`.
54
+
55
+ ## 2) Replay format
56
+
57
+ **Status:** 🟢 Implemented + public
58
+
59
+ Shipped helpers (root entrypoint):
60
+ - `ReplayRecorder`
61
+ - `replayTo`
62
+ - `serializeReplay`
63
+ - `deserializeReplay`
64
+
65
+ Example:
66
+
67
+ ```ts
68
+ import { ReplayRecorder, serializeReplay, deserializeReplay, replayTo } from "@its-not-rocket-science/ananke";
69
+
70
+ const world = {} as ConstructorParameters<typeof ReplayRecorder>[0];
71
+ const ctx = {} as Parameters<typeof replayTo>[2];
72
+ const recorder = new ReplayRecorder(world);
73
+ // ... record frames while stepping
74
+ const json = serializeReplay(recorder.toReplay());
75
+ const replay = deserializeReplay(json);
76
+ const worldAt100 = replayTo(replay, 100, ctx);
77
+ ```
78
+
79
+ Notes:
80
+ - Replay JSON includes custom Map handling used by serializer/reviver.
81
+ - This is the shipped deterministic replay contract.
82
+
83
+ ## 3) Schema migration
84
+
85
+ **Status:** 🟡 Partial
86
+
87
+ Shipped helpers:
88
+ - `registerMigration(fromVersion, toVersion, fn)`
89
+ - `migrateWorld(snapshot, toVersion?)`
90
+
91
+ Behavior today:
92
+ - If snapshot version equals target version, `migrateWorld` returns unchanged.
93
+ - If no migration is registered for the requested edge, `migrateWorld` throws.
94
+ - Legacy snapshots without `_ananke_version` are treated as `"0.0"`.
95
+
96
+ This is a real API, but migration coverage is only as complete as registered migration paths.
97
+
98
+ ## 4) Binary diff format
99
+
100
+ **Status:** 🟠 Implemented + internal
101
+
102
+ Shipped helpers (Tier 3 surface):
103
+ - `diffWorldState(prev, next)`
104
+ - `packDiff(diff)`
105
+ - `unpackDiff(bytes)`
106
+ - `applyDiff(base, diff)`
107
+
108
+ Entrypoint:
109
+ - `@its-not-rocket-science/ananke/tier3`
110
+
111
+ Scope:
112
+ - Useful for incremental state transport/storage.
113
+ - Not part of Tier-1 root stability contract.
114
+
115
+ ## 5) Lockstep message protocol
116
+
117
+ **Status:** 🔵 Planned
118
+
119
+ Current code provides deterministic primitives (`stepWorld`, replay, and `hashWorldState` in netcode), but **does not** provide a canonical exported lockstep message protocol module with fixed message kinds/envelopes.
120
+
121
+ Roadmap/proposed protocol details have been moved to `docs/planned-protocol-work.md`.
122
+
123
+ ## 6) Helper/path audit (docs code)
124
+
125
+ | Item | Exists in code | Exported | Classification |
126
+ |---|---|---|---|
127
+ | `stampSnapshot` (`src/schema-migration.ts`) | yes | `./schema`, `./schema-migration` | implemented + public |
128
+ | `validateSnapshot` (`src/schema-migration.ts`) | yes | `./schema`, `./schema-migration` | implemented + public |
129
+ | `migrateWorld` (`src/schema-migration.ts`) | yes | `./schema`, `./schema-migration` | partial |
130
+ | `registerMigration` (`src/schema-migration.ts`) | yes | `./schema`, `./schema-migration` | implemented + public |
131
+ | `serializeReplay` (`src/replay.ts`) | yes | root `.` | implemented + public |
132
+ | `deserializeReplay` (`src/replay.ts`) | yes | root `.` | implemented + public |
133
+ | `diffWorldState` (`src/snapshot.ts`) | yes | `./tier3` | implemented + internal |
134
+ | `packDiff` (`src/snapshot.ts`) | yes | `./tier3` | implemented + internal |
135
+ | `unpackDiff` (`src/snapshot.ts`) | yes | `./tier3` | implemented + internal |
136
+ | `applyDiff` (`src/snapshot.ts`) | yes | `./tier3` | implemented + internal |
137
+ | `schema/world.schema.json` | yes | package file artifact | implemented + public artifact |
138
+ | `schema/replay.schema.json` | yes | package file artifact | implemented + public artifact |
139
+ | Canonical lockstep message module | no | n/a | planned |
140
+
@@ -0,0 +1,200 @@
1
+ # World Evolution Backend (Host Integration)
2
+
3
+ ## Purpose
4
+
5
+ `world-evolution-backend` is an additive, deterministic composition layer for host platforms (for example OpenWorldBuilder) that need reproducible world-scale progression runs.
6
+
7
+ It does **not** replace existing systems. It orchestrates existing modules and emits host-facing outputs:
8
+
9
+ - final world snapshot
10
+ - per-step timeline/event log
11
+ - summary metrics
12
+ - optional deltas/checkpoints
13
+
14
+ ## Stable import path
15
+
16
+ Preferred additive subpath export:
17
+
18
+ ```ts
19
+ import {
20
+ runWorldEvolution,
21
+ listAvailableWorldEvolutionProfiles,
22
+ } from "@its-not-rocket-science/ananke/world-evolution-engine";
23
+ ```
24
+
25
+ Compatibility alias (still supported):
26
+
27
+ ```ts
28
+ import { runWorldEvolution } from "@its-not-rocket-science/ananke/world-evolution-backend";
29
+ ```
30
+
31
+ No Tier-1 root exports were changed.
32
+
33
+ ## Host flow
34
+
35
+ 1. Build an initial `WorldEvolutionSnapshot` (canon state).
36
+ 2. Pick a profile (`minimal_world_history`, `polity_dynamics`, `conflict_heavy`, `climate_and_migration`, `full_world_evolution`) and optionally layer deterministic host overrides on top.
37
+ 3. Run `runWorldEvolution({ snapshot, steps, ... })`.
38
+ 4. Read:
39
+ - `finalSnapshot`
40
+ - `timeline`
41
+ - `metrics`
42
+ - optional `deltas`/`checkpoints`
43
+
44
+ ## Canon vs derived timelines
45
+
46
+ - `initialSnapshot` is treated as canonical input.
47
+ - simulation mutations occur on an isolated runtime clone.
48
+ - `finalSnapshot` and timeline artifacts are derived outputs.
49
+
50
+ This keeps lore/canon source state separate from generated timeline projections.
51
+
52
+ ## Composition architecture (additive)
53
+
54
+ The backend composes existing systems in deterministic order per step:
55
+
56
+ 1. **Polity core**: `stepPolityDay` (trade, war, morale/stability baseline)
57
+ 2. **Governance**: `stepGovernanceCooldown`, `stepGovernanceStability`, `computeGovernanceModifiers`
58
+ 3. **Diplomacy**: treaty strength/expiry progression
59
+ 4. **Trade routes**: efficiency step + `applyDailyTrade`
60
+ 5. **Migration**: `resolveMigration` + `applyMigrationFlows`
61
+ 6. **Climate**: deterministic `generateClimateEvent`, aggregate effects, lifecycle stepping
62
+ 7. **Epidemic**: `stepEpidemic` + death pressure application
63
+
64
+ All sequencing is deterministic and order-stable (sorted IDs, no `Math.random`).
65
+
66
+ ## Deterministic profile presets
67
+
68
+ Profiles are explicit additive controls over existing modules and all share the same explicit pipeline order:
69
+
70
+ 1. `polity`
71
+ 2. `governance`
72
+ 3. `diplomacy`
73
+ 4. `trade`
74
+ 5. `migration`
75
+ 6. `climate`
76
+ 7. `epidemic`
77
+
78
+ Preset intent:
79
+
80
+ - `minimal_world_history`: only core polity day stepping.
81
+ - `polity_dynamics`: polity + governance + diplomacy + trade.
82
+ - `conflict_heavy`: polity/governance/diplomacy/trade/migration with no climate/epidemic load.
83
+ - `climate_and_migration`: polity + migration + climate + epidemic pressure.
84
+ - `full_world_evolution`: all currently integrated world-scale subsystems enabled.
85
+
86
+ Legacy IDs (`balanced`, `resilience`, `expansion`) are kept as deterministic aliases to `full_world_evolution` for compatibility.
87
+
88
+ ### Profile matrix (explicit ruleset behavior)
89
+
90
+ | Profile | Polity | Governance | Diplomacy | Trade | Migration | Climate | Epidemic | Intended host use |
91
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
92
+ | `minimal_world_history` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Lowest-cost long-horizon history baselines |
93
+ | `polity_dynamics` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | Political/economic simulation without environmental load |
94
+ | `conflict_heavy` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | War/mobility-driven scenarios with reduced overhead |
95
+ | `climate_and_migration` | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | Environmental stress, displacement, and disease pressure studies |
96
+ | `full_world_evolution` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Full-stack world simulation for maximal systemic richness |
97
+
98
+ All presets preserve the same deterministic subsystem execution order and only enable/disable or tune existing mechanics.
99
+
100
+ ## Host overrides layered on profile
101
+
102
+ Hosts can provide:
103
+
104
+ - `profileId` to select a preset
105
+ - `ruleOverrides` to override specific fields deterministically
106
+
107
+ Overrides are applied additively to the resolved profile; no random behavior is introduced.
108
+
109
+ Example:
110
+
111
+ ```ts
112
+ import {
113
+ resolveWorldEvolutionProfile,
114
+ runWorldEvolution,
115
+ } from "@its-not-rocket-science/ananke/world-evolution-backend";
116
+
117
+ const snapshot = {} as any;
118
+ const baseProfile = resolveWorldEvolutionProfile("polity_dynamics");
119
+
120
+ const result = runWorldEvolution({
121
+ snapshot,
122
+ steps: 90,
123
+ profile: {
124
+ ...baseProfile,
125
+ governanceStabilityDaysPerStep: 2,
126
+ },
127
+ });
128
+ ```
129
+
130
+ The resolved profile remains deterministic for a fixed snapshot/seed + override object.
131
+
132
+ ## Profile comparison example
133
+
134
+ See `examples/world-evolution-profiles-comparison.ts` for a side-by-side run comparing multiple profiles over the same input state and seed.
135
+
136
+ ## Determinism notes
137
+
138
+ - Uses fixed-point arithmetic and existing deterministic world primitives.
139
+ - Iteration order is normalized by IDs before applying updates.
140
+ - same snapshot + same profile + same step count => byte-for-byte equal result object.
141
+
142
+ ## Minimal API
143
+
144
+ - `createWorldEvolutionSnapshot(snapshot)`
145
+ - `runWorldEvolution(request)`
146
+ - `listAvailableWorldEvolutionProfiles()`
147
+ - plus profile helpers/types via the same subpath.
148
+
149
+
150
+
151
+ ## Host timeline/event layer for readable world history
152
+
153
+ For UI-facing history rendering, use the presentation-oriented event stream builder:
154
+
155
+ ```ts
156
+ import {
157
+ runWorldEvolution,
158
+ buildEvolutionTimeline,
159
+ sortTimelineEventsBySignificance,
160
+ } from "@its-not-rocket-science/ananke/world-evolution-backend";
161
+ import type { WorldEvolutionSnapshot } from "@its-not-rocket-science/ananke/world-evolution-backend";
162
+
163
+ declare const snapshot: WorldEvolutionSnapshot;
164
+
165
+ const result = runWorldEvolution({ snapshot, steps: 180, profileId: "full_world_evolution" });
166
+ const timeline = buildEvolutionTimeline(result, { includeSummaryText: true });
167
+ const highlights = sortTimelineEventsBySignificance(timeline).slice(0, 10);
168
+ ```
169
+
170
+ `buildEvolutionTimeline` converts subsystem output into a **stable chronological stream** with:
171
+
172
+ - deterministic `id` and `hash`
173
+ - event categories (`polity`, `migration`, `conflict`, `diplomacy`, `economy`, `disease`, `climate`, `infrastructure`, `governance`, `mythology_culture`)
174
+ - `severity` / `significance` scoring for host ranking
175
+ - optional summary text for direct UI rendering
176
+ - fact references (`factRefs`) pointing back to originating step/subsystem facts
177
+
178
+ ### Raw engine state vs presentation-oriented timeline events
179
+
180
+ - **Raw engine state** (`finalSnapshot`, step metrics, per-step subsystem arrays):
181
+ - canonical simulation outputs
182
+ - optimized for correctness/replay/integrity
183
+ - rich but not directly UI-friendly
184
+
185
+ - **Presentation timeline events** (`buildEvolutionTimeline` output):
186
+ - additive read-only projection layer
187
+ - normalized shape for host feeds, journals, and map overlays
188
+ - stable ordering + identity for caching/diffing in external platforms
189
+ - preserves links back to raw subsystem facts through `factRefs`
190
+
191
+ This separation lets hosts keep simulation truth in snapshots while rendering an approachable “world history” layer for players.
192
+
193
+ ## Session orchestration layer
194
+
195
+ For hosts that need a session-oriented API (`createEvolutionSession`, `stepEvolution`, serialization helpers), use `@its-not-rocket-science/ananke/world-evolution` and see `docs/world-evolution-orchestration.md`.
196
+
197
+
198
+ ## Host canonical schema
199
+
200
+ For external platform integrators, see `docs/host-world-evolution-schema.md` and `schema/world-evolution-input.schema.json`.