docs(tree): revision 4 — a bound key that does nothing is not an absent key

"NO `g`" WAS LITERALLY FALSE, in revisions 2 and 3 both.
`bind_local_keymap` binds `g -> listview.refresh` on EVERY panel
unconditionally (listview.lua:147). What three of the four consumers
lack is an `on_refresh`; `listview.refresh` then returns immediately.

I had been collapsing three distinct facts into one word: whether `g` is
BOUND, whether refresh is ADVERTISED in the header, and whether refresh
is FUNCTIONAL. The §1.3a table now separates them, because a reader
checking "does the outline have g?" against the source would have found
the framing wrong and had no way to tell which claim was the intended
one.

The consequence is worth recording on its own: THE OUTLINE HAS A DEAD
REFRESH BINDING. `g` is bound, dispatched, and silently does nothing —
no status, no feedback. That is a small UX wart independent of anything
this framing proposes, and it is recorded rather than fixed here.

COHERENCE.md §14 IS CORRECTED IN THIS BRANCH rather than deferred to
implementation or split into its own lane. §25 is explicit that when a
PR changes an audited claim, updating the file RIDES THAT PR — #204
added `*lsp*` and did not update the "exactly three call sites"
measurement, so the correction rides the framing that found it. The
ad41cf1 audit fact is retained as history rather than overwritten, with
the current count of four and `*lsp*` named as the post-audit addition;
§25 also says symbols are authoritative and notes the line numbers have
drifted.

The §0 scorecard row carried the same "3 call sites" and moves with the
body. A grade table that disagrees with the section it summarizes is the
same defect one screen apart.

§14 also now records that `*lsp*` is the only one of the four with a
working refresh, and that the other three carry the dead binding —
which is what makes the tree framing's refresh-scoping conclusion sound
rather than lucky.

Framing only, still unapproved. COHERENCE change is a correction of an
existing audited claim, not a new grade.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Neuwirth 2026-08-05 13:53:28 +02:00
parent 932b3ab179
commit 5186bfd67a
No known key found for this signature in database
2 changed files with 64 additions and 17 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; **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** |
| 14 | Workbench primitives | **Partial (best trajectory)** | Listview is a real primitive but only **4** call sites, all LSP panels (`*lsp*` added post-audit by #204); 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 |
@ -1282,10 +1282,23 @@ Primitive-by-primitive against the list above:
buffer-local keymap idiom (RET/SPC visit, n/p, g refresh, q quit)
that is inspectable and rebindable (§6's counter-example). **But its
adoption is narrower than this document claimed, and the correction
matters more than the grade.** Measured at `ad41cf1`: there are
matters more than the grade.** Measured at `ad41cf1`: there were
exactly **three** `pmacs.listview.open` call sites, **all three in
`builtin/runtime/lsp.lua`** — `*references*` (`:2056`), `*outline*`
(`:2102`) and `*lsp-help*` (`:2513`). The three other `listview`
(`:2102`) and `*lsp-help*` (`:2513`).
**Updated: there are now FOUR.** Journey Stage 1b-2 (**#204**) added
`*lsp*` via `lsp.status` — the audited claim above changed and that PR
did not update it, so this correction rides the tree-primitive framing
that found it (§25). All four remain in `lsp.lua`; per §25 the
symbols are authoritative and the `ad41cf1` line numbers have drifted.
**`*lsp*` is the only one of the four with a working refresh** — it is
the only one supplying `on_refresh`. `g` is bound on all four
unconditionally by `bind_local_keymap`, so the other three carry a
**dead refresh binding**: bound, dispatched, silently does nothing.
The three other `listview`
mentions under `builtin/` are comments in `compile.lua` and
`dired.lua` citing "the listview idiom", which is a *pattern being
copied*, not the primitive being used.

View File

@ -1,13 +1,33 @@
# Framing — the tree primitive
**Revision 3.** Status: framing only, **not yet approved**. No
implementation. Scouted against `githubsucks/main` @ `12f2970`.
**Revision 4.** Status: framing only, **not yet approved**. Scouted
against `githubsucks/main` @ `12f2970`. **This revision also carries a
correction to `COHERENCE.md` §14** — see below.
**Revision 3 → 4**:
- **"No `g`" was literally false**, in revisions 2 and 3 both.
`bind_local_keymap` binds `g → listview.refresh` on **every** panel
unconditionally (`listview.lua:147`). What three of the four lack is
an `on_refresh`. The §1.3a table now separates **`g` bound**,
**refresh advertised** and **refresh functional**, because those are
three different facts and I had been collapsing them into one.
Consequence worth noting on its own: the outline has a **dead refresh
binding** — `g` is dispatched and silently does nothing, with no
status message.
- **`COHERENCE.md` §14 is corrected in this branch**, not deferred.
§25 requires an audited claim to be updated by the PR that changes
it; **#204 changed it and missed it**, so the correction rides the
framing that found it. The `ad41cf1` audit fact is retained as
history, with the current count of four and `*lsp*` named as the
post-audit addition. The §0 scorecard row moves with the body, since
it carried the same "3 call sites".
**Revision 2 → 3**, three further review findings, all verified in
source:
- **`*references*` has no `g` and no `on_refresh`** — revision 2 claimed
it twice. The consumer with refresh is **`*lsp*`** (§1.5a). The error
- **`*references*` has no `on_refresh`** — revision 2 claimed it had
refresh, twice. The consumer with refresh is **`*lsp*`** (§1.5a). The error
came from reading `listview.lua`'s **module-docstring example**, which
illustrates the API using `*references*` with `g refresh` in the
header. An example is not a consumer. *The refresh-scoping conclusion
@ -165,12 +185,24 @@ hazard the moment it becomes one.)*
three in `builtin/runtime/lsp.lua`" at `ad41cf1`. **There are four**, and
the fourth matters here:
| call site | panel | `on_visit` | `on_refresh` / `g` |
|---|---|---|---|
| `lsp.lua:2442` | `*references*` | yes | **no** |
| `lsp.lua:2488` | `*outline*` | yes | **no** |
| `lsp.lua:2924` | `*lsp-help*` | no | **no** |
| `lsp.lua:3004` | `*lsp*` (`lsp.status`) | no | **yes** |
| call site | panel | `on_visit` | `on_refresh` | `g` bound? | refresh advertised? | refresh FUNCTIONAL? |
|---|---|---|---|---|---|---|
| `lsp.lua:2442` | `*references*` | yes | no | **yes** | no | **no** |
| `lsp.lua:2488` | `*outline*` | yes | no | **yes** | no | **no** |
| `lsp.lua:2924` | `*lsp-help*` | no | no | **yes** | no | **no** |
| `lsp.lua:3004` | `*lsp*` (`lsp.status`) | no | **yes** | **yes** | **yes** | **yes** |
**`g` is bound on ALL FOUR.** `bind_local_keymap` binds
`g → listview.refresh` for every panel unconditionally
(`listview.lua:147`), so "no `g`" — which revisions 2 and 3 both said —
is **literally false**. What three of them lack is an `on_refresh`, and
`listview.refresh` returns immediately without one.
**So `g` on the outline is a DEAD BINDING**: bound, dispatched,
silently does nothing. That is a small UX wart in its own right — a key
that responds to nothing, with no status message — and it is a separate
observation from anything this framing proposes. Recorded, not fixed
here.
`*lsp*` arrived with Journey Stage 1b-2 (#204), after §14's audit. It is
**the only listview consumer with refresh at all**, which is why §1.5a's
@ -244,11 +276,13 @@ whether selection and expansion can survive a model update at all.
Revision 1 built two acceptance criteria on `g` refresh without checking
that the outline supports it. **It does not:**
- its header offers `RET visit n/p move q quit`**no `g`**
(`lsp.lua:2490`);
- its header offers `RET visit n/p move q quit`refresh is **not
advertised** (`lsp.lua:2490`);
- it supplies **no `on_refresh`**;
- and `listview.refresh` opens `if not (p and p.on_refresh) then return
end` — **a no-op** for this panel (`listview.lua:262`).
- `listview.refresh` opens `if not (p and p.on_refresh) then return
end` — **a no-op** for this panel (`listview.lua:262`);
- and `g` **is** bound regardless (§1.3a), so the outline has a
**dead refresh binding**, not an absent one.
So "collapse state survives `g`" was unreachable for the only consumer
that exists. **Refresh is therefore out of scope for this stage** unless