Skip to content

AutoPrune deletions are invisible: no oplog record, no config knob, and same-second reaping of newborn insights #83

Description

@tars23364-bit

Summary

store.(*DB).AutoPrune (write-time retention GC) soft-deletes non-immune insights with no oplog record, no CLI-visible warning on success, and no configuration knob. Combined with same-second reaping of newly-written insights, this makes silent data loss both possible and undiagnosable from mnemon's own audit surfaces.

Observed on v0.1.14 (brew, macOS); release notes for 0.1.15–0.1.17 suggest the behavior is unchanged.

What we observed

On a production store (~2,300 live insights, heavy daily write traffic):

  • 25–70 unlogged soft-deletions per day over three weeks (~430 in 11 days). Detected only by joining insights.deleted_at against the oplog: every diff-replace and forget deletion has an oplog row, but AutoPrune deletions have nonedeleted_at gets set with no corresponding operation record.
  • Same-second create→reap: a batch writer inserting importance-3 insights had earlier items of its own batch soft-deleted by the write of later items, within the same second. Newborn insights with importance < 4 AND access_count < 3 are immediately eligible.
  • remember --no-diff does not affect it (expected — it's a separate path from duplicate/conflict detection), which surprised us when hardening against diff-replace: a "fully additive" write can still delete an unrelated insight as a side effect.
  • The only success-path trace is the auto_pruned counter in the write result; the identity of what was pruned is not reported anywhere.

Practical consequence: an agent audit process restored a soft-deleted insight, and a later unrelated remember re-reaped it hours later — invisible to oplog forensics. We now compensate by writing durable content at importance ≥ 4 and pairing restores with gc --keep, but that's convention, not a guarantee.

Requests (in priority order)

  1. Oplog entries for auto-prune — one row per pruned insight (operation: auto-prune, insight_id, and ideally the triggering write's id). This is the big one: reversible soft-deletes are a great design, but only if the deletion is discoverable.
  2. Min-age grace period — exempt insights younger than N hours/days from AutoPrune so a write burst can't eat its own batch before anything has had a chance to access them.
  3. Config knob — env var or per-store setting to tune/disable AutoPrune (threshold, or off), for deployments that prefer explicit, proposal-gated retirement.
  4. Report pruned ids in the remember result payload alongside the existing auto_pruned count.

Offer

Happy to contribute a PR for (1) and (2) if the direction sounds right — same author as PR #50 (index-aware fourth-path entity extraction). The oplog change looks small from the outside (the prune site already has the ids in hand); the grace period is one predicate in GetRetentionCandidates/prune-candidate selection.

Environment

  • mnemon 0.1.14 (mnemon-dev/tap via Homebrew), macOS (Apple Silicon)
  • Store: single SQLite store, ~2,300 live insights, ~82k edges, WAL mode
  • Detection query, for reference:
SELECT substr(i.deleted_at,1,10) AS day, COUNT(*) AS unlogged
FROM insights i
LEFT JOIN oplog o ON o.insight_id = i.id AND o.operation IN ('diff-replace','forget')
WHERE i.deleted_at IS NOT NULL AND o.insight_id IS NULL
GROUP BY day ORDER BY day;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions