Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions migration/shared/PortNames.affine
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>` 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
Expand Down Expand Up @@ -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)
}
52 changes: 52 additions & 0 deletions migration/shared/STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
<!-- Copyright (c) 2026 Joshua B. Jewell and Jonathan D.A. Jewell -->

# `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.
Loading