@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,310 +1,86 @@
1
- # Ananke — Host Integration Contract
2
-
3
- *Platform Hardening PH-3 Minimal Host Integration Contract*
4
-
5
- > **Scope:** This document covers only **Tier 1 (Stable)** exports. Every symbol listed
6
- > here will not change in a breaking way without a major semver bump and a migration guide.
7
- > See [`STABLE_API.md`](../STABLE_API.md) and [`docs/versioning.md`](versioning.md) for
8
- > the full tier table and stability guarantees.
9
- >
10
- > An engineer can embed Ananke in a host process using only this document and the three
11
- > [quickstart examples](#quickstart-examples) below — no `src/` reading required.
12
-
13
- ---
14
-
15
- ## 1 · World creation
16
-
17
- ```typescript
18
- import { mkWorld } from "ananke"; // src/sim/testing — Tier 3 helper, quickstart-safe
19
-
20
- const world = mkWorld(seed, entities);
21
- ```
22
-
23
- | Parameter | Type | Description |
24
- |-----------|------|-------------|
25
- | `seed` | `number` | World RNG seed. Same seed + same commands → identical output forever |
26
- | `entities` | `Entity[]` | Initial entity list. IDs must be unique; `mkWorld` throws on duplicates |
27
-
28
- `mkWorld` returns a `WorldState` with `tick: 0`. Entities are sorted by `id` ascending.
29
-
30
- **`WorldState` shape (stable fields):**
31
-
32
- ```typescript
33
- interface WorldState {
34
- tick: number; // current tick; incremented by stepWorld
35
- seed: number; // RNG seed passed at creation
36
- entities: Entity[]; // all live and dead entities; do not splice manually
37
- }
38
- ```
39
-
40
- > **Note on `createWorld`:** A `createWorld()` function will replace `mkWorld` when the
41
- > companion ecosystem ships (CE-2). Until then, use `mkWorld(seed, entities)`.
42
-
43
- ---
44
-
45
- ## 2 · Command injection (input protocol)
46
-
47
- Commands tell entities what to do next tick. They are **consumed and cleared** by
48
- `stepWorld`; you rebuild the map every tick.
49
-
50
- ```typescript
51
- import type { CommandMap, Command } from "ananke";
52
-
53
- // One entity can have multiple commands in priority order.
54
- const cmds: CommandMap = new Map<number, readonly Command[]>();
55
-
56
- cmds.set(entityId, [
57
- { kind: "attack", targetId: 2, weaponSlot: "mainHand" },
58
- ]);
59
-
60
- stepWorld(world, cmds, ctx);
61
- ```
62
-
63
- **Common command kinds** (all Tier 1):
64
-
65
- | `kind` | Required fields | Effect |
66
- |--------|----------------|--------|
67
- | `"move"` | `direction: Vec3, speed_mps: I32` | Move entity in direction at speed |
68
- | `"attack"` | `targetId: number, weaponSlot: string` | Initiate a weapon attack |
69
- | `"defend"` | `style: "block"\|"parry"\|"dodge"` | Adopt a defensive posture |
70
- | `"grapple"` | `targetId: number, mode: GrappleMode` | Initiate or advance a grapple |
71
- | `"treat"` | `targetId: number` | Apply first aid to a target |
72
- | `"set_prone"` | `prone: boolean` | Go prone or stand up |
73
-
74
- Entities without a command in the map are idle (continue any ongoing action or stand still).
75
-
76
- ---
77
-
78
- ## 3 · `stepWorld` — call contract
79
-
80
- ```typescript
81
- import { stepWorld } from "ananke";
82
- import type { KernelContext } from "ananke";
83
-
84
- const ctx: KernelContext = {
85
- tractionCoeff: q(0.80), // ground friction, typically q(0.75)–q(1.0)
86
- };
87
-
88
- stepWorld(world, cmds, ctx); // mutates world in place, returns void
89
- ```
90
-
91
- **Contract:**
92
-
93
- | Property | Value |
94
- |----------|-------|
95
- | Return value | `void` — world is mutated in place |
96
- | `world.tick` after call | incremented by 1 |
97
- | Determinism | `stepWorld(clone(world), cmds, ctx)` ≡ `stepWorld(world, cmds, ctx)` for identical inputs |
98
- | Thread safety | Not thread-safe. Call from one thread; snapshot with `structuredClone` for parallel reads |
99
- | `cmds` after call | Map entries are not modified; safe to reuse or discard |
100
- | `ctx` after call | `ctx.tractionCoeff` may be modified if `ctx.weather` applies weather modifiers |
101
-
102
- **`KernelContext` required fields:**
103
-
104
- | Field | Type | Notes |
105
- |-------|------|-------|
106
- | `tractionCoeff` | `Q` | Ground friction for movement. Use `q(0.80)` as a safe default |
107
-
108
- **`KernelContext` optional fields (all Tier 2 or Tier 3):**
109
-
110
- | Field | Effect when provided |
111
- |-------|---------------------|
112
- | `tuning` | Override default tuning constants (tactical / campaign / downtime preset) |
113
- | `sensoryEnv` | Ambient lighting, visibility range — defaults to full daylight |
114
- | `weather` | Applies rain/snow/wind modifiers to traction and senses |
115
- | `terrainGrid` | Per-cell traction lookup by entity position |
116
- | `obstacleGrid` | Impassable and partial-cover cells |
117
- | `elevationGrid` | Height above ground — affects reach and projectile range |
118
- | `ambientTemperature_Q` | Drives thermoregulation (heat/cold stress) |
119
- | `techCtx` | Technology era gate for era-appropriate item validation |
120
- | `trace` | Attach a `TraceSink` to receive per-tick debug events |
121
-
122
- ---
123
-
124
- ## 4 · Replay and serialization
125
-
126
- Replays work because `stepWorld` is a **pure function** of `(WorldState, CommandMap, KernelContext)`.
127
- Recording snapshots the initial world and logs commands; replaying re-applies them in order.
128
-
129
- ### Recording
130
-
131
- ```typescript
132
- import { ReplayRecorder } from "ananke/replay"; // src/replay.ts (Tier 2)
133
-
134
- const recorder = new ReplayRecorder(world); // deep-clones world at tick 0
135
-
136
- for (let i = 0; i < N; i++) {
137
- const cmds = buildCommands(world);
138
- recorder.record(world.tick, cmds); // log before step
139
- stepWorld(world, cmds, ctx);
140
- }
141
-
142
- const replay = recorder.toReplay(); // { initialState, frames }
143
- ```
144
-
145
- ### Serialization
146
-
147
- ```typescript
148
- import { serializeReplay, deserializeReplay } from "ananke/replay";
149
-
150
- const json = serializeReplay(replay); // → string (JSON)
151
- const replay2 = deserializeReplay(json); // → Replay
152
- ```
153
-
154
- `serializeReplay` / `deserializeReplay` round-trip is a **Tier 1 contract**: a serialized
155
- replay from version `0.x.y` must deserialize and replay identically on version `0.x.z` (same
156
- minor, higher patch).
157
-
158
- ### Replaying to a target tick
159
-
160
- ```typescript
161
- import { replayTo } from "ananke/replay";
162
-
163
- const worldAtTick50 = replayTo(replay, 50, ctx);
164
- // Returns a fresh WorldState cloned from the replay; does not mutate replay.
165
- ```
166
-
167
- ---
168
-
169
- ## 5 · Bridge data extraction (3D renderer integration)
170
-
171
- The bridge layer converts simulation state into renderer-friendly types. Extract each tick
172
- after `stepWorld` and pass to `BridgeEngine.update()`.
173
-
174
- ```typescript
175
- import { extractRigSnapshots, deriveAnimationHints } from "ananke"; // src/model3d.ts (Tier 2)
176
- import { BridgeEngine } from "ananke"; // src/bridge/index.ts (Tier 2)
177
-
178
- // --- simulation side ---
179
- const snapshots = extractRigSnapshots(world); // RigSnapshot[] — one per entity
180
- stepWorld(world, cmds, ctx);
181
-
182
- // --- renderer side (may run at higher frame rate) ---
183
- engine.update(snapshots, motionVectors);
184
- const state = engine.getInterpolatedState(entityId, renderTime_s);
185
- // state: { position_m, velocity_mps, facing, animation, poseModifiers, … }
186
- ```
187
-
188
- **Key types:**
189
-
190
- | Type | Source | Description |
191
- |------|--------|-------------|
192
- | `RigSnapshot` | `extractRigSnapshots(world)[i]` | Per-entity rig data at one tick |
193
- | `AnimationHints` | `deriveAnimationHints(entity)` | State flags: `isMoving`, `isGrappling`, `shockQ`, etc. |
194
- | `PoseModifier[]` | `derivePoseModifiers(entity)` | Per-segment injury weight for bone deformation |
195
- | `GrapplePoseConstraint` | `deriveGrappleConstraint(entity)` | IK constraint for grappling pairs |
196
-
197
- See [`docs/bridge-contract.md`](bridge-api.md) for the full double-buffer protocol and
198
- interpolation/extrapolation semantics.
199
-
200
- ---
201
-
202
- ## 6 · Quickstart-safe helpers
203
-
204
- These Tier 3 functions are **safe to use in quickstarts** despite being officially internal.
205
- They are small, stable in practice, and documented here to avoid source-diving.
206
-
207
- | Helper | Signature | Notes |
208
- |--------|-----------|-------|
209
- | `mkWorld(seed, entities)` | `(number, Entity[]) → WorldState` | Create a world from entity array |
210
- | `mkHumanoidEntity(id, attrs?)` | `(number, Partial<IndividualAttributes>?) → Entity` | Build a humanoid entity with defaults |
211
- | `generateIndividual(seed, archetype)` | `(number, Archetype) → IndividualAttributes` | Stat-rolled entity attributes (Tier 1) |
212
-
213
- ---
214
-
215
- ## Quickstart examples
216
-
217
- ### Minimal 1v1 duel loop
218
-
219
- ```typescript
220
- import { mkWorld, mkHumanoidEntity, stepWorld, q } from "ananke";
221
- import type { CommandMap } from "ananke";
222
-
223
- const a = mkHumanoidEntity(1);
224
- const b = mkHumanoidEntity(2);
225
- const world = mkWorld(42, [a, b]);
226
-
227
- const ctx = { tractionCoeff: q(0.80) };
228
-
229
- for (let tick = 0; tick < 200 && !a.dead && !b.dead; tick++) {
230
- const cmds: CommandMap = new Map([
231
- [1, [{ kind: "attack", targetId: 2, weaponSlot: "mainHand" }]],
232
- [2, [{ kind: "attack", targetId: 1, weaponSlot: "mainHand" }]],
233
- ]);
234
- stepWorld(world, cmds, ctx);
235
- }
236
-
237
- console.log(`a.dead=${a.dead} b.dead=${b.dead} ticks=${world.tick}`);
238
- ```
239
-
240
- ### Record and replay
241
-
242
- ```typescript
243
- import { mkWorld, mkHumanoidEntity, stepWorld, q } from "ananke";
244
- import { ReplayRecorder, serializeReplay, deserializeReplay, replayTo } from "ananke/replay";
245
-
246
- const world = mkWorld(99, [mkHumanoidEntity(1), mkHumanoidEntity(2)]);
247
- const ctx = { tractionCoeff: q(0.80) };
248
- const rec = new ReplayRecorder(world);
249
-
250
- for (let tick = 0; tick < 50; tick++) {
251
- const cmds = new Map([[1, [{ kind: "attack", targetId: 2, weaponSlot: "mainHand" }]]]);
252
- rec.record(world.tick, cmds);
253
- stepWorld(world, cmds, ctx);
254
- }
255
-
256
- const json = serializeReplay(rec.toReplay());
257
- const replay2 = deserializeReplay(json);
258
- const world50 = replayTo(replay2, 50, ctx);
259
-
260
- console.log("replay deterministic:", world50.entities[0]!.shock === world.entities[0]!.shock);
261
- ```
262
-
263
- ### 3D renderer integration (bridge)
264
-
265
- ```typescript
266
- import { mkWorld, mkHumanoidEntity, stepWorld, q } from "ananke";
267
- import { extractRigSnapshots } from "ananke";
268
- import { BridgeEngine } from "ananke";
269
-
270
- const world = mkWorld(1, [mkHumanoidEntity(1), mkHumanoidEntity(2)]);
271
- const engine = new BridgeEngine({ mappings: [], defaultBoneName: "root" });
272
- const ctx = { tractionCoeff: q(0.80) };
273
-
274
- engine.setEntityBodyPlan(1, "humanoid");
275
- engine.setEntityBodyPlan(2, "humanoid");
276
-
277
- function gameLoop(renderTime_s: number) {
278
- const snaps = extractRigSnapshots(world);
279
- stepWorld(world, new Map(), ctx);
280
- engine.update(snaps);
281
-
282
- // Renderer queries at any sub-tick time
283
- const state = engine.getInterpolatedState(1, renderTime_s);
284
- if (state) {
285
- // state.position_m, state.facing, state.animation, state.poseModifiers…
286
- }
287
- }
288
- ```
289
-
290
- ---
291
-
292
- ## Error handling
293
-
294
- `stepWorld` does not throw under normal operation. Errors you may encounter:
295
-
296
- | Situation | Error | Fix |
297
- |-----------|-------|-----|
298
- | Duplicate entity IDs passed to `mkWorld` | `Error: mkWorld: duplicate entity IDs` | Ensure each entity has a unique `id` |
299
- | `deserializeReplay(json)` called with malformed JSON | `SyntaxError` | Validate JSON before deserializing |
300
- | `replayTo(replay, tick, ctx)` with `tick > replay.frames.length` | Returns world at final recorded tick | Check `replay.frames.length` before calling |
301
-
302
- ---
303
-
304
- ## What this document does NOT cover
305
-
306
- - **Subsystem APIs** (disease, sleep, aging, mount, hazard) — see [`STABLE_API.md`](../STABLE_API.md) §Tier 2
307
- - **Bridge internals** — see [`docs/bridge-api.md`](bridge-api.md)
308
- - **Validation and calibration** — see [`tools/validation.ts`](../tools/validation.ts)
309
- - **Downtime and campaign simulation** — see [`src/downtime.ts`](../src/downtime.ts)
310
- - **Quest, settlement, narrative subsystems** — see [`STABLE_API.md`](../STABLE_API.md) §Tier 2
1
+ # Ananke — Host Integration Contract
2
+
3
+ This document covers **Tier 1 only**.
4
+
5
+ - Stable path: `@its-not-rocket-science/ananke`
6
+ - Tier 1 symbol manifest: `docs/stable-api-manifest.json`
7
+ - Stability table: `STABLE_API.md`
8
+
9
+ Anything not in Tier 1 must be imported from explicit Tier 2 / Tier 3 subpaths.
10
+
11
+ ---
12
+
13
+ ## 1) World creation
14
+
15
+ ```ts pseudocode
16
+ import { createWorld } from "@its-not-rocket-science/ananke";
17
+ import type { EntitySpec } from "@its-not-rocket-science/ananke";
18
+
19
+ const specs: EntitySpec[] = [
20
+ { id: 1, teamId: 1, seed: 11, archetype: "AMATEUR_BOXER", weaponId: "fists" },
21
+ { id: 2, teamId: 2, seed: 22, archetype: "AMATEUR_BOXER", weaponId: "fists" },
22
+ ];
23
+
24
+ const world = createWorld(42, specs);
25
+ ```
26
+
27
+ ## 2) Scenario loading
28
+
29
+ ```ts no-check-example
30
+ import { loadScenario, validateScenario } from "@its-not-rocket-science/ananke";
31
+
32
+ const errors = validateScenario(jsonScenario);
33
+ if (errors.length) throw new Error(errors.join("\n"));
34
+
35
+ const world = loadScenario(jsonScenario);
36
+ ```
37
+
38
+ ## 3) Step contract
39
+
40
+ ```ts no-check-example
41
+ import { stepWorld, q } from "@its-not-rocket-science/ananke";
42
+ import type { CommandMap, KernelContext } from "@its-not-rocket-science/ananke";
43
+
44
+ const cmds: CommandMap = new Map();
45
+ const ctx: KernelContext = { tractionCoeff: q(0.80) };
46
+
47
+ stepWorld(world, cmds, ctx); // mutates world, increments world.tick by 1
48
+ ```
49
+
50
+ ## 4) Replay / serialization
51
+
52
+ ```ts no-check-example
53
+ import {
54
+ ReplayRecorder,
55
+ replayTo,
56
+ serializeReplay,
57
+ deserializeReplay,
58
+ } from "@its-not-rocket-science/ananke";
59
+
60
+ const rec = new ReplayRecorder(world);
61
+ rec.record(world.tick, cmds);
62
+ stepWorld(world, cmds, ctx);
63
+
64
+ const replay = rec.toReplay();
65
+ const json = serializeReplay(replay);
66
+ const replay2 = deserializeReplay(json);
67
+ const worldAtTick = replayTo(replay2, 1, ctx);
68
+ ```
69
+
70
+ ## 5) Bridge extraction
71
+
72
+ ```ts no-check-example
73
+ import { extractRigSnapshots, deriveAnimationHints } from "@its-not-rocket-science/ananke";
74
+
75
+ const snapshots = extractRigSnapshots(world);
76
+ const hints = deriveAnimationHints(world.entities[0]!);
77
+ ```
78
+
79
+ ## 6) Non-tier-1 imports
80
+
81
+ Use explicit subpaths for non-stable APIs:
82
+
83
+ ```ts pseudocode
84
+ import { BridgeEngine } from "@its-not-rocket-science/ananke/tier2";
85
+ import { resolveTacticalEngagement } from "@its-not-rocket-science/ananke/tier3";
86
+ ```
@@ -0,0 +1,155 @@
1
+ # Host World Evolution Schema
2
+
3
+ ## Purpose
4
+
5
+ `WorldEvolutionInput` is a host-facing canonical schema for external worldbuilding platforms that want to use Ananke as a deterministic world-evolution backend without adopting internal Ananke state structures directly.
6
+
7
+ The schema is additive and adapter-based:
8
+
9
+ - Host systems own canonical source data (`entities`, `relationships`, `resources`, `hostMetadata`).
10
+ - Ananke owns derived simulation timeline state (`finalSnapshot`, metrics, timeline, checkpoints).
11
+
12
+ ## Stable import
13
+
14
+ ```ts
15
+ import {
16
+ normalizeHostWorldInput,
17
+ validateWorldEvolutionInput,
18
+ toAnankeEvolutionState,
19
+ fromAnankeEvolutionState,
20
+ toWorldEvolutionRunRequest,
21
+ fromWorldEvolutionRunResult,
22
+ type WorldEvolutionInput,
23
+ } from "@its-not-rocket-science/ananke/world-evolution-backend";
24
+ ```
25
+
26
+ ## Canonical host model
27
+
28
+ `WorldEvolutionInput` contains:
29
+
30
+ - `entities`: `HostPolity | HostSettlement | HostRegion`
31
+ - `relationships`: `border | trade_route | treaty | war`
32
+ - `resources`: `HostResourceNode`
33
+ - optional epidemiology payloads: `diseases`, `epidemics`
34
+ - optional `profileId` (preset evolution ruleset selector)
35
+ - optional `ruleOverrides`
36
+ - optional `hostMetadata` (opaque pass-through)
37
+ - optional `simulationState` (seed/restore simulation-owned derived data)
38
+
39
+ ## Validation and normalization
40
+
41
+ - `validateWorldEvolutionInput(input)` returns deterministic, path-addressed validation errors.
42
+ - `normalizeHostWorldInput(input)` validates first, then returns a sorted, deterministic normalized shape.
43
+
44
+ Normalization guarantees stable ordering across:
45
+
46
+ - entities
47
+ - relationships
48
+ - resources
49
+ - diseases
50
+ - epidemics
51
+
52
+ ## Adapter flow
53
+
54
+ ### Host → Ananke
55
+
56
+ 1. Validate + normalize host data.
57
+ 2. Map host entities and relationships into `WorldEvolutionSnapshot` via `toAnankeEvolutionState`.
58
+ 3. Run backend evolution.
59
+
60
+ ```ts
61
+ import {
62
+ runWorldEvolution,
63
+ toWorldEvolutionRunRequest,
64
+ type WorldEvolutionInput,
65
+ } from "@its-not-rocket-science/ananke/world-evolution-backend";
66
+
67
+ declare const hostInput: WorldEvolutionInput;
68
+
69
+ const req = toWorldEvolutionRunRequest(hostInput, 30, {
70
+ includeDeltas: true,
71
+ checkpointInterval: 5,
72
+ });
73
+ const result = runWorldEvolution(req);
74
+ ```
75
+
76
+ ### Ananke → Host
77
+
78
+ Use `fromAnankeEvolutionState(snapshot, context)` or `fromWorldEvolutionRunResult(result, context)`
79
+ for host-friendly payloads.
80
+
81
+ This preserves host metadata and resource nodes through `HostAdapterContext`.
82
+
83
+ ## Mapping notes
84
+
85
+ - Settlement/region populations can roll up into polity population when polity population is omitted.
86
+ - Borders map to polity pairs.
87
+ - `trade_route` maps to trade routes.
88
+ - `treaty` maps to diplomacy treaties.
89
+ - `war` maps to active wars.
90
+ - `profileId` selects a deterministic preset profile.
91
+ - `ruleOverrides` layer on top of the selected profile (or default `full_world_evolution`) for deterministic host control.
92
+
93
+ ## JSON Schema
94
+
95
+ Machine-readable schema document:
96
+
97
+ - `schema/world-evolution-input.schema.json`
98
+
99
+ Use this to validate payloads at host ingress.
100
+
101
+ ## Open world host adapter (OpenWorldBuilder-style)
102
+
103
+ For procedural/open-world platforms that produce generated world graphs (regions, settlements, factions, resources, trade links, climate and lore), use the additive adapter API:
104
+
105
+ ```ts
106
+ import {
107
+ canonicalizeOpenWorldInput,
108
+ mapOpenWorldHostToEvolutionInput,
109
+ toAnankeEvolutionStateFromOpenWorld,
110
+ type OpenWorldHostInput,
111
+ } from "@its-not-rocket-science/ananke/world-evolution-backend";
112
+ ```
113
+
114
+ ### Field classes
115
+
116
+ The adapter explicitly separates three classes of host data in metadata buckets:
117
+
118
+ - `simulation`: fields intended to influence simulation mapping.
119
+ - `descriptive`: UI/lore-facing fields that may enrich outputs but do not directly drive core evolution.
120
+ - `opaque`: host-owned passthrough fields that Ananke preserves without interpreting.
121
+
122
+ Unknown host fields should be placed in `metadata.opaque` (entity-level) or top-level `metadata.opaque`.
123
+
124
+ ### Minimal required fields
125
+
126
+ A minimal viable OpenWorld host payload requires:
127
+
128
+ - `worldSeed`.
129
+ - At least one `faction` with `id` + `name`.
130
+ - One or more regions/settlements only if you want territorial rollups.
131
+
132
+ This is enough to run deterministic evolution with default values for omitted optional fields.
133
+
134
+ ### Optional fields that improve fidelity
135
+
136
+ Simulation fidelity improves when you provide:
137
+
138
+ - explicit polity-level `population`, `treasury_cu`, `stabilityQ`, `moraleQ`.
139
+ - `tradeLinks` with `baseVolume_cu`, `routeQualityQ`, and optional treaty/war flags.
140
+ - region and settlement populations for roll-up inference.
141
+ - resource nodes with `stock` and ownership anchoring (`factionId`, `regionId`, `settlementId`).
142
+ - climate payload in `environment.climateByPolity` for direct continuity into simulation state.
143
+
144
+ ### Deterministic import ordering and canonicalization
145
+
146
+ Use `canonicalizeOpenWorldInput` prior to persistence or hashing. It sorts entities, links, tags, and metadata keys to guarantee deterministic import order regardless of host emission order.
147
+
148
+ ### Example fixtures and outputs
149
+
150
+ Sample OpenWorldBuilder-style assets are included under:
151
+
152
+ - `fixtures/world-evolution-open-worldbuilder/openworld-host-input.sample.json`
153
+ - `fixtures/world-evolution-open-worldbuilder/openworld-host-evolution-run.sample.json`
154
+ - `fixtures/world-evolution-open-worldbuilder/openworld-host-timeline.sample.json`
155
+ - `fixtures/world-evolution-open-worldbuilder/openworld-host-metrics.sample.json`