docs(context): core.md §21 third slice, test evidence, and two comment runs shortened (improvise-cq1q.13)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Claude (adr007-2c)
2026-10-09 12:55:41 -07:00
committed by Edward Langley
parent 5f741ee1da
commit bb7fe88547
5 changed files with 116 additions and 12 deletions
+25 -5
View File
@@ -1790,11 +1790,31 @@ beside the key and never in its place:
- The browser's render model keeps its field names and carries the display in them
(`RenderPageAxis.cat`, `RenderCatRow.label`), so `web/` is unchanged.
Still keys: `GridLayout::row_label`/`col_label` (commands and the status line
match on them), `edit_text`, the active-view marker, and everything status
messages and the view title say (improvise-cq1q.13). A display change alone does
not move `layout_fingerprint`, so it resets no cursor; no production path changes
one yet.
Still keys: `GridLayout::row_label`/`col_label` (commands match on them),
`edit_text` and the active-view marker. A display change alone does not move
`layout_fingerprint`, so it resets no cursor; no production path changes one yet.
**What the third slice covers (improvise-cq1q.13):** the view title and every
message that names a thing which exists. The title is the active view's display:
the TUI reads `app.view().display()`, the browser `ViewListCache::display_of`
(key when the list lacks the view). The statuses are the axis change (panel and
tile bar), `Filter:`, `Deleted category/item`, the formula-result and non-item
refusals, and the item-add prompt. Both ends say them through the same
`viewmodel` builders, so a wording is not spelled twice. The host reads
`Model::category_display`/`item_display`; the client reads
`CategoryTreeCache::category_display`/`item_display`. Each falls back to the key.
**A message that names something which does not exist says what was typed.**
`Unknown category 'x'`, `No view named "x"`, `View 'x' not found` and the
`workbook.rs` errors are lookup misses: there is no display to find, and the typed
text is what the person needs to correct. `Added category "x"` is optimistic and
names what was just typed, whose display equals its key at creation. A stray
coordinate in `non_item_refusal` is a key for the same reason; only the category
beside it is a display. **A display must never reach a lookup**: the item-add
mode keeps the category key (`AppMode::item_add_shown(key, shown)`) and the prompt
alone carries the display. A formula target has no display path yet
(`set_item_display` needs an explicit `_Measure` item), so `derived_refusal` reads
`item_display("_Measure", target)` and in practice says the key.
`AxisFrame::cat_displays` is projected and validated, but nothing paints it yet
(the corner label has no consumer); that is a gap to close when one does, not a
+72
View File
@@ -2143,3 +2143,75 @@ The display "Eastern Seaboard" against the key "E" gives 17, not the 4 minimum.
Rival: `col_groups` labels a run with `g.name`. The browser test for group displays
covered row groups only; this one folds a column frame whose group "Q1" is shown as
"First quarter" and fails on the key.
### draw::content_tests::the_grid_title_names_the_views_display_not_its_key
improvise-cq1q.13, TUI half. The view is displayed "Quarterly overview of regional
sales" under the key "Default", and the whole frame is drawn through `draw`. Rival:
`draw_content` passes `app.view_state.active_view` to the widget; the title reads
`View: Default` and this test fails on both assertions.
### render::tests::the_title_names_the_active_views_display_not_its_key
The browser half. Rival: `render_model` takes `view_name` from
`state.view.active_view`; the test fails. The second half is the control: a view
the list does not hold falls back to its key rather than to an empty title.
### resolve::display_tests::a_panel_status_names_what_a_person_sees_not_the_keys
The browser resolver's panel statuses: axis cycle, filter, delete category, delete
item. Displays are 36 to 38 characters against keys of 4 to 6. Rivals, each run:
the cycle status passes the key; the filter composes `SetPageSelection` with the
item's display; the item removal names the display. Each fails here, the last two
on the `Region/North` and `RemoveItem` assertions that pin the lookup to keys.
### resolve::display_tests::a_tile_status_names_the_display
Rival: the tile route passes `tile.category`; fails on the key.
### resolve::display_tests::the_item_prompt_names_the_display_and_the_mode_keeps_the_key
Rival: the mode is built with the display as its category
(`item_add_shown(shown, ..)`); the prompt reads right and the commit would add to
a category that does not exist. The test fails on `category == "Region"`.
### resolve::display_tests::a_refused_cell_names_the_displays_it_shows
Rival: the non-item refusal gets the category key. The derived arm is covered by
the item display for `_Measure`; the stray coordinate stays a key because it names
no item and has no display.
### command::cmd::panel::tests::a_panel_status_names_what_a_person_sees_not_the_keys
The host half of the panel statuses and the item-add prompt. Rivals, each run: the
axis status passes `cat_name` (fails); the filter status passes the item key
(fails); the item-add mode is built from the display (fails on the
`category: "Type"` assertion).
### command::cmd::tile::tests::a_tile_status_names_the_display_not_the_key
Rival: the status formats `name`. It fails on both the present display and the
absent key.
### command::cmd::cell::tests::a_refusal_names_the_formulas_display_not_its_key
A formula target has no display path today: `set_item_display` needs an explicit
`_Measure` item, so the fixture adds one named like the target. Rival:
`derived_refusal` skips the lookup; the test fails on "Operating profit...".
### ui::app::non_item_key_tests::a_non_item_refusal_names_the_categorys_display
Rival, run once per site: the refusal in `EditOrDrill` (the `i` key) and the one in
`CmdContext::value_cell_key` (the `p` key) each pass the key. Each fails here.
### ui::app::non_item_key_tests::a_commit_on_a_non_item_cell_names_the_categorys_display
The third site, `uneditable` in `cmd/core.rs`, which only a commit reaches once an
editor is open (`:enter-mode editing`). Rival: it passes the key; the two tests
above stay green and this one fails.
### ui::app::non_item_key_tests::a_refused_wire_write_names_the_displays_it_shows
The wire's own refusals in `Grids::unwritable`: the non-item arm and the formula
arm (once a third copy of the derived wording, now `derived_refusal`). Rivals: the
category key; a model with no display for the formula. Each fails.
+15
View File
@@ -5721,6 +5721,21 @@ mod non_item_key_tests {
assert!(status.ends_with(&refusal("Mars")), "{status}");
}
#[test]
fn a_commit_on_a_non_item_cell_names_the_categorys_display() {
const REGION: &str = "Sales territory of the whole company";
let mut app = paged_app("Mars");
let region = app.model_state.workbook.model.category_mut("Region");
region.unwrap().set_display(REGION);
press(&mut app, ":enter-mode editing\n");
press(&mut app, "5\n");
let said = &app.view_state.status_msg;
assert!(
said.ends_with(&improvise_protocol::non_item_refusal(REGION, "Mars")),
"{said}"
);
}
/// `uneditable` declines a non-item cell, so an advance re-opens no editor
/// on one (#265 meets #277). Evidence: `context/test-evidence/command.md`.
#[test]
+2 -4
View File
@@ -68,10 +68,8 @@ pub enum CellId {
NonItem { category: String, item: String },
}
/// Why a cell whose coordinate names no item takes no write. One wording for the
/// projection's decline and the host's refusal (improvise-gk51).
/// `category` is what a person sees for it; `item` is the stray coordinate as
/// stored, which has no display because it names no item.
/// Why a cell naming no item takes no write (improvise-gk51). `category` is a
/// display; `item` is the stray key, which has none (`core.md` §21).
pub fn non_item_refusal(category: &str, item: &str) -> String {
format!("{item} is no item of {category}, so there is no one cell to write")
}
+2 -3
View File
@@ -37,9 +37,8 @@ impl CatTreeEntry {
}
}
/// What a moved axis says on the status line. One home: the tile bar and the
/// category panel are two routes to one operation — improvise-25bf. Every
/// name here is a display: the status is read by a person, never matched on.
/// What a moved axis says on the status line: one home for the tile bar and the
/// panel (improvise-25bf). Takes displays; `core.md` §21.
pub fn axis_change_status(category: &str, axis: Axis) -> String {
format!("{category} → {}", axis.plain_label())
}