Skip to main content

Module observation

Module observation 

Source
Expand description

PostToolUse observation classifier.

Third supply line for candidate rules. When the Claude Code PostToolUse hook fires for an Edit / MultiEdit / Write tool, the CLI calls classify to turn the raw event into a structured Observation and enqueues it via OutboxQueue with kind="observation". The cloud consumer clusters those rows by content_hash and feeds the rule-promoter alongside remember_rule captures and GitHub-App PR-merge signatures.

Classification is deterministic and keyword-driven — no LLM call, sub-millisecond target. The heuristics are intentionally simple:

  • Write of a brand-new file ⇒ feature
  • Edit that strips a visible FIXME / BUG / TODObugfix
  • Edit where the diff is whitespace-only (no semantic deltas) ⇒ refactor
  • Anything else ⇒ change

discovery and decision are declared as valid obs_type values for forward-compat but are never emitted from the local classifier (they need LLM or conversation context).

Privacy guard: edits touching secret-bearing paths (.env*, *.secrets*, *.key, *.pem, id_rsa*, credentials*) are dropped before classification. The user cannot opt in — these files must never leave the local machine via the observation channel.

Re-exports§

pub use crate::cloud::api_types::Observation;
pub use crate::cloud::api_types::ObservationScope;

Structs§

ClassifyInput
Input payload for classify. Borrowed so the caller doesn’t have to clone every string coming out of the hook event; the classifier only needs read access.

Constants§

DIFF_EXCERPT_MAX_BYTES
Maximum size of the diff excerpt captured in the observation payload. The cloud side does its own heavier clustering; we ship just enough context for a human reviewer to recognise the edit.
NARRATIVE_MAX_CHARS
Hard narrative length cap.
TITLE_MAX_CHARS
Hard title length cap. Matches the title doc comment on Observation.

Functions§

classify
Classify a PostToolUse event. Returns None when the event should not produce an observation (non-edit tool, no file path, missing diff signal, or a privacy-denied path).
is_privacy_denied
true when the path matches one of the hardcoded secret patterns. Substring match (not full-glob) — good enough to cover src/config/.env.local and infra/prod.credentials.json.