feat(p3-bridge): cross-component stream-bridge emitter (#141, SR-33)#239
Conversation
SyntheticKind::StreamBridge tags the stream-bridge dispatch shims the #141 emitter appends to merged.functions, mirroring AdapterShim / TaskReturnShim. AdapterRole::StreamBridge maps it to <meld-adapter> line 10 (next free line; adapter_lines_are_distinct_and_nonzero pins the contract witness consumes). Co-Authored-By: Claude Opus 4.8 <[email protected]>
The headline #141 deliverable: when the resolver's StreamPairGraph is non-empty, the fuse pipeline (new step 2.6) emits the shim-dispatch bridge designed in the issue's v0.29.0 design comment: - one bridge memory (8 slots x 4096-byte power-of-two rings; 1 header page + 1 ring page, fixed 2 pages, compile-time layout asserts), - per-component shims for stream_new/read/write/drop_readable/ drop_writable with the component's merged memory index hardwired as an immediate (same-memory fusion = identical codegen, immediates 0), - bit-31 LOCAL_TAG handle dispatch: tagged -> ring ops, untagged -> the retained pulseengine:async host import (foreign fallback); slot exhaustion falls back to host stream_new, never errors, - ADR-2 contract preserved: write returns accepted count (0 = backpressure), read returns bytes (0 = EOF only after writer drop AND drain, -5 Pending while open+empty); u32 wrapping monotonic cursors, two-part wrapping memory.copy on both sides, - call sites rewired via function_index_map + body re-extraction (the wire_adapter_indices mechanism). Placement deviation from the prompt-level plan, recorded in the module docs: the emitter runs BEFORE adapter generation/wiring because adapters are encoded after merged.functions - appending shims after wire_adapter_indices had baked functions.len()-derived adapter indices into call sites would shift every adapter call off-target. ADR-3 amendment recorded: 'zero-copy same-memory ring' drops to 'no host crossing, single copy' (the ABI's caller-buffer contract requires the copy; fusion removes the double host round-trip). Co-Authored-By: Claude Opus 4.8 <[email protected]>
Fuses two hand-built components through the REAL pipeline (no test-only entry point) and executes the fused core module under wasmtime with pulseengine:async host stubs that TRAP on any bit-31-tagged handle — proving bridged streams never cross the host: - ls_st_1_round_trip_local_stream_never_crosses_host: [1,2,3,4] producer->consumer via a local stream, zero host calls (LS-ST-1 gate) - cross_memory_chain_preserves_data_across_distinct_memories: 3 memories (producer + consumer + bridge), 300-byte pattern intact - backpressure_partial_write_then_drain_then_resume: write > RING_CAP returns 4096 accepted, 0 on full ring, remainder after drain, two-part ring-wrap copy exercised on write AND read - ls_st_1_eof_only_after_writer_drop_and_drain: -5 Pending while open+empty; drop_writable -> drain -> sticky 0 EOF (LS-ST-1 gate) - slot_exhaustion_falls_back_to_host_stream: 9th stream is host-minted (untagged) and its ops reach the host imports exactly once each Pair-gating construction (documented in the test header): the detector needs CanonicalEntry::StreamWrite/StreamRead plus a resolved_imports connection. Components carry an UNTYPED stream type + canon stream.write/stream.read (untyped avoids the validator's memory-option requirement, which would force instantiating the module in-component and internalise the host imports), and are fusion-connected by the producer exporting its core module as "link" and the consumer importing a core module "link". The sanctioned fallback (driving the emitter with a constructed StreamPairGraph) was NOT needed. Co-Authored-By: Claude Opus 4.8 <[email protected]>
) LS-ST-1 (approved, UCA-A-4, H-1/H-3/H-4): the bridge mis-routes a stream op (local handle to host / foreign handle to ring) or corrupts ring cursors — fix pins the four p3_bridge_runtime oracles plus the host-fallback fixture, mirroring LS-D-3's structure. SR-33 planned -> implemented (milestone v0.29.0), keeping the honest history note: detection shipped v0.9.0 (#173), the emitter was MISSING until v0.29.0 (the earlier 'implemented' status was a bulk-sync error reverted in PR #221). Verification names the four runtime oracles and records the ADR-3 'zero-copy' -> 'no host crossing, single copy' amendment. rivet validate: 175 -> 174 pre-existing errors (this change closes one gap, introduces none). Co-Authored-By: Claude Opus 4.8 <[email protected]>
Mythos delta-pass requiredThis PR modifies one or more Tier-5 source files (per Before merge, run the Mythos discover protocol on the
Why this gate exists: LS-A-10 The gate check on this PR will pass once the label is |
LS-N verification gate
Approved Failed LS entries(none) Missing regression tests
Updated automatically by |
S1: a zero-length read with data available returned 0 — the EOF
sentinel — so a status probe could close a live stream. Now returns
Pending (-5); 0 stays reserved for writer-dropped-and-drained.
S2: a write to a slot whose reader (or writer) end was dropped
silently filled the ring and returned the accepted count. Now
returns AbiError::Closed per ADR-2 ('subsequent writes return
Closed').
Both flagged by the clean-room pass as contract deviations not
provable as divergence against the in-repo host stubs (which share
the same idealization) — fixed defensively since the production
host semantics are the authority. All 5 runtime oracles still pass.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Mythos clean-room delta-pass — Tier-5 files
|
Mythos delta-pass (auto)❌ 1 finding(s) across 3 Tier-5 file(s)
Auto-run via |
v0.29.0 scope — the emitter the SR-33 audit proved was never built (closes #141; design recorded on the issue, ADR-3 path-N follow-up).
What
meld-core/src/p3_bridge.rs(Tier-5, pre-registered in #238): when the fused module'sStreamPairGraphis non-empty, meld emits a bridge memory (slot headers + 8×4096B rings), per-component shims for the five stream intrinsics with the component's memory index hardwired, and rewires intrinsic call sites to the shims. Dispatch on handle bit 31: locally-minted handles → in-module ring ops (no host crossing); foreign handles → the retained host imports; slot exhaustion → hoststream_newfallback. ADR-2 contract preserved end-to-end (write returns accepted count, 0 = backpressure; read returns bytes, 0 = EOF only after writer-drop AND drain, −5 Pending; drop_writable half-closes).Oracles (runtime, through the REAL pipeline)
Five fixtures fuse two hand-built components (which trigger the real pair detector — untyped
streamcanon entries + fusion connection via a linked core-module export) and execute under wasmtime with host stubs that trap on any tagged handle plus zero-call counters:ls_st_1_round_trip_local_stream_never_crosses_hostcross_memory_chain_preserves_data_across_distinct_memoriesbackpressure_partial_write_then_drain_then_resumels_st_1_eof_only_after_writer_drop_and_drainslot_exhaustion_falls_back_to_host_streamAll pass; workspace 537/0; clippy/fmt clean. LS-ST-1 approved; SR-33 → implemented (honest history note kept; ADR-3 "zero-copy" amended to "no host crossing, single copy").
Notes & limitations (documented in-module)
Emitter runs before adapter wiring (adapter indices bake in
functions.len()); slots retire ondrop_readable(no reuse — degrades to host, never loses data); cancel ops stay host-routed (bridged ops never block); module-widestream_newtakeover is the accepted static-pairing over-approximation; shared-memory mode is the same codegen with immediates 0 but has no dedicated runtime fixture.Mythos clean-room pass to follow as comment + label before merge.
🤖 Generated with Claude Code