@its-not-rocket-science/ananke 0.1.69 → 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 +254 -0
  2. package/README.md +150 -432
  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,332 +1,140 @@
1
- # Ananke Bridge Contract
2
-
3
- *Platform Hardening PH-5 Bridge as First-Class Supported Surface*
4
-
5
- > **Stability:** All symbols in this document are **Tier 1 (Stable)**.
6
- > They will not change in a breaking way without a major semver bump and a migration guide.
7
- > See [`docs/versioning.md`](versioning.md) and [`STABLE_API.md`](../STABLE_API.md) for the full
8
- > stability policy.
9
- >
10
- > For the tutorial-oriented integration guide (step-by-step setup, mapping configuration,
11
- > performance tips) see [`docs/bridge-api.md`](bridge-api.md).
12
-
13
- ---
14
-
15
- ## Purpose
16
-
17
- This document is the authoritative **integration contract** for the Ananke renderer bridge.
18
- A renderer developer can implement a correct, forwards-compatible bridge consumer using only
19
- this document and the [quickstart example](#quickstart-example) below no source reading required.
20
-
21
- ---
22
-
23
- ## 1. Overview: Double-Buffer Protocol
24
-
25
- The bridge operates as a **double-buffered producer-consumer**:
26
-
27
- | Role | Caller | Rate | Entry point |
28
- |------|--------|------|-------------|
29
- | **Write side** (simulation) | host simulation thread | 20 Hz | `BridgeEngine.update(snapshots)` |
30
- | **Read side** (renderer) | host render thread | 60 Hz+ | `BridgeEngine.getInterpolatedState(id, t)` |
31
-
32
- **Write-side contract:**
33
-
34
- 1. After each `stepWorld(world, cmds, ctx)` call, extract the current state:
35
- ```typescript
36
- const snapshots = extractRigSnapshots(world); // RigSnapshot[] — one per entity
37
- ```
38
- 2. Call `engine.update(snapshots)` exactly once per simulation tick.
39
- 3. The bridge stores the two most recent snapshots per entity (previous and current).
40
- On the next `update`, `curr` becomes `prev` and the new snapshot becomes `curr`.
41
-
42
- **Read-side contract:**
43
-
44
- 1. Call `engine.getInterpolatedState(entityId, renderTime_s)` once per entity per render frame.
45
- 2. `renderTime_s` is a monotonic real-time clock in seconds (e.g., `performance.now() / 1000`).
46
- 3. The bridge returns `null` if no snapshots exist for the entity yet; always null-check the result.
47
- 4. The returned `InterpolatedState` is a **snapshot** — do not hold references between frames.
48
-
49
- ---
50
-
51
- ## 2. Interpolation and Extrapolation Semantics
52
-
53
- The interpolation factor **t** is computed from `renderTime_s` relative to the two stored
54
- simulation timestamps (`prevTime_s`, `currTime_s`):
55
-
56
- | Condition | Behaviour | t value |
57
- |-----------|-----------|---------|
58
- | `renderTime < prevTime` | Hold previous snapshot | `0` |
59
- | `prevTime renderTime ≤ currTime` | Normal linear interpolation | `(renderTime - prevTime) / (currTime - prevTime)` |
60
- | `renderTime > currTime`, `extrapolationAllowed: false` | Hold current snapshot | `SCALE.Q` |
61
- | `renderTime > currTime`, `extrapolationAllowed: true` | Velocity-based extrapolation | `> SCALE.Q` |
62
-
63
- **Determinism guarantee:** For a given simulation seed and command sequence, calling
64
- `getInterpolatedState(id, t)` with the same `t` value always returns identical output.
65
- This guarantee holds only when `extrapolationAllowed` is `false` (the default).
66
-
67
- **Extrapolation warning:** Extrapolation uses linear velocity projection. It can produce
68
- artefacts if entities are accelerating or turning. Enable it only if your simulation tick rate
69
- reliably keeps up with render time.
70
-
71
- ---
72
-
73
- ## 3. Body-Plan Segment ID Mapping Conventions
74
-
75
- ### Canonical segment IDs
76
-
77
- Segment IDs are **camelCase** strings matching Ananke's injury region keys:
78
-
79
- | Segment ID | Body location |
80
- |-------------|------------------------------|
81
- | `head` | Head and skull |
82
- | `torso` | Thorax and upper trunk |
83
- | `leftArm` | Left arm (shoulder to hand) |
84
- | `rightArm` | Right arm |
85
- | `leftLeg` | Left leg (hip to foot) |
86
- | `rightLeg` | Right leg |
87
-
88
- Additional segments appear in non-humanoid body plans (e.g., `tail`, `wing`, `midleg`).
89
- Use `segmentIds(bodyPlan)` to enumerate a plan's canonical segment IDs at runtime.
90
-
91
- ### Supplying a mapping
92
-
93
- ```typescript
94
- const humanoidMapping: BodyPlanMapping = {
95
- bodyPlanId: "humanoid",
96
- segments: [
97
- { segmentId: "head", boneName: "head" },
98
- { segmentId: "torso", boneName: "spine_02" },
99
- { segmentId: "leftArm", boneName: "arm_L" },
100
- { segmentId: "rightArm", boneName: "arm_R" },
101
- { segmentId: "leftLeg", boneName: "leg_L" },
102
- { segmentId: "rightLeg", boneName: "leg_R" },
103
- ],
104
- };
105
- ```
106
-
107
- Unmapped segments fall back to `defaultBoneName` (default `"root"`). Use
108
- `validateMappingCoverage(mapping, segmentIds(plan))` during development to catch gaps.
109
-
110
- ---
111
-
112
- ## 4. `AnimationHints` Field-by-Field Contract
113
-
114
- `AnimationHints` is derived by `deriveAnimationHints(entity)` and embedded in every
115
- `RigSnapshot`. All Q values are integers in `[0, SCALE.Q]` where `SCALE.Q = 10 000` ≡ 1.0.
116
-
117
- ### Locomotion blend weights (mutually exclusive)
118
-
119
- Exactly one of `idle`, `walk`, `run`, `sprint`, `crawl` equals `SCALE.Q` when the entity is
120
- mobile. All five are `0` when the entity is dead or unconscious.
121
-
122
- | Field | Type | Value | Usage |
123
- |----------|------|-------|-------|
124
- | `idle` | `Q` | `SCALE.Q` when standing still; `0` otherwise | Blend in idle animation clip |
125
- | `walk` | `Q` | `SCALE.Q` when walking; `0` otherwise | Blend in walk clip |
126
- | `run` | `Q` | `SCALE.Q` when running; `0` otherwise | Blend in run clip |
127
- | `sprint` | `Q` | `SCALE.Q` when sprinting; `0` otherwise | Blend in sprint clip |
128
- | `crawl` | `Q` | `SCALE.Q` when crawling (prone movement); `0` otherwise | Blend in crawl clip |
129
-
130
- ### Combat blend weights
131
-
132
- | Field | Type | Value | Usage |
133
- |--------------|------|-------|-------|
134
- | `guardingQ` | `Q` | `0`–`SCALE.Q` derived from `intent.defence.intensity` | Blend in guard/parry pose; `0` when not defending or dead |
135
- | `attackingQ` | `Q` | `SCALE.Q` while attack cooldown is active; `0` otherwise | Blend in swing/recovery animation; snaps off when cooldown expires |
136
-
137
- ### Physiological condition
138
-
139
- | Field | Type | Value | Usage |
140
- |----------|------|-------|-------|
141
- | `shockQ` | `Q` | `0`–`SCALE.Q` — direct pass-through of `entity.injury.shock` | Drive screen shake, stagger blend, vignette intensity |
142
- | `fearQ` | `Q` | `0`–`SCALE.Q` — direct pass-through of `entity.condition.fearQ` | Drive breathing rate, idle fidget blend, visual effects |
143
-
144
- ### Boolean state flags
145
-
146
- | Field | Type | True when | Usage |
147
- |---------------|-----------|-----------|-------|
148
- | `prone` | `boolean` | `intent.prone` is true, OR grapple position is `"prone"` or `"pinned"` | Switch to prone animation layer |
149
- | `unconscious` | `boolean` | `!dead` AND `consciousness < 0.20` (2000/10 000) | Switch to unconscious pose; suppress all locomotion |
150
- | `dead` | `boolean` | `entity.injury.dead === true` | Switch to death pose; freeze all animation |
151
-
152
- **Priority:** `dead` overrides `unconscious`; `unconscious` overrides locomotion.
153
- When `dead` is `true`, all locomotion weights are `0` and `unconscious` is `false`.
154
-
155
- ### Interpolation behaviour
156
-
157
- When interpolated by `BridgeEngine`, locomotion weights and condition Q values are lerped
158
- linearly. Boolean flags (`prone`, `unconscious`, `dead`) snap to the new value when the
159
- interpolation factor `t ≥ SCALE.Q / 2` (the midpoint of the tick interval).
160
-
161
- ---
162
-
163
- ## 5. `GrapplePoseConstraint` Usage Contract
164
-
165
- `GrapplePoseConstraint` is derived by `deriveGrappleConstraint(entity)` and embedded in every
166
- `RigSnapshot`. Use it to drive IK constraints or bone locks in your renderer when two entities
167
- are grappling.
168
-
169
- ### Fields
170
-
171
- | Field | Type | Description |
172
- |--------------------|-------------------|-------------|
173
- | `isHolder` | `boolean` | `true` when this entity is actively holding another |
174
- | `holdingEntityId` | `number?` | ID of the entity being held; **present only when `isHolder === true`** |
175
- | `isHeld` | `boolean` | `true` when this entity is being held by one or more others |
176
- | `heldByIds` | `number[]` | IDs of entities currently holding this entity; empty array when `isHeld === false` |
177
- | `position` | `GrapplePosition` | Current positional state: `"standing"` \| `"prone"` \| `"pinned"` \| `"mounted"` |
178
- | `gripQ` | `Q` | Grip strength `[0, SCALE.Q]`; `0` when not grappling |
179
-
180
- ### Usage patterns
181
-
182
- **Non-grappling entity (default state):**
183
- ```
184
- isHolder: false
185
- isHeld: false
186
- heldByIds: []
187
- position: "standing"
188
- gripQ: 0
189
- ```
190
-
191
- **Holder entity:**
192
- ```
193
- isHolder: true
194
- holdingEntityId: <target entity ID>
195
- isHeld: false (unless simultaneously held by someone else)
196
- position: "standing" | "mounted" | etc.
197
- gripQ: > 0
198
- ```
199
-
200
- **Held entity:**
201
- ```
202
- isHolder: false (unless simultaneously holding someone else)
203
- isHeld: true
204
- heldByIds: [<holder entity ID>, ...]
205
- position: "prone" | "pinned" | "standing" | etc.
206
- gripQ: 0
207
- ```
208
-
209
- ### Renderer usage
210
-
211
- 1. For each render frame, call `deriveGrappleConstraint` (or read it from `RigSnapshot.grapple`).
212
- 2. If `isHolder === true` and `isHeld === false`: the holder drives the constraint; lock the
213
- held entity's root to the holder's grip anchor point.
214
- 3. If `isHeld === true`: this entity's root is constrained; disable its root transform update
215
- and apply the holder's transform instead.
216
- 4. `position` drives the animation layer: `"prone"` or `"pinned"` → floor-level pose.
217
- 5. `gripQ` can drive a blend towards a "full grip" animation pose for the holder.
218
-
219
- **Important:** `holdingEntityId` is an optional property and is absent (not `undefined`)
220
- when `isHolder === false` — do not read it without checking `isHolder` first.
221
-
222
- **Interpolation behaviour:** Grapple constraints snap at `t ≥ SCALE.Q / 2` (no smooth
223
- transition between grapple states).
224
-
225
- ---
226
-
227
- ## 6. `InterpolatedState` Shape
228
-
229
- `BridgeEngine.getInterpolatedState(id, t)` returns `InterpolatedState | null`.
230
-
231
- | Field | Type | Description |
232
- |---------------------|-----------------|-------------|
233
- | `entityId` | `number` | Entity ID |
234
- | `tick` | `number` | Most recent simulation tick |
235
- | `interpolationFactor` | `number` | The computed `t` value `[0, SCALE.Q]` |
236
- | `position_m` | `{ x, y, z }` | World-space position in **real metres** (already divided by `SCALE.m`) |
237
- | `velocity_mps` | `{ x, y, z }` | Velocity in metres per second |
238
- | `facing` | `{ x, y, z }` | Unit facing vector (normalised) |
239
- | `animation` | `AnimationHints` | Interpolated animation hints (see §4) |
240
- | `poseModifiers` | `PoseModifier[]` | Per-segment injury deformation weights (one per mapped segment) |
241
- | `grapple` | `GrapplePoseConstraint` | Grapple state (snaps at midpoint; see §5) |
242
- | `condition` | `{ shockQ, fearQ, consciousnessQ, fluidLossQ }` | Interpolated condition scalars |
243
-
244
- ---
245
-
246
- ## 7. Scale Conventions
247
-
248
- All lengths in the simulation are stored as **fixed-point integers** with `SCALE.m = 10 000`
249
- (10 000 units = 1 metre). `InterpolatedState.position_m` has already been converted to real
250
- metres by the bridge.
251
-
252
- | What you receive | Unit | Conversion if needed |
253
- |-----------------|------|----------------------|
254
- | `position_m` from `InterpolatedState` | Real metres (float) | Already converted |
255
- | `position_m` from raw `Entity` | `SCALE.m` units (integer) | Divide by `SCALE.m` |
256
- | Q values (`shockQ`, `fearQ`, etc.) | `[0, SCALE.Q]` integer | Divide by `SCALE.Q` for `[0, 1]` float |
257
-
258
- Always import and use `SCALE.m` / `SCALE.Q` rather than hardcoding `10000`:
259
- ```typescript
260
- import { SCALE } from "ananke";
261
- const shockFloat = hints.shockQ / SCALE.Q; // → [0, 1]
262
- ```
263
-
264
- ---
265
-
266
- ## 8. Quickstart Example
267
-
268
- ```typescript
269
- import { mkWorld, mkKnight, stepWorld, q,
270
- extractRigSnapshots, BridgeEngine } from "ananke";
271
-
272
- // --- Setup ---
273
- const world = mkWorld(42, [mkKnight(1, 1, 0, 0), mkKnight(2, 2, 10000, 0)]);
274
- const engine = new BridgeEngine({ mappings: [], defaultBoneName: "root" });
275
- const ctx = { tractionCoeff: q(0.80) };
276
-
277
- engine.setEntityBodyPlan(1, "humanoid");
278
- engine.setEntityBodyPlan(2, "humanoid");
279
-
280
- // --- Simulation loop (20 Hz) ---
281
- function simTick(): void {
282
- const snapshots = extractRigSnapshots(world);
283
- stepWorld(world, new Map(), ctx);
284
- engine.update(snapshots);
285
- }
286
-
287
- // --- Render loop (60 Hz) ---
288
- function renderFrame(renderTime_s: number): void {
289
- const state = engine.getInterpolatedState(1, renderTime_s);
290
- if (!state) return; // no snapshots yet
291
-
292
- // state.position_m is already in metres
293
- myRenderer.setPosition(state.position_m.x, state.position_m.y);
294
-
295
- // Drive animations from AnimationHints
296
- myAnimator.setIdleWeight(state.animation.idle / SCALE.Q);
297
- myAnimator.setWalkWeight(state.animation.walk / SCALE.Q);
298
- myAnimator.setRunWeight (state.animation.run / SCALE.Q);
299
- myAnimator.setDead (state.animation.dead);
300
- myAnimator.setProne (state.animation.prone);
301
-
302
- // Drive per-region deformation
303
- for (const mod of state.poseModifiers) {
304
- myRenderer.setInjuryBlend(mod.boneName, mod.impairmentQ / SCALE.Q);
305
- }
306
- }
307
- ```
308
-
309
- ---
310
-
311
- ## 9. Stability Promise
312
-
313
- All types and functions listed in this document are **Tier 1 (Stable)**. See
314
- [`STABLE_API.md`](../STABLE_API.md) for the full tier table and breaking-change policy.
315
-
316
- | Export | Source module |
317
- |--------|---------------|
318
- | `extractRigSnapshots` | `src/model3d.ts` |
319
- | `deriveAnimationHints` | `src/model3d.ts` |
320
- | `derivePoseModifiers` | `src/model3d.ts` |
321
- | `deriveGrappleConstraint` | `src/model3d.ts` |
322
- | `deriveMassDistribution` | `src/model3d.ts` |
323
- | `deriveInertiaTensor` | `src/model3d.ts` |
324
- | `BridgeEngine` | `src/bridge/bridge-engine.ts` |
325
- | `BridgeConfig`, `BodyPlanMapping`, `SegmentMapping` | `src/bridge/types.ts` |
326
- | `InterpolatedState` | `src/bridge/types.ts` |
327
- | `AnimationHints` | `src/model3d.ts` |
328
- | `GrapplePoseConstraint` | `src/model3d.ts` |
329
- | `PoseModifier` | `src/model3d.ts` |
330
- | `RigSnapshot` | `src/model3d.ts` |
331
-
332
- *Generated by Claude Code during Platform Hardening PH-5, March 2026.*
1
+ # Ananke Bridge Contract
2
+
3
+ > **Source-of-truth for renderer bridge imports and stability.**
4
+ > Package name: `@its-not-rocket-science/ananke`.
5
+
6
+ For versioning policy see [`docs/versioning.md`](versioning.md) and [`STABLE_API.md`](../STABLE_API.md).
7
+
8
+ Maintainer support boundary details are in [`docs/support-boundaries.md`](support-boundaries.md).
9
+
10
+ <!-- CONTRACT:STABILITY_LABELS:start -->
11
+ ```json
12
+ [
13
+ { "kind": "subpath", "subject": ".", "status": "Tier 1 stable", "notes": "Root host API and stable bridge extraction helpers" },
14
+ { "kind": "subpath", "subject": "./tier2", "status": "Experimental", "notes": "Bridge runtime and mapping APIs" }
15
+ ]
16
+ ```
17
+ <!-- CONTRACT:STABILITY_LABELS:end -->
18
+
19
+ ## 1) Import-path and stability truth table
20
+
21
+ | Symbol | Import path | Stability |
22
+ |---|---|---|
23
+ | `createWorld`, `stepWorld`, `q`, `SCALE` | `@its-not-rocket-science/ananke` | Tier 1 stable |
24
+ | `extractRigSnapshots`, `deriveAnimationHints` | `@its-not-rocket-science/ananke` | Tier 1 stable |
25
+ | `RigSnapshot`, `AnimationHints` | `@its-not-rocket-science/ananke` | Tier 1 stable |
26
+ | `BridgeEngine` | `@its-not-rocket-science/ananke/tier2` | Experimental |
27
+ | `BridgeConfig`, `BodyPlanMapping`, `SegmentMapping`, `InterpolatedState`, `MappedPoseModifier` | `@its-not-rocket-science/ananke/tier2` | Experimental |
28
+ | `validateMappingCoverage` | `@its-not-rocket-science/ananke/tier2` | Experimental |
29
+ | `derivePoseModifiers`, `deriveGrappleConstraint`, `deriveMassDistribution`, `deriveInertiaTensor` | `@its-not-rocket-science/ananke/tier2` | Experimental |
30
+ | `PoseModifier`, `GrapplePoseConstraint` | `@its-not-rocket-science/ananke/tier2` | Experimental |
31
+ | `mkWorld`, `mkKnight` | _No package export path_ | Internal/test-only |
32
+
33
+ `BridgeEngine` is **not Tier-1 root**. Use `@its-not-rocket-science/ananke/tier2` and treat it as experimental.
34
+
35
+ If bridge correctness is production-critical, pin exact patch versions and re-run interpolation/mapping regression tests on every upgrade.
36
+
37
+ ## 2) Minimum bridge integration (Tier 1 stable)
38
+
39
+ This is the lowest-friction, stable path: run simulation, extract rig snapshots, and derive animation hints.
40
+
41
+ ```ts
42
+ import {
43
+ createWorld,
44
+ stepWorld,
45
+ extractRigSnapshots,
46
+ deriveAnimationHints,
47
+ q,
48
+ SCALE,
49
+ } from "@its-not-rocket-science/ananke";
50
+
51
+ const world = createWorld(42, [
52
+ { id: 1, teamId: 1, seed: 101, archetype: "KNIGHT_INFANTRY", weaponId: "wpn_longsword" },
53
+ { id: 2, teamId: 2, seed: 202, archetype: "KNIGHT_INFANTRY", weaponId: "wpn_longsword", x_m: 1.0 },
54
+ ]);
55
+
56
+ stepWorld(world, new Map(), { tractionCoeff: q(0.8) });
57
+
58
+ const snapshots = extractRigSnapshots(world);
59
+ const hints = deriveAnimationHints(world.entities[0]!);
60
+
61
+ console.log({
62
+ snapshotCount: snapshots.length,
63
+ idle01: hints.idle / SCALE.Q,
64
+ });
65
+ ```
66
+
67
+ ## 3) Advanced bridge runtime (Experimental tier2)
68
+
69
+ Use `BridgeEngine` when you need interpolation/extrapolation between simulation ticks.
70
+
71
+ ```ts
72
+ import { createWorld, stepWorld, extractRigSnapshots, q, SCALE } from "@its-not-rocket-science/ananke";
73
+ import {
74
+ BridgeEngine,
75
+ type BridgeConfig,
76
+ type BodyPlanMapping,
77
+ validateMappingCoverage,
78
+ } from "@its-not-rocket-science/ananke/tier2";
79
+
80
+ const humanoidMapping: BodyPlanMapping = {
81
+ bodyPlanId: "humanoid",
82
+ segments: [{ segmentId: "head", boneName: "Head" }],
83
+ };
84
+
85
+ const missing = validateMappingCoverage(humanoidMapping, ["head", "torso"]);
86
+ console.log("missingSegments", missing);
87
+
88
+ const config: BridgeConfig = {
89
+ mappings: [humanoidMapping],
90
+ defaultBoneName: "root",
91
+ extrapolationAllowed: false,
92
+ };
93
+
94
+ const world = createWorld(7, [
95
+ { id: 1, teamId: 1, seed: 11, archetype: "KNIGHT_INFANTRY", weaponId: "wpn_longsword" },
96
+ ]);
97
+
98
+ const engine = new BridgeEngine(config);
99
+ engine.setEntityBodyPlan(1, "humanoid");
100
+
101
+ stepWorld(world, new Map(), { tractionCoeff: q(0.8) });
102
+ engine.update(extractRigSnapshots(world));
103
+
104
+ stepWorld(world, new Map(), { tractionCoeff: q(0.8) });
105
+ engine.update(extractRigSnapshots(world));
106
+
107
+ const renderTime_s = engine.getLatestSimTime() - 1 / 40;
108
+ const state = engine.getInterpolatedState(1, renderTime_s);
109
+
110
+ if (state) {
111
+ console.log({
112
+ x_m: state.position_m.x / SCALE.m,
113
+ idle01: state.animation.idle / SCALE.Q,
114
+ fromTick: state.fromTick,
115
+ toTick: state.toTick,
116
+ });
117
+ }
118
+ ```
119
+
120
+ ### BridgeEngine behavior contract (Experimental)
121
+
122
+ - `update(snapshots, motion?, condition?)` shifts per-entity `curr` to `prev`, ingests new `curr`, and advances tick/time state.
123
+ - `getInterpolatedState(entityId, renderTime_s)` returns `null` if the entity has no snapshots.
124
+ - With only one snapshot for an entity, returned state is held (no interpolation).
125
+ - If `renderTime_s <= prevTime_s`, previous snapshot is held.
126
+ - If `renderTime_s >= currTime_s`:
127
+ - `extrapolationAllowed: false` => current snapshot is held.
128
+ - `extrapolationAllowed: true` => position extrapolates from current velocity.
129
+ - Numeric tracks interpolate in fixed-point Q-space.
130
+ - `animation.prone|unconscious|dead`, `condition.dead`, and `grapple` snap at midpoint (`t < SCALE.Q / 2` uses previous, otherwise current).
131
+
132
+ ## 4) Compile-tested reference example
133
+
134
+ - `examples/bridge-minimal.ts` is a compile-tested bridge example that uses only package export paths.
135
+ - Run it after build:
136
+
137
+ ```bash
138
+ npm run build
139
+ node dist/examples/bridge-minimal.js
140
+ ```
@@ -0,0 +1,63 @@
1
+ # Completion Evidence Dashboard (Artifact Status)
2
+
3
+ _Date: 2026-04-02_
4
+
5
+ This dashboard maps each maturity claim to explicit, machine-checkable evidence requirements.
6
+
7
+ ## Evidence mapping table (status → proof)
8
+
9
+ | Maturity dimension | Status | Measurable criteria | Objective proof currently present |
10
+ |---|---|---|---|
11
+ | Deterministic combat kernel | verified | `>=3` deterministic/kernel tests, phase-order invariant test, `>=2` conformance fixtures, validation dashboard artifact. | `test/kernel_determinism.test.ts`, `test/kernel_phase_order.test.ts`, `test/determinism.test.ts`, `test/invariants.test.ts`, `conformance/lockstep-sequence.json`, `conformance/phase-order.json`, `docs/dashboard/validation-dashboard.json`. |
12
+ | Injury, medical, survivability | verified | `>=3` survivability tests, at least one published medical validation artifact. | `test/medical.test.ts`, `test/injury_totals.test.ts`, `test/wound-aging.test.ts`, `docs/validation-first-aid-saves-lives-2026-03-18T19-34-10.md`. |
13
+ | Environment, hazards, climate | verified | `>=3` hazard/climate/thermoregulation tests, at least one thermoregulation validation artifact. | `test/hazards.test.ts`, `test/climate.test.ts`, `test/thermoregulation.test.ts`, `docs/validation-thermoregulation-core-stability-2026-03-18T00-07-52.md`. |
14
+ | AI, perception, cognition | partially verified | `>=3` subsystem tests plus behaviour invariant coverage (no M4 validation artifact required yet). | `test/ai_system.test.ts`, `test/perception_phase4.test.ts`, `test/cognition.test.ts`, `test/behavior-trees.test.ts`. |
15
+ | Campaign/world simulation | partially verified | `>=3` campaign/world tests and at least one deterministic snapshot/fixture artifact. | `test/campaign.test.ts`, `test/world-generation.test.ts`, `test/scenarios.test.ts`, `test/snapshots/kernel_behaviour_snapshot.json`. |
16
+ | Bridge/replay/integration surfaces | verified | `>=3` bridge/replay/netcode tests, `>=2` replay/bridge conformance fixtures, bridge contract artifact. | `test/bridge/integration.test.ts`, `test/replay.test.ts`, `test/netcode.test.ts`, `conformance/replay-parity.json`, `conformance/bridge-snapshot.json`, `docs/bridge-contract.md`. |
17
+ | Tooling/benchmarks/release checks | verified | Performance regression test, release artifact(s), and coverage signal in CI. | `test/performance.test.ts`, `docs/release-dashboard.md`, `npm run test:coverage`. |
18
+
19
+ ## Gap list (statuses that still exceed evidence quality)
20
+
21
+ 1. **Coverage thresholds are still implicit**: we require `npm run test:coverage` to run, but no per-dimension minimum line/branch threshold is encoded yet.
22
+ 2. **Freshness/SLA is not yet enforced**: evidence files exist, but we do not fail when validation/release artifacts become stale by date.
23
+ 3. **Invariant depth is uneven**: only selected dimensions explicitly require invariant tests; some M4 areas still rely mostly on scenario tests.
24
+ 4. **External reproducibility remains partial**: most evidence is in-repo and deterministic, but independent external rerun attestations are not required by CI.
25
+
26
+ ## Missing tests to add next
27
+
28
+ 1. Add explicit **AI decision determinism** test (fixed seed + identical action traces) to strengthen M3 AI evidence.
29
+ 2. Add **campaign long-horizon regression** test (multi-turn economy/governance drift bound) for hardened campaign stability.
30
+ 3. Add **bridge backward-compatibility matrix** test (current engine vs prior snapshot schema) to detect integration drift.
31
+ 4. Add **release-check smoke fixture test** that validates release metadata/version freshness constraints.
32
+
33
+ ## Missing validation artifacts to add next
34
+
35
+ 1. Add a dedicated **AI/perception validation report** (`docs/validation-ai-perception-*.md`) so that AI can graduate from M3 to M4 on objective grounds.
36
+ 2. Add a **campaign/world validation artifact** (scenario corpus or benchmark report) for M4 candidacy.
37
+ 3. Add a machine-readable **artifact freshness ledger** (e.g., `docs/validation-artifact-index.json`) with `generatedAt`, producer tool, and scenario set hash.
38
+
39
+ ## CI enforcement proposal
40
+
41
+ ### Implemented gate
42
+
43
+ - New source of truth: `docs/maturity-evidence-map.json`.
44
+ - New CI script: `tools/check-maturity-evidence.mjs`.
45
+ - CI integration: `npm run check-maturity-evidence` is now part of `npm run ci`.
46
+
47
+ ### Gate semantics (fail conditions)
48
+
49
+ The CI job fails when any of the following is true:
50
+
51
+ 1. A subsystem in `docs/maturity-matrix.json` has no entry in `docs/maturity-evidence-map.json`.
52
+ 2. A mapped subsystem claims a different maturity than `docs/maturity-matrix.json`.
53
+ 3. Any required evidence path is missing (tests, fixtures, invariants, validation artifacts).
54
+ 4. Any subsystem has zero `requiredTests` entries.
55
+ 5. Any M3 subsystem has fewer than 3 required tests.
56
+ 6. Any M4 subsystem lacks validation artifacts/conformance fixtures.
57
+ 7. Any subsystem has fewer than 2 total evidence signals.
58
+
59
+ ### Recommended next CI extensions (not yet implemented)
60
+
61
+ 1. Enforce per-dimension coverage floors (`lines/branches/functions`) instead of run-only coverage.
62
+ 2. Enforce artifact freshness windows (e.g., fail if release/validation artifact older than N days).
63
+ 3. Enforce fixture ↔ test linkage (each required fixture must be referenced by at least one test).