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