From 480158b460eb2c2fd7c528a8c2ca0b1189caf125 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Mon, 3 Aug 2026 11:18:04 -0400 Subject: [PATCH] docs: record Arc 7 complete, and what Stage 3 taught MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Branch-plan step 6. The lane, the handoff, COHERENCE, and the framing all still described Stage 3 as ahead. COHERENCE §14's scorecard row and §20's Priority 5 both said the adopter default flip was what remained on the panel. It is done, so the panel half of P5 is closed and the TREE PRIMITIVE becomes the arc's successor — §14 grades Tree ✗, and DAP's variables view is its next would-be inventor. That sequencing is now stated where the next framing doc will read it rather than living in a conversation. The lane is RETAINED, not removed, because the PR has not landed; rule 4 removes it once Stage 3 merges and its facts are in the handoff. Both conditions are half-met, and recording that honestly is cheaper than discovering later which half. Five durable facts move to the handoff §1, each of which cost a review round or a defect to learn: * A VISIT FROM A PANEL MUST NEVER USE THE RAW SWITCH. `pmacs.window.switch_buffer` replaces the buffer in the ACTIVE window, so from a panel it clobbers the panel itself. Q#BP11c is the contract: after RET, `M-,` must FOCUS the still-present panel rather than clone its buffer into the document — and an assertion on the active buffer NAME alone cannot distinguish those two outcomes. * AN OPT-OUT THAT DOES NOT SURVIVE REPLAY IS NOT AN OPT-OUT. Anything that replays a stored invocation must store the escape hatch with it, or the escape hatch is a one-shot that silently reverts. * Compile's chords are PANEL-LOCAL, deliberately, and `M-x compile.kill` still reaches the slot from anywhere. A global chord is a command-surface decision framed separately. * Two `q` mechanisms coexist by design — presentation history chains in the side slot; `p.prev` prevents raw-switch and capability-fallback loops. Neither supersedes the other. * A CAPABILITY FALLBACK MUST STRIP THE QUIT ACTION TOO, not just the side parameters. A quit action stranded on a document window makes a later `q` try to restore a presentation that never happened. The framing reaches revision 4 with the three defects the flip exposed (§1.7a–c) and the census lesson (§1.6d): it counted FAILURES, not CAUSES, and the two differed by an order of magnitude — thirteen listview failures were one missing frame-geometry declaration. Read a census as "how many assertions move", never "how many decisions are required". Co-Authored-By: Claude Opus 5 (1M context) --- COHERENCE.md | 9 +- docs/active-work.md | 127 ++++++++++++++-------------- docs/agent-handoff.md | 37 +++++++- docs/bottom-panel-stage3-framing.md | 81 +++++++++++++++++- 4 files changed, 181 insertions(+), 73 deletions(-) diff --git a/COHERENCE.md b/COHERENCE.md index c447a76..0efa19f 100644 --- a/COHERENCE.md +++ b/COHERENCE.md @@ -107,7 +107,7 @@ remain open to them. | 11 | Config layering + provenance | **Partial (foundation only)** | Typed registry is right; 5 settings live in it; no value provenance | | 12 | Profiles | **Missing** | One hardcoded default keymap; not a named concept | | 13 | Package lifecycle UX | **Resolution without lifecycle** | Mature resolver/lockfile; init-only install; no uninstall/disable/search | -| 14 | Workbench primitives | **Partial (best trajectory)** | Listview is a real primitive but only 3 call sites, all LSP panels; buffer-list and search re-implement it; bottom panel complete on BOTH frontends (#155 + Stage 2) | +| 14 | Workbench primitives | **Partial (best trajectory)** | Listview is a real primitive but only 3 call sites, all LSP panels; buffer-list and search re-implement it; **the bottom panel is COMPLETE — both frontends, and Stage 3 flipped the adopter default so omission means the panel**. **Tree is still ✗ and is now the arc's successor** | | 15 | Contextual affordances | **Weak** | Right-click menu only; code actions apply first-blindly; no git integration at all | | 16 | Semantic frontend | **Strong** | v6..=v21 schema support; production attach remains v20 during the dark panel slice; degradation practiced | | 17 | Distribution | **Partial** | **v1.1.0 ships prebuilt Linux/macOS binaries on tag** (#211) with checksums and a stated glibc floor. No channels, in-place update, rollback, signing, or package-manager distribution | @@ -1667,10 +1667,9 @@ and §18's floor ride on this. ### Priority 5: Finish the workbench convergence -**State: partial and moving (§14) — the bottom panel is now complete on -both frontends (Stage 1 #155 through Stage 2B-3), and listview is -proven.** Only the adopter default flip (Stage 3) remains on the panel -itself. Remaining elsewhere: the tree primitive (build it before dired +**State: the bottom panel is DONE (§14) — both frontends, Stage 1 #155 +through Stage 2B-3, and Stage 3 flipped the adopter default so omitting +`display` means the panel. Arc 7 is complete.** Remaining elsewhere: the tree primitive (build it before dired and the worker tree invent two), table/inspector/diff, help unification. Wiring plus one modest model piece (the tree model). diff --git a/docs/active-work.md b/docs/active-work.md index a3f2f83..f11e10e 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -311,82 +311,81 @@ them) and why `dired`/`listview` were the correct first two families. Whichever starts second integrates first. -## Bottom-panel lane (Arc 7) — STAGE 3 IN PROGRESS on `bottom-panel-stage3` +## Bottom-panel lane (Arc 7) — STAGE 3 COMPLETE; ARC DONE, pending PR -**Stage 1, the Stage 2 framing, and Stages 2A, 2B-1, 2B-2 and 2B-3 are -all on `main`** (#155, #175, #177, #184, #187, #198). Stage 2 is -complete. Durable facts are in `docs/agent-handoff.md` §1, including the -v20-baseline / v21-negotiated handshake that Stage 2B-3 made compatible. +**Arc 7 is finished.** Stage 1 (#155), the Stage 2 framing (#175), +Stages 2A (#177), 2B-1 (#184), 2B-2 (#187) and 2B-3 (#198) are on +`main`; **Stage 3 — the adopter default flip — is implemented on +`bottom-panel-stage3`** and is the arc's last step. Framing +`docs/bottom-panel-stage3-framing.md` revision 3, approved with +amendments after three review rounds. -**Stage 3 — the adopter default flip — is Arc 7's last step and is NOW -IN PROGRESS**, not "not started" as this lane said until 2026-08-01. -Framing `docs/bottom-panel-stage3-framing.md` revision 3, approved with -amendments after one review round. +**This lane is removed once Stage 3 merges and its facts are in +`docs/agent-handoff.md` §1** (rule 4). It is retained now only because +the PR has not landed. -- **Branch `bottom-panel-stage3`**, based on `githubsucks/main` @ - `21de0b2`. Developed in the primary checkout on the laptop, not a - worktree. **No PR yet** — the flip has not landed. -- **Steps 1 and 2 of the framing's §7 are DONE:** - - `0224c68` — the **fallout census**, taken before any change: a - throwaway flip, a full `--no-fail-fast` sweep, then revert. **37 - failures across 5 suites**, classified per test in framing §1.6c. - - `41d37fc` — the **shared resolver** - (`resolve_adopter_display` / `pmacs.window._resolve_display`), - replacing four hand-written copies of the same validation. - **Default-preserving with one intentional normalization** (below). -- **Steps 3–6 are NOT done:** the flip itself, the ~37 test revisions, - the capability-fallback criterion, and the lane/handoff close-out. +- **Branch `bottom-panel-stage3`** off `githubsucks/main` @ `21de0b2`, + pushed, upstream tracking set. Six commits: `fa12095` framing, + `0224c68` census, `41d37fc` shared resolver, `8d14e6a` lane/portability, + `c0eb16b` the flip + test revisions, `5f01ede` the fallback pin. +- **Verification:** full serialized sweep **3449 passed / 0 failed** + against a 3447 baseline (+2 new pins); fmt, diff-check, clippy with and + without `crdt`, `--lib` 1896, `--lib --features crdt` 2081, + pmacs-protocol 19, m4 149, required GPU 221, and + `bottom_panel_stage1_acceptance` 47/47 under both Lua flavors. -### What the census established, and why it ran first +### What Stage 3 changed -- **A census that stops at the first failing binary is not a census.** - The first sweep reported 2 failures in 1 suite because `cargo test` - halts after a failing binary; `--no-fail-fast` revealed 37 across 5. - **Any re-measurement must pass that flag.** -- **`m4_acceptance` is a transitive adopter nobody named.** Its two - failures are the LSP hover and outline panels, which are **listview - consumers**. Q#BP12's four rows are the *direct* population; the real - one is everything built on listview. -- **The proportions invert the obvious reading.** `compile_mode` has the - most failures (17) and the least placement content; `listview_ - acceptance` has 13 but loses **three quarters of its suite**. +Omitting `display` resolves to the **panel** for listview, compile and +terminal. **Dired keeps `"current"`** — `pmacs.path.directory_handler` +calls it with no `display`, so a flipped default would open `pmacs .` in +a bottom panel. Per-adopter `select`: listview `true`, compile `false`, +terminal `true`. -### The normalization step 2 changed, deliberately +Four hand-written copies of the `display` validator collapsed into one +shared `resolve_adopter_display(operation, raw, default)`; the default is +a **parameter**, which is what makes dired's exemption visible at its +call site rather than hidden in a divergent copy. -Terminal previously raised **mlua's type error** for a non-string -`display`; the Lua adopters stringified into their own message. Now all -four take the shared custom error, rendered **by type alone** -(`unknown display (integer)`), because it names the legal vocabulary. +### Three defects the flip exposed, each fixed rather than tested around -**Pinned** in `bottom_panel_stage1_acceptance::acc19` at the terminal -entry point, asserting the error *and* that nothing is created. The type -**spelling** is deliberately unpinned — Lua 5.4 says `integer` where -LuaJIT has no integer subtype, so a literal assertion would pass on one -CI flavor and fail on the other. **Verified 46/46 under both.** +- **The outline panel's `on_visit` used the RAW switch** + (`pmacs.window.switch_buffer`), which replaces the buffer in the ACTIVE + window. Harmless while the outline opened into a document window; + once the panel became the default, RET **clobbered the panel with the + source**. The references panel was migrated to `display_file` when the + arc landed — the outline was missed because nothing exercised it from a + panel until the flip. Q#BP11c names this exact corruption. +- **`compile._last` stored only `{cmdline, cwd}`**, so `g` reached + `start_run` with no `display` and took the new default: an explicit + `display = "current"` silently reverted on the next recompile. **An + opt-out that reverts is not an opt-out.** +- **`opts.display` on a nil `opts`** — a regression introduced by the + fix above and caught by `journey_acceptance`, which is what that + ratchet is for. -### Verification at this head +### Two contracts now pinned, not merely observed -Full serialized suite **3447 passed / 0 failed**, with **zero suites -differing from the pre-change baseline** — the measurement that makes -"default-preserving" a claim rather than an intention. Plus fmt, -diff-check, clippy, `--lib` 1896, and -`bottom_panel_stage1_acceptance` 46/46 under **both** Lua flavors. +- **Compile's chords are PANEL-LOCAL.** All are bound + `scope = "buffer"`, so with `select = false` none dispatch from the + document — `C-c C-k` included. `M-x compile.kill` still reaches the + running slot anywhere via its `or compile_slot()` fallback. A global + chord is a command-surface decision and belongs in its own framing. +- **The two `q` mechanisms are complementary, not competing.** + Presentation history chains in the side slot (`C → B → A → delete`, + Q#BP2c); `p.prev` prevents raw-switch and capability-fallback listview + loops. `s1_12` pins the second with explicit `display = "current"`; + the new `s3_1` pins the first. -### Recovery from a clean checkout +### The census lesson worth carrying past this arc -```sh -git fetch githubsucks -git worktree add ../pmacs-bp-stage3 \ - -b bottom-panel-stage3 \ - githubsucks/bottom-panel-stage3 -``` - -- **DAP waits for Stage 2, not Stage 1** — satisfied. Q#BP12's adopter - table already carries a `DAP stack/variables` row, so Stage 3 settles - the debugger's panel policy before the debugger exists. -- **The tree primitive is the next thing to scope after this**, per - `COHERENCE.md` §14: Tree is graded ✗, and DAP's variables view is its - next would-be inventor. +**It counted failures, not causes.** Thirteen listview failures had ONE +root cause — a panel is derived-hidden while frame geometry is unknown, +and that suite never declared any because it never needed to. The same +applied to `m4_acceptance` and `vterm_stage2_acceptance`. And +**`--no-fail-fast` is mandatory**: the first sweep reported 2 failures in +1 suite because `cargo test` halts after a failing binary; the real +figure was 37 across 5. ## Reap-ledger silent failures — MERGED (#202); kept for its parked follow-ons diff --git a/docs/agent-handoff.md b/docs/agent-handoff.md index 77c7642..6b633aa 100644 --- a/docs/agent-handoff.md +++ b/docs/agent-handoff.md @@ -77,6 +77,41 @@ commands, read `docs/active-work.md` immediately after this file. #204 and **1b-3** #205, the ambient-root isolation **implementation** #206, and discovery Stage 1 #207. Each has its own bullet below; this line is the head-of-`main` anchor and nothing else. +- **Bottom panel Arc 7 COMPLETE — Stage 3, the adopter default flip.** + Omitting `display` now means the panel for listview, compile and + terminal; **dired keeps `"current"`** because + `pmacs.path.directory_handler` calls it with no `display` and a flipped + default would open `pmacs .` in a bottom panel. Four hand-written + copies of the validator collapsed into one + `resolve_adopter_display(operation, raw, default)` — the default is a + **parameter**, which is what makes dired's exemption visible at its + call site instead of hidden in a divergent copy. Durable facts: + - **A visit FROM a panel must never use the raw switch.** + `pmacs.window.switch_buffer` replaces the buffer in the ACTIVE + window, so from a panel it clobbers the panel itself. The outline's + `on_visit` still did this; the references panel had been migrated to + `display_file` when the arc landed and the outline was missed, + because nothing exercised it from a panel until the default flipped. + **Q#BP11c is the contract**: after RET, `M-,` must FOCUS the + still-present panel, not clone its buffer into the document — and an + assertion on the active buffer name alone cannot tell those apart. + - **An opt-out that does not survive replay is not an opt-out.** + `compile._last` stored `{cmdline, cwd}` only, so `g` re-resolved + `display` and silently reverted an explicit `"current"` to the new + default. Anything that replays a stored invocation must store the + escape hatch with it. + - **Compile's chords are PANEL-LOCAL, deliberately.** All are bound + `scope = "buffer"`, so with `select = false` none dispatch from the + document — `C-c C-k` included. `M-x compile.kill` still works + anywhere via its `or compile_slot()` fallback. A global chord is a + command-surface decision, framed separately. + - **Two `q` mechanisms coexist by design**: presentation history + chains in the side slot (`C → B → A → delete`, Q#BP2c), while + `p.prev` prevents raw-switch and capability-fallback listview loops. + Neither supersedes the other. + - **A capability fallback must strip the QUIT ACTION too**, not just + the side parameters — a quit action stranded on a document window + makes a later `q` try to restore a presentation that never happened. - **pmacs is installable without cloning — Distribution Stage 1, #211, released as v1.1.0.** A `v*` tag builds `pmacs` and `pmacs-gpu` on pinned `ubuntu-22.04` / `macos-15` and publishes a GitHub Release with @@ -181,7 +216,7 @@ anchor, so every item is startable. | 2 | Workspace + location | Missing; model gap | The long-lead arc. Start before a fifth subsystem grows its own root convention — four have already diverged (§7) | | 3 | Extension ownership | Missing; prerequisite-shaped | **`pmacs.hook.remove` does not exist.** That one bug-sized gap blocks §13's disable/uninstall, §10's trust classes, and package-scoped cancellation | | 4 | **Discovery** | **Stage 1 MERGED (#207)** | Stage 2 candidates, in rough dependency order: richer M-x rows (**protocol change** — `MinibufferPrompt.candidates` is `Vec`; `CompletionPopupRow` already proves the pattern), `Command` gaining title/category/aliases/flags/arg-schema (~147 definition sites), predicate evaluation, help-layer unification, and the help-prefix decision | -| 5 | Workbench convergence | Partial, best trajectory | Bottom panel done both frontends; **Stage 3 = flip the adopter default**. Then the tree primitive — build it *before* dired and the worker tree invent two | +| 5 | Workbench convergence | Partial; **Arc 7 COMPLETE** (Stage 3 implemented) | The bottom panel is finished on both frontends and the adopter default is flipped. **The tree primitive is now the arc's successor** — `COHERENCE.md` §14 grades Tree ✗, and DAP's variables view is its next would-be inventor. Build it *before* dired's `i` and the worker tree invent two | | 6 | Config productization | Foundation only | Value provenance, then layering, then adoption migration (**table-valued settings are the hard prerequisite** — `ConfigValue` is four scalars) | | 7 | Package lifecycle | Not started | Correctly sequenced after P3 | | 8 | **Distribution** | **Stage 1 SHIPPED (v1.1.0, #211)** | Binaries on tag, checksums, machine-checked glibc floor. **Journey step 1 now works and the "invisible until this exists" blocker is lifted.** Next is a *decision* about channels / update / signing, not a queued plan | diff --git a/docs/bottom-panel-stage3-framing.md b/docs/bottom-panel-stage3-framing.md index 2002fa0..fa59cf2 100644 --- a/docs/bottom-panel-stage3-framing.md +++ b/docs/bottom-panel-stage3-framing.md @@ -1,8 +1,21 @@ # Framing — Bottom panel Stage 3: the adopter default flip -**Revision 3.** Status: **approved; branch-plan step 1 (the fallout -census) is complete and recorded in §1.6b–c.** Branch -`bottom-panel-stage3`, based on `githubsucks/main` @ `21de0b2`. +**Revision 4.** Status: **IMPLEMENTED — all six branch-plan steps done; +Arc 7 complete.** Branch `bottom-panel-stage3`, based on +`githubsucks/main` @ `21de0b2`. Full sweep 3449 passed / 0 failed +against a 3447 baseline. + +**Revision 3 → 4** records what implementation found. The plan held; the +surprises were all defects the flip exposed rather than design changes: + +- **Three defects, each fixed rather than tested around** — the outline's + raw-switch visit (§1.7a), compile's non-replayed opt-out (§1.7b), and + a nil-`opts` regression of my own that `journey_acceptance` caught. +- **The census counted failures, not causes** (§1.6d): 13 listview + failures had ONE root cause, a missing frame-geometry declaration. +- **Compile's chords are panel-local** (§1.5a), pinned by `acc34`. +- **The two `q` mechanisms are complementary** (§1.7c), pinned by + `s1_12` and the new `s3_1`. **Revision 2 → 3** adds the measurement and its classification. Two findings changed the plan rather than confirming it: the census needs @@ -348,6 +361,68 @@ one is about where output goes and the other is about moving within it. A sweep that classified per *suite* rather than per *test* would have got both wrong. + +### 1.6d The census counted failures, not causes + +The measured 37 was accurate as a count and misleading as a work +estimate. **Thirteen listview failures had one root cause:** a panel is +derived-hidden while frame geometry is unknown, and +`listview_acceptance` never declared any — it never needed to while +listview defaulted to the current window. One helper took it from 13 to +2. `m4_acceptance` and `vterm_stage2_acceptance` were the same. + +Read a census as "how many assertions move", never "how many decisions +are required". The two differed here by an order of magnitude. + +### 1.7a Defect: the outline visited through the RAW switch + +`lsp.lua`'s outline `on_visit` called `pmacs.window.switch_buffer`, +which replaces the buffer in the **active** window. Harmless while the +outline opened into a document window — the switch simply reused it. +Once the panel became the default, the active window WAS the outline +panel, so **RET clobbered the panel with the source file** and left +nothing for `M-,` to return to. + +The references panel (`visit_location`) was migrated to `display_file` +when the arc landed; the outline was missed because **nothing exercised +it from a panel until the default flipped**. Its own neighbouring +comment states the rule it violated: *"a visit FROM a panel must land in +the document target and leave the panel intact."* + +Q#BP11c names the corruption precisely, and it is why both the outline +and compile tests now assert `M-,` **focuses** the still-present panel +rather than cloning its buffer into a document window — the previous +assertion, on the active buffer name alone, could not tell those apart. + +### 1.7b Defect: an opt-out that did not survive replay + +`pmacs.compile._last` stored `{cmdline, cwd}` and no `display`, so `g` +reached `start_run` with the value omitted and took the new default. A +user who ran `compile.run{display="current"}` was moved into a panel the +moment they recompiled. + +**An opt-out that reverts on the next replay is not an opt-out.** +`display` is now stored and replayed, with `nil` kept as `nil` so an +omitted value still resolves to the current default rather than freezing +at the first run's resolution. The general form: *anything that replays +a stored invocation must store the escape hatch alongside it.* + +### 1.7c The two `q` mechanisms are complementary + +Stage 3 made a latent conflict live. `listview.lua`'s `p.prev` captures +the previous buffer only if it is not a panel; `QuitAction::Restore` +(Q#BP2c) deliberately chains `C → B → A → delete`. With two listviews +sharing one bottom slot, the second replaces the first and `q` walks the +restore chain. + +**The restore chain wins**, per criterion 20 — listview `q` routes +through `window.quit` and must not exempt itself from the panel +contract. The mechanisms are then complementary rather than competing: +**presentation history chains in the side slot; `p.prev` prevents +raw-switch and capability-fallback loops.** `s1_12` keeps its Q#GB18 +name-keyed-identity bite by pinning its panels in document windows, +isolating `p.prev`; the new `s3_1` pins the chain. + ## 2. Questions - **Q#S3-1 — DECIDED: unify NARROWLY.** A shared