Skip to content

feat(remediation): change-remediation policy for diff-impacted memories - #444

Merged
cdeust merged 1 commit into
mainfrom
claude/change-remediation
Aug 24, 2026
Merged

feat(remediation): change-remediation policy for diff-impacted memories#444
cdeust merged 1 commit into
mainfrom
claude/change-remediation

Conversation

@cdeust

@cdeust cdeust commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Summary

Detection (#443) marks a stale memory ⚠stale — it points at the bug. This adds the remediation half: when a commit's diff invalidates memories, decide how to make each one correct again, safely. It closes the loop from "this memory is stale" to "this memory is now right."

The impact-mapping already exists — change_impact.py (ADR-0046 Phase 4) maps a commit's changed files/symbols to the memories that reference them, but it's read-only (heat-bump/tag only). This PR supplies the missing remediation policy + action, with one safety split because auto-rewriting prose from a diff would fabricate:

  • code-derived memories (agent_context='codebase') → collect their changed files and re-ingest once; codebase_analyze (incremental, content-hash) supersedes the stale AST fact with the current one.
  • hand-authored memories (decisions, lessons) → mark_memory_stale and leave re-authoring to a human/LLM. Never silently rewritten.

Addresses cdeust/fleet-watch#110 (remediation, not just detection).

Scope — foundation only

This PR ships the pure policy + a DI orchestrator, both fully unit-tested. It deliberately does not yet wire itself into the commit path: the real reingest_fn is codebase_analyze, and the impact source is change_impact/post_commit_reindex's diff — that composition step needs AP enabled + a real codebase to validate, so it's a follow-up. Nothing runs automatically from this PR.

Type of change

  • New feature (non-breaking; new modules, nothing wired in yet)
  • Audit-finding closure (partial — cdeust/fleet-watch#110, remediation policy)

Test plan

Authoring env lacks the DB/AP, so the full suite runs in CI. Verified locally (executed the test functions directly — all 7 pass):

  • tests_py/core/test_change_remediation.py: code-derived by agent_context, by tag, by hash-tag; hand-authored → FLAG_STALE; missing fields default to FLAG_STALE.

  • tests_py/handlers/consolidation/test_change_remediation_pass.py: code-derived→reingest / hand-authored→flag split; re-ingest path dedup across memories; empty-input no-op.

  • ruff check + ruff format --check + craftsmanship gate: clean (no numeric constants introduced — the # source: lesson from feat(staleness): auto-fire is_stale via a bounded file-existence revalidation pass #443 applied).

  • All existing tests pass — deferred to CI.

  • New tests for new behavior.

  • Mutation check: swapping the reingest/flag branches, not deduping paths, or auto-marking code-derived stale (which would fight codebase_analyze's supersession) are all caught.

Coding-standards compliance

  • §2.2 Layer direction: pure policy in core/ (stdlib+enum only); I/O orchestration is a composition root taking injected store + callback.
  • §4.1 / §4.2 sizes within limits.
  • §4.4 No function > 4 parameters.
  • §8 No numeric constants introduced.

Breaking changes

None. New modules only; no existing behavior changed; not yet invoked anywhere.

Reviewer checklist

  • CHANGELOG.md — not touched; reviewer preference.
  • No secrets / PII in the diff.
  • CI passes on the latest commit — pending.

Generated by Claude Code

Detection (memory_staleness_pass, #443) marks an impacted memory stale — it
points at the bug. This adds the *remediation* half: given the memories a commit
impacted (change_impact's matcher ∩ the diff), decide how to make each correct
again, safely.

The safety split (auto-rewriting prose from a diff would fabricate):
- code-derived memories (agent_context='codebase') -> collect their changed
  files and re-ingest once; codebase_analyze (incremental, content-hash) then
  supersedes the stale AST fact with the current one.
- hand-authored memories (decisions, lessons) -> mark_memory_stale and leave the
  re-authoring to a human/LLM; never silently rewritten.

- core/change_remediation.py: pure classifier (is_code_derived / Remediation
  REINGEST|FLAG_STALE). Reuses agent_context=='codebase', the same marker the
  codebase-analyze and change_impact paths already use — no new signal.
- handlers/consolidation/change_remediation_pass.py: DI orchestrator
  (remediate_impacted) — pure policy + injected re-ingest callback + injected
  store. Batches/dedups re-ingest paths; flags hand-authored stale.

Foundation only: the real wiring (codebase_analyze as reingest_fn, the commit
diff via change_impact/post_commit as the impact source) is the composition step
and is validated against AP + a real codebase. Addresses cdeust/fleet-watch#110
(remediation, not just detection).

Tests (DB-free, fakes): classifier both classes + missing fields; orchestrator
split, path dedup, no-op. ruff + craftsmanship clean; all 7 run locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017fnomaXS71hgxMw2zr7HGR
@cdeust
cdeust merged commit 2f0dfa1 into main Aug 24, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants