@vgai/engine 0.5.14 → 0.5.16

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 (177) hide show
  1. package/dist/adapter/adapter-module.d.ts +228 -17
  2. package/dist/adapter/adapter-module.d.ts.map +1 -1
  3. package/dist/adapter/adapter-module.js +224 -42
  4. package/dist/adapter/authoring-seam-contract.d.ts +530 -0
  5. package/dist/adapter/authoring-seam-contract.d.ts.map +1 -0
  6. package/dist/adapter/authoring-seam-contract.js +162 -0
  7. package/dist/adapter/authoring.d.ts +237 -50
  8. package/dist/adapter/authoring.d.ts.map +1 -1
  9. package/dist/adapter/authoring.js +2 -1
  10. package/dist/adapter/entrypoint-selection-source.d.ts +35 -0
  11. package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -0
  12. package/dist/adapter/entrypoint-selection-source.js +151 -0
  13. package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts +7 -1
  14. package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
  15. package/dist/adapter/finders/scenes-from-entrypoint-selection.js +142 -21
  16. package/dist/adapter/first-party-systems.d.ts +13 -3
  17. package/dist/adapter/first-party-systems.d.ts.map +1 -1
  18. package/dist/adapter/first-party-systems.js +10 -3
  19. package/dist/adapter/index.d.ts +7 -2
  20. package/dist/adapter/index.d.ts.map +1 -1
  21. package/dist/adapter/index.js +4 -0
  22. package/dist/adapter/ingest/contract-system-adapters.d.ts +1 -1
  23. package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -1
  24. package/dist/adapter/ingest/contract-system-adapters.js +13 -25
  25. package/dist/adapter/ingest/game-contract-seams.d.ts +214 -0
  26. package/dist/adapter/ingest/game-contract-seams.d.ts.map +1 -0
  27. package/dist/adapter/ingest/game-contract-seams.js +62 -0
  28. package/dist/adapter/ingest/game-contract.d.ts +5 -3
  29. package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
  30. package/dist/adapter/ingest/game-contract.js +4 -1
  31. package/dist/adapter/ingest/merge-debug-adapters.d.ts +52 -0
  32. package/dist/adapter/ingest/merge-debug-adapters.d.ts.map +1 -0
  33. package/dist/adapter/ingest/merge-debug-adapters.js +141 -0
  34. package/dist/adapter/ingest/observation-debug-adapter.d.ts +56 -0
  35. package/dist/adapter/ingest/observation-debug-adapter.d.ts.map +1 -0
  36. package/dist/adapter/ingest/observation-debug-adapter.js +131 -0
  37. package/dist/adapter/ingest/scene-capture.d.ts +38 -13
  38. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
  39. package/dist/adapter/ingest/scene-capture.js +101 -24
  40. package/dist/adapter/ingest/visible-capture-window.d.ts +22 -5
  41. package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
  42. package/dist/adapter/ingest/visible-capture-window.js +46 -7
  43. package/dist/adapter/native-debug-module.d.ts +43 -0
  44. package/dist/adapter/native-debug-module.d.ts.map +1 -0
  45. package/dist/adapter/native-debug-module.js +230 -0
  46. package/dist/adapter/object3d-authoring-subject.d.ts +32 -0
  47. package/dist/adapter/object3d-authoring-subject.d.ts.map +1 -0
  48. package/dist/adapter/object3d-authoring-subject.js +25 -0
  49. package/dist/adapter/root-adapter.d.ts +21 -56
  50. package/dist/adapter/root-adapter.d.ts.map +1 -1
  51. package/dist/adapter/root-adapter.js +4 -20
  52. package/dist/adapter/root-seam-contract.d.ts +124 -0
  53. package/dist/adapter/root-seam-contract.d.ts.map +1 -0
  54. package/dist/adapter/root-seam-contract.js +36 -0
  55. package/dist/adapter/seam-evidence.d.ts +118 -0
  56. package/dist/adapter/seam-evidence.d.ts.map +1 -0
  57. package/dist/adapter/seam-evidence.js +231 -0
  58. package/dist/adapter/setup-three-root-adapter.d.ts +2 -3
  59. package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -1
  60. package/dist/adapter/setup-three-root-adapter.js +2 -3
  61. package/dist/adapter/system-adapter.d.ts +6 -1
  62. package/dist/adapter/system-adapter.d.ts.map +1 -1
  63. package/dist/adapter/system-seam-contract.d.ts +354 -0
  64. package/dist/adapter/system-seam-contract.d.ts.map +1 -0
  65. package/dist/adapter/system-seam-contract.js +95 -0
  66. package/dist/animation/xstate-animation-binding.d.ts +6 -0
  67. package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
  68. package/dist/animation/xstate-animation-binding.js +142 -4
  69. package/dist/asset-formats/collider.d.ts +1 -1
  70. package/dist/asset-formats/mesh.d.ts +2 -2
  71. package/dist/asset-formats/particles.d.ts +1 -1
  72. package/dist/canvas-react/engine-bridge.d.ts +6 -6
  73. package/dist/canvas-react/engine-bridge.d.ts.map +1 -1
  74. package/dist/canvas-react/engine-bridge.js +2 -2
  75. package/dist/canvas-react/index.d.ts +2 -2
  76. package/dist/canvas-react/index.d.ts.map +1 -1
  77. package/dist/canvas-react/index.js +1 -1
  78. package/dist/canvas-react/pixi-react-adapter.d.ts +15 -9
  79. package/dist/canvas-react/pixi-react-adapter.d.ts.map +1 -1
  80. package/dist/canvas-react/pixi-react-adapter.js +43 -29
  81. package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
  82. package/dist/canvas-react/pixi-react-root-factory.js +4 -0
  83. package/dist/canvas-react/world-context.d.ts +22 -12
  84. package/dist/canvas-react/world-context.d.ts.map +1 -1
  85. package/dist/canvas-react/world-context.js +12 -51
  86. package/dist/ecs/user-data.d.ts +5 -0
  87. package/dist/ecs/user-data.d.ts.map +1 -1
  88. package/dist/ecs/user-data.js +3 -0
  89. package/dist/input/input-manager.d.ts +49 -0
  90. package/dist/input/input-manager.d.ts.map +1 -1
  91. package/dist/input/input-manager.js +127 -0
  92. package/dist/manifest/schema.d.ts +9 -9
  93. package/dist/manifest/schema.js +1 -1
  94. package/dist/pixi/authoring.d.ts.map +1 -1
  95. package/dist/pixi/authoring.js +220 -1
  96. package/dist/pixi/ingest.d.ts +2 -1
  97. package/dist/pixi/ingest.d.ts.map +1 -1
  98. package/dist/pixi/ingest.js +4 -2
  99. package/dist/pixi/scene-capture.d.ts +9 -1
  100. package/dist/pixi/scene-capture.d.ts.map +1 -1
  101. package/dist/pixi/scene-capture.js +9 -6
  102. package/dist/render/particles-factory.d.ts +13 -6
  103. package/dist/render/particles-factory.d.ts.map +1 -1
  104. package/dist/render/particles-factory.js +6 -3
  105. package/dist/runtime/create-runtime.d.ts +23 -46
  106. package/dist/runtime/create-runtime.d.ts.map +1 -1
  107. package/dist/runtime/create-runtime.js +22 -67
  108. package/dist/runtime/game.d.ts +29 -71
  109. package/dist/runtime/game.d.ts.map +1 -1
  110. package/dist/runtime/game.js +15 -54
  111. package/dist/runtime/mount-game.d.ts.map +1 -1
  112. package/dist/runtime/mount-game.js +12 -1
  113. package/dist/runtime/mount-manifest.d.ts.map +1 -1
  114. package/dist/runtime/mount-manifest.js +4 -5
  115. package/dist/runtime/types.d.ts +18 -0
  116. package/dist/runtime/types.d.ts.map +1 -1
  117. package/dist/setup/setup-renderer.d.ts +3 -4
  118. package/dist/setup/setup-renderer.d.ts.map +1 -1
  119. package/dist/setup/setup-renderer.js +3 -4
  120. package/dist/world3d-react/engine-bridge.d.ts +1 -1
  121. package/dist/world3d-react/engine-bridge.js +1 -1
  122. package/dist/world3d-react/r3f-adapter.d.ts +7 -0
  123. package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
  124. package/dist/world3d-react/r3f-adapter.js +50 -21
  125. package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
  126. package/dist/world3d-react/r3f-root-factory.js +4 -0
  127. package/dist/world3d-react/world-context.d.ts +9 -3
  128. package/dist/world3d-react/world-context.d.ts.map +1 -1
  129. package/dist/world3d-react/world-context.js +5 -18
  130. package/package.json +1 -1
  131. package/schemas/engine-capabilities.json +8 -8
  132. package/schemas/vgai-project.schema.json +1 -1
  133. package/src/adapter/adapter-module.ts +407 -50
  134. package/src/adapter/authoring-seam-contract.ts +211 -0
  135. package/src/adapter/authoring.ts +242 -50
  136. package/src/adapter/entrypoint-selection-source.ts +159 -0
  137. package/src/adapter/finders/scenes-from-entrypoint-selection.ts +159 -28
  138. package/src/adapter/first-party-systems.ts +17 -3
  139. package/src/adapter/index.ts +51 -1
  140. package/src/adapter/ingest/contract-system-adapters.ts +26 -30
  141. package/src/adapter/ingest/game-contract-seams.ts +85 -0
  142. package/src/adapter/ingest/game-contract.ts +9 -4
  143. package/src/adapter/ingest/merge-debug-adapters.ts +193 -0
  144. package/src/adapter/ingest/observation-debug-adapter.ts +158 -0
  145. package/src/adapter/ingest/scene-capture.ts +126 -23
  146. package/src/adapter/ingest/visible-capture-window.ts +63 -9
  147. package/src/adapter/native-debug-module.ts +307 -0
  148. package/src/adapter/object3d-authoring-subject.ts +50 -0
  149. package/src/adapter/root-adapter.ts +21 -56
  150. package/src/adapter/root-seam-contract.ts +56 -0
  151. package/src/adapter/seam-evidence.ts +336 -0
  152. package/src/adapter/setup-three-root-adapter.ts +4 -6
  153. package/src/adapter/system-adapter.ts +6 -1
  154. package/src/adapter/system-seam-contract.ts +121 -0
  155. package/src/animation/xstate-animation-binding.ts +151 -4
  156. package/src/canvas-react/engine-bridge.ts +6 -6
  157. package/src/canvas-react/index.ts +2 -1
  158. package/src/canvas-react/pixi-react-adapter.tsx +55 -34
  159. package/src/canvas-react/pixi-react-root-factory.tsx +4 -0
  160. package/src/canvas-react/world-context.ts +26 -101
  161. package/src/ecs/user-data.ts +5 -0
  162. package/src/input/input-manager.ts +122 -0
  163. package/src/manifest/schema.ts +1 -1
  164. package/src/pixi/authoring.ts +236 -1
  165. package/src/pixi/ingest.ts +9 -2
  166. package/src/pixi/scene-capture.ts +20 -5
  167. package/src/render/particles-factory.ts +16 -6
  168. package/src/runtime/create-runtime.ts +41 -106
  169. package/src/runtime/game.ts +41 -111
  170. package/src/runtime/mount-game.ts +19 -1
  171. package/src/runtime/mount-manifest.ts +4 -5
  172. package/src/runtime/types.ts +22 -0
  173. package/src/setup/setup-renderer.ts +3 -4
  174. package/src/world3d-react/engine-bridge.ts +1 -1
  175. package/src/world3d-react/r3f-adapter.tsx +64 -24
  176. package/src/world3d-react/r3f-root-factory.tsx +4 -0
  177. package/src/world3d-react/world-context.ts +11 -36
@@ -0,0 +1,211 @@
1
+ /** Compiler-derived proof requirements for the AuthoringAdapter protocol.
2
+ *
3
+ * This is intentionally adjacent to the interface. Coverage used to enumerate
4
+ * only top-level provider names, which allowed `{}` to count as a complete
5
+ * provider. These descriptors cover every nested member and encode the
6
+ * weakest evidence that can truthfully verify it.
7
+ */
8
+
9
+ import type {
10
+ AssetDropProvider,
11
+ AssetSubjectProvider,
12
+ AuthoringAdapter,
13
+ BoxEditProvider,
14
+ ColorSampleProvider,
15
+ ComponentInstancesProvider,
16
+ HierarchyProvider,
17
+ InspectorProvider,
18
+ PersistenceProvider,
19
+ PickProvider,
20
+ RectProvider,
21
+ RelatedSubjectsProvider,
22
+ SelectionProvider,
23
+ SpatialHandlesProvider,
24
+ StoriesProvider,
25
+ StructureProvider,
26
+ TextProvider,
27
+ TransformProvider,
28
+ TruthProvider,
29
+ } from './authoring';
30
+ import { defineSeamShape, type SeamShape } from './seam-evidence';
31
+
32
+ export const AUTHORING_ADAPTER_SHAPE = defineSeamShape<AuthoringAdapter>()({
33
+ capabilities: { optional: false, kind: 'value', required: 'shape' },
34
+ provenance: { optional: true, kind: 'value', required: 'shape' },
35
+ hierarchy: { optional: false, kind: 'value', required: 'operation' },
36
+ selection: { optional: true, kind: 'value', required: 'effect' },
37
+ transforms: { optional: true, kind: 'value', required: 'effect' },
38
+ inspector: { optional: true, kind: 'value', required: 'effect' },
39
+ assetSubject: { optional: true, kind: 'value', required: 'operation' },
40
+ related: { optional: true, kind: 'value', required: 'operation' },
41
+ instances: { optional: true, kind: 'value', required: 'effect' },
42
+ structure: { optional: true, kind: 'value', required: 'round-trip' },
43
+ persistence: { optional: true, kind: 'value', required: 'round-trip' },
44
+ pickable: { optional: true, kind: 'value', required: 'operation' },
45
+ rects: { optional: true, kind: 'value', required: 'operation' },
46
+ boxEdit: { optional: true, kind: 'value', required: 'effect' },
47
+ text: { optional: true, kind: 'value', required: 'effect' },
48
+ colorSample: { optional: true, kind: 'value', required: 'operation' },
49
+ stories: { optional: true, kind: 'value', required: 'effect' },
50
+ truth: { optional: true, kind: 'value', required: 'operation' },
51
+ spatialHandles: { optional: true, kind: 'value', required: 'effect' },
52
+ assetDrop: { optional: true, kind: 'value', required: 'round-trip' },
53
+ subscribe: { optional: true, kind: 'function', required: 'effect' },
54
+ observeTransforms: { optional: true, kind: 'function', required: 'effect' },
55
+ });
56
+
57
+ export const HIERARCHY_PROVIDER_SHAPE = defineSeamShape<HierarchyProvider>()({
58
+ roots: { optional: false, kind: 'function', required: 'operation' },
59
+ node: { optional: false, kind: 'function', required: 'operation' },
60
+ object3D: { optional: true, kind: 'function', required: 'operation' },
61
+ idForObject3D: { optional: true, kind: 'function', required: 'effect' },
62
+ });
63
+
64
+ export const SELECTION_PROVIDER_SHAPE = defineSeamShape<SelectionProvider>()({
65
+ get: { optional: false, kind: 'function', required: 'operation' },
66
+ set: { optional: false, kind: 'function', required: 'effect' },
67
+ resolve: { optional: true, kind: 'function', required: 'operation' },
68
+ });
69
+
70
+ export const TRANSFORM_PROVIDER_SHAPE = defineSeamShape<TransformProvider>()({
71
+ dimensions: { optional: true, kind: 'function', required: 'operation' },
72
+ get: { optional: false, kind: 'function', required: 'operation' },
73
+ editability: { optional: true, kind: 'function', required: 'operation' },
74
+ beginEdit: { optional: false, kind: 'function', required: 'effect' },
75
+ apply: { optional: false, kind: 'function', required: 'effect' },
76
+ endEdit: { optional: false, kind: 'function', required: 'effect' },
77
+ remove: { optional: true, kind: 'function', required: 'round-trip' },
78
+ sourceCommit: { optional: true, kind: 'value', required: 'round-trip' },
79
+ });
80
+
81
+ export const INSPECTOR_PROVIDER_SHAPE = defineSeamShape<InspectorProvider>()({
82
+ properties: { optional: false, kind: 'function', required: 'operation' },
83
+ get: { optional: false, kind: 'function', required: 'operation' },
84
+ set: { optional: false, kind: 'function', required: 'effect' },
85
+ editability: { optional: true, kind: 'function', required: 'operation' },
86
+ remove: { optional: true, kind: 'function', required: 'round-trip' },
87
+ });
88
+
89
+ export const ASSET_SUBJECT_PROVIDER_SHAPE = defineSeamShape<AssetSubjectProvider>()({
90
+ get: { optional: false, kind: 'function', required: 'operation' },
91
+ entries: { optional: true, kind: 'function', required: 'operation' },
92
+ });
93
+
94
+ export const RELATED_SUBJECTS_PROVIDER_SHAPE = defineSeamShape<RelatedSubjectsProvider>()({
95
+ links: { optional: false, kind: 'function', required: 'operation' },
96
+ });
97
+
98
+ export const COMPONENT_INSTANCES_PROVIDER_SHAPE = defineSeamShape<ComponentInstancesProvider>()({
99
+ describe: { optional: false, kind: 'function', required: 'operation' },
100
+ openComponent: { optional: true, kind: 'function', required: 'effect' },
101
+ revert: { optional: false, kind: 'function', required: 'round-trip' },
102
+ applyToComponent: { optional: false, kind: 'function', required: 'round-trip' },
103
+ });
104
+
105
+ export const STRUCTURE_PROVIDER_SHAPE = defineSeamShape<StructureProvider>()({
106
+ create: { optional: false, kind: 'function', required: 'round-trip' },
107
+ remove: { optional: false, kind: 'function', required: 'round-trip' },
108
+ duplicate: { optional: false, kind: 'function', required: 'round-trip' },
109
+ reparent: { optional: false, kind: 'function', required: 'round-trip' },
110
+ reorder: { optional: true, kind: 'function', required: 'round-trip' },
111
+ creatableKinds: { optional: true, kind: 'function', required: 'operation' },
112
+ wrap: { optional: true, kind: 'function', required: 'round-trip' },
113
+ unwrap: { optional: true, kind: 'function', required: 'round-trip' },
114
+ group: { optional: true, kind: 'function', required: 'round-trip' },
115
+ ungroup: { optional: true, kind: 'function', required: 'round-trip' },
116
+ canUngroup: { optional: true, kind: 'function', required: 'operation' },
117
+ removeMany: { optional: true, kind: 'function', required: 'round-trip' },
118
+ copy: { optional: true, kind: 'function', required: 'effect' },
119
+ canCopy: { optional: true, kind: 'function', required: 'operation' },
120
+ cut: { optional: true, kind: 'function', required: 'round-trip' },
121
+ paste: { optional: true, kind: 'function', required: 'round-trip' },
122
+ canPaste: { optional: true, kind: 'function', required: 'operation' },
123
+ });
124
+
125
+ export const PERSISTENCE_PROVIDER_SHAPE = defineSeamShape<PersistenceProvider>()({
126
+ isDirty: { optional: false, kind: 'function', required: 'operation' },
127
+ lastError: { optional: true, kind: 'function', required: 'operation' },
128
+ save: { optional: false, kind: 'function', required: 'round-trip' },
129
+ destination: { optional: false, kind: 'value', required: 'shape' },
130
+ applyExternal: { optional: true, kind: 'function', required: 'effect' },
131
+ });
132
+
133
+ export const PICK_PROVIDER_SHAPE = defineSeamShape<PickProvider>()({
134
+ pick: { optional: false, kind: 'function', required: 'operation' },
135
+ candidates: { optional: true, kind: 'function', required: 'operation' },
136
+ });
137
+
138
+ export const STORIES_PROVIDER_SHAPE = defineSeamShape<StoriesProvider>()({
139
+ storiesFor: { optional: false, kind: 'function', required: 'operation' },
140
+ active: { optional: false, kind: 'function', required: 'operation' },
141
+ apply: { optional: false, kind: 'function', required: 'effect' },
142
+ isolate: { optional: true, kind: 'function', required: 'effect' },
143
+ title: { optional: true, kind: 'value', required: 'shape' },
144
+ });
145
+
146
+ export const ASSET_DROP_PROVIDER_SHAPE = defineSeamShape<AssetDropProvider>()({
147
+ accepts: { optional: false, kind: 'function', required: 'operation' },
148
+ drop: { optional: false, kind: 'function', required: 'round-trip' },
149
+ });
150
+
151
+ export const RECT_PROVIDER_SHAPE = defineSeamShape<RectProvider>()({
152
+ rect: { optional: false, kind: 'function', required: 'operation' },
153
+ contextRects: { optional: true, kind: 'function', required: 'operation' },
154
+ emptyContainers: { optional: true, kind: 'function', required: 'operation' },
155
+ });
156
+
157
+ export const BOX_EDIT_PROVIDER_SHAPE = defineSeamShape<BoxEditProvider>()({
158
+ begin: { optional: false, kind: 'function', required: 'effect' },
159
+ apply: { optional: false, kind: 'function', required: 'effect' },
160
+ end: { optional: false, kind: 'function', required: 'effect' },
161
+ gizmoOrigin: { optional: true, kind: 'function', required: 'operation' },
162
+ referencePoint: { optional: true, kind: 'function', required: 'operation' },
163
+ });
164
+
165
+ export const TEXT_PROVIDER_SHAPE = defineSeamShape<TextProvider>()({
166
+ get: { optional: false, kind: 'function', required: 'operation' },
167
+ set: { optional: false, kind: 'function', required: 'effect' },
168
+ });
169
+
170
+ export const COLOR_SAMPLE_PROVIDER_SHAPE = defineSeamShape<ColorSampleProvider>()({
171
+ backgroundChainAt: { optional: false, kind: 'function', required: 'operation' },
172
+ });
173
+
174
+ export const TRUTH_PROVIDER_SHAPE = defineSeamShape<TruthProvider>()({
175
+ resolve: { optional: false, kind: 'function', required: 'operation' },
176
+ });
177
+
178
+ export const SPATIAL_HANDLES_PROVIDER_SHAPE = defineSeamShape<SpatialHandlesProvider>()({
179
+ layers: { optional: false, kind: 'function', required: 'operation' },
180
+ preview: { optional: false, kind: 'function', required: 'effect' },
181
+ commit: { optional: false, kind: 'function', required: 'round-trip' },
182
+ });
183
+
184
+ type ObjectProviderKey = Exclude<
185
+ keyof AuthoringAdapter,
186
+ 'capabilities' | 'provenance' | 'hierarchy' | 'subscribe' | 'observeTransforms'
187
+ >;
188
+
189
+ /** The one nested-provider table. Its key type comes from AuthoringAdapter and
190
+ * each value is typed against that provider's own interface. */
191
+ export const AUTHORING_PROVIDER_SHAPES: {
192
+ readonly [K in ObjectProviderKey]: SeamShape<NonNullable<AuthoringAdapter[K]>>;
193
+ } = {
194
+ selection: SELECTION_PROVIDER_SHAPE,
195
+ transforms: TRANSFORM_PROVIDER_SHAPE,
196
+ inspector: INSPECTOR_PROVIDER_SHAPE,
197
+ assetSubject: ASSET_SUBJECT_PROVIDER_SHAPE,
198
+ related: RELATED_SUBJECTS_PROVIDER_SHAPE,
199
+ instances: COMPONENT_INSTANCES_PROVIDER_SHAPE,
200
+ structure: STRUCTURE_PROVIDER_SHAPE,
201
+ persistence: PERSISTENCE_PROVIDER_SHAPE,
202
+ pickable: PICK_PROVIDER_SHAPE,
203
+ rects: RECT_PROVIDER_SHAPE,
204
+ boxEdit: BOX_EDIT_PROVIDER_SHAPE,
205
+ text: TEXT_PROVIDER_SHAPE,
206
+ colorSample: COLOR_SAMPLE_PROVIDER_SHAPE,
207
+ stories: STORIES_PROVIDER_SHAPE,
208
+ truth: TRUTH_PROVIDER_SHAPE,
209
+ spatialHandles: SPATIAL_HANDLES_PROVIDER_SHAPE,
210
+ assetDrop: ASSET_DROP_PROVIDER_SHAPE,
211
+ };
@@ -58,7 +58,7 @@ export interface AuthoringProvenance {
58
58
  * - `foreign` — a live tree the editor does not own (an unmodified external
59
59
  * game, or any world edited only through an overlay); edits go to the
60
60
  * overlay, never the source;
61
- * - `live` — a running game adopted at play time; edits are not persisted;
61
+ * - `live` — a running game adopted at play time; ordinary edits are not persisted;
62
62
  * - `boundary` — a declared world with no live editing surface here.
63
63
  */
64
64
  source: 'document' | 'source-code' | 'foreign' | 'live' | 'boundary';
@@ -118,6 +118,12 @@ export interface PropertyDescriptor {
118
118
  label: string;
119
119
  type: 'string' | 'number' | 'boolean' | 'vec3' | 'color' | 'enum' | 'asset' | 'json';
120
120
  readonly?: boolean;
121
+ /**
122
+ * Why this field cannot be written, in the adapter's own vocabulary. The
123
+ * Inspector, its control surface, and the write refusal all project this
124
+ * same sentence; omitting it uses the generic read-only explanation.
125
+ */
126
+ readonlyReason?: string;
121
127
  options?: unknown[];
122
128
  /**
123
129
  * Optional domain-shaped grouping (T3.4 slice 2): properties sharing the
@@ -210,6 +216,42 @@ export interface TransformEditability {
210
216
  writable: boolean;
211
217
  /** Human-facing explanation for a disabled gizmo/field. */
212
218
  reason?: string;
219
+ /**
220
+ * Is there an AUTHORED override of this channel that {@link
221
+ * TransformProvider.remove} could drop? The per-channel analogue of a
222
+ * property descriptor's `resettable`, and it is answered the same way: the
223
+ * document actually carries this channel, and this session has a door that
224
+ * can take it back.
225
+ *
226
+ * It lives HERE rather than being inferred from `remove`'s presence because
227
+ * `remove` is a lane-wide fact while this is a per-subject one — a composite
228
+ * routes both, but only this one can say that THIS node's `position` is
229
+ * authored and THAT node's is not. Omitted ⇒ nothing to drop, and the
230
+ * protocol's removal door refuses by name instead of acking a revert it did
231
+ * not perform.
232
+ *
233
+ * EACH HALF IS ANSWERED BY WHOEVER CAN SEE IT, and a lane that cannot read
234
+ * its own document may not invent the first. A source lane holding the
235
+ * file's text answers both (the first-party R3F lane reads the callsite's
236
+ * attributes before saying yes). A lane whose client holds only an
237
+ * oid → file:line index and writes through a server that resolves the
238
+ * address — the vendored-ingest lane — can honestly answer only the SECOND,
239
+ * so that is what it answers, and `remove`'s own {@link WriteAck} reports
240
+ * whether an attribute was actually dropped (`persisted: false` when the
241
+ * callsite carried none). The inverse is what is forbidden on every lane:
242
+ * acking a drop that took no byte.
243
+ */
244
+ removable?: boolean;
245
+ }
246
+
247
+ /** The one lawful Play→source write: a person explicitly attributes the
248
+ * selected live node's current transform to its OID-anchored source literal.
249
+ * This is an atomic optional group so availability and the write door cannot
250
+ * drift apart. Ordinary transform gestures remain governed by begin/apply/end
251
+ * and therefore stay ephemeral while Play is running. */
252
+ export interface TransformSourceCommitProvider {
253
+ availability(id: string): { available: boolean; reason?: string };
254
+ commit(id: string): Promise<WriteAck>;
213
255
  }
214
256
 
215
257
  export interface TransformProvider {
@@ -231,6 +273,35 @@ export interface TransformProvider {
231
273
  * live-only floor rather than borrowing an answer from somewhere else.
232
274
  */
233
275
  endEdit(id: string): void | WriteAck | Promise<void | WriteAck>;
276
+ /**
277
+ * Optional — DROP one channel's authored transform entirely, rather than
278
+ * writing a value into it.
279
+ *
280
+ * THE SAME DOOR {@link InspectorProvider.remove} IS, for the channel the
281
+ * Transform section owns instead of a property row. `apply`/`endEdit` write a
282
+ * VALUE, so reverting a channel an authoring gesture APPENDED by writing the
283
+ * old numbers back leaves `position={[0, 0, 0]}` standing where the source
284
+ * carried nothing — the file ends one attribute heavier than it started, and
285
+ * no byte-level edit/revert round trip can close. Only removal expresses that
286
+ * absence, which is why a lane without this door grades UNVERIFIABLE rather
287
+ * than passing.
288
+ *
289
+ * SAME ACK CONTRACT AS {@link endEdit} — the pipe's per-edit {@link WriteAck}
290
+ * for THIS removal, awaited. Removal is a write; it moves bytes, and the
291
+ * caller has the same right to be told where they went. Returning nothing
292
+ * means this provider performed no persisted write and the shell reports the
293
+ * live-only floor.
294
+ *
295
+ * What "absent" MEANS is the dialect writer's to decide (deleting the JSX
296
+ * attribute, for the source lanes). Nothing above this seam infers it: a lane
297
+ * whose transform truth IS the running object — there is no document for the
298
+ * channel to be absent from — simply omits the method, and the protocol door
299
+ * refuses by name rather than falling back to a value write.
300
+ */
301
+ remove?(id: string, channel: TransformChannel): void | WriteAck | Promise<void | WriteAck>;
302
+ /** Explicit Play→source attribution. Absent outside an eligible live OID
303
+ * surface; never used implicitly by a transform gesture. */
304
+ readonly sourceCommit?: TransformSourceCommitProvider;
234
305
  }
235
306
 
236
307
  /**
@@ -262,14 +333,28 @@ export interface InspectorProvider {
262
333
  editability?(id: string, path: string): { writable: boolean; reason?: string };
263
334
  /**
264
335
  * Optional — REMOVE a property's authored override entirely (not "set to a
265
- * value"), letting whatever governs it in its absence take over. The one
266
- * concrete need today: a longhand CSS override (`style.borderTopLeftRadius`)
267
- * that a uniform edit of its shorthand (`style.borderRadius`) must clear so
268
- * the shorthand actually wins, instead of leaving a stale longhand that
269
- * silently overrides it on reload (spec 27 §5 C2, U2). Adapters with no
270
- * removable-override concept simply omit it callers optional-chain.
336
+ * value"), letting whatever governs it in its absence take over.
337
+ *
338
+ * THIS IS THE ONLY WAY TO EXPRESS BYTE-ABSENCE. {@link set} writes a VALUE:
339
+ * reverting a prop an authoring gesture ADDED by setting it back to what the
340
+ * default would have been leaves `position={[0, 0, 0]}` in the source where
341
+ * the file previously carried nothing at all. Only removal restores the
342
+ * bytes, which is why a lane with no `remove` cannot close its own
343
+ * edit/revert round trip and grades UNVERIFIABLE rather than passing.
344
+ *
345
+ * SAME ACK CONTRACT AS {@link set} — the pipe's per-edit {@link WriteAck}
346
+ * (`editor/src/authoring/write-pipe.ts`), awaited. Removal is a write; it
347
+ * moves bytes, and the caller has the same right to be told where they went
348
+ * and whether they moved. Returning nothing means this provider performed no
349
+ * persisted write, and the shell reports the live-only floor.
350
+ *
351
+ * What "absent" MEANS is the dialect writer's to decide — deleting a JSX
352
+ * attribute, dropping a CSS longhand so its shorthand cascades. Nothing above
353
+ * this seam infers it. Adapters with no removable-override concept simply
354
+ * omit the method; callers optional-chain, and the protocol door refuses by
355
+ * name rather than falling back to a value write.
271
356
  */
272
- remove?(id: string, path: string): void;
357
+ remove?(id: string, path: string): void | WriteAck | Promise<void | WriteAck>;
273
358
  }
274
359
 
275
360
  /**
@@ -340,6 +425,9 @@ export interface ComponentInstanceDescription {
340
425
  export interface ComponentInstanceApplyResult {
341
426
  readonly changed: boolean;
342
427
  readonly message: string;
428
+ /** This operation's own writer acknowledgement. Absent when the request was
429
+ * refused before any write; live-only is an explicit WriteAck. */
430
+ readonly write?: WriteAck;
343
431
  }
344
432
 
345
433
  /**
@@ -352,14 +440,84 @@ export interface ComponentInstancesProvider {
352
440
  describe(id: string): ComponentInstanceDescription | null;
353
441
  /** Open the native component/prefab board for this instance's surface. */
354
442
  openComponent?(id: string): void;
355
- revert(id: string, paths: readonly string[]): Promise<void>;
443
+ revert(id: string, paths: readonly string[]): Promise<void | WriteAck>;
356
444
  applyToComponent(id: string, path: string): Promise<ComponentInstanceApplyResult>;
357
445
  }
358
446
 
447
+ /**
448
+ * WHAT A STRUCTURAL VERB ANSWERS WITH — the same widening
449
+ * {@link TransformProvider.endEdit} and {@link InspectorProvider.set} carry,
450
+ * for the same reason: the ack is produced by the component that performed
451
+ * THIS edit, and there is nowhere else for a caller to get one.
452
+ *
453
+ * `void` is the honest answer for a lane whose verb performs no write of its
454
+ * own (an in-memory adapter mutating a graph it already owns); a lane that
455
+ * writes returns the persistence pipe's own {@link WriteAck}
456
+ * (`editor/src/authoring/write-pipe.ts`), awaited, so a caller that awaits the
457
+ * verb has awaited the byte.
458
+ *
459
+ * A structural verb resolves on ITS OWN verb: `remove` asks whether the struct
460
+ * writer is bound, never whether the PROP writer is — resolving on a
461
+ * value-write door is how a lane comes to ack `source-prop` for a door it does
462
+ * not have.
463
+ */
464
+ export type StructuralWriteOutcome = void | WriteAck | Promise<void | WriteAck>;
465
+
466
+ /**
467
+ * WHAT AN ID-RETURNING STRUCTURAL VERB ANSWERS WITH — the id and the ack, as
468
+ * ONE return.
469
+ *
470
+ * The writing verbs ({@link StructureProvider.remove} and friends) answer with
471
+ * a bare {@link StructuralWriteOutcome}, because the write IS their whole
472
+ * answer. `create`/`duplicate`/`group`/`ungroup` owe their caller a second
473
+ * thing — the id(s) the op produced — and those two halves settle at DIFFERENT
474
+ * TIMES:
475
+ *
476
+ * - the id is SYNCHRONOUS where a lane can mint one. The live canvas surface
477
+ * adds a real display object to the running tree and re-indexes before it
478
+ * returns, so `create` hands back an id its caller can select in the same
479
+ * turn ("create then immediately select it" is the ordinary shell
480
+ * sequence). Making the verb `async` to carry the ack would take that
481
+ * away from every caller to serve a value that is already known.
482
+ * - the ack is ASYNCHRONOUS wherever a byte is involved, and it is the only
483
+ * thing that says whether the edit reached the file it claims.
484
+ *
485
+ * So the return carries both. A source lane whose new id only exists after the
486
+ * remount stamps it answers `id: ''` (there is no honest synchronous id) with a
487
+ * real `ack`; a router that could not route answers with an unchanged id and
488
+ * `ack: undefined` (no write was attempted, which is exactly what `void` means
489
+ * in {@link StructuralWriteOutcome}).
490
+ *
491
+ * Firing the write and dropping the promise on the floor — `void structOp(…)`
492
+ * behind a `return ''` — is the shape this type exists to make unrepresentable:
493
+ * it left the caller unable to await the byte, which is how N same-file
494
+ * duplicates went out together, each reading the file before any wrote back.
495
+ */
496
+ export interface StructuralIdWrite<Id extends string | null = string> {
497
+ /** The id the op produced — `''` when the lane cannot know it until its own
498
+ * source write completes and the world remounts. */
499
+ readonly id: Id;
500
+ readonly ack: StructuralWriteOutcome;
501
+ }
502
+
503
+ /** The plural half of {@link StructuralIdWrite}, for a verb that promotes a set
504
+ * of ids rather than minting one. */
505
+ export interface StructuralIdsWrite {
506
+ readonly ids: readonly string[];
507
+ readonly ack: StructuralWriteOutcome;
508
+ }
509
+
510
+ /** Clipboard-backed structural writes retain the existing `false` refusal
511
+ * channel while answering a successful cut/paste with the write's real
512
+ * destination. A bare `true` cannot prove a byte landed anywhere. */
513
+ export type StructuralClipboardOutcome = false | WriteAck | Promise<false | WriteAck>;
514
+
359
515
  export interface StructureProvider {
360
- create(kind: string, parentId?: string): string;
516
+ /** Create a `kind` under `parentId`, answering with the new id AND this
517
+ * creation's own write ack (see {@link StructuralIdWrite}). */
518
+ create(kind: string, parentId?: string): StructuralIdWrite;
361
519
  /**
362
- * Remove `id`. May optionally return an awaitable (`Promise<void>`) when the
520
+ * Remove `id`. May optionally return an awaitable when the
363
521
  * underlying write is asynchronous (e.g. a react-world source-file edit) —
364
522
  * `deleteSelection` (`editor-hotkeys.ts`) awaits it per id so a same-file
365
523
  * multi-delete's writes land strictly one at a time (see that function's own
@@ -367,14 +525,22 @@ export interface StructureProvider {
367
525
  * a per-id loop sound for an adapter whose OID index isn't reindexed between
368
526
  * writes). An adapter with a synchronous/in-memory remove (e.g.
369
527
  * `UIAuthoringAdapter`) returns `void` — `await`ing it is a harmless no-op.
528
+ *
529
+ * A source-writing lane returns THIS removal's own {@link WriteAck} (see
530
+ * {@link StructuralWriteOutcome}).
370
531
  */
371
- remove(id: string): void | Promise<void>;
372
- duplicate(id: string): string;
373
- reparent(id: string, newParentId: string | null): void;
532
+ remove(id: string): StructuralWriteOutcome;
533
+ /** Copy `id`, answering with the copy's id AND this duplication's own write
534
+ * ack (see {@link StructuralIdWrite}). A source lane whose copy has no id
535
+ * until the remount stamps it answers with the SOURCE id it was given —
536
+ * `duplicate` has no refusal channel, so an empty string there would be
537
+ * indistinguishable from "refused". */
538
+ duplicate(id: string): StructuralIdWrite;
539
+ reparent(id: string, newParentId: string | null): StructuralWriteOutcome;
374
540
  /** Reorder `id` to sit immediately before `beforeSiblingId` among its siblings
375
541
  * (`null` = move to the end). Absent ⇒ the shell has no sibling-reorder UI
376
542
  * for this adapter. */
377
- reorder?(id: string, beforeSiblingId: string | null): void;
543
+ reorder?(id: string, beforeSiblingId: string | null): StructuralWriteOutcome;
378
544
  /**
379
545
  * The kinds `create` accepts for a given parent (`null` parentId = a new
380
546
  * root), each with a display label — drives the shell's creation palette.
@@ -387,18 +553,22 @@ export interface StructureProvider {
387
553
  * adapter-chosen, e.g. a `div`), re-parenting `id` as that container's sole
388
554
  * child. Absent ⇒ the shell's context menu shows no Wrap item for this
389
555
  * adapter. */
390
- wrap?(id: string, wrapperTag?: string): void;
556
+ wrap?(id: string, wrapperTag?: string): StructuralWriteOutcome;
391
557
  /** D3 (spec 27 §6) — replace `id` with its own children (the inverse of
392
558
  * `wrap`). Absent ⇒ the shell's context menu shows no Unwrap item for this
393
559
  * adapter. */
394
- unwrap?(id: string): void;
560
+ unwrap?(id: string): StructuralWriteOutcome;
395
561
  /** Group sibling authoring objects beneath one new spatial parent. The
396
562
  * adapter owns transform preservation, persistence, and undo semantics.
397
- * Returns the new group id, or null when the selection cannot be grouped. */
398
- group?(ids: readonly string[]): string | null;
399
- /** Dissolve a plain spatial group and return the ids of its promoted
400
- * children. Absent means this adapter has no lossless ungroup operation. */
401
- ungroup?(id: string): string[];
563
+ * Answers with the new group id `null` when the selection cannot be
564
+ * grouped at all AND this grouping's own write ack (see
565
+ * {@link StructuralIdWrite}). */
566
+ group?(ids: readonly string[]): StructuralIdWrite<string | null>;
567
+ /** Dissolve a plain spatial group, answering with the ids of its promoted
568
+ * children AND this dissolution's own write ack (see
569
+ * {@link StructuralIdsWrite}). Absent means this adapter has no lossless
570
+ * ungroup operation. */
571
+ ungroup?(id: string): StructuralIdsWrite;
402
572
  /** Preflight for an Ungroup affordance. Omit when ungroup is never offered. */
403
573
  canUngroup?(id: string): boolean;
404
574
  /**
@@ -410,15 +580,15 @@ export interface StructureProvider {
410
580
  * own single batched multi-delete undo). Absent ⇒ the shell falls back to
411
581
  * the per-id `remove` loop (today's N-entry behavior, unchanged) — an
412
582
  * honest degrade, not a silent behavior change, for any adapter that
413
- * hasn't implemented batching. May optionally return an awaitable
414
- * (`Promise<void>`), same widening `remove` documents above and for the
415
- * same reason (delete-order-residual fix): `deleteSelection`
583
+ * hasn't implemented batching. May optionally return an awaitable, same
584
+ * widening `remove` documents above ({@link StructuralWriteOutcome}) and for
585
+ * the same reason (delete-order-residual fix): `deleteSelection`
416
586
  * (`editor-hotkeys.ts`) awaits it so the caller can rely on the whole
417
587
  * batch's write having landed before it returns. A synchronous/in-memory
418
588
  * implementation (e.g. `UIAuthoringAdapter`'s) returns `void` — awaiting it
419
589
  * is a harmless no-op.
420
590
  */
421
- removeMany?(ids: readonly string[]): void | Promise<void>;
591
+ removeMany?(ids: readonly string[]): StructuralWriteOutcome;
422
592
  /** Copy authored entities through the host clipboard. The adapter owns the
423
593
  * native payload (JSX for a source-backed Three/Canvas world, native records
424
594
  * for a data-backed adapter); the shell never serializes an engine-private
@@ -428,11 +598,11 @@ export interface StructureProvider {
428
598
  canCopy?(ids: readonly string[]): boolean;
429
599
  /** Copy, then remove the same authored entities only after the clipboard
430
600
  * write succeeds. One adapter operation owns that ordering. */
431
- cut?(ids: readonly string[]): boolean | Promise<boolean>;
601
+ cut?(ids: readonly string[]): StructuralClipboardOutcome;
432
602
  /** Paste as children of `parentId`; `null` means the adapter document root.
433
603
  * The shell derives the current selection's parent so ordinary Paste creates
434
604
  * siblings, matching scene-hierarchy editors. */
435
- paste?(parentId: string | null): boolean | Promise<boolean>;
605
+ paste?(parentId: string | null): StructuralClipboardOutcome;
436
606
  /** Honest affordance preflight. It need only cover adapter/session state;
437
607
  * the actual paste still validates the live system clipboard. */
438
608
  canPaste?(parentId: string | null): boolean;
@@ -538,7 +708,11 @@ export interface AssetDropContext {
538
708
  /** Asset drop (hierarchy + viewport). */
539
709
  export interface AssetDropProvider {
540
710
  accepts(nodeId: string, assetPath: string, context?: AssetDropContext): boolean;
541
- drop(nodeId: string, assetPath: string, context?: AssetDropContext): void | Promise<void>;
711
+ /** A drop that lands as an ELEMENT in the game's own source is a structural
712
+ * write, so it answers with the same {@link StructuralWriteOutcome} the rest
713
+ * of {@link StructureProvider}'s writing verbs do — a lane whose drop only
714
+ * mutates the running graph returns `void`. */
715
+ drop(nodeId: string, assetPath: string, context?: AssetDropContext): StructuralWriteOutcome;
542
716
  }
543
717
 
544
718
  /** Plain-object rect shape shared by {@link RectProvider} — the same fields a real
@@ -708,6 +882,19 @@ export type NodeCreationSite =
708
882
  * the anchor; survives a cold remount, because the file is truth and the next
709
883
  * mount re-derives from it.
710
884
  * Proof: `packages/editor/test/oid-source-persistence.test.ts`.
885
+ * - `source-structure` — the edit is not a VALUE at all: it adds, removes,
886
+ * moves or re-nests an ELEMENT of the game's own source (`delete`,
887
+ * `duplicate`, `wrap`/`unwrap`, `create`, `reorder`, `reparent`, `group`).
888
+ * It is a kind rather than a flavour of `source-prop` because it resolves on
889
+ * a DIFFERENT DOOR — the dialect's structural writer, not its attribute
890
+ * writer — and a lane can have one without the other. Claiming `source-prop`
891
+ * for a structural edit is exactly the classifier/writer split the
892
+ * persistence pipe exists to make impossible: it names a value lane that
893
+ * never carried the bytes. Its landing contract adds one gate the prop lane
894
+ * does not have: the write is WHOLE-FILE and checksum-guarded, so an op
895
+ * resolved against a stale element index must fail rather than land at the
896
+ * wrong offset.
897
+ * Proof: `packages/editor/test/structural-write-pipe.test.ts`.
711
898
  * - `construction-literal` — the value is a literal in the CONSTRUCTION
712
899
  * EXPRESSION that built the object (`new THREE.Mesh(…)` in the game's own
713
900
  * served module). Same landing/remount contract, plus one gate the prop lane
@@ -740,6 +927,7 @@ export type NodeCreationSite =
740
927
  */
741
928
  export type WriteAnchorKind =
742
929
  | 'source-prop'
930
+ | 'source-structure'
743
931
  | 'construction-literal'
744
932
  | 'physics-binding'
745
933
  | 'data-record'
@@ -793,6 +981,7 @@ export interface WriteAck {
793
981
  */
794
982
  const WRITE_ANCHOR_KIND_PRESENCE: Readonly<Record<WriteAnchorKind, true>> = {
795
983
  'source-prop': true,
984
+ 'source-structure': true,
796
985
  'construction-literal': true,
797
986
  'physics-binding': true,
798
987
  'data-record': true,
@@ -824,22 +1013,23 @@ export function emptyWriteAnchorKindCounts(): Record<WriteAnchorKind, number> {
824
1013
  * document-backed world's truth is the document, not a construction site).
825
1014
  * Present ⇒ every id gets an answer, anchored or reasoned.
826
1015
  */
827
- export interface CreationSiteProvider {
828
- anchor(id: string): NodeCreationSite;
829
- /**
830
- * The write-side sibling of {@link anchor}: which LANE would carry an edit to
831
- * this node's transform, in the closed vocabulary above. Exposed beside the
832
- * anchor because that is where every reader already looks, and because a
833
- * caller sweeping a hierarchy needs it to find a representative node per kind
834
- * (`vgai doctor`'s edit-write walk is that caller).
835
- *
836
- * Absent ⇒ this adapter plans no lanes it can distinguish; the reader has only
837
- * the anchor's own shape, which cannot tell two source lanes apart.
838
- * `undefined` for a particular id this adapter plans no write for THAT id
839
- * (a synthetic hierarchy row, an id no child of a composite owns) — which is
840
- * not the same as `live-only`, a lane that really does land on a live object.
841
- */
842
- writeAnchorKind?(id: string): WriteAnchorKind | undefined;
1016
+ export interface AuthoringTruth {
1017
+ /** Where the runtime object came from in the game's own source or data. */
1018
+ readonly site: NodeCreationSite;
1019
+ /** The lane THIS property edit would actually take. `undefined` means the
1020
+ * subject is synthetic or otherwise has no write plan; `live-only` is a real
1021
+ * plan whose destination is the running object. */
1022
+ readonly writeAnchorKind: WriteAnchorKind | undefined;
1023
+ }
1024
+
1025
+ /**
1026
+ * The TRUTH-family binding. One resolution answers both where a subject came
1027
+ * from and where an edit to one of its properties lands. A caller may not read
1028
+ * those facts through separate providers: that was the classifier/writer split
1029
+ * the persistence pipe exists to remove.
1030
+ */
1031
+ export interface TruthProvider {
1032
+ resolve(id: string, property: string): AuthoringTruth;
843
1033
  }
844
1034
 
845
1035
  /** A plain world-space point. Component handles cross the authoring seam as
@@ -931,7 +1121,10 @@ export interface SpatialHandleLayer {
931
1121
  export interface SpatialHandlesProvider {
932
1122
  layers(id: string): readonly SpatialHandleLayer[];
933
1123
  preview(id: string, handleId: string, worldPosition: SpatialPoint3): void;
934
- commit(id: string, handleId: string, worldPosition: SpatialPoint3): void | Promise<void>;
1124
+ /** Commit the preview and pass through the owning writer's acknowledgement.
1125
+ * `void` remains the honest live-only/no-write floor; callers must not grade
1126
+ * it as a persisted round trip. */
1127
+ commit(id: string, handleId: string, worldPosition: SpatialPoint3): StructuralWriteOutcome;
935
1128
  }
936
1129
 
937
1130
  export interface AuthoringAdapter {
@@ -967,9 +1160,8 @@ export interface AuthoringAdapter {
967
1160
  readonly colorSample?: ColorSampleProvider;
968
1161
  /** D4 — storybook stories. Absent ⇒ no stories for any node in this adapter. */
969
1162
  readonly stories?: StoriesProvider;
970
- /** WO-IDX1 the source location that constructed a node's live object.
971
- * Absent this adapter indexes no creation sites. */
972
- readonly creationSite?: CreationSiteProvider;
1163
+ /** Projection subject source/data anchor and write lane, resolved together. */
1164
+ readonly truth?: TruthProvider;
973
1165
  /** Adapter-owned component guides and direct-manipulation points. */
974
1166
  readonly spatialHandles?: SpatialHandlesProvider;
975
1167
  /** Asset drop (hierarchy + viewport). Absent ⇒ this adapter accepts no drops. */
@@ -1030,7 +1222,7 @@ const AUTHORING_PROVIDER_PRESENCE: Readonly<Record<AuthoringProviderKey, true>>
1030
1222
  text: true,
1031
1223
  colorSample: true,
1032
1224
  stories: true,
1033
- creationSite: true,
1225
+ truth: true,
1034
1226
  spatialHandles: true,
1035
1227
  assetDrop: true,
1036
1228
  subscribe: true,