⏺ Tier 2 gate spike: PASSED — proven on workerd (real Workers #26

Merged
navicore merged 2 commits from wasm-t2 into main 2026-06-20 19:22:36 +00:00
Owner

runtime, V8 isolate, HTTP)

All four unknowns retired:

  1. Runtime functions on wasm32-unknown-unknown (no WASI) —
    compiles and runs; the I/O-free run_query path never
    touches the stdout/argv/exit stubs.
  2. musttail → return_call lowers and runs on V8 at depth —
    count(1000000) (a 1,000,000-deep call/1 recursion) returns
    {"count":1} inside a V8 isolate. return_call confirmed
    present (43) in the unknown-unknown object; V8 honors it →
    constant stack. This is Checkpoint 0 on the actual Workers
    engine, not just wasmtime.
  3. Buffer ABI round-trips — plg_init (hands over the
    Machine) + plg_rt_alloc/plg_rt_run_query/plg_rt_free over
    linear memory, driven by a ~30-line JS Worker. Query in →
    JSON out, byte-identical to native (dep(app, X) and the
    existence_error path both match).
  4. Module size: 1.65 MB raw — comfortably within the
    Workers budget (compresses well under the limit).

Two findings worth recording

  • The hard part was a Vec::with_capacity over-allocation
    bug in my buffer ABI, not anything architectural — freeing
    not a real gap.
  • WASI-vs-isolate is a real shape difference, as scoped:
    the same program runs unchanged on Tier 1 (wasi/wasmtime)
    and Tier 2 (unknown-unknown/V8) — only the entry (no main,
    an exported plg_init + buffer ABI) and the link (--no-entry
    --export, no libc) differ.

State / honest notes

  • One in-tree artifact: crates/runtime/src/reactor.rs (+
    pub mod reactor;). It's the throwaway buffer-ABI; it builds
    clean, is dead-stripped from native binaries, and the full
    runtime suite (123 tests) + clippy are green with it
    present. It's the natural seed for productization (the real
    I/O-free-core extraction would replace its duplicated JSON
    formatting) — or trivially revertable if you'd rather
    start clean. Everything else (the IR awk transform, the
    workerd config/worker.js) is throwaway in /tmp/t2.
  • Not productized, as agreed: no codegen target arm, no
    feature-gated reactor archive, no Worker template/wrangler,
    no docs, no write/1 capture. Those wait until you say
    continue.

Productization, when you want it, is now "engineering, no
existential unknowns": a Target::WasmReactor codegen arm
emitting plg_init instead of main, a second feature-gated
archive, the reactor link path in link.rs, the
I/O-free-core extraction (shared with INVOCATION's resident
mode), a Worker template, and docs.

runtime, V8 isolate, HTTP) All four unknowns retired: 1. Runtime functions on wasm32-unknown-unknown (no WASI) — compiles and runs; the I/O-free run_query path never touches the stdout/argv/exit stubs. 2. musttail → return_call lowers and runs on V8 at depth — count(1000000) (a 1,000,000-deep call/1 recursion) returns {"count":1} inside a V8 isolate. return_call confirmed present (43) in the unknown-unknown object; V8 honors it → constant stack. This is Checkpoint 0 on the actual Workers engine, not just wasmtime. 3. Buffer ABI round-trips — plg_init (hands over the Machine) + plg_rt_alloc/plg_rt_run_query/plg_rt_free over linear memory, driven by a ~30-line JS Worker. Query in → JSON out, byte-identical to native (dep(app, X) and the existence_error path both match). 4. Module size: 1.65 MB raw — comfortably within the Workers budget (compresses well under the limit). Two findings worth recording - The hard part was a Vec::with_capacity over-allocation bug in my buffer ABI, not anything architectural — freeing not a real gap. - WASI-vs-isolate is a real shape difference, as scoped: the same program runs unchanged on Tier 1 (wasi/wasmtime) and Tier 2 (unknown-unknown/V8) — only the entry (no main, an exported plg_init + buffer ABI) and the link (--no-entry --export, no libc) differ. State / honest notes - One in-tree artifact: crates/runtime/src/reactor.rs (+ pub mod reactor;). It's the throwaway buffer-ABI; it builds clean, is dead-stripped from native binaries, and the full runtime suite (123 tests) + clippy are green with it present. It's the natural seed for productization (the real I/O-free-core extraction would replace its duplicated JSON formatting) — or trivially revertable if you'd rather start clean. Everything else (the IR awk transform, the workerd config/worker.js) is throwaway in /tmp/t2. - Not productized, as agreed: no codegen target arm, no feature-gated reactor archive, no Worker template/wrangler, no docs, no write/1 capture. Those wait until you say continue. Productization, when you want it, is now "engineering, no existential unknowns": a Target::WasmReactor codegen arm emitting plg_init instead of main, a second feature-gated archive, the reactor link path in link.rs, the I/O-free-core extraction (shared with INVOCATION's resident mode), a Worker template, and docs.
⏺ Tier 2 gate spike: PASSED — proven on workerd (real Workers
All checks were successful
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 1m34s
af178572c1
runtime, V8 isolate, HTTP)

  All four unknowns retired:

  1. Runtime functions on wasm32-unknown-unknown (no WASI) —
  compiles and runs; the I/O-free run_query path never
  touches the stdout/argv/exit stubs.
  2. musttail → return_call lowers and runs on V8 at depth —
  count(1000000) (a 1,000,000-deep call/1 recursion) returns
  {"count":1} inside a V8 isolate. return_call confirmed
  present (43) in the unknown-unknown object; V8 honors it →
  constant stack. This is Checkpoint 0 on the actual Workers
  engine, not just wasmtime.
  3. Buffer ABI round-trips — plg_init (hands over the
  Machine) + plg_rt_alloc/plg_rt_run_query/plg_rt_free over
  linear memory, driven by a ~30-line JS Worker. Query in →
  JSON out, byte-identical to native (dep(app, X) and the
  existence_error path both match).
  4. Module size: 1.65 MB raw — comfortably within the
  Workers budget (compresses well under the limit).

  Two findings worth recording

  - The hard part was a Vec::with_capacity over-allocation
  bug in my buffer ABI, not anything architectural — freeing
  not a real gap.
  - WASI-vs-isolate is a real shape difference, as scoped:
  the same program runs unchanged on Tier 1 (wasi/wasmtime)
  and Tier 2 (unknown-unknown/V8) — only the entry (no main,
  an exported plg_init + buffer ABI) and the link (--no-entry
  --export, no libc) differ.

  State / honest notes

  - One in-tree artifact: crates/runtime/src/reactor.rs (+
  pub mod reactor;). It's the throwaway buffer-ABI; it builds
  clean, is dead-stripped from native binaries, and the full
  runtime suite (123 tests) + clippy are green with it
  present. It's the natural seed for productization (the real
  I/O-free-core extraction would replace its duplicated JSON
  formatting) — or trivially revertable if you'd rather
  start clean. Everything else (the IR awk transform, the
  workerd config/worker.js) is throwaway in /tmp/t2.
  - Not productized, as agreed: no codegen target arm, no
  feature-gated reactor archive, no Worker template/wrangler,
  no docs, no write/1 capture. Those wait until you say
  continue.

  Productization, when you want it, is now "engineering, no
  existential unknowns": a Target::WasmReactor codegen arm
  emitting plg_init instead of main, a second feature-gated
  archive, the reactor link path in link.rs, the
  I/O-free-core extraction (shared with INVOCATION's resident
  mode), a Worker template, and docs.
Author
Owner

Review — Tier 2 gate spike (workerd / V8 / wasm32-unknown-unknown)

Right scope, right discipline. The gate is "does this approach actually work end-to-end on the real target?" and the answer (constant-stack to a million-deep on V8, buffer ABI round-trips byte-identical to native, module fits the budget) is the only answer that lets productization proceed without speculative architecture work. The PR description's "engineering, no existential unknowns" line is the correct framing — that's what a spike is for.

What the gate actually proved, restated for the reader who's looking at the disposition later:

  1. No-WASI is viable. The I/O-free run_query path never touches stdout/argv/exit stubs, so the no-stdlib environment isn't a blocker. The static MACHINE slot + reactor entry points show how: hand the host an ABI it can drive over linear memory and never touch libc.
  2. musttail lowers to return_call on V8 at depth. This is the load-bearing finding for the whole Tier 2 plan. wasmtime (Tier 1) honoring tail-calls didn't guarantee V8 would; you'd have had to retreat to CPS-style trampolining in the runtime if it hadn't. It does, so the IR investment from PRs #20/#24 carries forward to Workers without rework.
  3. Buffer ABI shape is workable. alloc / run_query / free over a packed (len << 32) | ptr return is a known-good pattern; that it round-trips byte-identical to native including the existence_error path is the gate's strongest evidence — it means the JSON format and the error rendering path don't need parallel implementations.
  4. Size fits. 1.65 MB raw is the headline; compresses well, which is what Workers actually rate-limits on.

The spike itself is small, contained, and the engineering inside it is honest. Below: things worth flagging as gate findings (for the productization disposition to react to) and one footgun to record before the file gets repurposed.

Findings worth recording for productization

1. The Vec::with_capacityLayout-exact allocator switch is a real ABI rule. Documented in raw_alloc's comment. Worth lifting that observation to the productization plan: any host-visible buffer that the host frees by (ptr, len) must be alloc::alloc(Layout::from_size_align(len, 1)), not Vec::with_capacity(len). The next contributor implementing the productized reactor will reach for Vec reflexively. The current comment ("Vec::with_capacity may over-allocate") tells what but not why — adding "the host frees by requested-length, so actual-capacity > requested-length corrupts the allocator" would prevent the recurrence.

2. MACHINE as a static AtomicPtr is single-tenant, single-in-flight per isolate. V8 isolates aren't multithreaded, but a Worker can have multiple in-flight async tasks sharing one isolate. For the gate (one query, JS awaits the result) this is fine. For productization either (a) document "one in-flight query per isolate" as the contract, or (b) take per-request state out of MACHINE and pass it through the buffer ABI. The contract route is much simpler and matches how most Workers use isolates; worth recording the choice now while the rationale is fresh.

3. reset() is the inverse of Machine::new. It clears each per-query field by name. This works for the spike but it knows the Machine's field set — a future field added without reset coverage gives the next query state-leak symptoms that won't show up in single-query smoke tests. A Machine::reset_per_query() method living next to the field declarations would make "did I forget to clear this?" a local question. Worth noting now because productization will reuse this exact reset, and the leak class is the kind of bug that only surfaces under sustained traffic.

4. The hard-coded "exhausted":true is correct for the spike (no --limit, so solve always runs to completion). Productization needs to take limit from the request and compute exhausted exactly like entry.rs:225: args.limit.is_none_or(|l| count < l). Worth pinning this in a productization note — it's the kind of detail that gets cargo-culted from the spike and then doesn't get revisited.

5. The 1 GB step limit override is documented as spike-only ("productization passes it alongside the query"). Productization needs both per-request step and metacall-depth limits over the buffer ABI; mirror the PLG_MAX_STEPS / PLG_METACALL_DEPTH precedent. The metacall depth one matters because PR #25's doc note acknowledged a wasm engine's ~1 MB default stack is smaller than native's ~8 MB.

6. JSON formatting duplication is acknowledged. The PR description names this: "productization extracts a single I/O-free core shared by the WASI shell and this one." Right call to not extract during the spike — gate-vs-design separation. When extraction happens, the shared core wants three knobs: (a) limit-aware exhausted computation, (b) error JSON shape, (c) success JSON shape. entry.rs:113–138 and reactor.rs:104–122 are very nearly the same code with different output sinks; an io::Write parameter would be enough.

Honest concerns inside the spike

7. plg_rt_run_query returns (len << 32) | ptr assuming wasm32. Correct on the actual target; would break on wasm64. Worth one line in the function doc — not a code change for the spike, but a record for the productization plan since wasm64 is an emerging target and the packed-u64 ABI would need rethinking there.

8. MACHINE is never freed. Long-running isolates accumulate one Machine per cold start, which is what you want — but a productization plan that supports updating the embedded program inside a live isolate would need a teardown entry point. Spike-acceptable; flag for productization.

9. The unsafe(no_mangle) pub extern "C" signature pattern is correctly used throughout. Each entry has a # Safety doc except plg_rt_alloc, which is genuinely safe (returns a pointer; the host's subsequent writes are its unsafety, not ours). The consistency is good — readers can trust that "no Safety doc = safe".

10. raw_alloc returns NonNull::dangling().as_ptr() for len == 0. Correct behavior (alloc with size 0 is UB in Rust's allocator API), and the host's plg_rt_free correctly no-ops on len == 0 to match. The two halves agree, but they're load-bearing-by-convention rather than load-bearing-by-API. One line on plg_rt_free ("len == 0 must match a len == 0 allocation; the dangling sentinel from raw_alloc(0) is implicit") would close the convention.

Disposition recommendations

  • Keep reactor.rs in-tree. It's small (133 lines), clippy-clean, dead-stripped from native, and forms the natural seed for productization. Reverting and re-deriving from history would lose the "Vec::with_capacity" lesson that's now baked into the comments. The PR description's "trivially revertable" is honest, but the file pays its own freight as a reference.
  • Don't extract the I/O-free core yet. Gate vs. design separation is the right discipline; both consumers exist now, so when extraction lands the abstraction will be shaped by two real call sites instead of one and a guess.
  • Record the productization items in a docdocs/design/WASM_TIER_2.md if one doesn't exist, or appended to WASM.md if it does. Findings 1–6 above are exactly the kind of "I forgot why we did it this way" knowledge that disappears between the spike and the productization PR.

What's good

  • The spike answers four real unknowns with concrete evidence (1M-deep on workerd, byte-identical round-trip, 1.65 MB module). Each is the kind of "would have been a multi-week dead end if it didn't work" question that justifies the spike.
  • MACHINE as Relaxed atomic is correct for single-threaded wasm and explicitly justified in the comment. The next reader doesn't have to wonder about memory ordering.
  • raw_alloc exact-Layout allocation is the right primitive given the host frees by (ptr, len). The bug it replaced (Vec over-allocation) is exactly the kind of finding that would have ambushed productization if not caught in a spike.
  • reset() keeps program state and clears per-query state. Atoms/registry/srcmap/limits survive. That's the right split — productization just needs to make the limits parameterizable.
  • Duplication is intentional, not accidental. Two implementations of "format solutions as JSON" co-exist because the spike is meant to be standalone. The PR description names this explicitly. That's the right tradeoff for a gate.
  • The reactor builds clean on native too (dead-stripped), so the spike doesn't force conditional compilation discipline before productization decides the feature split. Lets the productization PR be the one that introduces #[cfg(feature = "wasm_reactor")] or whatever it ends up being.
  • The IR awk transform and workerd config sit in /tmp/t2 — exactly right for spike-only scaffolding. Nothing throwaway leaks into the repo.

Suggested order

Nothing to fix in the spike — it serves its purpose as-is. The items above are productization-plan inputs, not PR change requests. The one judgment call worth making before merge: write the productization brief now (findings 1–6 in a WASM_TIER_2.md section) while the lessons are fresh, or trust that the spike's comments + this review carry the knowledge forward.

## Review — Tier 2 gate spike (workerd / V8 / wasm32-unknown-unknown) Right scope, right discipline. The gate is "does this approach actually work end-to-end on the real target?" and the answer (constant-stack to a million-deep on V8, buffer ABI round-trips byte-identical to native, module fits the budget) is the only answer that lets productization proceed without speculative architecture work. The PR description's "engineering, no existential unknowns" line is the correct framing — that's what a spike is for. What the gate actually proved, restated for the reader who's looking at the disposition later: 1. **No-WASI is viable.** The I/O-free `run_query` path never touches stdout/argv/exit stubs, so the no-stdlib environment isn't a blocker. The static `MACHINE` slot + reactor entry points show *how*: hand the host an ABI it can drive over linear memory and never touch `libc`. 2. **`musttail` lowers to `return_call` on V8 at depth.** This is the load-bearing finding for the whole Tier 2 plan. wasmtime (Tier 1) honoring tail-calls didn't guarantee V8 would; you'd have had to retreat to CPS-style trampolining in the runtime if it hadn't. It does, so the IR investment from PRs #20/#24 carries forward to Workers without rework. 3. **Buffer ABI shape is workable.** `alloc / run_query / free` over a packed `(len << 32) | ptr` return is a known-good pattern; that it round-trips byte-identical to native including the `existence_error` path is the gate's strongest evidence — it means the JSON format and the error rendering path don't need parallel implementations. 4. **Size fits.** 1.65 MB raw is the headline; compresses well, which is what Workers actually rate-limits on. The spike itself is small, contained, and the engineering inside it is honest. Below: things worth flagging as gate findings (for the productization disposition to react to) and one footgun to record before the file gets repurposed. ### Findings worth recording for productization **1. The `Vec::with_capacity` → `Layout`-exact allocator switch is a real ABI rule.** Documented in `raw_alloc`'s comment. Worth lifting that observation to the productization plan: any host-visible buffer that the host frees by `(ptr, len)` must be `alloc::alloc(Layout::from_size_align(len, 1))`, not `Vec::with_capacity(len)`. The next contributor implementing the productized reactor will reach for `Vec` reflexively. The current comment ("Vec::with_capacity may over-allocate") tells *what* but not *why* — adding "the host frees by requested-length, so actual-capacity > requested-length corrupts the allocator" would prevent the recurrence. **2. `MACHINE` as a `static AtomicPtr` is single-tenant, single-in-flight per isolate.** V8 isolates aren't multithreaded, but a Worker can have multiple in-flight async tasks sharing one isolate. For the gate (one query, JS awaits the result) this is fine. For productization either (a) document "one in-flight query per isolate" as the contract, or (b) take per-request state out of `MACHINE` and pass it through the buffer ABI. The contract route is much simpler and matches how most Workers use isolates; worth recording the choice now while the rationale is fresh. **3. `reset()` is the inverse of `Machine::new`.** It clears each per-query field by name. This works for the spike but it knows the Machine's field set — a future field added without reset coverage gives the next query state-leak symptoms that won't show up in single-query smoke tests. A `Machine::reset_per_query()` method living next to the field declarations would make "did I forget to clear this?" a local question. Worth noting *now* because productization will reuse this exact reset, and the leak class is the kind of bug that only surfaces under sustained traffic. **4. The hard-coded `"exhausted":true`** is correct for the spike (no `--limit`, so `solve` always runs to completion). Productization needs to take limit from the request and compute exhausted exactly like `entry.rs:225`: `args.limit.is_none_or(|l| count < l)`. Worth pinning this in a productization note — it's the kind of detail that gets cargo-culted from the spike and then doesn't get revisited. **5. The 1 GB step limit override** is documented as spike-only ("productization passes it alongside the query"). Productization needs both per-request step and metacall-depth limits over the buffer ABI; mirror the `PLG_MAX_STEPS` / `PLG_METACALL_DEPTH` precedent. The metacall depth one matters because PR #25's doc note acknowledged a wasm engine's ~1 MB default stack is smaller than native's ~8 MB. **6. JSON formatting duplication is acknowledged.** The PR description names this: "productization extracts a single I/O-free core shared by the WASI shell and this one." Right call to *not* extract during the spike — gate-vs-design separation. When extraction happens, the shared core wants three knobs: (a) limit-aware exhausted computation, (b) error JSON shape, (c) success JSON shape. `entry.rs:113–138` and `reactor.rs:104–122` are very nearly the same code with different output sinks; an `io::Write` parameter would be enough. ### Honest concerns inside the spike **7. `plg_rt_run_query` returns `(len << 32) | ptr` assuming wasm32.** Correct on the actual target; would break on wasm64. Worth one line in the function doc — not a code change for the spike, but a record for the productization plan since wasm64 *is* an emerging target and the packed-u64 ABI would need rethinking there. **8. `MACHINE` is never freed.** Long-running isolates accumulate one `Machine` per cold start, which is what you want — but a productization plan that supports updating the embedded program inside a live isolate would need a teardown entry point. Spike-acceptable; flag for productization. **9. The `unsafe(no_mangle) pub extern "C"`** signature pattern is correctly used throughout. Each entry has a `# Safety` doc except `plg_rt_alloc`, which is genuinely safe (returns a pointer; the host's subsequent writes are *its* unsafety, not ours). The consistency is good — readers can trust that "no Safety doc = safe". **10. `raw_alloc` returns `NonNull::dangling().as_ptr()` for `len == 0`.** Correct behavior (alloc with size 0 is UB in Rust's allocator API), and the host's `plg_rt_free` correctly no-ops on `len == 0` to match. The two halves agree, but they're load-bearing-by-convention rather than load-bearing-by-API. One line on `plg_rt_free` ("`len == 0` must match a `len == 0` allocation; the dangling sentinel from `raw_alloc(0)` is implicit") would close the convention. ### Disposition recommendations - **Keep `reactor.rs` in-tree.** It's small (133 lines), clippy-clean, dead-stripped from native, and forms the natural seed for productization. Reverting and re-deriving from history would lose the "Vec::with_capacity" lesson that's now baked into the comments. The PR description's "trivially revertable" is honest, but the file pays its own freight as a reference. - **Don't extract the I/O-free core yet.** Gate vs. design separation is the right discipline; both consumers exist now, so when extraction lands the abstraction will be shaped by two real call sites instead of one and a guess. - **Record the productization items in a doc** — `docs/design/WASM_TIER_2.md` if one doesn't exist, or appended to `WASM.md` if it does. Findings 1–6 above are exactly the kind of "I forgot why we did it this way" knowledge that disappears between the spike and the productization PR. ### What's good - **The spike answers four real unknowns** with concrete evidence (1M-deep on workerd, byte-identical round-trip, 1.65 MB module). Each is the kind of "would have been a multi-week dead end if it didn't work" question that justifies the spike. - **`MACHINE` as `Relaxed` atomic** is correct for single-threaded wasm and explicitly justified in the comment. The next reader doesn't have to wonder about memory ordering. - **`raw_alloc` exact-`Layout` allocation** is the right primitive given the host frees by `(ptr, len)`. The bug it replaced (Vec over-allocation) is exactly the kind of finding that would have ambushed productization if not caught in a spike. - **`reset()` keeps program state and clears per-query state.** Atoms/registry/srcmap/limits survive. That's the right split — productization just needs to make the limits parameterizable. - **Duplication is intentional, not accidental.** Two implementations of "format solutions as JSON" co-exist *because the spike is meant to be standalone*. The PR description names this explicitly. That's the right tradeoff for a gate. - **The reactor builds clean on native too** (dead-stripped), so the spike doesn't force conditional compilation discipline before productization decides the feature split. Lets the productization PR be the one that introduces `#[cfg(feature = "wasm_reactor")]` or whatever it ends up being. - **The IR awk transform and workerd config sit in `/tmp/t2`** — exactly right for spike-only scaffolding. Nothing throwaway leaks into the repo. ### Suggested order Nothing to fix *in the spike* — it serves its purpose as-is. The items above are productization-plan inputs, not PR change requests. The one judgment call worth making before merge: write the productization brief now (findings 1–6 in a `WASM_TIER_2.md` section) while the lessons are fresh, or trust that the spike's comments + this review carry the knowledge forward.
⏺ Build clean, fmt clean, clippy clean. Here's how I handled
All checks were successful
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 59s
9e7d35f74e
the review.

  Assessment

  This review is explicitly zero code-change requests —
  "Nothing to fix in the spike — it serves its purpose as-is.
  The items above are productization-plan inputs." It
  validates the gate (the four unknowns retired with concrete
  evidence) and frames findings 1–10 as knowledge to carry
  to the productization PR. So the only actionable is the one
  the review names: capture the brief while the lessons are
  fresh.

  What I did

  - Wrote the productization brief into docs/design/WASM.md
  (a new "Tier 2 — gate result & productization brief"
  section), since WASM.md already exists and owns the wasm
  design narrative — which is the review's own "appended to
  WASM.md if it does [exist]" branch. It records the gate
  result and all ten findings as a checklist: the allocator
  ABI rule (the Layout-exact lesson + the why), the
  single-in-flight MACHINE concurrency contract,
  reset_per_query next to the field decls, limit-aware
  exhausted, per-request step/depth limits over the ABI, the
  I/O-free-core extraction shape (the io::Write-param
  three-knob core shared with INVOCATION's resident mode),
  the wasm32 packed-return assumption, MACHINE teardown, the
  Safety-doc convention, and the len==0 sentinel pairing.
  - Two trivial doc clarifications to the in-tree reference
  file (reactor.rs), exactly as the review proposed: finding
  7 (the packed return assumes wasm32) on plg_rt_run_query,
  and finding 10 (the len==0 ↔ raw_alloc(0) convention) on
  plg_rt_free. Finding 1's "why" was already in raw_alloc's
  comment, so I left it.

  Disposition followed

  - Kept reactor.rs in-tree — the review recommends it
  (small, clippy-clean, dead-stripped from native, the
  productization seed, and it carries the Vec::with_capacity
  lesson in its comments).
  - Did not extract the I/O-free core — gate-vs-design
  separation; both call sites exist now, so the abstraction
  gets shaped by two real consumers when extraction lands.

  No behavior changed (comments + a doc only); runtime build,
  fmt, and clippy are green.
navicore deleted branch wasm-t2 2026-06-20 19:22:36 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-prolog!26
No description provided.