feat: ADR-0034 — history journal records err + replay parses/filters the journal

Replay (§3): run_replay parses <ts>|<status>|<source> journal records — runs ok, skips non-ok — while still accepting bare .commands scripts (prefix-detected so a | inside a bare command isn't misread). Fixes replay history.log, which died on line 1.

Journal failures (§1/§2): failed commands are recorded err via a new Action::JournalFailure, emitted by the pure-sync App for both parse failures and worker-execution failures (runtime appends best-effort, never fatal). Hydration reads all records so typo'd/rejected commands are recallable across sessions.

Amendment 1 — replay filters app-lifecycle commands: a working replay history.log exposed that the journal also records save as/load/new/export/import/rebuild/mode (which would panic the worker dispatch or abort replay). Replay now re-applies only schema/data writes and skips every app-lifecycle command + nested replay, classified by entry word so modal/incomplete forms (save as, bare mode) and quit skip uniformly rather than aborting. All skips continue (reversing the nested-replay refusal); import and nested replay warn. replay.error_nested removed; replay.skipped_import/_replay added; ReplayCompleted carries warnings. requirements.md U3/U4 updated; app-command runtime-failure journalling tracked as a follow-up.

1659 passing / 0 failing / 0 skipped / 1 ignored. Clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-24 18:59:06 +00:00
parent 504c24c996
commit e4f2f5fa15
18 changed files with 730 additions and 76 deletions
+7 -1
View File
@@ -108,7 +108,12 @@ fn typing_invalid_simple_input_shows_a_parse_error_not_an_echo() {
let theme = Theme::dark();
type_str(&mut app, "hello world");
let actions = submit(&mut app);
assert!(actions.is_empty());
// The failed line journals `err` (ADR-0034) but does not echo
// or dispatch a command.
assert!(
matches!(actions.as_slice(), [Action::JournalFailure { .. }]),
"expected only a JournalFailure; got {actions:?}",
);
let rendered = rendered_text(&mut app, &theme, 80, 24);
assert!(
rendered.contains("parse error"),
@@ -605,6 +610,7 @@ fn dsl_failure_shows_friendly_error_in_output() {
kind: rdbms_playground::db::SqliteErrorKind::NoSuchTable,
},
facts: rdbms_playground::friendly::FailureContext::default(),
source: String::new(),
});
let rendered = rendered_text(&mut app, &Theme::dark(), 80, 24);
assert!(