2026-05-16 — Stone C1 SHIPPED; Stone C2 PARTIAL
Stone C1 shipped at commit 77c99d9. ThreadPeer<I, O> + Thread/readln + Thread/println + make_thread_peer_pair_for_test Rust helper. 3/3 tests green. ~35 min sonnet.
Stone C2 is PARTIAL on disk — implementation complete, but the test fixture took sub-decision (b) (Rust-only mock with make_process_peer_for_test) instead of sub-decision (a) (real spawn-process round-trip). The user flagged this as the easy framing before commit:
“mocks?.. is that an honest word or are we actuallly measuring what we must be… simple things should be trivial to test - we test in a hermetic env if we must… spawn a read server and talk to it…”
What sonnet shipped (working tree, uncommitted at this entry)
Section titled “What sonnet shipped (working tree, uncommitted at this entry)”| File | Status | LOC |
|---|---|---|
src/types.rs | modified | +61 (ProcessPeer<I, O> struct) |
src/check.rs | modified | +52 (Process/readln + Process/println type schemes) |
src/runtime.rs | modified | +151 (eval handlers + dispatch arms) |
src/typed_channel.rs | modified | +87 (make_process_peer_for_test helper) |
tests/wat_arc170_stone_c2_processpeer.rs | new | 8850 bytes, 3 tests, all mock-driven |
docs/arc/2026/05/170-program-entry-points/SCORE-STONE-C2-PROCESSPEER.md | new (orchestrator-written) | ~150 lines |
All 3 tests green; workspace baseline unchanged. The substrate wiring is correct.
Why the BRIEF allowed (b)
Section titled “Why the BRIEF allowed (b)”BRIEF § Implementation protocol step 5: “Option A: spawn a real process via existing spawn-process; construct ProcessPeer from its Process/stdin + Process/stdout; round-trip. Option B: Rust-side mock similar to C1’s helper. Sonnet picks based on simplicity.”
EXPECTATIONS predicted: “Sub-decision (b) Rust mock — faster, less integration-y.”
Sonnet picked (b) per the explicit BRIEF authority. The BRIEF was wrong to authorize it. The right framing was: real-spawn integration is non-negotiable for a Process peer; the mock cuts the integration story the type EXISTS to provide.
The substrate gap the mock hides
Section titled “The substrate gap the mock hides”make_process_peer_for_test exercises the same Value-layer dispatch paths (typed Sender/Receiver over PipeFd) that real spawn-process would. But it bypasses the substrate construction surface: a wat user has no way to mint ProcessPeer<I, O> from a Process<I, O> handle today. The verb is defined; the path to construct one is missing.
Stone D’s bracket macro is supposed to wire this — but until Stone D ships, the only proof that ProcessPeer is reachable from wat is via an explicit constructor verb. Without it: ProcessPeer is defined, verbs dispatch, tests pass — and a wat user cannot use any of it.
First reflex (rejected) — mint ProcessPeer/from-process constructor verb
Section titled “First reflex (rejected) — mint ProcessPeer/from-process constructor verb”Orchestrator’s first response was to propose minting a new substrate verb :wat::kernel::ProcessPeer/from-process to wrap the composition. Wrong on multiple discipline anchors:
feedback_no_new_types— STOP signal on wrapper-verb-creation reflex when substrate already has the partsfeedback_assertion_demands_evidence— the proposal asserted “the substrate gap is no constructor” without verifying that existing primitives compose. The???in the orchestrator’s own pseudocode was the ignorance signal
Grep verification (post-compaction) revealed every primitive needed already exists:
:wat::kernel::Process/stdin proc -> :wat::io::IOWriter— src/check.rs:12916:wat::kernel::Process/stdout proc -> :wat::io::IOReader— src/check.rs:12925:wat::kernel::Sender/from-pipe writer -> :Sender<O>— existing wat-level helper:wat::kernel::Receiver/from-pipe reader -> :Receiver<I>— existing wat-level helper:wat::kernel::ProcessPeer/new rx tx -> :ProcessPeer<I,O>— auto-generated by struct mechanism (src/runtime.rs:2470 registers<type>/newfor every struct, including substrate-registered builtins)
ZERO substrate additions needed.
User-facing IPC framing — the question Stone C2’s test promotes
Section titled “User-facing IPC framing — the question Stone C2’s test promotes”User caught the deeper concern: drafting a real-spawn integration test that wires spawn-process + peer composition + drain-and-join LOOKS like documenting the user-facing IPC pattern. That promotes the test fixture to teaching artifact — and the artifact would teach users to manually compose lifecycle primitives.
Run four-questions on what the user-facing IPC surface SHOULD be:
(a) drain-and-join + manual peer composition = user-facing pattern
- Obvious: marginal — Stone B made
drain-and-joinpublic, but is manual composition the FULL surface? - Simple: YES (minimal substrate)
- Honest: NO — users will forget drain, wire rx/tx backwards, no panic cascade, no supervision across N processes
- Good UX: NO — three-line peer construction every call site
→ Fails on honest + good UX.
(b) Stone D’s run-processes bracket = user-facing surface; Stone C2’s test = substrate-composition proof
- Obvious: YES — primitives compose; macro hides composition
- Simple: YES — users learn ONE form
- Honest: YES — bracket enforces drain, peer direction, supervision — users CAN’T fuck up
- Good UX: YES — one bracket form, all lifecycle hidden
→ YES YES YES YES.
Resolved direction (b). No new manager layer needed — Stone D IS the manager layer (that’s exactly its job). Stone C2’s integration test is the substrate-composition proof, not the user-facing IPC pattern. Framing must reflect that explicitly:
- Test file renamed
tests/wat_arc170_stone_c2_processpeer.rs→tests/wat_process_peer_ipc_round_trip.rs(concept-anchored) - Header comment names Stone D as the user-facing surface: “this exercises the substrate primitives Stone D’s
run-processesbracket macro will compose; user code never writes this manually — it writes the bracket” drain-and-joinIS public (Stone B made it the canonical safe lifecycle primitive), but its public availability does NOT promote it to the user-facing IPC surface — Stone D wraps it for normal use
Stone C2 revision plan (post-direction-(b))
Section titled “Stone C2 revision plan (post-direction-(b))”- Drop the constructor-verb reflex from this INTERSTITIAL + SCORE
- Rewrite the test: T1 (type mint) + T3 (asymmetry) stay; T2 becomes real-spawn round-trip composing
Process/stdin+Process/stdout+Sender/from-pipe+Receiver/from-pipe+ProcessPeer/new+Process/println+Process/readln+Process/drain-and-join— every primitive already exists - Rename test file (concept-anchored) + header comment names Stone D as user-facing surface
- Retire
make_process_peer_for_testRust helper (no longer needed — real-spawn test replaces its role) - Verify workspace green
- Tick Stone C2
[x]inBRACKET-IMPLEMENTATION-STONES.md§ Status - Commit atomically + push
Calibration lessons
Section titled “Calibration lessons”BRIEFs MUST NOT authorize the easy framing. The BRIEF named (a) real-spawn and (b) Rust mock as equivalent options “based on simplicity.” That phrasing invited sonnet to pick the easy version — and sonnet did. The feedback_refuse_easy_solutions discipline applies at BRIEF-drafting time, not just user-review-time. See feedback_brief_no_easy_auth.
Constructor-verb reflex is wrapper-type creation. Adding ProcessPeer/from-process to “make composition pleasant” was the reflex feedback_no_new_types catches. The verbose composition is the honest form (feedback_verbose_is_honest): it REVEALS that ProcessPeer wraps a Receiver + Sender; that the Receiver reads from child’s stdout; that the Sender writes to child’s stdin. Three nested calls in a test fixture is fine; the macro hides them for everyday use.
Substrate-level vs user-facing distinction. Stone C2 ships the type + verbs; Stone D ships the user-facing bracket. The integration test PROVES Stone C2’s primitives compose — it does NOT document the user-facing IPC pattern. Header framing must make this explicit, or the test becomes misleading teaching material.