@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,33 +1,13 @@
1
1
  /**
2
- * AuthoringAdapter — the editor's authoring contract. The editor talks to THIS,
3
- * keyed by opaque string node ids, instead of to a concrete document format.
2
+ * Authoring providers expose document operations through opaque node ids. Each
3
+ * integration owns its model and adapter; the editor consumes these interfaces
4
+ * and declared capabilities without selecting an implementation by type.
4
5
  *
5
- * An implementer's document model is its own private business; an ingested
6
- * game's adapter implements the same
7
- * providers directly over its live `Object3D` tree. The editor sees only the
8
- * interface and the advertised `capabilities` — it never branches on which
9
- * implementer it is talking to.
10
- *
11
- * The interface lives in the engine (not the editor) so `MountedThreeRoot.authoring`
12
- * can reference it without the engine depending on the editor; implementers live
13
- * in `packages/editor/src/authoring/`.
14
- *
15
- * **Reserved inspector paths convention:** the property paths `name`, `visible`,
16
- * `locked`, `renderVisible` and `exclude` are reserved — an adapter that
17
- * supports them exposes them through the ordinary `InspectorProvider.get`/`set`
18
- * (no separate provider). The shell renders the hierarchy panel's rename field,
19
- * eye (visibility) toggle, lock toggle, render-camera toggle and
20
- * exclude checkbox against these paths, generically, for any adapter that
21
- * reports them via `InspectorProvider.properties`.
22
- *
23
- * A COLUMN FOLLOWS WHAT THE ADAPTER ANSWERS, never a chrome knob (owner ruling,
24
- * 2026-09-19). `renderVisible` and `exclude` are answered by almost nothing —
25
- * three.js has ONE `Object3D.visible` governing viewport and render alike, and
26
- * nothing but a view layer has an exclusion — so their columns are absent or a
27
- * reserved blank cell, and appear the moment an adapter whose truth really does
28
- * separate them says so. `exclude` and `renderVisible` are reported in the
29
- * sense their name carries: `exclude: true` means EXCLUDED, `renderVisible:
30
- * false` means hidden from renders.
6
+ * Reserved inspector paths `name`, `visible`, `locked`, `renderVisible` and
7
+ * `exclude` use ordinary InspectorProvider properties/get/set. Hierarchy columns
8
+ * follow the properties the adapter reports. `exclude: true` means excluded;
9
+ * `renderVisible: false` means hidden from renders. An adapter with one shared
10
+ * visibility flag need not invent separate render visibility or exclusion.
31
11
  */
32
12
 
33
13
  import type { Transform } from './transform';
@@ -48,7 +28,7 @@ export interface AuthoringCapabilities {
48
28
 
49
29
  /**
50
30
  * The adapter's own answer to "what is truth behind these rows, and how
51
- * writable is it" (spec 29 §5). Provenance never varies WITHIN one adapter's
31
+ * writable is it". Provenance never varies WITHIN one adapter's
52
32
  * rows — it is a property of the world boundary — so it is declared ONCE per
53
33
  * adapter and rendered at the SEAM (the composite's `world:<id>` group row,
54
34
  * or the panel header for a bare adapter), never per row. Only the adapter
@@ -62,11 +42,11 @@ export interface AuthoringProvenance {
62
42
  /**
63
43
  * What the rows project:
64
44
  * - `document` — an authored data file the editor owns (e.g. a scene file);
65
- * - `source-code` — the game's own source is the document (e.g. JSX);
45
+ * - `source-code` — the project's own source is the document (e.g. JSX);
66
46
  * - `foreign` — a live tree the editor does not own (an unmodified external
67
- * game, or any world edited only through an overlay); edits go to the
47
+ * project, or any world edited only through an overlay); edits go to the
68
48
  * overlay, never the source;
69
- * - `live` — a running game adopted at play time; ordinary edits are not persisted;
49
+ * - `live` — a running project adopted at play time; ordinary edits are not persisted;
70
50
  * - `boundary` — a declared world with no live editing surface here.
71
51
  */
72
52
  source: 'document' | 'source-code' | 'foreign' | 'live' | 'boundary';
@@ -82,7 +62,7 @@ export interface AuthoringProvenance {
82
62
  * Semantic place a node occupies in the universal authoring hierarchy.
83
63
  *
84
64
  * `kind` below remains the adapter's native kind (`mesh`, `button`,
85
- * `pixi-container`, ...). `role` says what the row MEANS to the editor shell,
65
+ * `sprite`,...). `role` says what the row MEANS to the editor shell,
86
66
  * so presentation and interaction never have to infer semantics from an id,
87
67
  * label, or substrate-specific kind string.
88
68
  */
@@ -99,11 +79,13 @@ export type EditorNodeRole =
99
79
 
100
80
  /** A node in the authoring hierarchy — format-neutral. */
101
81
  export interface EditorNode {
102
- /** STABLE id — survives reload (see ingest structural-path ids). */
82
+ /** Stable node id that survives reload. */
103
83
  id: string;
104
84
  label: string;
105
- /** Adapter-declared semantic role. Optional for third-party/older adapters;
106
- * the shell falls back to the native `kind` only for icon selection. */
85
+ /**
86
+ * Optional semantic role. Icon selection uses the native `kind` when no role
87
+ * is declared.
88
+ */
107
89
  role?: EditorNodeRole;
108
90
  /** Optional source-owned context kept visually subordinate to `label`, such
109
91
  * as `<header>` beneath a React component name or a document's file path. */
@@ -121,16 +103,14 @@ export interface EditorNode {
121
103
  childIds: string[];
122
104
  /**
123
105
  * THIS ADAPTER'S OWN DEFAULT OPEN STATE for this row, overriding the shell's
124
- * shape heuristic (`GameHierarchy.tsx`'s `defaultExpansionByNode`, which
106
+ * shape heuristic (`Hierarchy.tsx`'s `defaultExpansionByNode`, which
125
107
  * infers openness from authored depth and branch shape).
126
108
  *
127
109
  * It exists because one adapter genuinely knows better than any shape rule
128
- * can: Blender's Outliner has an explicit per-element flag
129
- * (`outliner_tree.cc:139` makes every fresh element CLOSED; only the Scene
130
- * Collection and the layer collections clear it,
131
- * `tree_display_view_layer.cc:130,167`), so "collections open, objects
132
- * closed" is a fact about the tree rather than a guess about it. Omitted —
133
- * which is every other adapter — leaves the heuristic exactly as it was.
110
+ * can: a tree whose own model stores an explicit per-element open flag
111
+ * answers "collections open, objects closed" as a fact about the tree rather
112
+ * than a guess about it. Omitted — which is every adapter without such a
113
+ * flag — leaves the heuristic exactly as it was.
134
114
  */
135
115
  defaultExpanded?: boolean;
136
116
  /** Opaque project-owned identity shared by authored rows across adapter roots. */
@@ -160,15 +140,9 @@ export interface PropertyDescriptor {
160
140
  readonlyReason?: string;
161
141
  options?: unknown[];
162
142
  /**
163
- * Optional domain-shaped grouping (T3.4 slice 2): properties sharing the
164
- * same `group` label render together under a titled sub-section in the
165
- * generic inspector, instead of the flat property grid. Backward-compatible
166
- * — omitted (or two descriptors with different/no `group`) renders exactly
167
- * as before this field existed. This is the ONLY authoring-richness
168
- * addition v1 makes; there is no schema and no new PropertyDescriptor
169
- * `type` — a custom adapter reports its own domain concepts
170
- * (health/score/an enum, …) through the SAME provider, merely labeled into
171
- * a named group.
143
+ * Optional domain grouping. Properties with the same `group` label render in a
144
+ * named subsection of the generic inspector. Ungrouped properties retain the
145
+ * flat grid; grouping does not change descriptor types or write semantics.
172
146
  */
173
147
  group?: string;
174
148
  /**
@@ -216,18 +190,18 @@ export interface HierarchyProvider {
216
190
  */
217
191
  crossSurfaceStructureSignature?(): string | null;
218
192
  /**
219
- * OPTIONAL, THREE-SURFACE — the live scene object for raycast/gizmo binding
220
- * (null for a node this adapter has no object for), opaque here: its Three
221
- * typing is `@volter/editor-threejs`'s `threeHierarchy`. A Pixi, DOM or React
193
+ * OPTIONAL, STAGE-SURFACE — the live scene object for raycast/gizmo binding
194
+ * (null for a node this adapter has no object for), opaque here: its native
195
+ * typing is the 3D integration's. A canvas, DOM or React
222
196
  * adapter has no scene object at all: it OMITS this method rather than
223
197
  * implementing a `() => null` stub that pretends the concept applies. Callers
224
198
  * already treat `null` as "no object here", so absence and `null` mean the
225
- * same thing to them — optional-chain it (`hierarchy.object3D?.(id) ?? null`).
199
+ * same thing to them — optional-chain it (`hierarchy.nativeObject?.(id) ?? null`).
226
200
  */
227
- object3D?(id: string): unknown;
228
- /** OPTIONAL, THREE-SURFACE — inverse of {@link object3D}. Same rule: an
201
+ nativeObject?(id: string): unknown;
202
+ /** OPTIONAL, STAGE-SURFACE — inverse of {@link nativeObject}. Same rule: an
229
203
  * adapter with no scene-object tree omits it. */
230
- idForObject3D?(o: unknown): string | null;
204
+ idForNativeObject?(o: unknown): string | null;
231
205
  }
232
206
 
233
207
  export interface SelectionProvider {
@@ -236,9 +210,11 @@ export interface SelectionProvider {
236
210
  * already name a real node (for example a folded-internal light inventory);
237
211
  * the default keeps the adapter's normal semantic component resolution. */
238
212
  set(ids: string[], options?: { intent?: 'semantic' | 'exact' }): void;
239
- /** Resolve a raw native/render hit to the semantic authoring subject. The
240
- * shell must not infer ownership from ancestry or substrate-specific kinds.
241
- * Older adapters may omit this; raw ids then remain the selection ids. */
213
+ /**
214
+ * Resolve a native/render hit to its semantic authoring subject. The shell
215
+ * does not infer ownership from ancestry or substrate-specific kinds.
216
+ * Without this optional resolver, raw ids are the selection ids.
217
+ */
242
218
  resolve?(
243
219
  rawId: string,
244
220
  options?: { intent?: 'normal' | 'deep'; scopeId?: string | null },
@@ -274,16 +250,9 @@ export interface TransformEditability {
274
250
  * protocol's removal door refuses by name instead of acking a revert it did
275
251
  * not perform.
276
252
  *
277
- * EACH HALF IS ANSWERED BY WHOEVER CAN SEE IT, and a lane that cannot read
278
- * its own document may not invent the first. A source lane holding the
279
- * file's text answers both (the first-party R3F lane reads the callsite's
280
- * attributes before saying yes). A lane whose client holds only an
281
- * oid → file:line index and writes through a server that resolves the
282
- * address — the vendored-ingest lane — can honestly answer only the SECOND,
283
- * so that is what it answers, and `remove`'s own {@link WriteAck} reports
284
- * whether an attribute was actually dropped (`persisted: false` when the
285
- * callsite carried none). The inverse is what is forbidden on every lane:
286
- * acking a drop that took no byte.
253
+ * A provider reads its own document before declaring an override. A client with only a
254
+ * source address leaves the decision to its writer; the removal's WriteAck reports whether
255
+ * bytes were removed, with persisted: false when the document carried no override.
287
256
  */
288
257
  removable?: boolean;
289
258
  }
@@ -362,7 +331,7 @@ export interface InspectorProvider {
362
331
  /**
363
332
  * Optional — SHOW a value without writing it, for a control the user drags.
364
333
  *
365
- * The gizmo already has this pair ({@link TransformProvider.writeTransform}
334
+ * The gizmo already has this pair ({@link TransformProvider.apply}
366
335
  * live, {@link TransformProvider.endEdit} persisting), and a colour picker
367
336
  * needs the same shape: it streams values continuously while the pointer
368
337
  * moves, and persisting each one is a write burst the absorb cycle cannot
@@ -392,7 +361,7 @@ export interface InspectorProvider {
392
361
  * edit/revert round trip and grades UNVERIFIABLE rather than passing.
393
362
  *
394
363
  * SAME ACK CONTRACT AS {@link set} — the pipe's per-edit {@link WriteAck}
395
- * (`editor/src/authoring/write-pipe.ts`), awaited. Removal is a write; it
364
+ * awaited. Removal is a write; it
396
365
  * moves bytes, and the caller has the same right to be told where they went
397
366
  * and whether they moved. Returning nothing means this provider performed no
398
367
  * persisted write, and the shell reports the live-only floor.
@@ -406,36 +375,6 @@ export interface InspectorProvider {
406
375
  remove?(id: string, path: string): void | WriteAck | Promise<void | WriteAck>;
407
376
  }
408
377
 
409
- /**
410
- * An authored node that is itself an asset, even when its bytes are embedded
411
- * in another source document instead of living at a standalone project path.
412
- *
413
- * The first concrete case is inline SVG in a DOM/React root: its `<path>` and
414
- * `<g>` implementation is no more part of the authoring hierarchy than the
415
- * meshes inside an imported model. The adapter owns the native-to-asset
416
- * conversion; the shell only opens the returned image in its ordinary Asset
417
- * Editor document.
418
- */
419
- export interface AuthoringAssetSubject {
420
- readonly kind: 'image';
421
- readonly name: string;
422
- readonly mediaType: 'image/svg+xml';
423
- readonly text: string;
424
- /** Project-root-relative source definition, when the adapter can prove it. */
425
- readonly sourcePath?: string;
426
- }
427
-
428
- export interface AssetSubjectProvider {
429
- get(id: string): AuthoringAssetSubject | null;
430
- /**
431
- * Assets known to this authoring document even when they are not in its
432
- * currently focused hierarchy projection. React story boards use this to
433
- * expose SVGs mounted in inactive story frames without flattening those
434
- * frames into the active hierarchy.
435
- */
436
- entries?(): ReadonlyArray<{ id: string; subject: AuthoringAssetSubject }>;
437
- }
438
-
439
378
  /** A semantic jump from one authored subject to another document/subject.
440
379
  * The adapter owns resolution and opening; the inspector only presents the
441
380
  * title and exposes the same verb to human and serialized projections. */
@@ -502,8 +441,7 @@ export interface ComponentInstancesProvider {
502
441
  * `void` is the honest answer for a lane whose verb performs no write of its
503
442
  * own (an in-memory adapter mutating a graph it already owns); a lane that
504
443
  * writes returns the persistence pipe's own {@link WriteAck}
505
- * (`editor/src/authoring/write-pipe.ts`), awaited, so a caller that awaits the
506
- * verb has awaited the byte.
444
+ * awaited, so a caller that awaits the verb has awaited the byte.
507
445
  *
508
446
  * A structural verb resolves on ITS OWN verb: `remove` asks whether the struct
509
447
  * writer is bound, never whether the PROP writer is — resolving on a
@@ -537,10 +475,7 @@ export type StructuralWriteOutcome = void | WriteAck | Promise<void | WriteAck>;
537
475
  * `ack: undefined` (no write was attempted, which is exactly what `void` means
538
476
  * in {@link StructuralWriteOutcome}).
539
477
  *
540
- * Firing the write and dropping the promise on the floor — `void structOp(…)`
541
- * behind a `return ''` — is the shape this type exists to make unrepresentable:
542
- * it left the caller unable to await the byte, which is how N same-file
543
- * duplicates went out together, each reading the file before any wrote back.
478
+ * Callers await ack before starting another write to the same file.
544
479
  */
545
480
  export interface StructuralIdWrite<Id extends string | null = string> {
546
481
  /** The id the op produced — `''` when the lane cannot know it until its own
@@ -583,8 +518,7 @@ export interface StructureProvider {
583
518
  * multi-delete's writes land strictly one at a time (see that function's own
584
519
  * doc comment for why serialization + deletion order together are what make
585
520
  * a per-id loop sound for an adapter whose OID index isn't reindexed between
586
- * writes). An adapter with a synchronous/in-memory remove (e.g.
587
- * `UIAuthoringAdapter`) returns `void` — `await`ing it is a harmless no-op.
521
+ * writes). An adapter with a synchronous in-memory remove can return void.
588
522
  *
589
523
  * A source-writing lane returns THIS removal's own {@link WriteAck} (see
590
524
  * {@link StructuralWriteOutcome}).
@@ -613,12 +547,12 @@ export interface StructureProvider {
613
547
  * an adapter-specific affordance outside the generic palette).
614
548
  */
615
549
  creatableKinds?(parentId: string | null): CreatableKind[];
616
- /** D3 (spec 27 §6) — wrap `id` in a new container element (default tag
550
+ /** wrap `id` in a new container element (default tag
617
551
  * adapter-chosen, e.g. a `div`), re-parenting `id` as that container's sole
618
552
  * child. Absent ⇒ the shell's context menu shows no Wrap item for this
619
553
  * adapter. */
620
554
  wrap?(id: string, wrapperTag?: string): StructuralWriteOutcome;
621
- /** D3 (spec 27 §6) — replace `id` with its own children (the inverse of
555
+ /** replace `id` with its own children (the inverse of
622
556
  * `wrap`). Absent ⇒ the shell's context menu shows no Unwrap item for this
623
557
  * adapter. */
624
558
  unwrap?(id: string): StructuralWriteOutcome;
@@ -636,25 +570,23 @@ export interface StructureProvider {
636
570
  /** Preflight for an Ungroup affordance. Omit when ungroup is never offered. */
637
571
  canUngroup?(id: string): boolean;
638
572
  /**
639
- * D4.R2 (spec27 §6 D4 reopen) — remove every id in `ids` as ONE undoable
573
+ * remove every id in `ids` as ONE undoable
640
574
  * op (a single Ctrl+Z restores the whole batch), instead of the caller
641
575
  * looping `remove(id)` per id (which — one `remove` call = one undo push
642
576
  * per adapter, by design — produces N separate undo entries: Ctrl+Z then
643
577
  * restores them one at a time, asymmetric with the first-party adapter's
644
578
  * own single batched multi-delete undo). Absent ⇒ the shell falls back to
645
- * the per-id `remove` loop (today's N-entry behavior, unchanged) — an
646
- * honest degrade, not a silent behavior change, for any adapter that
579
+ * the per-id `remove` loop — an honest degrade for any adapter that
647
580
  * hasn't implemented batching. May optionally return an awaitable, same
648
581
  * widening `remove` documents above ({@link StructuralWriteOutcome}) and for
649
- * the same reason (delete-order-residual fix): `deleteSelection`
582
+ * the same reason: `deleteSelection`
650
583
  * (`editor-hotkeys.ts`) awaits it so the caller can rely on the whole
651
584
  * batch's write having landed before it returns. A synchronous/in-memory
652
- * implementation (e.g. `UIAuthoringAdapter`'s) returns `void` — awaiting it
653
- * is a harmless no-op.
585
+ * implementation can return void.
654
586
  */
655
587
  removeMany?(ids: readonly string[]): StructuralWriteOutcome;
656
588
  /** Copy authored entities through the host clipboard. The adapter owns the
657
- * native payload (JSX for a source-backed Three/Canvas world, native records
589
+ * native payload (JSX for a source-backed stage or canvas world, native records
658
590
  * for a data-backed adapter); the shell never serializes an engine-private
659
591
  * shape. */
660
592
  copy?(ids: readonly string[]): boolean | Promise<boolean>;
@@ -691,30 +623,21 @@ export interface PersistenceProvider {
691
623
  * Human/agent-readable destination this provider persists to — e.g.
692
624
  * `"src/world.tsx"`, `"live-only (not saved)"`, or `"ephemeral
693
625
  * (discarded on stop)"`. Drives the save-status UI and makes routing
694
- * inspectable (design §2). Implementers whose destination can change during
626
+ * inspectable. Implementers whose destination can change during
695
627
  * the session (a provider that tracks the currently focused document, say)
696
628
  * should expose this as a live getter rather than a value captured once.
697
629
  */
698
630
  readonly destination: string;
699
631
  /**
700
- * The reload contract (design §5): apply an external change to this
701
- * provider's persisted artifact into the RUNNING session (e.g. a file-watcher
702
- * update to the artifact). Absent ⇒ the host must remount to
703
- * pick up external changes — the honest floor, and correctly what the
704
- * ephemeral provider reports.
705
- *
706
- * `rawContent`, when the host has it (the file-watcher SSE payload carries the
707
- * artifact's exact bytes), is an OPTIONAL second parameter enabling own-echo
708
- * detection — an event that is exactly what this provider itself last wrote is
709
- * its own save reflected back, and should be skipped even outside the normal
710
- * debounce window (see `EditorStore.applyExternalUpdate`'s doc comment). This
711
- * stays optional/provider-specific — only the first-party provider currently
712
- * uses it — so other implementers can ignore the parameter entirely.
632
+ * Optional adapter hook for applying an external artifact change to the
633
+ * running session. The adapter owns delivery of these changes and chooses
634
+ * whether to update in place or remount. A caller can supply exact artifact
635
+ * bytes in `rawContent` for provider-specific own-write detection.
713
636
  */
714
637
  applyExternal?(content: unknown, rawContent?: string): void;
715
638
  }
716
639
 
717
- /** D12 — per-layer viewport picking. Coordinates are client (browser) px. */
640
+ /** per-layer viewport picking. Coordinates are client (browser) px. */
718
641
  export interface PickProvider {
719
642
  pick(clientX: number, clientY: number): string | null;
720
643
  /**
@@ -727,7 +650,7 @@ export interface PickProvider {
727
650
  candidates?(clientX: number, clientY: number, options?: { includeLocked?: boolean }): readonly string[];
728
651
  }
729
652
 
730
- /** D4 — storybook stories. */
653
+ /** Storybook stories. */
731
654
  export interface StoryRef {
732
655
  id: string;
733
656
  label: string;
@@ -736,57 +659,39 @@ export interface StoriesProvider {
736
659
  storiesFor(nodeId: string): StoryRef[]; // [] = none
737
660
  active(nodeId: string): string | null;
738
661
  apply(nodeId: string, storyId: string | null): void; // null clears
739
- /** Render ONLY this node against the story (storybook canvas); null exits. */
740
- isolate?(nodeId: string | null, storyId?: string): void;
741
- /**
742
- * Display vocabulary for this provider's section — e.g. an ingested game's
743
- * contract scenes say "Scenes"; absent means the default "Stories". The
744
- * states are the same seam either way; only the word the human reads
745
- * changes, so a game's own screens are never labelled with a design-time
746
- * term the game never uses.
747
- */
748
- readonly title?: string;
749
662
  /**
750
663
  * Why this provider could not enumerate, or `null` when it did. An empty
751
664
  * `storiesFor` and an UNREAD one look identical from the section that draws
752
665
  * them, and only the provider knows which it is — so the PROVIDER answers,
753
666
  * and the shell never reaches past it into one particular provider's own
754
- * discovery to find out (a CSF registry, a game contract's screen list).
667
+ * discovery to find out (a CSF registry, a project contract's screen list).
755
668
  * Absent means the provider always ran.
756
669
  */
757
670
  unavailable?(): string | null;
758
671
  }
759
672
 
760
673
  /**
761
- * Where a drop landed, when the shell knows. The VIEWPORT knows a world point
762
- * (its ground-plane raycast under the cursor); the hierarchy panel does not,
763
- * and passes nothing. Optional both ways: an adapter with no placement concept
764
- * ignores it, and a caller with no point omits it — so neither side has to
765
- * pretend. Coordinates are world-space, in the adapter's own units.
674
+ * What a drop places: a project component, named by its source module and
675
+ * export, and the surface it mounts on. The project's native source remains
676
+ * truth.
766
677
  */
767
678
  export interface AssetDropContext {
768
- readonly position?: readonly [number, number, number];
769
- /** Transient drag metadata. The project's native source remains truth; this
770
- * only tells an adapter whether the path names a file or a source component. */
771
- readonly item?:
772
- | { readonly kind: 'file'; readonly name: string }
773
- | {
774
- readonly kind: 'component';
775
- readonly name: string;
776
- readonly sourcePath: string;
777
- readonly exportKind: 'default' | 'named';
778
- readonly surface: 'three' | 'canvas' | 'dom' | 'unknown';
779
- };
679
+ readonly item: {
680
+ readonly name: string;
681
+ readonly sourcePath: string;
682
+ readonly exportKind: 'default' | 'named';
683
+ readonly surface: 'stage' | 'canvas' | 'dom' | 'unknown';
684
+ };
780
685
  }
781
686
 
782
- /** Asset drop (hierarchy + viewport). */
687
+ /** Component drop (the canvas Add menu's project components). */
783
688
  export interface AssetDropProvider {
784
- accepts(nodeId: string, assetPath: string, context?: AssetDropContext): boolean;
785
- /** A drop that lands as an ELEMENT in the game's own source is a structural
689
+ accepts(nodeId: string, assetPath: string, context: AssetDropContext): boolean;
690
+ /** A drop that lands as an ELEMENT in the project's own source is a structural
786
691
  * write, so it answers with the same {@link StructuralWriteOutcome} the rest
787
692
  * of {@link StructureProvider}'s writing verbs do — a lane whose drop only
788
693
  * mutates the running graph returns `void`. */
789
- drop(nodeId: string, assetPath: string, context?: AssetDropContext): StructuralWriteOutcome;
694
+ drop(nodeId: string, assetPath: string, context: AssetDropContext): StructuralWriteOutcome;
790
695
  }
791
696
 
792
697
  /** Plain-object rect shape shared by {@link RectProvider} — the same fields a real
@@ -799,11 +704,11 @@ export interface DOMRectLike {
799
704
  height: number;
800
705
  }
801
706
 
802
- /** Per-node screen geometry — the KEYSTONE omission today (nothing produces rects the
803
- * ported inspect.ts math consumes). Coordinates are **host-relative** px: relative to
804
- * the adapter's own mounted world surface (its `position:absolute; inset:0` layer), the
805
- * same surface the A3 selection overlay is hosted over, so the overlay can draw against
806
- * these rects directly. (NOT viewport-client — the JSDoc formerly said so in error.) */
707
+ /**
708
+ * Per-node screen geometry in host-relative pixels: relative to the adapter’s
709
+ * mounted surface, where the selection overlay draws. These are not viewport
710
+ * client coordinates.
711
+ */
807
712
  export interface RectProvider {
808
713
  /** selectable node id → its current bounding rect, or null if unmounted/offscreen */
809
714
  rect(id: string): { x: number; y: number; width: number; height: number } | null;
@@ -814,7 +719,7 @@ export interface RectProvider {
814
719
  paddingBox?: DOMRectLike;
815
720
  };
816
721
  /**
817
- * D3 (spec 27 §6) — every currently-empty container this adapter's tree
722
+ * every currently-empty container this adapter's tree
818
723
  * contains right now: no visible children, no text, a collapsed dimension
819
724
  * (the `ui-source/inspect.ts:findEmptyContainers` math) — each with a
820
725
  * grown-to-tappable placeholder rect and a display name, driving the
@@ -881,11 +786,10 @@ export interface BoxEditProvider {
881
786
  referencePoint?(id: string): BoxEditReferencePoint | null;
882
787
  }
883
788
 
884
- /** Text-content editing, ON the contract: the react-world adapter implements this
885
- * provider (`react-world-authoring-adapter.ts`'s `readonly text: TextProvider`),
886
- * bringing its own `editText(id)` write path onto the seam, so the overlay's
887
- * double-click-to-edit-text (D3) is contract-driven rather than reaching for a
888
- * concrete adapter's method. */
789
+ /**
790
+ * Text-content editing through the authoring contract. The overlay delegates
791
+ * in-place text edits to the owning adapter through this provider.
792
+ */
889
793
  export interface TextProvider {
890
794
  /** the element's editable pure-text, or null if the body is dynamic/has child elements */
891
795
  get(id: string): string | null;
@@ -893,7 +797,7 @@ export interface TextProvider {
893
797
  }
894
798
 
895
799
  /**
896
- * D3 (spec 27 §6) — background-color sampling at a viewport point, for the
800
+ * background-color sampling at a viewport point, for the
897
801
  * eyedropper's FALLBACK path (browsers with no native `EyeDropper` API). The
898
802
  * native API, where available, samples real rendered pixels itself and needs
899
803
  * none of this. Distinct from {@link PickProvider} (which returns a node id,
@@ -912,23 +816,23 @@ export interface ColorSampleProvider {
912
816
  }
913
817
 
914
818
  /**
915
- * Where a node's live object CAME FROM in the game's own truth.
819
+ * Where a node's live object CAME FROM in the project's own truth.
916
820
  *
917
821
  * `display` is the string the editor shows verbatim. The unanchored shape
918
822
  * carries a `reason` rather than a blank, because "we don't know" and "a
919
823
  * library built it" are different facts and neither may present as silence
920
824
  * (the honest-floor rule).
921
825
  *
922
- * TWO ANCHORED KINDS, because a game has two kinds of truth and only one of
826
+ * TWO ANCHORED KINDS, because a project has two kinds of truth and only one of
923
827
  * them is source.
924
828
  *
925
829
  * - `source` is the original: a `new` expression at a `file:line:col` in the
926
- * game's own code, which is where an edit to a constructor literal has to be
830
+ * project's own code, which is where an edit to a constructor literal has to be
927
831
  * written.
928
- * - `data` is the sibling a level-based game needs. Its placed cargo — the
832
+ * - `data` is the sibling a level-based project needs. Its placed cargo — the
929
833
  * enemies, the pickups, the objectives — reaches no source literal at all;
930
- * it is binary records inside a level file that the game parses at load. An
931
- * authored move of one is therefore an edit to THE GAME'S OWN DATA, and
834
+ * it is binary records inside a level file that the project parses at load. An
835
+ * authored move of one is therefore an edit to THE PROJECT'S OWN DATA, and
932
836
  * the honest anchor is the data file plus the index of the record that
933
837
  * addresses the object inside it.
934
838
  *
@@ -946,7 +850,7 @@ export type NodeCreationSite =
946
850
  | {
947
851
  anchored: true;
948
852
  kind: 'data';
949
- /** Project-relative path of the game's own data file, POSIX separators. */
853
+ /** Project-relative path of the project's own data file, POSIX separators. */
950
854
  file: string;
951
855
  /** Index of the record inside that file which addresses this object. */
952
856
  record: number;
@@ -964,15 +868,14 @@ export type NodeCreationSite =
964
868
  * two are not the same fact — two nodes can both be `{ kind: 'source' }` at a
965
869
  * `file:line` and still be written by different lanes with different gates.
966
870
  *
967
- * Each kind's CORRECTNESS CONTRACT, and the named test that proves it:
871
+ * Each kind's persistence contract:
968
872
  *
969
- * - `source-prop` — the value is a literal PROP on an element of the game's own
873
+ * - `source-prop` — the value is a literal PROP on an element of the project's own
970
874
  * source (a JSX attribute the serve-time authoring stamp addresses). Lands at
971
875
  * the anchor; survives a cold remount, because the file is truth and the next
972
876
  * mount re-derives from it.
973
- * Proof: `packages/editor/test/oid-source-persistence.test.ts`.
974
877
  * - `source-structure` — the edit is not a VALUE at all: it adds, removes,
975
- * moves or re-nests an ELEMENT of the game's own source (`delete`,
878
+ * moves or re-nests an ELEMENT of the project's own source (`delete`,
976
879
  * `duplicate`, `wrap`/`unwrap`, `create`, `reorder`, `reparent`, `group`).
977
880
  * It is a kind rather than a flavour of `source-prop` because it resolves on
978
881
  * a DIFFERENT DOOR — the dialect's structural writer, not its attribute
@@ -983,13 +886,11 @@ export type NodeCreationSite =
983
886
  * does not have: the write is WHOLE-FILE and checksum-guarded, so an op
984
887
  * resolved against a stale element index must fail rather than land at the
985
888
  * wrong offset.
986
- * Proof: `packages/editor/test/structural-write-pipe.test.ts`.
987
889
  * - `construction-literal` — the value is a literal in the CONSTRUCTION
988
- * EXPRESSION that built the object (`new THREE.Mesh(…)` in the game's own
890
+ * EXPRESSION that built the object (a constructor call in the project's own
989
891
  * served module). Same landing/remount contract, plus one gate the prop lane
990
892
  * does not have: a site that constructed more than one object is refused,
991
893
  * because rewriting its literal would move all of them.
992
- * Proof: `packages/editor/test/ingest-source-persistence.test.ts`.
993
894
  * - `physics-binding` — the node's own transform props are DEAD: a simulated
994
895
  * body writes its matrix every frame from a spawn it reads elsewhere, so the
995
896
  * write lands at the literal that binding reads (the component callsite whose
@@ -999,20 +900,14 @@ export type NodeCreationSite =
999
900
  * body must have re-spawned at the AUTHORED pose, not at the old one. Being
1000
901
  * silently re-posed by the owner is this kind's whole failure mode, and the
1001
902
  * reason it is a kind rather than a flavour of `source-prop`.
1002
- * Proof: `packages/editor/test/r3f-physics-binding.test.ts`.
1003
- * - `data-record` — the value is a field of a RECORD in the game's own data
903
+ * - `data-record` — the value is a field of a RECORD in the project's own data
1004
904
  * file, addressed by index; no source literal mentions it. Lands in that
1005
- * file through the game's own declared writer; survives a cold remount for
905
+ * file through the project's own declared writer; survives a cold remount for
1006
906
  * the same reason source does.
1007
- * Proof: `packages/editor/test/ingest-source-persistence.test.ts`
1008
- * ("a level-data record is an anchor too").
1009
907
  * - `live-only` — there is no anchor. The edit lands on the running object for
1010
908
  * the session and NOTHING else, and the contract is that the surface SAYS SO:
1011
909
  * a write ack that claims a destination it did not reach is the lie this kind
1012
910
  * exists to keep nameable.
1013
- * Proof: `packages/editor/test/oid-source-persistence.test.ts`
1014
- * ("an object with no source stamp is refused by name")
1015
- * + `packages/editor/test/inspection-write-destination.test.ts`.
1016
911
  */
1017
912
  export type WriteAnchorKind =
1018
913
  | 'source-prop'
@@ -1023,29 +918,15 @@ export type WriteAnchorKind =
1023
918
  | 'live-only';
1024
919
 
1025
920
  /**
1026
- * WHAT ONE EDIT DID — the ack the persistence pipe returns for THAT edit, from
1027
- * the component that actually performed the write.
1028
- *
1029
- * It is deliberately NOT a property of an adapter, a session or a surface. The
1030
- * fact it carries is per-edit, because that is the only granularity at which it
1031
- * can be true: one composite may hold a source-backed three root beside a
1032
- * live-only DOM root, and one three root holds body-placed nodes beside
1033
- * ordinary JSX props. An answer computed from the SURFACE rather than from the
1034
- * edit reports whichever child happened to be persist-capable — measured on the
1035
- * vendored racing game, where a three-root edit acked the other root's
1036
- * destination with `persisted: true`.
1037
- *
1038
- * `persisted: false` is an ANSWER, never a failure: `live-only` is a real
1039
- * anchor kind and the contract of that kind is that the surface SAYS SO. What
1040
- * is forbidden is a `destination` no byte reached being reported with
1041
- * `persisted: true`.
921
+ * The persistence result for one edit, returned by the component that performed
922
+ * the write. Composite roots and nodes may have different destinations, so an
923
+ * adapter-wide destination cannot describe an individual edit.
1042
924
  *
1043
- * A provider that performs a write returns this; one that has no write to
1044
- * perform returns nothing and the shell reports the live-only floor. There is
1045
- * no third answer, and in particular there is no adapter-wide blanket
1046
- * destination standing in for one — {@link PersistenceProvider.destination}
1047
- * answers the different question "where do this surface's SAVES go", for the
1048
- * save-status chrome.
925
+ * `persisted: false` is a valid live-only result. `persisted: true` requires that
926
+ * bytes reached the reported destination. A provider with no write result
927
+ * returns nothing and the shell reports the live-only floor.
928
+ * {@link PersistenceProvider.destination} describes where saves go for status
929
+ * chrome; it does not substitute for a per-edit result.
1049
930
  */
1050
931
  export interface WriteAck {
1051
932
  /** Where THIS edit's bytes landed, in the writer's own words — or the
@@ -1103,7 +984,7 @@ export function emptyWriteAnchorKindCounts(): Record<WriteAnchorKind, number> {
1103
984
  * Present ⇒ every id gets an answer, anchored or reasoned.
1104
985
  */
1105
986
  export interface AuthoringTruth {
1106
- /** Where the runtime object came from in the game's own source or data. */
987
+ /** Where the runtime object came from in the project's own source or data. */
1107
988
  readonly site: NodeCreationSite;
1108
989
  /** The lane THIS property edit would actually take. `undefined` means the
1109
990
  * subject is synthetic or otherwise has no write plan; `live-only` is a real
@@ -1111,12 +992,8 @@ export interface AuthoringTruth {
1111
992
  readonly writeAnchorKind: WriteAnchorKind | undefined;
1112
993
  }
1113
994
 
1114
- /**
1115
- * The TRUTH-family binding. One resolution answers both where a subject came
1116
- * from and where an edit to one of its properties lands. A caller may not read
1117
- * those facts through separate providers: that was the classifier/writer split
1118
- * the persistence pipe exists to remove.
1119
- */
995
+ /** One truth resolution describes a subject's creation site and the
996
+ * destination of its property writes. */
1120
997
  export interface TruthProvider {
1121
998
  resolve(id: string, property: string): AuthoringTruth;
1122
999
  }
@@ -1188,7 +1065,7 @@ export interface SpatialDragHandle {
1188
1065
  }
1189
1066
 
1190
1067
  /** One adapter-owned component projection. `category` is a viewport overlay
1191
- * channel (for example `audio`), not a game-object kind; unknown categories
1068
+ * channel (for example `audio`), not a scene-object kind; unknown categories
1192
1069
  * obey the master Helpers toggle instead of being rejected. */
1193
1070
  export interface SpatialHandleLayer {
1194
1071
  readonly id: string;
@@ -1218,7 +1095,7 @@ export interface SpatialHandlesProvider {
1218
1095
 
1219
1096
  export interface AuthoringAdapter {
1220
1097
  readonly capabilities: AuthoringCapabilities;
1221
- /** Spec 29 §5 — seam-level provenance ("what is truth behind these rows").
1098
+ /** Seam-level provenance ("what is truth behind these rows").
1222
1099
  * Absent ⇒ the shell shows no provenance badge (degrade silently — never
1223
1100
  * fabricate a claim the adapter didn't make). */
1224
1101
  readonly provenance?: AuthoringProvenance;
@@ -1226,57 +1103,42 @@ export interface AuthoringAdapter {
1226
1103
  readonly selection?: SelectionProvider;
1227
1104
  readonly transforms?: TransformProvider;
1228
1105
  readonly inspector?: InspectorProvider;
1229
- /** The selected node is an atomic asset that can open in the Asset Editor. */
1230
- readonly assetSubject?: AssetSubjectProvider;
1231
1106
  readonly related?: RelatedSubjectsProvider;
1232
1107
  /** Source-derived prefab/component instance overrides and native writes. */
1233
1108
  readonly instances?: ComponentInstancesProvider;
1234
1109
  readonly structure?: StructureProvider;
1235
1110
  readonly persistence?: PersistenceProvider;
1236
- /** D12 — per-layer viewport picking. Absent ⇒ this adapter is not pickable. */
1111
+ /** per-layer viewport picking. Absent ⇒ this adapter is not pickable. */
1237
1112
  readonly pickable?: PickProvider;
1238
- /** T0 (spec 27 §2) — per-node screen geometry for a DOM visual editor's overlay/
1113
+ /** per-node screen geometry for a DOM visual editor's overlay/
1239
1114
  * snap/measure math. Absent ⇒ this adapter produces no rects (no overlay). */
1240
1115
  readonly rects?: RectProvider;
1241
- /** T0 (spec 27 §2) — spatial drag-resize/move → source/data write for non-Object3D
1242
- * (DOM) nodes. Absent ⇒ no box-edit gesture for this adapter. */
1116
+ /** spatial drag-resize/move → source/data write for DOM
1117
+ * nodes, which have no scene object. Absent ⇒ no box-edit gesture for this adapter. */
1243
1118
  readonly boxEdit?: BoxEditProvider;
1244
- /** T0 (spec 27 §2) — double-click-to-edit-text on the contract (wires the react
1245
- * adapter's existing off-contract `editText`). Absent ⇒ no in-place text edit. */
1119
+ /**
1120
+ * Optional in-place text editing. Absent means this adapter does not
1121
+ * support the overlay’s double-click text edit gesture.
1122
+ */
1246
1123
  readonly text?: TextProvider;
1247
- /** D3 (spec 27 §6) — eyedropper fallback color sampling. Absent ⇒ the
1124
+ /** eyedropper fallback color sampling. Absent ⇒ the
1248
1125
  * canvas eyedropper swatch has no non-native path for this adapter. */
1249
1126
  readonly colorSample?: ColorSampleProvider;
1250
- /** D4 — storybook stories. Absent ⇒ no stories for any node in this adapter. */
1127
+ /** Storybook stories. Absent ⇒ no stories for any node in this adapter. */
1251
1128
  readonly stories?: StoriesProvider;
1252
1129
  /** Projection subject → source/data anchor and write lane, resolved together. */
1253
1130
  readonly truth?: TruthProvider;
1254
1131
  /** Adapter-owned component guides and direct-manipulation points. */
1255
1132
  readonly spatialHandles?: SpatialHandlesProvider;
1256
- /** Asset drop (hierarchy + viewport). Absent ⇒ this adapter accepts no drops. */
1133
+ /** Component drop (the canvas Add menu). Absent ⇒ this adapter accepts no drops. */
1257
1134
  readonly assetDrop?: AssetDropProvider;
1258
1135
  /** Change notification → UI refresh. */
1259
1136
  subscribe?(listener: () => void): () => void;
1260
1137
  }
1261
1138
 
1262
- /**
1263
- * THE PROVIDER VOCABULARY — every member of {@link AuthoringAdapter} beyond the
1264
- * two an adapter cannot exist without.
1265
- *
1266
- * Why this exists: the editor's coverage warnings are supposed to make stopping
1267
- * short of a native root IMPOSSIBLE, and they cannot do that while the row
1268
- * vocabulary is a hand-written list frozen at what its author happened to know.
1269
- * A list like that goes quiet at "all warnings closed" while whole capabilities
1270
- * — stories, asset drop, related subjects, structure — were never enumerated at
1271
- * all, so nothing ever warned about them. So the vocabulary IS the type: adding
1272
- * a provider to `AuthoringAdapter` and forgetting it here is a COMPILE ERROR
1273
- * (the `Record` below is missing a key), and inventing a key the type does not
1274
- * have is an excess-property error. There is no third way to add a provider.
1275
- *
1276
- * `capabilities` and `hierarchy` are excluded because they are required members
1277
- * — every adapter has them by construction, so "absent" is not a state they can
1278
- * be in and a row about them could never say anything.
1279
- */
1139
+ /** Provider vocabulary derived from AuthoringAdapter. The exhaustive Record
1140
+ * requires an entry for each optional provider and rejects unknown keys.
1141
+ * Required capabilities and hierarchy are excluded because every adapter has them. */
1280
1142
  export type AuthoringProviderKey = Exclude<keyof AuthoringAdapter, 'capabilities' | 'hierarchy'>;
1281
1143
 
1282
1144
  /**
@@ -1290,7 +1152,6 @@ const AUTHORING_PROVIDER_PRESENCE: Readonly<Record<AuthoringProviderKey, true>>
1290
1152
  selection: true,
1291
1153
  transforms: true,
1292
1154
  inspector: true,
1293
- assetSubject: true,
1294
1155
  related: true,
1295
1156
  instances: true,
1296
1157
  structure: true,
@@ -1322,7 +1183,7 @@ export const AUTHORING_PROVIDER_KEYS: readonly AuthoringProviderKey[] = Object.k
1322
1183
  /**
1323
1184
  * Which providers this adapter actually exposes — PRESENCE, measured off the
1324
1185
  * object itself. Nothing here consults `capabilities`, the adapter's class, the
1325
- * route that built it, or the game's id: an adapter this function has never
1186
+ * route that built it, or the project's id: an adapter this function has never
1326
1187
  * heard of gets the same answer as one it has.
1327
1188
  */
1328
1189
  export function measureAuthoringProviders(