feat: auto-suggest correction from KG thesaurus during capture #40

Open
opened 2026-04-25 10:02:03 +02:00 by product-owner · 0 comments

Problem Statement

The learning-capture spec (docs/specifications/learning-capture-specification-interview.md, "Auto-suggest before store") mandates a specific capture-pipeline ordering:

"When capturing, first try to match error against existing KG terms. If match found, suggest correction automatically. If no KG match, store failure only. User can add correction later via terraphim-agent learn correct."

The pipeline diagram in crates/terraphim_agent/src/learnings/mod.rs reflects this:

Failed Command -> Secret Redaction -> Auto-Suggest -> Store as Markdown

In practice the auto-suggest step is missing. capture_failed_command (capture.rs:933) goes redact -> annotate-with-entities -> write file. There is no call into terraphim_automata::find_matches() against the project thesaurus, so:

  • A learning is always stored as "uncorrected" even when the failing command exactly matches a known synonym (e.g. npm install -> bun install is in docs/src/kg/bun.md)
  • Users have to run terraphim-agent learn correct <id> manually for corrections that the KG already encodes
  • ~/.cargo/bin/terraphim-agent learn query returns the failure with no suggested correction, defeating the "Don't repeat past mistakes" loop

crates/terraphim_agent/src/learnings/suggest.rs exists but is gated behind #[cfg(feature = "shared-learning")] and only wraps shared-learning lookups -- it does not consult the local KG thesaurus during capture.

Acceptance Criteria

  • Add pub fn suggest_correction_from_kg(command: &str, thesaurus: &Thesaurus) -> Option<String> in learnings/suggest.rs (always-on, not feature-gated)
  • Call it from capture_failed_command after redaction and before storage; populate CapturedLearning.correction with the result
  • If a correction is found, also set CorrectionType::AutoSuggested (new variant) so the markdown file records it as "auto-suggested" vs "user-corrected"
  • Use find_kg_dir() + build_kg_thesaurus_from_dir() (already in capture.rs) to load the thesaurus once per capture; cache via OnceLock for the process lifetime
  • When no thesaurus is available (e.g. project without docs/src/kg/), the function returns None and capture proceeds with correction: None (no error)
  • terraphim-agent learn list displays auto-suggested: <correction> for entries that have one
  • Add unit test covering: (a) command matches a synonym -> correction set, (b) command has no KG match -> correction is None, (c) thesaurus missing -> correction is None
  • cargo test -p terraphim_agent learnings::capture::auto_suggest passes
  • cargo test -p terraphim_agent passes; cargo clippy -p terraphim_agent -- -D warnings passes

Proposed Approach

Crates affected: crates/terraphim_agent only.

Files to add/modify:

  • crates/terraphim_agent/src/learnings/suggest.rs -- add the new always-on function (the existing shared-learning code stays under its feature gate)
  • crates/terraphim_agent/src/learnings/capture.rs -- (1) extend CorrectionType with AutoSuggested, (2) call suggest_correction_from_kg between redact_secrets and the markdown writer, (3) include correction in the markdown frontmatter
  • crates/terraphim_agent/src/learnings/mod.rs -- re-export the new function

Key types:

pub enum CorrectionType {
    Manual,
    Imported,
    AutoSuggested,   // new
}

pub fn suggest_correction_from_kg(command: &str, thesaurus: &Thesaurus) -> Option<String> {
    let matches = terraphim_automata::find_matches(command, thesaurus.clone(), true).ok()?;
    matches.into_iter()
        .max_by_key(|m| m.term.len())   // longest match wins (LeftmostLongest)
        .map(|m| m.normalized_term.value.to_string())
}

Thesaurus access patterns are already used by the hook validation pipeline in kg_validation.rs -- reuse the same loader.

Dependencies

None. Independent PR.

Verification

cargo test -p terraphim_agent learnings::capture
cargo clippy -p terraphim_agent -- -D warnings

# Smoke test (requires a project with docs/src/kg/)
cd /home/alex/terraphim-ai
~/.cargo/bin/terraphim-agent learn capture "npm install foo" --error "ENOENT" --exit-code 1
~/.cargo/bin/terraphim-agent learn list | grep auto-suggested
# Expect: line shows "auto-suggested: bun install"

Scope

Single PR, ~200-300 lines including tests. Add the suggester, wire it into capture, extend CorrectionType, add tests. Do not change the markdown schema beyond adding one frontmatter field, do not refactor the existing capture flow ordering.

## Problem Statement The learning-capture spec (`docs/specifications/learning-capture-specification-interview.md`, "Auto-suggest before store") mandates a specific capture-pipeline ordering: > "When capturing, first try to match error against existing KG terms. If match found, suggest correction automatically. If no KG match, store failure only. User can add correction later via `terraphim-agent learn correct`." The pipeline diagram in `crates/terraphim_agent/src/learnings/mod.rs` reflects this: ```text Failed Command -> Secret Redaction -> Auto-Suggest -> Store as Markdown ``` In practice the auto-suggest step is missing. `capture_failed_command` (capture.rs:933) goes redact -> annotate-with-entities -> write file. There is no call into `terraphim_automata::find_matches()` against the project thesaurus, so: - A learning is always stored as "uncorrected" even when the failing command exactly matches a known synonym (e.g. `npm install` -> `bun install` is in `docs/src/kg/bun.md`) - Users have to run `terraphim-agent learn correct <id>` manually for corrections that the KG already encodes - `~/.cargo/bin/terraphim-agent learn query` returns the failure with no suggested correction, defeating the "Don't repeat past mistakes" loop `crates/terraphim_agent/src/learnings/suggest.rs` exists but is gated behind `#[cfg(feature = "shared-learning")]` and only wraps shared-learning lookups -- it does not consult the local KG thesaurus during capture. ## Acceptance Criteria - [ ] Add `pub fn suggest_correction_from_kg(command: &str, thesaurus: &Thesaurus) -> Option<String>` in `learnings/suggest.rs` (always-on, not feature-gated) - [ ] Call it from `capture_failed_command` after redaction and before storage; populate `CapturedLearning.correction` with the result - [ ] If a correction is found, also set `CorrectionType::AutoSuggested` (new variant) so the markdown file records it as "auto-suggested" vs "user-corrected" - [ ] Use `find_kg_dir()` + `build_kg_thesaurus_from_dir()` (already in `capture.rs`) to load the thesaurus once per capture; cache via `OnceLock` for the process lifetime - [ ] When no thesaurus is available (e.g. project without `docs/src/kg/`), the function returns `None` and capture proceeds with `correction: None` (no error) - [ ] `terraphim-agent learn list` displays `auto-suggested: <correction>` for entries that have one - [ ] Add unit test covering: (a) command matches a synonym -> correction set, (b) command has no KG match -> correction is None, (c) thesaurus missing -> correction is None - [ ] `cargo test -p terraphim_agent learnings::capture::auto_suggest` passes - [ ] `cargo test -p terraphim_agent` passes; `cargo clippy -p terraphim_agent -- -D warnings` passes ## Proposed Approach **Crates affected:** `crates/terraphim_agent` only. **Files to add/modify:** - `crates/terraphim_agent/src/learnings/suggest.rs` -- add the new always-on function (the existing shared-learning code stays under its feature gate) - `crates/terraphim_agent/src/learnings/capture.rs` -- (1) extend `CorrectionType` with `AutoSuggested`, (2) call `suggest_correction_from_kg` between `redact_secrets` and the markdown writer, (3) include `correction` in the markdown frontmatter - `crates/terraphim_agent/src/learnings/mod.rs` -- re-export the new function **Key types:** ```rust pub enum CorrectionType { Manual, Imported, AutoSuggested, // new } pub fn suggest_correction_from_kg(command: &str, thesaurus: &Thesaurus) -> Option<String> { let matches = terraphim_automata::find_matches(command, thesaurus.clone(), true).ok()?; matches.into_iter() .max_by_key(|m| m.term.len()) // longest match wins (LeftmostLongest) .map(|m| m.normalized_term.value.to_string()) } ``` `Thesaurus` access patterns are already used by the hook validation pipeline in `kg_validation.rs` -- reuse the same loader. ## Dependencies None. Independent PR. ## Verification ``` cargo test -p terraphim_agent learnings::capture cargo clippy -p terraphim_agent -- -D warnings # Smoke test (requires a project with docs/src/kg/) cd /home/alex/terraphim-ai ~/.cargo/bin/terraphim-agent learn capture "npm install foo" --error "ENOENT" --exit-code 1 ~/.cargo/bin/terraphim-agent learn list | grep auto-suggested # Expect: line shows "auto-suggested: bun install" ``` ## Scope Single PR, ~200-300 lines including tests. Add the suggester, wire it into capture, extend `CorrectionType`, add tests. Do not change the markdown schema beyond adding one frontmatter field, do not refactor the existing capture flow ordering.
Sign in to join this conversation.