Skip to content
Merged
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
45 changes: 45 additions & 0 deletions affinescript-dom/src/dom.affine
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,45 @@ pub fn text(content: String) -> VNode = VText(content);
pub fn h(tag: String, attrs: [(String, String)], children: [VNode]) -> VNode =
VElem(tag, attrs, children);

/// Convenience constructors (#56-A). Thin wrappers over `h` so idaptik's
/// pilot `Dom.div` / `Dom.h1` / `Dom.p` surface compiles without going
/// through the low-level tag string.
pub fn div(attrs: [(String, String)], children: [VNode]) -> VNode =
h("div", attrs, children);

pub fn h1(attrs: [(String, String)], content: String) -> VNode =
h("h1", attrs, [text(content)]);

pub fn h2(attrs: [(String, String)], content: String) -> VNode =
h("h2", attrs, [text(content)]);

pub fn h3(attrs: [(String, String)], content: String) -> VNode =
h("h3", attrs, [text(content)]);

pub fn p(attrs: [(String, String)], content: String) -> VNode =
h("p", attrs, [text(content)]);

pub fn span(attrs: [(String, String)], content: String) -> VNode =
h("span", attrs, [text(content)]);

pub fn button(attrs: [(String, String)], content: String) -> VNode =
h("button", attrs, [text(content)]);

pub fn input(attrs: [(String, String)]) -> VNode =
h("input", attrs, []);

pub fn a(attrs: [(String, String)], content: String) -> VNode =
h("a", attrs, [text(content)]);

pub fn img(attrs: [(String, String)]) -> VNode =
h("img", attrs, []);

pub fn ul(attrs: [(String, String)], children: [VNode]) -> VNode =
h("ul", attrs, children);

pub fn li(attrs: [(String, String)], children: [VNode]) -> VNode =
h("li", attrs, children);

// `len` is not available in the standalone wasm-AOT subset; a `for`-count
// helper is (proven: tests/codegen/test_for_loop). Monomorphic, not
// generic, since wasm codegen generic support is not relied on here.
Expand Down Expand Up @@ -103,6 +142,12 @@ pub fn mount(selector: String, vnode: VNode) -> Bool {
}
}

/// Replace `document.body` contents with `vnode` (#56-A). Equivalent
/// to `mount("body", vnode)`. Declared below `mount` because codegen
/// is single-pass in source order.
pub fn replaceBody(vnode: VNode) -> Bool =
mount("body", vnode);

// ── Reconciler: minimal mutation between two VNode trees ─────────────────────

fn attr_has(attrs: [(String, String)], key: String) -> Bool {
Expand Down
17 changes: 13 additions & 4 deletions docs/CAPABILITY-MATRIX.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,11 @@ The compiler emits to many targets. *Maturity is not uniform; do not claim
|Target |Status |Notes

|WASM (core) |works |Cross-module imports + `extern fn` → WASM import
emission. The reference target.
emission. The reference target. WASI preview1 on-demand imports cover
clock/env/args/sockets/filesystem (`file_open`/`file_read`/
`file_fd_write`/`file_close`, `net_recv`/`net_send`/`net_accept`);
`tools/componentize.sh` defaults to the command adapter (#486). Native
preview2 import lowering is still the one-way door.

|WASM-GC |partial |Variant-with-args + same-arity `PatCon` destructuring;
silent-bad-codegen fallbacks eliminated. Mixed-arity matches need uniform
Expand Down Expand Up @@ -285,9 +289,14 @@ their own ledger (link:SOUNDNESS.adoc[SOUNDNESS.adoc]), gated by
* typed-wasm is *not* an AffineScript subsystem. It is a separate,
language-agnostic target with its own repo, proofs, and other producers
(ephapax). AffineScript is one producer among several.
* The satellite ecosystem is mostly scaffold/skeleton — see the satellite
registry in `ECOSYSTEM.adoc`. `affinescript-tea`/`-dom-loader`/`-cadre`
were imaginary until the #175 scaffolds; `-dom`/`-pixijs` are skeletons.
* The satellite ecosystem is mixed maturity — see the satellite registry
in `ECOSYSTEM.adoc`. `affinescript-tea` (INT-07/#182) and
`affinescript-dom` (INT-08/#183) shipped runtimes; `affinescript-cadre`
(INT-09) closed 2026-07-25. `affinescript-pixijs` in-tree bindings
(`stdlib/Pixi.affine` + UI/Sound) plus 56-A `stdlib/Dom.affine` /
`stdlib/Console.affine` landed 2026-09-21; full idaptik surface is
still open (#56). The `-dom-loader` satellite was folded into
`affine-js` (#489).

== Change control

Expand Down
48 changes: 29 additions & 19 deletions docs/ECOSYSTEM.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,20 @@ files; `test/test_stdlib_aot.ml`; issues #128/#135.
|*D* |Base-language soundness + ecosystem connective tissue: CORE-01 borrow
Phase-3 (#177); the INT-01..12 integration roadmap; the satellite registry
made real; #228/ADR-014 module-qualified paths (estate port unblocker). |
*ACTIVE — current frontier* |This stage. Substrate = INT-01..04 + CORE-01.
*CLOSED 2026-07-25* |Substrate (INT-01..04 + CORE-01) closed 2026-05-31;
remaining original #175 children INT-07 (#182) and INT-08 (#183) closed
2026-07-25. Residual WASI follow-ups #485 (S5 filesystem) and #487
(socket recv/send/accept) have preview1 lowerings + gated smokes in
tree; #486 flipped the wrap default to `--command` without the one-way
compiler re-target. Satellite remainder is #56 (56-A Dom/Console + Pixi
gap-fill landed). Stage E is now the frontier.

|*E* |typed-wasm convergence hardening (the transition runway): the
AffineScript↔typed-wasm contract widened from L7+L10 toward full
L1–6/L13–16 emitted-wasm enforcement; estate-wide re-validation (#235);
effect-threaded async-boundary recogniser (#234); the #225/#160 convergence
ABI matured to "shared with Ephapax". |*planned* |Begins when D's substrate
(INT-01..04, CORE-01) is closed; ends at a stable, multi-producer
ABI matured to "shared with Ephapax". |*ACTIVE — current frontier* |D's
substrate (INT-01..04, CORE-01) is closed. Ends at a stable, multi-producer
typed-wasm convergence ABI.
|===

Expand Down Expand Up @@ -154,7 +160,10 @@ runtime is *VERIFIED end-to-end* (2026-07-07): `affinescript-dom/e2e/run.sh`
drives mount + in-place attr patch + text update + surplus-child removal
against a real Int-handle host DOM, mutation log asserted.

|`affinescript-pixijs` |skeleton |Migration-prerequisite scaffold (#56).
|`affinescript-pixijs` |in-tree bindings |#56 partial: `stdlib/Pixi.affine`
(+ UI/Sound) is the operational binding; 56-A also landed
`stdlib/Dom.affine` + `stdlib/Console.affine`. Remaining: full idaptik
surface.

|`affinescript-vite` |scaffold |Build-tool integration shell.

Expand Down Expand Up @@ -188,8 +197,10 @@ object, `typedwasm.ownership` accessor.
== Integration roadmap — INT-01..12

Critical path: *INT-01..INT-04 are substrate and block most satellites.*
Filed sub-issues are children of #175; severity/blocked-by also in
link:TECH-DEBT.adoc[TECH-DEBT.adoc].
GitHub index #175: 8/8 original sub-issues complete; severity/blocked-by
also in link:TECH-DEBT.adoc[TECH-DEBT.adoc]. Residual WASI work is
#485/#486/#487 (preview1 FS + sockio + wrap-default in tree; native
preview2 re-target still the one-way door); satellite remainder is #56.

[cols="1,3,1,2"]
|===
Expand Down Expand Up @@ -239,16 +250,15 @@ dropped; 81d9fc7). Test gates:
`tests/componentize/{smoke,command_smoke,sockets_smoke}.sh` cover
the contract end-to-end; ownership section byte-survives every
wrap; combo-import canonical-order regression. Residual (filed
2026-05-31): #485 — S5 native `wasi:filesystem`
(open/read/write/close), unblocks INT-06 (server-side runtime
profile); #486 — S6c flip default wasm target preview1 →
component, bundled with native-preview2 cleanup replacing the
on-demand preview1 shims emitted by S4a/S4b/S6b with direct
`wasi:clocks` / `wasi:cli` / `wasi:sockets` calls (only pays off
once preview2 is the default — the one-way door that ends
reversibility); #487 — socket recv/send/accept primitives
(byte-level buffer / network-address marshalling, helper mirrors
`gen_str_at_via_get`). WIT world of record: `wit/affinescript.wit`
2026-05-31, status 2026-09-21): #485 — S5 native `wasi:filesystem`
preview1 lowering LANDED (`file_open`/`file_read`/`file_fd_write`/
`file_close`, Effect `File`, gated
`tests/componentize/filesystem_smoke.sh`); #486 — S6c wrap default
flipped to `--command` (S3 reactor smoke now passes `--reactor`);
compiler still emits preview1 core; native-preview2 cleanup remains
the one-way door; #487 — `net_recv`/`net_send`/`net_accept` preview1
lowering LANDED (gated `tests/componentize/sockio_smoke.sh`). WIT
world of record: `wit/affinescript.wit`
|INT-04 |Publish compiler + runtime to JSR (then npm) |#181 |runtime
packaging READY (affine-js + affinescript-tea JSR dry-run green;
manual-only `publish-jsr.yml`; docs/PACKAGING.adoc). INT-01 dep
Expand All @@ -258,11 +268,11 @@ auth). Compiler-binary distribution = design fork #260
(INT-02 dep cleared 2026-05-31 via #179; no other blockers — next-up
candidate for an issue-spinout)
|INT-06 |Server-side runtime profile (on INT-03 WASI p2) |ledger-only
|planned (blocked by #485)
|INT-07 |`affinescript-tea` runtime satellite |#182 |runtime + run loop
|planned (#485 FS slice unblocked 2026-09-21)
|INT-07 |`affinescript-tea` runtime satellite |#182 **CLOSED 2026-07-25** |runtime + run loop
shipped (`TeaApp`/`parseTeaLayout`, Linear-msg enforced); INT-01 dep
cleared (#253). Router/nav = separate INT-09
|INT-08 |DOM reconciler in `affinescript-dom` |#183 |reconciler
|INT-08 |DOM reconciler in `affinescript-dom` |#183 **CLOSED 2026-07-25** |reconciler
implemented + compiles (resolve→typecheck→codegen→wasm); `.as`→`.affine`
corrected. INT-02 dep cleared. #255 (wasm loop-codegen defect) that gated
the runtime is FIXED (PR #257, closed 2026-05-19); runtime VERIFIED
Expand Down
75 changes: 57 additions & 18 deletions docs/TECH-DEBT.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ Reconstructed and made authoritative 2026-05-19 (it was cited by #175–183 /
#228 / #177 but never committed). For the spine + contract see
link:ECOSYSTEM.adoc[ECOSYSTEM.adoc]; for readiness see
link:CAPABILITY-MATRIX.adoc[CAPABILITY-MATRIX.adoc].

GitHub index #175: **8/8 original sub-issues CLOSED** as of 2026-07-25
(#176 DOC, #177 CORE-01, #178 INT-01, #179 INT-02, #180 INT-03,
#181 INT-04-JSR, #182 INT-07, #183 INT-08). The GitHub ticket is a pure
index whose children are complete; this ledger remains the durable
index. Residual WASI follow-ups #485 (S5 filesystem) and #487
(socket recv/send/accept) have preview1 lowerings + gated smokes in
tree; #486 flipped the *wrap* default to `--command` without the
one-way compiler re-target (native-preview2 cleanup still open).
Satellite remainder is #56 (56-A Dom/Console + Pixi gap-fill landed;
full idaptik surface still open).
====

toc::[]
Expand All @@ -36,8 +47,8 @@ Stage D CLOSED ── CORE-01 (#177) ─┐ (base l
└─ INT-04 (#181) ◄─┘ (◄ INT-01) │
ADR-014 (#228) ── estate port unblocker, S1 │
▼
Stage E planned ── typed-wasm convergence hardening
(#234, #235, #225/#160 line, INT-12) ──► stable multi-producer ABI
Stage E ACTIVE ── typed-wasm convergence hardening
(#234 closed, #235, #225/#160 line, INT-12) ──► stable multi-producer ABI
----

The mandate's order of operations: *CORE-01 first* (the one named
Expand Down Expand Up @@ -401,11 +412,21 @@ export lifting via `_start : () -> ()` shim around parameter-less
`wasi_snapshot_preview1.sock_shutdown` (command adapter bridges to
`wasi:sockets/tcp`; 81d9fc7). Gates:
`tests/componentize/{smoke,command_smoke,sockets_smoke}.sh`.
Residual (filed 2026-05-31): #485 S5 native `wasi:filesystem`
(open/read/write/close — unblocks INT-06), #486 S6c flip default
wasm target preview1 → component + native-preview2 cleanup (one-way
door), #487 socket recv/send/accept primitives (byte-level buffer
/ network-address marshalling)
Residual (filed 2026-05-31, status 2026-09-21):
#485 S5 native `wasi:filesystem` — *preview1 lowering LANDED*
(`file_open`/`file_read`/`file_fd_write`/`file_close` →
`path_open`/`fd_read`/`fd_write`/`fd_close`, Effect `File`,
dirfd 3, combo canonical-order lock + gated
`tests/componentize/filesystem_smoke.sh` hermetic `--dir`
round-trip). Unblocks INT-06's FS slice.
#486 S6c — *wrap default LANDED* (`tools/componentize.sh` now
defaults to `--command`; S3 reactor smoke requests `--reactor`
explicitly). Compiler still emits preview1 core wasm;
native-preview2 import cleanup remains the one-way door.
#487 socket recv/send/accept — *preview1 lowering LANDED*
(`net_recv`/`net_send`/`net_accept` → `sock_recv`/`sock_send`/
`sock_accept`, Effect `Net`, gated
`tests/componentize/sockio_smoke.sh`)
|INT-04 |Publish to JSR/npm |S2 |#181 packaging READY (dry-run green,
manual workflow); compiler-binary distribution decided = **ADR-019**
(#260, Releases + thin Deno/JSR shim, staged S1..S4; per-file record
Expand All @@ -420,12 +441,15 @@ drift fixed via #297/#300 (single source of truth `lib/version.ml`
`0.1.1`). Repo-wide SPDX surface relicensed MPL-2.0 →
MPL-2.0 (#301/#304; PMPL preserved as the narrative-preferred
licence in root `LICENSE`).
|INT-07 |`affinescript-tea` runtime |S2 |#182 runtime + run loop shipped
(TeaApp/parseTeaLayout, Linear-msg enforced); INT-01 cleared (#253)
|INT-08 |DOM reconciler |S2 |#183 implemented + compiles; `.as`→`.affine`
fixed; #255 (wasm loop-codegen defect) that gated the runtime is fixed
(PR #257, closed 2026-05-19) — runtime VERIFIED end-to-end 2026-07-07 via
`affinescript-dom/e2e/run.sh`
|INT-07 |`affinescript-tea` runtime |S2 |**#182 CLOSED 2026-07-25** —
runtime + run loop shipped (TeaApp/parseTeaLayout, Linear-msg enforced);
INT-01 cleared (#253)
|INT-08 |DOM reconciler |S2 |**#183 CLOSED 2026-07-25** — implemented +
compiles; `.as`→`.affine` fixed; #255 (wasm loop-codegen defect) that
gated the runtime is fixed (PR #257, closed 2026-05-19) — runtime
VERIFIED end-to-end 2026-07-07 via `affinescript-dom/e2e/run.sh`
|INT-09 |`affinescript-cadre` router/navigation runtime |S2 |**CLOSED
2026-07-25** (CadreRouter JS wrapper + E2E tests)
|INT-10 |`affinescript-lsp` distribution |S2 |**DONE end-to-end live**
(#282, ADR-019 S4): LSP resolves the compiler via `AFFINESCRIPT_COMPILER`
→ `affinescript` on `PATH` → the `@hyperpolymath/affinescript` shim
Expand All @@ -434,22 +458,34 @@ unit tests green. As of 2026-05-20 the shim is published on JSR
(`@hyperpolymath/affinescript@0.1.2`) and pins all three v0.1.1
binaries by SHA256, so the shim resolution path actually executes
end-to-end now (was code-correct but inert before today's publish).
|INT-05/06/09/11/12 |ledger-only; filed when blocker closes |— |planned
|INT-05 |Loader-driven multi-module app bundling |S2 |planned (INT-02 dep
cleared 2026-05-31 via #179; candidate for an issue-spinout)
|INT-06 |Server-side runtime profile (on INT-03 WASI p2) |S2 |planned
(#485 FS slice unblocked 2026-09-21; remaining: native-preview2
clocks/cli/sockets + a real server profile)
|INT-11 |Browser host parity (`affine-js` loader + `affinescript-dom`
reconciler end-to-end) |S2 |**CLOSED 2026-07-25**
|INT-12 |typed-wasm convergence fixtures |S1 |planned (Stage E;
coordinates with `hyperpolymath/typed-wasm` C5.1)
|===

== Section E — SAT (satellite repos)

[cols="1,3,1,2"]
|===
|ID |Item |Sev |Status
|SAT-01 |`affinescript-dom` reconciler (skeleton → real) |S2 |open (=INT-08)
|SAT-01 |`affinescript-dom` reconciler (skeleton → real) |S2 |**DONE**
(=INT-08, #183 CLOSED 2026-07-25) — runtime VERIFIED e2e 2026-07-07 via
`affinescript-dom/e2e/run.sh`
|SAT-02 |`packages/affine-js` hardcoded path / env-only imports |S1
|*FIXED* by INT-02 (Refs #179): `Deno.readFile(url.pathname)` replaced by
host-agnostic `readBytes`; `env`-only import shape replaced by
multi-namespace `buildImportObject`; `mod.js` consumes the loader.
Proven + locked (see INT-02)
|SAT-03 |`affinescript-tea` runtime build-out |S2 |open (=INT-07)
|SAT-04 |`affinescript-cadre` router runtime |S2 |planned (=INT-09)
|SAT-03 |`affinescript-tea` runtime build-out |S2 |**DONE** (=INT-07,
#182 CLOSED 2026-07-25)
|SAT-04 |`affinescript-cadre` router runtime |S2 |**DONE** (=INT-09,
CLOSED 2026-07-25)
|SAT-05 |`affinescript-pixijs` migration prerequisite |S3 |open #56
|===

Expand Down Expand Up @@ -490,7 +526,10 @@ link:specs/TYPED-WASM-ROADMAP.adoc[TYPED-WASM-ROADMAP] (Tranches B/C/D)
== Update protocol

* One PR per ledger unit; `Refs #N` (never `Closes` on the #175 epic — only
the owner closes it).
the owner closes it). Residual follow-ups after the original 8 children
are #485/#486/#487/#56 — do not re-open #175 to track them. Owner may
close #175 once those residuals are accepted; GitHub write is a PAT
capability, not a ledger action.
* Every capability-affecting PR updates `CAPABILITY-MATRIX.adoc` *and* this
ledger in the same change; `STATE.a2ml` mirrors after.
* `dune runtest --force` must stay green (baseline 260, 2026-05-19).
Expand Down
Loading
Loading