fix(hint): labelled tier-3 block format + snapshot (ADR-0053 /runda)

Final /runda found the rendered block was three bare unlabelled lines,
deviating from the approved exemplar format. Fix:
- emit_tier3_block now renders a `Hint` heading + aligned `What:` /
  `Example:` / `Concept:` lines (hint.block.* labels); concept stays muted
- lock the format with an insta snapshot (hint_block_insert)
- amend ADR-0053 D2/D4 + exemplars: drop the `Next:` line (tier-2 ambient
  already owns live position-awareness — user-confirmed), align exemplars
  to the shipped format

2499 pass / 1 ignored, clippy clean.
This commit is contained in:
claude@clouddev1
2026-06-15 16:45:47 +00:00
parent 447112b17f
commit 329adfc935
5 changed files with 82 additions and 29 deletions
@@ -113,7 +113,7 @@ into the output journal. (It must therefore be handled in `handle_key`
| Trigger | Buffer / state | Result | | Trigger | Buffer / state | Result |
|---|---|---| |---|---|---|
| **F1** | non-empty input | tier-3 hint for the command being typed, plus the live "expected next" (from the walker's `tail_expected` / parser `expected`) | | **F1** | non-empty input | tier-3 hint for the command being typed. (No "expected next" line — the always-on tier-2 ambient panel already shows it live; tier-2 owns position-awareness.) |
| **F1** | empty input, a recent error exists | tier-3 expansion of that error | | **F1** | empty input, a recent error exists | tier-3 expansion of that error |
| **F1** | empty input, no recent error | a short "getting started" pointer (press F1 while typing a command; `help` for the full list) | | **F1** | empty input, no recent error | a short "getting started" pointer (press F1 while typing a command; `help` for the full list) |
| **`hint`** (submitted) | a recent error exists | tier-3 expansion of that error (primary use) | | **`hint`** (submitted) | a recent error exists | tier-3 expansion of that error (primary use) |
@@ -205,14 +205,17 @@ mechanics (e.g. `quit`); `what` + `example` are always present.
### D4 — Rendering ### D4 — Rendering
Both surfaces render through one new renderer, `App::note_hint*` (sibling Both surfaces render through the `App::note_hint*` family (sibling of
of `note_help`/`note_help_topic`, `src/app.rs`), emitting a small framed `note_help`/`note_help_topic`, `src/app.rs`) via `emit_tier3_block`,
block into the `output` buffer as `OutputKind::System` with emitting into the `output` buffer as `OutputKind::System`: a **`Hint`
`OutputStyleClass::Hint` on the `what`/`concept` prose and `Neutral` on heading** followed by aligned **`What:` / `Example:` / `Concept:`** lines
the `example` line. The block is **persistent** (scrolls in the journal), (labels + heading from `hint.block.*`). The `concept` line is muted
unlike the transient ambient panel — pressing F1 is an explicit request (`OutputStyleClass::Hint`); the rest are plain. The block is
to *keep* the deeper guidance on screen. The bottom keybinding strip **persistent** (scrolls in the journal), unlike the transient ambient
(ADR-0051) advertises F1 in the editing/typing state. panel — pressing F1 is an explicit request to *keep* the deeper guidance
on screen. Its rendered shape is locked by an `insta` snapshot
(`hint_block_insert`). The bottom keybinding strip (ADR-0051) advertises
F1 in the editing (leading) and default states.
### D5 — "Most recent (runtime) error" state ### D5 — "Most recent (runtime) error" state
@@ -278,32 +281,31 @@ maintainer owns, content is produced in two stages:
**reviewable batches** (grouped by area: DDL, DML, app commands, **reviewable batches** (grouped by area: DDL, DML, app commands,
error classes), not one monolithic drop. error classes), not one monolithic drop.
### Exemplars (the style reference to approve) ### Exemplars (the style reference; shipped as the rendered format)
**Command (F1 live-input), `insert`:** **Command (F1 live-input), `insert`** (the rendered shape, locked by the
`hint_block_insert` snapshot — a `Hint` heading + aligned labels, no
`Next:` line since tier-2 owns position-awareness):
``` ```
Hint — insert Hint
What: Add one or more rows to a table. What: Add one or more rows to a table.
Example: insert into Customers values ('Ann', 'ann@x.io') Example: insert into Customers values ('Ann', 'ann@example.io')
Concept: A row is one record; each value lines up with a column, in Concept: A row is one record; each value lines up with a column, in
order. Columns typed serial/shortid fill themselves — leave order. Columns typed serial/shortid fill themselves — leave
them out. them out.
Next: a value list `(...)`, or `(col, ...) values (...)` to name columns
``` ```
(The "Next:" line is the live expected-set from the walker, shown only on
the non-empty-input F1 path.)
**Error (`hint` command), foreign-key child-side violation:** **Error (`hint` command), foreign-key child-side violation:**
``` ```
Hint — no parent row to point at Hint
What: The value you inserted into Orders.customer_id doesn't match What: The value you gave for the child column doesn't match any
any Customers row, so the foreign key has nothing to point at. parent row, so the foreign key has nothing to point at.
Example: First insert into Customers values ('Ann', ...) Example: First insert the parent (insert into Customers …), then the
Then insert into Orders values (..., 'Ann') child that references it.
Concept: A foreign key is a promise that every child points at a real Concept: A foreign key is a promise that every child points at a real
parent. The parent must exist first. To allow orphans on parent, so the parent must exist first. To allow orphans on
delete instead, set the relationship's `on delete` to delete instead, set the relationship's `on delete` to
`set null` or `cascade`. `set null` or `cascade`.
``` ```
@@ -311,7 +313,7 @@ Hint — no parent row to point at
**Command (F1 live-input), `add 1:n relationship`:** **Command (F1 live-input), `add 1:n relationship`:**
``` ```
Hint — add relationship Hint
What: Link two tables so a parent row can own many child rows. What: Link two tables so a parent row can own many child rows.
Example: add 1:n relationship from Customers.id to Orders.customer_id Example: add 1:n relationship from Customers.id to Orders.customer_id
Concept: The "1:n" means one parent, many children. The child column Concept: The "1:n" means one parent, many children. The child column
+37 -6
View File
@@ -3205,17 +3205,32 @@ impl App {
/// polish (the framed block) lands with the corpus. /// polish (the framed block) lands with the corpus.
fn emit_tier3_block(&mut self, stem: &str) -> bool { fn emit_tier3_block(&mut self, stem: &str) -> bool {
let cat = crate::friendly::catalog(); let cat = crate::friendly::catalog();
if cat.get(&format!("{stem}.what")).is_none() { let what_key = format!("{stem}.what");
if cat.get(&what_key).is_none() {
return false; return false;
} }
self.note_system(crate::friendly::translate(&format!("{stem}.what"), &[])); // Labelled block (ADR-0053 D4): a `Hint` heading, then aligned
// `What:` / `Example:` / `Concept:` lines. `concept` renders
// muted (`OutputStyleClass::Hint`); the rest are plain system.
let labelled = |label: &str, value: &str| {
// Pad `<Label>:` to a common width so the values align.
format!(" {:<9}{value}", format!("{label}:"))
};
self.note_system(crate::t!("hint.block.heading"));
self.note_system(labelled(
&crate::t!("hint.block.what"),
&crate::friendly::translate(&what_key, &[]),
));
if cat.get(&format!("{stem}.example")).is_some() { if cat.get(&format!("{stem}.example")).is_some() {
self.note_system(crate::friendly::translate(&format!("{stem}.example"), &[])); self.note_system(labelled(
&crate::t!("hint.block.example"),
&crate::friendly::translate(&format!("{stem}.example"), &[]),
));
} }
if cat.get(&format!("{stem}.concept")).is_some() { if cat.get(&format!("{stem}.concept")).is_some() {
self.push_category_three_prose(crate::friendly::translate( self.push_category_three_prose(labelled(
&format!("{stem}.concept"), &crate::t!("hint.block.concept"),
&[], &crate::friendly::translate(&format!("{stem}.concept"), &[]),
)); ));
} }
true true
@@ -5813,6 +5828,22 @@ mod tests {
); );
} }
/// Locks the rendered tier-3 block format (ADR-0053 D4): a `Hint`
/// heading + aligned `What:` / `Example:` / `Concept:` lines.
#[test]
fn insert_hint_block_renders_in_the_labelled_format() {
let mut app = App::new();
type_str(&mut app, "insert into Customers ");
f1(&mut app);
let block = app
.output
.iter()
.map(|l| l.text.as_str())
.collect::<Vec<_>>()
.join("\n");
insta::assert_snapshot!("hint_block_insert", block);
}
#[test] #[test]
fn f1_on_add_relationship_renders_the_relationship_block() { fn f1_on_add_relationship_renders_the_relationship_block() {
let mut app = App::new(); let mut app = App::new();
+4
View File
@@ -224,6 +224,10 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
), ),
("hint.ambient_expected", &["expected"]), ("hint.ambient_expected", &["expected"]),
("hint.getting_started", &[]), ("hint.getting_started", &[]),
("hint.block.heading", &[]),
("hint.block.what", &[]),
("hint.block.example", &[]),
("hint.block.concept", &[]),
// Tier-3 teaching blocks (ADR-0053 D3) — Phase-B exemplars. // Tier-3 teaching blocks (ADR-0053 D3) — Phase-B exemplars.
("hint.cmd.insert.what", &[]), ("hint.cmd.insert.what", &[]),
("hint.cmd.insert.example", &[]), ("hint.cmd.insert.example", &[]),
+7
View File
@@ -391,6 +391,13 @@ hint:
# H2 / ADR-0053: shown by `hint` / F1 when there is nothing specific # H2 / ADR-0053: shown by `hint` / F1 when there is nothing specific
# to expand on (no recent error, empty input). # to expand on (no recent error, empty input).
getting_started: "Start typing a command and press F1 for a hint, or type `help` for the full command list." getting_started: "Start typing a command and press F1 for a hint, or type `help` for the full command list."
# Tier-3 block scaffolding (ADR-0053 D4): the heading + the labels the
# `what` / `example` / `concept` parts render under.
block:
heading: "Hint"
what: "What"
example: "Example"
concept: "Concept"
# ── Tier-3 teaching blocks (ADR-0053 D3) ────────────────────────── # ── Tier-3 teaching blocks (ADR-0053 D3) ──────────────────────────
# Per-form command hints (`hint.cmd.<form>`) and per-class error # Per-form command hints (`hint.cmd.<form>`) and per-class error
# hints (`hint.err.<class>`), each a `what` (12 sentences) / `example` # hints (`hint.err.<class>`), each a `what` (12 sentences) / `example`
@@ -0,0 +1,9 @@
---
source: src/app.rs
assertion_line: 5844
expression: block
---
Hint
What: Add one or more rows to a table.
Example: insert into Customers values ('Ann', 'ann@example.io')
Concept: A row is one record; each value lines up with a column, in order. Columns typed `serial`/`shortid` fill themselves — leave them out.