feat: load LearningCaptureConfig from .terraphim/learning-capture.toml #38

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

Problem Statement

The learning-capture specification (docs/specifications/learning-capture-specification-interview.md, Configuration Design section) explicitly requires a TOML config file at .terraphim/learning-capture.toml with [learnings], [learnings.ignore], and [learnings.redaction] sections. This lets users:

  • Disable capture per-project (enabled = false)
  • Override project_dir and global_dir paths
  • Add or replace ignore_patterns (e.g. cargo bench*, make check*) without recompiling
  • Add custom_patterns to the redactor (e.g. company-specific token formats)

Today, crates/terraphim_agent/src/learnings/mod.rs exposes LearningCaptureConfig but only as hard-coded defaults. There is no from_toml() / load() / load_or_default() constructor, no precedence logic between project and home configs, and the redaction.rs SECRET_PATTERNS slice is const -- impossible to extend at runtime. As a result the capture hook is one-size-fits-all and forces users to fork the binary for any policy tweak.

Acceptance Criteria

  • Add LearningCaptureConfig::load(path: &Path) -> Result<Self, LearningError> that parses TOML matching the spec layout
  • Add LearningCaptureConfig::load_or_default() that walks ./.terraphim/learning-capture.toml -> ~/.terraphim/learning-capture.toml (XDG-friendly via dirs) -> baked defaults
  • [learnings.ignore].commands array merges with (does not silently replace) the built-in test-runner patterns; document precedence in the rustdoc
  • [learnings.redaction].custom_patterns array is compiled into a Vec<(Regex, &'static str)> at load time and consumed by redact_secrets() alongside the const slice
  • Invalid TOML returns a structured LearningError::Config(String); missing file falls back to defaults without error
  • cargo test -p terraphim_agent learnings::config covers happy-path load, merge of ignore patterns, custom redaction, missing file, malformed TOML
  • 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/mod.rs -- add load, load_or_default, Config deserialisation struct (private), and LearningError::Config variant via capture::LearningError
  • crates/terraphim_agent/src/learnings/redaction.rs -- accept &[(Regex, &str)] extra patterns; keep the const slice as the floor
  • crates/terraphim_agent/src/learnings/capture.rs -- thread the loaded config through capture_failed_command so custom patterns reach redact_secrets
  • New module crates/terraphim_agent/src/learnings/config.rs (optional split) for the serde shapes

Key types:

#[derive(Debug, Deserialize)]
struct ConfigFile {
    learnings: LearningsSection,
}
#[derive(Debug, Deserialize)]
struct LearningsSection {
    project_dir: Option<PathBuf>,
    global_dir: Option<PathBuf>,
    enabled: Option<bool>,
    #[serde(default)]
    ignore: IgnoreSection,
    #[serde(default)]
    redaction: RedactionSection,
}

Use toml = "0.8" (already in workspace) and regex (already a dependency of redaction). No new crates required.

Dependencies

None. This issue is independently implementable. It is a sibling to #25 ([aliases] TOML), but the two configs live in different files (learning-capture.toml vs aliases.toml or central agent config); pick the dispatch already used by #25 if merged first.

Verification

cargo test -p terraphim_agent learnings
cargo clippy -p terraphim_agent -- -D warnings
echo '[learnings]\nenabled=false' > /tmp/lc.toml
RUST_LOG=debug terraphim-agent learn capture "git push" --error "boom" --config /tmp/lc.toml
# Expect: capture exits with "Capture disabled"

Scope

Single PR, ~250-350 lines including tests. Extending LearningCaptureConfig, parsing, merging, and threading custom redaction patterns through redact_secrets() only. Do not change the capture pipeline semantics, do not introduce a CLI --config flag (separate follow-up if needed).

## Problem Statement The learning-capture specification (`docs/specifications/learning-capture-specification-interview.md`, Configuration Design section) explicitly requires a TOML config file at `.terraphim/learning-capture.toml` with `[learnings]`, `[learnings.ignore]`, and `[learnings.redaction]` sections. This lets users: - Disable capture per-project (`enabled = false`) - Override `project_dir` and `global_dir` paths - Add or replace `ignore_patterns` (e.g. `cargo bench*`, `make check*`) without recompiling - Add `custom_patterns` to the redactor (e.g. company-specific token formats) Today, `crates/terraphim_agent/src/learnings/mod.rs` exposes `LearningCaptureConfig` but only as hard-coded defaults. There is no `from_toml()` / `load()` / `load_or_default()` constructor, no precedence logic between project and home configs, and the `redaction.rs` `SECRET_PATTERNS` slice is `const` -- impossible to extend at runtime. As a result the capture hook is one-size-fits-all and forces users to fork the binary for any policy tweak. ## Acceptance Criteria - [ ] Add `LearningCaptureConfig::load(path: &Path) -> Result<Self, LearningError>` that parses TOML matching the spec layout - [ ] Add `LearningCaptureConfig::load_or_default()` that walks `./.terraphim/learning-capture.toml` -> `~/.terraphim/learning-capture.toml` (XDG-friendly via `dirs`) -> baked defaults - [ ] `[learnings.ignore].commands` array merges with (does not silently replace) the built-in test-runner patterns; document precedence in the rustdoc - [ ] `[learnings.redaction].custom_patterns` array is compiled into a `Vec<(Regex, &'static str)>` at load time and consumed by `redact_secrets()` alongside the const slice - [ ] Invalid TOML returns a structured `LearningError::Config(String)`; missing file falls back to defaults without error - [ ] `cargo test -p terraphim_agent learnings::config` covers happy-path load, merge of ignore patterns, custom redaction, missing file, malformed TOML - [ ] `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/mod.rs` -- add `load`, `load_or_default`, `Config` deserialisation struct (private), and `LearningError::Config` variant via `capture::LearningError` - `crates/terraphim_agent/src/learnings/redaction.rs` -- accept `&[(Regex, &str)]` extra patterns; keep the const slice as the floor - `crates/terraphim_agent/src/learnings/capture.rs` -- thread the loaded config through `capture_failed_command` so custom patterns reach `redact_secrets` - New module `crates/terraphim_agent/src/learnings/config.rs` (optional split) for the serde shapes **Key types:** ```rust #[derive(Debug, Deserialize)] struct ConfigFile { learnings: LearningsSection, } #[derive(Debug, Deserialize)] struct LearningsSection { project_dir: Option<PathBuf>, global_dir: Option<PathBuf>, enabled: Option<bool>, #[serde(default)] ignore: IgnoreSection, #[serde(default)] redaction: RedactionSection, } ``` Use `toml = "0.8"` (already in workspace) and `regex` (already a dependency of redaction). No new crates required. ## Dependencies None. This issue is independently implementable. It is a sibling to #25 (`[aliases]` TOML), but the two configs live in different files (`learning-capture.toml` vs `aliases.toml` or central agent config); pick the dispatch already used by #25 if merged first. ## Verification ``` cargo test -p terraphim_agent learnings cargo clippy -p terraphim_agent -- -D warnings echo '[learnings]\nenabled=false' > /tmp/lc.toml RUST_LOG=debug terraphim-agent learn capture "git push" --error "boom" --config /tmp/lc.toml # Expect: capture exits with "Capture disabled" ``` ## Scope Single PR, ~250-350 lines including tests. Extending `LearningCaptureConfig`, parsing, merging, and threading custom redaction patterns through `redact_secrets()` only. Do not change the capture pipeline semantics, do not introduce a CLI `--config` flag (separate follow-up if needed).
Sign in to join this conversation.