From 066b8652b8778fcca11dfeeb62943a00ab047b3b Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 25 Jul 2026 11:37:21 -0400 Subject: [PATCH] docs: add COHERENCE.md as a required doc, audited against the codebase COHERENCE.md states the product-coherence thesis (pmacs should be immediately excellent, progressively understandable, completely inspectable, and ultimately replaceable) and, per-section, the audited ground truth of how the codebase measures against it: a scorecard across 19 concerns, the golden-journey verdict table (breaks at "open a real project" -- `pmacs .` exits 1), the six hardcoded key-interception shadows with no transient-keymap mechanism to migrate them to, the discoverability substrate-without-surface gap, the package/worker identity gap, and three cross-cutting patterns (substrate without surface, the silence asymmetry, per-arc coherence debt) that explain most of the individual findings. CLAUDE.md and AGENTS.md now list it as required reading alongside agent-handoff.md and active-work.md, and ask new framing docs to state their coherence impact. No runtime code changes. --- AGENTS.md | 16 +- CLAUDE.md | 16 +- COHERENCE.md | 1547 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1571 insertions(+), 8 deletions(-) create mode 100644 COHERENCE.md diff --git a/AGENTS.md b/AGENTS.md index 58c28f9..823b6e5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,11 +1,15 @@ # pmacs agent instructions -**Start here: read `docs/agent-handoff.md`, then +**Start here: read `docs/agent-handoff.md`, then `COHERENCE.md`, then `docs/active-work.md`, before taking on any work.** The handoff carries durable project state, working method, substrate invariants, and the -standing backlog. The active-work ledger carries volatile branches, +standing backlog. `COHERENCE.md` carries the product-coherence thesis +and its audited ground truth (scorecard, per-concern gaps, priority +order) — it is the standard new work gets evaluated against, not just a +backlog item; read it before framing anything and cite the section a +framing doc serves. The active-work ledger carries volatile branches, checkpoints, verification, and exact cross-machine recovery commands. -Keep both updated according to their own update protocols. +Keep all three updated according to their own update protocols. Always true, independent of the handoff: @@ -14,7 +18,11 @@ Always true, independent of the handoff: (`pmacs-protocol`). `#![forbid(unsafe_code)]`. - Workflow: framing doc in `docs/` -> user approval -> branch -> implement -> full gate suite -> PR -> user review rounds -> user says when to - merge. Never merge unprompted. One feature, one branch, one PR. + merge. Never merge unprompted. One feature, one branch, one PR. A + framing doc for coherence-affecting work should state its coherence + impact (journey steps touched, interaction islands added, config + registry adoption, background-work attribution) per `COHERENCE.md` + §20. - Gates before any PR: `cargo fmt --check`; `cargo clippy --workspace --all-targets -- -D warnings` (as its own step); `cargo test --lib`; `cargo test --lib --features crdt`; the touched acceptance suites; diff --git a/CLAUDE.md b/CLAUDE.md index 58c28f9..823b6e5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,11 +1,15 @@ # pmacs agent instructions -**Start here: read `docs/agent-handoff.md`, then +**Start here: read `docs/agent-handoff.md`, then `COHERENCE.md`, then `docs/active-work.md`, before taking on any work.** The handoff carries durable project state, working method, substrate invariants, and the -standing backlog. The active-work ledger carries volatile branches, +standing backlog. `COHERENCE.md` carries the product-coherence thesis +and its audited ground truth (scorecard, per-concern gaps, priority +order) — it is the standard new work gets evaluated against, not just a +backlog item; read it before framing anything and cite the section a +framing doc serves. The active-work ledger carries volatile branches, checkpoints, verification, and exact cross-machine recovery commands. -Keep both updated according to their own update protocols. +Keep all three updated according to their own update protocols. Always true, independent of the handoff: @@ -14,7 +18,11 @@ Always true, independent of the handoff: (`pmacs-protocol`). `#![forbid(unsafe_code)]`. - Workflow: framing doc in `docs/` -> user approval -> branch -> implement -> full gate suite -> PR -> user review rounds -> user says when to - merge. Never merge unprompted. One feature, one branch, one PR. + merge. Never merge unprompted. One feature, one branch, one PR. A + framing doc for coherence-affecting work should state its coherence + impact (journey steps touched, interaction islands added, config + registry adoption, background-work attribution) per `COHERENCE.md` + §20. - Gates before any PR: `cargo fmt --check`; `cargo clippy --workspace --all-targets -- -D warnings` (as its own step); `cargo test --lib`; `cargo test --lib --features crdt`; the touched acceptance suites; diff --git a/COHERENCE.md b/COHERENCE.md new file mode 100644 index 0000000..1594b69 --- /dev/null +++ b/COHERENCE.md @@ -0,0 +1,1547 @@ +# Product Coherence for Pmacs + +## Status of this document + +This document has two jobs. It states the **product-coherence thesis** +for pmacs, and it records the **audited ground truth** of how the +codebase measures against that thesis, so that no future agent or +contributor has to re-excavate it. + +- The vision prose is durable. The **Ground truth** subsections were + established **2026-07-25** by a four-lane code audit (discoverability, + interaction islands, packages/workers, first-run journey) plus a + distribution check, on branch `lsp-multi-root-affinity` + (= `main` @ `0827dd1` plus the multi-root LSP work). +- Citations name **symbols first, `file:line` second**. Line numbers + drift with the tree — `docs/keybindings.md` drifted by 250–1000 lines + within days of its "last verified" stamp (§24) — so treat the symbol + name and the structural claim as authoritative and the line number as + a hint. Re-grep before relying on a number. +- Grades used below: **Strong / Partial / Weak / Missing** (and + **Broken** where something actively fails). +- Update protocol is §25. When a PR changes any audited claim here, + updating this file rides that PR, the same way `docs/agent-handoff.md` + does. + +Relationship to the other required documents: `docs/agent-handoff.md` +carries durable project state and working method; `docs/active-work.md` +carries volatile branches and recovery; `docs/side-quest-backlog.md` +carries item-level deferrals. This document carries **product direction +and the measured distance to it**. It is not a second backlog; it is the +standard the backlog gets ranked against. + +--- + +## Purpose + +Pmacs already has an unusually strong technical foundation for an editor +at its stage of development. Its daemon/frontend split, semantic +rendering protocol, CRDT-based editing, structured worker runtime, Lua +programmability, language tooling, package resolver, terminal support, +and remote-capable architecture all point toward a system with genuine +long-term differentiation. + +The next challenge is not primarily adding more isolated capabilities. +It is making the existing and planned capabilities converge into a +coherent product. + +Visual Studio Code is used throughout this document as a reference point +because it is an exceptionally successful modern editor. Pmacs is +obviously not trying to become VS Code. Its goals are substantially +different: live programmability, inspectability, stronger concurrency +semantics, frontend plurality, and deeper user control are central to +Pmacs in ways they are not central to VS Code. The useful lesson is +therefore not to copy VS Code's interface or architecture wholesale, but +to understand how a technically complex system can become immediately +useful, progressively discoverable, and easy to adopt. + +The deeper reference point is Emacs. Emacs's beauty comes from its +ontological unity: the editor is text, Lisp, commands, buffers, and a +running system that the user can interrogate and change. Its enduring +achievement is not any single feature, but that it created the kind of +environment in which generations of users could build almost anything. + +Pmacs should preserve that unity while correcting the accidental +historical constraints beneath it: cooperative rather than general +parallelism, unclear ownership, global mutation, difficult unloading, +rendering coupled too closely to the core, opaque latency, implicit +remote context, and inconsistent package lifecycle. + +Pmacs does not need to contain everything Emacs contains before it can +be considered a successor. It must instead remain the kind of system in +which everything Emacs contains could eventually be built — with clearer +ownership, stronger concurrency, richer frontends, explicit execution +locations, and fewer historical traps. + +That places the VS Code comparison in its proper role. VS Code +demonstrates how a complex development environment can be coherent, +approachable, and immediately useful. Emacs demonstrates how an editor +can become a live, fertile, user-transformable world. Pmacs should +combine the adoption discipline of the former with the programmability +and unity of the latter. + +The core product objective should be: + +> **Pmacs should be immediately excellent, progressively understandable, +> completely inspectable, and ultimately replaceable.** + +A user should receive a polished workstation before they become an +editor engineer. If they choose to become one, the entire system should +remain open to them. + +--- + +## 0. Scorecard (audited 2026-07-25) + +| § | Concern | Grade | One-line state | +|---|---|---|---| +| 2 | Golden product journey | **Broken at entry** | `pmacs .` exits 1; only "launch" and "edit" pass cleanly zero-config | +| 3 | Zero-configuration state | **Partial** | Defaults genuinely strong; missing-tool failure is silent, not graceful | +| 4 | Progressive disclosure | **Inverted** | The advanced level is real; the beginner level is the missing one | +| 5 | Unified discoverability | **Substrate without surface** | Best-in-class registration metadata; almost no way for a user to reach it | +| 6 | Interaction islands | **Weak, and growing** | Six hardcoded key-interception shadows; no transient-keymap mechanism exists | +| 7 | First-class workspaces | **Missing (conventions only)** | Marker walk + four independent consumers; no workspace object | +| 8 | Execution locations | **Missing (architecture ready)** | SSH attach works; "location" is not a value anywhere | +| 9 | Worker ownership | **Mechanism without identity** | Cancellation solid; no owner/purpose/hierarchy; four disjoint activity views | +| 10 | Extension trust classes | **Missing (one class)** | Shared Lua state, `__index = _G`; MCP is the one out-of-process seam | +| 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 shared primitive; bottom panel landed (#155) | +| 15 | Contextual affordances | **Weak** | Right-click menu only; code actions apply first-blindly; no git integration at all | +| 16 | Semantic frontend | **Strong** | v6..=v20 negotiated protocol; degradation practiced; TUI/GPU share the model | +| 17 | Distribution | **Missing** | CI is test-only; no binaries, channels, checksums, or update path | +| 18 | Onboarding | **Missing** | No welcome, no tutorial; `C-h` deletes a word; `M-x` is the only door in | +| 19 | Coherence acceptance tests | **Missing (culture ready)** | Superb per-arc acceptance discipline; zero cross-subsystem journey tests | + +Three cross-cutting patterns explain most of the table; they are +detailed in §1.1–§1.3: **substrate without surface**, **the silence +asymmetry**, and **per-arc coherence debt**. + +Coherence-shaped work already in flight at audit time: find-file / +dired Stage 0 (`C-x C-f`, PR #162, `docs/dired-framing.md`), bottom +panel Stage 1 (merged #155), multi-root LSP affinity (branch +`lsp-multi-root-affinity`), the config registry foundation (merged +#127). + +--- + +## 1. The Product Problem + +Pmacs is building many difficult things correctly and in parallel. That +is appropriate for an early systems project. The risk is that the +project succeeds architecturally while remaining fragmented +experientially. + +A technically sophisticated editor can still feel incoherent when: + +- installation requires repository knowledge; +- capabilities exist but are difficult to discover; +- subsystems expose unrelated interaction conventions; +- project, process, terminal, language-server, and remote state are + modeled separately; +- configuration is powerful but provenance is unclear; +- packages can extend the editor but cannot be understood, controlled, + or attributed; +- background work is concurrent but not meaningfully owned; +- new users must configure the system before they can experience its + strengths. + +The relevant distinction is between **capability completeness** and +**product coherence**. Capability completeness asks "can pmacs do X?". +Product coherence asks whether a user naturally encounters X at the +right time, whether X behaves by shared conventions, whether the user +can understand why X is active, and whether X feels like part of one +editor rather than an adjacent demonstration. + +Pmacs is well on its way toward capability completeness in several major +areas. Product coherence must now become an explicit development track +rather than an emergent consequence of subsystem work. The 2026-07-25 +audit found that every one of the eight bullet points above is true of +pmacs today, and that they share three structural causes. + +### 1.1 Ground truth: substrate without surface + +The single most consistent audit finding, appearing independently in all +four lanes: **the mechanism layer is disciplined, often best-in-class; +the product surface that would make it perceptible is missing.** The +July 2026 roadmap named an instance of this "dark matter — built but +unwired" and treated it as a one-time backlog. It is not one-time; it is +the project's default failure mode. The audited inventory of complete, +working, unreachable capability: + +- **The entire rich help system.** `src/help.rs` implements a + self-navigable `*help*` buffer with `[command:]` / `[key:]` / + `[mode:]` / `[hook:]` / `[buffer:]` / `[view:]` cross-reference links + and `follow_link_at`, installed as `pmacs.help.show_command` / + `show_key` / `show_buffer` / `show_mode` / `show_hook` / `show_view` + (`install_help_module`, `src/lua_bindings/mod.rs:5597`). `grep -rn + "pmacs.help" builtin/` returns **zero hits** — no command, no + keybinding, no caller. +- **File-name completion.** `CompletionSource::Files { root }` + (`src/minibuffer.rs:589`) is reachable from Lua as `source = "files"` + + `source_root` — zero builtin callers. +- **Command availability.** `Command.predicate` is stored on every + command and **never evaluated** by `invoke`, `invoke_interactive`, + keymap dispatch, M-x filtering, or the menu (§5). +- **Package ownership.** `CurrentlyLoadingPackage` is a stack correctly + pushed/popped around every package chunk + (`src/lua_bindings/mod.rs:3953-3966`) and consulted by exactly one + binding (`on_unload`'s fallback). Every registrar ignores it (§13). +- **LSP health.** `LspManager::status_buffer_text()` (`src/lsp.rs:1204`) + is Lua-bound; no builtin command opens `*lsp*` (§2, §9). +- **Interactive file opening.** `pmacs.buffer.find_or_open` + (`src/lua_bindings/mod.rs:3103`) had no interactive caller at audit + time; a complete 1,384-line dired exists as a frozen test fixture + (`tests/fixtures/pmacs-dired/init.lua`). Being fixed now: dired Stage + 0 (PR #162). + +The strategic consequence: **most coherence gaps in pmacs are doors, +not engines** — deliberately deferred surface, not design error. That is +the cheap kind of gap, and it should change how the remaining work is +costed. + +### 1.2 Ground truth: the silence asymmetry + +Synchronous, user-initiated failures report well: `M-x` errors surface +as `"M-x error: "` (`builtin/commands/default.lua:633-641`), +compile spawn failures print in-buffer and on the status line +(`builtin/runtime/compile.lua:850-855`), and `pmacs --gpu` with no +`pmacs-gpu` binary produces the best missing-tool message in the +codebase — it names both the sibling path it tried and the PATH fallback +(`src/main.rs:367-379`). + +Automatic, background failures are swallowed. The canonical case, hit on +**every file open** when a language server is preconfigured but not +installed: `Command::spawn` ENOENT propagates up through +`LspManager::spawn` and raises in Lua — where `ensure_server` `pcall`s +it and returns nil (`builtin/runtime/lsp.lua:614-626`), and the +`buffer.after-load` hook `pcall`s the whole attach +(`builtin/runtime/lsp.lua:895-897`). Net user-visible result: nothing. +No status message, no `*errors*` entry, no modeline marker (the LSP +segment is gated on an attachment record existing, so absence is +indistinguishable from "unsupported file type"). Working tree-sitter +highlighting **actively masks** the failure — the user sees colored text +and assumes language intelligence is on. Post-crash is the same shape: +`LspEventKind::Crashed` is pushed (`src/lsp.rs:2394`) and no builtin +subscriber surfaces it. + +This directly contradicts the product thesis (§23): the "without +freezing" half is delivered; the "without becoming opaque" half is +currently false for exactly the failures a new user will hit first. + +**Rule to adopt:** anything that fails automatically must leave a +user-visible trace with a named owner. A `pcall` around background +wiring must log attributed failure, never discard it. + +### 1.3 Ground truth: coherence debt compounds per-arc + +Three audited growth patterns show subsystem work accruing coherence +debt with no counter-pressure: + +- Each new modal UI **extended the shadow family** instead of building + the keymap-layer mechanism (menu → completion → query-replace, §6) — + and each addition must hand-sync three guard lists (`dispatch_key`, + `dispatch_idle_for`, `dispatch_paste`). +- Each new subsystem **added its own activity view** (`*workers*`, + `pmacs.process.list`, `*lsp*`, the terminal-private id set, §9), + because no common identity key exists to join them. +- Each new option **individually decides** whether to adopt the config + registry; five have, everything else has not (§11). + +The framing-doc workflow (scout → framing → approval → acceptance +criteria → bite-verified review) is exactly the right tool to reverse +this — no framing has ever carried a product-coherence acceptance +criterion. Adding them is a process change, not an engineering arc, and +it is what makes this document *required* rather than advisory. + +--- + +## 2. The Golden Product Journey + +Pmacs should maintain one protected end-to-end experience against which +all major work is tested: + +1. Install Pmacs. +2. Launch it without prior configuration. +3. Open a real project. +4. Understand the visible interface. +5. Edit immediately. +6. Receive language intelligence. +7. Find a symbol or file. +8. Open a terminal. +9. Build or test the project. +10. Inspect and act on an error. +11. Understand what background work is running. +12. Close and later restore the workspace. + +This does not need to exercise every advanced feature. It exists to +prove that the editor's components form a usable whole. A strong initial +target is a Rust project, because Rust stresses many of pmacs's intended +strengths: project detection, toolchain discovery, language-server +lifecycle, async diagnostics, build/test integration, terminal use, +large compilation workloads, symbol search, background indexing, +structured error presentation. + +```text +Install Pmacs + ↓ +Run `pmacs .` + ↓ +Project root detected + ↓ +Rust mode activated + ↓ +rust-analyzer found or installation guidance shown + ↓ +Files, diagnostics, terminal, and project actions available + ↓ +Build or test command discoverable + ↓ +Errors become navigable structured results +``` + +This journey should become a release gate. New architectural work should +be evaluated partly by whether it improves, preserves, or complicates +the journey. + +### Ground truth: the journey today + +**Grade: broken at step 3.** Verified empirically at audit time: + +``` +$ ./target/release/pmacs . +pmacs: Is a directory (os error 21) +EXIT=1 +``` + +The literal first arrow of the diagram above fails. `load_file` +(`src/file_io.rs:81-87`) does `File::open` (succeeds on a directory) +then `read_to_end` → EISDIR, which is not `NotFound`, so +`EditorState::open` returns `Err` and `main` prints and exits +(`src/main.rs:411-414`). Multiple file arguments are also rejected +(`"multiple files not yet supported"`, `src/main.rs:227`). Everything +from step 6 onward is gated on a file being open, and the only +zero-config way to open one is naming it on the command line — which +requires already knowing the path. + +Full verdict table: + +| # | Step | Verdict | Evidence | +|---|---|---|---| +| 1 | Install | **Partial** | Source build only: `cargo build --release --workspace --features pmacs/crdt` (`README.md`). No binaries, no packaging. Runtime deps (`/bin/sh`, git, tar, coreutils) documented, never checked at runtime | +| 2 | Launch unconfigured | **Works** | `EditorState::new()` → empty `*scratch*`; missing config is not an error (`src/config.rs:7-9`); recentf/saveplace/autosave default-on | +| 3 | Open real project | **Missing** | `pmacs .` exits 1 (above). No directory handling anywhere | +| 4 | Understand interface | **Partial** | Mode line gives name/modified/L:C/scroll + mode/LSP/terminal segments; but no welcome text (`EditorCore::new` sets `status: String::new()`), no cheat sheet, and `C-h` deletes a word (§18) | +| 5 | Edit | **Works** | Full CUA + Emacs keymap in 161 lines (`builtin/keymaps/default.lua`); isearch, query-replace, kill ring, undo/redo, auto-indent/pair/comment, atomic save. Genuinely excellent zero-config | +| 6 | Language intelligence | **Partial** | Rust grammar bundled and auto-attaches; rust-analyzer preconfigured (`builtin/runtime/lsp.lua:44-52`) — but a missing binary fails silently (§1.2) and highlighting masks it. No LSP status command exists to diagnose | +| 7 | Find symbol / file | **File: missing → in flight (PR #162). Symbol: works but undiscoverable** | No find-file/dired/picker existed at audit; `M-.`/`M-?`/`C-c o` bound but advertised nowhere and server-gated; no workspace-symbol command; `pmacs.index.*` has no UI | +| 8 | Open terminal | **Works but undiscoverable** | Full PTY with scrollback + modeline segment — reachable only as `M-x terminal`, no keybinding | +| 9 | Build / test | **Partial** | `M-x compile.run` works, defaults cwd to detected project root, parses Rust `-->` errors — but no keybinding, an **empty first prompt** (`initial = last and last.cmdline or ""`, `builtin/runtime/compile.lua:1134-1138`), and no `cargo build`/`cargo test` suggestion despite `ProjectKind::Cargo` existing (`src/project.rs:77`) | +| 10 | Inspect error | **Partial (good once reached)** | `E:n W:n` modeline counts, underlines, `M-g n/p` + ``C-x ` `` walking a unified compile/grep/diag source, message echo, `RET` visits. Gated entirely on step 6 or 9 succeeding first | +| 11 | See background work | **Works but undiscoverable** | `*workers*` view via `M-x editor.list-workers`; `C-c C-k` cancel-at-point. No keybinding, no statusline spinner/progress indicator anywhere (§9) | +| 12 | Close + restore | **Partial** | Per-file cursor+scroll (saveplace), recent files, minibuffer history, autosave recovery all restore zero-config. Open-buffer set and window layout do **not**: desktop-save is opt-in (`pmacs.session.desktop_mode(true)`) *and* a documented no-op under a daemon (`src/desktop.rs:323-326`, `:353-356`, Q#DS9) | + +A journey observation worth keeping verbatim from the audit: +**keybinding coverage is inverted relative to frequency** — `C-c @ +C-M-s` opens all folds, while opening a file, opening a terminal, and +running a build have no bindings at all. + +--- + +## 3. A Strong Zero-Configuration State + +Pmacs should not require configuration before it becomes pleasant. The +default experience should demonstrate the editor's thesis: responsive +editing, visible asynchronous work, coherent project awareness, language +intelligence, integrated terminal and task execution, helpful +diagnostics, discoverable commands, graceful failure when external tools +are absent. + +Configuration should be an escalation path: + +1. The editor works. +2. The user notices a preference. +3. The relevant setting or command is easy to find. +4. The user changes it. +5. The editor explains where the effective value came from. +6. Advanced users can replace the behavior entirely. + +### Recommended default surface + +The graphical frontend should have a deliberate default workspace with a +restrained number of visible regions: main editor area; compact +statusline; optional project/files surface; bottom panel for terminal, +build output, diagnostics, and other transient tools; command palette; +contextual actions; unobtrusive background activity indicator. The TUI +should express the same conceptual model within terminal constraints. +The goal is not identical geometry across frontends — it is shared +nouns, commands, lifecycle, and state. + +### Ground truth + +**Grade: partial — the defaults half is strong, the graceful-failure +half fails.** + +What already works with zero configuration, and is a real asset: + +- Missing config is **not an error by contract** (`src/config.rs:7-9`); + no config directory is created or required; a *broken* `init.lua` + does not block startup — the error lands in `*errors*` and the status + line (`src/config.rs:10-13`). +- Default-on persistence: recentf (`builtin/runtime/recentf.lua`, cap + 50, `C-x C-r`), saveplace (`builtin/runtime/saveplace.lua`, restores + cursor + view on `after-load`), autosave every 30 s with next-session + recovery (`builtin/runtime/autosave.lua:24`), per-bucket minibuffer + history. State root: `PMACS_STATE_HOME` → `$XDG_STATE_HOME/pmacs` → + `~/.local/state/pmacs` (`user_state_dir`, `src/state.rs:54-70`), + wired only in real entry points (`install_state_dirs`) so tests stay + hermetic. +- Atomic saves, full editing surface, bundled grammars for every + preconfigured LSP language. + +What fails the escalation path: + +- Step 3 ("easy to find") fails for both settings and commands (§5). +- Step 5 ("explains where the value came from") is **unanswerable + today**: config overrides are stored as bare values with no source + (§11). +- "Graceful failure when external tools are absent" is the silence + asymmetry (§1.2). The `--gpu` message (`src/main.rs:367-379`) is the + pattern to replicate; LSP auto-attach is the anti-pattern. + +--- + +## 4. Progressive Disclosure + +Pmacs should support several levels of use without requiring users to +inhabit the most advanced one. These levels should be different +presentations of the same underlying objects — a command selected from a +context menu, invoked through `M-x`, bound to a key, called from Lua, or +triggered by an agent should be the same command object. + +### Ground truth + +**Grade: inverted.** The advanced level is largely real; the beginner +level is the one missing. Audited level-by-level: + +**Beginner** (should see: files, buffers, search, diagnostics, terminal, +build actions, menus, missing-tool guidance): + +- files ✗ (no find-file at audit; PR #162 in flight) · buffers ✓ (`C-x + b`, `*buffer-list*`) · search ✓ (`C-s`/`C-r`/`C-M-s`; project.search + is M-x-only) · diagnostics ✓ once a server runs · terminal ✓ but + M-x-only · build ✓ but M-x-only with empty prompt · menus △ + (right-click only, 11 items) · missing-tool guidance ✗ (§1.2). + +**Intermediate** (should discover: palette, keybinding search, workspace +settings, profiles, package management, task definitions, +frontend/language settings): + +- palette △ (`M-x` fuzzy over bare names, §5) · keybinding search ✗ (no + list-keybindings/where-is commands) · workspace settings ✗ (no + workspace scope, §11) · profiles ✗ (§12) · package management ✗ + in-session (§13) · task definitions ✗ · frontend customization △ + (themes, `pmacs.gpu.set_font`, statusline providers — all Lua-only) · + language settings △ (raw Lua tables, outside the registry). + +**Advanced** (should be able to: inspect implementations, redefine live, +create packages, new views, providers, keymap layers, workspace policy, +orchestrate workers, replace interaction models): + +- inspect ✓ (SourceLocation on everything; no jump-to-source command + though) · redefine live ✓ (`unregister` + `define`) · packages ✓ + (authoring is real, §13) · new views ✓ (listview is Lua-usable) · + providers ✓ (statusline; completion/minibuffer sources are a fixed + Rust vocabulary) · keymap layers ✗ (§6 — the mechanism does not + exist) · workspace policy ✗ · orchestrate workers △ + (`pmacs.workers.register` funnels into builtin dispatchers, §9) · + replace interaction models ✗ (the shadows, §6). + +The "same command object" principle largely holds where surfaces exist — +menu items, keybindings, and M-x all resolve command names into the one +registry — with one caveat: menu items are a **parallel registry of +labels** whose command references are unvalidated (§5). + +--- + +## 5. Unify Discoverability + +Pmacs already has the beginnings of a strong command registry. This +should become the center of a broader discovery model. Every meaningful +action should eventually expose: stable symbolic identity, title, +description, category, aliases, current keybindings, provenance, +applicability predicate (with an explanation when unavailable), argument +schema, destructive/asynchronous/reversible flags, locality, related +commands and settings, and source location. Settings should expose name, +type, description, default, effective value, provenance, scope, +validation rules, listeners, related commands. Packages and workers +should expose the analogous sets (§13, §9). + +This suggests a general pmacs principle: + +> **Anything that can affect the user should be discoverable as a +> structured object with identity, provenance, ownership, and +> lifecycle.** + +### Ground truth + +**Grade: substrate without surface — the sharpest instance of §1.1.** + +**What the substrate already has (genuinely strong):** + +- `Command` (`src/command.rs:66-79`) = `{ name, description, source, + body, predicate }`. Description is **mandatory and validated** (R42); + duplicate names are a hard error, not an overwrite; `SourceLocation + { file, line }` is auto-captured from Lua debug info on **every** + command, hook, menu item, config definition, config listener, and + keybinding — the user cannot forge it. ~147 `pmacs.command.define` + sites across `builtin/`. +- `ConfigDefinition` (`src/config_registry.rs:396-410`) is **richer + than `Command`**: name, mandatory description, `ConfigKind` + (Boolean/Integer/Number/String/Enum with bounds, choices, + allow_empty), default, `Live`/`StartupOnly` mutability, source. + `pmacs.config.list()` returns full descriptor tables. +- Reverse keybinding lookup exists as data: `KeymapStack::iter_all()` + (`src/keymap_stack.rs:295-311`) enumerates every binding; + `pmacs.describe.command(name).key_bindings` computes where-is on + demand. +- `pmacs.describe.*` (`src/lua_bindings/mod.rs:6042-6162`) returns + structured tables for command/key/buffer/view/mode/hook, and + `describe.key` resolves against the **active buffer + major mode**. +- M-x matching is fuzzy (case-insensitive subsequence with + boundary/consecutive bonuses, `fuzzy_score`, + `src/minibuffer.rs:637-666`). + +**What is missing, itemized:** + +- `Command` has **no title, no category, no aliases, no argument + schema, no destructive/async/reversible flags**. The dotted-name + prefix (`buffer.`, `lsp.`) is convention, not data. MCP tooling works + around the missing schema by stuffing rendered JSON schema text into + the description string. +- **`Command.predicate` is dead metadata.** It is read in exactly two + places (a literal line in the unreachable help renderer, and a test) + and **never evaluated** by `invoke`, `invoke_interactive`, dispatch, + M-x filtering, or the menu. The doc comment's claim that "the command + palette (T M2.7) uses it to gray out unavailable entries" describes + something that never shipped. +- **M-x shows bare name strings.** `CompletionSource::Commands` returns + `Vec` of names; the wire type `MinibufferPrompt.candidates` + is `Vec` (`pmacs-protocol/src/message.rs:994-1006`). No + description, no keybinding, no category alongside candidates — while + `CompletionPopupRow` (`:1231`) already carries `kind` and `detail`, + proving richer rows are a solved wire problem in this codebase. +- **The entire Rust help layer is orphaned** (§1.1). Consequence: two + parallel `*help*` implementations exist — `help.rs`'s + cross-referenced renderer and the Lua `show_help_text` in + `builtin/commands/default.lua:1103-1136` — and the one users can + actually reach (`M-x editor.describe-command`) renders **less** than + the unreachable one (no source, no scope, no predicate note). +- **Missing as commands entirely:** describe-key, describe-mode, + describe-hook, describe-buffer, where-is, list-commands, + list-settings, list-keybindings, apropos. What exists: + `editor.describe-command`, `editor.describe-setting`, + `editor.describe-instance[-buffer]`, `editor.list-buffers`, + `editor.list-workers`. `M-x describe-setting` prompts **free-text + with no completion source** (deliberately skipped — + `builtin/commands/default.lua:1180-1185`); a typo yields a status + line error. +- **No help prefix key.** `C-h` is `buffer.delete-word-backward` + (`builtin/keymaps/default.lua:86`, with a comment noting the key "was + free"). No `F1`, no `C-h k/f/b`. +- **Settings value provenance is absent.** Overrides are stored as bare + values (`global: HashMap`, + `src/config_registry.rs:693-708`); `describe-setting`'s "Source:" is + the *definition* site. "Why is this setting 4 and who set it?" is + unanswerable (§11). +- **Menu items are a parallel registry.** `MenuItem` + (`src/menu.rs:56-78`) carries its own hand-written `label` duplicating + the command's description, with a lazily-resolved `command` name + string that is **never validated to exist** — a typo'd item silently + does nothing when clicked. The wire row is label + separator only + (`MenuPromptRow`): no key hints, no grayed state. Note the asymmetry: + `pmacs.menu.list` reports `has_predicate`; `pmacs.describe.command` + does not. +- **The two key-lookup APIs disagree.** `pmacs.keymap.lookup` is + global-only (it resolves with no buffer and no modes, + `src/lua_bindings/mod.rs:6294-6307`) while `pmacs.describe.key` is + context-aware. `pmacs.keymap.list` erases `source` and renders + `Scope::Buffer(id)` as bare `"buffer"` (id erased), so full-fidelity + enumeration requires per-command `describe.command` calls. There is no + which-key-style prefix surface. + +**Shape of the fix:** roughly (a) three metadata additions on `Command` +(title, category, predicate actually evaluated + reported), (b) value +provenance in the config registry, (c) a dozen interactive commands and +richer M-x candidate rows over introspection that **already exists**. +This is the highest payoff-per-effort concern in the document. + +--- + +## 6. Eliminate Hardcoded Interaction Islands + +Pmacs's public programmability story will be strongest when all major +interaction layers pass through ordinary registries and extension +points. Temporary or modal interfaces — incremental search, query +replace, minibuffer prompts, completion menus, context menus, transient +selectors — should eventually use inspectable keymap layers rather than +special Rust-level interception. A general transient keymap model +includes priority, activation condition, owner, lifetime, fallback +behavior, discoverability, help labels, and cancellation behavior. + +### Ground truth + +**Grade: weak, and growing by one island per modal feature.** + +Everything funnels through one function: `EditorInstance::dispatch_key` +(`src/editor.rs:901`), a single input-precedence state machine (its own +`#[allow(too_many_lines)]` says as much). The audited precedence order: + +| # | Surface | Guard site | Decoder | Kind | +|---|---|---|---|---| +| 0 | popup-vs-modal auto-close | `editor.rs:917-925` | — | pre-step | +| 1 | Context menu | `editor.rs:933` | `MenuKey::from_chord` (`editor.rs:3005`) | **full shadow** | +| 2 | isearch | `editor.rs:939` | `SearchKey::from_chord` (`editor.rs:2902`) | **full shadow** | +| 3 | query-replace | `editor.rs:945` | `QueryReplaceKey::from_chord` (`editor.rs:2967`) | **full shadow** | +| 4 | Minibuffer | `editor.rs:951` | `MinibufferAction::from_chord` (`src/minibuffer.rs:468`) | **full shadow** | +| 5 | Completion popup | `editor.rs:958-971` | `CompletionPopupKey::from_chord` (`editor.rs:3056`) | **partial shadow** (control chords only; skipped while a multi-key prefix is pending) | +| 6 | Terminal transport + `C-c` escape | `editor.rs:973-1010` | `is_terminal_escape_chord` (`editor.rs:4355`) | **partial, transport-level** | +| 7 | Ordinary dispatch | `editor.rs:1018-1032` | `KeymapStack::resolve` | the only inspectable layer | + +Facts that define the gap: + +- **Full shadows eat every key**, including unrecognized ones (each + decoder has an `Ignore`/`Dismiss` fallback arm). While a terminal + buffer is focused and unescaped, *all* keys encode to the child — + `C-c`-leading user bindings are **structurally unreachable** in a + terminal buffer. +- **No transient-keymap mechanism exists to migrate to.** `KeymapStack` + has exactly three fixed scopes — `Buffer(BufferId)`, `Mode(String)`, + `Global` (`src/keymap_stack.rs:37-44`); resolution order buffer → + mode → global with cooperative prefix-pending across scopes + (`resolve`, `keymap_stack.rs:235-291`). No layer stack, no push/pop, + no priority, no lifetime. The Lua scope accept-list hard-rejects + anything else. So this is not "migrate the shadows to the layer + system" — **the layer system must be built first.** (`active_modes` + is also at most one mode today; minor modes are unbuilt.) +- **`describe-key` lies while a shadow is active.** With the completion + popup open, `describe-key C-n` reports `cursor.down @global`; the + literal arm `'n' => Some(Self::Next)` fires instead. Introspection + has zero awareness of the shadows; Lua can observe only a boolean per + surface (`popup_visible`, `search_active`, `query_replace_active`, + minibuffer-active). +- **This is deliberate and documented** — rationale R51 + (`docs/keybindings.md`, `src/minibuffer.rs:470`): the shadows are + intentionally not user-configurable. The completion framing + considered and rejected buffer-local binds on teardown-lifecycle + grounds (`docs/in-buffer-completion-framing.md:93-105`) — the + objection was a *leaked binding outliving its session*, which is an + argument for a lifetime-owning layer handle, not against layers. +- **Three hand-synced guard lists** must be updated per shadow: + `dispatch_key`, `dispatch_idle_for` (`editor.rs:791` — deliberately + omits the partial popup shadow; load-bearing for CRDT frontends' + optimistic-apply correctness), and `dispatch_paste` + (`editor.rs:1129-1140`). +- Off-path hardcodes: client-side **F12 detach** (`is_detach_key`, + `src/attach.rs:997-1006`) and the GPU **optimistic key classifier** + (`crate::optimistic::classify_key`) — the latter is classification, + not routing, and is kept honest by `dispatch_idle_for`. + +**The counter-example that proves the idiom:** the entire picker/panel +family — listview (references, outline), project-search, buffer-list, +compile-mode, REPL, terminal scroll commands — uses ordinary +**buffer-local keymaps** via `pmacs.keymap.bind { scope = "buffer" }` +(`builtin/runtime/listview.lua:76-88` and siblings). These are +inspectable, correctly reported by describe-key, and rebindable from +`init.lua`. Roughly half the transient UI already lives on the right +side of the line. + +**The concrete missing primitive** is small and well-scoped: a transient +overlay consulted before buffer scope (a `Scope::Transient` or an +overlay `Vec`), with (a) push/pop tied to session lifetime via a +lifetime-owning handle (RAII on the Rust side), (b) a full-shadow vs +partial-shadow flag (isearch eats everything and falls back to +search-self-insert; the popup intercepts eight chords and falls +through), and (c) `dispatch_idle_for` **derived** from the stack ("any +active layer is full-shadow") instead of hand-maintained. With that, the +six ladder rungs collapse into "session pushes a layer on open, pops on +close," and describe-key becomes truthful for free. + +--- + +## 7. First-Class Workspaces + +Project-root detection is useful, but pmacs needs a richer workspace +object. A project answers "which root contains this file?"; a workspace +answers "which persistent development environment owns this set of +activity?" A workspace should eventually own: + +```text +Workspace +├── identity +├── one or more roots +├── execution location +├── environment and toolchain +├── configuration layers +├── trust policy +├── enabled packages +├── language-server instances +├── indexes +├── terminals and processes +├── tasks +├── debugger sessions +├── open buffers and views +├── frontend layout state +└── persistence and restoration policy +``` + +This matters for multi-root language servers, monorepos, generated +files, remote projects, containers, HPC environments, per-project +packages, task ownership, session restoration, and project-specific +trust. The workspace should be a core runtime entity, not an informal +convention shared across unrelated subsystems. + +### Ground truth + +**Grade: missing — what exists is a marker walk plus four independent +per-subsystem conventions.** + +- **Detection**: `src/project.rs` — `default_markers()` is + `Cargo.toml`, `go.mod`, `package.json`, `.git` (directory), with the + deliberate rule that **language markers outrank `.git`** at the same + ancestor level; upward walk, innermost wins; `set_search_boundary` + honored; `ProjectKind` (e.g. `Cargo`) exists and is **consumed by + nothing** user-facing. +- **Four independent consumers**, each resolving on its own: + LSP root (`project_root_for`, `builtin/runtime/lsp.lua:554-570`: + config override → marker walk → file's own directory, returning + `root, source` where source ∈ config/detected/fallback); compile cwd + (`project_root_of_active`, `builtin/runtime/compile.lua:600-608`); + project-search root (`resolve_search_root`, + `builtin/commands/default.lua:843-857`, falls back to `"."`); the + project symbol index (`.pmacs/index.json`, `src/project_index.rs`). +- **There is no "current project" independent of the active buffer's + path.** With only `*scratch*` open, every consumer above returns + nil/`"."`. Nothing owns the set {roots, servers, terminals, tasks, + layout} — which is why desktop-save under a daemon had nothing + principled to attach to (Q#DS9, §2 step 12). +- **First slice in flight**: the multi-root LSP server-affinity work + (branch `lsp-multi-root-affinity`) makes *(language, found-root)* the + server identity — the first time a root functions as an identity key + rather than a spawn parameter. Note it is again per-subsystem: LSP + learns roots; compile, search, index, and trust do not share the + object. + +A workspace entity is a **model gap** (real arc), not wiring. It is also +the prerequisite that keeps §8 (locations), §9 (task ownership), §11 +(workspace config scope), and step 12 of the journey from each inventing +their own ownership story. + +--- + +## 8. First-Class Execution Locations + +Pmacs's daemon/frontend architecture gives it an excellent basis for +remote development. The next step is to model execution location +explicitly — a value that can be inspected and assigned, not an +implementation detail hidden inside file access or process spawning: + +```text +Location +├── local +├── ssh://host +├── container://name +├── slurm://allocation +├── daemon://session +└── custom provider +``` + +Filesystem roots, processes, terminals, language servers, workers, +debuggers, indexers, package services, and build/test tasks should all +carry a location. That makes answerable: where is this server running? +where will this build execute? is this terminal local? can this worker +migrate? what happens if the remote daemon disconnects? + +### Ground truth + +**Grade: missing as a model; the architecture half already works.** + +What exists: `pmacs --attach user@host` (remote TUI over SSH), +`ssh:user@host/instance` / `local:/path.sock` addressing, mosh-modeled +reconnect-on-drop, and the daemon/frontend split itself — i.e. +`daemon://session` exists implicitly and robustly. What does not exist: +any `Location` value. Every `ProcessSpec` spawn, LSP server, terminal +PTY, and worker is implicitly daemon-local; no resource carries a +location field; nothing can be asked "where is this running?". No +container/slurm/provider concept anywhere. + +This concern is deliberately *after* §7 in dependency order: a location +without a workspace to scope it has nothing to attach to. For the +research/HPC ambition (§12's Research Workstation profile), this pair is +the long-lead differentiator — nothing else in the editor market models +it well. + +--- + +## 9. Extend the Worker Model into Structured Concurrency + +Pmacs's worker system is one of its most distinctive strengths. +Cancellation, supersession, streaming, frame-aware draining, and the +`*workers*` view provide a strong basis. The next step is ownership and +hierarchy: every substantial task should have an owner, a workspace, an +optional buffer/view, a parent, children, a latency class, a +cancellation scope, a resource budget, an execution location, progress, +and failure attribution. + +```text +Workspace: pmacs +└── Command: project-build + ├── Task: save-dirty-buffers + ├── Task: cargo-check + │ ├── Process: cargo + │ └── Stream: compiler-diagnostics + └── Task: refresh-diagnostics +``` + +Cancelling `project-build` should cancel its children. Closing a +workspace should terminate or detach workspace-owned work. Reloading a +package should stop package-owned tasks. The activity view should answer +what is running, why, who owns it, where, what depends on it, and what +cancellation will affect. That turns parallelism into a product feature +rather than an implementation claim. + +### Ground truth + +**Grade: mechanism without identity.** + +**The mechanism layer is solid:** cooperative per-job cancellation +tokens with panic isolation (`src/worker.rs:13-28`); supersession with +correct settle-time pruning (`src/async_runtime.rs:688-701`) — a +genuinely good primitive; a completions ring (cap 64); `register_external` +so non-pool work (LSP requests, MCP) appears uniformly; one shared +`ProcessSupervisor` under everything (`src/editor.rs:341`); the +`*workers*` view (`src/workers_buffer.rs`, opened by `M-x +editor.list-workers`, auto-refreshing, `C-c C-k` cancel-at-point). + +**The identity layer is absent:** + +- `PendingJob` (`src/async_runtime.rs:365-392`) carries `{cancel, + state, supersede_key, stream_buffer, max_batch, kind, + dispatched_at}`. **No owner. No purpose string. No + workspace/buffer association. No parent.** The one buffer link that + exists (parse job → buffer) lives in a `SyntaxCoordinator` side map, + invisible to the workers view. +- `JobKind` is a **closed 12-variant enum** (Sleep, ComputeSum, EmitN, + Grep, Parse, FsReadDir, FsStat, FsRename, FsChmod, FsRemove, + McpRequest, LspRequest). `pmacs.workers.register` funnels Lua jobs + into existing Rust dispatchers, so **every third-party job renders + under a builtin's label**. +- Supersession is opt-in per dispatch site and underused: `"search"` + (grep) and `lsp:{method}:{sid}:{uri}` use it; **parse jobs and all + MCP requests pass `None`** — a fast typist stacks parse jobs. +- Cancellation scopes: per-id and per-key only. No cancel-all, + by-kind, by-buffer, by-owner, or by-subtree — there is no scope to + range over. +- **Four disjoint activity planes with no join key:** + +| Plane | Surface | What it misses | +|---|---|---| +| Async jobs | `*workers*` | processes, servers, terminals | +| OS processes | `pmacs.process.list` (no buffer view exists) | **filters to `LineOriented` only — terminal PTYs are invisible**; `spawn_terminal` bypasses the public path entirely | +| LSP servers | `*lsp*` status text | **no builtin command opens it**; LSP sets `RestartPolicy::Never` on the supervisor and runs its own restart logic | +| Terminals | private id set drained after the supervisor tick | user-visible in none of the above | + + A terminal PTY appears in **no** user-visible activity view. An LSP + server appears in `*lsp*` (unreachable) and `list()`; its requests + appear in `*workers*`; nothing joins them. +- **No progress indicator exists anywhere** — no statusline spinner, + no busy count (grep for progress/spinner/busy in `src/statusline.rs` + is empty). "Visible asynchronous work" (§3) is currently false unless + the user knows to run `M-x editor.list-workers`. +- `ProcessSpec.label` is the nearest thing to attribution: caller- + supplied, unvalidated convention (`lsp:{name}`, terminal buffer + name). + +The audit's conclusion, worth preserving verbatim: *because identity is +missing, scoped cancellation has nothing to scope over and a unified +activity view has nothing to group by — the four views exist precisely +because there is no common key to merge them on.* Owner/purpose/parent +fields on the job and process specs are the prerequisite; the unified +view and the ownership tree fall out of them. + +--- + +## 10. Define Extension Trust and Isolation Classes + +Pmacs should preserve live, low-friction programmability — it should not +force all extensions into rigid out-of-process APIs. At the same time, +namespace isolation inside a shared Lua state is not enough for fault +containment, security, latency containment, memory accounting, +native-code isolation, reliable unloading, or project-local trust. Pmacs +should define extension classes before the ecosystem becomes large: + +- **10.1 Trusted core packages** — in-process, deep API access, + distributed with pmacs or explicitly trusted. +- **10.2 Normal Lua packages** — shared/managed runtime, declared + capabilities, owned registrations and workers, execution budgets, + measurable latency, reloadable lifecycle, package-level error + attribution. +- **10.3 Isolated service extensions** — separate process, typed RPC, + crash recovery, resource accounting, explicit fs/process/network + permissions. +- **10.4 Project-local / untrusted** — explicit approval, restricted + capabilities, strong isolation, workspace-scoped trust, easy + revocation. + +### Ground truth + +**Grade: missing — one class exists.** + +Every package today is a 10.1/10.2 hybrid with none of 10.2's +machinery: in-process, per-package `_ENV` with `__index = _G` +(namespace hygiene, not containment), full API access, no capability +declarations, no budgets, no latency measurement, no owned-registration +lifecycle (§13). The only containment primitive in the tree is the +instruction-count hook that can cancel a hot-looping main-thread chunk +(`src/lua_isolation.rs:1-39`) — a runaway guard, not an isolation class. + +Two real assets to build on: the loader's `exports` gating (the package +searcher is deliberately inserted at position 1 of `package.searchers` +so exports are enforceable, `src/lua_bindings/mod.rs:3891-3900`), and +**MCP as the existing 10.3 seam** — packages can already spawn MCP +servers and consume their tools over a typed transport +(`docs/mcp-for-package-authors.md`), which is exactly the +separate-process/typed-RPC shape 10.3 asks for. Project-local trust +(10.4) has a natural anchor once §7's workspace exists. + +Sequencing note: 10.2's "owned registrations, reloadable lifecycle, +error attribution" is the same work as §13's ownership gap — do it once, +under one arc. + +--- + +## 11. Configuration as Typed, Layered Data + +Pmacs's typed configuration registry is the correct foundation. It +should develop into a layered system with explicit provenance. Likely +layers: built-in defaults; profile defaults; user settings; +machine-local; remote-location; workspace; root/folder; language/mode; +buffer-local; session overrides. A setting inspection view should show +the full chain and the active source: + +```text +setting: editor.tab-width +effective value: 4 +type: integer +scope: workspace + +defined by: + built-in default: 8 + Rust profile: 4 + user setting: 2 + workspace override: 4 + +active source: + ~/src/pmacs/.pmacs/settings.lua +``` + +Pmacs should also preserve three distinct levels — **settings** (typed +declarative data), **behavioral customization** (commands, hooks, +keymaps, Lua), **package construction** (new capabilities) — so that +users do not need executable Lua for ordinary preferences, while +advanced users can still replace the mechanism. + +### Ground truth + +**Grade: partial — the foundation shipped (#127) and is correct; the +layering, provenance, and adoption have not followed.** + +- The registry is typed, described, duplicate-rejected, freeze-aware + (`StartupOnly`), listener-bearing, and introspectable — see §5. Its + design decisions (always-store overrides, explicit buffer, no ambient + scope) are recorded in `docs/config-registry-framing.md`. +- **Two scopes exist** of the ten layers listed above: global and + buffer-local. Per-language and per-project are patterns (a hook + calling `set_local`), not scopes. No profile, workspace, machine, or + remote layer. +- **Value provenance is absent** (§5): overrides are bare + `ConfigValue`s; `describe-setting`'s "Source:" names where `define()` + ran. The inspection view sketched above is currently impossible to + render. +- **Adoption is five settings**: `editing.auto-pair` (pair.lua), + `editing.trim-on-save` (editops.lua), `autosave.interval-ms` + (autosave.lua), `window.panel-height` + `window.min-height` + (window.lua). Everything else a user might set — theme, fonts, LSP + server config, killring size, recentf/saveplace/desktop enables, + pair sets, comment strings, `pmacs.parse.*` — lives in raw Lua + outside the registry and is therefore invisible to `describe-setting` + and any future settings UI. The migration list is already written: + `docs/config-registry-framing.md` "named deferrals" (table-valued + settings are the hard prerequisite for LSP/pair/comment tables). +- **No persistence**: settings changed at runtime do not survive + restart (the `custom-file` split-brain question is a named deferral). +- The three-level separation holds in principle today (registry / + hooks+keymaps / packages), but with five settings registered, level 1 + is effectively empty — users need executable Lua for nearly every + ordinary preference, which is the exact failure the section warns + about. + +--- + +## 12. Profiles as Product-Level Bundles + +Pmacs should offer a small number of official profiles bundling default +keymaps, visible interface regions, package recommendations, settings, +task conventions, discovery hints, and onboarding: **Pmacs Standard** +(approachable graphical workstation), **Emacs** (familiar bindings, +minibuffer-centered), **Minimal**, and later **Research Workstation** +(terminals, remote machines, Slurm, proof assistants, long-running +builds). Profiles must not create separate products — they exercise the +same registries and primitives. + +### Ground truth + +**Grade: missing.** Not a named concept anywhere in the tree. There is +one hardcoded default: a single 161-line keymap +(`builtin/keymaps/default.lua`) that is already a de-facto hybrid of the +"Standard" and "Emacs" profiles (CUA selection + Emacs kill/yank/isearch +chords). No profile object, no bundle format, no selection mechanism, no +per-profile defaults layer (§11's missing profile scope is the same +gap). Prerequisites: the config profile layer, and enough registry +adoption that a profile has something declarative to set. + +--- + +## 13. Package Experience, Not Merely Package Resolution + +Pmacs already has serious package-resolution machinery. Product +coherence requires a package *lifecycle* experience: search, +installation, updates, disable, reload, uninstall, version inspection, +dependency graph, compatibility warnings, capability declarations, +ownership inspection, error history, active-worker inspection, resource +use, trust state. Installation should work during a running session. +Users should be able to install coherent capability bundles ("Rust +Development") rather than individual packages. Marketplace sequencing: +stable format → ownership/reload lifecycle → in-editor manager → curated +registry → bundles → publisher identity → public marketplace. + +### Ground truth + +**Grade: resolution without lifecycle — the artifact layer is mature, +the lifecycle layer assumes a single author iterating on their own +package.** + +**Mature (keep):** `pmacs.toml` manifest (validated name, semver, +`pmacs_required`, dependencies/conflicts, entry, exports); git-address +installs (`github:`/`gitlab:`/URL; auth delegated to git config; no +registry service); iterate-to-fixed-point resolver with deterministic +ordering and honest unsatisfiability errors (documented no-backtracking +tradeoff); merged SHA-256 lockfile; per-package `_ENV`; `exports` +enforced by a position-1 searcher; bundled packages through the +identical path. + +**The lifecycle facts:** + +| Operation | State | +|---|---| +| `install` / `install_project` / `install_local` / `update` | exist, **init.lua-only** — `require_init_phase` raises `InitOnlyApi` mid-session; the error text admits there is no CLI equivalent ("restart with an updated init.lua") | +| `reload(name)` | **works in-session and is well-built**: unload hooks → loaded-table invalidation (name + `name.` prefixes) → env clear → re-require | +| `installed()` / `describe(name)` / `load(name)` / `on_unload(fn)` | work in-session; `describe` returns manifest metadata only | +| uninstall / remove | **absent** — the documented procedure is `rm` in a shell (`src/packages/installer.rs:1178-1180`) | +| disable / enable | **absent** — no concept | +| search / list-available | **absent** — no registry, no index; you must already know a git URL | +| inspect contributions | **absent** — `describe` cannot say which commands/hooks/keys/settings a package contributed; no `*packages*` view exists | + +**Structural findings that any lifecycle arc must address:** + +- **The roster is in-memory per session**, rebuilt from `init.lua` + calls. A package on disk that init.lua doesn't `install` is invisible + to `require`/`installed()`. And because `do_install` unconditionally + runs the resolver, **every startup runs `git fetch --prune --tags` + per package** before the idempotent fast-path can trigger — a + first-launch latency and offline-use problem. (The Rust-side + `UpdatePolicy::Frozen` that would fix offline installs is + **unreachable from Lua** — dead code.) +- **Ownership is not tracked.** `SourceLocation` is path attribution, + not package attribution (for `install_local` the path may be the dev + tree, not the install root); nothing indexes registrations by source; + there is no "what did package X register" query and no + bulk-unregister. The correct signal (`CurrentlyLoadingPackage`) + already exists and is ignored by every registrar (§1.1). +- **The teardown surface is incomplete in a way that makes the + documented convention unsatisfiable: `pmacs.hook.remove` does not + exist** (`install_hook_module` exposes define/add/list/run; + `HookRegistry` has no removal method at all). A package that calls + `pmacs.hook.add` leaks a callback on every reload, permanently. The + package-author guide's hand-rolled `OWNED = {}` cleanup pattern + (`docs/package-author-guide.md:379-415`) cannot be followed for + hooks. (Independently rediscovered by the Lean 4 arc scout.) +- **Error attribution exists on exactly one code path** — + `packages.load` wraps require and logs `[package ] load failed` + to `*errors*` — **and nothing in `builtin/` uses it**. Plain + `require` from init.lua attributes only by traceback; a failing + `install` aborts the whole init.lua with no per-package isolation. +- Install-root directory names are the manifest name's last segment, so + same-basename packages collide on disk (knowingly accepted, + `installer.rs:44-50`). + +Sequencing: ownership + `hook.remove` + attribution is the same work as +§10's class 10.2 and is the prerequisite for disable/uninstall/inspect; +in-session install requires reworking the init-phase gate; search/ +bundles/marketplace remain correctly last. + +--- + +## 14. Coherent Workbench Primitives + +Pmacs should resist implementing each subsystem with a custom UI +vocabulary. It should provide a small set of reusable view primitives — +editable text view, virtual list, tree, structured table, inspector, +output channel, diagnostics collection, task/progress view, diff view, +transient selector, contextual popup, side panel, bottom panel, +help/documentation view — and packages should provide structured models +to them. Git status, project files, symbol outlines, package +dependencies, and worker trees should share one tree model with +consistent selection, expansion, filtering, action discovery, mouse and +keyboard behavior, persistence, and accessibility. + +### Ground truth + +**Grade: partial, with the best trajectory of any concern.** + +Primitive-by-primitive against the list above: + +- **Editable text view** ✓ — the buffer itself, everywhere. +- **List** ✓ — **listview is a real shared primitive**, the strongest + coherence asset in the UI layer: references, outline, buffer-list, + and project-search all use it, with a shared buffer-local keymap + idiom (RET/SPC visit, n/p, g refresh, q quit) that is inspectable and + rebindable (§6's counter-example). +- **Output channel** ✓ — the compile-mode `*compilation*` model + (streamed, intercept-read-only, error-rule parsing), reused by grep + and shell-command. +- **Diagnostics collection** ✓ — `DiagnosticStore` + signs + unified + `error.next` source. +- **Transient selector** ✓ — the minibuffer (though its `source` + vocabulary is fixed Rust-side). +- **Contextual popup** ✓ — completion popup, context menu (each a + shadow, §6). +- **Bottom/side panel** ✓ — landed as bottom-panel Stage 1 (#155): + `WindowParams` side/fixed_rows/dedicated, `display = "current" | + "panel"` adopted by listview/compile/terminal, quit-action, divider + drag. Stage 2 (GPU band) pending its own framing. +- **Task/progress view** △ — `*workers*` exists but joins nothing + (§9). +- **Help view** △ — exists twice (§5); needs unification, not + invention. +- **Tree** ✗ — none. The named future consumers (project files, symbol + hierarchy, package dependency graph, worker trees, git status) will + each need it; building it once *before* dired's directory view and + the workers tree harden their own conventions is exactly this + section's point. +- **Structured table / inspector / diff view** ✗ — none. (`describe.*` + tables are the inspector's data model without a view; the + wire-declared `ResourceOffer` family was reserved for diff/blame + sources and remains unproduced.) + +--- + +## 15. Contextual Affordances + +Pmacs should remain excellent for keyboard-driven users while making +capabilities visible to users who do not know their names: a diagnostic +should offer code actions; a test definition run/debug; a Git change +stage/revert/diff; a missing formatter configuration guidance; a symbol +references/rename/definition/documentation; a remote workspace its +location; a long-running task progress and cancellation. Affordances +should invoke ordinary commands, never separate logic paths. + +### Ground truth + +**Grade: weak.** + +What exists: the right-click context menu — 11 items in 4 groups +(edit/symbol/diagnostic/history, `builtin/menus/default.lua:117-142`), +with a closed context vocabulary (`always`/`selection`/`symbol`/ +`diagnostic`, `src/menu.rs:44`) and per-item predicates that *are* +evaluated (unlike command predicates). It correctly invokes ordinary +commands by name. Its limits: right-click only (no keyboard path in), +no key hints on rows, invisible items filtered rather than grayed, and +the unvalidated command references of §5. + +What does not: + +- **Code actions apply the first action blindly** — no picker (a + roadmap "dark matter" item still true at audit). +- **There is no Git integration at all** — no status, stage, diff, + blame, or gutter markers anywhere in the tree (gutter git riders and + the `ResourceOffer` diff/blame family are named deferrals). The Git + affordance list above has nothing to attach to yet. +- No test run/debug affordances (DAP is a future arc, + `docs/dap-debugging-framing.md`). +- No missing-tool guidance affordances (§1.2 — the diagnostic that + *should* say "rust-analyzer not found — install with rustup" says + nothing). +- No remote-location display (§8 — nothing carries a location). +- Task progress/cancellation affordances exist only inside `*workers*` + (§9); a long-running task shows nothing at the point of origin. + +--- + +## 16. Productize the Semantic Frontend Architecture + +The semantic protocol should be visible as a product advantage: native +frontend rendering, frontend-specific typography, high-quality +decorations, efficient incremental updates, accessible semantic +information, multiple simultaneous frontends, stable remote attachment, +frontend experimentation without reimplementing editor semantics. To +preserve coherence: core commands frontend-neutral; stable semantic +identities; explicit capability negotiation; graceful degradation; +layout state separated from semantic state; no frontend becoming the de +facto privileged implementation. + +### Ground truth + +**Grade: strong — the healthiest concern in this document, and most of +its asks are already practiced.** + +- Versioned, negotiated protocol `SUPPORTED=[6..=20]` with deliberate + encoding-breaking bumps, both-frontends support required per bump, + and byte-pin discipline for appended variants (handoff §4). +- Two genuine frontends share the conceptual model; CRDT concurrent + editing with presence across them; remote attach + reconnect. +- **Graceful per-frontend degradation is practiced, not aspirational**: + fold projection is per-frontend (`FrontendView.fold_projection`, + selected from the negotiated `semantic_render` bit) so a grid + frontend collapses folds while a simultaneous GPU session does not + skip lines (#149/#148). +- The GPU frontend exceeds the TUI (minimap, squiggles, typography) + without the TUI losing the model — the "no privileged frontend" rule + is holding under real divergence pressure. + +Remaining, honestly small relative to the section's ambition: capability +negotiation is per-bit rather than a first-class declared capability +set; layout state vs semantic state separation is partial (window layout +is daemon-side; desktop restore under a daemon is unresolved, §2 step +12); and the advantage is invisible as *product* because §17 means +nobody outside the repo can try it. + +--- + +## 17. Distribution Is Part of the Product + +Pmacs should eventually be installable without repository familiarity: +reproducible release builds, Linux and macOS binaries, checksums and +signatures, stable and nightly channels, one-command update, rollback, +protocol- and package-API compatibility reporting. First launch should +create/locate config directories, explain the default profile, identify +optional external tools, and let the user open a project immediately. + +### Ground truth + +**Grade: missing — zero release machinery exists.** + +`.github/workflows/` contains exactly one workflow, `ci.yml`, and it is +test-only (fmt/clippy/test matrix; the only `release` strings in it are +`cargo test --release` flags). No release job, no artifact upload, no +tags-to-binaries path, no checksums, no channels, no update or rollback +mechanism. Installation is `git clone` + `cargo build --release +--workspace --features pmacs/crdt` (README), which additionally requires +knowing the feature-flag matrix (luajit vs lua54 × crdt). Runtime +dependencies (`/bin/sh`, `stty`, git, tar) are documented in the README +and never checked at runtime. First launch creates nothing and explains +nothing (§18) — though by design it also *requires* nothing (§3), which +is the right half to have. + +This concern is independent of every other arc and can start anytime; +until it does, every other coherence improvement is invisible outside +the repository. + +--- + +## 18. Onboarding + +Pmacs needs onboarding that teaches concepts through use: open a +project → command palette → find a file → terminal → inspect a +diagnostic → view workers → change a setting → inspect where it came +from → Lua REPL → redefine a command. That sequence communicates the +whole thesis: already useful, discoverable, visible computation, +explainable settings, programmable internals. It should be an ordinary, +restartable help workspace, not a one-time modal wizard. + +### Ground truth + +**Grade: missing entirely.** + +No welcome buffer, no tutorial, no first-run detection, no cheat sheet +reachable from inside the editor (`docs/keybindings.md` exists on disk +only). `C-h` is `buffer.delete-word-backward`; there is no help prefix +key and no `F1`. The sole discovery affordance is knowing to press +`M-x` (`builtin/keymaps/default.lua:141` — whose own header comment +calls it the "command palette"). The empty `*scratch*` buffer that +greets a new user says nothing (`EditorCore::new` sets an empty +status). + +Note the dependency: five of the ten onboarding steps above currently +lead somewhere broken or invisible (find a file — in flight; inspect a +diagnostic — silent-failure risk; view workers — undiscoverable; +setting provenance — unanswerable). Onboarding is correctly sequenced +*after* the P1/P4 fixes, but the cheap floor — a welcome buffer in +`*scratch*` naming `M-x`, the keybinding cheat sheet as a help buffer, +and a help prefix decision — has no prerequisites at all. + +--- + +## 19. Product Coherence Acceptance Tests + +Pmacs should add acceptance tests that exercise product behavior across +subsystems, complementing (not replacing) subsystem tests: + +- **Installation/first launch** — no config, open a directory, usable + workspace, actionable guidance for missing tools. +- **Command discovery** — search by title and synonym; display + keybinding, provenance, availability; invoke from palette and menu + through the same object. +- **Workspace lifecycle** — multi-root open, servers, terminal, build, + close, restore, ownership cleanup. +- **Worker ownership** — start completion/search/build, inspect, + cancel a parent, confirm child cancellation and UI recovery. +- **Package lifecycle** — install in-session, inspect contributions, + disable, confirm disappearance, reload, uninstall cleanly. +- **Remote execution** — attach to remote daemon, edit optimistically, + remote terminal and server, disconnect/reconnect, coherent state. + +### Ground truth + +**Grade: missing — but the culture that would make them excellent is the +project's strongest process asset.** + +Zero cross-subsystem journey tests exist. Every acceptance suite in the +tree pins one subsystem's contract (superbly — bite-verified, +falsified-by-revert, vacuity-checked). Several of the scenarios above +are currently *untestable* because the behavior doesn't exist (install +in-session, disable, open a directory); the ones that are testable +(first launch, command discovery, worker cancellation, remote +attach/reconnect) could be written today and would immediately pin the +journey against regression. The first coherence acceptance suite should +be the §2 journey itself, growing a step at a time as steps become +real — that is how "the journey is a release gate" stops being +aspirational. + +(Related lesson already in the handoff: `compile_mode_acceptance` +accidentally reads the real user config — an *unintentional* +whole-product test that keeps catching real coherence bugs. That is +evidence this class of test has teeth.) + +--- + +## 20. Recommended Priority Order + +Each priority is annotated with its audited state and whether the gap is +**wiring** (surface over existing machinery — cheap) or **model** (a +missing runtime entity — a real arc). + +### Priority 1: Protect the golden product journey + +Establish the end-to-end workflow; treat regressions as release +blockers. **State: broken at step 3 (§2). Mostly wiring, and unusually +cheap:** directory-argument handling; a find-file surface (in flight, +PR #162); surfacing the LSP spawn failure with guidance (§1.2); a +compile keybinding + `cargo build`/`test` default from the existing +`ProjectKind::Cargo`; a terminal keybinding; a welcome buffer. The +journey acceptance suite (§19) is the ratchet that keeps it fixed. + +### Priority 2: Make workspace and location explicit + +Otherwise project, LSP, remote, task, and persistence accumulate +incompatible ownership models — the audit confirms four have already +diverged (§7). **State: missing; first slice in flight (multi-root LSP +affinity). Model gap:** the Workspace entity (§7), then Location values +(§8). This is the long-lead arc; start it before the fifth and sixth +subsystems grow their own root conventions. + +### Priority 3: Strengthen extension ownership and isolation + +**State: missing; prerequisite-shaped. Model gap, with one bug-sized +prerequisite: `pmacs.hook.remove` does not exist (§13).** The work +unit: registrations carry their owning package (the +`CurrentlyLoadingPackage` signal already exists), removal APIs complete +the set, error attribution becomes default rather than opt-in. This +single arc unblocks §13's disable/uninstall/inspect, §10's class 10.2, +and package-scoped task cancellation in §9. + +### Priority 4: Unify discovery + +**State: substrate without surface. Almost pure wiring — the best +payoff-per-effort in this document (§5):** a dozen interactive commands +over existing introspection, richer M-x rows (the wire pattern already +exists), title/category on `Command`, predicate evaluation, help-layer +unification, a help prefix key. Most of P1's "understand the interface" +and §18's floor ride on this. + +### Priority 5: Finish the workbench convergence + +**State: partial and moving (§14) — bottom panel Stage 1 landed, GPU +band pending; listview proven.** Remaining: 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). + +### Priority 6: Productize configuration + +**State: foundation only (§11). Model-lite:** value provenance in the +registry, then layering (profile/workspace scopes — depends on P2 for +workspace, §12 for profiles), then adoption migration (table-valued +settings are the hard prerequisite), then persistence. + +### Priority 7: Build package lifecycle UX + +**State: not started; correctly sequenced after P3.** In-session +install (init-gate rework), disable/uninstall over P3's ownership, +`*packages*` view over P5's primitives, then bundles and registry +sequencing per §13. + +### Priority 8: Ship binaries and release channels + +**State: zero (§17). Independent of everything — can start anytime.** +The editor becomes testable by users who are not repository +contributors; every other priority's value is invisible until this one +exists. + +### How this maps to arcs + +Candidate arc cuts, honoring one-feature-one-branch-one-PR and the +framing workflow (each needs its own scout + framing before any +implementation — this list is direction, not commitment): + +1. **Journey Stage 1** (P1): directory open + compile defaults + + LSP-failure surfacing + bindings + welcome buffer + the first + journey acceptance suite. Rides alongside the in-flight dired arc. +2. **Discovery surface** (P4): the describe/list/where-is command + family, M-x rich rows, help unification, help prefix. +3. **Transient keymap layer** (§6): the overlay scope + lifetime + handle + derived `dispatch_idle`, then migrate shadows one per PR. +4. **Extension ownership** (P3): `hook.remove`, owner-carrying + registrations, attribution-by-default. +5. **Worker identity** (§9): owner/purpose/parent on jobs and + processes, join the four planes, statusline activity indicator. +6. **Workspace entity** (P2): the object, then location values. +7. **Config provenance + adoption** (P6). +8. **Package lifecycle** (P7, after 4). +9. **Distribution** (P8, anytime). + +A standing process change accompanies all of them (§1.3): **every new +framing doc must state its coherence impact** — which journey steps it +touches, whether it adds an interaction island, whether its options +enter the config registry, whether its background work is attributed — +so the debt stops compounding silently. + +--- + +## 21. What Pmacs Should Borrow + +Proven adoption-cost reducers from successful modern editors, with +audited status: immediate usefulness (△ — editing yes, journey no); +strong defaults (✓ where they exist, §3); progressive disclosure (✗ +inverted, §4); searchable commands (△ names-only, §5); integrated +language tooling (✓ data layer / △ surface); project awareness (△ +conventions, §7); visible contextual actions (△ §15); coherent +task/terminal integration (✓ mechanics / ✗ visibility, §9); package +discoverability (✗, §13); configuration layering (△ foundation, §11); +remote development as core workflow (△ works, unmodeled, §8); smooth +distribution and updates (✗, §17); consistent interface primitives (△ +best trajectory, §14); explicit missing-tool guidance (✗ except +`--gpu`, §1.2). + +--- + +## 22. What Pmacs Should Preserve and Deepen + +Pmacs should not trade away the qualities that justify its existence — +and the audit confirms these are today's genuine strengths: live +programmability (redefine/unregister at runtime, per-package envs); +implementation inspectability (SourceLocation on every registration, +mandatory descriptions); replaceable interaction models (aspirational — +§6 is the gap); multiple genuine frontends and semantic rendering (✓, +§16 — the strongest concern); explicit parallel work with +cancellability and observability (mechanics ✓, product visibility ✗, +§9); remote daemon architecture (✓); user control over the editor as a +running system (✓). + +The goal is not to make pmacs less powerful so that it becomes +approachable. The goal is to make power **progressively available**. + +--- + +## 23. Product Thesis + +Emacs offers: *the editor is a programmable environment, and the user +may transform it completely.* VS Code offers: *the editor is already a +coherent development workstation, and extensions fill in the remaining +gaps.* Pmacs should offer: + +> **The editor is already an excellent workstation, and every part of +> that workstation remains inspectable, programmable, concurrent, and +> replaceable.** + +Its strongest distinctive proposition is not "Emacs in Rust" or "Emacs +with threads": + +> **Pmacs is a live-programmable editor in which computation, +> interfaces, ownership, and execution locations are explicit — allowing +> local, remote, interactive, and background work to coexist without +> freezing or becoming opaque.** + +The audit's one-line verdict on the thesis: **"without freezing" is +delivered; "without becoming opaque" is not yet true** — for the +failures a new user meets first (§1.2), for background work (§9), for +settings (§11), and for what a key will do while a modal surface is +active (§6). Product coherence is what will make the architecture +perceptible. Without it, pmacs risks becoming an impressive collection +of subsystems. With it, pmacs becomes a workstation whose complexity is +available without being imposed. + +--- + +## 24. Known documentation drift (as of 2026-07-25) + +Found during the audit; fix opportunistically, ideally before this +document is wired into CLAUDE.md/AGENTS.md as required reading: + +- `docs/keybindings.md` — every `src/editor.rs` line citation in §3 is + stale by ~250–1000 lines despite a "last verified @ `f8096ff` + (2026-07-20)" stamp; its shadow list also omits the terminal `C-c` + escape (reports 5 shadows, actual 6). +- `builtin/api/packages.lua` (EmmyLua annotations) — missing + `install_local`, `reload`, `load`, `describe`, `on_unload`; claims + `update` is unimplemented (it is implemented). +- `CHANGELOG.md` (~line 300) — claims a `describe-key` command for + self-introspection; no such command ever shipped (the Lua API + `pmacs.describe.key` exists; the interactive command does not). +- `docs/config-registry-framing.md` (~658) — claims `describe-setting` + renders through `src/help.rs`; it hand-builds its own text in + `builtin/commands/default.lua`. +- `src/workers_buffer.rs` module doc — says the completions ring caps + at 32; `COMPLETED_RING_CAP` is 64. +- `src/command.rs` doc comment on `predicate` — describes palette + gray-out behavior (T M2.7) that never shipped. + +--- + +## 25. Update protocol for this document + +- **When a PR changes any audited claim here, updating this file rides + that PR** — flip the grade, rewrite the fact, note the PR number. + Same discipline as `docs/agent-handoff.md`. +- Line numbers are hints; symbols are authoritative. When touching a + section anyway, re-verify its citations; do not let this document + accumulate the drift §24 catalogs in others. +- Grades change only with evidence (a landed PR, a re-audit), never + aspirationally. +- The **Ground truth** subsections are a snapshot dated 2026-07-25. If + a future comprehensive re-audit is performed, update the date in the + header and prune superseded facts rather than appending — this is a + briefing, not a log. +- Framing docs for coherence-affecting work should cite the section + they serve (e.g. "COHERENCE §6") and state their coherence impact per + §20's standing process change.