From 81b2b3e0c7c26c52947ae3f2b88a9f0f6e88bd89 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Wed, 19 Aug 2026 11:51:27 +0200 Subject: [PATCH 1/3] feat(tests): make the 2026-11-17 key expiry fire on its own (C-84, #224) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #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) --- reports/technical_risk_register.md | 6 +- tests/test_credential_expiry.py | 92 ++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 tests/test_credential_expiry.py diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index 66ba62c..66015ff 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -700,7 +700,7 @@ It is a worked example wearing a risk's clothes. **Give it a real trigger or mov | ID | C-84 | | Tier | 2 — not silent. The delivery fails loudly and completely, which is the correct behaviour and also the whole problem: there is no degraded mode, no fallback identity, and the date is known in advance. A foreseeable total outage that nobody has scheduled work against is a structural risk, not an operational surprise. | | Source | views-appwrite coordinate registry v1.4.3/v1.4.4 — operator console read, 2026-08-05 (þing-02 A3(i)) | -| Trigger | **A date, unusually — 2026-11-17.** The registry records `VIEWS Pipeline Core` expiring 12:35 and `UN FAO` 16:10 that afternoon. Act when the un_fao delivery is next scheduled within a month of it, or when anyone plans a rotation, whichever is first. | +| Trigger | **A date, unusually — 2026-11-17.** The registry records `VIEWS Pipeline Core` expiring 12:35 and `UN FAO` 16:10 that afternoon. Act when the un_fao delivery is next scheduled within a month of it, or when anyone plans a rotation, whichever is first. **Since 2026-08-19 the first arm fires by itself**: `tests/test_credential_expiry.py` goes red from 2026-10-18, so the date no longer depends on anyone remembering it. | | Owner | Simon, and only Simon — issuing and installing keys is a console action. This entry exists so the date is visible from *this* repo's planning surface rather than only from the platform's. | | Location | `views_postprocessing/{unfao,crafd}/appwrite_env.py` — the declared coordinates; the values live in the environment and the registry, never here. | @@ -712,6 +712,10 @@ The FAO delivery authenticates with the `UN FAO` key. That key expires **2026-11 **Deliberately not fixed here, and the reason is C-84's own shape.** A preflight that checks key validity means an authenticated call at startup, and the only project to make it against is production — which **þing-01 D2** forbids for tests and this would not quite be — and which that verdict explicitly permits as *read-only preflight validation*, so the obstacle here is the authenticated call, not the prohibition (see C-95). 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. Registered so the date is not discovered by an outage. +**The trigger now fires on its own (2026-08-19), and this is not the mechanism above.** The first arm of the trigger — *"act when the un_fao delivery is next scheduled within a month of it"* — was a trigger nobody could notice: it fired in someone's memory or not at all, which is the same defect that withdrew the third arm of C-94's trigger and which ADR-014 §4 exists to forbid. `tests/test_credential_expiry.py` declares the two expiries and fails from 30 days out, naming the dates, the 3h35m gap, who owns the rotation (operator; views-appwrite#12, key split 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. + +**It is emphatically not the key-validity preflight this entry rejected.** No authenticated call, no credential, no network — a calendar and two declared datetimes. The rejection above stands and is unaffected: what was wrong was building a mechanism to *discover* a fact already known; what was missing was making the known fact impossible to forget. A second test pins the two datetimes and the gap against this entry, so a typo in the constant fails rather than quietly moving the tripwire. + Cross-refs: **C-81** (the same operator session's other half — branch protection and the CI token), **C-27** (no rotation mechanism for a secret value upstream), **C-57** (the pinned-registry detector, which is how this arrived here at all — it demanded the v1.4.4 bump and the bump is what surfaced the expiry), þing-02 A3(i), views-appwrite C-65 and C-66. --- diff --git a/tests/test_credential_expiry.py b/tests/test_credential_expiry.py new file mode 100644 index 0000000..b357555 --- /dev/null +++ b/tests/test_credential_expiry.py @@ -0,0 +1,92 @@ +"""A dated tripwire for the platform key expiry (register C-84, issue #224). + +**What this is not.** C-84 considered a key-validity preflight and rejected it: *"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 stands, and this is not that. +There is no authenticated call here, no credential, and no network — only a calendar. + +**What this is.** C-84's trigger reads *"act when the un_fao delivery is next scheduled +within a month of it, or when anyone plans a rotation, whichever is first."* That is a +trigger nobody can notice: it fires in someone's memory or not at all, and this +repository has written down more than once that a trigger nobody can notice is a wish +(ADR-014 §4; the third arm of C-94's trigger was withdrawn for exactly this). The date +is known 90 days in advance and the consequence is a total, foreseeable outage of every +identity on the seam. So the trigger is made to fire on its own. + +**Why the date is declared here rather than read from anywhere.** It cannot be derived: +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. The +values below come from an operator console read on 2026-08-05, recorded in views-appwrite +registry v1.4.4 and in C-84. + +**When this fails, there are exactly two honest responses**, and both are stated in the +failure: rotate the keys and update the dates below, or — if a key was replaced early — +update the dates below to the new expiry. Deleting the test is the third option and it +is the one that produces the outage. +""" + +from __future__ import annotations + +from datetime import date, datetime + +import pytest + +#: The platform keys this repository's deliveries authenticate with, and when they die. +#: Read from the operator console 2026-08-05 (þing-02 A3(i)); recorded in views-appwrite +#: coordinate registry v1.4.4 and in register C-84. Both of this repo's paths — the FAO +#: delivery and the CRAF'd delivery — run under `VIEWS Pipeline Core`, the earlier one. +KEY_EXPIRY: dict[str, datetime] = { + "VIEWS Pipeline Core": datetime(2026, 11, 17, 12, 35), + "UN FAO": datetime(2026, 11, 17, 16, 10), +} + +#: How long before the earliest expiry this starts failing. A month, because that is the +#: window C-84's own trigger names, and because rotation needs an overlap period that +#: has to be scheduled with an operator rather than squeezed in on the day. +LEAD_DAYS = 30 + + +def test_the_platform_keys_are_not_about_to_expire(): + """Fails 30 days out, so the date cannot pass unnoticed (C-84, #224).""" + earliest_name = min(KEY_EXPIRY, key=lambda k: KEY_EXPIRY[k]) + earliest = KEY_EXPIRY[earliest_name] + days_left = (earliest.date() - date.today()).days + if days_left > LEAD_DAYS: + return + + spread = max(KEY_EXPIRY.values()) - min(KEY_EXPIRY.values()) + listing = "\n".join( + f" {name:22} {when:%Y-%m-%d %H:%M}" for name, when in sorted(KEY_EXPIRY.items(), key=lambda kv: kv[1]) + ) + pytest.fail( + f"the platform Appwrite keys expire in {days_left} days:\n{listing}\n\n" + f"Both of this repository's delivery paths — un_fao and crafd — authenticate " + f"with {earliest_name!r}, the earlier one. The two keys are " + f"{spread} apart, which is not a stagger: neither can carry traffic while the " + "other is replaced, so a rotation that assumes a window has none. After the " + "later time every identity on the seam is dead at once — model and ensemble " + "writes, both partner deliveries, and FAO's own read access.\n\n" + "This repository cannot rotate anything and must not hold credentials " + "(þing-01 D3). Issuing and installing keys is an operator console action " + "(views-appwrite#12); the key split is views-faoapi#338.\n\n" + "Two honest ways to make this pass: rotate, then update KEY_EXPIRY above; or, " + "if a key was replaced early, update KEY_EXPIRY to the new expiry. Deleting " + "this test is the third way and it is the one that produces the outage " + "(register C-84, issue #224)." + ) + + +def test_the_recorded_expiries_are_the_ones_C_84_names(): + """The dates are load-bearing, so a typo must fail here rather than silently. + + A tripwire keyed to a date nobody re-checked is worth very little; this pins the + two values against the entry that sourced them, so editing one without the other + is a failure rather than a drift. + """ + assert KEY_EXPIRY["VIEWS Pipeline Core"] == datetime(2026, 11, 17, 12, 35) + assert KEY_EXPIRY["UN FAO"] == datetime(2026, 11, 17, 16, 10) + gap = KEY_EXPIRY["UN FAO"] - KEY_EXPIRY["VIEWS Pipeline Core"] + assert gap.total_seconds() == 3 * 3600 + 35 * 60, ( + "C-84's finding is the 3h35m gap, not the dates themselves — if these move, " + "re-read the entry rather than adjusting the constant to match" + ) From 3147ee5d9940e818500387851d1e6f33b5a4a711 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Wed, 19 Aug 2026 11:53:36 +0200 Subject: [PATCH 2/3] =?UTF-8?q?fix(tests):=20prove=20the=20expiry=20tripwi?= =?UTF-8?q?re=20fires=20=E2=80=94=20/review-diff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- reports/technical_risk_register.md | 2 +- tests/test_credential_expiry.py | 81 +++++++++++++++++++++++------- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index 66015ff..939d130 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -714,7 +714,7 @@ The FAO delivery authenticates with the `UN FAO` key. That key expires **2026-11 **The trigger now fires on its own (2026-08-19), and this is not the mechanism above.** The first arm of the trigger — *"act when the un_fao delivery is next scheduled within a month of it"* — was a trigger nobody could notice: it fired in someone's memory or not at all, which is the same defect that withdrew the third arm of C-94's trigger and which ADR-014 §4 exists to forbid. `tests/test_credential_expiry.py` declares the two expiries and fails from 30 days out, naming the dates, the 3h35m gap, who owns the rotation (operator; views-appwrite#12, key split 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. -**It is emphatically not the key-validity preflight this entry rejected.** No authenticated call, no credential, no network — a calendar and two declared datetimes. The rejection above stands and is unaffected: what was wrong was building a mechanism to *discover* a fact already known; what was missing was making the known fact impossible to forget. A second test pins the two datetimes and the gap against this entry, so a typo in the constant fails rather than quietly moving the tripwire. +**It is emphatically not the key-validity preflight this entry rejected.** No authenticated call, no credential, no network — a calendar and two declared datetimes. The rejection above stands and is unaffected: what was wrong was building a mechanism to *discover* a fact already known; what was missing was making the known fact impossible to forget. Three companion tests keep the tripwire honest rather than decorative: the firing branch is exercised against a simulated 2026-10-25 (**C-102** — a guard that has never run is unproven, and this one would otherwise first execute live in October, on the day it matters); the two datetimes and the 3h35m gap are pinned against this entry, so a typo fails rather than quietly moving the tripwire; and the lead window is asserted in both directions, because shrinking `LEAD_DAYS` is how a dated guard gets neutered without anyone deleting it. All three are mutation-proven. Cross-refs: **C-81** (the same operator session's other half — branch protection and the CI token), **C-27** (no rotation mechanism for a secret value upstream), **C-57** (the pinned-registry detector, which is how this arrived here at all — it demanded the v1.4.4 bump and the bump is what surfaced the expiry), þing-02 A3(i), views-appwrite C-65 and C-66. diff --git a/tests/test_credential_expiry.py b/tests/test_credential_expiry.py index b357555..edc053c 100644 --- a/tests/test_credential_expiry.py +++ b/tests/test_credential_expiry.py @@ -35,6 +35,9 @@ #: Read from the operator console 2026-08-05 (þing-02 A3(i)); recorded in views-appwrite #: coordinate registry v1.4.4 and in register C-84. Both of this repo's paths — the FAO #: delivery and the CRAF'd delivery — run under `VIEWS Pipeline Core`, the earlier one. +#: Naive datetimes, deliberately: the console reports local time and the window here is +#: 30 days, so an hour of timezone either way changes nothing. Do not add tzinfo to make +#: it look rigorous — it would imply a precision the source does not have. KEY_EXPIRY: dict[str, datetime] = { "VIEWS Pipeline Core": datetime(2026, 11, 17, 12, 35), "UN FAO": datetime(2026, 11, 17, 16, 10), @@ -46,36 +49,80 @@ LEAD_DAYS = 30 -def test_the_platform_keys_are_not_about_to_expire(): - """Fails 30 days out, so the date cannot pass unnoticed (C-84, #224).""" - earliest_name = min(KEY_EXPIRY, key=lambda k: KEY_EXPIRY[k]) - earliest = KEY_EXPIRY[earliest_name] - days_left = (earliest.date() - date.today()).days - if days_left > LEAD_DAYS: - return +def days_until_earliest_expiry(today: date) -> tuple[str, int]: + """(which key dies first, how many days until it does) — a pure function of a date. + + Split out so the FAILING branch below can be exercised in the suite. A tripwire + whose firing path has never run is unproven however carefully it was written + (register C-102), and this one would otherwise first execute in October, live, on + the day it matters. + """ + name = min(KEY_EXPIRY, key=lambda k: KEY_EXPIRY[k]) + return name, (KEY_EXPIRY[name].date() - today).days + +def expiry_warning(name: str, days_left: int) -> str: + """What the tripwire says when it fires. Separate so it can be read without waiting.""" spread = max(KEY_EXPIRY.values()) - min(KEY_EXPIRY.values()) listing = "\n".join( - f" {name:22} {when:%Y-%m-%d %H:%M}" for name, when in sorted(KEY_EXPIRY.items(), key=lambda kv: kv[1]) + f" {key:22} {when:%Y-%m-%d %H:%M}" + for key, when in sorted(KEY_EXPIRY.items(), key=lambda kv: kv[1]) ) - pytest.fail( + return ( f"the platform Appwrite keys expire in {days_left} days:\n{listing}\n\n" f"Both of this repository's delivery paths — un_fao and crafd — authenticate " - f"with {earliest_name!r}, the earlier one. The two keys are " - f"{spread} apart, which is not a stagger: neither can carry traffic while the " - "other is replaced, so a rotation that assumes a window has none. After the " - "later time every identity on the seam is dead at once — model and ensemble " - "writes, both partner deliveries, and FAO's own read access.\n\n" + f"with {name!r}, the earlier one. The two keys are {spread} apart, which is not " + "a stagger: neither can carry traffic while the other is replaced, so a rotation " + "that assumes a window has none. After the later time every identity on the seam " + "is dead at once — model and ensemble writes, both partner deliveries, and FAO's " + "own read access.\n\n" "This repository cannot rotate anything and must not hold credentials " "(þing-01 D3). Issuing and installing keys is an operator console action " "(views-appwrite#12); the key split is views-faoapi#338.\n\n" - "Two honest ways to make this pass: rotate, then update KEY_EXPIRY above; or, " - "if a key was replaced early, update KEY_EXPIRY to the new expiry. Deleting " - "this test is the third way and it is the one that produces the outage " + "Two honest ways to make this pass: rotate, then update KEY_EXPIRY; or, if a key " + "was replaced early, update KEY_EXPIRY to the new expiry. Deleting this test is " + "the third way and it is the one that produces the outage " "(register C-84, issue #224)." ) +def test_the_platform_keys_are_not_about_to_expire(): + """Fails 30 days out, so the date cannot pass unnoticed (C-84, #224).""" + name, days_left = days_until_earliest_expiry(date.today()) + if days_left > LEAD_DAYS: + return + pytest.fail(expiry_warning(name, days_left)) + + +def test_the_tripwire_actually_fires_inside_the_lead_window(): + """The failing branch, exercised now rather than first executing in October. + + C-102: a guard that has never run is unproven, however carefully it was written. + """ + name, days_left = days_until_earliest_expiry(date(2026, 10, 25)) + assert name == "VIEWS Pipeline Core", "the earlier key is the one both paths use" + assert days_left == 23 + assert days_left <= LEAD_DAYS, ( + f"a date 23 days out must be inside the lead window, and LEAD_DAYS is " + f"{LEAD_DAYS}. Shrinking it is how this tripwire gets quietly neutered — " + "the window is C-84's own, and rotation needs an overlap period scheduled " + "with an operator rather than squeezed in on the day." + ) + message = expiry_warning(name, days_left) + assert "expire in 23 days" in message + assert "3:35:00 apart" in message, "the gap is C-84's finding, not the dates" + assert "not a stagger" in message + assert "views-appwrite#12" in message, "the reader must be told who can act" + assert "Deleting this test" in message, ( + "the third option must be named, or it is the one that gets taken quietly" + ) + + +def test_the_tripwire_is_silent_outside_the_lead_window(): + _, days_left = days_until_earliest_expiry(date(2026, 1, 1)) + assert days_left > LEAD_DAYS, "a tripwire that is always red is a tripwire nobody reads" + + def test_the_recorded_expiries_are_the_ones_C_84_names(): """The dates are load-bearing, so a typo must fail here rather than silently. From 2eddd0a3a7b1e1b0a681ca41bd2674f18d6f3f46 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Wed, 19 Aug 2026 12:01:13 +0200 Subject: [PATCH 3/3] =?UTF-8?q?fix(tests):=20give=20the=20expiry=20tripwir?= =?UTF-8?q?e=20an=20escape=20hatch=20=E2=80=94=20/code-review=20high?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- reports/technical_risk_register.md | 4 +- tests/test_credential_expiry.py | 191 ++++++++++++++++++----------- 2 files changed, 120 insertions(+), 75 deletions(-) diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index 939d130..58ea10a 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -714,7 +714,9 @@ The FAO delivery authenticates with the `UN FAO` key. That key expires **2026-11 **The trigger now fires on its own (2026-08-19), and this is not the mechanism above.** The first arm of the trigger — *"act when the un_fao delivery is next scheduled within a month of it"* — was a trigger nobody could notice: it fired in someone's memory or not at all, which is the same defect that withdrew the third arm of C-94's trigger and which ADR-014 §4 exists to forbid. `tests/test_credential_expiry.py` declares the two expiries and fails from 30 days out, naming the dates, the 3h35m gap, who owns the rotation (operator; views-appwrite#12, key split 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. -**It is emphatically not the key-validity preflight this entry rejected.** No authenticated call, no credential, no network — a calendar and two declared datetimes. The rejection above stands and is unaffected: what was wrong was building a mechanism to *discover* a fact already known; what was missing was making the known fact impossible to forget. Three companion tests keep the tripwire honest rather than decorative: the firing branch is exercised against a simulated 2026-10-25 (**C-102** — a guard that has never run is unproven, and this one would otherwise first execute live in October, on the day it matters); the two datetimes and the 3h35m gap are pinned against this entry, so a typo fails rather than quietly moving the tripwire; and the lead window is asserted in both directions, because shrinking `LEAD_DAYS` is how a dated guard gets neutered without anyone deleting it. All three are mutation-proven. +**It is emphatically not the key-validity preflight this entry rejected.** No authenticated call, no credential, no network — a calendar and two declared datetimes. The rejection above stands and is unaffected: what was wrong was building a mechanism to *discover* a fact already known; what was missing was making the known fact impossible to forget. **The acknowledgement is the load-bearing part, and the first draft did not have it.** `/code-review high` found two design faults that would each have ended with the test deleted. (a) A literal pin on the two datetimes made the tripwire's own prescribed remediation — *rotate, then update `KEY_EXPIRY`* — fail a second test whose message said not to adjust the constant. A guard that refuses its own documented fix is worse than no guard, and it would have landed on the one person who could not route around it. The pin is gone. (b) From 2026-10-18 the gate would have been red for **every unrelated pull request**, clearable only by an operator console action the repository cannot perform — which is precisely what `pyproject.toml` 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*, which **cannot be set on or after the expiry** — so it postpones attention and can never replace it. + +Four companion tests keep it honest rather than decorative: the firing branch is exercised against a probe **derived from** `KEY_EXPIRY` (**C-102** — a guard that has never run is unproven; deriving it rather than hardcoding means the proof survives a rotation instead of quietly expiring with it); an acknowledgement past the expiry is refused; `LEAD_DAYS` is floored, because shaving a week off the warning neuters the guard while leaving it looking present; and the outage-day text is checked, since the first draft would have told an operator the keys expired *"in -3 days"* while the seam was down. All verified by mutation. Cross-refs: **C-81** (the same operator session's other half — branch protection and the CI token), **C-27** (no rotation mechanism for a secret value upstream), **C-57** (the pinned-registry detector, which is how this arrived here at all — it demanded the v1.4.4 bump and the bump is what surfaced the expiry), þing-02 A3(i), views-appwrite C-65 and C-66. diff --git a/tests/test_credential_expiry.py b/tests/test_credential_expiry.py index edc053c..36f9392 100644 --- a/tests/test_credential_expiry.py +++ b/tests/test_credential_expiry.py @@ -1,33 +1,36 @@ """A dated tripwire for the platform key expiry (register C-84, issue #224). -**What this is not.** C-84 considered a key-validity preflight and rejected it: *"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 stands, and this is not that. -There is no authenticated call here, no credential, and no network — only a calendar. +**What this is not.** C-84 considered a key-validity preflight and rejected it: *"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 stands. There is no authenticated call here, +no credential, no network — only a calendar. **What this is.** C-84's trigger reads *"act when the un_fao delivery is next scheduled -within a month of it, or when anyone plans a rotation, whichever is first."* That is a -trigger nobody can notice: it fires in someone's memory or not at all, and this -repository has written down more than once that a trigger nobody can notice is a wish -(ADR-014 §4; the third arm of C-94's trigger was withdrawn for exactly this). The date -is known 90 days in advance and the consequence is a total, foreseeable outage of every -identity on the seam. So the trigger is made to fire on its own. - -**Why the date is declared here rather than read from anywhere.** It cannot be derived: -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. The -values below come from an operator console read on 2026-08-05, recorded in views-appwrite -registry v1.4.4 and in C-84. - -**When this fails, there are exactly two honest responses**, and both are stated in the -failure: rotate the keys and update the dates below, or — if a key was replaced early — -update the dates below to the new expiry. Deleting the test is the third option and it -is the one that produces the outage. +within a month of it"*. That is a trigger nobody can notice: it fires in someone's memory +or not at all, which ADR-014 §4 forbids and which withdrew the third arm of C-94's +trigger. The date is known 90 days ahead and the consequence is a total outage of every +identity on the seam, so the trigger is made to fire on its own. + +**The acknowledgement is the load-bearing part, and the first draft did not have it.** +A gate that goes red on a date, cannot be cleared from inside the repository, and blocks +every unrelated pull request is a gate that gets deleted — `pyproject.toml` says exactly +that about ruff, citing ADR-014 §3. Rotation is an operator console action this repo +cannot perform, so without an in-repo escape the tripwire would hold the merge queue +hostage from 2026-10-18 until someone with console access acted. ``ACKNOWLEDGED_UNTIL`` +is that escape: a declared date that says *we have seen this and will act by then*. It is +a deliberate, reviewed, dated edit — and it **cannot be set past the expiry**, so it can +postpone attention but never replace it. + +**There is deliberately no test pinning these datetimes to literals.** The first draft had +one, and it made the remediation the tripwire itself prescribes — *rotate, then update +``KEY_EXPIRY``* — fail a second test whose message said not to adjust the constant. A +guard that refuses its own documented fix is worse than no guard, and it would have landed +on the one person who could not route around it. """ from __future__ import annotations -from datetime import date, datetime +from datetime import date, datetime, timedelta import pytest @@ -35,105 +38,145 @@ #: Read from the operator console 2026-08-05 (þing-02 A3(i)); recorded in views-appwrite #: coordinate registry v1.4.4 and in register C-84. Both of this repo's paths — the FAO #: delivery and the CRAF'd delivery — run under `VIEWS Pipeline Core`, the earlier one. -#: Naive datetimes, deliberately: the console reports local time and the window here is -#: 30 days, so an hour of timezone either way changes nothing. Do not add tzinfo to make -#: it look rigorous — it would imply a precision the source does not have. +#: +#: Naive datetimes, deliberately: the console reports local time and the window below is +#: 30 days, so an hour either way changes nothing. Adding tzinfo would imply a precision +#: the source does not have. KEY_EXPIRY: dict[str, datetime] = { "VIEWS Pipeline Core": datetime(2026, 11, 17, 12, 35), "UN FAO": datetime(2026, 11, 17, 16, 10), } -#: How long before the earliest expiry this starts failing. A month, because that is the -#: window C-84's own trigger names, and because rotation needs an overlap period that -#: has to be scheduled with an operator rather than squeezed in on the day. +#: How long before the earliest expiry this starts failing. C-84's own window, and the +#: time a rotation needs to be scheduled with an operator rather than squeezed in. LEAD_DAYS = 30 +#: Set to a date to silence the tripwire until then — *"seen, and being acted on"*. +#: Must be before the expiry (asserted below), so it postpones attention rather than +#: removing it. ``None`` means unacknowledged. +ACKNOWLEDGED_UNTIL: date | None = None + def days_until_earliest_expiry(today: date) -> tuple[str, int]: - """(which key dies first, how many days until it does) — a pure function of a date. + """(which key dies first, days until it does) — a pure function of a date. - Split out so the FAILING branch below can be exercised in the suite. A tripwire - whose firing path has never run is unproven however carefully it was written - (register C-102), and this one would otherwise first execute in October, live, on - the day it matters. + Split out so the FAILING branch can be exercised in the suite. A tripwire whose + firing path has never run is unproven however carefully it was written (C-102), and + this one would otherwise first execute live in October, on the day it matters. """ name = min(KEY_EXPIRY, key=lambda k: KEY_EXPIRY[k]) return name, (KEY_EXPIRY[name].date() - today).days def expiry_warning(name: str, days_left: int) -> str: - """What the tripwire says when it fires. Separate so it can be read without waiting.""" + """What the tripwire says when it fires.""" spread = max(KEY_EXPIRY.values()) - min(KEY_EXPIRY.values()) + hours, remainder = divmod(int(spread.total_seconds()), 3600) + gap = f"{hours}h{remainder // 60:02d}m" + when = ( + f"in {days_left} days" if days_left > 0 + else "TODAY" if days_left == 0 + else f"{-days_left} days ago — the seam is already dead" + ) listing = "\n".join( - f" {key:22} {when:%Y-%m-%d %H:%M}" - for key, when in sorted(KEY_EXPIRY.items(), key=lambda kv: kv[1]) + f" {key:22} {stamp:%Y-%m-%d %H:%M}" + for key, stamp in sorted(KEY_EXPIRY.items(), key=lambda kv: kv[1]) ) return ( - f"the platform Appwrite keys expire in {days_left} days:\n{listing}\n\n" - f"Both of this repository's delivery paths — un_fao and crafd — authenticate " - f"with {name!r}, the earlier one. The two keys are {spread} apart, which is not " - "a stagger: neither can carry traffic while the other is replaced, so a rotation " - "that assumes a window has none. After the later time every identity on the seam " - "is dead at once — model and ensemble writes, both partner deliveries, and FAO's " - "own read access.\n\n" + f"the platform Appwrite keys expire {when}:\n{listing}\n\n" + f"Both of this repository's delivery paths — un_fao and crafd — authenticate with " + f"{name!r}, the earlier one. The two keys are {gap} apart, which is not a stagger: " + "neither can carry traffic while the other is replaced, so a rotation that assumes " + "a window has none. After the later time every identity on the seam is dead at " + "once — model and ensemble writes, both partner deliveries, and FAO's own read " + "access.\n\n" "This repository cannot rotate anything and must not hold credentials " "(þing-01 D3). Issuing and installing keys is an operator console action " "(views-appwrite#12); the key split is views-faoapi#338.\n\n" - "Two honest ways to make this pass: rotate, then update KEY_EXPIRY; or, if a key " - "was replaced early, update KEY_EXPIRY to the new expiry. Deleting this test is " - "the third way and it is the one that produces the outage " + "THREE ways to make this pass, in order of preference:\n" + " 1. rotate the keys, then update KEY_EXPIRY to the new expiries;\n" + " 2. if a key was replaced early, update KEY_EXPIRY to match;\n" + " 3. set ACKNOWLEDGED_UNTIL to a date before the expiry — this says the rotation " + "is scheduled and stops the tripwire blocking unrelated work until then.\n" + "Deleting this test is the fourth way and it is the one that produces the outage " "(register C-84, issue #224)." ) def test_the_platform_keys_are_not_about_to_expire(): - """Fails 30 days out, so the date cannot pass unnoticed (C-84, #224).""" - name, days_left = days_until_earliest_expiry(date.today()) + """Fails inside the lead window unless the date is explicitly acknowledged.""" + today = date.today() + name, days_left = days_until_earliest_expiry(today) if days_left > LEAD_DAYS: return + if ACKNOWLEDGED_UNTIL is not None and today <= ACKNOWLEDGED_UNTIL: + return pytest.fail(expiry_warning(name, days_left)) +def test_an_acknowledgement_cannot_outlive_the_expiry(): + """The escape hatch postpones attention; it must not be able to remove it. + + An open-ended acknowledgement is just the deletion in finding 4's clothing, and it + would read as a live guard while being none. + """ + if ACKNOWLEDGED_UNTIL is None: + return + earliest = min(KEY_EXPIRY.values()).date() + assert ACKNOWLEDGED_UNTIL < earliest, ( + f"ACKNOWLEDGED_UNTIL is {ACKNOWLEDGED_UNTIL}, on or after the earliest expiry " + f"({earliest}). An acknowledgement that outlives the thing it acknowledges is a " + "silent deletion — the suite would stay green straight through the outage." + ) + + def test_the_tripwire_actually_fires_inside_the_lead_window(): - """The failing branch, exercised now rather than first executing in October. + """The failing branch, exercised now rather than first executing in October (C-102). - C-102: a guard that has never run is unproven, however carefully it was written. + The probe date is DERIVED from `KEY_EXPIRY`, so this keeps proving something after a + rotation. Hardcoding it — as the first draft did — meant the proof quietly expired + the moment the constant was legitimately updated. """ - name, days_left = days_until_earliest_expiry(date(2026, 10, 25)) + earliest = min(KEY_EXPIRY.values()).date() + probe = earliest - timedelta(days=LEAD_DAYS - 7) + name, days_left = days_until_earliest_expiry(probe) + assert name == "VIEWS Pipeline Core", "the earlier key is the one both paths use" - assert days_left == 23 - assert days_left <= LEAD_DAYS, ( - f"a date 23 days out must be inside the lead window, and LEAD_DAYS is " - f"{LEAD_DAYS}. Shrinking it is how this tripwire gets quietly neutered — " - "the window is C-84's own, and rotation needs an overlap period scheduled " - "with an operator rather than squeezed in on the day." - ) + assert 0 < days_left <= LEAD_DAYS, "the probe must sit inside the window it tests" + message = expiry_warning(name, days_left) - assert "expire in 23 days" in message - assert "3:35:00 apart" in message, "the gap is C-84's finding, not the dates" + assert f"expire in {days_left} days" in message + assert "3h35m apart" in message, "the gap is C-84's finding, not the dates" assert "not a stagger" in message assert "views-appwrite#12" in message, "the reader must be told who can act" + assert "ACKNOWLEDGED_UNTIL" in message, "and how to clear it without deleting it" assert "Deleting this test" in message, ( - "the third option must be named, or it is the one that gets taken quietly" + "the last option must be named, or it is the one that gets taken quietly" ) def test_the_tripwire_is_silent_outside_the_lead_window(): - _, days_left = days_until_earliest_expiry(date(2026, 1, 1)) - assert days_left > LEAD_DAYS, "a tripwire that is always red is a tripwire nobody reads" + earliest = min(KEY_EXPIRY.values()).date() + _, days_left = days_until_earliest_expiry(earliest - timedelta(days=LEAD_DAYS + 60)) + assert days_left > LEAD_DAYS, "a tripwire that is always red is one nobody reads" -def test_the_recorded_expiries_are_the_ones_C_84_names(): - """The dates are load-bearing, so a typo must fail here rather than silently. +def test_the_lead_window_is_not_quietly_shrunk(): + """Shaving days off `LEAD_DAYS` neuters this without deleting anything. - A tripwire keyed to a date nobody re-checked is worth very little; this pins the - two values against the entry that sourced them, so editing one without the other - is a failure rather than a drift. + A floor rather than an equality: widening the window is always safe, and pinning the + exact value would recreate the problem the removed literal-pin caused. """ - assert KEY_EXPIRY["VIEWS Pipeline Core"] == datetime(2026, 11, 17, 12, 35) - assert KEY_EXPIRY["UN FAO"] == datetime(2026, 11, 17, 16, 10) - gap = KEY_EXPIRY["UN FAO"] - KEY_EXPIRY["VIEWS Pipeline Core"] - assert gap.total_seconds() == 3 * 3600 + 35 * 60, ( - "C-84's finding is the 3h35m gap, not the dates themselves — if these move, " - "re-read the entry rather than adjusting the constant to match" + assert LEAD_DAYS >= 30, ( + f"LEAD_DAYS is {LEAD_DAYS}. C-84's window is a month, and rotation needs an " + "overlap period scheduled with an operator — shortening the warning is how a " + "dated guard gets neutered while still looking present." ) + + +def test_the_outage_day_message_still_reads(tmp_path): + """The text an operator reads while the seam is down must not say '-3 days'.""" + earliest = min(KEY_EXPIRY.values()).date() + for probe, expected in ((earliest, "TODAY"), (earliest + timedelta(days=3), "3 days ago")): + name, days_left = days_until_earliest_expiry(probe) + assert expected in expiry_warning(name, days_left)