fix(hint): correct H2 corpus errors + add parse guard (handoff-71)
Semantic verification pass over the tier-3 `hint` corpus (ADR-0053). Four content errors corrected in src/friendly/strings/en-US.yaml: - cmd.create_table: the example `with pk id(serial), name(text), email(text)` declares a 3-column COMPOUND primary key, not a PK plus regular columns (every `with pk` column is a key member, ADR-0005). Rewritten to a single-column PK + `add column` for the rest; what/concept aligned. - cmd.save: `save as my-shop` does not parse — `save as` takes no inline name, it opens a path-entry prompt. Example -> `save as`; what no longer implies inline naming; added a temp-vs-named concept. - cmd.import: target `shop-copy` does not parse — the `as <target>` slot is a NewName ident that rejects hyphens. -> `shop_copy`. - err.foreign_key.child_side: dropped the bogus `on delete set null/cascade` remedy — that governs the parent direction; a child-side violation is fixed by inserting the parent first (matches the tier-1 hint). Adds every_cmd_hint_example_parses_in_its_mode — a catalog-driven guard that parses every hint.cmd.* example in its taught mode, backstopping syntactic drift (it caught the save and import errors). Registers the new hint.cmd.save.concept key. docs: drop two stale "deferred" entries from CLAUDE.md — project storage (export/import, --resume, input history, migration scaffold) and m:n convenience (C4) are all implemented (ADR-0015/0045); record the verification pass on requirements.md H2.
This commit is contained in:
@@ -1012,6 +1012,75 @@ mod hint_key_tests {
|
||||
assert!(cat.get(&key).is_some(), "missing tier-3 error block `{key}`");
|
||||
}
|
||||
}
|
||||
|
||||
/// Semantic-verification guard (handoff-71): every `hint.cmd.<form>`
|
||||
/// **example** must parse in the mode the form is taught for. This
|
||||
/// backstops the bug class found in the H2 corpus pass — an example
|
||||
/// that drifts out of the real grammar (a typo, a removed clause, or
|
||||
/// an argument the command never accepted, e.g. an inline name on
|
||||
/// `save as` which opens a modal instead). It cannot police the
|
||||
/// *semantics* of an example that happens to parse (that is the
|
||||
/// manual pass), but it locks the syntactic floor so future edits
|
||||
/// can't ship an unparseable teaching line.
|
||||
///
|
||||
/// The mode per form mirrors `hint_key_for_input_in_mode`: the
|
||||
/// advanced-SQL forms are taught in advanced mode; everything else
|
||||
/// (DSL + app commands) in simple mode.
|
||||
#[test]
|
||||
fn every_cmd_hint_example_parses_in_its_mode() {
|
||||
use crate::dsl::parser::parse_command_in_mode;
|
||||
use crate::mode::Mode;
|
||||
|
||||
// Advanced-mode forms — the SQL surface (ADR-0030–0039). Every
|
||||
// other form (DSL + app commands) is taught in simple mode. This
|
||||
// mirrors the mode split `hint_key_for_input_in_mode` resolves.
|
||||
const ADVANCED: &[&str] = &[
|
||||
"sql_create_table",
|
||||
"sql_alter_table",
|
||||
"sql_create_index",
|
||||
"sql_drop_index",
|
||||
"sql_drop_table",
|
||||
"sql_insert",
|
||||
"sql_update",
|
||||
"sql_delete",
|
||||
"select",
|
||||
"with",
|
||||
"explain_sql",
|
||||
];
|
||||
|
||||
// Iterate the *catalog* (the corpus is the source of truth), not the
|
||||
// REGISTRY: this reaches every `hint.cmd.<id>` block including any
|
||||
// not owned by a command node, so an orphaned or mis-keyed example
|
||||
// can't slip past the guard.
|
||||
let cat = crate::friendly::catalog();
|
||||
let mut checked = 0usize;
|
||||
for key in cat.keys() {
|
||||
let Some(id) = key
|
||||
.strip_prefix("hint.cmd.")
|
||||
.and_then(|rest| rest.strip_suffix(".example"))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let example = cat.get(key).expect("key came from the catalog");
|
||||
let mode = if ADVANCED.contains(&id) {
|
||||
Mode::Advanced
|
||||
} else {
|
||||
Mode::Simple
|
||||
};
|
||||
assert!(
|
||||
parse_command_in_mode(example, mode).is_ok(),
|
||||
"hint.cmd.{id}.example does not parse in {mode:?} mode: {example:?}",
|
||||
);
|
||||
checked += 1;
|
||||
}
|
||||
// Floor guard: the corpus had 49 command forms at the time of
|
||||
// writing (ADR-0053). If this drops, a block (and its example
|
||||
// coverage) silently vanished.
|
||||
assert!(
|
||||
checked >= 49,
|
||||
"expected at least 49 hint.cmd.* examples, checked {checked}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user