docs: record Arc 7 complete, and what Stage 3 taught

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) <noreply@anthropic.com>
This commit is contained in:
Levi Neuwirth 2026-08-03 11:18:04 -04:00
parent 5f01edea87
commit 480158b460
No known key found for this signature in database
4 changed files with 181 additions and 73 deletions

View File

@ -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).

View File

@ -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 36 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

View File

@ -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<String>`; `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 |

View File

@ -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.6bc.** 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