diff --git a/context/test-evidence/clients.md b/context/test-evidence/clients.md index 684a84ef..07045b49 100644 --- a/context/test-evidence/clients.md +++ b/context/test-evidence/clients.md @@ -1458,6 +1458,17 @@ is prose, and nothing checks it against the command it documents. That is a neighbour of improvise-wk18, not the same gap — wk18 is about `literal()` rows whose *keys* go unchecked; here the keys were right and the words were wrong. +**improvise-jhdf closed the paraphrase, and the paragraph above is what it +found.** Rewording Enter's caption to "Edit the cell (same as i)" — the same lie +in new words — passed both help tests. The test now asks by **command**, which +wk18's `help::help_bindings()` made possible: every Normal-mode help row whose +caption says "edit" in any case must run `edit-or-drill`. It fails on that +rival naming `(Normal, "enter-advance", None, "Edit the cell (same as i)")`. +Two controls stop it passing by looking away: `edit-or-drill` and +`enter-advance` must each still have a Normal row, or the filter has nothing to +catch. What it still cannot see is a false caption that avoids the word — "Opens +the cell" — which is the honest limit of any check over prose. + ### a_view_row_click_focuses_moves_and_switches_to_the_row_clicked improvise-iioi, and it is the **same trap** as the category click one level @@ -2071,6 +2082,50 @@ mode `declined` refuses at the door, so a browser never stands in front of the hint; the other two are improvise-gcvb's records-cell commit, which needs `record_col`. +**improvise-3t5x and improvise-wk18 widened it again, in two directions, and the +list above is superseded by the one in the test.** + +*What "answers" means.* It counted any `Local` as answered, so a lone +`SetStatus` refusal passed — which is why improvise-s8h7's false "Edit current +cell" stayed green. Injected to show it: make `edit_or_drill`'s `Record` arm +return only `SetStatus("RIVAL")`, so `i` refuses on every cell — the old guard +**passed**. The line drawn now: a caption is a promise, so an advertised key +must *act*. A refusal is legitimate — a feature a browser honestly lacks should +say so rather than go silent — but it is not what the caption says, so it has to +be **named** in the list with its reason, like a black hole. The test therefore +sorts every key into three outcomes, `Acts`, `Declines` (said something, did +nothing) and `Silent`, and lists the last two. The same injection now fails +naming `NORMAL: a/i edit-or-drill Declines`. + +*Judged by what moved, not by the variant.* The first version of the new +classifier called anything but an all-status `Local` an action, and a rival that +unhooked `jump-first-row` from `composed` **passed** — because a chord's second +key always returns `Local([SetTransientPrefix(None), ..])`, the disarm, even +when nothing else happens. That is the laundering this entry already warns +about, one level down: `KeyOutcome::of` launders an empty vec into `Ignored`, +and `chord` launders a no-op into a `Local`. The disarm is now ignored when +deciding whether a key did anything, and the same rival fails naming `NORMAL: +gg jump-first-row Silent`. + +*Which keys.* It read `help::advertised`, the status-bar table, and the help +overlay is a second body of advertising the browser also paints +(improvise-uln5). It now also reads `help::help_bindings()`, which records every +keys row the page builders ask for — the same builders `pages()` renders, so the +two cannot drift — including prefix sequences (`gg`, `yy`, `ZZ`), which it +presses by arming the prefix first. That is what added the five host-only +black holes (`/`, `n`, `H`, `z`, Normal's `Enter`) to the list: each was already +in `the_command_names_that_still_need_the_host_are_these`, and the help page +advertised it anyway. + +*Which cells.* `furnished` stood on a record cell only, so `i` on an aggregate +(s8h7's whole subject) was outside its reach. Normal mode is now pressed on a +record, an aggregate and a derived cell, and a non-record cell is reported only +where it changes the answer, so the list does not triple. `furnished_on` also +carries a history in both directions and a yanked value, so `<`, `,` and `p` +are not refused by an empty stack — state refusals are correct behaviour and +not this test's subject, the same argument that gave `furnished` three of +everything. + ### a_locally_committed_creating_column_agrees_with_the_host improvise-gcvb. `resolve`'s commit returned `None` for the whole of records diff --git a/context/test-evidence/protocol.md b/context/test-evidence/protocol.md index e0c49021..f22fc469 100644 --- a/context/test-evidence/protocol.md +++ b/context/test-evidence/protocol.md @@ -969,3 +969,35 @@ That assertion is the discriminating one: a fold arm that removed the key would give `None` and fail. Asserting only that `"command"` survived would pass under either behaviour, which is what the old spelling amounted to for the cleared key. + +### help::tests::no_help_row_spells_a_key_by_hand + +improvise-wk18. The help pages had one kind of row whose keys the keymap never +saw: `literal("gg", …)`, `literal("yy", …)`, `literal("ZZ", …)`, and a dim tip +reading "press Tab or l/n". Every other key row comes off `keys_for`, so a +rebinding reaches it; these would have gone on saying `gg` after `g g` stopped +meaning anything, and no guard could see them because a string carries no +command name. At HEAD the test failed naming all four. + +The fix is structural rather than a second scanner. The three prefix rows are +ordinary `keys(..)` rows now — `keys` also asks `Keymap::prefixed_keys_running` +for two-key sequences — and the tip is a `keys` row in `ModeKey::Help`. +`literal()` and `dim()` are gone; the only way left to build a +`HelpLine::Literal` is the `ex!` macro, which writes the leading colon itself, so +what remains hand-written is ex-command text — checked against the parser by +`every_ex_command_the_help_pages_show_is_one_the_parser_accepts`, which reads +exactly the text a user would type. This test is what stops a direct +`HelpLine::Literal { keys: "gg", .. }` coming back past the macro. + +### help::tests::every_keys_row_the_pages_ask_for_finds_its_keys + +A `keys` row whose lookup finds nothing is **dropped**, by design — a page never +advertises a key that does not exist. The cost of that design is that a broken +lookup looks like a shorter page: `every_help_page_key_row_names_keys_that_exist` +only checks the rows that survived, plus a floor of twenty. So this asks the +question from the other end, over `help_bindings()` — every row the builders +*asked for* — and requires each to find a key. + +The injection: comment out the `prefixed_labels` extension in `keys`. The three +prefix rows vanish from the page, every other help test stays green, and this +fails naming `Normal jump-first-row`, `Normal yank` and `Normal wq`.