Skip to content

feat(codegen): persistent component state + inter-thread connections (REQ-CODEGEN-WIT-STATE-001)#325

Merged
avrabe merged 4 commits into
mainfrom
feat/codegen-state
Jul 11, 2026
Merged

feat(codegen): persistent component state + inter-thread connections (REQ-CODEGEN-WIT-STATE-001)#325
avrabe merged 4 commits into
mainfrom
feat/codegen-state

Conversation

@avrabe

@avrabe avrabe commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

What

Removes the two v0.25 data-plane limitations. The generated component Guest now:

  • Persistent state — holds ONE instance of each thread's component in a thread_local! { RefCell<{Struct}Default> }, borrowed per compute, instead of constructing {Struct}Default fresh each dispatch. Component state now survives across dispatches. The skeleton struct gains #[derive(Default)] for the one-time init. A wasip2 component is single-threaded and non-reentrant, so borrow_mut cannot alias.
  • Inter-thread connections — a ConnectionInstance with BOTH ends Some(...) (thread A out → thread B in, previously skipped) now routes through a thread_local! buffer with latest-value / sampling semantics: the source's Out field writes the buffer after compute, the destination's In field reads the latest value before compute. (resolve_interthread_routes + InterThreadRoute.) This is sampling, not AADL immediate/delayed connection timing — out of scope, stated in the requirement.

Oracles

  • Resolver / shape (fast): interthread_resolver_is_exact, state_bindings_emit_threadlocal_and_interthread_buffer.
  • Golden lock + cargo check of the emitted STATE crate (green under -D warnings).
  • wasmtime state-oracle (out-of-workspace, the load-bearing gate) — on ONE live instance:
    • persistence: an accumulator fed 5 then 7 returns 12 (fresh-construct or a call-local buffer would give 7 — the number discriminates);
    • inter-thread: pr.po → buffer → cn.ci → sink-out delivers 42 across two separate dispatches.
    STATE ORACLE PASSED: persistence acc(5,7)->12 across two dispatches, and
    inter-thread pr.po->buffer->cn.ci->sink-out delivered 42
    

Falsification (both sabotage-verified non-vacuous)

  • Reverting the thread_local to a per-call Default drops persistence to 7.
  • Dropping the buffer write drops inter-thread delivery to 0.

Both wired into the codegen-oracle CI job. The v0.25 DATAPLANE exec oracle still passes (its generated code now carries the state/inter-thread wiring).

Stated limitation

User state lives in the generated {thread}.rs skeleton, which carries DO NOT EDIT — regenerate; adding real state means editing a file regen overwrites. The mechanism (instance reuse) ships here; a regen-safe authoring split is a separate successor.

🤖 Generated with Claude Code

avrabe and others added 4 commits July 11, 2026 06:34
… inter-thread)

Word to the mechanism, not an authoring story: persistent state = the binding
REUSES the component instance across dispatches (thread_local RefCell), so state
survives. Inter-thread = thread_local buffer with LATEST-VALUE/SAMPLING semantics
(last-writer-wins), explicitly NOT AADL immediate/delayed timing. Oracle on ONE
live instance: accumulator (5,7 -> 12 discriminates persistence) + producer->
consumer across separate dispatches, both sabotaged for non-vacuity. Stated
limitation: user state lives in the DO-NOT-EDIT skeleton (regen-safe authoring is
a successor).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(REQ-CODEGEN-WIT-STATE-001)

Generated Guest now:
- reuses ONE component instance per thread across dispatches via a thread_local
  RefCell (borrowed per compute) instead of constructing {Struct}Default fresh, so
  component state persists. Skeleton struct gains #[derive(Default)] for the
  one-time init. Single-threaded, non-reentrant wasip2 => borrow_mut cannot alias.
- routes inter-thread connections (both ends Some, previously skipped by
  resolve_port_routes) through a thread_local buffer with latest-value/sampling
  semantics: source Out writes the buffer after compute, dest In reads it before.
  New resolve_interthread_routes + InterThreadRoute::buffer_ident (UPPER_SNAKE).

Oracles: resolver + shape fast tests (interthread_resolver_is_exact,
state_bindings_emit_threadlocal_and_interthread_buffer), STATE golden-lock +
cargo-check (green under -D warnings), and the load-bearing wasmtime state-oracle
bin proving on ONE live instance: accumulator acc(5,7)->12 (persistence) and
pr.po->buffer->cn.ci->sink-out delivers 42 (inter-thread). Persistence sabotage
(fresh-construct) drops to 7 as predicted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oracle job

REQ-CODEGEN-WIT-STATE-001: the codegen-oracle job now also runs the state-oracle
bin (persistence acc(5,7)->12 + inter-thread delivery 42), alongside the data-plane
exec oracle. Both proofs sabotage-verified non-vacuous locally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…solved

Clean-room review caught the generated lib.rs header + generate_process_bindings
rustdoc still disclaiming "constructed fresh per dispatch, state does NOT persist,
inter-thread not yet plumbed" and "initialize/finalize keep the plain construct-
and-call body" — all falsified by REQ-CODEGEN-WIT-STATE-001 (the wrapper borrows
the persistent instance in every method; inter-thread is plumbed). The header
shipped in every generated component, contradicting the persistent-state comment
later in the same file. Now both state the reused-instance + latest-value buffers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Rivet verification gate

20/20 passed

count
Passed 20
Failed 0
Skipped (no steps) 0

Filter: (and (= type "feature") (or (has-tag "v093") (has-tag "v0100")))

Failed artifacts

(none)

Updated automatically by tools/post_verification_comment.py. Source of truth: artifacts/verification.yaml.

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.24561% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/spar-codegen/src/rust_gen.rs 98.24% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe merged commit 457ed86 into main Jul 11, 2026
19 checks passed
@avrabe avrabe deleted the feat/codegen-state branch July 11, 2026 07:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant