Bound audit-event growth via retention pruning
Summary
Closes the one critical scaling gap found in a full review of the merged audit stream (#411/#412/#413): the event table had no retention — audit events outlived the deployments they describe and grew without limit.
-
RetentionPolicyEnforcernow prunes audit events (snapshots cascade via FK) for the server tasks it cleans up, viaIEventService.PruneByServerTaskIdsAsync. The stream stays bounded under the same deployment-centric policy that already governs logs/tasks/checkpoints. - Add the missing partial index
event(server_task_id, id) WHERE server_task_id IS NOT NULL— the original migration had none, and both the per-task audit feed and this prune need it.
Non-breaking: additive method + one prune call placed beside the existing server-task child deletes + a new index. Document-audit events (config edits, no server task) are deliberately not pruned here — they are low-volume; a time-based/partitioned sweep for them is the next step.
Test plan
-
Integration: PruneByServerTaskIdsAsyncdeletes exactly the targeted task's events, leaving other tasks' events AND config-audit orphans (no server task) intact; empty input is a no-op. -
Non-breaking: 26 Retention integration tests green with the enforcer now pruning events; 24 Events integration green; full solution build clean. -
The new index migration applies cleanly (verified against real Postgres in the integration run).
Review context (gaps found, scoped as follow-ups)
A deep audit confirmed the architecture is generic/simple and the implemented paths well-tested. Remaining items, specced in memory/history_parity_plan.md: generic (document_type, document_id) self-reference to per-doc-filter the long-tail documents (lifecycle, library variable set, machine policy, team) + register them; time-based/partition prune for config-audit events; bulk-op (ExecuteUpdate/Delete) bypass is by-design (audit-worthy mutations must use tracked SaveChanges); provenance polish; frontend.