@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/CLAUDE.md +87 -0
  2. package/README.md +84 -2
  3. package/dist/dist-manifest.json +218 -0
  4. package/dist/eyes/index.js +109 -0
  5. package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
  6. package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
  7. package/dist/fat/pkg-node/package.json +12 -0
  8. package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
  9. package/dist/frqtl/compat/boot-node.js +34 -0
  10. package/dist/frqtl/compat/engine.js +127 -0
  11. package/dist/frqtl/compat/package-root.js +24 -0
  12. package/dist/frqtl/compat/providers/wasm-engine.js +2501 -0
  13. package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
  14. package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
  15. package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
  16. package/dist/frqtl/compat/renderer/Filters.js +163 -0
  17. package/dist/frqtl/compat/renderer/RenderFamily.js +85 -0
  18. package/dist/frqtl/compat/renderer/RenderMesh.js +100 -0
  19. package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
  20. package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
  21. package/dist/frqtl/compat/renderer/captureStill-common.js +259 -0
  22. package/dist/frqtl/compat/renderer/display-slot.js +96 -0
  23. package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
  24. package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
  25. package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
  26. package/dist/frqtl/compat/renderer/providers/index.js +22 -0
  27. package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
  28. package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
  29. package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
  30. package/dist/frqtl/compat/renderer/providers/three-provider.js +2474 -0
  31. package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
  32. package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
  33. package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
  34. package/dist/frqtl/compat/vendor.js +196 -0
  35. package/dist/frqtl/node-entry.js +17 -0
  36. package/dist/frqtl/objects/FrqtlApp.js +2922 -0
  37. package/dist/frqtl/objects/ObjectFactory.js +297 -0
  38. package/dist/frqtl/objects/RecipeContext.js +322 -0
  39. package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
  40. package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
  41. package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
  42. package/dist/frqtl/objects/instruments/capture-host.js +453 -0
  43. package/dist/frqtl/objects/instruments/capture.js +711 -0
  44. package/dist/frqtl/objects/instruments/hosted-eyes.js +985 -0
  45. package/dist/frqtl/objects/kinds-entry.js +14 -0
  46. package/dist/frqtl/objects/kinds.js +190 -0
  47. package/dist/frqtl/objects/objects/Box.js +273 -0
  48. package/dist/frqtl/objects/objects/Bullet.js +155 -0
  49. package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
  50. package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
  51. package/dist/frqtl/objects/objects/DirectedTunnel.js +412 -0
  52. package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
  53. package/dist/frqtl/objects/objects/LightSource.js +354 -0
  54. package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
  55. package/dist/frqtl/objects/objects/Tube.js +471 -0
  56. package/dist/frqtl/objects/objects/absorbRadiation.js +310 -0
  57. package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
  58. package/dist/frqtl/objects/objects/controlAction.js +89 -0
  59. package/dist/frqtl/objects/objects/controlBind.js +278 -0
  60. package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
  61. package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
  62. package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
  63. package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
  64. package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
  65. package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
  66. package/dist/frqtl/objects/objects/makeControlsForObject.js +824 -0
  67. package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
  68. package/dist/frqtl/objects/objects/slitLinerScatter.js +263 -0
  69. package/dist/frqtl/objects/objects/sustainedSource.js +299 -0
  70. package/dist/frqtl/objects/objects/tagger.js +228 -0
  71. package/dist/frqtl/objects/schema/engine-scopes.js +469 -0
  72. package/dist/frqtl/objects/schema/index.js +256 -0
  73. package/dist/frqtl/objects/schema/scopes.js +457 -0
  74. package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
  75. package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
  76. package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
  77. package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
  78. package/dist/frqtl/objects/setup/_worldInit.js +162 -0
  79. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
  80. package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
  81. package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
  82. package/dist/frqtl/objects/setup/setupConeEmitter3D.js +251 -0
  83. package/dist/frqtl/objects/setup/setupDoubleSlit.js +327 -0
  84. package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
  85. package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
  86. package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
  87. package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
  88. package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
  89. package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
  90. package/dist/frqtl/objects/setup/setupLightSourceLab.js +280 -0
  91. package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
  92. package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
  93. package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
  94. package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1259 -0
  95. package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
  96. package/dist/frqtl/objects/setup/setupTunnelLab.js +242 -0
  97. package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
  98. package/dist/frqtl/objects/setup/setupWaveInterference.js +178 -0
  99. package/dist/frqtl/objects/v1-replacements.js +30 -0
  100. package/dist/frqtl/package-core.js +23 -0
  101. package/dist/frqtl/parts/CameraControls.js +643 -0
  102. package/dist/frqtl/parts/CameraPath.js +575 -0
  103. package/dist/frqtl/parts/CollisionActions.js +35 -0
  104. package/dist/frqtl/parts/CollisionTypes.js +31 -0
  105. package/dist/frqtl/parts/DetectorWall.js +496 -0
  106. package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
  107. package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
  108. package/dist/frqtl/parts/FrqtlVision.js +818 -0
  109. package/dist/frqtl/parts/GraphingDetectorWall.js +490 -0
  110. package/dist/frqtl/parts/LargeObject.js +406 -0
  111. package/dist/frqtl/parts/LightColor.js +255 -0
  112. package/dist/frqtl/parts/LiveChart.js +285 -0
  113. package/dist/frqtl/parts/LogicGate.js +720 -0
  114. package/dist/frqtl/parts/OvenBox.js +612 -0
  115. package/dist/frqtl/parts/PointSource.js +763 -0
  116. package/dist/frqtl/parts/Rectangle.js +200 -0
  117. package/dist/frqtl/parts/SlitLightSource.js +234 -0
  118. package/dist/frqtl/parts/SlitScreen.js +628 -0
  119. package/dist/frqtl/parts/SphereEmitter.js +946 -0
  120. package/dist/frqtl/parts/Trace.js +508 -0
  121. package/dist/frqtl/parts/Tunnel.js +1057 -0
  122. package/dist/frqtl/parts/Wall.js +1211 -0
  123. package/dist/frqtl/parts/index.js +33 -0
  124. package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
  125. package/dist/frqtl/scenes/absorbing-wall.js +32 -0
  126. package/dist/frqtl/scenes/atomic-model.js +88 -0
  127. package/dist/frqtl/scenes/big-bang.js +46 -0
  128. package/dist/frqtl/scenes/blackbody.js +81 -0
  129. package/dist/frqtl/scenes/circuit-sat.js +127 -0
  130. package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
  131. package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
  132. package/dist/frqtl/scenes/counting-wall.js +38 -0
  133. package/dist/frqtl/scenes/double-slit.js +31 -0
  134. package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
  135. package/dist/frqtl/scenes/dse-one-slit.js +114 -0
  136. package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
  137. package/dist/frqtl/scenes/dse-tunnel-rebuild.js +244 -0
  138. package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
  139. package/dist/frqtl/scenes/dse-two-slits.js +132 -0
  140. package/dist/frqtl/scenes/empty.js +14 -0
  141. package/dist/frqtl/scenes/laser-buildup.js +49 -0
  142. package/dist/frqtl/scenes/mirror-3d.js +56 -0
  143. package/dist/frqtl/scenes/mirror-route.js +140 -0
  144. package/dist/frqtl/scenes/mirror.js +38 -0
  145. package/dist/frqtl/scenes/omni-3d.js +27 -0
  146. package/dist/frqtl/scenes/omni-source.js +22 -0
  147. package/dist/frqtl/scenes/self-interference.js +39 -0
  148. package/dist/frqtl/scenes/smoking-gun.js +122 -0
  149. package/dist/frqtl/scenes/straight-walk.js +22 -0
  150. package/dist/frqtl/scenes/sun.js +39 -0
  151. package/dist/frqtl/scenes/two-source.js +37 -0
  152. package/dist/frqtl/scenes/wave.js +26 -0
  153. package/dist/shim/bundle/frqnb.js +112 -0
  154. package/dist/shim/bundle/index.js +121 -0
  155. package/dist/shim/bundle/indexHtml.js +107 -0
  156. package/dist/shim/bundle/manifest.js +95 -0
  157. package/dist/shim/bundle/zip.js +243 -0
  158. package/dist/shim/bus.js +67 -0
  159. package/dist/shim/controls/index.js +627 -0
  160. package/dist/shim/controls/registry.js +274 -0
  161. package/dist/shim/engine/artifact-shell.js +212 -0
  162. package/dist/shim/engine/boot-phases.js +44 -0
  163. package/dist/shim/engine/engine-stack-boot.js +59 -0
  164. package/dist/shim/engine/router.js +692 -0
  165. package/dist/shim/experiment-frame/index.js +296 -0
  166. package/dist/shim/expr/ast-schema.js +369 -0
  167. package/dist/shim/field-descriptor/index.js +326 -0
  168. package/dist/shim/frame-data/index.js +224 -0
  169. package/dist/shim/index.js +67 -0
  170. package/dist/shim/notebook/canvas-guard.js +82 -0
  171. package/dist/shim/notebook/catalog.js +35 -0
  172. package/dist/shim/notebook/format.js +736 -0
  173. package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
  174. package/dist/shim/notebook/kernel.js +672 -0
  175. package/dist/shim/notebook/knob.js +151 -0
  176. package/dist/shim/notebook/module-resolver.js +260 -0
  177. package/dist/shim/notebook/notebook-knobs.js +65 -0
  178. package/dist/shim/notebook/produced-bindings.js +67 -0
  179. package/dist/shim/notebook/realm-registry.js +121 -0
  180. package/dist/shim/notebook/realm-transport.js +73 -0
  181. package/dist/shim/notebook/setup-builder.js +510 -0
  182. package/dist/shim/notebook/store.js +159 -0
  183. package/dist/shim/notebook/vendor-importmap.js +134 -0
  184. package/dist/shim/notebook/view-builder.js +189 -0
  185. package/dist/shim/notebook/view-ctx.js +100 -0
  186. package/dist/shim/notebook/view-handle.js +202 -0
  187. package/dist/shim/notebook/warmup.js +102 -0
  188. package/dist/shim/package.json +4 -0
  189. package/dist/shim/page-params/index.js +214 -0
  190. package/dist/shim/project/identity.js +112 -0
  191. package/dist/shim/project/index.js +19 -0
  192. package/dist/shim/project/link.js +402 -0
  193. package/dist/shim/project/node-specifier-hook.mjs +35 -0
  194. package/dist/shim/project/register-node.mjs +17 -0
  195. package/dist/shim/project/specifiers.js +106 -0
  196. package/dist/shim/registry/copy-gate.js +110 -0
  197. package/dist/shim/registry/exhibits.js +455 -0
  198. package/dist/shim/registry/experiments.js +215 -0
  199. package/dist/shim/registry/index.js +19 -0
  200. package/dist/shim/registry/scene-source.js +157 -0
  201. package/dist/shim/registry/walkthrough.js +807 -0
  202. package/dist/shim/render/capture-envelope.js +279 -0
  203. package/dist/shim/render/heat-colormap.js +92 -0
  204. package/dist/shim/render/render-stages.js +855 -0
  205. package/dist/shim/render/sun-look.js +33 -0
  206. package/dist/shim/render-state/apply.js +262 -0
  207. package/dist/shim/render-state/index.js +74 -0
  208. package/dist/shim/rpc/index.js +207 -0
  209. package/dist/shim/runtime/index.js +1023 -0
  210. package/dist/shim/storage/boot.js +69 -0
  211. package/dist/shim/storage/index.js +169 -0
  212. package/dist/shim/storage/local-storage-provider.js +230 -0
  213. package/dist/shim/storage/make-namespace-store.js +132 -0
  214. package/dist/shim/storage/modules.js +249 -0
  215. package/dist/shim/storage/projects.js +150 -0
  216. package/llms.txt +12 -0
  217. package/package.json +62 -4
@@ -0,0 +1,161 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * makeControlsForRenderSurface.js — the RENDER-side factory twin of
5
+ * `makeControlsForObject.js` (WS Studio Controls Realignment, Bite S5a).
6
+ *
7
+ * ── Layer 1: API contract ────────────────────────────────────────────────────
8
+ *
9
+ * `makeControlsForRenderSurface({runner})` → `{ controls }`.
10
+ *
11
+ * Walks the ACTIVE renderer provider's OPTIONAL `renderCapabilities()` method
12
+ * (`lib/frqtl/compat/renderer/providers/{p5,three}-provider.js` — the
13
+ * `getCameraControl()` optional-capability precedent: absent ⇒ no render
14
+ * controls, never an error) and mechanically emits ONE `channel:'render'`
15
+ * contract control descriptor per declared capability — never a hand-mirrored
16
+ * per-page control array (this is exactly what retires `_egptFx.controls`,
17
+ * `fx-experiment.js`'s bespoke `{key,label,min,max,step}` vocabulary — see
18
+ * that file's own updated docstring).
19
+ *
20
+ * Each renderCapabilities() entry is a `lib/shim/field-descriptor`-shaped
21
+ * field descriptor (`{name, type, constraint, unit, default, description}` —
22
+ * §7 r2 CEO call C1's shared vocabulary): `type:'number'` → a `scalar`
23
+ * control; `type:'enum'` → an `enum` control; a render-only `type:'boolean'`
24
+ * → a 0/1 `scalar`; `type:'string'` (an OPEN-ID asset id) → no control. The
25
+ * conversions REUSE `makeControlsForObject.js`'s OWN handlers (exposed as
26
+ * `RENDER_TYPE_HANDLERS`) rather than re-deriving bounds/label/format logic.
27
+ *
28
+ * Every generated control binds `{object:'render', prop:capability.name}`, and
29
+ * `controlBind.js`'s render branch ferries it to `runner.setRenderOption` /
30
+ * `runner.getRenderOption` — the ONE render-option gate (declared key, valid
31
+ * value, and the `filter.<name>` toggle routing all live there).
32
+ *
33
+ * @param {object} opts
34
+ * @param {object} opts.runner — the FrqtlApp (REQUIRED — the only way
35
+ * to reach the active provider's `renderCapabilities()`/read the runner's
36
+ * own universal render options).
37
+ * @returns {{controls: object[]}} `controls` are ALREADY normalized
38
+ * (`lib/shim/controls` `normalizeControlDescriptor` has already run —
39
+ * fail-fast: a factory bug throws HERE, at generation time).
40
+ * @throws {Error} FAIL-LOUD — missing `opts.runner`, a capability descriptor
41
+ * whose `type` is not `RENDER_TYPE_HANDLERS`-dispatchable (see
42
+ * `makeControlsForObject.js`'s own docstring for why the remaining
43
+ * ObjectSchema field types are NOT reused here), or a malformed capability
44
+ * descriptor (`lib/shim/field-descriptor`'s own `normalizeFieldDescriptor`
45
+ * validates the envelope before conversion).
46
+ *
47
+ * ── Arrow legality ────────────────────────────────────────────────────────
48
+ *
49
+ * Same precedent as `makeControlsForObject.js`'s own "Arrow legality" note:
50
+ * this file `require`s `lib/shim/controls/index.js` and
51
+ * `lib/shim/field-descriptor/index.js` directly (an `objects → shim` schema-
52
+ * contract edge, NOT an engine dependency) — `lib/frqtl/objects` is annotated
53
+ * in `scripts/verify-arrows.mjs` as "user-space sugar — NOT a violation."
54
+ *
55
+ * ── Layer 2: physics behavior ────────────────────────────────────────────────
56
+ *
57
+ * Zero physics — render is output-boundary only (root CLAUDE.md §Object
58
+ * architecture: "render never feeds physics"). Generating a control changes
59
+ * NOTHING about the rendered scene until a host actually calls the generated
60
+ * control's `bind` through `resolveBind`, which itself calls
61
+ * `setRenderOption`/`filter()` — the IDENTICAL effect as calling those
62
+ * directly by hand.
63
+ *
64
+ */
65
+
66
+ import * as ControlsSchema from '../../../shim/controls/index.js';
67
+ import * as FieldDescriptorSchema from '../../../shim/field-descriptor/index.js';
68
+ import { MakeControlsForObject as _MakeControlsForObjectNode } from './makeControlsForObject.js';
69
+
70
+
71
+ // ── Imports (lazy-guarded, mirrors makeControlsForObject.js's own precedent) ─
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+ /**
81
+ * Resolve a capability's CURRENT live value (to seed the generated control) through
82
+ * `runner.getRenderOption` — the one read path (it answers `filter.<name>` too). Falls back
83
+ * to `field.default` while the option has never been written.
84
+ */
85
+ function _resolveCapabilityValue(runner, field) {
86
+ var live = runner.getRenderOption(field.name);
87
+ return (live !== undefined) ? live : field.default;
88
+ }
89
+
90
+ /** A synthetic single-field wrapper — see file docstring: `_numberHandler`/
91
+ * `_enumHandler` fall through to their generic `wrapper[propName]` read for
92
+ * any `env.kind` outside {'Frame','Universe'}, so this is all `env.wrapper`
93
+ * needs to be for them to work UNMODIFIED. */
94
+ function _singleFieldWrapper(name, value) {
95
+ var w = {};
96
+ w[name] = value;
97
+ return w;
98
+ }
99
+
100
+ /**
101
+ * @see file docstring for the full contract.
102
+ */
103
+ function makeControlsForRenderSurface(opts) {
104
+ opts = opts || {};
105
+ var runner = opts.runner;
106
+ if (!runner) {
107
+ throw new Error('[makeControlsForRenderSurface] requires opts.runner (an FrqtlApp). ' +
108
+ '(FAIL-LOUD.)');
109
+ }
110
+
111
+ var normalizeControlDescriptor = ControlsSchema.normalizeControlDescriptor;
112
+ var normalizeFieldDescriptor = FieldDescriptorSchema.normalizeFieldDescriptor;
113
+ var RENDER_TYPE_HANDLERS = _MakeControlsForObjectNode.RENDER_TYPE_HANDLERS;
114
+
115
+ // Optional capability (the getCameraControl() precedent) — absent
116
+ // renderCapabilities() ⇒ empty output, never an error.
117
+ var rawCaps = (typeof runner.renderCapabilities === 'function') ? runner.renderCapabilities() : null;
118
+ if (!Array.isArray(rawCaps)) { rawCaps = []; }
119
+
120
+ var controls = [];
121
+
122
+ for (var i = 0; i < rawCaps.length; i++) {
123
+ // Fail-fast: a provider's malformed renderCapabilities() entry throws
124
+ // HERE, at generation time — never silently skipped.
125
+ var field = normalizeFieldDescriptor(rawCaps[i]);
126
+
127
+ var handler = RENDER_TYPE_HANDLERS[field.type];
128
+ if (!handler) {
129
+ throw new Error('[makeControlsForRenderSurface] capability "' + field.name + '" declares ' +
130
+ 'type "' + field.type + '" — only ' + Object.keys(RENDER_TYPE_HANDLERS).join(', ') +
131
+ ' are dispatchable render-capability types today (the ones real render capabilities ' +
132
+ 'use). (FAIL-LOUD — never a silent drop.)');
133
+ }
134
+
135
+ var value = _resolveCapabilityValue(runner, field);
136
+ var env = {
137
+ kind: 'RenderSurface',
138
+ wrapper: _singleFieldWrapper(field.name, value),
139
+ ctx: {},
140
+ opts: {},
141
+ objectName: 'render'
142
+ };
143
+ var result = handler(field, env);
144
+ if (!result || result.kind !== 'control') { continue; } // defined skip — no live value to seed a control
145
+
146
+ var descriptor = result.descriptor;
147
+ descriptor.channel = 'render'; // override the physics default — this IS a render-channel control
148
+ controls.push(normalizeControlDescriptor(descriptor));
149
+ }
150
+
151
+ return { controls: controls };
152
+ }
153
+
154
+ // ── Export ────────────────────────────────────────────────────────────────────
155
+
156
+ var MakeControlsForRenderSurface = {
157
+ makeControlsForRenderSurface: makeControlsForRenderSurface
158
+ };
159
+
160
+ export { MakeControlsForRenderSurface };
161
+
@@ -0,0 +1,263 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * slitLinerScatter.js — the ONE authority for the double-slit edge-diffraction
5
+ * liner's collision op-program (behavior × resetLocalTime, ORTHOGONAL).
6
+ *
7
+ * DSE LAB (2026-07-13, CEO-chartered systematic controls): "behavior × reset are
8
+ * ORTHOGONAL controls" — this file composes the velocity-tail (`behavior`) and the
9
+ * `local_time=0` reset independently, so every (behavior, resetLocalTime) combo is
10
+ * reachable, INCLUDING combos that had no name before (`bounce`+reset,
11
+ * `ejectY`+reset, `ejectYReflect`+reset). The pre-existing compound `slitScatter`
12
+ * variants (EXPERIMENT LOOP 2026-07-12 + MATRIX EXTENSION) map onto specific combos
13
+ * — see `LEGACY_VARIANT_MAP` — proven byte-identical by
14
+ * `slit-liner-scatter.test.mjs`'s composed-vs-legacy equality tests.
15
+ *
16
+ * ONE AUTHORITY, NO FORK: both `SlitScreen.js` (construction-time, via the legacy
17
+ * `linerScatter` string) and the `dse-lab` experiment page (LIVE, via explicit
18
+ * `{behavior, resetLocalTime}`) call `buildLinerProgram`/`applyLinerScatter` here —
19
+ * neither re-derives the op shapes.
20
+ *
21
+ * ## Ground-truth (pkg-node instrumentation, EXPERIMENT LOOP delivery report)
22
+ * The liner's compiled `RANDOM_XY` op DOES fire; its OWN writes are `vx=random()`
23
+ * (ALWAYS non-negative) and `vy=random()*2-1` (already symmetric) — the sign bias
24
+ * the CEO's original hypothesis named (vy) was actually in `vx`, which the engine's
25
+ * 2D diagonal wave-kick (`frame.rs` `axy` branch, `dy_perp=(vx>0?+1:-1)*is_heads`)
26
+ * keys its transverse-Y sign off; `is_heads` is coherently biased by the SAME
27
+ * `local_time=0` phase-lock mechanism (`compute_spin`) the theorist found driving
28
+ * the separately-escalated 3D z-asymmetry. `local_time=0` is deliberate — it is
29
+ * what CREATES the y-interference fringes for the un-amended collision — hence
30
+ * this file's variants for visual/statistical A/B comparison, not a "fix."
31
+ *
32
+ * ## The FIVE composable BEHAVIORS (velocity-tail + eject-prefix + charge-draw?)
33
+ * - `randomXY` — generic BLOCK eject prefix + `vx=random()` (always +), `vy` symmetric; charge redrawn.
34
+ * - `symmetric` — generic BLOCK eject prefix + `vx` ALSO symmetric (fixes the actual bias found); charge redrawn.
35
+ * - `bounce` — generic BLOCK eject prefix + `vy=-vy` only (elastic reflection); no charge redraw.
36
+ * - `ejectY` — EXPLICIT Y-into-cavity eject prefix (not generic 'closest' — see
37
+ * `buildLinerProgram`'s docstring for why); NO velocity writes at all; no charge redraw.
38
+ * - `ejectYReflect` — `ejectY`'s prefix PLUS `vy=-vy`; no charge redraw.
39
+ *
40
+ * `resetLocalTime` (boolean, independent axis): when true, appends `set frame.local_time = 0`
41
+ * AFTER the velocity-tail and BEFORE any charge-draw (matching the ORIGINAL native
42
+ * RANDOM_XY/RANDOM_Y op order this file's `randomXY`+reset=true combo reproduces
43
+ * byte-for-byte).
44
+ */
45
+
46
+ import { compileActions as _compileActions, buildEjectOps as _buildEjectOpsFn, buildTriggerExpr as _buildTriggerExprFn, applyProgramTrigger as _applyProgramTriggerFn } from './actionCompiler.js';
47
+ import * as _astSchema from '../../../shim/expr/ast-schema.js';
48
+ import { CollisionTypes } from '../../parts/CollisionTypes.js';
49
+
50
+ // ── Imports (Node.js) or globals (browser) ──────────────────────────
51
+
52
+
53
+ /** The five composable behaviors (see module docstring). */
54
+ var BEHAVIORS = ['randomXY', 'symmetric', 'bounce', 'ejectY', 'ejectYReflect'];
55
+
56
+ /**
57
+ * LEGACY_VARIANT_MAP — the pre-existing compound `slitScatter` string ->
58
+ * {behavior, resetLocalTime} this composable builder reproduces byte-for-byte.
59
+ * `'current'` maps to `null` — the SPECIAL no-op sentinel (construction leaves
60
+ * the LO's own naturally-compiled `RANDOM_XY` program untouched, ZERO
61
+ * `updateObjectProperty` calls — verified by test). `'current'` the STRING is
62
+ * therefore NOT the same as calling this builder with `{behavior:'randomXY',
63
+ * resetLocalTime:true}` (which WOULD produce an identical-shaped program, but
64
+ * via an explicit push — the two are proven ops-equal by test, but only the
65
+ * string sentinel gets the "untouched, zero side effects" default-path contract).
66
+ */
67
+ var LEGACY_VARIANT_MAP = {
68
+ current: null,
69
+ noreset: { behavior: 'randomXY', resetLocalTime: false },
70
+ symmetric: { behavior: 'symmetric', resetLocalTime: true },
71
+ combined: { behavior: 'symmetric', resetLocalTime: false },
72
+ bounce: { behavior: 'bounce', resetLocalTime: false },
73
+ ejectY: { behavior: 'ejectY', resetLocalTime: false },
74
+ ejectYReflect: { behavior: 'ejectYReflect', resetLocalTime: false },
75
+ };
76
+
77
+ /**
78
+ * Resolve a legacy `slitScatter` string to `{behavior, resetLocalTime}`, or
79
+ * `null` for `'current'`/omitted (the no-op sentinel). Throws on an unknown
80
+ * string (fail-loud, never silent).
81
+ * @param {string|undefined|null} slitScatterString
82
+ * @returns {{behavior:string, resetLocalTime:boolean}|null}
83
+ */
84
+ function resolveLegacyVariant(slitScatterString) {
85
+ if (slitScatterString === undefined || slitScatterString === null) return null;
86
+ if (!Object.prototype.hasOwnProperty.call(LEGACY_VARIANT_MAP, slitScatterString)) {
87
+ throw new Error('[slitLinerScatter] unknown slitScatter legacy variant: ' + slitScatterString);
88
+ }
89
+ return LEGACY_VARIANT_MAP[slitScatterString];
90
+ }
91
+
92
+ /**
93
+ * THE composable builder: `behavior` (velocity-tail + its own eject-prefix
94
+ * choice + whether it redraws charge) × `resetLocalTime` (an independent
95
+ * boolean — appends `local_time=0` after the tail, before any charge-draw).
96
+ *
97
+ * WHY `ejectY`/`ejectYReflect` use an EXPLICIT `intoCavityDir` instead of the
98
+ * generic `'closest'` nearest-face eject every other behavior reuses: the
99
+ * liner is a WIDE, THIN strip (much wider in x than tall in y) sitting at one
100
+ * edge of a slit's cavity — `'closest'` could pick a side (L/R) face for some
101
+ * approach angles/positions, ejecting the particle OUT of the cavity or back
102
+ * the way it came, the opposite of "lands between the slits" (ground-truthed,
103
+ * MATRIX EXTENSION delivery report). A `_top`-positioned liner always ejects
104
+ * `'bottom'` (deeper into the cavity below it); a `_bottom`-positioned liner
105
+ * always ejects `'top'` — the caller supplies which via `intoCavityDir`.
106
+ *
107
+ * @param {'randomXY'|'symmetric'|'bounce'|'ejectY'|'ejectYReflect'} behavior
108
+ * @param {boolean} resetLocalTime - independent reset axis (see module docstring).
109
+ * @param {Object} ast - the ast-schema module (Lit/Bin/Cond/Cmp/Fn/Ref/HANDLE_FRAME/HANDLE_SELF).
110
+ * @param {Function} buildEjectOpsFn - `actionCompiler.js`'s exported `buildEjectOps`.
111
+ * @param {Function} compileActionsFn - `actionCompiler.js`'s exported `compileActions`
112
+ * (used ONLY by randomXY/symmetric/bounce to derive the generic BLOCK/'closest'
113
+ * eject prefix — the SAME helper the liner's own constructor uses).
114
+ * @param {string} loName - the liner LO's name (passed to compileActionsFn for the
115
+ * generic-eject-prefix behaviors; irrelevant to ejectY/ejectYReflect).
116
+ * @param {string} reposition - the liner LO's `.reposition` (normally 'closest');
117
+ * passed through to compileActionsFn for the generic-eject-prefix behaviors.
118
+ * @param {'top'|'bottom'|'left'|'right'|'near'|'far'} [intoCavityDir] - REQUIRED for
119
+ * ejectY/ejectYReflect only. It is forwarded VERBATIM to `_buildEjectOps`, whose named-
120
+ * direction domain is all six faces — so this is the eject NORMAL, not a Y-only knob.
121
+ * The `ejectY` NAME is historical (the DSE liner is a horizontal strip that always ejects
122
+ * on Y); a vertical corridor (`Tunnel` axis 'y') passes 'left'/'right' through the SAME
123
+ * builder. Documenting the true domain, not widening it — no code change.
124
+ * @param {boolean} [hasRealDepth=false] - ws-dse-3d AC2 (CEO drive #2 round-2
125
+ * ruling: "It is immediately obvious that the reflections on one of the
126
+ * axes is missing... immediately obvious that we are not using the same
127
+ * objects with the same contracts"): when true, `randomXY`/`symmetric`
128
+ * (the two SCATTER behaviors — a wall-normal-agnostic velocity redraw, not
129
+ * a physical y-wall reflection) ALSO append `set frame.vz = random()*2-1`
130
+ * — the SAME `randomSigned()` term already used for `vy` — so a scatter
131
+ * event on a real-depth-armed tunnel redraws all 3 axes, not just 2.
132
+ * `bounce` (a genuine y-NORMAL wall reflection) and `ejectY`/`ejectYReflect`
133
+ * (Y-specific by design, the DSE liner's own cavity-eject convention) are
134
+ * UNCHANGED regardless of this flag — reflecting vz off a Y-normal wall
135
+ * would be physically wrong (this is a TOP/BOTTOM face; NEAR/FAR is
136
+ * handled correctly already by native BOUNCE, see the round-1 deliverable).
137
+ * Default `false` — byte-identical for every existing caller (`SlitScreen.js`'s
138
+ * own 2D liner construction, which never passes this).
139
+ * @returns {Array<Object>} the FULL composed ops array (eject-prefix + tail +
140
+ * [reset] + [charge-draw]) — ready to wrap as `{on:'collision', ops}`.
141
+ */
142
+ function buildLinerProgram(behavior, resetLocalTime, ast, buildEjectOpsFn, compileActionsFn, loName, reposition, intoCavityDir, hasRealDepth) {
143
+ var Lit = ast.Lit, Bin = ast.Bin, Cond = ast.Cond, Cmp = ast.Cmp, Fn = ast.Fn, Ref = ast.Ref;
144
+ var HF = ast.HANDLE_FRAME, HS = ast.HANDLE_SELF;
145
+ var randomSigned = function () { return Bin('-', Bin('*', Fn('random', []), Lit(2)), Lit(1)); }; // random()*2-1
146
+ var chargeDraw = function () { return Cond(Cmp('<', Fn('random', []), Lit(0.5)), Lit(0), Lit(1)); };
147
+
148
+ var ejectPrefix, velocityTail, includesCharge;
149
+
150
+ if (behavior === 'ejectY' || behavior === 'ejectYReflect') {
151
+ ejectPrefix = buildEjectOpsFn(intoCavityDir, HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn) || [];
152
+ velocityTail = (behavior === 'ejectYReflect')
153
+ ? [{ set: HF + '.vy', to: Bin('*', Ref(HF, 'vy'), Lit(-1)) }]
154
+ : [];
155
+ includesCharge = false;
156
+ } else if (behavior === 'randomXY') {
157
+ var ejectOnlyRX = compileActionsFn(['BLOCK'], loName, reposition, CollisionTypes.NONE);
158
+ ejectPrefix = ejectOnlyRX.program ? ejectOnlyRX.program.ops : [];
159
+ velocityTail = [
160
+ { set: HF + '.vx', to: Fn('random', []) },
161
+ { set: HF + '.vy', to: randomSigned() },
162
+ ];
163
+ if (hasRealDepth) { velocityTail.push({ set: HF + '.vz', to: randomSigned() }); }
164
+ includesCharge = true;
165
+ } else if (behavior === 'symmetric') {
166
+ var ejectOnlySym = compileActionsFn(['BLOCK'], loName, reposition, CollisionTypes.NONE);
167
+ ejectPrefix = ejectOnlySym.program ? ejectOnlySym.program.ops : [];
168
+ velocityTail = [
169
+ { set: HF + '.vx', to: randomSigned() },
170
+ { set: HF + '.vy', to: randomSigned() },
171
+ ];
172
+ if (hasRealDepth) { velocityTail.push({ set: HF + '.vz', to: randomSigned() }); }
173
+ includesCharge = true;
174
+ } else if (behavior === 'bounce') {
175
+ var ejectOnlyBounce = compileActionsFn(['BLOCK'], loName, reposition, CollisionTypes.NONE);
176
+ ejectPrefix = ejectOnlyBounce.program ? ejectOnlyBounce.program.ops : [];
177
+ velocityTail = [{ set: HF + '.vy', to: Bin('*', Ref(HF, 'vy'), Lit(-1)) }];
178
+ includesCharge = false;
179
+ } else {
180
+ throw new Error('[slitLinerScatter] unknown behavior: ' + behavior);
181
+ }
182
+
183
+ var ops = ejectPrefix.concat(velocityTail);
184
+ if (resetLocalTime) { ops = ops.concat([{ set: HF + '.local_time', to: Lit(0) }]); }
185
+ if (includesCharge) { ops = ops.concat([{ set: HF + '.charge', to: chargeDraw() }]); }
186
+ return ops;
187
+ }
188
+
189
+ /**
190
+ * Apply a scatter/behavior combo to an already-constructed liner LO — pushes
191
+ * the composed op-program LIVE via `universe.updateObjectProperty` (the SAME
192
+ * mechanism `LargeObject.prototype.recompileActions` uses for its own live
193
+ * collision-type edits). Accepts EITHER a legacy `slitScatter` string (used by
194
+ * `SlitScreen`'s constructor) OR an explicit `{behavior, resetLocalTime}` pair
195
+ * (used LIVE by the `dse-lab` page's controls) — ONE authority, no fork.
196
+ *
197
+ * `'current'`/omitted (legacy string) is the SPECIAL no-op sentinel: the LO
198
+ * keeps its own naturally-compiled program, ZERO `updateObjectProperty` calls.
199
+ *
200
+ * @param {Object} linerLO - the liner LargeObject instance (has `.handle`,
201
+ * `.name`, `.reposition`, `.program`).
202
+ * @param {Object} universe
203
+ * @param {'top'|'bottom'} intoCavityDir - REQUIRED for ejectY/ejectYReflect only.
204
+ * @param {Object} [opts]
205
+ * @param {string} [opts.slitScatter] - legacy compound variant string (mutually
206
+ * exclusive with opts.behavior/opts.resetLocalTime).
207
+ * @param {string} [opts.behavior] - explicit composable behavior (LIVE controls).
208
+ * @param {boolean} [opts.resetLocalTime] - explicit composable reset (LIVE controls).
209
+ * @param {boolean} [opts.hasRealDepth=false] - ws-dse-3d AC2 — see
210
+ * `buildLinerProgram`'s own param doc. Default false — byte-identical for
211
+ * every existing caller.
212
+ */
213
+ function applyLinerScatter(linerLO, universe, intoCavityDir, opts) {
214
+ opts = opts || {};
215
+ var resolved;
216
+ if (opts.behavior !== undefined) {
217
+ resolved = { behavior: opts.behavior, resetLocalTime: !!opts.resetLocalTime };
218
+ } else {
219
+ resolved = resolveLegacyVariant(opts.slitScatter);
220
+ }
221
+ if (resolved === null) return; // 'current'/omitted — no-op sentinel, LO untouched.
222
+
223
+ var ast = _astSchema;
224
+ var ops = buildLinerProgram(
225
+ resolved.behavior, resolved.resetLocalTime, ast,
226
+ _buildEjectOpsFn, _compileActions,
227
+ linerLO.name, linerLO.reposition, intoCavityDir, !!opts.hasRealDepth
228
+ );
229
+ var newProgram = { on: 'collision', ops: ops };
230
+
231
+ // O4b FIX (tester Rule-0 finding, 2026-07-13): every liner is FIRST
232
+ // constructed via `new LargeObject(..., CollisionTypes.RANDOM_XY,
233
+ // [CollisionActions.BLOCK], ...)`, whose NATIVE `compileActions` compilation
234
+ // attaches a program-level `trigger` (`Fn('intersects',[HANDLE_FRAME,
235
+ // HANDLE_SELF])` — see actionCompiler.js's G2b design: "if trigger==0, ALL
236
+ // ops are skipped atomically"). This function's REPLACEMENT program was
237
+ // missing that `trigger` entirely — pkg-node isolation proved this ALONE
238
+ // (re-pushing an AST-IDENTICAL ops array onto an already-'current'-
239
+ // constructed liner, with no other change) regressed detector total
240
+ // 103->10, i.e. every push-based variant (every non-'current' legacy
241
+ // string, AND every LIVE composable combo) has been running with a
242
+ // missing/no-op trigger since this mechanism existed — not a behavior- or
243
+ // reset-specific effect. All liner collision types here are BLOCK-style
244
+ // overlap-only (never WALL), so `withPastEdge=false` always; reuse the
245
+ // SAME builder+attacher `compileActions` itself uses (one authority).
246
+ var trigger = _buildTriggerExprFn(
247
+ linerLO.reposition || null, false,
248
+ ast.HANDLE_FRAME, ast.HANDLE_SELF,
249
+ ast.Lit, ast.Ref, ast.Cmp, ast.Cond, ast.Fn
250
+ );
251
+ _applyProgramTriggerFn(newProgram, trigger);
252
+
253
+ linerLO.program = newProgram; // mirror on the JS wrapper (inspector/serialize source of truth)
254
+ if (universe && typeof universe.updateObjectProperty === 'function') {
255
+ universe.updateObjectProperty(linerLO.handle, 'LargeObject', 'program', newProgram);
256
+ }
257
+ }
258
+
259
+ // ── Export ───────────────────────────────────────────────────────────
260
+
261
+
262
+ export { BEHAVIORS, LEGACY_VARIANT_MAP, resolveLegacyVariant, buildLinerProgram, applyLinerScatter };
263
+