@volter/project 0.5.211 → 0.5.212

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 (52) hide show
  1. package/NOTICE +2 -2
  2. package/README.md +8 -15
  3. package/package.json +5 -26
  4. package/schemas/volter-project.schema.json +33 -145
  5. package/schemas/volter-settings.schema.json +11 -49
  6. package/src/adapter/adapter-module.ts +204 -465
  7. package/src/adapter/adapter-surface.ts +4 -32
  8. package/src/adapter/authoring-seam-contract.ts +7 -24
  9. package/src/adapter/authoring.ts +126 -265
  10. package/src/adapter/editor-looks.ts +96 -127
  11. package/src/adapter/finders/registry.ts +6 -21
  12. package/src/adapter/host-context.ts +17 -72
  13. package/src/adapter/index.ts +4 -77
  14. package/src/adapter/manifest-interpreter.ts +7 -50
  15. package/src/adapter/physics-adapter-2d.ts +4 -21
  16. package/src/adapter/root-adapter.ts +37 -111
  17. package/src/adapter/root-seam-contract.ts +3 -6
  18. package/src/adapter/root-stacking.ts +4 -46
  19. package/src/adapter/seam-evidence.ts +0 -16
  20. package/src/adapter/system-adapter.ts +24 -656
  21. package/src/adapter/system-seam-contract.ts +1 -91
  22. package/src/adapter/transform.ts +4 -3
  23. package/src/adapter/workspace-arrangement.ts +29 -49
  24. package/src/manifest/editor-port.ts +10 -36
  25. package/src/manifest/filename.ts +3 -42
  26. package/src/manifest/load-file.ts +9 -15
  27. package/src/manifest/load.ts +44 -179
  28. package/src/manifest/locate.ts +6 -29
  29. package/src/manifest/schema.ts +155 -427
  30. package/src/semver.ts +16 -0
  31. package/src/settings/keys.ts +9 -11
  32. package/src/settings/schema.ts +55 -44
  33. package/schemas/engine-api.json +0 -57
  34. package/schemas/engine-api.md +0 -22
  35. package/src/adapter/binding.ts +0 -445
  36. package/src/adapter/debug-registry-ref.ts +0 -19
  37. package/src/adapter/frame-capture.ts +0 -80
  38. package/src/adapter/ingest/contract-system-slots.ts +0 -28
  39. package/src/adapter/ingest/game-contract-seams.ts +0 -86
  40. package/src/adapter/ingest/game-contract.ts +0 -506
  41. package/src/adapter/ingest/mount-readiness.ts +0 -140
  42. package/src/adapter/loop-gate-report.ts +0 -53
  43. package/src/adapter/native-entry-surface.ts +0 -100
  44. package/src/adapter/render-audio.ts +0 -20
  45. package/src/adapter/render-memory.ts +0 -34
  46. package/src/adapter/renderer-config.ts +0 -84
  47. package/src/adapter/system-slot.ts +0 -301
  48. package/src/core/system-phase.ts +0 -52
  49. package/src/inspection.ts +0 -281
  50. package/src/manifest/configuration-kinds.ts +0 -241
  51. package/src/manifest/kind-modules.ts +0 -58
  52. package/src/manifest/runtime-environment.ts +0 -48
@@ -1,32 +1,10 @@
1
- /**
2
- * Adapter interfaces — the seams the engine host and the editor DEPEND ON.
3
- *
4
- * host → RootAdapter ← { the editor's R3F root mounts, IngestRootAdapter, … }
5
- * editor → AuthoringAdapter ← { ThreeAuthoringAdapter, ReactRootAuthoringAdapter, … }
6
- * game → SystemAdapters (physics/networking/navigation/audio/camera/debug)
7
- *
8
- * The first-party Rapier/Colyseus stack is ONE implementer of these
9
- * interfaces, not the engine's vocabulary.
10
- *
11
- * **This module is TYPE-ONLY, and must stay that way (P-6).** It used to
12
- * value-export its own implementers — `createRapierPhysicsAdapter`,
13
- * `createColyseusNetworkingAdapter`, `createNavigationAdapter` — so
14
- * `import type { AuthoringAdapter } from './index'` was the only
15
- * thing keeping the three/Rapier/Colyseus stack out of a Pixi or React
16
- * consumer's module graph. A seam that ships its implementers is not a
17
- * seam. Import an implementer from its OWN path instead:
18
- *
19
- * `@volter/threejs-runtime/adapter/rapier-physics-adapter` createRapierPhysicsAdapter
20
- * `@volter/threejs-runtime/adapter/first-party-navigation-system` createNavigationAdapter
21
- */
1
+ /** Project adapter contracts and neutral contract helpers. Implementations live in the integrations that own their library objects. */
22
2
 
23
3
  export type { AdapterSurface } from './adapter-surface';
24
4
  export type {
25
5
  AssetDropContext,
26
6
  AssetDropProvider,
27
- AssetSubjectProvider,
28
7
  AuthoringAdapter,
29
- AuthoringAssetSubject,
30
8
  AuthoringCapabilities,
31
9
  AuthoringProvenance,
32
10
  AuthoringProviderKey,
@@ -86,38 +64,20 @@ export {
86
64
  AUTHORING_PROVIDER_SHAPES,
87
65
  HIERARCHY_PROVIDER_SHAPE,
88
66
  } from './authoring-seam-contract';
89
- export type {
90
- EntryStaticSurface,
91
- ObservationBinding,
92
- ProjectBinding,
93
- ProjectionBinding,
94
- ProtocolFamily,
95
- RootBinding,
96
- RootBindingParts,
97
- RootDeclaration,
98
- SubstrateBinding,
99
- TruthBinding,
100
- } from './binding';
101
- export { createRootBinding } from './binding';
102
67
  export type {
103
68
  CanvasHostContext,
104
69
  DomHostContext,
105
- HostContextBase,
106
70
  HostContextFor,
107
71
  HostSurface,
108
- ThreeHostContext,
72
+ StageHostContext,
109
73
  } from './host-context';
110
- /** The offline-audio contract an `AudioAdapter.renderOffline` implements.
111
- * Defined beside the render-mode harness that first needed it; re-exported
112
- * here so a world binding the seam imports one name from one place. */
113
- export type { OfflineAudioRenderer, RenderedAudio } from './render-audio';
114
74
  export type {
115
75
  MountedCanvasRoot,
116
76
  MountedCanvasSubstrate,
117
77
  MountedReactRoot,
118
78
  MountedRoot,
119
79
  MountedRootBase,
120
- MountedThreeRoot,
80
+ MountedStageRoot,
121
81
  RootAdapter,
122
82
  RootStateObserver,
123
83
  SurfaceAdapter,
@@ -127,7 +87,7 @@ export {
127
87
  MOUNTED_CANVAS_SURFACE_SHAPE,
128
88
  MOUNTED_DOM_SURFACE_SHAPE,
129
89
  MOUNTED_ROOT_BASE_SHAPE,
130
- MOUNTED_THREE_SURFACE_SHAPE,
90
+ MOUNTED_STAGE_SURFACE_SHAPE,
131
91
  ROOT_ADAPTER_SHAPE,
132
92
  ROOT_STATE_OBSERVER_SHAPE,
133
93
  } from './root-seam-contract';
@@ -150,44 +110,16 @@ export {
150
110
  seamStageAtLeast,
151
111
  } from './seam-evidence';
152
112
  export type {
153
- AudioAdapter,
154
- AudioDebugEvent,
155
- AudioGraphNode,
156
- AudioMeterFrame,
157
- AudioMeterHandle,
158
- AudioRecordingHandle,
159
- AudioTransportState,
160
113
  CameraAdapter,
161
114
  CameraRuntimeCamera,
162
115
  CameraRuntimeSnapshot,
163
116
  CameraRuntimeTransition,
164
- ConnectionState,
165
- DebugAdapter,
166
- NavCrowdAgentState,
167
- NavigationAdapter,
168
- NavPoint,
169
- NetConditioning,
170
- NetMessageEvent,
171
- NetPeer,
172
- NetPlayerIdentity,
173
- NetConditioningLimits,
174
- NetRates,
175
- NetServerClient,
176
- NetServerInspection,
177
- NetServerRoom,
178
- NetTypeTraffic,
179
- NetEntityTraffic,
180
- NetServerConfig,
181
- NetworkingAdapter,
182
117
  PhysicsAdapter,
183
118
  PhysicsCarrier,
184
119
  PhysicsColliderShape,
185
120
  PhysicsColliderSnapshot,
186
121
  PhysicsJointSnapshot,
187
122
  PhysicsJointType,
188
- RenderDebugAdapter,
189
- ReplicationStats,
190
- RoomInfo,
191
123
  SystemAdapters,
192
124
  Unsubscribe,
193
125
  } from './system-adapter';
@@ -195,14 +127,9 @@ export type {
195
127
  * `PhysicsAdapter`'s comment for why it is tagged at all. */
196
128
  export { displayKeyedPhysics, nodeKeyedPhysics, physicsOwnerOf } from './system-adapter';
197
129
  export {
198
- AUDIO_ADAPTER_SHAPE,
199
130
  CAMERA_ADAPTER_SHAPE,
200
- DEBUG_ADAPTER_SHAPE,
201
- NAVIGATION_ADAPTER_SHAPE,
202
- NETWORKING_ADAPTER_SHAPE,
203
131
  PHYSICS_2D_ADAPTER_SHAPE,
204
132
  PHYSICS_ADAPTER_SHAPE,
205
- RENDER_DEBUG_ADAPTER_SHAPE,
206
133
  SYSTEM_ADAPTERS_SHAPE,
207
134
  SYSTEM_PROVIDER_SHAPES,
208
135
  } from './system-seam-contract';
@@ -1,27 +1,12 @@
1
1
  /**
2
- * The manifest's SOLE INTERPRETER (owner ruling, 2026-08-20 — see
3
- * docs/ARCHITECTURE-CORE.md §adapter). `volter.project.json` is static
4
- * inventory; the adapter seam is the ONE layer that turns it into execution
5
- * bindings, and host code consumes the manifest through THESE derivations —
6
- * never by reading `manifest.roots` directly (the guard is
7
- * `packages/engine/test/manifest-sole-interpreter.test.ts`).
2
+ * Pure manifest interpretation shared by host consumers. These functions
3
+ * turn loaded inventory into adapter bindings without evaluating project
4
+ * modules. Their structural input types accept resolved or lenient edit-mode
5
+ * manifest views.
8
6
  *
9
- * Why a doorway and not a convention: one deriver means one direction of
10
- * truth (manifest → adapter → host). When the project's own `editor/volter.adapter.ts`
11
- * later interposes on a derivation, every host path that asked the
12
- * interpreter gets the project's answer for free; a path that read the raw
13
- * field would silently keep the mechanical one.
14
- *
15
- * The module-free audiences named by the ruling (a bundled CLI's raw
16
- * discovery over unvalidated files, Vite config time, synchronous
17
- * dep-optimization, and the loader that CONSTRUCTS the resolution) remain
18
- * direct readers by design; everything else asks here.
19
- *
20
- * These functions are pure and synchronous over an already-loaded manifest —
21
- * interpreting inventory never evaluates project modules, so any host realm
22
- * may import this. They are structurally generic so both the loader's
23
- * `ResolvedGameManifest` and the editor's lenient `EditModeManifest` view
24
- * flow through the same doorway.
7
+ * Raw discovery, config-time dependency optimization and the loader that
8
+ * constructs resolution read inventory directly; execution hosts use these
9
+ * derivations.
25
10
  */
26
11
 
27
12
  /**
@@ -34,31 +19,3 @@ export function declaredRoots<Root>(manifest: {
34
19
  }): readonly Root[] {
35
20
  return manifest.roots;
36
21
  }
37
-
38
- /** The root declaring `id`, or undefined — never a silent first-wins pick. */
39
- export function rootById<Root extends { readonly id: string }>(
40
- manifest: { readonly roots: readonly Root[] },
41
- id: string,
42
- ): Root | undefined {
43
- return manifest.roots.find((root) => root.id === id);
44
- }
45
-
46
- /** Whether any declared root mounts on `surface` (e.g. the editor's
47
- * "does this project have a Three world" gates). The parameter is the closed
48
- * surface union, not `string` — a typo'd literal must stay a compile error,
49
- * not a silent forever-false. */
50
- export function hasRootOnSurface(
51
- manifest: { readonly roots: readonly { readonly surface: string }[] },
52
- surface: 'three' | 'canvas' | 'dom',
53
- ): boolean {
54
- return manifest.roots.some((root) => root.surface === surface);
55
- }
56
-
57
- /** The declared ingest roots (`adapter.type === 'ingest'`), in mount order.
58
- * Composite-manifest rules (exactly one, ≥2 is a loud named error) belong to
59
- * the call sites that state them. */
60
- export function ingestRoots<
61
- Root extends { readonly adapter: { readonly type: string } },
62
- >(manifest: { readonly roots: readonly Root[] }): Root[] {
63
- return manifest.roots.filter((root) => root.adapter.type === 'ingest');
64
- }
@@ -1,27 +1,10 @@
1
1
  /**
2
- * The canvas surface's physics seam, keyed by the substrate's display object
3
- * (`D`, opaque by default). The implementer names its own display type
4
- * (`@volter/editor-game/runtime/pixi/system-adapters` is `PhysicsAdapter2D<Container>`).
5
- */
6
-
7
- /**
8
- * PhysicsAdapter2D — the Pixi analog of the 3D PhysicsAdapter (rapier-physics-adapter).
9
- * Lets the editor coordinate editing of a Rapier-2D-driven entity WITHOUT owning the
10
- * simulation: report transform ownership, then freeze → commit → unfreeze so a drag
11
- * sticks instead of being stomped by the next physics step.
12
- *
13
- * KEYED BY THE DISPLAY OBJECT, and that is this surface's own vocabulary rather
14
- * than a shortcut: the canvas gizmo's real caller
15
- * (`editor/src/authoring/pixi-live-write-target.ts`) resolves node id → display
16
- * itself before it asks, so there is no node-id hop on this lane at all.
17
- *
18
- * `keyedBy` is REQUIRED here and optional on the 3D {@link PhysicsAdapter} —
19
- * see that interface's doc comment for why the union has to be tagged, and why
20
- * the newcomer is the shape obliged to say so.
2
+ * Coordinate canvas physics edits through freeze, commit and unfreeze, keyed by the native
3
+ * display object. The canvas caller resolves node ids before entering this seam. The required
4
+ * display tag distinguishes it from the node-id physics contract in system-adapter.ts.
21
5
  */
22
6
  export interface PhysicsAdapter2D<D = unknown> {
23
- /** Discriminates this shape from the node-id-keyed {@link PhysicsAdapter}
24
- * inside `SystemAdapters['physics']`. */
7
+ /** Discriminates this display-keyed shape inside SystemAdapters.physics. */
25
8
  readonly keyedBy: 'display';
26
9
  ownerOf(display: D): 'physics' | 'none';
27
10
  freeze(display: D): void;
@@ -2,7 +2,7 @@
2
2
  * RootAdapter — the host ⇄ runtime contract. A ROOT is *anything that mounts*.
3
3
  *
4
4
  * This is the interface the host depends on. First-party content and
5
- * unmodified external games are peer root adapters. A game is the
5
+ * unmodified external projects are peer root adapters. A project is the
6
6
  * manifest's complete roots array, while each adapter owns exactly one
7
7
  * root.
8
8
  */
@@ -10,131 +10,78 @@
10
10
  import type { AdapterSurface } from './adapter-surface';
11
11
  import type { AuthoringAdapter } from './authoring';
12
12
  import type { HostContextFor } from './host-context';
13
- import type { WorldRendererConfig } from './renderer-config';
14
13
  import type { SystemAdapters } from './system-adapter';
15
14
 
16
15
  /**
17
- * The ingested-world observation contract (T7.4 slice 2, "the
18
- * must-answer"). Cross-world data flow with a
19
- * foreign game is observation via an adapter-provided interface, not ordinary
20
- * shared state (that's D7's answer for first-party↔first-party flow).
21
- * Optional on {@link MountedThreeRoot} —
22
- * absence means the adapter has nothing genuinely observable to expose (the
23
- * anti-shim rule: never fabricate state), and the world takes the honest
24
- * "no state bridge" tier hit (see `Game.registerRoot` in `runtime/game.ts`)
25
- * rather than silently offering an `undefined`-forever subscription.
16
+ * An adapter-provided observation interface for a mounted root’s state.
17
+ * Optional absence means the root exposes no state bridge. Consumers report
18
+ * that absence rather than fabricating state or an undefined-only subscription.
26
19
  */
27
20
  export interface RootStateObserver {
28
21
  /**
29
- * Notified at most once per frame IF the adapter can hook the game's own
30
- * update (`loop: 'gated'` roots, §(d)); self-driven roots (`loop:
22
+ * Notified at most once per frame IF the adapter can hook the project's own
23
+ * update (`loop: 'gated'` roots); self-driven roots (`loop:
31
24
  * 'self-driven'`, raw-rAF) may notify on their OWN rAF cadence instead —
32
25
  * consumers must not assume our frame timing. Returns an unsubscribe
33
26
  * function.
34
27
  */
35
28
  subscribe(onChange: () => void): () => void;
36
29
  /**
37
- * A cheap, adapter-chosen snapshot of this world's observable state.
38
- * Returns a STABLE reference while nothing has changed (so a selector run
39
- * over it can cheaply bail out by reference, same spirit as
40
- * `GameStateBridge`'s frame-version cache). Shape is adapter-defined —
41
- * not centrally schematized in v1.
30
+ * A cheap snapshot of the root’s observable state. Preserve its reference
31
+ * while unchanged so selectors can skip recomputation. The adapter owns
32
+ * the snapshot’s shape.
42
33
  */
43
34
  snapshot(): unknown;
44
35
  }
45
36
 
46
37
  /**
47
38
  * Everything a mounted world provides except its render surface. Tagged
48
- * mounted-root variants carry their native surface fields: a Three scene and
39
+ * mounted-root variants carry their native surface fields: a stage scene and
49
40
  * camera, a declared canvas substrate root, or a DOM container.
50
41
  */
51
42
  export interface MountedRootBase {
52
43
  /**
53
- * Loop model:
54
- * - `false` (host-driven): first-party + "clean" externals. The host ticks
55
- * `update(dt)` in its loop.
56
- * - `true` (self-driven): an unmodified game that owns its renderer + rAF.
57
- * The host does NOT tick it. Pause/step control is a DECLARED CAPABILITY,
58
- * not a promise: implement `setPaused`/`step` only where the game exposes
59
- * a sanctioned pause/step mechanism. Gating a raw-rAF loop from outside
60
- * was demonstrated and REJECTED (D5 — it halts the loop rather than
61
- * pausing it). Where `setPaused` is absent the host reports the
62
- * shortfall loudly, naming the mechanism that would close it (T7.6).
44
+ * Loop ownership: host-driven roots receive `update(dt)` from the host;
45
+ * self-driven roots own their rendering loop and are not ticked by the host.
46
+ * Expose pause/step only when the project supplies those operations. Missing
47
+ * pause support is reported as a capability shortfall.
63
48
  */
64
49
  readonly drivesOwnLoop: boolean;
65
50
 
66
51
  /**
67
- * Which host clock owns an opaque mounted root's single `update(dt)` hook.
68
- *
69
- * `fixed` (the default) preserves the adapter seam's original contract:
70
- * the callback runs once per consumed simulation substep. `display` is for
71
- * a reconciler whose one native advance is inseparably BOTH its callbacks
72
- * and its draw (React Three Fiber's `advance()` is the measured case). The
73
- * real host then withholds it from fixed catch-up substeps and invokes it
74
- * exactly once from the display pass. A direct/offline `runFrame` that does
75
- * not skip render phases still invokes it once, preserving capture and
76
- * explicit-tick semantics.
77
- *
78
- * Absent means `fixed`. This changes no foreign/ingest adapter unless that
79
- * adapter opts in explicitly.
52
+ * Host clock for the mounted root’s `update(dt)` hook.
53
+ * `fixed` requests simulation substeps. `display` requests one update per
54
+ * display pass for reconcilers whose advance combines callbacks and drawing.
80
55
  */
81
56
  readonly updateCadence?: 'fixed' | 'display';
82
57
 
83
58
  update?(dt: number): void; // host-driven only
84
59
  fixedUpdate?(dt: number): void;
85
- setPaused?(paused: boolean): void; // capability, not promise — see loop-model note above
60
+ setPaused?(paused: boolean): void; // optional adapter-owned pause operation
86
61
  step?(): void;
87
62
 
88
63
  resize?(width: number, height: number): void;
89
- /** Does this world claim a pointer at (x, y), in CSS pixels from its surface's top-left? The input
90
- * router asks it of a world stacked above the bottom one (a 2D layer's interactive Pixi objects).
91
- * Absent: such a world claims no point, and input falls through to the bottom world. */
92
- hitTest?(x: number, y: number): boolean;
93
64
  dispose(): void;
94
65
  /** Resolves after an asynchronous native reconciler has run every component
95
66
  * cleanup started by {@link dispose}. Omitted by synchronously-disposing
96
- * roots. Hosts use this before auditing/reclaiming realm-owned resources. */
67
+ * roots. */
97
68
  readonly disposeComplete?: Promise<void>;
98
69
 
99
70
  /** Optional capability providers — absence = "not supported", host degrades. */
100
71
  readonly authoring?: AuthoringAdapter;
101
72
  readonly systems?: SystemAdapters;
102
- /** Adapter-owned evaluation identity used to bind static system factories. */
103
- readonly systemScope?: object;
104
- /** Optional state-observation capability (T7.4 slice 2, §4) — absent means
105
- * "no state bridge"; `Game.registerRoot` reports this loudly, once, for
106
- * self-driven mount. A host-driven mount ticks inside the game's frame,
107
- * where the editor reads it directly, so it needs none. */
73
+ /** Optional state-observation capability. Absence means no state bridge. */
108
74
  readonly observe?: RootStateObserver;
109
75
  }
110
76
 
111
- /** A live, mounted Three world. The host obtains `scene`/`camera` to render
112
- * and author it; they are opaque here, and `@volter/editor-threejs` names the
113
- * Three-typed root. */
114
- export interface MountedThreeRoot<Scene = unknown, Camera = unknown> extends MountedRootBase {
115
- readonly kind: 'three';
77
+ /** A live, mounted `stage` world. The host obtains `scene`/`camera` to render
78
+ * and author it; they are opaque here, and a runtime extends the root with its
79
+ * own types. */
80
+ export interface MountedStageRoot<Scene = unknown, Camera = unknown> extends MountedRootBase {
81
+ readonly kind: 'stage';
116
82
  /** The live scene + camera the editor inspects/renders for authoring. */
117
83
  readonly scene: Scene;
118
84
  readonly camera: Camera;
119
- /**
120
- * The colour pipeline this world was authored for, REPORTED rather than
121
- * applied — the adapter has already applied it to the renderer its own host
122
- * handed it (`./renderer-config.ts`).
123
- *
124
- * It is here because the host that MOUNTS a world is not always the host
125
- * that DRAWS it. The editor's design session mounts against a
126
- * non-rasterizing renderer on purpose and then draws the mounted scene with
127
- * the editor viewport's own `WebGLRenderer`, so the world's declaration
128
- * lands on a surface with no pixels while the surface with pixels never
129
- * hears it. Reporting the declaration is what lets that host apply the same
130
- * config, through the same `applyWorldRendererConfig`, to the renderer that
131
- * actually produces the frame.
132
- *
133
- * Absent means the world declared nothing and the drawing host should leave
134
- * its own configuration alone — the same "absent = don't touch" rule the
135
- * config's own fields follow.
136
- */
137
- readonly rendererConfig?: WorldRendererConfig | undefined;
138
85
  }
139
86
 
140
87
  /**
@@ -145,10 +92,6 @@ export interface MountedThreeRoot<Scene = unknown, Camera = unknown> extends Mou
145
92
  export interface MountedCanvasSubstrate<T = unknown> {
146
93
  readonly name: string;
147
94
  readonly root: T;
148
- /** The substrate's own module namespace when authoring needs constructors
149
- * (for example Babylon's native GizmoManager). Omitted when the projection
150
- * only needs the root itself, as Pixi's does. */
151
- readonly api?: unknown;
152
95
  }
153
96
 
154
97
  /** A live, mounted canvas world. The host owns the canvas and lifecycle; the
@@ -161,23 +104,20 @@ export interface MountedCanvasRoot extends MountedRootBase {
161
104
  readonly substrate: MountedCanvasSubstrate;
162
105
  }
163
106
 
164
- /** A live, mounted react world (T6.2) — the react analog of
165
- * {@link MountedThreeRoot}. `container` is the DOM-root layer the host
166
- * handed the adapter's `mount` (the SAME element `RootInstance.reactRoot()`
167
- * returns) — a react world's tree renders into it via `createRoot`. */
107
+ /** A mounted DOM world. Its container is the same host-owned element supplied to mount. */
168
108
  export interface MountedReactRoot extends MountedRootBase {
169
109
  readonly kind: 'dom';
170
110
  readonly container: HTMLElement;
171
111
  }
172
112
 
173
113
  /** Every kind of live, mounted world. */
174
- export type MountedRoot = MountedThreeRoot | MountedCanvasRoot | MountedReactRoot;
114
+ export type MountedRoot = MountedStageRoot | MountedCanvasRoot | MountedReactRoot;
175
115
 
176
116
  /** Map a {@link AdapterSurface} to its mounted-world shape — lets generic
177
117
  * code over
178
118
  * `K extends AdapterSurface` name the right surface without a manual union. */
179
- export type MountedRootFor<K extends AdapterSurface> = K extends 'three'
180
- ? MountedThreeRoot
119
+ export type MountedRootFor<K extends AdapterSurface> = K extends 'stage'
120
+ ? MountedStageRoot
181
121
  : K extends 'canvas'
182
122
  ? MountedCanvasRoot
183
123
  : K extends 'dom'
@@ -185,27 +125,13 @@ export type MountedRootFor<K extends AdapterSurface> = K extends 'three'
185
125
  : never;
186
126
 
187
127
  /**
188
- * The interface every root implements to run on the host. Generic over
189
- * {@link AdapterSurface} so `mount` receives and returns its native surface
190
- * types. Bare `RootAdapter` denotes the first-party Three surface.
191
- *
192
- * `K` parameterizes both halves of `mount`: `HostContextFor<K>` selects the
193
- * host context and `MountedRootFor<K>` selects the mounted result. Production
194
- * adapters are genuine `RootAdapter<K>` subtypes —
195
- * `Pixi2DRootAdapter extends RootAdapter<'canvas'>`, `ReactRootAdapter extends
196
- * RootAdapter<'dom'>` (`runtime/create-runtime.ts`).
197
- */
198
- /**
199
- * The `= 'three'` default is type-level only: an author who writes bare
200
- * `RootAdapter` for
201
- * a pixi root gets a compile error on `mount`'s return type, because
202
- * `MountedRootFor<'three'>` demands `scene`/`camera` a `MountedCanvasRoot` has
203
- * not got.
128
+ * A root adapter's surface selects both its host context and mounted result. The type-level
129
+ * default is stage; a canvas or DOM adapter declares its own surface to receive that context.
204
130
  */
205
- export interface RootAdapter<K extends AdapterSurface = 'three'> {
131
+ export interface RootAdapter<K extends AdapterSurface = 'stage'> {
206
132
  /** Stable id (telemetry/registry/conformance). */
207
133
  readonly id: string;
208
- /** Build/start the game against the host context for ITS surface; return the
134
+ /** Build/start the project against the host context for ITS surface; return the
209
135
  * handle. */
210
136
  mount(host: HostContextFor<K>): Promise<MountedRootFor<K>>;
211
137
  }
@@ -215,11 +141,11 @@ export interface RootAdapter<K extends AdapterSurface = 'three'> {
215
141
  *
216
142
  * A resolver that turns a manifest root into a mountable
217
143
  * adapter handles every surface in one function, so its return type has to
218
- * span all three. Returning the bare union `RootAdapter<'three'> |
144
+ * span all three. Returning the bare union `RootAdapter<'stage'> |
219
145
  * RootAdapter<'canvas'> | RootAdapter<'dom'>` does NOT work — a union of
220
146
  * function types INTERSECTS its parameters, so `.mount(host)` becomes
221
147
  * uncallable with any single host value (no one object is simultaneously a
222
- * `ThreeHostContext`, a `CanvasHostContext` and a `DomHostContext`).
148
+ * `StageHostContext`, a `CanvasHostContext` and a `DomHostContext`).
223
149
  *
224
150
  * Carrying the tag ALONGSIDE the adapter fixes that structurally: this is a
225
151
  * discriminated union on `surface`, so `switch (resolved.surface)` narrows
@@ -234,10 +160,10 @@ export type SurfaceAdapterFor<K extends AdapterSurface> = {
234
160
 
235
161
  /** Every surface's {@link SurfaceAdapterFor}, as one discriminated union.
236
162
  * Written out member-by-member (rather than as a distributive conditional)
237
- * so `assertNever` sees exactly three variants and a hypothetical 4th
163
+ * so an exhaustive switch sees exactly three variants and a hypothetical 4th
238
164
  * `AdapterSurface` member fails to compile HERE, at the vocabulary, rather
239
165
  * than silently widening every consumer's exhaustiveness guard. */
240
166
  export type SurfaceAdapter =
241
- | SurfaceAdapterFor<'three'>
167
+ | SurfaceAdapterFor<'stage'>
242
168
  | SurfaceAdapterFor<'canvas'>
243
169
  | SurfaceAdapterFor<'dom'>;
@@ -5,7 +5,7 @@ import type {
5
5
  MountedCanvasRoot,
6
6
  MountedReactRoot,
7
7
  MountedRootBase,
8
- MountedThreeRoot,
8
+ MountedStageRoot,
9
9
  RootAdapter,
10
10
  RootStateObserver,
11
11
  } from './root-adapter';
@@ -24,12 +24,10 @@ export const MOUNTED_ROOT_BASE_SHAPE = defineSeamShape<MountedRootBase>()({
24
24
  setPaused: { optional: true, kind: 'function', required: 'effect' },
25
25
  step: { optional: true, kind: 'function', required: 'effect' },
26
26
  resize: { optional: true, kind: 'function', required: 'effect' },
27
- hitTest: { optional: true, kind: 'function', required: 'operation' },
28
27
  dispose: { optional: false, kind: 'function', required: 'effect' },
29
28
  disposeComplete: { optional: true, kind: 'value', required: 'effect' },
30
29
  authoring: { optional: true, kind: 'value', required: 'operation' },
31
30
  systems: { optional: true, kind: 'value', required: 'operation' },
32
- systemScope: { optional: true, kind: 'value', required: 'operation' },
33
31
  observe: { optional: true, kind: 'value', required: 'effect' },
34
32
  });
35
33
 
@@ -38,15 +36,14 @@ export const ROOT_STATE_OBSERVER_SHAPE = defineSeamShape<RootStateObserver>()({
38
36
  snapshot: { optional: false, kind: 'function', required: 'operation' },
39
37
  });
40
38
 
41
- type ThreeSurface = Omit<MountedThreeRoot<object, object>, keyof MountedRootBase>;
39
+ type StageSurface = Omit<MountedStageRoot<object, object>, keyof MountedRootBase>;
42
40
  type CanvasSurface = Omit<MountedCanvasRoot, keyof MountedRootBase>;
43
41
  type DomSurface = Omit<MountedReactRoot, keyof MountedRootBase>;
44
42
 
45
- export const MOUNTED_THREE_SURFACE_SHAPE = defineSeamShape<ThreeSurface>()({
43
+ export const MOUNTED_STAGE_SURFACE_SHAPE = defineSeamShape<StageSurface>()({
46
44
  kind: { optional: false, kind: 'value', required: 'shape' },
47
45
  scene: { optional: false, kind: 'value', required: 'effect' },
48
46
  camera: { optional: false, kind: 'value', required: 'effect' },
49
- rendererConfig: { optional: true, kind: 'value', required: 'shape' },
50
47
  });
51
48
 
52
49
  export const MOUNTED_CANVAS_SURFACE_SHAPE = defineSeamShape<CanvasSurface>()({
@@ -1,35 +1,18 @@
1
1
  /**
2
- * WHICH ROOT OWNS A POINT — the pure half of D5 §2a, a rule about how several
3
- * adapter roots share one surface, so it belongs beside the other seam
4
- * contracts rather than inside the runtime that first needed it.
5
- *
6
- * Two callers with nothing else in common depend on it: the engine's
7
- * `runtime/input-router.ts`, which wires it to real canvases and forwards
8
- * claimed events; and the EDITOR, which asks the same question at design time
9
- * with no game running (`authoring/layered-pick.ts` picks through stacked
10
- * authoring layers, `authoring/design-time-layers.ts` orders the layers it
11
- * mounts). That second caller is why the file moved here 2026-09-18
12
- * (WORK.md §The open-source launch, phase 1 unit 7): the host may import the
13
- * adapter CONTRACT and must not import the game runtime, and this was the one
14
- * `runtime/` module left in its closure. No DOM, no canvas, no mount.
2
+ * Pure stacking and point-ownership rules for adapter roots. Runtime and
3
+ * design-time authoring use the same z-order decisions without depending on
4
+ * DOM, canvas or mounting implementations.
15
5
  */
16
6
 
17
7
  /** The pure decision inputs — no DOM. */
18
8
  export interface ClaimEntry {
19
9
  readonly id: string;
20
10
  readonly zOrder: number;
21
- /**
22
- * Optional claim predicate over a point RELATIVE TO THE CONTAINER. Absent
23
- * means: "this world claims only if it is the bottom (lowest zOrder)
24
- * world" — D5 §2a's stated default ("transparent upper roots do not
25
- * claim, the bottom world claims everything").
26
- */
27
- readonly hitTest?: ((x: number, y: number) => boolean) | undefined;
28
11
  }
29
12
 
30
13
  /**
31
14
  * Bottom-to-top paint order: ascending `zOrder`, ties -> original array
32
- * order — the SAME rule `manifest/load.ts`'s `loadGameManifest` sorts
15
+ * order — the SAME rule `manifest/load.ts`'s `loadProjectManifest` sorts
33
16
  * roots by, so a world's DOM stacking position always matches its
34
17
  * manifest-resolved zOrder. The last element is the topmost (visually on
35
18
  * top / highest z-index).
@@ -40,28 +23,3 @@ export function stackOrder<T extends ClaimEntry>(entries: readonly T[]): T[] {
40
23
  .sort((a, b) => a.entry.zOrder - b.entry.zOrder || a.index - b.index)
41
24
  .map(({ entry }) => entry);
42
25
  }
43
-
44
- /**
45
- * Resolve which world's id claims a container-relative point `(x, y)` — D5
46
- * §2a: walk top-down (topmost first); the first world whose `hitTest`
47
- * returns true wins ("topmost-claim wins"). A world with no `hitTest` never
48
- * claims UNLESS it is the bottom-most world, which claims unconditionally
49
- * (or via its own `hitTest`, if it declares one) — "no-claim falls to
50
- * bottom". Returns `null` only when `entries` is empty.
51
- */
52
- export function resolveClaimingRoot(
53
- entries: readonly ClaimEntry[],
54
- x: number,
55
- y: number,
56
- ): string | null {
57
- if (entries.length === 0) return null;
58
- const order = stackOrder(entries); // bottom -> top
59
- for (let i = order.length - 1; i >= 1; i--) {
60
- const entry = order[i]!;
61
- if (entry.hitTest?.(x, y)) return entry.id;
62
- }
63
- // No upper world claimed (or there is only one world) -> the bottom world
64
- // claims — honoring its own hitTest if it declares one (rare: the bottom
65
- // world usually claims everything unconditionally per D5 §2a's default).
66
- return order[0]!.id;
67
- }
@@ -178,22 +178,6 @@ export class SeamEvidenceLedger {
178
178
  );
179
179
  }
180
180
 
181
- verdict(options: {
182
- readonly seam: string;
183
- readonly subject: string;
184
- readonly epoch: string;
185
- readonly required: SeamProofStage;
186
- readonly notApplicable?: string | undefined;
187
- }): SeamEvidenceVerdict {
188
- return gradeSeamEvidence({ ...options, receipts: this.#receipts });
189
- }
190
-
191
- clearSubject(subject: string): void {
192
- for (let index = this.#receipts.length - 1; index >= 0; index -= 1) {
193
- if (this.#receipts[index]?.subject === subject) this.#receipts.splice(index, 1);
194
- }
195
- }
196
-
197
181
  clear(): void {
198
182
  this.#receipts.length = 0;
199
183
  }