feat: learn prune --older-than for age-based learning cleanup #18

Open
opened 2026-04-24 14:20:36 +02:00 by product-owner · 0 comments

Problem Statement

The learning-capture-specification-interview.md spec (line 122-123) explicitly defines terraphim-agent learn prune --older-than 90d for manual cleanup, but no such subcommand exists. The spec deliberately chose "no automatic retention" and relies on this manual command as the escape valve. Without it, users who accept the "keep everything" default have no supported way to trim the store as it grows — they must delete files by hand, which risks breaking the companion KG-compiled-corrections thesaurus.

Acceptance Criteria

  • terraphim-agent learn prune --older-than <duration> subcommand exists
  • Duration parser accepts 90d, 12w, 6m, 1y (minimum 1 day; reject zero/negative)
  • Default is dry-run: prints what would be deleted, exits 0 without deleting
  • --yes (or --confirm) actually deletes the matching files
  • --global scopes to the global learnings directory
  • Learnings with a user-added correction are preserved regardless of age UNLESS --include-corrected is passed (corrections are the expensive artefact; safe default is keep)
  • Output (human): counts of files scanned, matched, deleted; exit code 0 on success, non-zero only on I/O errors
  • --json emits RobotOutput envelope with structured result
  • Unit tests cover: empty dir, no matches, mixed ages with correction-preservation rule, --include-corrected override, dry-run vs confirm
  • cargo test -p terraphim_agent passes
  • cargo clippy -p terraphim_agent -- -D warnings passes

Proposed Approach

Add Prune { older_than: String, yes: bool, global: bool, include_corrected: bool, json: bool } to LearnSub in crates/terraphim_agent/src/main.rs. Implement in a new crates/terraphim_agent/src/learnings/prune.rs module:

  • Parse duration string into chrono::Duration (tiny helper)
  • Walk the learnings dir, parse each file via existing CapturedLearning::from_markdown, compare captured_at to Utc::now() - duration
  • Skip files whose correction field is Some unless include_corrected is set
  • In dry-run, just print; with --yes, fs::remove_file each match
  • Return a PruneReport { scanned, matched, deleted, skipped_corrected } for JSON output

Affected crates: terraphim_agent only.

Dependencies

None.

Verification

cargo test -p terraphim_agent --lib learnings::prune
cargo clippy -p terraphim_agent -- -D warnings

# Dry-run exercise
cargo run -p terraphim_agent -- learn prune --older-than 30d
cargo run -p terraphim_agent -- learn prune --older-than 30d --yes
cargo run -p terraphim_agent -- learn prune --older-than 1y --json

Scope

~180 lines of new code (duration parser + prune module + wiring + 5 unit tests). Single PR. Safe defaults (dry-run, preserve corrections) keep blast radius small.

## Problem Statement The `learning-capture-specification-interview.md` spec (line 122-123) explicitly defines `terraphim-agent learn prune --older-than 90d` for manual cleanup, but no such subcommand exists. The spec deliberately chose "no automatic retention" and relies on this manual command as the escape valve. Without it, users who accept the "keep everything" default have no supported way to trim the store as it grows — they must delete files by hand, which risks breaking the companion KG-compiled-corrections thesaurus. ## Acceptance Criteria - [ ] `terraphim-agent learn prune --older-than <duration>` subcommand exists - [ ] Duration parser accepts `90d`, `12w`, `6m`, `1y` (minimum 1 day; reject zero/negative) - [ ] Default is dry-run: prints what would be deleted, exits 0 without deleting - [ ] `--yes` (or `--confirm`) actually deletes the matching files - [ ] `--global` scopes to the global learnings directory - [ ] Learnings with a user-added correction are preserved regardless of age UNLESS `--include-corrected` is passed (corrections are the expensive artefact; safe default is keep) - [ ] Output (human): counts of files scanned, matched, deleted; exit code 0 on success, non-zero only on I/O errors - [ ] `--json` emits RobotOutput envelope with structured result - [ ] Unit tests cover: empty dir, no matches, mixed ages with correction-preservation rule, `--include-corrected` override, dry-run vs confirm - [ ] `cargo test -p terraphim_agent` passes - [ ] `cargo clippy -p terraphim_agent -- -D warnings` passes ## Proposed Approach Add `Prune { older_than: String, yes: bool, global: bool, include_corrected: bool, json: bool }` to `LearnSub` in `crates/terraphim_agent/src/main.rs`. Implement in a new `crates/terraphim_agent/src/learnings/prune.rs` module: - Parse duration string into `chrono::Duration` (tiny helper) - Walk the learnings dir, parse each file via existing `CapturedLearning::from_markdown`, compare `captured_at` to `Utc::now() - duration` - Skip files whose `correction` field is `Some` unless `include_corrected` is set - In dry-run, just print; with `--yes`, `fs::remove_file` each match - Return a `PruneReport { scanned, matched, deleted, skipped_corrected }` for JSON output Affected crates: `terraphim_agent` only. ## Dependencies None. ## Verification ``` cargo test -p terraphim_agent --lib learnings::prune cargo clippy -p terraphim_agent -- -D warnings # Dry-run exercise cargo run -p terraphim_agent -- learn prune --older-than 30d cargo run -p terraphim_agent -- learn prune --older-than 30d --yes cargo run -p terraphim_agent -- learn prune --older-than 1y --json ``` ## Scope ~180 lines of new code (duration parser + prune module + wiring + 5 unit tests). Single PR. Safe defaults (dry-run, preserve corrections) keep blast radius small.
Sign in to join this conversation.