diff --git a/migration/shared/PortNames.affine b/migration/shared/PortNames.affine index 1cb17d99..47c59b18 100644 --- a/migration/shared/PortNames.affine +++ b/migration/shared/PortNames.affine @@ -18,11 +18,18 @@ // 2. `let f = (a: T): U => body` → `fn f(a: T) -> U { body }`. // 3. The `coprocessorDomains` array is now typed `[String]` (vs the // untyped `array` of the source). Same runtime. -// 4. The `isCoprocessorPort` function is kept simple — uses Array.contains -// instead of split-then-Array.get-then-pattern-match, so the -// behaviour is "any port that *equals* a known domain". The full -// "domain:command" prefix split is left as a follow-up that needs -// String.split, which is in stdlib but uncertain shape. +// 4. `isCoprocessorPort` uses `collections::any` + `string::starts_with` +// for the any-prefix match. The original .res computed a `_prefix` +// via `String.split + Array.get` but never used it — the +// `Array.some(domain => startsWith(port, domain))` is the +// semantically-meaningful body. This port matches that semantics. +// +// Depends on: affinescript#505 — the two stdlib fns above (`any`, +// `starts_with`) only became `pub` in that PR; pre-505 this file +// fails to resolve them. + +use collections::{any}; +use string::{starts_with}; // ─── System ports (always available) ──────────────────────────────────── const console: String = "console"; // Terminal text output @@ -84,3 +91,8 @@ const cpGraphics: String = "graphics"; // Visual effects // not parseable in user source, even though the stdlib uses it // pervasively. const coprocessorDomains: [String] = [cpCrypto, cpVector, cpMaths, cpIO, cpNeural, cpQuantum, cpPhysics, cpAudio, cpTensor, cpGraphics]; + +// Test whether a port name targets a coprocessor domain. +fn isCoprocessorPort(port: String) -> Bool { + any(fn(domain) => starts_with(port, domain), coprocessorDomains) +} diff --git a/migration/shared/STATUS.md b/migration/shared/STATUS.md new file mode 100644 index 00000000..cbfcb658 --- /dev/null +++ b/migration/shared/STATUS.md @@ -0,0 +1,52 @@ + + + +# `migration/shared/` parity status + +Snapshot of the Wave 3 .affine pilot files vs. their `src/shared/*.res` originals. Updated 2026-05-31 alongside `PortNames.affine` completion (PR closing standards#279 STEP 8 — first compilable subsystem in this directory). + +## Parity table + +| File | `affinescript check` | Mode | Status | Notes | +|---|---|---|---|---| +| `PortNames.affine` | ✅ **passes** | A (compiles) | **Ready** | Full parity with `src/shared/PortNames.res` incl. `isCoprocessorPort`. Depends on affinescript#505 for `string::starts_with` + `collections::any` being `pub`. | +| `GameEvent.affine` | ✗ parse error | B (design demo) | Aspirational | Uses `import VMEvent;` syntax + qualified module paths (`VMEvent.t`) — neither parses in current AS. Rewrite needed when affinescript#228 lands or alternative imports surface. | +| `VMEvent.affine` | ✗ parse error | B | Aspirational | Uses `import Int;` syntax + `List<(String, Int)>` tuple types in generics — neither currently supported. | +| `DeviceEvent.affine` | ✗ | B | Aspirational | Same shape as VMEvent. | +| `NetworkEvent.affine` | ✗ | B | Aspirational | Same. | +| `PlayerEvent.affine` | ✗ | B | Aspirational | Same. | +| `WiringEvent.affine` | ✗ | B | Aspirational | Same. | +| `InventoryEvent.affine` | ✗ | B | Aspirational | Same. | +| `CoopEvent.affine` | ✗ | B | Aspirational | Same. | + +**Score (2026-05-31): 1 / 10 compiles.** + +## Why 1/10 + +The 9 GameEvent-domain files were written in early-Wave-3 "Mode B" — *design demos* that use spec features (`import X;`, inline record fields `A({field: T})`, qualified type paths like `VMEvent.t`) that were anticipated but never landed in v0.1.0. They serve as the *target shape* for the .affine port, not as compilable replacements. + +The single file that compiles (`PortNames.affine`) does so because it stays inside the actually-implemented language subset: bare constants, simple `fn` declarations, `[T]` arrays, and stdlib calls — no qualified paths, no `import X;`, no record-field destructuring. + +## What unblocks the rest + +| Blocker | AS issue | Effect when closed | +|---|---|---| +| `import X;` / `use X::{...}` for in-tree (non-stdlib) modules | affinescript#228 + affinescript#262 | All 9 Mode-B files can switch to `use vmevent::{T};` etc. | +| Qualified type paths `Pkg.T` in user code | affinescript#228 | Top-level `GameEvent.affine` can re-export domain sums. | +| Record-field destructuring in match arms (`A({field}) => ...`) | unverified — not yet a tracked issue | Every domain module's `toTag` body needs this. | +| Tuple types inside generic args (`List<(String, Int)>`) | unverified | `VMEvent.StateChanged` uses this shape. | + +Until those land, the 9 design demos stay as documentation of the intended port. The 1 compiling file (`PortNames.affine`) demonstrates that **incremental promotion of Mode-B files to Mode-A is possible** as AS features arrive. + +## Replacement of `src/shared/PortNames.res` + +`PortNames.affine` is the *target* — it does not yet replace `src/shared/PortNames.res` in the build. The .res still owns the runtime contract: it compiles to `src/shared/PortNames.res.mjs`, which `tests/unit/shared/PortNames_test.mjs` and consumer modules `import`. + +Replacement requires AS-to-ESM codegen for this module, plus migration of (or compatibility shim for) the existing consumer set: + +- `__tests__/PortNames_test.res` — 12 test cases against `PortNames.*` +- `src/app/multiplayer/VMMessageBus.res` — `// Port naming convention (from shared/PortNames.res):` (comment-only) +- `src/shared/Kernel_Quantum.res` — `// ... PortNames.alert ...` (comment-only) +- `tests/unit/shared/PortNames_test.mjs` — pre-compiled, also imports + +The .affine→.mjs codegen is part of AS's existing Deno-target work; the test port is a separate slice. Until both arrive, the .affine sits in `migration/shared/` as the verified-correct future shape.