Findings retention and custody

Findings owns the findings, findings_security, analytics and riskengine schemas in its service database. The consolidated hosts select the embedded StellaOps.Findings.Persistence migration chain at startup. Old library chains remain source-visible but are not selected by these hosts.

The RFC-4 forward migration records each ordinary table’s custody separately. The declaration and hash-bound proof set are in the retention source inventory. These are source and isolated PostgreSQL acceptance results, not a live deployment receipt.

Tenant offboarding

The five LIST partitions are tenant isolation, with no age-based partition window. FindingsTenantLifecycleHandler drops findings_projection, which can be reconstructed from retained ledger events. It archives ledger_events, ledger_merkle_roots, vex_decisions and vex_override_envelopes. Migration 028 retired the other two projections the handler used to drop, finding_history and triage_actions: the projector wrote them and nothing read them. RFC-4 does not change those decisions. Its ordinary-table labels do not add those tables to the partition handler or claim a general tenant erasure procedure.

Ledger tenant registry and projector checkpoints

findings.ledger_tenants (migration 018, SPRINT_20260930_006 PCD-241) is source-of-truth. It lists the tenants that have ledger events; the projector reads only registered tenants, so removing a row stops that tenant from being projected. Never delete rows on an age window. A trigger on ledger_events writes the rows, and the deploy runbook’s backfill statement re-derives them from the leaf partitions after an ATTACH PARTITION of a pre-filled table. ledger_projection_offsets keeps its class and now holds one cursor per (worker_id, tenant_id); resetting a cursor is still an explicit replay.

Raw runtime traces

findings:ledger:runtime:retention configures the existing hosted worker:

SettingDefaultEffect
EnabledtrueRun the retention worker.
StartDelay00:05:00Delay the first sweep.
SweepInterval1.00:00:00Repeat daily.
DefaultRetention90.00:00:00Delete traces captured before the tenant cutoff.
TenantsemptyDiscover identifiers of all tenants with stored traces, including expired-only tenants. A nonempty list selects explicit tenants.
PerTenantemptyPositive durations override the default; these tenant keys are included in the sweep. Nonpositive overrides fall back to the positive default.

Default retention and the interval must be positive; the start delay cannot be negative. Startup validation rejects invalid settings. Payload deletion always uses a tenant predicate. Discovery reads tenant identifiers only. A tenant failure does not prevent other tenants from being processed; a discovery failure is retried at the next interval.

Trace aggregates, runtime timeline events and captured score results survive raw trace expiry. They are retained records: expired input observations cannot be reconstructed from the remaining traces. No new cleanup window is applied to signed artifacts, risk explanations, air-gap receipts or operator settings.

Projections and recovery

Advisory corpus generations can be reconstructed from retained owner publications. Existing reclamation preserves serving and recovery candidates, respects recovery holds, and drains only eligible generations. The operator recovery hold is retained control state and never an expiring cache entry.

Migration 032 adds the system recovery record findings_security.advisory_reclamation_progress (source-of-truth). Its identity is (generation_id, table_name), with exactly four allowed drain table names. last_key is one scalar natural key; last_row_id is the membership surrogate; deleted_rows counts committed deletions and completed records a committed empty-range probe. The record holds no advisory document or projection payload. Cursor and deletion share one fenced transaction, so a crash or rollback cannot certify an uncommitted deletion. The primary key indexes every reader and the generation FK; a generation has at most four records, and native cascade removes them with that claimed generation. No age window, legal hold or provenance lifetime changes. Loss of this operational record can safely restart from the range head but loses efficient dead-range resume; it must not be synthesized ahead of actual committed deletion. Existing claims are not reset or backfilled with guessed keys by the upgrade. This is the narrow system-record exemption described by the data-identity register; it does not exempt the child advisory data or generation lifecycle.

SBOM snapshot staging is replaced under a fenced snapshot session; commit replaces serving versions atomically. Version retirement tombstones prevent stale event resurrection until a complete owner snapshot replaces the epoch. Match rows and their published fence must agree on the exact manifest and SBOM checkpoint.

The durable disposition cache is rebuilt only from a successful complete tenant ledger snapshot. Its name does not imply a TTL. Scanner projections rebuild through the existing tenant-scoped event path and fenced epoch reset; missing replay input remains a failure rather than a clean empty result.

Withdrawn projections

The current security reader uses active-generation projections. Historical security_finding_projection records remain preserved because the withdrawn writer’s past output is not reconstructible from current inputs. No production reader or writer is restored for that table.

The unused disposition, SBOM component and SBOM graph projections retire only when empty. The forward migration locks all three tables, checks all of them before any drop, refuses nonempty data with SQLSTATE 55000, and does not use CASCADE. If refused, preserve and review the records before any separate disposition decision. The matching retirement in the retained Security chain closes its old declarations; it does not activate that chain or touch another database.

Verification

Verified against production source commit d0b8370d778909f3363e79998e62bbb5a9d00a12. The verification receipt records executed tests, negative controls and the source census.

Run the repository’s targeted xUnit helper for ConsolidatedFindingsSchemaTests, FindingsTenantLifecycleHandlerTests, RuntimeTraceRetentionWorkerTests and RuntimeTraceRetentionIntegrationTests. The migration tests cover fresh/repeated convergence and refusal for each nonempty retired projection. The trace integration test exercises the real repository and PostgreSQL cleanup path.

The five P13 gates and the verified source commit are recorded in the RFC-4 receipt in the Program sprint.