Skip to main content

Handler: retention.ttl_prune

Generic TTL-based row deleter. Drops rows from row.table_name where row.age_column is older than row.ttl_days days. Optional row.filter_sql narrows the scope so the same table can host multiple policies (e.g. embeddings partitioned by source_table).

Row configuration

Safety

  • Batched deletes prevent long exclusive locks on large tables. The handler loops with ctid-keyed DELETE statements until a batch returns fewer rows than batch_size.
  • Identifier validation rejects anything non-alphanumeric in table_name / age_column at runtime. String interpolation is used to build SQL (asyncpg can’t parameterize identifiers), but a malformed seed migration can’t slip SQL injection through.
  • Dry run gives a count-only preview before enabling.

Operator runbook

Enabling a seeded policy

Creating a new policy

Then:

Seeded policies (all disabled)

NameTableFilterTTL
embeddings.claude_sessionsembeddingssource_table = 'claude_sessions'30 days
embeddings.auditembeddingssource_table = 'audit'90 days
embeddings.brainembeddingssource_table = 'brain'180 days
audit_logaudit_log90 days
brain_decisionsbrain_decisions90 days

Disabling

Expected outcome

Running the seeded embeddings.claude_sessions policy once against the current DB (13,105 total embeddings, 7,095 from claude_sessions, most from the Apr 20 backfill) will delete roughly 6,369 rows — the Apr 20 backfill dump — and leave the ~200/day steady-state untouched.
  • Framework overview: Integrations
  • Database schema reference: docs/architecture/database-schema.md
  • Sibling handlers: retention.downsample, retention.temporal_summarize (deferred)
  • GH-110 (retention framework issue)
  • GH-106 (original embeddings retention issue)