feat(app): log retention — keep_recent_runs + retention_days (Enhancement 4)

LoggingCfg gains two fields with serde defaults: keep_recent_runs
(default 100, top-N file retention) and retention_days (default 30,
time-based retention for both ndjson files and the SQLite mirror).

IngestLogWriter::open now runs cleanup_old_logs before creating a new
ingest-*.ndjson — delete iff (idx >= keep_recent) OR (modified <=
cutoff). ingest_with_config_opts also calls
SqliteStore::prune_pdf_ocr_events(retention_days) at ingest start so
the SQLite mirror tracks the same retention window.

Backward compat (AC-9): both new fields use #[serde(default = ...)],
so a pre-v0.20.x config with only [logging] ingest_log_enabled +
ingest_log_dir parses unchanged. kebab init writes the new defaults
automatically via Config::default() -> toml::to_string_pretty (AC-12).

docs/SMOKE.md config example synced.

Closure r1 F5: explicit OR-on-stale comment inside cleanup_old_logs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 06:17:47 +00:00
parent d9ec7b8dc3
commit 35c987df1c
8 changed files with 186 additions and 1 deletions

View File

@@ -42,3 +42,28 @@ fn pre_v020_config_without_logging_section_gets_defaults() {
assert!(w.logging.ingest_log_enabled);
assert_eq!(w.logging.ingest_log_dir, PathBuf::from("{state_dir}/logs"));
}
// Test 4 (AC-9 v0.20.x r2): old config with only ingest_log_enabled + ingest_log_dir
// parses without error and produces correct defaults for keep_recent_runs + retention_days.
#[test]
fn old_logging_config_parses_with_defaults() {
let toml = r#"
[logging]
ingest_log_enabled = true
ingest_log_dir = "{state_dir}/logs"
"#;
let w: LoggingWrapper = toml::from_str(toml).expect("old logging config must parse");
assert!(w.logging.ingest_log_enabled);
assert_eq!(
w.logging.ingest_log_dir,
PathBuf::from("{state_dir}/logs")
);
assert_eq!(
w.logging.keep_recent_runs, 100,
"keep_recent_runs must default to 100"
);
assert_eq!(
w.logging.retention_days, 30,
"retention_days must default to 30"
);
}