feat: ADR-0035 Amendment 1 follow-up — enrich replay errors + close message gaps

- F2-broad: replay failures now render with real schema context instead of
  a contextless friendly_message(). Extract App::build_translate_context into
  the shared App::translate_context_for(command, facts, verbosity); run_replay
  enriches via enrich_dsl_failure + that builder. ctx_* fallbacks degrade to
  neutral prose so the rare non-replay contextless callsites can't leak raw
  {name} either. (SQL INSERT/UPDATE values aren't retained — ADR-0033 verbatim
  — so those show real table/column + neutral "that value".)
- Gap C: SQL ALTER … ADD FOREIGN KEY on a missing child column refuses with an
  SQL-appropriate "add it first", not the DSL-only --create-fk flag.
- Gap B: dropping a single-column-UNIQUE column refuses with a pointer to
  `drop constraint unique from T.col` (was an opaque generic refusal).
- Gap D: 4e drop/rename CHECK-guard + 4f change-type FK-guard refusals reworded
  to explain why; static_refusal reasons left as-is.

Tests: +4, 3 strengthened. 1926 pass / 0 fail / 0 skip; clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-26 18:30:31 +00:00
parent cb8ff8a7c2
commit f8a91f41c9
8 changed files with 328 additions and 41 deletions
+24 -12
View File
@@ -1559,20 +1559,32 @@ impl App {
));
}
/// Construct a [`TranslateContext`] by combining the
/// runtime-supplied [`FailureContext`] (schema-resolved
/// facts) with the operation derived from the originating
/// [`Command`] and the App's current verbosity.
///
/// Schema-resolved facts win over Command-derived
/// fallbacks where the runtime supplied them — typically
/// the runtime knows more (the FK-relationship lookup
/// produces `parent_table` that the Command alone can't
/// reveal).
/// Construct a [`TranslateContext`] from a [`Command`] + schema-
/// resolved [`FailureContext`], using the App's current verbosity.
/// Thin wrapper over [`Self::translate_context_for`], which is shared
/// with the replay path (it supplies its own verbosity — ADR-0035
/// Amendment 1, F2 follow-up).
fn build_translate_context(
&self,
command: &Command,
facts: crate::friendly::FailureContext,
) -> crate::friendly::TranslateContext {
Self::translate_context_for(command, facts, self.messages_verbosity)
}
/// Combine the runtime-supplied [`FailureContext`] (schema-resolved
/// facts) with the operation derived from the originating [`Command`]
/// and an explicit `verbosity`. Schema-resolved facts win over
/// Command-derived fallbacks where the runtime supplied them
/// (typically the FK-relationship lookup yields a `parent_table` the
/// Command alone can't reveal). Shared by interactive rendering and
/// the replay failure path (ADR-0035 Amendment 1, F2 follow-up), so a
/// replayed failing command shows real names instead of leaking
/// `{name}` placeholders.
pub(crate) fn translate_context_for(
command: &Command,
facts: crate::friendly::FailureContext,
verbosity: crate::friendly::Verbosity,
) -> crate::friendly::TranslateContext {
use crate::dsl::{AlterTableAction, Command as C, IndexSelector, RelationshipSelector};
use crate::friendly::{Operation, TranslateContext};
@@ -1714,7 +1726,7 @@ impl App {
// An `explain` failure (e.g. unknown table) is best
// described by the wrapped query it failed to plan.
C::Explain { query } => {
return self.build_translate_context(query, facts);
return Self::translate_context_for(query, facts, verbosity);
}
// App-lifecycle commands never reach this path —
// `dispatch_input` routes them through
@@ -1741,7 +1753,7 @@ impl App {
value: facts.value,
diagnostic_table: facts.diagnostic_table,
check_rule: facts.check_rule,
verbosity: self.messages_verbosity,
verbosity,
}
}