diff --git a/infra/earnings_archive.tf b/infra/earnings_archive.tf index 3c3b1d50..07bbc8aa 100644 --- a/infra/earnings_archive.tf +++ b/infra/earnings_archive.tf @@ -51,10 +51,29 @@ resource "google_storage_bucket" "earnings_archive" { force_destroy = false public_access_prevention = "enforced" - # NO lifecycle_rule: the 35-01 gate was signed `approve` with NO retention cap - # (D-27.9 amendment, no conditions) — the archive is retained indefinitely. - # Contrast the transient earnings-audio-handoff bucket (cloud_run.tf) which - # reaps at age = 1 day. + # NO general lifecycle-delete rule: the 35-01 gate was signed `approve` with NO + # retention cap (D-27.9 amendment, no conditions) — the ARCHIVE is retained + # indefinitely. Contrast the transient earnings-audio-handoff bucket + # (cloud_run.tf) which reaps at age = 1 day. + # + # ONE narrow exception, scoped by prefix to `earnings/_preflight/` ONLY: the + # archive-repo CLIs (earnings-archive commit 944d6fa) write a unique + # per-invocation credential-probe object (`probe-.txt`, ~9 bytes) before + # touching any source feed, and the cleanup delete is deliberately + # best-effort — so probe leftovers can accumulate when deletes fail (codex + # review P3, 2026-07-14). These are ephemeral credential probes, NOT archive + # artifacts (audio lives under `earnings/audio/`, transcripts under + # `earnings/transcripts/` — neither matches this prefix), so reaping them + # does not touch the D-27.9 retained-archive decision. + lifecycle_rule { + condition { + age = 1 + matches_prefix = ["earnings/_preflight/"] + } + action { + type = "Delete" + } + } labels = { phase = "35"