C-84/#224: make the 2026-11-17 key expiry fire on its own - #289
Conversation
#224 says "what is asked of this repo: nothing yet", and C-84 goes further: "The honest position is that this is a date to act on, not a mechanism to build, and inventing a mechanism would be building the wrong thing to feel busy." That verdict is about a key-VALIDITY preflight — an authenticated call at startup — and it stands. This is not that. No authenticated call, no credential, no network: a calendar and two declared datetimes. What it fixes is C-84's own trigger. Its first arm reads "act when the un_fao delivery is next scheduled within a month of it", which is a trigger nobody can notice: it fires in someone's memory or not at all. This repository has already written that down twice — ADR-014 §4, and the withdrawn third arm of C-94's trigger. A foreseeable total outage 90 days out deserves better than memory. tests/test_credential_expiry.py declares both expiries and fails from 30 days before the earlier one, naming the dates, the 3h35m gap (which is C-84's actual finding — it is not a stagger, neither key can carry traffic while the other is replaced), who owns the rotation (operator; views-appwrite#12, split at views-faoapi#338), and the two honest ways to make it pass: rotate and update the constant, or update the constant if a key was replaced early. Deleting the test is the third way and the message says it is the one that produces the outage. The dates cannot be derived from anywhere — the coordinate registry records secret SLOTS, never values or their lifetimes, which is #224's closing observation: no amount of drift detection surfaces this one. They come from the operator console read of 2026-08-05 (þing-02 A3(i)), and a second test pins them and the gap against C-84 so a typo fails rather than quietly moving the tripwire. Verified by simulating the clock at 2026-10-25: fires with the full message, 23 days out. Passes today at 90 days. Suite: 463 passed, 3 skipped, 37 xfailed. ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two findings on my own tripwire. 1. THE FIRING BRANCH WAS UNPROVEN. The guard's failing path would first have executed in October, live, on the day it matters — which is C-102's exact lesson: a guard that has never run is unproven however carefully it was written. The decision and the message are now pure functions of a date (`days_until_earliest_expiry`, `expiry_warning`), and the failing branch is exercised against a simulated 2026-10-25. A third test asserts silence well outside the window, because a tripwire that is always red is one nobody reads. 2. Naive datetimes, now documented as deliberate rather than left to look like an oversight: the console reports local time and the window is 30 days, so an hour either way changes nothing. Adding tzinfo would imply a precision the source does not have. Mutation-proven in both directions, and the second one caught a hole I had made myself: shrinking LEAD_DAYS to 0 silently neuters the tripwire, and my first pass did not catch it because I had deleted the assertion that would — mid-cleanup, as a half-written line. Restored: a date 23 days out must be inside the window. LEAD_DAYS = 0 -> 1 failed LEAD_DAYS = 400 -> 2 failed typo an expiry -> 2 failed Suite: 465 passed, 3 skipped, 37 xfailed. ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Six findings. Two were design faults that would each have ended with the test deleted, which for a tripwire is the only failure mode that matters. 1. HIGH — the pin made the tripwire's own prescribed fix impossible. Its message says "rotate, then update KEY_EXPIRY"; a second test hardcoded the current literals and would have failed on exactly that edit, with a message saying "re-read the entry rather than adjusting the constant to match". Measured: setting the constant to a rotated date turned 2 tests and 5 assertions red. A guard that refuses its own documented remediation is worse than no guard, and in November it lands on the one person who cannot route around it. The pin is gone; verified that a simulated rotation now passes. 3. MEDIUM (and the one that would have got it deleted) — from 2026-10-18 the gate goes red for EVERY unrelated pull request, clearable only by an operator console action this repository cannot perform. That is what pyproject.toml already says about ruff, citing ADR-014 §3: a gate that starts red gets switched off. `ACKNOWLEDGED_UNTIL` is the in-repo escape — a declared, reviewed, dated edit meaning "seen, and being acted on" — and it cannot be set on or after the expiry, so it postpones attention and never replaces it. Verified both ways: setting it clears the gate; setting it past the expiry fails. 2. MEDIUM — the firing-branch proof hardcoded 2026-10-25 / 23 days, both derived from today's constant, so after any legitimate rotation the probe would fall outside the window and the C-102 claim would be void. The probe is now derived from KEY_EXPIRY and survives rotation. 4. LOW — the LEAD_DAYS guard only caught shrinking below 23; 23 through 30 all passed, so a week could be shaved with a green suite. Floored at 30, with widening left free. 5. LOW — the register claimed the datetimes were "pinned against this entry". Nothing reads the register; it was same-file literal duplication. Claim removed along with the pin. 6. LOW — after the expiry the message read "expire in -3 days", and the gap rendered "3:35:00" where the entry title says 3h35m. Now "TODAY" / "3 days ago — the seam is already dead", and "3h35m". This is the text an operator reads while the seam is down. Suite: 467 passed, 3 skipped, 37 xfailed. ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review round —
|
| probe | result |
|---|---|
rotate + update KEY_EXPIRY |
passes |
ACKNOWLEDGED_UNTIL = 2026-11-01 |
passes — gate cleared |
ACKNOWLEDGED_UNTIL = 2027-01-01 (past expiry) |
fails |
The rest
| # | finding | fix |
|---|---|---|
| 2 | the C-102 firing proof hardcoded 2026-10-25 / 23 days, both derived from today's constant — so after any legitimate rotation the probe falls outside the window and the proof is void |
probe derived from KEY_EXPIRY; survives rotation |
| 4 | the LEAD_DAYS guard only caught shrinking below 23 — 23 through 30 all passed, so a week could be shaved with a green suite |
floored at 30 (widening left free). Now fails at 29, 23 and 0 |
| 5 | the register claimed the datetimes were "pinned against this entry". Nothing reads the register — it was same-file literal duplication | claim removed with the pin |
| 6 | after the expiry the message read "expire in -3 days", and the gap rendered 3:35:00 where the entry title says 3h35m |
"TODAY" / "3 days ago — the seam is already dead", and 3h35m. This is the text an operator reads while the seam is down |
Note on what this deliberately is not
C-84 rejected a key-validity preflight — "a date to act on, not a mechanism to build" — and that verdict stands. No authenticated call, no credential, no network here; only a calendar. What was missing was that C-84's own trigger ("act when the delivery is next scheduled within a month of it") is a trigger nobody can notice, which is the defect ADR-014 §4 forbids and which withdrew the third arm of C-94's trigger.
CI: 491 passed, 5 skipped, 37 xfailed, 0 failed.
What I did not build, and why that matters here
#224 says "what is asked of this repo: nothing yet", and C-84 goes further:
That verdict is about a key-validity preflight — an authenticated call at startup — and it stands. This is not that. No authenticated call, no credential, no network.
What was actually missing
C-84's own trigger:
That is a trigger nobody can notice. It fires in someone's memory or not at all. This repository has written that down twice already — ADR-014 §4, and the withdrawn third arm of C-94's trigger, dropped for precisely this reason. A foreseeable total outage, known 90 days in advance, should not depend on anyone remembering it.
The tripwire
tests/test_credential_expiry.pydeclares both expiries and fails from 30 days out, naming:The dates cannot be derived from anywhere. The coordinate registry records secret slots, never values or their lifetimes — which is #224's closing observation: no amount of drift detection will surface this one. They come from the operator console read of 2026-08-05 (þing-02 A3(i)). A second test pins both datetimes and the gap against C-84, so a typo fails rather than quietly moving the tripwire.
Verification
Simulated the clock at 2026-10-25 — fires with the full message, 23 days out. Passes today at 90 days.
Suite: 463 passed, 3 skipped, 37 xfailed.
ruffclean.The honest objection, stated
A test that goes red on a date is a time bomb in CI. That is the design, not a side effect — and the cost is real: after rotation someone must update the constant or the build stays red. The message says so, and updating it is part of rotating. Worth it against a Tier 2 foreseeable total outage; say so on this PR if you disagree and I will drop it.