feat: memory retention expiry sweep (#6 follow-up)#58
Merged
Conversation
A periodic server sweep (first tick at boot, then every 6h) expires memory audit rows older than retention_days; revocations are never expired, since an expired revocation would silently un-revoke memory. record_memory_activity now honors an explicit timestamp for deterministic sweeps/tests. Completes the last deferred item of the memory governance contract. Signed-off-by: Val Alexander <bunsthedev@gmail.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Implements the adapter-side half of hosted memory retention expiry (issue #6 follow-up) by adding store APIs to expire old memory-audit rows and a server background task to sweep on a fixed interval when configured.
Changes:
- Added
Store::expire_memory_activity_before/Store::expire_memory_activityplus tests ensuring audit expiry doesn’t affect revocations. - Updated
record_memory_activityto honor an explicit timestamp when provided (enabling deterministic tests/backfills). - Added a server-side background sweep that runs immediately on boot and then every 6 hours when
memory.retention_daysis set; updated docs/config notes accordingly.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| docs/memory-contract.md | Updates contract status text to reflect retention expiry completion. |
| crates/store/src/lib.rs | Adds audit-expiry APIs, adjusts timestamp handling for memory activity, and adds tests. |
| crates/server/src/main.rs | Spawns periodic retention sweep task when configured. |
| config/example.toml | Clarifies retention_days affects both runtime bytes and adapter audit expiry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+757
to
+761
| let conn = conn.lock().expect("store mutex poisoned"); | ||
| let n = conn.execute( | ||
| "DELETE FROM memory_activity WHERE at < ?1", | ||
| params![cutoff], | ||
| )?; |
Comment on lines
+753
to
+761
| pub async fn expire_memory_activity_before(&self, cutoff: &str) -> Result<usize> { | ||
| let conn = self.conn.clone(); | ||
| let cutoff = cutoff.to_string(); | ||
| tokio::task::spawn_blocking(move || { | ||
| let conn = conn.lock().expect("store mutex poisoned"); | ||
| let n = conn.execute( | ||
| "DELETE FROM memory_activity WHERE at < ?1", | ||
| params![cutoff], | ||
| )?; |
Comment on lines
+611
to
+617
| // Honor an explicit timestamp when provided (tests/backfills); | ||
| // otherwise stamp the current time. | ||
| let at = if row.at.is_empty() { | ||
| now_rfc3339() | ||
| } else { | ||
| row.at.clone() | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The last deferred item from #6: adapter-side retention expiry.
What it does
expire_memory_activity_before(cutoff)/expire_memory_activity(days)delete memory-audit rows older than the horizon. Revocations are deliberately never expired — an expired revocation would silently un-revoke memory.record_memory_activitynow honors an explicit timestamp (empty → now), which makes the sweep deterministically testable.[memory] retention_daysis set, a background task sweeps on a 6-hour interval, with the first tick firing immediately so a stale audit is trimmed at boot.Boundary
This expires the adapter's memory audit. The actual memory bytes live in coven-code and are expired there via the same
retention_daysthe policy already forwards — the bilateral half, unchanged. Per-repo retention overrides (retention is currently global) could be a later nicety; not needed for the contract.With this, every part of #6's design is implemented on the adapter side — nothing deferred remains.
Local gates:
cargo check --all-targets+cargo clippy --all-targets -- -D warnings+cargo test --all(211 passed, 0 failed).🤖 Generated with Claude Code