Skip to content

feat(retention): policy-driven data retention for entities (#275) - #294

Merged
illeatmyhat merged 8 commits into
mainfrom
feat/275-retention
Jul 23, 2026
Merged

feat(retention): policy-driven data retention for entities (#275)#294
illeatmyhat merged 8 commits into
mainfrom
feat/275-retention

Conversation

@illeatmyhat

@illeatmyhat illeatmyhat commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

What

Adds data retention — the half of #275 that main still lacks. A declarative policy selects entities by type and age and either flags them for review or deletes them, with a session → derived cascade. Dry run everywhere by default.

PII is deliberately out of scope for this PR.

Why

Memories accumulate: some go stale, some are never read again, and session transcripts often carry data with a bounded agreed retention. Deleting a session without deleting the memories extracted from it just leaves the same data behind under another name — hence the provenance cascade.

Policy

rules:
  - name: stale-guidelines
    entity_type: guideline
    max_age_days: 90
    action: flag

  - name: unused-guidelines
    entity_type: guideline
    max_unused_days: 180
    action: delete

  - name: old-sessions
    entity_type: trajectory
    max_age_days: 365
    action: delete
    cascade_derived: true
evolve retention run --policy examples/retention.example.yaml            # dry run
evolve retention run --policy examples/retention.example.yaml --apply    # enforce

Rules are evaluated top-to-bottom, first match wins; a cascade delete supersedes an earlier flag. The engine drives the EvolveClient public API, so it is backend-agnostic and every delete flows through memory_pre_delete — a legal-hold plugin can veto a retention delete, and the veto lands in report.errors without aborting the sweep.

Synergy with the merged hook seam (#287)

This is the substantive improvement over the earlier prototype. The seam changed two things in retention's favour and both are wired up here:

1. last_accessed is now actually populated. AccessStampPlugin stamps it on memory_post_read, so max_unused_days finally has a real signal — in the prototype record_access had zero callers, so the unused rule silently degraded into an age rule for every entity. Now the fallback is explicit rather than silent: an entity with no stamp gets a per-item detail naming the fallback, and the run carries a RetentionReport.warning:

FLAG    1   guideline  reason=unused       rule=unused-guidelines
        why: not read for 200.0d > max_unused_days=90 — no metadata.last_accessed on this
             entity, so disuse was measured from created_at; enable AccessStampPlugin (or
             call EvolveClient.record_access) for a real recall signal
warning: 4 of 5 entities carry no metadata.last_accessed, so their disuse was measured from
created_at — for those entities an unused rule behaves like an age rule.

2. trace_id is now reliably stamped. MetadataNormalizerPlugin copies task_idtrace_id, closing the MCP-vs-Phoenix convention split that previously broke the cascade for MCP-saved trajectories. The engine additionally falls back to task_id at read time so sessions written before the normalizer still cascade (trace_id wins when both are present). Both conventions are covered by tests, including one that runs the actual normalize_entities core over an MCP-shaped payload.

record_access decision — kept, but no longer a second disjoint mechanism. It now shares the plugin's build_access_stamps core, so key, format and one-stamp-per-batch behaviour cannot drift. The plugin is the automatic path; record_access is the explicit path for callers not running hooks, or for recording a use that was not a store read (a memory pulled from cache and acted on). A test asserts the two produce byte-identical stamps. Dropping it would have left non-hook users with no way to feed the unused rule at all.

Plugin-side parity, and its gaps

evolve-lite gets a stdlib-only retention lib + skill rendered to all four variants (claude / claw-code / codex / bob), with the same schema, actions, dry-run default and cascade concept over the .evolve/ file store, auditing every applied action. The signals are genuinely weaker, and the code, the skill and the guide all say so rather than implying parity:

Signal Package Plugin Consequence
age created_at file mtime — no created_at exists in the store mtime is a modification time: editing an entity resets its age. Flagging deliberately restores mtime so the sweep never resets it.
unused metadata.last_accessed (automatic, AccessStampPlugin) latest recall row in .evolve/audit.log the recall audit is model-invoked, so a missing row means "no recorded recall", not "not used". Same degraded-signal warning is emitted.
cascade metadata.source_task_id == trace_id a trajectory: frontmatter key nothing in the shipped plugin writes that key today. entity_io supports and preserves it, but neither the save flow nor adapt-memory populates it — so cascade_derived is effectively inert plugin-side. A test pins this so it cannot be mistaken for working.

Out of scope for the plugin sweep: entities/subscribed/ (sync-owned git clones — a local delete would just be restored), public/, symlinks, .git.

Safety

RetentionEngine.apply() defaults to dry_run=True and the CLI requires an explicit --apply. A dry run computes every decision and mutates nothing; the report is identical in shape to an enforced one. delete has no undo — flag exists so you can stage a review queue first.

Test evidence

  • Full suite: 930 passed, 1 skipped (uv run --no-sync pytest tests -q).
  • 26 new package tests (tests/unit/test_retention.py) and 16 plugin tests (tests/platform_integrations/test_retention_plugin.py), covering age/unused/dry-run/cascade, the degraded-signal reporting, both trace-id conventions, first-match-wins, delete-supersedes-flag, per-entity failure isolation, and the record_access ↔ plugin equivalence.
  • uv lock --check clean; pre-commit run --all-files green (mypy, ruff, detect-secrets, plugins-rendered).
  • examples/retention_demo.py run end-to-end: dry run shows every decision with its "why" and mutates nothing, --apply flags the stale guideline, deletes the 400-day-old session together with the memory derived from it (matched via task_id), and leaves the guideline recalled yesterday alone.

Closes part of #275 (retention half).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added policy-driven data retention to evaluate entity age/unused signals and flag or delete accordingly.
    • Introduced retention run CLI with YAML/JSON policies, namespace sweep, dry-run default, and --apply enforcement.
    • Added degraded-signal handling with “skipped” reporting, plus optional cascading deletion of derived entities.
    • Added manual access recording to improve unused detection.
  • Documentation
    • Added retention guides, CLI reference, examples, and platform-specific retention skill docs.
  • Tests
    • Added unit and integration coverage for policy parsing/validation, dry-run vs apply behavior, warnings/errors, and cascading rules.

Post-review fixes (tiered internal review)

A tiered review (fast-model diff mapping → higher-tier adversarial review with repro-verified fixes) ran over this PR. All findings reproduced, fixed, and re-verified by re-running the repros:

  • [HIGH · data-loss] Cascade over-deletion on empty/falsy trace id. A trajectory carrying trace_id="" cascade-deleted every entity with source_task_id=="", regardless of real provenance. Fixed: falsy ids never form a cascade bucket (guarded on both package and plugin side); genuine matches (including int↔str, e.g. 1"1") still cascade. Regression tests added.
  • Unstamped unused-delete → configurable, fail-safe. An unused DELETE rule previously deleted never-access-stamped entities by measuring disuse from created_at — silent data loss when stamping is off. Now a per-rule on_missing_access_signal: skip | flag | delete with a fail-safe skip default; skipped entities are surfaced in the report and CLI. Wired through the engine, the plugin (all 4 variants), the CLI, and the docs.
  • Configurable scan limit + truncation warning. The 100k fetch cap is now a scan_limit parameter and emits a warning when a namespace may exceed it (previously silent).
  • Example policy fixed. Reordered so every shipped rule is reachable (first-match escalation), with a docs section on first-match shadowing.
  • Test gap closed. Added a real-filesystem dry-run non-mutation test (previously only the fake client proved this package-side).

Retention tests 42 → 59; full suite green.

illeatmyhat and others added 3 commits July 20, 2026 11:29
#275)

Adds altk_evolve/retention/: a RetentionPolicy of ordered rules (age via
created_at, disuse via metadata.last_accessed) that flag or delete entities,
plus a session -> derived cascade keyed on provenance metadata. Driven through
the EvolveClient public API, so it is backend-agnostic and every delete flows
through the memory_pre_delete hook (a legal hold can veto one).

Builds on the merged hook seam (#287) in two ways:

- last_accessed is now actually populated by AccessStampPlugin, so unused
  rules have a real signal. Where an entity carries no stamp the engine falls
  back to created_at and SAYS SO — per-item `detail` plus a run-level
  RetentionReport.warning — instead of the prototype's silent substitution.
- MetadataNormalizerPlugin makes trace_id canonical for MCP-saved sessions;
  the engine also falls back to task_id at read time so sessions written
  before the normalizer still cascade.

EvolveClient.record_access is kept as the explicit path for callers not
running hooks, but now shares the plugin's build_access_stamps core so the two
mechanisms cannot drift.

`evolve retention run --policy <file> [--apply]` is dry-run by default and
reports the evidence behind every decision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a stdlib-only plugin-source/lib/retention.py and a `retention` skill,
rendered to all four platform variants. Same rule schema, same flag/delete
actions, same dry-run default and cascade concept as the package, running over
the .evolve/ file store; every applied action appends an event: "retention"
row to .evolve/audit.log.

The signals are weaker than package-side and the code and skill now say so
rather than implying parity: age comes from file mtime (there is no created_at
in the store, so an edit resets the clock), disuse comes from the
model-invoked recall audit log, and the `trajectory:` cascade link — while
supported and preserved by entity_io — is written by nothing in the shipped
plugin today, so cascade_derived is effectively inert there. A test pins that
last gap so it cannot be mistaken for working.

Subscribed entities (git clones owned by the sync skill) stay out of scope.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…emo (#275)

Adds docs/guides/retention.md in the house style of the memory-hooks guide:
policy format, flag vs delete, dry-run-by-default and how to apply, the
session cascade and the MCP/Phoenix convention split the normalizer closed,
and an honest account of what the unused rule depends on (AccessStampPlugin
or record_access — without either it is an age rule wearing a different name).

Documents plugin-side parity and its three real gaps in a table (mtime as age,
model-invoked recall audit, and the unwritten `trajectory:` cascade link),
plus what the plugin sweep excludes (entities/subscribed/, public/, symlinks).

Also wires Memory Hooks and Data Retention into the mkdocs nav, cross-links
the two guides, and ships examples/retention.example.yaml and a runnable
examples/retention_demo.py.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@illeatmyhat, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 58 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a00af2a3-9d21-4e6e-a78b-50052ea69755

📥 Commits

Reviewing files that changed from the base of the PR and between d86fb6a and 1108a74.

📒 Files selected for processing (3)
  • altk_evolve/retention/engine.py
  • docs/guides/retention.md
  • tests/unit/test_retention.py
📝 Walkthrough

Walkthrough

Retention support is added across the core client and evolve-lite plugins. Policies can flag or delete entities using age or access signals, cascade session deletions, run through CLI and skills, report warnings and errors, and default to dry-run behavior.

Changes

Data retention

Layer / File(s) Summary
Policy and retention engine
altk_evolve/retention/*
Adds validated retention rules, age/unused matching, flag/delete actions, provenance cascades, dry-run reports, and per-entity error handling.
CLI and access stamping
altk_evolve/cli/cli.py, altk_evolve/frontend/client/evolve_client.py
Adds retention run and EvolveClient.record_access(), including policy loading, action reporting, warnings, and apply-mode handling.
Evolve-lite implementations
platform-integrations/*/evolve-lite/..., plugin-source/*
Adds filesystem scanning, recall-based unused detection, frontmatter flagging, deletion, audit logging, policy loading, and retention entry points across plugin variants.
Documentation and examples
docs/..., examples/..., platform-integrations/..., mkdocs.yaml
Documents retention behavior and adds example policies, demos, CLI references, and skill workflows.
Validation
tests/unit/test_retention.py, tests/platform_integrations/test_retention_plugin.py
Covers policy validation, matching, access stamps, dry runs, cascades, warnings, audit logging, flag persistence, and exclusions.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: vinodmut

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding policy-driven data retention for entities.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/275-retention

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
altk_evolve/retention/engine.py (1)

102-102: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Silent truncation at FETCH_LIMIT.

If a namespace has more than 100k entities, get_all_entities truncates silently and the sweep won't see the remainder — no warning is emitted in this case, unlike the "no last_accessed" degraded-signal warning. For a compliance-driven retention feature, consider surfacing a warning when len(entities) == FETCH_LIMIT so operators know the run may be incomplete.

Also applies to: 172-172

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@altk_evolve/retention/engine.py` at line 102, Update get_all_entities to emit
a warning whenever the fetched entity count equals FETCH_LIMIT, indicating that
results may be truncated and the retention sweep may be incomplete; apply the
same check at the other FETCH_LIMIT usage noted in the comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@altk_evolve/retention/engine.py`:
- Around line 204-220: Restrict the cascade block in the retention engine to
session/trajectory entities by adding the same type guard used by the mirrored
implementation before processing derived records. Keep the existing delete and
cascade_derived checks and all downstream tracing/recording behavior unchanged
for eligible trajectory entities.

---

Nitpick comments:
In `@altk_evolve/retention/engine.py`:
- Line 102: Update get_all_entities to emit a warning whenever the fetched
entity count equals FETCH_LIMIT, indicating that results may be truncated and
the retention sweep may be incomplete; apply the same check at the other
FETCH_LIMIT usage noted in the comment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8bc44d10-b9bf-4283-a8f5-174748cc2d7f

📥 Commits

Reviewing files that changed from the base of the PR and between 1d2edc2 and 9034873.

📒 Files selected for processing (30)
  • altk_evolve/cli/cli.py
  • altk_evolve/frontend/client/evolve_client.py
  • altk_evolve/retention/__init__.py
  • altk_evolve/retention/engine.py
  • altk_evolve/retention/policy.py
  • docs/guides/memory-hooks.md
  • docs/guides/retention.md
  • docs/reference/cli.md
  • examples/retention.example.yaml
  • examples/retention_demo.py
  • mkdocs.yaml
  • platform-integrations/bob/evolve-lite/commands/evolve-lite-retention.md
  • platform-integrations/bob/evolve-lite/lib/evolve-lite/retention.py
  • platform-integrations/bob/evolve-lite/skills/evolve-lite-retention/SKILL.md
  • platform-integrations/bob/evolve-lite/skills/evolve-lite-retention/scripts/run_retention.py
  • platform-integrations/claude/plugins/evolve-lite/lib/evolve-lite/retention.py
  • platform-integrations/claude/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/claude/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • platform-integrations/claw-code/plugins/evolve-lite/lib/evolve-lite/retention.py
  • platform-integrations/claw-code/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/claw-code/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • platform-integrations/codex/plugins/evolve-lite/lib/evolve-lite/retention.py
  • platform-integrations/codex/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/codex/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • plugin-source/lib/retention.py
  • plugin-source/skills/evolve-lite/retention/SKILL.md.j2
  • plugin-source/skills/evolve-lite/retention/scripts/run_retention.py
  • pyproject.toml
  • tests/platform_integrations/test_retention_plugin.py
  • tests/unit/test_retention.py

Comment thread altk_evolve/retention/engine.py Outdated
illeatmyhat and others added 3 commits July 21, 2026 11:31
…scan_limit

FIX 1: an empty-string (or otherwise falsy) trace_id / source_task_id no
longer forms a cascade bucket, so a session with no real provenance link can
no longer cascade-delete every entity that merely lacks provenance. Both sides
of the match are normalized to str and required non-empty, so an int trace_id
still matches a str source_task_id of the same value but never an empty one.

FIX 2: new per-rule RetentionRule.on_missing_access_signal
("skip" default / "flag" / "delete"). An `unused` DELETE that matched only
because it fell back to created_at (no real last_accessed stamp) now skips by
default (fail-safe), recorded in the new RetentionReport.skipped list; "flag"
downgrades it to a non-destructive flag; "delete" is the explicit opt-in to the
old behavior. Only bites deletes — flag rules and stamped/age matches are
unaffected. Wired through the engine, report, and CLI (skipped table).

FIX 3: RetentionEngine.apply()/evaluate() take a scan_limit param
(default FETCH_LIMIT=100_000); a scan that comes back holding exactly the limit
emits a RetentionReport.warnings entry that entities beyond it were not
evaluated (and not cascaded).

FIX 5: adds a filesystem-backend dry-run test asserting a real store is not
mutated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports FIX 2 to the plugin-side store (plugin-source/lib/retention.py): rules
accept on_missing_access_signal ("skip" default / "flag" / "delete") validated
against VALID_MISSING_SIGNAL, an `unused` DELETE that fell back to file mtime
(no recall row) skips by default into a new report["skipped"] list, and
run_retention.py prints SKIP lines. SKILL.md documents the knob and the
first-match-shadowing ordering caveat.

FIX 1 note: the plugin cascade was already falsy-safe (empty `trajectory:`
frontmatter is dropped at parse time and guarded by `if link:`); added a
regression test that a literal empty trajectory link does not cascade.

Re-rendered to all four variants (claude / claw-code / codex / bob).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…h shadowing

FIX 4: reorder examples/retention.example.yaml so the long-threshold
`unused-guidelines` DELETE precedes the `stale-guidelines` FLAG — first-match
otherwise made the delete unreachable — and set on_missing_access_signal: skip
explicitly so an unstamped guideline is spared, not deleted from created_at.

Guide updates: on_missing_access_signal table, a "When the unused signal is
missing" section, a "First-match shadowing" section explaining the trap, and
the scan_limit / boundary-warning operational note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/guides/retention.md`:
- Around line 39-40: Update the retention guide sample so the output for the
unused-guidelines rule matches its on_missing_access_signal: skip configuration:
show the unstamped delete match as skipped rather than FLAG. Keep the documented
policy and surrounding examples unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78c466e6-7315-448f-bd86-4ce6b9cdb3dc

📥 Commits

Reviewing files that changed from the base of the PR and between 9034873 and d86fb6a.

📒 Files selected for processing (23)
  • altk_evolve/cli/cli.py
  • altk_evolve/retention/engine.py
  • altk_evolve/retention/policy.py
  • docs/guides/retention.md
  • examples/retention.example.yaml
  • examples/retention_demo.py
  • platform-integrations/bob/evolve-lite/lib/evolve-lite/retention.py
  • platform-integrations/bob/evolve-lite/skills/evolve-lite-retention/SKILL.md
  • platform-integrations/bob/evolve-lite/skills/evolve-lite-retention/scripts/run_retention.py
  • platform-integrations/claude/plugins/evolve-lite/lib/evolve-lite/retention.py
  • platform-integrations/claude/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/claude/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • platform-integrations/claw-code/plugins/evolve-lite/lib/evolve-lite/retention.py
  • platform-integrations/claw-code/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/claw-code/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • platform-integrations/codex/plugins/evolve-lite/lib/evolve-lite/retention.py
  • platform-integrations/codex/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/codex/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • plugin-source/lib/retention.py
  • plugin-source/skills/evolve-lite/retention/SKILL.md.j2
  • plugin-source/skills/evolve-lite/retention/scripts/run_retention.py
  • tests/platform_integrations/test_retention_plugin.py
  • tests/unit/test_retention.py
🚧 Files skipped from review as they are similar to previous changes (18)
  • platform-integrations/codex/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/claw-code/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/claude/plugins/evolve-lite/skills/evolve-lite/retention/SKILL.md
  • platform-integrations/claw-code/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • platform-integrations/bob/evolve-lite/skills/evolve-lite-retention/scripts/run_retention.py
  • examples/retention.example.yaml
  • examples/retention_demo.py
  • plugin-source/skills/evolve-lite/retention/scripts/run_retention.py
  • altk_evolve/cli/cli.py
  • platform-integrations/codex/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • platform-integrations/bob/evolve-lite/skills/evolve-lite-retention/SKILL.md
  • tests/platform_integrations/test_retention_plugin.py
  • altk_evolve/retention/engine.py
  • plugin-source/skills/evolve-lite/retention/SKILL.md.j2
  • platform-integrations/claude/plugins/evolve-lite/skills/evolve-lite/retention/scripts/run_retention.py
  • platform-integrations/claude/plugins/evolve-lite/lib/evolve-lite/retention.py
  • plugin-source/lib/retention.py
  • platform-integrations/claw-code/plugins/evolve-lite/lib/evolve-lite/retention.py

Comment thread docs/guides/retention.md
illeatmyhat and others added 2 commits July 22, 2026 10:01
A cascade_derived delete fanned out along source_task_id for ANY deleted
entity, so a rule with entity_type=None (or a non-trajectory type) that
matched a non-trajectory carrier holding a trace_id/task_id would
mass-delete every entity linked by source_task_id. Only a trajectory owns
a provenance tree, so gate the cascade on e.type == TRAJECTORY_TYPE,
matching the plugin side's guard. The existing falsy-trace guard
(if not trace: continue) is preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dry-run sample showed an unstamped `unused` match as FLAG under the
`unused-guidelines` delete rule, but that rule ships
on_missing_access_signal: skip, so such a match renders as skipped, not
flagged. Regenerated the block from a real engine run of the shipped
example policy: age-delete -> DELETE, cascade -> DELETE, stale age -> FLAG
(stale-guidelines), unstamped unused-delete -> SKIP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@illeatmyhat
illeatmyhat merged commit 976721a into main Jul 23, 2026
19 checks passed
illeatmyhat added a commit that referenced this pull request Jul 23, 2026
…keep always-on hooks

# Conflicts:
#	altk_evolve/cli/cli.py
#	docs/guides/memory-hooks.md
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.

1 participant