tidaldb/.sdlc/features/m9-retroactive-purge/audit.md

40 lines
2.4 KiB
Markdown

# Audit: Retroactive Signal Purge
## Spec Compliance
| Requirement | Status | Notes |
|-------------|--------|-------|
| FR-1: Contribution logging on signal write | PASS | `try_cohort_attribution` calls `contribution_log.push()` after each `cohort_ledger.record()` |
| FR-2: `retract()` on ledger with CAS floor at 0 | PASS | `HotSignalState::subtract_contribution`, `BucketedCounter::subtract_bucket` both implemented with 0 floor |
| FR-3: PurgeManifest persisted to `Tag::PurgeManifest` key | PASS | `db/purge.rs` writes to `encode_key(user_id, PurgeManifest, purge_id.to_be_bytes())` |
| FR-4: WAL event (original spec) | DEFERRED | Conscious design decision: storage manifest used instead; WAL replay covered by `m9-purge-rematerialization` |
| FR-5: `request_community_purge()` public API | PASS | Returns `(PurgeId, PurgeManifest)` |
| FR-6: Idempotency — second purge is no-op | PASS | Log is drained on first purge; second purge finds empty log |
| FR-7: Thread safety via `Mutex<VecDeque>` | PASS | All log access is mutex-guarded |
## Architecture Compliance
- Single responsibility: `PurgeCoordinator` handles in-memory retraction only; `db/purge.rs` handles storage I/O. Clean separation.
- No WAL dependency from purge path: purge writes to storage directly, avoids coupling to WAL format.
- Lock-free hot path: signal writes only hold the contribution log mutex for `push()`, which is O(1). CAS loops in hot/warm tier are unblocked.
- `#[allow(dead_code)]` on struct fields that are wired but not yet read by external code — appropriate for staged implementation.
## Security / Correctness
- `subtract_contribution` accounts for decay since contribution time — correctly removes the decayed portion of weight, not the full original weight.
- Score floors at 0.0 throughout — no negative scores possible.
- Multi-user isolation: `drain_for(user_id, cohort)` filters by both dimensions; other users' records untouched.
- `evicted_before_purge` in manifest signals to `m9-purge-rematerialization` when a full WAL replay is needed for correctness.
## Test Coverage
- 5 unit tests in `cohort::purge::tests`
- ~8 unit tests in `cohort::contribution::tests`
- 8 integration tests in `tests/m9_retroactive_purge.rs`
- 1299 lib tests passing
- fmt clean, build clean
## Verdict
APPROVED. Implementation correctly satisfies all in-scope requirements. The WAL-event deferral is a documented design decision consistent with the existing re-materialization architecture.