feat(history): mode-tagged history + top-of-chain journaling (#30)
Record the submission mode per history entry so advanced commands are reusable in simple mode, and fix the bug where a ':'-one-shot command lost its ':' across sessions (ADR-0052, closing #30). Format: the history.log status token gains an optional ':adv' suffix (ok / ok:adv / err / err:adv); 'source' stays last and canonical, so replay is unaffected. The in-memory ring (still Vec<String>) stores advanced entries ': '-prefixed; recall strips the ':' in advanced mode and keeps it in simple; hydration reconstructs the prefix from the tag. Journaling moved from the worker to the dispatch layer (spawn_dsl_- dispatch / run_replay / app-command sites), where the mode is in scope with no worker plumbing; finalize_persistence writes only yaml/csv (commit-db-last still atomic for state). The journal write is now best-effort (command already committed), consistent with the failure path. App commands journal simple, so they recall bare. Journaling is now uniform (every successful command, per ADR-0034) — closing a gap where show tables/relationships/explain didn't journal. Amends ADR-0034 (status tag + journaling location), ADR-0015 §6 (history.log out of the worker tx), ADR-0040 (journal-write best-effort). 15 worker-level journaling tests retired, re-covered at the new layer (history.rs format, app.rs recall matrix, iteration6 cross-session regression, replay). 2471 pass / 0 fail / 0 skip, clippy clean.
This commit is contained in:
@@ -141,9 +141,9 @@ fn read_recent_history_returns_appended_entries_in_order() {
|
||||
let tmp = tempdir();
|
||||
let project = Project::create_temp(tmp.path()).unwrap();
|
||||
let p = Persistence::new(project.path().to_path_buf());
|
||||
p.append_history("create table A with pk").unwrap();
|
||||
p.append_history("create table B with pk").unwrap();
|
||||
p.append_history("create table C with pk").unwrap();
|
||||
p.append_history("create table A with pk", false).unwrap();
|
||||
p.append_history("create table B with pk", false).unwrap();
|
||||
p.append_history("create table C with pk", false).unwrap();
|
||||
let entries = p.read_recent_history(10).unwrap();
|
||||
assert_eq!(
|
||||
entries,
|
||||
@@ -165,9 +165,9 @@ fn hydration_reads_both_ok_and_err_records() {
|
||||
let tmp = tempdir();
|
||||
let project = Project::create_temp(tmp.path()).unwrap();
|
||||
let p = Persistence::new(project.path().to_path_buf());
|
||||
p.append_history("create table A with pk").unwrap();
|
||||
p.append_history_failure("insert into A (1, 2, 3)").unwrap();
|
||||
p.append_history("show data A").unwrap();
|
||||
p.append_history("create table A with pk", false).unwrap();
|
||||
p.append_history_failure("insert into A (1, 2, 3)", false).unwrap();
|
||||
p.append_history("show data A", false).unwrap();
|
||||
let entries = p.read_recent_history(10).unwrap();
|
||||
assert_eq!(
|
||||
entries,
|
||||
@@ -194,6 +194,32 @@ fn seed_history_replaces_in_memory_history() {
|
||||
assert_eq!(app.history, vec!["x".to_string(), "y".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn advanced_command_journalled_then_hydrated_recalls_with_colon_in_simple() {
|
||||
// ADR-0052 (issue #30) — the headline cross-session regression: an
|
||||
// advanced command journalled `ok:adv`, then hydrated on a fresh
|
||||
// session, recalls WITH its `:` so it re-runs in simple mode. (Before
|
||||
// the fix, the `:` was lost on disk and the command came back bare.)
|
||||
let tmp = tempdir();
|
||||
let project = Project::create_temp(tmp.path()).unwrap();
|
||||
let p = Persistence::new(project.path().to_path_buf());
|
||||
// The dispatch layer journals the canonical source + advanced flag.
|
||||
p.append_history("select * from T", true).unwrap();
|
||||
p.append_history("create table T with pk", false).unwrap();
|
||||
|
||||
// Fresh session: hydrate the ring from disk.
|
||||
let entries = p.read_recent_history(10).unwrap();
|
||||
let mut app = App::new();
|
||||
app.seed_history(entries);
|
||||
|
||||
// In simple mode the simple command recalls bare, the advanced one
|
||||
// recalls `:`-prefixed (runnable via the one-shot escape).
|
||||
app.update(key(KeyCode::Up));
|
||||
assert_eq!(app.input, "create table T with pk");
|
||||
app.update(key(KeyCode::Up));
|
||||
assert_eq!(app.input, ": select * from T");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seed_history_preserves_chronological_order_for_navigation() {
|
||||
let mut app = App::new();
|
||||
|
||||
Reference in New Issue
Block a user