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:
@@ -132,30 +132,6 @@ fn no_column_list_full_arity_insert_persists() {
|
||||
assert!(csv.contains("full-arity"), "CSV reflects the row: {csv:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_appends_literal_line_to_history() {
|
||||
let (project, db, _dir) = open_project_db();
|
||||
let rt = rt();
|
||||
create_t(&db, &rt);
|
||||
// ADR-0030 §11: the literal submitted line lands in history.log.
|
||||
let source = "insert into T (a, b) values (1, 'logged')";
|
||||
rt.block_on(db.run_sql_insert(
|
||||
"insert into T (a, b) values (1, 'logged')".to_string(),
|
||||
Some(source.to_string()),
|
||||
"T".to_string(),
|
||||
Vec::new(),
|
||||
String::new(),
|
||||
false,
|
||||
))
|
||||
.expect("insert runs");
|
||||
let body = std::fs::read_to_string(project.path().join("history.log"))
|
||||
.expect("history.log present after an INSERT");
|
||||
assert!(
|
||||
body.contains(source),
|
||||
"history.log records the literal INSERT line: {body:?}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_insert_rolls_back_and_does_not_repersist() {
|
||||
let (project, db, _dir) = open_project_db();
|
||||
@@ -583,26 +559,6 @@ fn combined_serial_and_shortid_autofill() {
|
||||
assert_eq!(rows[0][2], "x", "name preserved: {rows:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn autofill_logs_original_source_not_rewritten_sql() {
|
||||
// ADR-0030 §11: even though the worker rewrites the executed
|
||||
// statement to bind synthesised shortids, history.log records
|
||||
// the user's original line verbatim.
|
||||
let (project, db, _dir) = open_project_db();
|
||||
let rt = rt();
|
||||
create_cols(&db, &rt, "t", &[("id", Type::ShortId), ("label", Type::Text)], &["id"]);
|
||||
let input = "insert into t (label) values ('x')";
|
||||
run_sqlinsert(&db, &rt, input).expect("auto-fill insert runs");
|
||||
let body = std::fs::read_to_string(project.path().join("history.log"))
|
||||
.expect("history.log present");
|
||||
assert!(body.contains(input), "original line logged: {body:?}");
|
||||
// The rewritten parameterised INSERT must not leak into history.
|
||||
assert!(
|
||||
!body.contains("INSERT INTO") && !body.contains("?1"),
|
||||
"rewritten SQL must not be logged: {body:?}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shortid_autofill_respects_mixed_case_column_name() {
|
||||
// ADR-0009 / 3d DA gate: identifiers are case-preserving. The
|
||||
|
||||
Reference in New Issue
Block a user