pmacs/pmacs-gpu
Levi Neuwirth f976dc1042
feat(gpu): 1-pre P2 --- the effect harness, outbound AND local
Review round 1's blocker: P2 requires outbound events plus local
effects, and the harness recorded `Route` classifications only. The
wheel is the proof it was not enough --- a wheel route carries a delta,
and whether that becomes a viewport update, a panel event, a terminal
event or nothing at all is `State`'s to decide. A route names the
family; only running the body names the effect.

**`App::dispatch_window_event` is what makes P2 reachable, and it is the
substantive change here.** Left inside `window_event`, the dispatch
would force a harness to re-implement it --- and a harness that
re-implements the thing it tests witnesses its own copy. `window_event`
is now four lines: call dispatch, exit if it asks. **P3 narrows from a
33-line match to a single `if`.**

`EffectHarness` drives production code end to end:

  * a REAL `AttachClient` over a `socketpair`, through the real
    handshake, outbox, writer thread and encoder, so what is recorded is
    the wire and not a mock's idea of it (`connect_stream_for_test` in
    attach.rs exists only because the constructor is private to that
    module; it adds no behaviour);
  * a REAL windowless `State`, so the bodies take their real branches;
  * `dispatch_window_event` itself.

Local effects have no wire trace, so each is read where it lands: exit
from the returned `EventOutcome`, redraw from a test-only
`State::render_calls`, resize from the surface config, the modifier
mutation from `App::modifiers`, and the scroll from `scroll_top`.

**Steps are delimited by a sentinel key, not a sleep.** "This step sent
nothing" is otherwise undecidable without waiting, and a fixed-duration
wait against a writer thread is the core-count assumption PR #235's CI
red was made of. The sentinel is not coalesceable (only viewport and
drag kinds are), so it can neither replace nor be replaced by a recorded
event. It does sit between steps, so cross-step coalescing that
production would perform is absent here --- stated in the harness doc,
since it makes the transcript per-step rather than as-coalesced.

**Never skips.** Per the ruling, a missing wgpu adapter is an assertion
failure and not a skip: this project has twice recorded a suite that
returned `ok` without running. Mutation M21 makes `new_headless` return
`None` and all NINE effect rows fail loudly while the thirteen pure
routing rows, which need no GPU, stay green --- the two tiers behaving
exactly as intended.

TWO ROWS WERE WRONG AND THE MUTATIONS FOUND THEM, WHICH IS THE POINT:

  * the wheel row asserted `.all(|e| matches!(e, Viewport))` over the
    transcript --- VACUOUSLY TRUE ON AN EMPTY ONE, so an outbound-blind
    harness passed it. Now asserts non-empty first.
  * with that fixed it still failed, for a second reason: the fixture
    was two lines and could not scroll, and a headless `State` has no
    attached buffer, so `scroll_by_lines` returned `None` and withheld
    every send. Both are absences the harness manufactured itself ---
    the same shape as the panel wire, below.

The panel wire is the third of those. `resumed` sets the frontend id and
the session version on the state before any geometry flush; the harness
did not, so `flush_panel_geometry` silently withheld the declaration and
the resize row failed against an absence of its own making. The harness
now mirrors that wiring and drains the attach-time declaration, so each
row's transcript holds only what its own event produced.

Evidence --- 22 rows (13 routing, 9 effect), 6 further mutations:

  M18 exit effect discarded            -> the close row
  M19 redraw effect discarded          -> the redraw row
  M20 apply_resize stops declaring     -> the resize row
  M21 no wgpu adapter                  -> all NINE effect rows, loudly
  M22 harness blind to OUTBOUND        -> resize + wheel
  M23 harness blind to LOCAL           -> six rows

M22 and M23 together are P2's contract made executable: blind the
harness to either half and rows fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
2026-08-12 10:36:12 +02:00
..
fonts feat(math): bundle Latin Modern Math and read its MATH constants 2026-07-24 18:27:34 -04:00
src feat(gpu): 1-pre P2 --- the effect harness, outbound AND local 2026-08-12 10:36:12 +02:00
Cargo.toml feat(release): binaries on tag — Distribution Stage 1 2026-08-01 14:40:47 -04:00