@its-not-rocket-science/ananke 0.1.69 → 0.5.4
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 +288 -0
- package/README.md +150 -432
- package/STABLE_API.md +113 -490
- package/conformance/README.md +46 -44
- package/conformance/lockstep-sequence.json +395 -395
- package/conformance/phase-order.json +21 -0
- package/conformance/replay-parity.json +65 -65
- package/conformance/state-hash-regression.json +39 -0
- package/conformance/state-hash.json +23 -22
- package/dist/src/conformance.d.ts +8 -2
- package/dist/src/content/index.d.ts +3 -0
- package/dist/src/content/index.js +2 -0
- package/dist/src/content/injector.d.ts +19 -0
- package/dist/src/content/injector.js +61 -0
- package/dist/src/content/loader.d.ts +3 -0
- package/dist/src/content/loader.js +170 -0
- package/dist/src/content/types.d.ts +56 -0
- package/dist/src/content/types.js +1 -0
- package/dist/src/content/validator.d.ts +6 -0
- package/dist/src/content/validator.js +14 -0
- package/dist/src/content-pack.d.ts +3 -2
- package/dist/src/content-pack.js +205 -10
- package/dist/src/data-governance.d.ts +73 -0
- package/dist/src/data-governance.js +188 -0
- package/dist/src/debug.d.ts +6 -0
- package/dist/src/debug.js +9 -0
- package/dist/src/determinism.d.ts +12 -0
- package/dist/src/determinism.js +93 -0
- package/dist/src/dialogue.d.ts +41 -0
- package/dist/src/dialogue.js +113 -2
- package/dist/src/economy.d.ts +97 -3
- package/dist/src/economy.js +191 -37
- package/dist/src/history/autosave.d.ts +19 -0
- package/dist/src/history/autosave.js +98 -0
- package/dist/src/history/timetravel.d.ts +28 -0
- package/dist/src/history/timetravel.js +61 -0
- package/dist/src/index.d.ts +19 -31
- package/dist/src/index.js +20 -38
- package/dist/src/model3d.js +63 -2
- package/dist/src/narrative/combat-logger.d.ts +25 -0
- package/dist/src/narrative/combat-logger.js +97 -0
- package/dist/src/narrative/plausibility.d.ts +30 -0
- package/dist/src/narrative/plausibility.js +70 -0
- package/dist/src/narrative-layer.d.ts +2 -0
- package/dist/src/narrative-layer.js +2 -0
- package/dist/src/navigation/causal-chain.d.ts +26 -0
- package/dist/src/navigation/causal-chain.js +74 -0
- package/dist/src/netcode.d.ts +1 -1
- package/dist/src/netcode.js +1 -1
- package/dist/src/performance/adaptive-tick.d.ts +23 -0
- package/dist/src/performance/adaptive-tick.js +50 -0
- package/dist/src/plugins/loader.d.ts +23 -0
- package/dist/src/plugins/loader.js +86 -0
- package/dist/src/plugins/registry.d.ts +11 -0
- package/dist/src/plugins/registry.js +28 -0
- package/dist/src/plugins/types.d.ts +31 -0
- package/dist/src/plugins/types.js +7 -0
- package/dist/src/replay.js +5 -2
- package/dist/src/scenario.js +2 -1
- package/dist/src/serialization/binary.d.ts +5 -0
- package/dist/src/serialization/binary.js +193 -0
- package/dist/src/session.d.ts +115 -0
- package/dist/src/session.js +228 -0
- package/dist/src/sim/ai/decide.js +1 -1
- package/dist/src/sim/ai/personality.js +2 -2
- package/dist/src/sim/bodyplan.d.ts +30 -0
- package/dist/src/sim/context.d.ts +2 -0
- package/dist/src/sim/kernel.js +80 -1294
- package/dist/src/sim/normalization.d.ts +12 -0
- package/dist/src/sim/normalization.js +92 -0
- package/dist/src/sim/resolvers/attack-resolver.d.ts +30 -0
- package/dist/src/sim/resolvers/attack-resolver.js +310 -0
- package/dist/src/sim/resolvers/capability-resolver.d.ts +15 -0
- package/dist/src/sim/resolvers/capability-resolver.js +270 -0
- package/dist/src/sim/resolvers/grapple-resolver.d.ts +28 -0
- package/dist/src/sim/resolvers/grapple-resolver.js +70 -0
- package/dist/src/sim/resolvers/shoot-resolver.d.ts +27 -0
- package/dist/src/sim/resolvers/shoot-resolver.js +212 -0
- package/dist/src/sim/resolvers/treat-resolver.d.ts +16 -0
- package/dist/src/sim/resolvers/treat-resolver.js +77 -0
- package/dist/src/sim/step/apply/intents.d.ts +6 -0
- package/dist/src/sim/step/apply/intents.js +83 -0
- package/dist/src/sim/step/kernel-pipeline.d.ts +9 -0
- package/dist/src/sim/step/kernel-pipeline.js +15 -0
- package/dist/src/sim/step/phases/capability-phase.d.ts +8 -0
- package/dist/src/sim/step/phases/capability-phase.js +65 -0
- package/dist/src/sim/step/phases/cooldowns-phase.d.ts +3 -0
- package/dist/src/sim/step/phases/cooldowns-phase.js +37 -0
- package/dist/src/sim/step/phases/prepare-phase.d.ts +10 -0
- package/dist/src/sim/step/phases/prepare-phase.js +33 -0
- package/dist/src/sim/step/pipeline.d.ts +2 -0
- package/dist/src/sim/step/pipeline.js +15 -0
- package/dist/src/sim/step/resolvers/impact-resolver.d.ts +9 -0
- package/dist/src/sim/step/resolvers/impact-resolver.js +55 -0
- package/dist/src/sim/step/world-phases.d.ts +7 -0
- package/dist/src/sim/step/world-phases.js +15 -0
- package/dist/src/sim/step/world-step-context.d.ts +23 -0
- package/dist/src/sim/step/world-step-context.js +1 -0
- package/dist/src/sim/systemic-toxicology.js +1 -1
- package/dist/src/sim/team.js +2 -2
- package/dist/src/sim/testing.js +4 -3
- package/dist/src/sim/toxicology.d.ts +1 -1
- package/dist/src/sim/toxicology.js +1 -1
- package/dist/src/sim/world.d.ts +17 -24
- package/dist/src/snapshot.js +3 -6
- package/dist/src/tier2.d.ts +19 -0
- package/dist/src/tier2.js +21 -0
- package/dist/src/tier3.d.ts +10 -0
- package/dist/src/tier3.js +12 -0
- package/dist/src/version.d.ts +6 -0
- package/dist/src/version.js +8 -0
- package/dist/src/wasm/bridge.d.ts +14 -0
- package/dist/src/wasm/bridge.js +84 -0
- package/dist/src/wasm-kernel.d.ts +2 -0
- package/dist/src/wasm-kernel.js +1 -0
- package/dist/src/world-evolution-backend/engine.d.ts +4 -0
- package/dist/src/world-evolution-backend/engine.js +347 -0
- package/dist/src/world-evolution-backend/host-schema.d.ts +145 -0
- package/dist/src/world-evolution-backend/host-schema.js +375 -0
- package/dist/src/world-evolution-backend/index.d.ts +1 -0
- package/dist/src/world-evolution-backend/index.js +1 -0
- package/dist/src/world-evolution-backend/open-world-host-adapter.d.ts +96 -0
- package/dist/src/world-evolution-backend/open-world-host-adapter.js +208 -0
- package/dist/src/world-evolution-backend/profiles.d.ts +4 -0
- package/dist/src/world-evolution-backend/profiles.js +136 -0
- package/dist/src/world-evolution-backend/public.d.ts +11 -0
- package/dist/src/world-evolution-backend/public.js +7 -0
- package/dist/src/world-evolution-backend/reproducibility.d.ts +8 -0
- package/dist/src/world-evolution-backend/reproducibility.js +35 -0
- package/dist/src/world-evolution-backend/timeline.d.ts +6 -0
- package/dist/src/world-evolution-backend/timeline.js +337 -0
- package/dist/src/world-evolution-backend/types.d.ts +138 -0
- package/dist/src/world-evolution-backend/types.js +1 -0
- package/dist/src/world-evolution-host-backend.d.ts +51 -0
- package/dist/src/world-evolution-host-backend.js +97 -0
- package/dist/src/world-evolution.d.ts +201 -0
- package/dist/src/world-evolution.js +543 -0
- package/dist/src/world-factory.js +2 -1
- package/dist/tools/pack-cli.js +55 -2
- package/docs/bridge-contract.md +140 -332
- package/docs/completion-evidence-dashboard.md +63 -0
- package/docs/host-contract.md +86 -310
- package/docs/host-world-evolution-schema.md +155 -0
- package/docs/integration-primer.md +29 -22
- package/docs/migration-monolith-to-modular.md +102 -120
- package/docs/package-architecture.md +116 -323
- package/docs/plugins/README.md +42 -0
- package/docs/plugins/hooks.md +22 -0
- package/docs/plugins/publishing.md +29 -0
- package/docs/plugins/security-model.md +273 -0
- package/docs/project-overview.md +30 -30
- package/docs/release-dashboard.md +47 -0
- package/docs/session-api.md +127 -0
- package/docs/stable-api-manifest.json +52 -0
- package/docs/trust-dashboard.md +37 -0
- package/docs/versioning.md +256 -245
- package/docs/wire-protocol.md +140 -209
- package/docs/world-evolution-backend.md +200 -0
- package/docs/world-evolution-engine.md +150 -0
- package/docs/world-evolution-host-backend.md +70 -0
- package/docs/world-evolution-orchestration.md +248 -0
- package/package.json +125 -6
- package/schema/content-pack.schema.json +85 -0
- package/schema/plugin.schema.json +57 -0
- package/schema/world-evolution-input.schema.json +196 -0
package/docs/host-contract.md
CHANGED
|
@@ -1,310 +1,86 @@
|
|
|
1
|
-
# Ananke — Host Integration Contract
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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`
|