feat(hint): H2 Phase D — coverage gate, F1 strip, status flips (ADR-0053)

Completes H2:
- comprehensiveness coverage tests: every REGISTRY command form has a
  hint_id resolving to a hint.cmd.* block, and every runtime error class
  resolves to a hint.err.* block (enforces ADR-0053 D6)
- ADR-0051 keybinding strip advertises F1 in the editing (leads) and
  default states; +shortcut.hint label; 12 full-panel snapshots
  re-accepted (status-bar line only)
- flip ADR-0053 -> implemented, requirements H2 + A1 -> [x], README

2498 pass / 1 ignored, clippy clean.
This commit is contained in:
claude@clouddev1
2026-06-15 16:34:10 +00:00
parent 417cbc8df9
commit 447112b17f
21 changed files with 111 additions and 43 deletions
+50
View File
@@ -962,6 +962,56 @@ mod hint_key_tests {
// Unknown entry word → None (tier-2 fallback).
assert_eq!(hint_key_for_input_in_mode("zzz", Mode::Simple), None);
}
/// Comprehensiveness gate (ADR-0053 D6): every command form in the
/// REGISTRY carries at least one `hint_id`, and each resolves to a
/// tier-3 `hint.cmd.<id>` block. `keys.rs` checks referenced keys
/// resolve; this checks every command *has* one.
#[test]
fn every_command_form_has_a_tier3_block() {
let cat = crate::friendly::catalog();
for (node, _category) in super::REGISTRY {
assert!(
!node.hint_ids.is_empty(),
"command `{}` has no hint_ids (ADR-0053 D6)",
node.entry.primary
);
for id in node.hint_ids {
let key = format!("hint.cmd.{id}.what");
assert!(
cat.get(&key).is_some(),
"missing tier-3 block `{key}` for command `{}`",
node.entry.primary
);
}
}
}
/// Comprehensiveness gate (ADR-0053 D6): every runtime error class
/// `friendly::error_hint_class` can return resolves to a tier-3
/// `hint.err.<class>` block. Keep this list in sync with
/// `error_hint_class` (its own unit tests pin the outputs).
/// Diagnostic classes are deferred (issue #38), so not checked here.
#[test]
fn every_runtime_error_class_has_a_tier3_block() {
let cat = crate::friendly::catalog();
let classes = [
"unique",
"foreign_key.child_side",
"foreign_key.parent_side",
"not_null",
"check",
"type_mismatch",
"not_found",
"already_exists",
"generic",
"invalid_value",
];
for c in classes {
let key = format!("hint.err.{c}.what");
assert!(cat.get(&key).is_some(), "missing tier-3 error block `{key}`");
}
}
}
#[cfg(test)]
+1
View File
@@ -647,6 +647,7 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
("shortcut.confirm", &[]),
("shortcut.cycle", &[]),
("shortcut.del_word", &[]),
("shortcut.hint", &[]),
("shortcut.history", &[]),
("shortcut.home_end", &[]),
("shortcut.load", &[]),
+1
View File
@@ -1165,6 +1165,7 @@ shortcut:
browse: "browse"
clear: "clear"
complete: "complete"
hint: "hint"
history: "history"
home_end: "home/end"
del_word: "del word"
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 2836
assertion_line: 2839
expression: snapshot
---
╭ Output ──────────────────────────────────────────────────────────────────────╮
@@ -26,4 +26,4 @@ expression: snapshot
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 2819
assertion_line: 2822
expression: snapshot
---
╭ Output ──────────────────────────────────────────────────────────────────────╮
@@ -26,4 +26,4 @@ expression: snapshot
│for SQL │
╰──────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 2827
assertion_line: 2830
expression: snapshot
---
╭ Output ──────────────────────────────────────────────────────────────────────╮
@@ -26,4 +26,4 @@ expression: snapshot
│for SQL │
╰──────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 3442
assertion_line: 3445
expression: snapshot
---
╭ Output ────────────────────────────────────────────────────────────────────────────────╮
@@ -28,4 +28,4 @@ expression: snapshot
│ │
╰────────────────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 3388
assertion_line: 3391
expression: snapshot
---
╭ Output ────────────────────────────────────────────────────────────────────────────────╮
@@ -28,4 +28,4 @@ expression: snapshot
│ │
╰────────────────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 3378
assertion_line: 3381
expression: snapshot
---
╭ Output ────────────────────────────────────────────────────────────────────────────────╮
@@ -28,4 +28,4 @@ expression: snapshot
│ │
╰────────────────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 3431
assertion_line: 3434
expression: snapshot
---
╭ Output ────────────────────────────────────────────────────────────────────────────────╮
@@ -28,4 +28,4 @@ expression: snapshot
│ │
╰────────────────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 3457
assertion_line: 3460
expression: snapshot
---
╭ Output ────────────────────────────────────────────────────────────────────────────────╮
@@ -28,4 +28,4 @@ expression: snapshot
│ │
╰────────────────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 2880
assertion_line: 2882
expression: snapshot
---
╭ Output ──────────────────────────────────────────────────────────────────────╮
@@ -26,4 +26,4 @@ expression: snapshot
│insert into <Table> [(<col>[, ...])] [values] (<value>[, ...]) │
╰──────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Esc clear · Ctrl-A/E home/end · Ctrl-W del word · Enter run
F1 hint · Esc clear · Ctrl-A/E home/end · Ctrl-W del word · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 2896
assertion_line: 2898
expression: snapshot
---
╭ Output ──────────────────────────────────────────────────────────────────────╮
@@ -26,4 +26,4 @@ expression: snapshot
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Esc clear · Ctrl-A/E home/end · Ctrl-W del word · Enter run
F1 hint · Esc clear · Ctrl-A/E home/end · Ctrl-W del word · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 3099
assertion_line: 3102
expression: snapshot
---
╭ Tables ──────────────────╮╭ Output ────────────────────────────────────────────────────────────────────────╮
@@ -26,4 +26,4 @@ expression: snapshot
│ ││for SQL │
╰──────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 2909
assertion_line: 2912
expression: snapshot
---
╭ Output ──────────────────────────────────────────────────────────────────────╮
@@ -26,4 +26,4 @@ expression: snapshot
│for SQL │
╰──────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -1,6 +1,6 @@
---
source: src/ui.rs
assertion_line: 3209
assertion_line: 3212
expression: snapshot
---
╭ Tables ──────────────────╮╭ Output ────────────────────────────────────────────────────────────────────────╮
@@ -26,4 +26,4 @@ expression: snapshot
│ Orders.customer_id ││for SQL │
╰──────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
Project: Term Planner
Ctrl-O sidebar · Tab complete · ↑ history · Enter run
Ctrl-O sidebar · Tab complete · ↑ history · F1 hint · Enter run
@@ -46,4 +46,4 @@ expression: snapshot
│with `mode advanced`, or prefix the line with `:` to run… │
╰──────────────────────────────────────────────────────────╯
Project: Term Planner
Esc clear · Ctrl-A/E home/end · Ctrl-W del word · Ente
F1 hint · Esc clear · Ctrl-A/E home/end · Ctrl-W del w
+9 -6
View File
@@ -1894,22 +1894,25 @@ fn status_bar_bindings(app: &App) -> Vec<(&'static str, String)> {
("Enter", crate::t!("shortcut.run")),
];
}
// 4. Editing — the input has text: surface the readline edit keys
// (ADR-0049). The highest-value subset stays within the width
// budget; Ctrl-K/U remain unadvertised muscle memory.
// 4. Editing — the input has text: F1 (the contextual hint for what
// you're typing, ADR-0053) leads, then the readline edit keys
// (ADR-0049). Ctrl-K/U remain unadvertised muscle memory.
if !app.input.is_empty() {
return vec![
("F1", crate::t!("shortcut.hint")),
("Esc", crate::t!("shortcut.clear")),
("Ctrl-A/E", crate::t!("shortcut.home_end")),
("Ctrl-W", crate::t!("shortcut.del_word")),
("Enter", crate::t!("shortcut.run")),
];
}
// 5. Default — empty input, Input focus.
// 5. Default — empty input, Input focus. F1 here expands on the most
// recent error, or points the user at getting started (ADR-0053).
vec![
("Ctrl-O", crate::t!("shortcut.nav")),
("Tab", crate::t!("shortcut.complete")),
("", crate::t!("shortcut.history")),
("F1", crate::t!("shortcut.hint")),
("Enter", crate::t!("shortcut.run")),
]
}
@@ -2664,7 +2667,7 @@ mod tests {
#[test]
fn strip_default_state_is_nav_complete_history_run() {
let app = App::new();
assert_eq!(strip_keys(&app), vec!["Ctrl-O", "Tab", "", "Enter"]);
assert_eq!(strip_keys(&app), vec!["Ctrl-O", "Tab", "", "F1", "Enter"]);
}
#[test]
@@ -2675,7 +2678,7 @@ mod tests {
app.input.push_str("create ta");
assert_eq!(
strip_keys(&app),
vec!["Esc", "Ctrl-A/E", "Ctrl-W", "Enter"],
vec!["F1", "Esc", "Ctrl-A/E", "Ctrl-W", "Enter"],
);
}