Initial commit

This commit is contained in:
fraqtal
2026-07-12 08:15:46 +00:00
commit ee0fec0691
1397 changed files with 127242 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
---
id: 04-retention-purge-job
epic: compliance-manifests-pii-retention-subprocessors
title: Background retention purge job in core-shared
type: technical-story
status: done
feature: core-shared
depends-on: [01-pii-retention-type-primitives]
blocks: []
created: 2026-05-18T17:52:09Z
updated: 2026-05-18T18:58:23.920Z
---
## Goal
Add `core-shared/payload/retention-purge/retention-purge.job.ts` — a module that walks every Payload collection's `custom.retention.purgeSchedule`, registers a per-collection scheduled job via `IJobQueue`, queries rows whose active-retention period has elapsed, then either pseudonymizes or hard-deletes each row while emitting one `IAuditLog.record(...)` entry per row. Optional `auditLog` is skipped gracefully when `core-audit` is not wired.
## Why
Retention without automated purge is a compliance statement with no enforcement. The job makes `custom.retention` actionable: declarations in code become real deletes on schedule.
## Done when
- `packages/core-shared/src/payload/retention-purge/retention-purge.job.ts` exists; receives `IJobQueue` + `SanitizedConfig` (+ optional `IAuditLog`) via constructor/factory.
- At registration time, one scheduled job is created per collection that declares `custom.retention.purgeSchedule`.
- Job body queries by `createdAt` (trigger `from-creation`) or `updatedAt` (trigger `from-last-access`), applies `pseudonymize` (NULL PII fields) or `hard-delete` (Payload cascade delete) per `postDeletion.action`.
- Each processed row emits `auditLog.record({ action: "DELETE", subject: row.id, actor: "system", reason: "retention-policy" })`; when `auditLog` is undefined the emission is silently skipped.
- Unit tests with an in-memory Payload mock cover: schedule registration per collection, row matching per trigger type, audit emission, pseudonymize vs hard-delete branches, and graceful auditLog skip.
- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass.
## In scope
- `packages/core-shared/src/payload/retention-purge/retention-purge.job.ts` + sibling unit test.
- Uses existing `IJobQueue` from `core-shared/jobs` and `IAuditLog` from `core-audit` (optional injection).
## Out of scope
- App-side boot wiring — the module is the deliverable; consumers integrate it into their `bindAll()`.
- `lastAccessedAt` field hook for true "from-last-access" tracking (deferred; see PRD Q2).
- Advisory lock for concurrent purge guard (deferred; see PRD Q4).
## Tasks
- [x] Add `retention-purge.job.ts` + unit tests to `packages/core-shared/src/payload/retention-purge/` — walks `custom.retention.purgeSchedule` per collection, registers scheduled jobs via `IJobQueue`, executes pseudonymize or hard-delete per `postDeletion.action`, emits optional audit entry per row, unit tests cover all branches including graceful auditLog skip — all gates pass on this commit.