diff --git a/proposals/MIGRATION-PLAN.adoc b/proposals/MIGRATION-PLAN.adoc index 50e65c0..ccc04da 100644 --- a/proposals/MIGRATION-PLAN.adoc +++ b/proposals/MIGRATION-PLAN.adoc @@ -195,9 +195,12 @@ Heuristic: | C2 wave 2a | DONE | Scalar multiply/divide (2026-06-05, Opus). `Mul`/`Div` turned out to be pure *scalar* value-transforms (not memory ops), so they need *no* array ABI — split out of "wave 2" and landed now. *1 kernel* `VmMulDiv` (11 exports) under `proposals/idaptik/migrated/`. Brain = the reversible ancilla multiply (`c := c + a*b`, inverse `c := c - a*b`) + the quotient/remainder divide whose dividend is reconstructable (`q*b + r == a` for all b incl. 0); the intentional-flaw in-place/simple variants migrated as value transforms with no rt==id claim. *Four gates green:* 1/1 compile, *3322/3322 parity* (incl. mul reversibility roundtrip + div reconstruction), G3 n/a (numeric transform), 1/1 assail-clean. *2 new i32 ABI facts:* (a) JS `a*b` loses precision >2^53 → oracle must use `Math.imul` to match `i32.mul`; (b) `i32.div_s` TRAPS on `b==0` and `INT_MIN/-1` (ReScript wraps the latter) → brain guards both (nested-`if`, avoiding the unverified `&&` codegen path) so the wasm is total. Evidence: `migrated/EVIDENCE-C2-wave2a.adoc`. NEXT: C2 wave 2b. | C2 wave 2b | DONE | Memory/stack/port/control opcodes (2026-06-05, Opus). *The "needs an array/linear-memory ABI" premise was WRONG and re-decomposition overturned it:* reading the sources, the VM's memory/stack/port buffers are not arrays in the brain — `VmState.res` stores every one as string-keyed dict slots (`_mem:N`, `_s:N`, `_pin:port`), i.e. host-side STATE (senses). The integer brains are all scalar. *4 kernels* `VmMemory` (LOAD/STORE), `VmStack` (PUSH/POP), `VmPort` (RECV/SEND), `VmControl` (IF_POS/IF_ZERO/LOOP) covering *9 opcodes*. *Four gates green:* 4/4 compile, *1568/1568 parity* (incl. load/store/sp/recv/send round-trips), G3 n/a (transforms/predicates), 4/4 assail-clean. No-kernel senses (classified, not migrated): Call (orchestration), CoprocessorCall (tombstone — never implemented), State/VmState/VM/SubroutineRegistry/VmStateCoprocessor/InstructionCoprocessor (state containers / bridges). Evidence: `migrated/EVIDENCE-C2-wave2b.adoc`. *No array ABI was required* (the blocker was a triage-bucket artefact). *Cluster C2 COMPLETE.* NEXT: C3. | C3 | DONE | Coprocessor wiring layer (2026-06-05, Opus) — *classified, no new brains to extract.* C3's 17 `src/shared/` files are the host-side bridge layer that exposes C1's already-migrated wasm brains to the game engine. Verified de-dup (by constant, not name): the integer cores C3 needs (`compute_gate`/`data_limit_for_domain` 513/16/259/1024, `max_concurrent_compute` 10, `is_transient` 503/504/429, policy table, DeviceType/GameEvent ordinals) are *all* already migrated + 4-gate-verified in C1. The 12 `*Coprocessor.res` are pure-FFI passthrough (0 logic lines); `Kernel_Compute`/`RetryPolicy` (src/shared) are async/float wrappers over C1 brains; `DeviceType`/`GameEvent` add string-gated ops (string wall, Phase F). No 4-gate run (nothing new to verify). Bridges stay host-side as the essential wasm-loading glue (ADDITIVE, FeaturePacks-flagged); their ReScript→glue fate is a Phase-Ω cutover decision. Evidence: `migrated/EVIDENCE-C3.adoc`. NEXT: C5. -| C5 | DONE | Engine coprocessors (2026-06-05, Opus). *2 integer brains extracted + 9 senses classified.* `Maths` (clamp/lerp-milli/dist_sq — float-wall convention, sqrt host-side) and — the strategic one — `Random`, the deterministic **xmur3 + mulberry32 PRNG**, the multiplayer-reproducibility backbone (host iterates the seed string + does the final u32→[0,1) float div; brain owns the i32 mixing). *Four gates green:* 2/2 compile, *2108/2108 parity* (Random **bit-exact** vs the JS source over the full i32 domain + UTF-16 code units, incl. the `>>>` emulation + signed-i32 multiplier constants), G3 n/a (transforms/mixing), 2/2 assail-clean. Senses (classified, not migrated): 5 `*Coprocessor` FFI bridges, Resize/GetResolution (float+DOM), Audio (Pixi), Navigation (screen-stack orchestration + async asset load, effect-gated), WaitFor (timing). Evidence: `migrated/EVIDENCE-C5.adoc`. NEXT: C11. -| C9..C12 | TODO | Remaining clusters per `migration-map.json`: *C11 (10, "pure integer presentation state" — font scaling/keyboard-nav/popup logic)* next (highest brain-yield); then C9 (16, game-loop/AffineTEA/VeriSim types — mixed) and C10 (21, utils/tools/companions/narrative/proven — mixed); C12 (43, render-glue screens/PixiJS bindings — senses-heavy, migrated last). Established scalar/enum/predicate recipe. Genuine compiler gates remain: string wall (52 non-test .res — note the `[len:i32][utf8]` layout is already in `codegen.ml:375`, only the *ops* are missing), effect wall (110); unary-`~` codegen bug is a candidate Phase-F fix. -| F+ | TODO | Compiler walls (string backend, then effects). +| C5 | DONE | Engine coprocessors (2026-06-05, Opus). *2 integer brains extracted + 9 senses classified.* `Maths` (clamp/lerp-milli/dist_sq — float-wall convention, sqrt host-side) and — the strategic one — `Random`, the deterministic **xmur3 + mulberry32 PRNG**, the multiplayer-reproducibility backbone (host iterates the seed string + does the final u32→[0,1) float div; brain owns the i32 mixing). *Four gates green:* 2/2 compile, *2108/2108 parity* (Random **bit-exact** vs the JS source over the full i32 domain + UTF-16 code units, incl. the `>>>` emulation + signed-i32 multiplier constants), G3 n/a (transforms/mixing), 2/2 assail-clean. Senses (classified, not migrated): 5 `*Coprocessor` FFI bridges, Resize/GetResolution (float+DOM), Audio (Pixi), Navigation (screen-stack orchestration + async asset load, effect-gated), WaitFor (timing). Evidence: `migrated/EVIDENCE-C5.adoc`. NEXT: C11 (done below). +| C11 | DONE | UI and accessibility coprocessors (2026-06-05) — *classified, NO_NEW_BRAINS.* All 10 files are host-side senses. `DualAlert.res` integer core (alert-level ordinals, escalation, trigger-milli, scrub, overall-threat, dispatch predicates) was already extracted in C6 (`DualAlert.affine`, 613/613 parity, LOSSLESS boundary proof). `DualAlertCoprocessor.res` is the existing ReScript FFI bridge to that wasm. `ForceLayoutCoprocessor.res` wraps `forcelayout.wasm`, a float-only physics kernel (euclidean/repulsion/attraction/velocity/alpha — f64 throughout; float-wall: brain stays host-side). Remaining 7 files (`Button`, `HubButton`, `NavButton`, `Label`, `VolumeSlider`, `InventoryUI`, `HardwareWiring`) are pure PixiJS/PixiUI senses with zero separable integer computation. No 4-gate run. Evidence: `migrated/EVIDENCE-C11.adoc`. NEXT: C9. +| C9 | DONE | Game-loop / AffineTEA / VeriSim types (2026-06-05) — *classified, NO_NEW_BRAINS.* All 16 files are host-side senses. *4 bridge files* wrap already-live wasm brains: `AffineTEA.res` → `titlescreen.wasm` (TEA title-screen state machine), `AffineTEARouter.res` → `router.wasm` (affine nav back-stack), `PixiCoprocessor.res` → `pixi.wasm` (AffineScript PixiJS scene driver), `VmCoprocessor.res` → `vmcoprocessor.wasm` (C2). *8 VeriSimDB/Burble HTTP clients* (`SaveGame`, `VeriSimDrift`, `VeriSimFederation`, `VeriSimHexad`, `VeriSimProvenance`, `VeriSimSearch`, `VeriSimVcl`, `BurbleIntegration`) are async `fetch` wrappers — effect-gated throughout. `VeriSimTypes.res` enum variants (`modality`/`hexadStatus`/`driftLevel`) appear integer-clean but `modalityToString`/`modalityFromString` use string literals and all enums are used only in string-bearing records — string-wall gated, no standalone computation. `VeriSimError.res` constructors all carry `string` payloads — string-wall gated. `Main.res` / `GameLoop.res` are effect-gated orchestration; GameLoop integer counters are trivial increments interleaved with float physics and mutable JS objects. No 4-gate run. Evidence: `migrated/EVIDENCE-C9.adoc`. NEXT: C10. +| C10 | DONE | Utils/tools/companions/narrative/proven (2026-06-05) — *classified, NO_NEW_BRAINS.* All 21 files are host-side senses. *9 bridge files* wrap already-live wasm brains: `moletairecoprocessors.wasm` (Moletaire behaviour FSM), `moletairehunger.wasm` (hunger ordinals), `missionbriefing.wasm` (mission briefing state machine), `safefloat.wasm` (safe-float arithmetic), `passwordcracker.wasm` (password-crack score), `portscanner.wasm` (port-scan classifier), `colorpalette.wasm` (color lookup), `fontscale.wasm` (font-scale arithmetic), `gamei18n.wasm` (plural-form + language-cycle). Float-wall: `MoletaireHunger.res` (f64 gravity/hunger physics — integer enum functions already in `moletairehunger.wasm`), `SafeAngle.res` (Math.atan2/cos/sin/mod_float, all f64). String-wall: `ProvenError.res` (all constructors carry string fields), `MoletairePersistence.res` (equipmentToString/FromString), `Locales.res` (5-language translation dicts, ~650 string pairs), `PolyglotI18n.res` (String.replaceAll, async fetch of locale JSON). Effect-gated: `Announcer.res` (AccessibilitySettings + DomA11y), `DomA11y.res` (%raw DOM), `ColorPalette.res` (AccessibilitySettings mutable refs; color tables already in wasm), `KeyboardNav.res` (%raw window events), `PanicHandler.res` (Console.error), `UserSettings.res` (Storage/Audio/DesktopIntegration; stance enum string-serialized). No 4-gate run. Evidence: `migrated/EVIDENCE-C10.adoc`. NEXT: C12. +| C12 | DONE | Render-glue screens/PixiJS bindings (2026-06-05) — *classified, NO_NEW_BRAINS.* All 43 files are host-side senses. *12 bridge files* wrap already-live wasm brains (companionsrenderlogic, devicesrenderlogic, enemiesrenderlogic, toolspickupsrenderlogic, playerrenderlogic, narrativepopupsrenderlogic, balanceanalyser, locationdata, screenglitchfx, screensrunlooplogic, uirenderlogic, verisimprovenrenderlogic). *23 screen/popup files* are effect-gated PixiJS rendering orchestrators (GameI18n string calls, Motion.animate, mutable refs, Navigation APIs). *4 binding files* (Motion, Pixi, PixiSound, PixiUI) are pure `external` FFI declarations wrapping host-side JS libraries. *Phase Ω cluster sequence (C11→C9→C10→C12) complete.* Genuine compiler gates remain: string wall (71 corpus files — `[len:i32][utf8]` layout already in `codegen.ml:375`, only ops missing), effect wall (111 corpus files). Evidence: `migrated/EVIDENCE-C12.adoc`. +| F+ | TODO | Compiler walls (string backend, then effects). *Cluster migration complete; these are the next milestones.* | Ω | TODO (access-gated) | Cutover + ReScript extinction. |=== diff --git a/proposals/idaptik/migrated/EVIDENCE-C10.adoc b/proposals/idaptik/migrated/EVIDENCE-C10.adoc new file mode 100644 index 0000000..64cb2a2 --- /dev/null +++ b/proposals/idaptik/migrated/EVIDENCE-C10.adoc @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell += Cluster C10 (utils/tools/companions/narrative/proven) — migration evidence (2026-06-05) +:toc: macro + +[IMPORTANT] +==== +*Verdict: NO_NEW_BRAINS — coprocessor bridges, float-wall physics, string-wall +i18n/persistence/errors, and effect-gated accessibility/settings orchestration.* +All 21 files are host-side senses. No new AffineScript kernels are required +because: + +1. Nine `*Coprocessor.res` bridge files are pure `external` FFI declarations + wrapping already-compiled wasm brains: + `moletairecoprocessors.wasm` (Moletaire behaviour FSM), + `moletairehunger.wasm` (hunger ordinals — already extracted), + `missionbriefing.wasm` (mission briefing state machine), + `safefloat.wasm` (safe-float arithmetic coprocessor), + `passwordcracker.wasm` (password-crack score engine), + `portscanner.wasm` (port-scan result classifier), + `colorpalette.wasm` (color-palette lookup), + `fontscale.wasm` (font-scale arithmetic), + `gamei18n.wasm` (plural-form selection + language-cycle arithmetic). +2. Two float-wall files (`MoletaireHunger.res`, `SafeAngle.res`): + `MoletaireHunger.res` contains gravity/hunger physics + (`calculatePull`, `calculateTotalPull`, `calculateHungerRate` — all f64); + the integer-flavoured `getBehaviour`/`hungerColor`/`willEatObjective` enum + functions are already extracted to `moletairehunger.wasm`. + `SafeAngle.res` is pure trigonometry (`Math.atan2`, `normalize`, + `Math.cos`, `Math.sin`, `mod_float`) — float-wall throughout. +3. String-wall files: `ProvenError.res` (all constructors carry `operation`, + `message`, `errorType` string fields), `MoletairePersistence.res` + (`equipmentToString`/`equipmentFromString` string serialization via + `localStorage`), `Locales.res` (five-language translation dicts; all data + is string key→value, `getTranslations(string)` dispatches on string param), + `PolyglotI18n.res` (`String.replaceAll`, JSON string parsing, async + `fetch` of locale JSON files, string template literals in `tw`/`tn`). +4. Effect-gated files: `Announcer.res` (gated on + `AccessibilitySettings.isScreenReaderEnabled()`; passes string messages to + `DomA11y.announce`), `DomA11y.res` (all `%raw` DOM manipulation — + `window.matchMedia`, `document.createElement`, `region.textContent`), + `ColorPalette.res` (branches on `AccessibilitySettings.isHighContrastEnabled()` + and `getColorBlindMode()` mutable JS refs; integer color tables are + already extracted to `colorpalette.wasm`), `KeyboardNav.res` (all `%raw` + `window.addEventListener/removeEventListener`, `e.key`, `e.preventDefault`), + `PanicHandler.res` (`Console.error`, string template literals), + `UserSettings.res` (`Storage` API, `Audio` API, `DesktopIntegration`; + the `stance` enum serializes via string literals and has no standalone + integer computation). + +No 4-gate recipe is applied; no new wasm is produced. +==== + +toc::[] + +== File-by-file disposition + +[cols="3,1,4",options="header"] +|=== +| File | Disposition | Reason +| `src/app/companions/MoletaireCoprocessorsCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `MoletaireCoprocessorsBridge.js` → `moletairecoprocessors.wasm`. Pure `external` declarations. Moletaire behaviour FSM brain already live. +| `src/app/companions/MoletaireHunger.res` | SENSES (float-wall) | Gravity/hunger physics: `calculatePull`, `calculateTotalPull`, `calculateHungerRate` — all f64. Integer-flavoured functions (`getBehaviour`, `hungerColor`, `willEatObjective`) already extracted to `moletairehunger.wasm`. +| `src/app/companions/MoletaireHungerCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `MoletaireHungerCoprocessorBridge.js` → `moletairehunger.wasm`. Pure `external` declarations. Hunger-ordinal brain already live. +| `src/app/companions/MoletairePersistence.res` | SENSES (string-wall gated) | localStorage persistence: `equipmentToString`/`equipmentFromString` string serialization, `Storage.getString`/`setBool`. String-wall prevents extraction. +| `src/app/narrative/MissionBriefingCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `MissionBriefingCoprocessorBridge.js` → `missionbriefing.wasm`. Pure `external` declarations. Mission briefing state machine already live. +| `src/app/proven/ProvenError.res` | SENSES (string-wall gated) | Error type `provenError` with `operation: string`, `message: string`, `errorType: string` fields. All constructors string-bearing. String-wall prevents extraction. +| `src/app/proven/SafeAngle.res` | SENSES (float-wall) | Pure trigonometry: `Math.atan2`, `normalize`, `Math.cos`, `Math.sin`, `mod_float`. All f64 operations. No integer brain. +| `src/app/proven/SafeFloatCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `SafeFloatCoprocessorBridge.js` → `safefloat.wasm`. Pure `external` declarations. Safe-float arithmetic brain already live. +| `src/app/tools/PasswordCrackerCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `PasswordCrackerCoprocessorBridge.js` → `passwordcracker.wasm`. Pure `external` declarations. Password-crack score brain already live. +| `src/app/tools/PortScannerCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `PortScannerCoprocessorBridge.js` → `portscanner.wasm`. Pure `external` declarations. Port-scan classifier brain already live. Inline `deviceTypeId`/`securityLevelId` enum dispatch needs no wasm. +| `src/app/utils/Announcer.res` | SENSES (effect-gated) | Screen-reader announcer: gated on `AccessibilitySettings.isScreenReaderEnabled()`, passes string messages to `DomA11y.announce`. No integer computation. +| `src/app/utils/ColorPalette.res` | SENSES (effect-gated) | Color lookup tables (`alertColor(int)`, `deviceColor`, `securityColor`) read `AccessibilitySettings.isHighContrastEnabled()` and `getColorBlindMode()` mutable JS refs. Integer color tables already extracted to `colorpalette.wasm`. Effect-gated selector stays host-side. +| `src/app/utils/ColorPaletteCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `ColorPaletteCoprocessorBridge.js` → `colorpalette.wasm`. Pure `external` declarations. Color-palette lookup brain already live. +| `src/app/utils/DomA11y.res` | SENSES (effect-gated) | DOM accessibility utilities: all `%raw` — `window.matchMedia`, `document.createElement`, `region.textContent`, event listeners. No integer computation. +| `src/app/utils/FontScaleCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `FontScaleCoprocessorBridge.js` → `fontscale.wasm`. Pure `external` declarations (`scaleFloat`, `scaleInt`, `clampScale`, `scaleIntClamped`). Font-scale arithmetic brain already live. +| `src/app/utils/GameI18nCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `GameI18nCoprocessorBridge.js` → `gamei18n.wasm`. Pure `external` declarations (`languageClamp`, `nextLanguage`, `selectPluralForm`). Plural-form selection + language-cycle brain already live. +| `src/app/utils/KeyboardNav.res` | SENSES (effect-gated) | All `%raw` window event wiring: `window.addEventListener/removeEventListener('keydown')`, `e.key`, `e.preventDefault`. No integer computation. +| `src/app/utils/Locales.res` | SENSES (string-wall gated) | Five-language translation dicts (~130 keys × 5 languages). `getTranslations(string)` dispatches on string lang-code parameter. All data is string key→string value. String-wall throughout. +| `src/app/utils/PanicHandler.res` | SENSES (effect-gated) | Error reporting: `Console.error`, string template literals, `Promise.resolve()`. No integer computation. +| `src/app/utils/PolyglotI18n.res` | SENSES (string-wall + effect-gated) | Polyglot i18n wrapper: async `fetch` of locale JSON files, `String.replaceAll` interpolation, `tw`/`tn` string ops, mutable `ref` language state. String-wall + effect-wall prevents extraction. +| `src/app/utils/UserSettings.res` | SENSES (effect-gated) | Settings persistence: `Storage` API (getString/setBool/setNumber), `Audio` API (setMasterVolume/setBgmVolume/setSfxVolume), `DesktopIntegration`. The `stance` enum (Jessica/Q) serializes via string literals — string-wall gated; no standalone integer computation. +|=== + +== Already-live wasm brains referenced by C10 bridges + +The nine bridge files reference wasm kernels that are already compiled and +live in `public/assets/wasm/`: + +[cols="2,2,3",options="header"] +|=== +| Bridge file | Wasm target | Brain description +| `MoletaireCoprocessorsCoprocessor.res` | `moletairecoprocessors.wasm` | Moletaire companion behaviour FSM. Position/action decisions for the robotic mole companion. +| `MoletaireHungerCoprocessor.res` | `moletairehunger.wasm` | Hunger ordinal decisions: `getBehaviour`, `hungerColor`, `willEatObjective`, `objectiveAtRisk`. Already extracted; float-wall physics stays host-side. +| `MissionBriefingCoprocessor.res` | `missionbriefing.wasm` | Mission briefing state machine. Screen-progression logic. +| `SafeFloatCoprocessor.res` | `safefloat.wasm` | Safe-float arithmetic coprocessor. Guards float operations with formal safety bounds. +| `PasswordCrackerCoprocessor.res` | `passwordcracker.wasm` | Password-crack score engine. Integer scoring for crack success/failure states. +| `PortScannerCoprocessor.res` | `portscanner.wasm` | Port-scan result classifier. Maps scan outcomes to integer result codes. +| `ColorPaletteCoprocessor.res` | `colorpalette.wasm` | Color-palette lookup. Integer RGB hex values for alert/device/security levels. +| `FontScaleCoprocessor.res` | `fontscale.wasm` | Font-scale arithmetic. `scaleFloat`, `scaleInt`, `clampScale`, `scaleIntClamped` pure integer/float pixel-size operations. +| `GameI18nCoprocessor.res` | `gamei18n.wasm` | Plural-form selection and language-cycle arithmetic. `languageClamp` (0..4 band), `nextLanguage` (EN→ES→FR→DE→JA→EN cycle), `selectPluralForm` (count == 1 → singular). +|=== + +C10 being NO_NEW_BRAINS does not add to the wasm surface — all nine brains +were already live before C10 was triaged. + +== MoletaireHunger.res — float-wall analysis + +`MoletaireHunger.res` contains f64 physics that cannot cross the float-wall: + +* `calculatePull(dist: float, strength: float): float` — gravitational + attraction formula (`strength / (dist * dist)`). Pure f64. +* `calculateTotalPull(positions: array<...>, strength: float): (float, float)` + — sum of pull vectors over all attractor positions. f64 throughout. +* `calculateHungerRate(hungerMultiplier: float, ...): float` — speed-based + hunger drain. f64 physics. + +The integer-flavoured functions are NOT extractable because they are already +in `moletairehunger.wasm` from a prior migration pass: + +* `getBehaviour(hungerScore: float): behaviour` — float threshold → enum. +* `hungerColor(hungerScore: float): int` — float → RGB hex. Already in wasm. +* `willEatObjective(...)`, `objectiveAtRisk(...)` — float comparisons. + +Verdict: no new brain needed; the integer surface is already live. + +== SafeAngle.res — float-wall analysis + +All six functions in `SafeAngle.res` are pure f64 trigonometry: + +* `fromAtan2(dy: float, dx: float): safeAngle` — `Math.atan2`. +* `normalize(a: safeAngle): safeAngle` — fmod-based angle normalization. +* `diff(a: safeAngle, b: safeAngle): safeAngle` — angle subtraction with + normalization. +* `cos(a: safeAngle): float` — `Math.cos`. +* `sin(a: safeAngle): float` — `Math.sin`. +* `toFloat(a: safeAngle): float` — identity cast. + +The float-wall convention (AffineScript wasm backend has no f64 `sqrt` +or general float codegen) requires these to remain host-side. The `safefloat.wasm` +brain handles safe arithmetic on simple float ranges; trigonometry is a +separate concern with no integer-only reformulation possible. + +== String-wall analysis: ProvenError, MoletairePersistence, Locales, PolyglotI18n + +[cols="1,1,4",options="header"] +|=== +| File | Wall type | Why NOT extracted +| `ProvenError.res` | String-wall | Every constructor (`ProvenError` record) carries `operation: string`, `message: string`, `errorType: string` fields. No integer computation separable from the string payloads. +| `MoletairePersistence.res` | String-wall + effect | `equipmentToString`/`equipmentFromString` use string serialization throughout; `Storage.getString`/`setBool` are DOM-side effects. No integer brain separable. +| `Locales.res` | String-wall | Five-language dicts (~650 string pairs total). `getTranslations(langCode: string)` dispatches on a string parameter and returns `dict`. No integer computation exists — the entire module is translation data. +| `PolyglotI18n.res` | String-wall + effect | `tw` uses `String.replaceAll`; `tn` compares `count == 1.0` (float); async `fetch` for locale JSON; mutable `ref` language state. String ops throughout; effect-wall (async + mutable ref). +|=== + +In contrast, `GameI18nCoprocessor.res` already wraps `gamei18n.wasm` which +handles the *integer* core (`languageClamp`, `nextLanguage`, +`selectPluralForm`) — the AffineScript brain for this cluster's numeric +i18n logic is already live. + +== UserSettings.res — stance enum analysis + +`UserSettings.res` contains a 2-value `stance` enum (Jessica | Q). +At first glance it appears potentially extractable, but: + +1. `getCharacterStance()` reads `Storage.getString(keyStance)` and matches on + string values `"Q"` / `_` — string-wall gated. +2. `setCharacterStance(s: stance)` serializes to string literals `"Jessica"` / + `"Q"` before calling `Storage.setString` — string-wall gated. +3. The remaining functions (`getMasterVolume`, `getBgmVolume`, `getSfxVolume`, + `init`, `setMasterVolume`, `setBgmVolume`, `setSfxVolume`, + `setSystemTrayEnabled`) are pure effect-gated orchestration over `Audio` + and `Storage` APIs with float volume values. + +No standalone integer brain is separable from the string serialization and +effect-heavy orchestration. + +== Impact on remaining clusters + +C10 being NO_NEW_BRAINS does not affect the C12 roadmap. All nine wasm +brains referenced by C10 bridges are already live. The utilities/tools/ +companions/narrative/proven modules remain host-side ReScript throughout +Phase Ω. The completion-drive directive proceeds to *C12*. diff --git a/proposals/idaptik/migrated/EVIDENCE-C11.adoc b/proposals/idaptik/migrated/EVIDENCE-C11.adoc new file mode 100644 index 0000000..b163e5d --- /dev/null +++ b/proposals/idaptik/migrated/EVIDENCE-C11.adoc @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell += Cluster C11 (UI and accessibility coprocessors) — migration evidence (2026-06-05) +:toc: macro + +[IMPORTANT] +==== +*Verdict: NO_NEW_BRAINS — wiring/senses cluster.* All 10 files are host-side +senses (PixiJS UI components, coprocessor bridges, float-physics wiring). No +new AffineScript kernels are required because: + +1. The integer brain of `DualAlert.res` (alert-level ordinals, escalation, + trigger-milli, scrub, overall-threat, dispatch predicates) was already + extracted in *cluster C6* (`DualAlert.affine`, 613/613 parity, LOSSLESS + boundary proof). `DualAlertCoprocessor.res` is the existing ReScript + bridge to that wasm. +2. `ForceLayoutCoprocessor.res` is a bridge to `forcelayout.wasm` (a + float-only physics kernel — euclidean distance, repulsion/attraction + velocity deltas, velocity decay, position integration, alpha cooling). + The brain is f64-only; the float-wall convention defers it to host-side. +3. `Button`, `HubButton`, `NavButton`, `Label`, `VolumeSlider`, + `InventoryUI`, `HardwareWiring` are pure PixiJS/PixiUI components with + zero separable integer computation. + +No 4-gate recipe is applied; no new wasm is produced. +==== + +toc::[] + +== File-by-file disposition + +[cols="3,1,4",options="header"] +|=== +| File | Disposition | Reason +| `src/app/ui/DualAlert.res` | SENSES (brain in C6) | Integer core (`levelValue`, `levelColor`, `escalateLevel`, `scrubStep`, trigger-milli, cooldown-milli, `overallThreat`, `shouldDispatchAntiHacker`, `guardsCheckingTerminals`) already lives in `DualAlert.affine` (C6, 613/613 parity). Float progress/trigger-severity orchestration stays host-side. +| `src/app/ui/DualAlertCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `DualAlertCoprocessorBridge.js` → `dualalert.wasm`. Pure `external` declarations; no logic. +| `src/app/popups/ForceLayoutCoprocessor.res` | SENSES (float-brain bridge) | Wraps `forcelayout.wasm` float kernel (euclidean, repelDv, attractDv, decayVelocity, integratePosition, decayAlpha). All f64 primitives; float-wall convention: brain deferred to host-side. +| `src/app/ui/Button.res` | SENSES (PixiJS) | FancyButton nine-slice wrapper + FontScale + Audio SFX. Zero integer computation. +| `src/app/ui/HardwareWiring.res` | SENSES (PixiJS) | Interactive patch-panel with float catenary sag (`computeSagLocal`), quadratic Bézier cable rendering, pointer-event wiring, string template port IDs. No separable integer brain. +| `src/app/ui/HubButton.res` | SENSES (PixiJS) | Programmatic Graphics FancyButton variant. Zero integer computation. +| `src/app/ui/InventoryUI.res` | SENSES (UI) | `formatQuickbar` maps inventory slots to HUD display records. No integer computation. +| `src/app/ui/Label.res` | SENSES (PixiJS) | `Text.make` wrapper with centered anchor. Zero integer computation. +| `src/app/ui/NavButton.res` | SENSES (PixiJS) | Sidebar navigation FancyButton with active-indicator Graphics. Zero integer computation. +| `src/app/ui/VolumeSlider.res` | SENSES (PixiJS) | PixiUI Slider component factory. Zero integer computation. +|=== + +== DualAlert — de-duplication verification + +The C11 cluster description names `src/app/ui/DualAlert.res` as a source file. +C6's kernel inventory entry is: + +---- +DualAlert.affine | DualAlert.res — dual-threat alert level machine +---- + +Cross-check of C6 evidence versus C11 `DualAlert.res` content: + +* C6 `DualAlert.affine` exports: `level_value`, `level_color`, `escalate_level`, + `scrub_step`, `trigger_escalates`, `trigger_progress`, `trigger_level`, + `cooldown_progress`, `overall_threat`, `should_dispatch_anti_hacker`, + `guards_checking_terminals`. +* C11 `DualAlertCoprocessor.res` bridge methods: `levelValue`, `levelColour`, + `escalateLevel`, `scrubStep`, `triggerEscalates`, `triggerProgress`, + `triggerLevel`, `cooldownProgress`, `overallThreat`, `shouldDispatchAntiHacker`, + `guardsCheckingTerminals` — exact semantic correspondence. +* The float functions in `DualAlert.res` (`physicalSeverity`, `cyberSeverity`, + `triggerPhysical`, `triggerCyber`, `updateCooldown`) operate on the mutable + `alertState` record (float progress/cooldown fields) — these are host-side + orchestration that feeds integer primitives to the wasm. + +Verdict: *no new brain needed*; C6's `DualAlert.affine` covers the integer surface. +C6 evidence: `proposals/idaptik/migrated/EVIDENCE-C6.adoc` (Gate 2: 613/613 parity, +Gate 3: LOSSLESS boundary proof, Gate 4: clean). + +== Host-side senses: ForceLayout physics + +`ForceLayoutCoprocessor.res` bridges the following float-only exports of +`forcelayout.wasm`: + +* `euclidean(bridge, x1, y1, x2, y2): float` — Euclidean distance +* `repelDv(bridge, dist, d, strength, radius): float` — repulsion velocity delta +* `attractDv(bridge, dist, d, strength): float` — attraction velocity delta +* `decayVelocity(bridge, v, decay): float` — velocity damping +* `integratePosition(bridge, pos, v, alpha): float` — position integration step +* `decayAlpha(bridge, alpha, alphaDecay): float` — simulation cooling + +All parameters and return values are `float`. This is a pure f64 physics +kernel; the float-wall convention requires the brain to remain host-side +(the AffineScript wasm backend has no f64 sqrt or general float codegen). +No integer decomposition is applicable — the physics simulation requires +continuous-domain arithmetic throughout. + +== Impact on remaining clusters + +C11 being NO_NEW_BRAINS does not affect the C9/C10/C12 roadmap. The +`DualAlertCoprocessor` wasm is already live; the UI components remain host-side +ReScript throughout Phase Ω. The completion-drive directive proceeds to *C9*. diff --git a/proposals/idaptik/migrated/EVIDENCE-C12.adoc b/proposals/idaptik/migrated/EVIDENCE-C12.adoc new file mode 100644 index 0000000..7edf801 --- /dev/null +++ b/proposals/idaptik/migrated/EVIDENCE-C12.adoc @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell += Cluster C12 (render-glue screens, UI, and bindings) — migration evidence (2026-06-05) +:toc: macro + +[IMPORTANT] +==== +*Verdict: NO_NEW_BRAINS — render-logic coprocessor bridges, effect-gated screen +implementations, PixiJS/Motion/Sound/UI binding declarations.* +All 43 files are host-side senses. No new AffineScript kernels are required +because: + +1. Twelve `*RenderLogicCoprocessor.res` / `*Coprocessor.res` bridge files are + pure `external` FFI declarations wrapping already-compiled wasm brains: + `companionsrenderlogic.wasm`, `devicesrenderlogic.wasm`, + `enemiesrenderlogic.wasm`, `toolspickupsrenderlogic.wasm`, + `playerrenderlogic.wasm`, `narrativepopupsrenderlogic.wasm`, + `balanceanalyser.wasm`, `locationdata.wasm`, `screenglitchfx.wasm`, + `screensrunlooplogic.wasm`, `uirenderlogic.wasm`, + `verisimprovenrenderlogic.wasm`. +2. Twenty-three screen and popup implementation files (`CreditsScreen.res`, + `TitleScreen.res`, `LoadScreen.res`, `LoadoutScreen.res`, + `JessicaCustomiseScreen.res`, `ObserverScreen.res`, `QViewScreen.res`, + `SkillTreeScreen.res`, eleven location screens, `Bouncer.res`, `Logo.res`, + `MainScreen.res`, `TrainingMenuScreen.res`, `FeaturePacksPopup.res`, + `IntegrationsPopup.res`, `LanguagePopup.res`, `PausePopup.res`, + `LocationBase.res`) are effect-gated PixiJS rendering orchestrators. + Every file uses one or more of: `GameI18n.t()` string calls, string + template literals, `Motion.animate` tweening, `Navigation.showScreen`/ + `presentPopup` routing, `Announcer.status()`, mutable `ref(...)` animation + state, `Container`/`Sprite`/`Graphics`/`Text` DOM APIs, `%raw` JS blocks. + No standalone integer computation separable from the PixiJS/string/effect + orchestration. +3. Four binding files (`Motion.res`, `Pixi.res`, `PixiSound.res`, + `PixiUI.res`) are pure `external` FFI declarations wrapping host-side + JavaScript libraries (`motion`, `pixi.js`, `@pixi/sound`, `@pixi/ui`). + They are SENSES by definition — they ARE the host/wasm boundary. + +No 4-gate recipe is applied; no new wasm is produced. +==== + +toc::[] + +== File-by-file disposition + +[cols="3,1,4",options="header"] +|=== +| File | Disposition | Reason +| `src/app/companions/CompanionsRenderLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `CompanionsRenderLogicCoprocessorBridge.js` → `companionsrenderlogic.wasm`. Pure `external` declarations. Companions render-logic brain already live. +| `src/app/devices/DevicesRenderLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `DevicesRenderLogicCoprocessorBridge.js` → `devicesrenderlogic.wasm`. Pure `external` declarations. Devices render-logic brain already live. +| `src/app/enemies/EnemiesRenderLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `EnemiesRenderLogicCoprocessorBridge.js` → `enemiesrenderlogic.wasm`. Pure `external` declarations. Enemies render-logic brain (detection ordinals, euclidean distance, angle functions) already live. +| `src/app/pickups/ToolsPickupsRenderLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `ToolsPickupsRenderLogicCoprocessorBridge.js` → `toolspickupsrenderlogic.wasm`. Pure `external` declarations. Tools/pickups render-logic brain already live. +| `src/app/player/PlayerRenderLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `PlayerRenderLogicCoprocessorBridge.js` → `playerrenderlogic.wasm`. Pure `external` declarations. Player render-logic brain (body geometry, animation frames, trajectory arc) already live. +| `src/app/popups/FeaturePacksPopup.res` | SENSES (effect-gated) | PixiJS popup UI: `GameI18n.t()` string calls, `Motion.animate` tweening, `Announcer.status(string)`, `Navigation.dismissPopup`, `FeaturePacks.setInvertibleProgramming`/`setMoletaire` toggle callbacks. No integer computation. +| `src/app/popups/IntegrationsPopup.res` | SENSES (effect-gated) | PixiJS popup UI: string template literals (`\`${label}: ON/OFF\``), `mutable ref(bool)` toggle state, `DesktopIntegration` API calls, `UserSettings.setSystemTrayEnabled`, `GameI18n.t()`. No integer computation. +| `src/app/popups/LanguagePopup.res` | SENSES (effect-gated) | PixiJS popup UI: `LanguageSettings.cycleLanguage()`/`languageDisplayName()` string calls, `GameI18n.setLanguage()`, `Text.setText()` string updates, `Motion.animate`. No integer computation. +| `src/app/popups/NarrativePopupsRenderLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `NarrativePopupsRenderLogicCoprocessorBridge.js` → `narrativepopupsrenderlogic.wasm`. Pure `external` declarations. Network-desktop tree layout, curved-edge geometry, pan/zoom, `feedbackCycleNext` brain already live. +| `src/app/popups/PausePopup.res` | SENSES (effect-gated) | PixiJS popup UI: `GameI18n.t()` string calls, `Announcer.alert(string)`, `BlurFilter`, `Motion.animate`, `Navigation.dismissPopup/presentPopup`. No integer computation. +| `src/app/screens/BalanceAnalyserCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `BalanceAnalyserCoprocessorBridge.js` → `balanceanalyser.wasm`. Pure `external` declarations. Balance analysis brain (`impactValue`, `passesImpact`, `impactColor`, `winRatePct`) already live. +| `src/app/screens/CreditsScreen.res` | SENSES (effect-gated) | Scrolling credits screen: `%raw` `window.addEventListener/removeEventListener('keydown')`, `Motion.animate`, mutable `ref` scroll state, `Navigation.goBack`. No integer computation. +| `src/app/screens/JessicaCustomiseScreen.res` | SENSES (effect-gated) | Appearance customisation UI: string-returning `hairStyleName`/`hairColorName`/`clothingName` dispatch functions, PixiJS rendering, mutable customisation state record. String-wall + effect-gated. +| `src/app/screens/LoadScreen.res` | SENSES (effect-gated) | Asset loading progress bar: `Motion.animate`, `progress /. 100.0` scale arithmetic wired directly to PixiJS `Container.scale`. No standalone integer brain. +| `src/app/screens/LoadoutScreen.res` | SENSES (effect-gated) | Loadout selection UI: string concatenation (`"Current: " ++ current`, `"Available: " ++ Int.toString(...) ++ " options"`), `Text.make`, PixiJS containers, mutable refs. String-wall + effect-gated. +| `src/app/screens/LocationDataCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `LocationDataCoprocessorBridge.js` → `locationdata.wasm`. Pure `external` declarations. Location count/layout brain already live. +| `src/app/screens/ObserverScreen.res` | SENSES (effect-gated) | Multiplayer observer interface: string fields (`id`, `deviceIp`, `connectionId`), `Text.make` for camera grid/tactical display, mutable arrays. String-wall + effect-gated. +| `src/app/screens/QViewScreen.res` | SENSES (effect-gated) | Q's CCTV/blueprint view: string fields (`id`, `ipAddress`, `deviceType`, `status`), PixiJS rendering, mutable device/blueprint/camera state. String-wall + effect-gated. +| `src/app/screens/ScreenGlitchFxCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `ScreenGlitchFxCoprocessorBridge.js` → `screenglitchfx.wasm`. Pure `external` declarations. Glitch FX arithmetic brain already live. +| `src/app/screens/ScreensRunLoopLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `ScreensRunLoopLogicCoprocessorBridge.js` → `screensrunlooplogic.wasm`. Pure `external` declarations. Run-loop decisions, layout helpers, SkillTree XP, `victoryScore`, `gradeOrdinal` brain already live. +| `src/app/screens/SkillTreeScreen.res` | SENSES (effect-gated) | Skill tree display: string-returning `rankName`/`categoryName` dispatch; `rankColor` returns static int constants but is tightly coupled to PixiJS Text/Graphics rendering with no separable algorithmic content. SkillTree XP arithmetic already extracted to `screensrunlooplogic.wasm`. String-wall + effect-gated. +| `src/app/screens/TitleScreen.res` | SENSES (effect-gated) | Title screen consumer of already-live `titlescreen.wasm` (via `AffineTEA.load`) and `router.wasm` (via `AffineTEARouter.load`). Wires Wasm state back into PixiJS: `applyView` reads `AffineTEA.getSelected` and calls `Motion.animate`; navigation decisions come from Wasm. Audio BGM/SFX. Mutable `ref` bridge handles. The brain IS the already-live wasm; this file is the sense layer. +| `src/app/screens/locations/AtlasScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"atlas"`, `Navigation.showScreen`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/BackboneScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"backbone"`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/BusinessISPScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"business-isp"`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/CityScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"city"`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/DevHubScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"devhub"`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/FieldScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"field"`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/LabScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"lab"`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/LocationBase.res` | SENSES (effect-gated) | Thin delegate to `WorldBuilder.buildLocationScreen(location)`. The platformer world construction, PixiJS scene building, and physics are all in the effect-gated WorldBuilder layer. No integer computation. +| `src/app/screens/locations/NexusScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"nexus"`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/RegionalISPScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"regional-isp"`. Effect-gated via `LocationBase`. +| `src/app/screens/locations/RuralISPScreen.res` | SENSES (effect-gated) | Thin delegate: `LocationBase.makeLocationScreen(locationData, ...)` with string ID `"rural-isp"`. Effect-gated via `LocationBase`. +| `src/app/screens/main/Bouncer.res` | SENSES (effect-gated) | Bouncing-logo animation manager: `Random.float`, `Motion.animate`, mutable logo arrays and bounds `ref`. Effect-gated throughout. +| `src/app/screens/main/Logo.res` | SENSES (effect-gated) | Logo sprite with `Random.bool()` texture selection, mutable `direction`/`speed` state, float bounds computed from `Sprite.width/height`. Effect-gated + float arithmetic host-side. +| `src/app/screens/main/MainScreen.res` | SENSES (effect-gated) | Main game screen UI: `FancyButton`, `Navigation.presentPopup`, mutable `paused` state, `Bouncer` animation. Effect-gated. +| `src/app/screens/training/TrainingMenuScreen.res` | SENSES (effect-gated) | Training scenario menu: `GameI18n.t()` string calls for all titles/descriptions, PixiJS button grid, `Navigation.showScreen`. String-wall + effect-gated. +| `src/app/ui/UiRenderLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `UiRenderLogicCoprocessorBridge.js` → `uirenderlogic.wasm`. Pure `external` declarations. HUD, KernelMonitor, PowerView, ParticleField, FontScale, RoundedBox arithmetic brain already live. +| `src/app/verisimdb/VerisimProvenRenderLogicCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `VerisimProvenRenderLogicCoprocessorBridge.js` → `verisimprovenrenderlogic.wasm`. Pure `external` declarations. Play-time decomposition arithmetic (`playTimeHours`, `playTimeMinutes`, `playTimeHasHours`) brain already live. String formatting (`"Xh Ym"`) stays host-side. +| `src/bindings/Motion.res` | SENSES (binding) | Pure `external` FFI declarations wrapping the `motion` tweening library (`animate`, `then_`, `animateAsync`). SENSES by definition — this IS the host-library boundary. +| `src/bindings/Pixi.res` | SENSES (binding) | Pure `external` FFI declarations wrapping `pixi.js` (`Container`, `Sprite`, `Graphics`, `Text`, `Texture`, DOM types, `window`). SENSES by definition — this IS the PixiJS boundary. +| `src/bindings/PixiSound.res` | SENSES (binding) | Pure `external` FFI declarations wrapping `@pixi/sound` (`Sound.play/stop`, `sound.find/play/muteAll/pauseAll`). SENSES by definition. +| `src/bindings/PixiUI.res` | SENSES (binding) | Pure `external` FFI declarations wrapping `@pixi/ui` (`FancyButton`, `Slider`, `ProgressBar`, `List`, `Signal`). SENSES by definition. +|=== + +== Already-live wasm brains referenced by C12 bridges + +The twelve bridge files reference wasm kernels that are already compiled and +live in `public/assets/wasm/`: + +[cols="2,2,3",options="header"] +|=== +| Bridge file | Wasm target | Brain description +| `CompanionsRenderLogicCoprocessor.res` | `companionsrenderlogic.wasm` | Companions render-logic: visual Y, underground flag, body/nose/eye/lens/arc geometry, mound dimensions, hitbox positions, carried item offset. +| `DevicesRenderLogicCoprocessor.res` | `devicesrenderlogic.wasm` | Devices render-logic: uptime decomposition, traffic units, camera geometry, UPS battery, laptop storage, radial angle, status/power colours, drag/close arithmetic. +| `EnemiesRenderLogicCoprocessor.res` | `enemiesrenderlogic.wasm` | Enemies render-logic: detection-level ordinals (GUARD_DETECT/DRONE_DETECT/DOG_DETECT), euclidean distance, angle arithmetic, guard/drone/dog detect and colour functions. +| `ToolsPickupsRenderLogicCoprocessor.res` | `toolspickupsrenderlogic.wasm` | Tools/pickups render-logic: `pulsePhase`, `pulseAlpha`, `pickupDistance`, `pickupInRange`, `collectRise`, `pinY`, `crackProgressPct`. +| `PlayerRenderLogicCoprocessor.res` | `playerrenderlogic.wasm` | Player render-logic: body geometry, animation frame/speed/state, trajectory preview arc maths. +| `NarrativePopupsRenderLogicCoprocessor.res` | `narrativepopupsrenderlogic.wasm` | Narrative popups render-logic: NetworkDesktop tree layout, curved-edge geometry, pan/zoom, legend grid, slider percent, PowerView, `feedbackCycleNext`. +| `BalanceAnalyserCoprocessor.res` | `balanceanalyser.wasm` | Balance analyser: `impactValue`, `passesImpact`, `impactColor`, `hasCriticalIssues`, `round1`, `pct100`, `winRatePct`. +| `LocationDataCoprocessor.res` | `locationdata.wasm` | Location data: `locationCount`, `worldWidth`, `backgroundColor`, `environmentKind`, `deviceCount`, `buildingCount`. +| `ScreenGlitchFxCoprocessor.res` | `screenglitchfx.wasm` | Screen glitch FX: `glitchAdvance`, `glitchCycle`, `glitchRemainder`, `glitchFires`. +| `ScreensRunLoopLogicCoprocessor.res` | `screensrunlooplogic.wasm` | Screens run-loop logic: `dtSeconds`, `clampPlayerX`, `cameraTargetX`, layout helpers, grid/stack layout, SkillTree XP thresholds, `victoryScore`, `gradeOrdinal`. +| `UiRenderLogicCoprocessor.res` | `uirenderlogic.wasm` | UI render-logic: HUD HP/alert/inventory/minimap arithmetic, KernelMonitor bar colour/fill/redraw, PowerView battery/node grid, ParticleField alpha clamp/wrap, FontScale, RoundedBox anchors. +| `VerisimProvenRenderLogicCoprocessor.res` | `verisimprovenrenderlogic.wasm` | VeriSim/Proven render-logic: play-time decomposition (`playTimeTotalMinutes`, `playTimeHours`, `playTimeMinutes`, `playTimeHasHours`). String formatting of the caption stays host-side. +|=== + +C12 being NO_NEW_BRAINS does not add to the wasm surface — all twelve brains +were already live before C12 was triaged. + +== Screen and popup files — effect-gated analysis + +All 23 screen/popup implementation files are effect-gated PixiJS rendering +orchestrators. The common pattern: + +* *String-wall*: every screen calls `GameI18n.t(key)` for all user-visible + text; popup titles, button labels, and descriptions are all string-valued. + String template literals (`\`${label}: ON\``) appear in toggle buttons. + Display-name functions (`hairStyleName`, `rankName`, `categoryName`) produce + strings from enum dispatch. String concatenation for loadout slot captions. + These cannot cross the wasm boundary while the string backend is absent. + +* *PixiJS effect-wall*: all screens construct `Container`/`Sprite`/`Graphics`/ + `Text` objects, set x/y positions, call `Motion.animate` for tweening, + fire `Navigation.showScreen`/`presentPopup`/`dismissPopup`, read mutable + DOM state. + +* *Mutable ref state*: scroll position, navigation guards (`navigating := true`), + animation handles, screen dimensions — all `ref` values that cannot be + isolated into a pure-integer kernel. + +* *Special case — `TitleScreen.res`*: the title screen IS a consumer of the + already-live `titlescreen.wasm` (loaded via `AffineTEA.load`) and + `router.wasm` (loaded via `AffineTEARouter.load`). The `applyView` step + reads `AffineTEA.getSelected(tea) : int` and drives a PixiJS alpha pulse; + navigation decisions come FROM the Wasm state. The integer brain is the + already-live wasm; TitleScreen.res is the sense layer wiring Wasm output + to PixiJS. No new brain required. + +* *Special case — `SkillTreeScreen.res`*: `rankColor` returns static hex + constants (`0x888888` through `0xff4488`) with no algorithmic content — + these are display palette constants, not a separable integer kernel. SkillTree + XP threshold arithmetic is already extracted to `screensrunlooplogic.wasm`. + The string-returning `rankName`/`categoryName` functions and PixiJS rendering + make the file string-wall + effect-gated throughout. + +* *Location screen delegates*: eleven location screens (`AtlasScreen`, + `BackboneScreen`, `BusinessISPScreen`, `CityScreen`, `DevHubScreen`, + `FieldScreen`, `LabScreen`, `NexusScreen`, `RegionalISPScreen`, + `RuralISPScreen` via `LocationBase`) follow a single structural pattern: + call `LocationBase.makeLocationScreen(locationData, ~onExit)`. + The `locationData` record carries string fields (`id`, `name`, `description`, + `environment`) and float/int layout values (`worldWidth`, `backgroundColor`, + `devicePositions`). The integer content (`worldWidth`, `backgroundColor`, + device counts) is already handled by `locationdata.wasm` (C12 bridge 8). + The `LocationBase` delegate calls `WorldBuilder.buildLocationScreen` which + constructs the full PixiJS platformer scene — effect-gated throughout. + +== PixiJS binding files — SENSES by definition + +The four binding files (`Motion.res`, `Pixi.res`, `PixiSound.res`, +`PixiUI.res`) are pure `external` FFI declaration files. They ARE the +host/wasm boundary: every function in these files is a host-side JavaScript +API. Extracting them to wasm would mean running PixiJS inside wasm, which +is not the architecture. + +[cols="1,4",options="header"] +|=== +| File | Classification reason +| `Motion.res` | `@module("motion") external animate: ...`. The `motion` animation library is a host-side DOM API. +| `Pixi.res` | `@new @module("pixi.js") external make: ...`. All PixiJS types (`Container`, `Sprite`, `Graphics`, `Text`, `Texture`, `window`, `document`) are host-side DOM/WebGL APIs. +| `PixiSound.res` | `@module("@pixi/sound") external ...`. Web Audio API wrapper — host-side. +| `PixiUI.res` | `@new @module("@pixi/ui") external ...`. PixiJS UI widgets — host-side. +|=== + +== Impact on Phase Ω + +With C12 as NO_NEW_BRAINS, the Phase Ω cluster sequence (C11 → C9 → C10 → C12) +is complete. All twelve wasm brains referenced by C12 bridges were already live +before C12 was triaged. The screen, popup, and binding modules remain host-side +ReScript throughout Phase Ω. + +The remaining migration walls: + +* *String wall*: 71 files across the corpus require `String.length`, indexing, + `startsWith`/`endsWith`/`slice`, `fromCharCode`, or string ops not yet in + the AffineScript codegen backend. Unblocked by Phase F string backend. +* *Effect wall*: 111 files use module-level `ref()`, `Console.log`, + `module-level Dict.make()`, `Date.now`, or `Math.random`. Unblocked by + Phase F+ effect codegen. + +Phase Ω cluster migration is complete. Phase F (string backend) and Phase F+ +(effect codegen) are the next compiler-side milestones. diff --git a/proposals/idaptik/migrated/EVIDENCE-C9.adoc b/proposals/idaptik/migrated/EVIDENCE-C9.adoc new file mode 100644 index 0000000..7421fd7 --- /dev/null +++ b/proposals/idaptik/migrated/EVIDENCE-C9.adoc @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell += Cluster C9 (game-loop/AffineTEA/VeriSim types) — migration evidence (2026-06-05) +:toc: macro + +[IMPORTANT] +==== +*Verdict: NO_NEW_BRAINS — bridges, HTTP clients, effect-gated orchestration, and +string-wall type definitions.* All 16 files are host-side senses. No new +AffineScript kernels are required because: + +1. The four coprocessor bridges (`AffineTEA.res`, `AffineTEARouter.res`, + `PixiCoprocessor.res`, `VmCoprocessor.res`) are pure `external` FFI + declarations wrapping already-compiled wasm brains: + `titlescreen.wasm` (TEA title-screen state machine), + `router.wasm` (affine navigation back-stack), + `pixi.wasm` (AffineScript PixiJS scene driver), and + `vmcoprocessor.wasm` (reversible VM — C2 migration). +2. Eight VeriSimDB/Burble HTTP clients (`SaveGame.res`, `VeriSimDrift.res`, + `VeriSimFederation.res`, `VeriSimHexad.res`, `VeriSimProvenance.res`, + `VeriSimSearch.res`, `VeriSimVcl.res`, `BurbleIntegration.res`) are async + `fetch`-based wrappers with JSON serialization throughout. Effect-gated. +3. `VeriSimTypes.res` defines enum variants (`modality`/`hexadStatus`/ + `driftLevel`) that appear integer-clean, but `modalityToString` / + `modalityFromString` use string literals and all enums are used exclusively + as fields in string-bearing record structures. No standalone integer + computation exists — the enums are type tags for HTTP serialization. + String-wall prevents extraction. +4. `VeriSimError.res` error variant `fromStatus(int) → t` dispatches on HTTP + status codes, but every constructor carries a `string` payload + (`BadRequest(string)`, `NotFound(string)`, etc.). The string payloads make + this string-wall gated. +5. `Main.res` is the app entry point: side-effect screen imports, multiplayer + init, Burble voice bridge startup. Effect-gated (Console.log, Promise, + %raw, DOM manipulation). +6. `GameLoop.res` is the per-frame orchestrator. Integer counters + (`commandsExecuted`, `devicesHacked`, `maxAlertReached`, etc.) exist but + are trivial increments interleaved with float physics (playerX/playerY, + dt accumulation), mutable JS guard/dog arrays, and AI state calls. No + standalone integer brain is separable from the effect-heavy orchestration. + +No 4-gate recipe is applied; no new wasm is produced. +==== + +toc::[] + +== File-by-file disposition + +[cols="3,1,4",options="header"] +|=== +| File | Disposition | Reason +| `src/Main.res` | SENSES (effect-gated) | App entry point: side-effect screen imports, multiplayer init, Burble voice startup, PixiJS engine creation. DOM manipulation, `Promise.catch` error display. No separable integer computation. +| `src/app/GameLoop.res` | SENSES (effect-gated) | Per-frame game loop coordinator. Integer counters (`commandsExecuted`, `devicesHacked`, `maxAlertReached`) are trivial increments interleaved with float physics (`playerX`/`playerY`/`dt`), mutable JS guard/dog arrays, and AI orchestration calls. No standalone integer brain separable from the effect-heavy orchestration. +| `src/app/burble/BurbleIntegration.res` | SENSES (effect-gated) | HTTP/WebSocket voice-server client: async `fetch`, URL string construction, connection state enum. No integer computation. +| `src/app/pixi/PixiCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `PixiCoprocessorBridge.js` → `pixi.wasm`. Pure `external` declarations. AffineScript PixiJS brain already live. +| `src/app/tea/AffineTEA.res` | SENSES (bridge) | ReScript FFI wrapping `AffineTEA.js` → `titlescreen.wasm`. Pure `external` declarations. TEA title-screen brain already live. +| `src/app/tea/AffineTEARouter.res` | SENSES (bridge) | ReScript FFI wrapping `AffineTEARouter.js` → `router.wasm`. Pure `external` declarations. Affine navigation router brain already live. +| `src/app/verisimdb/SaveGame.res` | SENSES (effect-gated) | VeriSimDB save/load: async HTTP, JSON serialization, `formatPlayTime` string formatting. Effect-gated throughout. +| `src/app/verisimdb/VeriSimDrift.res` | SENSES (HTTP client) | Async HTTP wrappers for drift score/status/normalize endpoints. No integer computation. +| `src/app/verisimdb/VeriSimError.res` | SENSES (string-wall gated) | Error variant `fromStatus(int) → t` dispatches HTTP codes, but every constructor carries a `string` payload. String-wall prevents integer brain extraction. +| `src/app/verisimdb/VeriSimFederation.res` | SENSES (HTTP client) | Async HTTP wrappers for federation peer/query endpoints. No integer computation. +| `src/app/verisimdb/VeriSimHexad.res` | SENSES (HTTP client) | Async HTTP wrappers for hexad CRUD + list. No integer computation. +| `src/app/verisimdb/VeriSimProvenance.res` | SENSES (HTTP client) | Async HTTP wrappers for provenance chain/event/verify. No integer computation. +| `src/app/verisimdb/VeriSimSearch.res` | SENSES (HTTP client) | Async HTTP wrappers for text/vector/spatial/nearest/related search. No integer computation. +| `src/app/verisimdb/VeriSimTypes.res` | SENSES (string-wall gated) | Type definitions: `modality` (8 enum), `hexadStatus` (4 enum), `driftLevel` (5 enum). Variants themselves have no string payloads, but `modalityToString`/`modalityFromString` use string literals and all enums appear exclusively as fields in string-bearing records. No standalone integer computation exists on these enums. String-wall gated. +| `src/app/verisimdb/VeriSimVcl.res` | SENSES (HTTP client) | Async HTTP wrappers for VCL execute/explain. No integer computation. +| `src/app/vm/VmCoprocessor.res` | SENSES (bridge) | ReScript FFI wrapping `VmCoprocessorBridge.js` → `vmcoprocessor.wasm`. Pure `external` declarations. VM brain migrated in C2. +|=== + +== Already-live wasm brains referenced by C9 bridges + +The four bridge files reference wasm kernels that are already compiled and +live in `public/assets/wasm/`: + +[cols="2,2,3",options="header"] +|=== +| Bridge file | Wasm target | Brain description +| `AffineTEA.res` | `titlescreen.wasm` | AffineScript TEA title-screen state machine (Msg dispatch, Selected tag, screen resize). Linear message consumption. +| `AffineTEARouter.res` | `router.wasm` | Affine navigation back-stack (push/pop/presentPopup/dismissPopup). Affine resource — stack consumed on pop. +| `PixiCoprocessor.res` | `pixi.wasm` | AffineScript PixiJS scene driver (registerContainer, scene, makeAndDestroy). Linear ownership path exercised via `makeAndDestroy`. +| `VmCoprocessor.res` | `vmcoprocessor.wasm` | Reversible VM co-processor (13 opcodes, step/run). Migrated in C2. +|=== + +C9 being NO_NEW_BRAINS does not add to the wasm surface — these four brains +were already live before C9 was triage. + +== GameLoop.res — no separable integer brain + +`GameLoop.res` contains the following integer-flavoured state: + +* Counters: `commandsExecuted`, `devicesHacked`, `covertLinksDiscovered`, + `maxAlertReached`, `distractionsCalled`, `assassinKillAttempts`, + `critSuccessCount`, `critFailureCount`. +* Running-max: `if currentAlertInt > state.maxAlertReached { state.maxAlertReached = currentAlertInt }`. + +Why these are NOT extractable as a wasm brain: + +1. `currentAlertInt` comes from `DetectionSystem.getAlertInt(state.detection)`, + which reads a mutable float `alertScore` field inside a JS object. The + integer is computed from a float inside a mutable structure — the senses and + the integer are interleaved at the source level. +2. Every counter increment happens inside `Array.forEach` loops over mutable + `GuardNPC.t` and `SecurityDog.t` records. Extracting a pure-integer + "counter kernel" would require the caller to pre-aggregate events from + those mutable arrays, defeating the purpose of a senses/brain split. +3. The counters are simple increments (`+= 1`) and a max comparison — they + have no algorithmic content worth isolating. Compare with `DualAlert` (C6): + that brain had escalation logic, cooldown-milli ticking, trigger-progress + computation, and overall-threat aggregation. GameLoop's counters are + trivial running tallies. + +Verdict: GameLoop integer state stays host-side. + +== VeriSimTypes.res — string-wall analysis for enum candidates + +Three enum variants in `VeriSimTypes.res` appear potentially extractable: + +[cols="1,1,4",options="header"] +|=== +| Variant | Constructors | Why NOT extracted +| `modality` | Graph/Vector/Tensor/Semantic/Document/Temporal/Provenance/Spatial (8) | `modalityToString`/`modalityFromString` use string literals. Used exclusively as fields in string-bearing records (`hexadInput.modalities`, `textSearchParams.modalities`). String-wall gated. +| `hexadStatus` | Active/Archived/Draft/Deleted (4) | Used only as `hexad.status` field alongside `hexad.id: string`, `hexad.createdAt: string`, etc. No computation — only serialized to/from JSON. String-wall gated. +| `driftLevel` | Stable/Low/Moderate/High/Critical (5) | Used only as `driftStatusReport.level` field. No comparison, escalation, or ordinal arithmetic in C9 code — the level is received from the HTTP server and displayed. No integer computation to extract. +|=== + +In contrast, `DualAlert.affine` (C6) had computation: `level_value` mapped +ordinals to display integers, `escalate_level` implemented the state machine, +`overall_threat` aggregated physical + cyber levels. The VeriSimTypes enums +have none of this — they are pure serialization tags with no logic in the +ReScript sources of C9. + +== Impact on remaining clusters + +C9 being NO_NEW_BRAINS does not affect the C10/C12 roadmap. The four wasm +brains referenced by C9 bridges are already live. The VeriSimDB HTTP client +modules remain host-side ReScript throughout Phase Ω. The completion-drive +directive proceeds to *C10*. diff --git a/proposals/idaptik/migration-map.json b/proposals/idaptik/migration-map.json index 007f5f7..1db5410 100644 --- a/proposals/idaptik/migration-map.json +++ b/proposals/idaptik/migration-map.json @@ -315,7 +315,15 @@ "id": "C9", "name": "Game loop, AffineTEA, and VeriSim types", "description": "Top-level game loop, AffineTEA architecture, VeriSim integration types, and the entry point.", - "status": "TODO", + "status": "DONE", + "done": { + "date": "2026-06-05", + "phase": "G", + "verdict": "NO_NEW_BRAINS — bridges, HTTP clients, effect-gated orchestration, string-wall type definitions", + "note": "All 16 files are host-side senses. 4 bridge files wrap already-live wasm brains: AffineTEA.res → titlescreen.wasm (TEA title-screen machine), AffineTEARouter.res → router.wasm (affine nav back-stack), PixiCoprocessor.res → pixi.wasm (AS PixiJS scene driver), VmCoprocessor.res → vmcoprocessor.wasm (C2). 8 VeriSimDB/Burble HTTP clients are async fetch wrappers (effect-gated). VeriSimTypes enums (modality/hexadStatus/driftLevel) have no standalone integer computation — used only as serialization tags in string-bearing records; modalityToString uses string literals (string-wall). VeriSimError constructors all carry string payloads (string-wall). Main.res/GameLoop.res are effect-gated orchestration; GameLoop integer counters are trivial increments interleaved with float physics and mutable JS objects.", + "no_4gate": "nothing new to compile/parity/prove; four wasm brains already live (titlescreen, router, pixi, vmcoprocessor); remaining code is senses or string-wall gated", + "evidence": "proposals/idaptik/migrated/EVIDENCE-C9.adoc" + }, "files": [ "src/Main.res", "src/app/GameLoop.res", @@ -340,7 +348,15 @@ "id": "C10", "name": "Utils, tools, companions, narrative, and proven modules", "description": "Game utilities, tools coprocessors, Moletaire companion, mission briefing, and formal-proof integration.", - "status": "TODO", + "status": "DONE", + "done": { + "date": "2026-06-05", + "phase": "G", + "verdict": "NO_NEW_BRAINS — coprocessor bridges, float-wall physics, string-wall i18n/persistence/errors, effect-gated accessibility/settings orchestration", + "note": "All 21 files are host-side senses. 9 bridge files wrap already-live wasm brains (moletairecoprocessors, moletairehunger, missionbriefing, safefloat, passwordcracker, portscanner, colorpalette, fontscale, gamei18n). Float-wall: MoletaireHunger (f64 gravity/hunger physics; integer enum functions already in moletairehunger.wasm), SafeAngle (Math.atan2/cos/sin/mod_float — all f64). String-wall: ProvenError (all constructors carry string fields), MoletairePersistence (equipmentToString/FromString), Locales (5-language translation dicts, ~650 string pairs), PolyglotI18n (String.replaceAll, async fetch locale JSON). Effect-gated: Announcer (AccessibilitySettings + DomA11y), DomA11y (%raw DOM), ColorPalette (AccessibilitySettings mutable refs; color tables already in colorpalette.wasm), KeyboardNav (%raw window events), PanicHandler (Console.error), UserSettings (Storage/Audio/DesktopIntegration; stance enum string-serialized).", + "no_4gate": "nothing new to compile/parity/prove; nine wasm brains already live; remaining code is float-wall, string-wall, or effect-gated senses", + "evidence": "proposals/idaptik/migrated/EVIDENCE-C10.adoc" + }, "files": [ "src/app/companions/MoletaireCoprocessorsCoprocessor.res", "src/app/companions/MoletaireHunger.res", @@ -370,7 +386,15 @@ "id": "C11", "name": "UI and accessibility coprocessors", "description": "UI widget logic, font scaling, i18n, keyboard navigation, popup logic \u2014 pure integer presentation state.", - "status": "TODO", + "status": "DONE", + "done": { + "date": "2026-06-05", + "phase": "G", + "verdict": "NO_NEW_BRAINS \u2014 wiring/senses cluster", + "note": "All 10 files are host-side senses. DualAlert integer brain already extracted in C6 (DualAlert.affine, 613/613 parity, LOSSLESS boundary proof). DualAlertCoprocessor.res = bridge to C6 wasm. ForceLayoutCoprocessor.res = bridge to float-only physics wasm (euclidean/repulsion/attraction/velocity/alpha kernel; float-wall: deferred host-side). Button/HubButton/NavButton/Label/VolumeSlider/InventoryUI/HardwareWiring = pure PixiJS/UI senses with zero integer computation.", + "no_4gate": "nothing new to compile/parity/prove; DualAlert integer core C6-verified (613/613), float physics stays host-side", + "evidence": "proposals/idaptik/migrated/EVIDENCE-C11.adoc" + }, "files": [ "src/app/popups/ForceLayoutCoprocessor.res", "src/app/ui/Button.res", @@ -389,7 +413,15 @@ "id": "C12", "name": "Render-glue screens, UI, and bindings", "description": "Screen implementations, render-logic coprocessors, and PixiJS bindings. Migrated last \u2014 these call the AffineScript brain from the host JS/Pixi layer.", - "status": "TODO", + "status": "DONE", + "done": { + "date": "2026-06-05", + "phase": "G", + "verdict": "NO_NEW_BRAINS \u2014 render-logic coprocessor bridges, effect-gated screen implementations, PixiJS/Motion/Sound/UI binding declarations", + "note": "All 43 files are host-side senses. 12 bridge files wrap already-live wasm brains (companionsrenderlogic, devicesrenderlogic, enemiesrenderlogic, toolspickupsrenderlogic, playerrenderlogic, narrativepopupsrenderlogic, balanceanalyser, locationdata, screenglitchfx, screensrunlooplogic, uirenderlogic, verisimprovenrenderlogic). 23 screen/popup files are effect-gated PixiJS rendering orchestrators (GameI18n string calls, Motion.animate, Navigation APIs, mutable refs, %raw DOM). 4 binding files (Motion, Pixi, PixiSound, PixiUI) are pure external FFI declarations wrapping host-side JS libraries.", + "no_4gate": "nothing new to compile/parity/prove; twelve wasm brains already live; remaining code is effect-gated screen/popup/binding senses", + "evidence": "proposals/idaptik/migrated/EVIDENCE-C12.adoc" + }, "files": [ "src/app/companions/CompanionsRenderLogicCoprocessor.res", "src/app/devices/DevicesRenderLogicCoprocessor.res",