D70 — Age-based retention prunes + the config-driven D31 7-year compliance floor (RESOLVED)
Architecture decision record, relocated verbatim from the retired single-file
docs/tenancy/decision log. Status, thematic clusters, and how to record a new ADR: the decision log index.
The three deferred Phase-0 prune follow-ups (docs/tracking/conduit-phase-0-prerequisites.md §B) have
LANDED as scheduled console commands, and the D31 7-year KYC/AML + audit retention floor now lives in a
single config value — config('foundation.compliance_retention_floor_days') (2555) — the one place every
compliance-relevant prune clamps to (env()-free literal; larastan forbids env() outside root config/).
foundation:prune-command-dedup-records(daily,onOneServer()->withoutOverlapping()): per-tenant under RLS, SOFT-deletescommand_dedup_recordsclaims older thanconfig('foundation.dedup_retention_days')(30 — frees the partial-unique tuple for re-use), then HARD-deletes rows pastconfig('foundation.dedup_hard_delete_after_days')(7). NO floor clamp — a dedup record is exactly-once bookkeeping, not compliance content. Iterates tenants viaStancl\Tenancy\Tenancy::query()(NOTModules\Tenancy\Models\Tenant) so the Foundation kernel keeps its zero-domain-dependency (ModuleBoundaryTest).rails:prune-vendor-logs(daily): CENTRAL (no tenant iteration), batchedDELETE ... WHERE id IN (SELECT id ... LIMIT 5000)on thepgsql_logsconnection until drained. Guarded byconfig('rails.telemetry_only')(default true) — the vendor log is transport/audit TELEMETRY, not the KYB system of record, so it prunes atconfig('rails.retention_days')(90); flip it false and the cutoff clamps UP to the floor.mirrors:prune-drift(daily): per-tenant under RLS, force-deletes RESOLVEDmirror_driftolder thanmax(config('provider-mirror.drift_retention_days') /*365*/, floor). An OPEN drift is NEVER deleted at any age (live evidence); a resolved drift on a regulated resource is evidence, hence the floor clamp.
All three are sanctioned in CqrsBoundaryTest by exact class (the dedup/vendor-log commands use the DB
facade; the drift command uses its own MirrorDrift model) — the SideEffectLogWriter /
ReconcileStaleMirrorsCommand precedents. Compliance ratification of the exact 7-year window is still
pending (§A, D31 / #3) — but it is now a one-line config change, not a code change.