Merge branch 'main' into pty-terminate-eperm

Same one-line ledger conflict as the previous integration, now against
the refreshed main (#168): keep this lane's PTY terminate section, take
main's newer Lean 4 lane heading.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RuhVYUPHXMHG8r2z4tsDPR
This commit is contained in:
Levi Neuwirth 2026-07-26 15:00:49 -04:00
commit 8943b4c277
4 changed files with 321 additions and 96 deletions

View File

@ -14,13 +14,16 @@ backlog.
machine-local: `origin` may name this canonical URL, a release mirror, machine-local: `origin` may name this canonical URL, a release mirror,
or something else, and therefore has no authority by name alone. or something else, and therefore has no authority by name alone.
- Canonical base at this snapshot: - Canonical base at this snapshot:
`githubsucks/main` @ `d400f30` (Lean 4 Stage 3b #170 atop Stage 3a `githubsucks/main` @ `a27f646` (Lean 4 Stage 4a #179 atop bottom-panel
#167, the bottom-panel landed-doc refresh #156, the inline-math slice Stage 2A #177, the bottom-panel Stage 2 framing #175, terminal
#158, dired Stage 1 #165, the GPU terminal input fix #166, Lean 4 configuration Stage 1 #173, Lean 4 Stage 3b #170, Stage 3a #167, the
Stage 2 #161, the dired framing #164, COHERENCE.md #163, find-file CRDT undo repro #157, the inline-math landed-doc refresh #172, the
#162, Lean 4 Stage 1 #160, and the minimap blank-slab fix #159; bottom-panel landed-doc refresh #156, the inline-math slice #158,
protocol v20). The previous snapshot named `d152120`; the recovery dired Stage 1 #165, the GPU terminal input fix #166, Lean 4 Stage 2
check below accepts it or anything newer. #161, the dired framing #164, COHERENCE.md #163, find-file #162, Lean 4
Stage 1 #160, and the minimap blank-slab fix #159; protocol v20). The
previous snapshot named `d152120`; the recovery check below accepts it
or anything newer.
- On the transfer source, `origin/main` named a release mirror at - On the transfer source, `origin/main` named a release mirror at
`d3fa632` and lagged badly. On the current destination, `origin` names `d3fa632` and lagged badly. On the current destination, `origin` names
the canonical URL. This difference is why all recovery begins by the canonical URL. This difference is why all recovery begins by
@ -143,7 +146,7 @@ If it does not, stop and repair the remote/fetch configuration.
to recur; the next occurrence carries its own evidence under whoever's to recur; the next occurrence carries its own evidence under whoever's
PR, and a Stage B framing follows then. PR, and a Stage B framing follows then.
## Lean 4 lane (Arc 8) — Stages 1, 2, 3a, 3b MERGED; Stage 4a IN REVIEW ## Lean 4 lane (Arc 8) — Stages 1, 2, 3a, 3b, 4a MERGED; 4b is next
- **Stages 1, 2, 3a and 3b are MERGED**#160 (`main` @ `0827dd1`), - **Stages 1, 2, 3a and 3b are MERGED**#160 (`main` @ `0827dd1`),
#161 (`46a1b8f`), #167 (`6f348c9`), #170 (`d400f30`). Their full #161 (`46a1b8f`), #167 (`6f348c9`), #170 (`d400f30`). Their full
@ -159,9 +162,12 @@ If it does not, stop and repair the remote/fetch configuration.
(`main` @ `d400f30`), 2026-07-26. Both were integrated against a main (`main` @ `d400f30`), 2026-07-26. Both were integrated against a main
that had advanced 50 commits mid-review; the only conflict either time that had advanced 50 commits mid-review; the only conflict either time
was this ledger's own lane headings, resolved by keeping both sides. was this ledger's own lane headings, resolved by keeping both sides.
- Worktree `../pmacs-lean-stage4`, branched off `main` @ `d400f30`. - **Stage 4a merged as #179** (`main` @ `a27f646`, 2026-07-26) — the
Framing-only so far: `docs/lean4-mode-framing.md` **revision 8**. No typed-edit consumer chain. Worktree `../pmacs-lean-stage4`, branched
code. Awaiting user approval before implementation, per the workflow. off `main` @ `d400f30`; retained, carrying nothing unmerged.
`docs/lean4-mode-framing.md` **revision 8** remains the approved
framing. **Stage 4b (the Lean-specific half) is framed and not
started.**
- **Round 6 review found five P1s, four of them internal to rev 6** - **Round 6 review found five P1s, four of them internal to rev 6**
facts about pmacs the revision asserted without checking, while its facts about pmacs the revision asserted without checking, while its
external (upstream) facts held. Fixed in rev 7: Stage 4a's footprint external (upstream) facts held. Fixed in rev 7: Stage 4a's footprint
@ -458,68 +464,109 @@ If it does not, stop and repair the remote/fetch configuration.
resolve *to*, and `pmacs .` should route into it rather than growing a resolve *to*, and `pmacs .` should route into it rather than growing a
second directory surface. second directory surface.
## GPU terminal input lane — IN REVIEW ## The CRDT half of the test corpus is dark in CI — NEEDS A LANE
- Portable branch: `githubsucks/gpu-terminal-input`, worktree - **No branch, no framing yet.** Found while gating #166, then measured
`../pmacs-gui-term-input`, based on `githubsucks/main` @ `46a1b8f`. properly during the vterm as-framed audit. Deliberately kept out of #166 so
- Approved framing: `docs/gpu-terminal-input-framing.md` revision 2, a CI change would not arrive after review approval.
committed as the branch's first commit (`9a0df21`). Bug fix, not a - **Root cause:** `.github/workflows/ci.yml` never enables the `crdt` feature
feature; **no protocol change (stays v20)**. anywhere — zero hits across the workflow directory. The `test` job runs
- Reported as "text input within the terminal doesn't work on GUI, this is `cargo test --all-targets --no-default-features --features luajit|lua54`.
fine in TUI". Root cause: the dispatcher applied **both** terminal-layout Every `#[cfg(feature = "crdt")]` test is therefore **not compiled** in CI,
syncs to **every** attached frontend, and a semantic session satisfies both not merely skipped.
conditions (a `term_sizes` entry from `AttachRequest` *and* a terminal - **Measured, `--list` under CI's exact flags versus the same flags plus
declaration). Its PTY was resized twice per tick forever — grid arm installs `crdt`: 3,024 vs 3,288 — 264 tests dark.** Per target:
the TUI placement size, semantic arm installs the declared content
rectangle, each arm's idempotence guard seeing only what the other just
wrote — so the child took a `SIGWINCH` storm at tick cadence.
- **The fix is a split, not a guard.** The grid arm is also the only per-tick
controller-liveness release a semantic frontend gets, and
`sync_semantic_terminal_layout` cannot take that over: the buffer-follow
snapshot clears the viewport declaration (`on_buffer_snapshot_sent`), so
that arm stops running in exactly the switch-away case that needs the
release. `sync_terminal_layout` is therefore split into a
frontend-kind-neutral half (panel reconcile + liveness) and a grid-only
geometry half, with the loop body extracted to
`sync_terminal_layouts_for_tick` so the exclusivity is structural and tests
drive the real thing.
- **Trap for anyone touching this again:** the release at the "no
`window_placements` entry" arm reads like liveness and is grid geometry. A
semantic frontend has no placement entry at all, so moving it into the
neutral half releases a GPU controller every tick.
- Bite-verified against **two** pre-images, because the naive guard fixes the
storm and introduces the leak:
| pin | `main` | naive guard | the split | | dark | CI | full | target |
|---|---|---|---| |---:|---:|---:|---|
| settle (acc 2+3) | FAIL | pass | pass | | 177 | 1,832 | 2,009 | **the library itself** (`src/lib.rs`) |
| controller release (acc 6) | pass | FAIL | pass | | 21 | 15 | 36 | `m5_5_acceptance` |
| grid still resizes (acc 5) | pass | pass | pass | | 13 | 1 | 14 | `gpu_invocation_acceptance` |
| 13 | 1 | 14 | `gpu_initial_target_acceptance` |
| 8 | 0 | 8 | `m10_11_acceptance` |
| 6 | 0 | 6 | `auto_pair_crdt_acceptance` |
| 6 | 0 | 6 | `m10_2_perf` |
| 4 | 5 | 9 | `vterm_stage3_acceptance` |
| 4 | 0 | 4 | `m10_10_perf` |
| 3 | 0 | 3 | `compile_mode_crdt_acceptance` |
| 2 | 22 | 24 | `theme_faces_acceptance` |
| 2 | 0 | 2 | `m11_5_semantic_acceptance` |
| 1 | 9 | 10 | `vterm_stage1_acceptance` |
| 1 | 7 | 8 | `statusline_segments_acceptance` |
| 1 | 10 | 11 | `gpu_font_acceptance` |
| 1 | 0 | 1 | `auto_indent_crdt_acceptance` |
| 1 | 0 | 1 | `m10_11_perf` |
- Real-path evidence: a quiet child trapping `SIGWINCH` reports **144 frames - **The single worst line is the library.** `cargo test --lib --features crdt`
in 4 s and `WINCH 1..12` on screen** against the pre-fix tree, versus a is a REQUIRED local gate in `CLAUDE.md`, and CI has never run it. 177
settled screen with the fix. library tests — the whole CRDT half — are developer-machine-only.
- **Deliberately out of scope, named:** interactive-shell echo on a raw-mode - **Ten suites run zero or one test in CI**, including `gpu_initial_target`
PTY (Q#GT5 — reproduces in-process too, so it is not the GUI/TUI (#148's entire acceptance, 1/14), `gpu_invocation` (#141's, 1/14), and
asymmetry), and a geometry change appearing to clear the visible screen `a37`, the Vterm Stage 3 real-daemon/real-PTY/real-wgpu path that #135
(reproduces pre-fix; why acceptance 4 latches its observation across built specifically because "a decoded-message fixture would prove none of
frames). the three fit together".
- Verification on this branch: `cargo fmt --check` clean; strict workspace - **`a37` will report green in the new job without running, unless the
Clippy clean; 1,829 default + 2,006 CRDT library tests; vterm Stage 1/2/3 job builds `pmacs-gpu` AND sets `PMACS_REQUIRE_GPU=1`.** Measured
10 / 6 / 9 CRDT; bottom-panel Stage 1 46; M4 121; required GPU 155; 2026-07-26 while gating #173. `a37_real_daemon_real_pty_and_headless_gpu_
**isolated-config workspace sweep 3,177 across 92 suites, zero failures**; render_one_terminal_session` derives its sibling binary path from
`git diff --check` clean. Gates were run against the committed tree. `CARGO_BIN_EXE_pmacs`, and on a missing binary it `eprintln!`s a skip and
**returns `ok`**. A fresh worktree running
`cargo test --features crdt --test vterm_stage3_acceptance` reports **9/9
in 0.17 s having never run it**; a real run takes ~4 s. Only
`PMACS_REQUIRE_GPU=1` promotes that skip to a failure, and `CLAUDE.md`
applies that flag to `cargo test -p pmacs-gpu` — a **different package**,
so the required local gate does not cover a37 either. The `gpu-render`
job already sets the flag, which is what makes fix-shape part 2 sound;
state it as a **requirement** of that job rather than inheriting it by
luck, because a `crdt` leg added to the plain `test` job would run a37
vacuously.
- **`a37` is also load-sensitive, which changes how to read the expected
first-run failures.** It passed at `d152120` and failed at that *same
commit* twenty minutes later, with a second agent saturating the machine
with `rustc` in between; it then failed identically on `d152120`,
`04c5ad1`, and the #173 merge commit, which is how #173 established the
failure was not its own. The signature is `last_frame_text` all spaces
with `rendered_nonuniform_frames` nonzero — frames arrive, content does
not. `pmacs-gpu`'s own suite flaked the same way under the same load
(201/202, then 202/202 on immediate rerun). **So a red a37 on the first
CI run is ambiguous by construction**: before treating it as a real
failure, run the same command on the merge base, and prefer serialized
execution for this suite over retry-until-green.
- **Sort deliberate from accidental before proposing a fix.** Some of the 264
are perf suites that are `#[ignore]`d by default and belong to their own
jobs (`m10_2_perf` 6, `m10_11_perf` 1). `m10_10_perf` has **no** `#[ignore]`
and no CI job naming it, so it looks accidental. This classification is not
finished and is the lane's first task.
- **Fix shape, two parts** (the flag combination is verified to work:
`--no-default-features --features luajit,crdt` lists 10 vterm Stage 1 tests
versus 9 without):
1. a `crdt` leg on the `test` job for the non-GPU suites and the library;
2. the GPU-requiring `crdt` suites onto the existing `gpu-render` job, which
already has lavapipe and `PMACS_REQUIRE_GPU=1`
`vterm_stage3_acceptance`, `gpu_invocation_acceptance`,
`gpu_initial_target_acceptance`, `gpu_font_acceptance`.
- **Expect first-run failures, and budget for them.** These would execute in
CI for the first time ever: real PTY timing on CI runners, wgpu under
lavapipe, and daemon-socket tests at unfamiliar concurrency. Start
ubuntu-only and decide about macOS from evidence. A red first run is the
lane working, not the lane failing.
- Mitigating fact, verified rather than assumed: #166's three unit pins are
**not** `crdt`-gated and do run under CI's exact flags, including the
controller-release pin whose only job is catching the plausible wrong fix.
## Terminal config + copy mode arc — Stage 1 IN REVIEW ## Terminal config + copy mode arc — Stage 1 MERGED; Stage 2 is next
- Approved framing: `docs/terminal-config-and-copy-mode-framing.md` - Approved framing: `docs/terminal-config-and-copy-mode-framing.md`
**revision 4** (four review rounds), committed as the first commit of **revision 4** (four review rounds), committed as the first commit of
Stage 1's branch. Two stages, two branches, two PRs; **no protocol Stage 1's branch. Two stages, two branches, two PRs; **no protocol
change**. change**.
- **Stage 1 = `githubsucks/terminal-config`**, worktree - **Stage 1 MERGED as #173** (`main` @ `cf54270`, 2026-07-26, one review
`../pmacs-terminal-config`, based on `githubsucks/main` @ `d152120` round, all twelve checks green). Branch `githubsucks/terminal-config`
and merged up to `c93f9ee` during review round 1. Profiles, and worktree `../pmacs-terminal-config` retained. Profiles, scrollback,
scrollback, escape key, and the `C-c t` opening binding. a per-terminal configurable escape key, and the `C-c t` opening
binding; no protocol change. Main was integrated **twice** during the
single review round (`ccf29e3`, then `c93f9ee` after the first merge
left the PR conflicting) — see the no-CI-while-conflicting fact below.
- **Stage 2 = `terminal-copy-mode`, not started.** Branch it off `main` - **Stage 2 = `terminal-copy-mode`, not started.** Branch it off `main`
after Stage 1 merges: no dependency, but both edit after Stage 1 merges: no dependency, but both edit
`builtin/runtime/terminal.lua`. `builtin/runtime/terminal.lua`.
@ -604,22 +651,34 @@ If it does not, stop and repair the remote/fetch configuration.
- `pmacs-gpu` itself failed 201/202 once under the same load and passed - `pmacs-gpu` itself failed 201/202 once under the same load and passed
202/202 on immediate rerun. 202/202 on immediate rerun.
## Bottom-panel lane (Arc 7) — Stage 1 + framing MERGED; Stage 2A IN REVIEW ## Bottom-panel lane (Arc 7) — Stages 1, 2A + framing MERGED; 2B is next
Stage 1 and the Stage 2 framing are on `main`. **Stage 2A is Stage 1, the Stage 2 framing, and Stage 2A are all on `main`. **Stage 2B
implemented and in review.** has not started.**
- **Stage 2A — portable branch `githubsucks/bottom-panel-stage2a`**, - **Stage 2A MERGED as #177** (`main` @ `0a3fcd1`, 2026-07-26, all twelve
worktree `../pmacs-bp-stage2a`, **canonical `main` @ `cf54270` checks green at `8424172`, three review rounds). Branch
integrated** (review round 1, finding 4 — the terminal-config lane `githubsucks/bottom-panel-stage2a` and worktree `../pmacs-bp-stage2a`
#173 also changes `src/editor.rs`, so gates were rerun on the merge are retained and carry nothing unmerged. Five commits: the classified
result, not the old combination). Five commits: the classified census census routing, the painter extraction + acceptance, the lane record,
routing, the painter extraction + acceptance, the lane record, then then the round-1, round-2 and round-3 review fixes. **No protocol
the round-1, round-2 and round-3 review fixes. **No protocol change; no behavior change; no behavior change for any frontend today** — with
change for any frontend today** — with `panel_capable = false` for `panel_capable = false` for semantic sessions,
semantic sessions, `primary_document_window` returns `view.active` `primary_document_window` returns `view.active` in every existing
in every existing configuration, so this is seam adoption that configuration, so this is seam adoption that becomes load-bearing in
becomes load-bearing in 2B. 2B.
- **Stage 2B is approved and unstarted.** It branches from `main`, **not
stacked on 2A**, per the framing §9. Scope: protocol v21, the daemon
panel projection, the GPU band, and the negotiated `panel_capable`
flip. `docs/bottom-panel-stage2-framing.md` §7.2 carries its five
acceptance criteria (A2B-1..5) plus the reassertion of parent
criterion 52, and §8 records **no open items**, so 2B needs no further
framing round. Its sharpest trap is §5.3's three-boundary split: the
GPU `text_area_bottom` is `status_band_top`,
`geometry_capacity_bottom` and `document_text_bottom` at once, and a
blanket rewrite moves the status chrome along with the document while
still satisfying an "everything moved" assertion — hence A2B-4's
contrast form.
- Verification on the merge result: `cargo fmt --check` clean; strict - Verification on the merge result: `cargo fmt --check` clean; strict
workspace Clippy clean; **1,832 default + 2,015 CRDT** library tests; workspace Clippy clean; **1,832 default + 2,015 CRDT** library tests;
`bottom_panel_stage2a_acceptance` **17**; bottom-panel Stage 1 46; `bottom_panel_stage2a_acceptance` **17**; bottom-panel Stage 1 46;
@ -778,6 +837,23 @@ git worktree add --track \
## Closed since the last snapshot ## Closed since the last snapshot
- **GPU terminal input (the double terminal-layout sync) — MERGED as #166**
(`main` @ `b889873`, 2026-07-25, one review round, all twelve checks green
after a macOS PTY-timing rerun). The dispatcher applied **both**
terminal-layout syncs to **every** attached frontend; a semantic session
satisfies both conditions, so its PTY was resized twice per tick forever and
the child took a `SIGWINCH` storm that made a GPU terminal untypable while
output still flowed. `sync_terminal_layout` is now split into a
frontend-kind-neutral half (panel reconcile + controller liveness) and a
grid-only geometry half, with the loop body extracted to
`sync_terminal_layouts_for_tick` so the exclusivity is structural. No
protocol change (v20). Durable lessons are in `docs/agent-handoff.md` §5;
the framing (`docs/gpu-terminal-input-framing.md` rev 2) carries three
falsified hypotheses, the two-pre-image bite matrix, and two named
out-of-scope items (Q#GT5 interactive-shell echo on a raw PTY, which
reproduces in-process and so is not the GUI/TUI asymmetry; and a geometry
change appearing to clear the visible screen, which reproduces pre-fix).
Branch `gpu-terminal-input` and worktree `../pmacs-gui-term-input` retained.
- **Inline-math slice — MERGED as #158** (`main` @ `5aa9044`, - **Inline-math slice — MERGED as #158** (`main` @ `5aa9044`,
2026-07-25). Detect → parse → layout → draw for `$…$`, entirely inside 2026-07-25). Detect → parse → layout → draw for `$…$`, entirely inside
`pmacs-gpu`, no protocol change. Verified by the user's manual pass on `pmacs-gpu`, no protocol change. Verified by the user's manual pass on

View File

@ -1,9 +1,18 @@
# Agent handoff — cross-machine continuity # Agent handoff — cross-machine continuity
**Last updated: 2026-07-26, after Lean 4 stages 3a and 3b (#167, #170) **Last updated: 2026-07-26, after Lean 4 Stage 4a (#179) — the typed-edit
landed — pmacs' first Lean language server — following the inline-math consumer chain — and bottom-panel Stage 2A (#177), the classified census
slice (#158), the first mathematical typesetting in pmacs, and find-file (#162), routing that makes every Projection-class consumer ask
the dired arc's Stage 0, and COHERENCE.md (#163), Lean 4 Stage 1 (#160), the `primary_document_window`; following the bottom-panel Stage 2 framing
(#175), terminal configuration Stage 1 (#173) — profiles, scrollback, a
per-terminal configurable escape key, and the `C-c t` opening binding —
Lean 4 stages 3a and 3b (#167, #170), pmacs' first Lean language server;
the GPU terminal input fix (#166), the double terminal-layout sync that
made a GPU terminal untypable; the CRDT undo repro (#157), the
inline-math landed-doc refresh (#172), the inline-math slice (#158), the
first mathematical typesetting in pmacs; dired Stage 1 (#165), Lean 4
Stage 2 (#161), the dired framing pair (#163/#164), find-file (#162) —
the dired arc's Stage 0 — COHERENCE.md (#163), Lean 4 Stage 1 (#160), the
minimap blank-slab fix (#159), bottom-panel Stage 1 (#155), the minimap blank-slab fix (#159), bottom-panel Stage 1 (#155), the
inline-math re-scout (#154), the vterm PTY-flake fix (#153), and the inline-math re-scout (#154), the vterm PTY-flake fix (#153), and the
GPU initial-target doc refresh (#152); and before that GPU GPU initial-target doc refresh (#152); and before that GPU
@ -28,13 +37,17 @@ commands, read `docs/active-work.md` immediately after this file.
## 1. Where the project stands (2026-07-26) ## 1. Where the project stands (2026-07-26)
- `main` @ `d400f30` (Lean 4 Stage 3b #170 atop Stage 3a #167, the - `main` @ `a27f646` (Lean 4 Stage 4a #179 atop bottom-panel Stage 2A
bottom-panel landed-doc refresh #156, the inline-math slice #158, #177, the bottom-panel Stage 2 framing #175, terminal configuration
dired Stage 1 #165, the GPU terminal input fix #166, Lean 4 Stage 2 Stage 1 #173, Lean 4 Stage 3b #170, Stage 3a #167, the CRDT undo repro
#161, the dired framing #164, COHERENCE.md #163, find-file #162, Lean #157, the inline-math landed-doc refresh #172, the bottom-panel
4 Stage 1 #160, minimap blank-slab #159, bottom-panel Stage 1 #155). landed-doc refresh #156, the inline-math slice #158, dired Stage 1
Protocol unchanged at **v20**. The bullets below describe the arcs in #165, the GPU terminal input fix #166, Lean 4 Stage 2 #161, the dired
their own terms; this line is the head-of-`main` anchor. framing #164, COHERENCE.md #163, find-file #162, Lean 4 Stage 1 #160,
minimap blank-slab #159, bottom-panel Stage 1 #155). Protocol unchanged
at **v20** — bottom-panel Stage 2A deliberately carries no wire change;
v21 arrives with Stage 2B. The bullets below describe the arcs in their
own terms; this line is the head-of-`main` anchor.
- **`COHERENCE.md` is now required reading and a required framing input - **`COHERENCE.md` is now required reading and a required framing input
#163.** It carries the product-coherence thesis, an audited #163.** It carries the product-coherence thesis, an audited
scorecard, per-concern gaps, and §20's priority order, and it is the scorecard, per-concern gaps, and §20's priority order, and it is the
@ -758,6 +771,44 @@ commands, read `docs/active-work.md` immediately after this file.
DAP, 8 GPU splits, plus the `.ipynb` arc (its JSON-grammar DAP, 8 GPU splits, plus the `.ipynb` arc (its JSON-grammar
prerequisite shipped in #123). prerequisite shipped in #123).
- **GPU terminal input LANDED — #166** (`main` @ `b889873`;
`docs/gpu-terminal-input-framing.md` rev 2; one review round). The
dispatcher applied **both** terminal-layout syncs to **every** attached
frontend each tick. A semantic session satisfies both conditions — a
`term_sizes` entry from `AttachRequest` *and* a terminal declaration — so
its PTY was resized twice per tick forever: the grid arm installed the TUI
placement size, the semantic arm the declared content rectangle, each arm's
`old_size == size` guard seeing only what the other had just written. The
child took a `SIGWINCH` storm at tick cadence, which made typing into a GPU
terminal impossible while output kept flowing. TUI was structurally
unaffected.
- `EditorInstance::sync_terminal_layout` is split into
`sync_terminal_controller_liveness` (frontend-kind **neutral**: panel
reconcile + release of a controller whose window moved away — reads only
views/windows/controller, never a grid size) and
`sync_terminal_grid_geometry` (**grid only**: TUI placement + resize).
`sync_terminal_layout` survives as the composition, so `editor::run` and
`LOCAL` are byte-identical.
- `daemon::sync_terminal_layouts_for_tick` is the extracted loop body:
liveness for every frontend once per tick, then **exactly one** geometry
arm keyed on `semantic_states` membership — the same fact session
establishment uses, so the arms cannot both fire.
- **The trap, kept in a comment:** the release on a missing
`window_placements` entry reads like liveness and is grid geometry. A
semantic frontend has no placement entry at all, so moving it into the
neutral half would release a GPU controller every tick.
- Why not the one-line guard: the grid arm was also the **only** per-tick
controller-liveness release a semantic frontend got, and
`sync_semantic_terminal_layout` cannot take it over — the buffer-follow
snapshot clears the viewport declaration, so that arm stops running in
exactly the switch-away case that needs the release.
- No protocol change (v20). Gates: 1,829 default + 2,006 CRDT library
tests; vterm Stage 1/2/3 10/6/9 CRDT; bottom-panel 46; M4 121; required
GPU 155; isolated-config workspace sweep 3,177 across 92 suites.
- **Known gap, its own lane:** CI never enables `crdt`, so the Stage 3
real-path acceptance (including `a37`) is not compiled there. #166's unit
pins are not `crdt`-gated and do run. See `docs/active-work.md`.
## 2. How we work (the part that must not drift) ## 2. How we work (the part that must not drift)
The user is expert and reviews deeply — they falsify framings and find The user is expert and reviews deeply — they falsify framings and find
@ -884,6 +935,34 @@ final variant — its own round-trip cannot detect a discriminant shift.
## 5. Hard-won ops lessons ## 5. Hard-won ops lessons
- **A test that skips on a missing precondition reports `ok`, and a gate log
cannot tell that apart from a pass.** `vterm_stage3_acceptance::a37` — the
only acceptance driving a real daemon, a real PTY and a real wgpu render
together — derives `pmacs-gpu` from `CARGO_BIN_EXE_pmacs` and, when that
binary is absent from the target directory, prints a skip and returns.
A fresh worktree reports the suite 9/9 **in 0.17 s having never run it**;
a real run takes ~4 s. `PMACS_REQUIRE_GPU=1` is what promotes the skip to
a failure, and the standing gate list applies that flag to
`cargo test -p pmacs-gpu`, a *different package*. Two habits follow:
build the workspace before believing any suite that reaches for a sibling
binary, and **judge such a suite by its elapsed time**, not its verdict.
- **Before attributing a red test to your branch, run it on the merge base.**
`a37` failed on the #173 branch, which looked like a regression; it failed
identically on the PR's own base and on two intermediate commits, and had
*passed* on that same base twenty minutes earlier. The variable was machine
load from a second agent compiling continuously. Load-sensitive tests make
both verdicts uninformative in isolation, so the base-commit run is the
cheapest way to tell a regression from weather — and it is much cheaper
than the bisect it replaces.
- **A daemon-side fix is not deployed until the daemon is restarted from a
tree that contains it.** #166's reporter rebuilt and saw no change: the
running daemon had been started from a shared checkout still on a pre-fix
branch, and `pmacs --gpu` attaches to whatever process already owns the
socket. Rebuilding a binary does nothing to a running process. When
validating a daemon-side fix by hand, check the running process's binary
path and start time against the tree you think you fixed —
`ps -eo pid,lstart,args | grep '[p]macs --daemon'` — before concluding the
fix failed.
- **Two operations that must be alternatives are not made alternatives by - **Two operations that must be alternatives are not made alternatives by
being adjacent.** The dispatcher applied its grid and semantic being adjacent.** The dispatcher applied its grid and semantic
terminal-layout syncs to every attached frontend; a semantic session terminal-layout syncs to every attached frontend; a semantic session

View File

@ -81,7 +81,11 @@ character:
| | frames for a static screen | typed `Z` ever visible at the prompt | | | frames for a static screen | typed `Z` ever visible at the prompt |
|---|---|---| |---|---|---|
| `main` today | **730** in a 20 s window | **no** | | `main` today | **730** in a 20 s window | **no** |
| with the guard | **2** | (see Q#GT5 — a separate question) | | with the fix | **2** | yes |
Bet B2 is **scored TRUE**: with the fix deployed, the reporter confirmed
typing into a GPU terminal works. The earlier caveat here pointed at Q#GT5,
which is now retracted — see "Deferred (named)".
The TUI is unaffected: a grid session has no semantic terminal declaration, so The TUI is unaffected: a grid session has no semantic terminal declaration, so
only one arm ever runs for it. This is a **frontend-kind** defect, which is only one arm ever runs for it. This is a **frontend-kind** defect, which is
@ -281,11 +285,12 @@ change. Stays v20.
snapshot that signals the switch-away). Hence the split in Q#GT1. Recorded snapshot that signals the switch-away). Hence the split in Q#GT1. Recorded
rather than deleted: the failure mode is one a reviewer or a future rather than deleted: the failure mode is one a reviewer or a future
simplification will re-propose. simplification will re-propose.
- **B2.** The user's reported symptom is this defect. *Partially scored: the - **B2 — SCORED TRUE 2026-07-25.** "The user's reported symptom is this
storm is proven and GUI-only, and its shape (line editor unusable, output defect." Confirmed in real use after the fix was deployed: typing into a GPU
still flowing) matches the report. Not fully scored until the user, or an terminal works. The confirmation needed a daemon **restart** built from a
acceptance running the **user's own shell**, confirms typing works after the tree containing the fix — the first attempt reported no change because a
fix. Q#GT5 is the reason this bet is stated rather than assumed.* pre-fix daemon still owned the socket, which is worth remembering whenever a
daemon-side fix is being validated by hand.
- **B3.** No other pair of per-frontend-kind daemon operations is applied as - **B3.** No other pair of per-frontend-kind daemon operations is applied as
siblings rather than alternatives. *Scored by an explicit audit of the siblings rather than alternatives. *Scored by an explicit audit of the
dispatcher's per-frontend loop during implementation — this defect's shape dispatcher's per-frontend loop during implementation — this defect's shape
@ -294,7 +299,14 @@ change. Stays v20.
## Deferred (named) ## Deferred (named)
- Interactive-shell echo on a raw-mode PTY (Q#GT5) — its own scout. - ~~Interactive-shell echo on a raw-mode PTY (Q#GT5)~~ — **RETRACTED
2026-07-25.** The observation behind it (a `bash --norc -i` fixture not
echoing typed characters) does not reproduce in real use: with the fix
deployed, typing into a GPU terminal echoes normally. The fixture was almost
certainly measuring its own timing — polling a published screen snapshot
before readline had finished initialising — not a product behaviour. Recorded
as retracted rather than deleted so nobody re-derives it from the framing's
earlier revision and spends a scout on it.
- **A geometry change appears to clear the visible screen.** Observed while - **A geometry change appears to clear the visible screen.** Observed while
building acceptance 4: after the probe's deliberate 25×92 → 20×71 resize, building acceptance 4: after the probe's deliberate 25×92 → 20×71 resize,
the next frame's visible grid is entirely blank even though the content the next frame's visible grid is entirely blank even though the content

View File

@ -1674,6 +1674,64 @@ GPU assertions remain in `pmacs-protocol` and `pmacs-gpu` respectively.
- **37:** one real-daemon/real-PTY/headless-wgpu acceptance path; it is not - **37:** one real-daemon/real-PTY/headless-wgpu acceptance path; it is not
replaced by a decoded-message fixture. replaced by a decoded-message fixture.
### 0.12 As-framed audit, 2026-07-25 (after #166)
Prompted by a GPU terminal input defect that shipped in Stage 3 and was fixed
in #166. The arc is structurally complete — all 37 criteria have
implementations, and every test named in the Stage 2 verification map exists —
but the audit found two gaps worth recording against the criteria themselves.
**Criterion 22's "without thrash" was never pinned.** The criterion reads
"unchanged, zero, passive, and failed resize cases preserve prior geometry
*without thrash*". The word appears nowhere in `src/` or `tests/`. The suite
pinned the four enumerated single-arm cases and never the cross-arm
interaction — which is exactly where the thrash lived: the daemon applied
both the grid and the semantic terminal-layout sync to every attached
frontend, so a semantic session's PTY was resized twice per tick forever.
Criterion 31's "only the exact durable controller changes PTY geometry" was
violated in the same event, in spirit rather than letter: the controller was
the right frontend, but the geometry came from the grid projection. #166 adds
the settle pins; the gap was open from #135 (2026-07-22) until then.
**Why the Stage 3 suite could not see it.** Of its nine tests, only three
drive a real daemon; the other six construct `EditorState` directly and never
execute the dispatcher loop where the defect lived. `a31`, which is about two
semantic frontends sharing one session, therefore passes on the broken tree.
The same structural blindness explains why `bottom_panel_stage1_acceptance`
was unaffected. A criterion about *dispatcher* behavior needs a test that
runs the dispatcher.
**Four of the nine Stage 3 tests do not run in CI at all**, because they are
`#[cfg(feature = "crdt")]` and the workflow never enables that feature:
`a37`, the two added by #166, and
`terminal_mode_keeps_reporting_presence_so_peers_drop_the_stale_caret` — which
is Stage 3 review round 1's own regression guard. Stage 1's
`read_only_empty_crdt_bootstrap_is_immutable_against_remote_content`, the CRDT
half of criterion 14, is dark for the same reason. Stage 2 is fully covered
(6/6). This is not a vterm problem: 264 tests workspace-wide are dark,
including 177 in the library. It has its own lane in `docs/active-work.md`.
**And `a37` is darker still than that count implies: it reports `ok` without
running whenever `pmacs-gpu` is absent from the same target directory**
(measured 2026-07-26 while gating #173). It derives the sibling binary from
`CARGO_BIN_EXE_pmacs` and, finding nothing, prints a skip notice and returns.
A fresh worktree reports the suite 9/9 in 0.17 s having executed the arc's
only real-daemon/real-PTY/real-wgpu path zero times; a genuine run takes
about four seconds. `PMACS_REQUIRE_GPU=1` is the only thing that turns that
skip into a failure, and the standing gate list applies that flag to
`cargo test -p pmacs-gpu`, a different package. So the audit's claim that
"only 3 of 9 Stage 3 tests drive a real daemon" was itself optimistic —
**on a target directory without the frontend binary the honest number is 2**,
and nothing in the gate log says so. It is also load-sensitive: it passed and
then failed at the same commit twenty minutes apart under machine
contention. Criterion 22's unpinned "without thrash" and this are the arc's
two standing verification gaps.
**Not audited:** §11's blanket claim that "deferral means graceful ignore or
documented absence, never escape leakage, panic, unbounded allocation, or
child leak". That covers roughly twenty deferred items and none were
spot-checked. It remains an unproven claim rather than a known gap.
## 10. Gates and bite verification ## 10. Gates and bite verification
Every PR runs the standing full gates from `AGENTS.md`, sequentially, plus its Every PR runs the standing full gates from `AGENTS.md`, sequentially, plus its