@@ -985,8 +985,22 @@ Staged plan (ledger INT-03; each row = one gated PR):
985985 post-codegen step wraps it with the standard preview1->preview2
986986 adapter into a component; ownership section survival asserted;
987987 wasmtime component-run smoke. Codegen unchanged ⇒ reversible.
988- - S4: native `wasi:clocks` + environment + argv via preview2 (wasmtime
989- host-testable), replacing the preview1 shims behind the component path.
988+ - S4 (DONE, slices S4a clock + S4b env_count/arg_count): on-demand
989+ preview1 imports (`wasi_snapshot_preview1.clock_time_get`,
990+ `environ_sizes_get`, `args_sizes_get`) via the Effect_sites
991+ pre-scan, canonical-order indexing through
992+ `ctx.wasi_func_indices`. NOTE divergence from this ADR's original
993+ S4 wording ("native `wasi:clocks` + environment + argv via
994+ preview2"): the slice shipped as preview1-shim, NOT native
995+ preview2 calls. This is sufficient under the component path
996+ because both adapters bridge the preview1 imports to
997+ `wasi:clocks` / `wasi:cli` internally; switching the codegen
998+ to emit `wasi:clocks/monotonic-clock@0.2.x.now()` directly only
999+ pays off once preview2 is the default (deferred into S6c). The
1000+ ADR is amended here to record what actually shipped rather than
1001+ pretending S4 fulfilled the original wording. String accessors
1002+ (env_at/arg_at) need byte-level wasm IR (I32Load8U/I32Store8) for
1003+ buffer marshalling and remain a tracked follow-up.
9901004- S5: `wasi:filesystem` (open/read/write/close) — unblocks INT-06.
9911005- S6a (WIT export lifting, DONE): codegen emits a `_start : () -> ()`
9921006 shim that calls `main` and drops its i32 result whenever a
@@ -999,13 +1013,33 @@ Staged plan (ledger INT-03; each row = one gated PR):
9991013 the component (exit 0), the ownership section survives, and the
10001014 lift is asserted. Purely additive: reactor consumers + game-loop
10011015 hooks are byte-unchanged.
1002- - S6b: `wasi:sockets`.
1016+ - S6b (sockets on-ramp, DONE): new `net_shutdown(fd, how) -> Int`
1017+ builtin (Effect `Net`, reserved) lowers to
1018+ `wasi_snapshot_preview1.sock_shutdown` via the same on-demand
1019+ canonical-order pattern as S4a/S4b (`optional_wasi` entry 4 in
1020+ `lib/codegen.ml`). The command adapter bridges to
1021+ `wasi:sockets/tcp` internally without surfacing a host-side
1022+ `wasi:sockets/*` requirement; `tests/componentize/sockets_smoke.sh`
1023+ gates the lift + a clock+env+sock combo for canonical-order
1024+ regression. Real-host invoke under wasmtime exits 0 on
1025+ `net_shutdown(stdin, RDWR)` (ENOTSOCK dropped). Larger socket
1026+ primitives (recv/send/accept) need byte-level wasm IR for buffer
1027+ / network-address marshalling and remain a tracked follow-up
1028+ alongside env_at/arg_at.
10031029- S6c: flip the default wasm target to component and demote the
1004- preview1 stdout path to a named legacy target.
1030+ preview1 stdout path to a named legacy target. Bundled cleanup:
1031+ replace the on-demand preview1 shims emitted by S4a/S4b/S6b
1032+ (clock_time_get, environ_sizes_get, args_sizes_get,
1033+ sock_shutdown) with native preview2 calls
1034+ (`wasi:clocks/monotonic-clock`, `wasi:cli/environment`,
1035+ `wasi:sockets/tcp`) — this only pays off once preview2 is the
1036+ default, hence its placement here rather than as a standalone
1037+ pre-flip slice.
10051038"""
10061039consequences = """
10071040- End-state is one-way (the component model becomes the canonical wasm
1008- target) but reversible-in-progress: preview1 retained through S5.
1041+ target) but reversible-in-progress: preview1 retained as the default
1042+ through S6b; the flip happens at S6c.
10091043- S3..S6 are HARD-GATED on S2 (toolchain). Tracked as a follow-up
10101044 issue; this is disclosed, not hidden.
10111045- typed-wasm ownership-section ABI is unchanged; the coordination
@@ -1070,34 +1104,23 @@ Thread per-call-site effect rows from typecheck to codegen via a
10701104 `let`-RHS call's effect row ⊇ `Async`” via a table lookup, replacing
10711105 `is_async_prim_call`/`async_primitives`.
10721106- *Fallback / safety.* If the table has no entry for a site (e.g. a
1073- pre-typecheck embedder path, or a synthesised node), or the consumer
1074- detects a producer/consumer count-mismatch, [Effect_sites.is_async_call]
1075- returns false ⇒ the CPS transform simply does not fire for that call.
1076- The pre-S4 plan retained the structural recogniser as the fallback
1077- *until the table path was proven*; S4 (PR #278) retired the
1078- hardcoded `async_primitives` set, so the steady-state fallback is
1079- "no transform" — over-conservative, always sound.
1107+ pre-typecheck embedder path, or a synthesised node), codegen falls
1108+ back to the structural recogniser. The hardcoded set is retired only
1109+ once the table path is proven (final slice); over-conservative
1110+ fallback is always sound (= today's behaviour).
10801111
10811112Staged (ledger #234; each a gated PR, full `dune test --force` +
10821113wasm e2e):
1083- - S1 (ADR-016 + plan, PR #270): DONE — no code change.
1084- - S2a (`lib/effect_sites.ml` shared numbering, PR #275): DONE — pure,
1085- gate-neutral.
1086- - S2b (typecheck builds & returns the side-table, PR #276): DONE — no
1087- codegen behaviour change yet.
1088- - S3 (pipeline threads the table; codegen boundary predicate switches
1089- to the table with structural fallback; new e2e
1090- `tests/codegen/effect_async_boundary.affine` proving a *user-defined*
1091- `Async` fn triggers the transform — PR #277): DONE. All existing
1092- http_cps_* / http_response_reader stay green.
1093- - S4 (retire the hardcoded `async_primitives` set; boundary is now
1094- exactly `Effect_sites.is_async_call`; fallback remains for table-
1095- empty / count-mismatch only — PR #278): DONE.
1096-
1097- *Delivery status:* CLOSED 2026-05-19 end-to-end. Issue #234 closed
1098- completed (`hyperpolymath/affinescript#234`). The structural name set
1099- no longer exists; the boundary is single-sourced from the typecheck
1100- effect side-table via the shared `Effect_sites` ordinal.
1114+ - S1 (this): ADR-016 + plan. No code change.
1115+ - S2: `lib/effect_sites.ml` shared numbering + typecheck builds &
1116+ returns the side-table. NO codegen behaviour change (table built,
1117+ unused) — pure, gate-neutral.
1118+ - S3: pipeline threads the table; codegen boundary predicate switches
1119+ to the table with structural fallback; new e2e proving a
1120+ *user-defined* `Async` fn triggers the transform (the payoff). All
1121+ existing http_cps_* / http_response_reader stay green.
1122+ - S4: retire the hardcoded `async_primitives` set (fallback remains
1123+ for table-miss only); doc truthing.
11011124"""
11021125consequences = """
11031126- Generalises to user-defined `Async` functions; new async primitives
@@ -1286,8 +1309,9 @@ references = [
12861309
12871310[[adr]]
12881311id = "ADR-020"
1289- status = "proposed "
1312+ status = "accepted "
12901313date = "2026-05-23"
1314+ ratified = "2026-05-24"
12911315title = "Ownership-section schema versioning (0xAF sentinel + u8 version)"
12921316context = """
12931317The `affinescript.ownership` Wasm custom section has been frozen at
@@ -1366,8 +1390,9 @@ references = [
13661390
13671391[[adr]]
13681392id = "ADR-021"
1369- status = "proposed "
1393+ status = "accepted "
13701394date = "2026-05-23"
1395+ ratified = "2026-05-24"
13711396title = "Multi-producer ABI coordination model for typed-wasm carriers"
13721397context = """
13731398typed-wasm is a separate, language-agnostic compilation target with
0 commit comments