docs(discovery): finalize Stage 1 framing review

Correct the M-x prompt census: six of the eleven canonical commands
need a second prompt, including describe-command. Make the apropos
substring negative discriminate against fuzzy matching with a concrete
non-contiguous fixture and a no-substring precondition. Update the help
index mutation to the twelfth canonical command, and carry all three
corrections into the active-work ledger.
This commit is contained in:
Levi Neuwirth 2026-07-31 18:28:35 -04:00
parent f4359f12b4
commit 513a7dfa58
2 changed files with 45 additions and 18 deletions

View File

@ -380,14 +380,15 @@ which would have re-conflicted on every merge.
githubsucks/journey-stage1b3-welcome
```
## Discovery Stage 1 (P4) — FRAMING APPROVED (rev 5); implementation next
## Discovery Stage 1 (P4) — FRAMING APPROVED (rev 6); implementation next
- **Branch `discovery-stage1-commands`**, worktree `../pmacs-p4-discovery`,
based on `githubsucks/main` @ `54a092e`. **Framing only; no code, no
PR yet.** `docs/discovery-stage1-command-family-framing.md` revision 5,
PR yet.** `docs/discovery-stage1-command-family-framing.md` revision 6,
three review rounds closed (round 1: two blocking, two major; round 2:
two blocking, two major; round 3: two factual corrections; all
accepted), and **Q#D2 / Q#D3 decided by the user**.
accepted), Q#D2 / Q#D3 decided by the user, and the final review's
acceptance corrections applied.
- **What it is.** `COHERENCE.md` §20 Priority 4 — "almost pure wiring,
the best payoff-per-effort in this document". **Eleven commands under
one `help.*` prefix**: nine new (describe-key/mode/hook/buffer,
@ -399,7 +400,10 @@ which would have re-conflicted on every merge.
- **`apropos` matches by SUBSTRING, not fuzzy** (Q#D3). `fuzzy_score`
is subsequence-based and descriptions are long sentences, so fuzzy
would match nearly every command. Pinned by a
subsequence-that-is-not-a-substring finding nothing.
`test.apropos-subsequence-fixture` whose `qzjx` letters occur as `q z
j x`, only after asserting no registered name or description contains
`qzjx` as a substring; it must find nothing, whereas fuzzy finds the
fixture.
- **It adds no Rust.** `pmacs.describe.*`, `pmacs.keymap.list()`,
`pmacs.command.list()` and `pmacs.config.list()` already return
everything needed, and `parse_completion_source` accepts a **Lua
@ -422,8 +426,11 @@ which would have re-conflicted on every merge.
corrected, repeated one PR later. The path is dispatch `M-x`
`editor.execute-command` → assert the selected candidate **before**
RET (`accept()` does `session.take()`) → accept → `invoke_interactive`.
Five of the nine commands open a **second** prompt the pins must drive
too; a pin that stops after the first RET has tested the palette.
Six of the eleven canonical commands (`help.describe-command`,
`help.describe-setting`, `help.describe-key`, `help.describe-hook`,
`help.where-is`, and `help.apropos`) open a **second** prompt the pins
must drive too; a pin that stops after the first RET has tested the
palette.
- **One owner for `*help*` writes — NOT a one-site migration.**
`src/help.rs` has semantic renderers for command/key/buffer/mode/
hook/view and **none for settings, lists or apropos**, and

View File

@ -1,11 +1,28 @@
# Discovery Stage 1 — the describe/list command family
**Status: framing, rev 5 — Q#D2 and Q#D3 decided; ready for implementation approval.**
**Status: framing, rev 6 — final review corrections applied; ready for
implementation approval.**
**Serves `COHERENCE.md` §5 (unify discoverability), §1.1 (substrate
without surface), §20 Priority 4.**
## 0. Revision history
- rev 6 (2026-07-31) — final review corrections applied.
- **The second-prompt census now includes `help.describe-command`.**
Six of the eleven canonical commands take an argument, not five:
`describe-command`, `describe-setting`, `describe-key`,
`describe-hook`, `where-is`, and `apropos`. The full M-x driver must
accept each command's second prompt before it has tested the command.
- **The negative substring pin now has a discriminating fixture.** It
registers `test.apropos-subsequence-fixture` with a description whose
`qzjx` letters occur only as the non-contiguous sequence `q z j x`, then
first asserts that no registered command name or description contains
`qzjx` as a substring. `help.apropos qzjx` finding nothing therefore
fails under a fuzzy implementation rather than passing as an ordinary
no-match.
- **The index-property arithmetic follows the eleven-command family.**
Its targeted mutation is now adding a twelfth canonical command
without indexing it.
- rev 5 (2026-07-31) — **Q#D2 and Q#D3 answered by the user**; no review
findings at `1cc9d96`.
- **Q#D2 → `help.*` is canonical, with two forwarders.**
@ -397,11 +414,11 @@ The pre-RET assertion is not decoration: `accept()` does
`session.take()`, so afterwards nothing about the accepted value
survives, and a selected candidate shadows typed text.
**Commands that take an argument open a SECOND prompt** (`where-is`,
`describe-key`, `describe-hook`, `describe-setting`, `apropos`). Those
pins drive that prompt too, and assert against it with the same
pre-accept discipline. A pin that stops after the first RET has tested
the palette, not the command.
**Six of the eleven canonical commands take an argument and open a SECOND
prompt** (`describe-command`, `describe-setting`, `describe-key`,
`describe-hook`, `where-is`, `apropos`). Those pins drive that prompt too,
and assert against it with the same pre-accept discipline. A pin that stops
after the first RET has tested the palette, not the command.
### 4.1 Pins
@ -418,11 +435,14 @@ the palette, not the command.
4. **N — `apropos` matches descriptions, not only names, and does so by
substring.** Two assertions: a word appearing in exactly one
command's *description* and no command *name* finds that command
(what distinguishes apropos from a name filter); and a
**subsequence that is not a substring** — letters present in order
but not contiguous — finds **nothing**. The second is what pins
Q#D3's decision rather than leaving matching semantics to whatever
the implementation reaches for.
(what distinguishes apropos from a name filter); and a deliberately
discriminating negative: register
`test.apropos-subsequence-fixture`, whose description's `qzjx` letters
occur only as the non-contiguous sequence `q z j x`, assert that **no**
registered command name or description contains `qzjx` as a substring,
then assert `help.apropos qzjx` finds **nothing**. A fuzzy implementation
finds the fixture, so this pins Q#D3's substring decision rather than
passing as an ordinary no-match.
5. **N — `describe-setting` completes, and a non-matching typo still
reaches the existing error path.** Two assertions, because §3.2 has
two outcomes: (a) typing a real setting's prefix makes it the
@ -432,7 +452,7 @@ the palette, not the command.
*Rev 1 asserted a typo "cannot reach `on_accept`", which
`resolve_accepted_value` contradicts.*
6. **N — `M-x help` lists the family.** A property over the family list,
so adding a tenth command without indexing it fails.
so adding a twelfth canonical command without indexing it fails.
7. **P — every command's `*help*` write goes through `_show_help`.**
Replace that function with a counting stub, drive all **eleven**
through §4.0's path, and assert the count equals eleven. Pins §3.4's