From 8062a6693c554482f085c626979e3f02ddec0548 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Sun, 2 Aug 2026 12:02:14 +0200 Subject: [PATCH 1/2] docs(ops): record the operator's correction decisions; put the FAO half to them formally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simon Polichinel von der Maase is responsible for contacting the FAO when a delivery is found wrong — direct email, not an automated alert, as soon as the scope is established rather than after a correction is prepared. The intended treatment of a bad delivery is WITHDRAWAL. What is implemented is SUPERSESSION, and the procedure now states that gap in the section an operator reads at 22:00, because believing a bad delivery becomes unretrievable when it does not is worse than knowing it stays. Withdrawal has no wire mechanism: it needs an ADR-013 amendment and matching views-faoapi work, and whether that is worth building depends on FAO's answer about audit requirements. Both are put to FAO as Pre-Release Note 07 Topic B, with a table of the interim defaults marked as placeholders rather than policy — including the one that is the option we are NOT recommending. Recipients are deliberately not in this repository. It is public. Naming a responsible person on our side is one thing; publishing an external organisation's individual email addresses to a public repo as a side effect of documenting a runbook is another. The contacts live in the FAO-02 project materials. tests/test_doc_accuracy.py now refuses @fao.org anywhere in the repo, mutation-proven. The doc-accuracy guard fired on this change, which is what it was written for: it required that removing the "not decided" marker be accompanied by the decision. Its replacement guards the subtler failure — a half-answered question quietly becoming a whole answer, so the outstanding half stops being asked. Co-Authored-By: Claude Opus 5 (1M context) --- docs/operations/correction_procedure.md | 62 +++++++++++++-------- reports/technical_risk_register.md | 10 ++++ tests/test_doc_accuracy.py | 72 +++++++++++++++++++++---- 3 files changed, 113 insertions(+), 31 deletions(-) diff --git a/docs/operations/correction_procedure.md b/docs/operations/correction_procedure.md index 2161402..90fa179 100644 --- a/docs/operations/correction_procedure.md +++ b/docs/operations/correction_procedure.md @@ -80,9 +80,21 @@ defect; fix the producer and rebuild. ## 3. Correct on the wire -**The contract has no retraction primitive. It has supersession**, and that is -deliberate: the manifest-last commit ordering means the way to replace a run is to -publish a *new complete run*, not to mutate an old one. +**The intended policy is WITHDRAWAL. What is implemented is SUPERSESSION.** That gap is +deliberate, recorded, and you need to know about it before you act. + +- **Intended** (operator decision, 2026-08-02): a delivery found to be materially wrong + should be *removed or blocked* so it can no longer be served. Data known to be wrong + should not stay retrievable through the production interface. +- **In force**: supersession. The manifest-last commit ordering means a run is replaced + by publishing a *new complete run*; the old one stays in the bucket and remains + retrievable by a client that asks for it specifically. + +Supersession is in force **because it is what the wire does**, not because it was chosen. +Withdrawal has no mechanism today: it needs an ADR-013 amendment and matching work in +views-faoapi. Put to FAO as **Pre-Release Note 07, Decision Point B.2**, which also asks +whether they have an audit or reproducibility requirement that would argue *against* +withdrawal — that answer decides whether the amendment is worth building. 1. Fix the cause — the lookup, the producer, or the code — and land it. 2. Rebuild any affected artifact. For the lookup: @@ -112,31 +124,37 @@ because it is *correct*. Two consequences: --- -## 4. Telling the UN FAO — **not decided; the operator must answer this** +## 4. Telling the UN FAO -Everything above can be executed by whoever is on the keyboard. This cannot, and it is -the step that matters most to the partner. +**Simon Polichinel von der Maase is responsible for making contact.** Not an automated +alert — a correction needs judgement about scope and impact that an automated message +cannot carry. -**Two questions, in plain language:** +**Recipients and channel are recorded outside this repository.** This repo is **public**, +and publishing named FAO staff members' email addresses in it is not something to do by +default. The contacts are in the FAO-02 project materials +(`brain/2_projects/fao02/…/prerelease_notes/fao_02_pre_release_note_07/`, Decision +Point B.1) and in the operator's address book. -> **1. When a delivery is found to be wrong, who contacts the UN FAO, through what -> channel, and how quickly?** -> Right now nobody has said. There is no named person, no address, and no expectation -> about timing — so in practice the answer would be improvised by whoever noticed, -> under time pressure, which is the worst moment to invent a process. +**Send as soon as the scope of the error is established** — not after a correction has +been prepared. The partner's ability to act is time-sensitive and independent of our +remediation timeline. -> **2. Does the UN FAO expect us to *retract* the bad delivery, or to *supersede* it?** -> These need different behaviour. Supersession is what the contract does today: the old -> run stays in the bucket and a newer one wins. Retraction would mean removing or -> marking the old run so it cannot be served — which the wire has no mechanism for, and -> which would need an ADR-013 amendment and agreement from views-faoapi. -> **This is a question for them, not a decision for us.** +### Still awaiting FAO's answer -Per `CLAUDE.md`, anything touching an external party is the operator's call. Until both -are answered, treat this step as: **stop, and ask Simon.** Do not contact the partner -ad hoc; an inconsistent first message is harder to correct than a slow one. +Two things are proposed but not confirmed, and both are put to FAO in **Pre-Release +Note 07, Topic B**: ---- +1. **Are those the right recipients, and is there an expected notification period?** A + named individual on leave is a single point of failure in exactly the situation where + delay is costly. FAO may prefer a shared address or a rota. +2. **Does FAO expect a correction notice for *every* error, or only one material to + published outputs?** A wrong admin label on a handful of cells and a wrong forecast + across a region are different events; treating them alike either floods them with + immaterial notices or buries a serious one. + +Until FAO answers, follow what is written above. Do not improvise a different channel — +an inconsistent first message to a partner is harder to correct than a slow one. ## 5. Preserve the evidence diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index c11f869..c26eb3b 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -580,6 +580,16 @@ See also C-40 (the inheritance/representation coupling this migration unwinds), Pinned by three checks in `tests/test_doc_accuracy.py`: the document exists and names the identification fields; it describes ADR-013 mechanisms and none of the deleted ones; and it still flags its undecided step. +**⚠ UPDATE 2026-08-02, later the same day — the operator answered the PRIO half; the FAO half is now formally asked.** + +- **Who notifies:** **Simon Polichinel von der Maase**, by direct email, as soon as the scope of the error is established. Adopted. +- **Treatment of an affected delivery:** the intended policy is **withdrawal**. What is *implemented* is **supersession**, and the procedure now says so explicitly — supersession is in force because it is what the wire does, not because it was chosen. Withdrawal needs an ADR-013 amendment plus views-faoapi work, and whether that is worth building depends on FAO's answer about audit requirements. +- **Put to FAO** as **Pre-Release Note 07, Topic B** (Decision Points B.1 and B.2), which also records the interim defaults in force as placeholders rather than policy. + +Recipients are deliberately **not in this repository**, which is public; naming a responsible person on our side is one thing, publishing an external organisation's individual email addresses is another. `tests/test_doc_accuracy.py` now refuses `@fao.org` anywhere in the repo, mutation-proven. + +The original residual, kept for the record: + **⚠ RESIDUAL — one step is written but NOT decided, and it is the step that reaches the partner.** Two questions belong to the operator (`CLAUDE.md`: anything touching an external party): 1. **Who contacts the UN FAO when a delivery is found wrong, through what channel, and how fast?** No named person, no address, no timing expectation. In practice it would be improvised by whoever noticed, under time pressure. diff --git a/tests/test_doc_accuracy.py b/tests/test_doc_accuracy.py index 6a81cee..896ba0d 100644 --- a/tests/test_doc_accuracy.py +++ b/tests/test_doc_accuracy.py @@ -343,16 +343,70 @@ def test_the_correction_procedure_describes_the_delivery_that_exists(): ) -def test_the_procedure_states_the_questions_only_the_operator_can_answer(): - """The two external-party decisions must stay visible, not quietly become defaults. +def test_the_procedure_names_who_notifies_and_still_flags_what_fao_has_not_answered(): + """Half of §4 is decided; half is not, and both halves must stay visible. + + **Decided 2026-08-02 (operator):** Simon Polichinel von der Maase makes contact, and + the intended treatment of a bad delivery is *withdrawal*. **Not decided:** whether + FAO agrees the recipients and timing (B.1), and whether they have an audit + requirement arguing against withdrawal (B.2). Both are put to them in Pre-Release + Note 07, Topic B. + + An earlier version of this test asserted the document said "not decided", and its + docstring instructed whoever removed that marker to replace it with the decision. + That is what happened — the guard fired on the operator's answer, which is the + behaviour it was written for rather than a false positive. + + The failure mode it now guards is subtler: a half-answered question quietly becoming + a whole answer, so the outstanding half stops being asked. + """ + text = _CORRECTION.read_text() + assert "Simon Polichinel von der Maase is responsible" in text, ( + "the procedure no longer names who contacts the partner — an unowned step is " + "improvised by whoever notices, under time pressure" + ) + assert "Still awaiting FAO's answer" in text, ( + "the procedure no longer flags what FAO has not answered. If they have " + "answered, record the answer and the date — do not simply drop the question." + ) + assert "Pre-Release Note 07" in text, ( + "the procedure must cite where the outstanding questions were put, or they " + "become questions nobody remembers asking" + ) + + +def test_the_procedure_distinguishes_intended_policy_from_what_is_implemented(): + """The gap that would otherwise be discovered mid-incident. - Who contacts the UN FAO, and whether they expect retraction or supersession. Per - CLAUDE.md both are the operator's; the failure mode is that an undecided step gets - silently improvised the first time it is needed, under time pressure. + Withdrawal is the decision; supersession is what the wire actually does, and it is + in force only because nothing else exists. An operator reading this at 22:00 must + not believe a bad delivery becomes unretrievable when it does not. """ text = _CORRECTION.read_text() - assert "not decided" in text.lower(), ( - "the procedure no longer flags its undecided step — if it has been decided, " - "replace the marker with the decision and say who made it" + assert "intended policy is WITHDRAWAL" in text.replace("**", "") + assert "implemented is SUPERSESSION" in text.replace("**", "") + assert "ADR-013 amendment" in text, ( + "the procedure must say what withdrawal would COST — otherwise the gap reads " + "as an oversight rather than as unbuilt work with a known price" + ) + + +def test_no_partner_contact_details_are_published_in_this_repository(): + """This repo is public. FAO staff email addresses do not belong in it. + + The contacts live in the FAO-02 project materials and the operator's address book. + Naming a responsible person on our side is fine; publishing an external + organisation's individual addresses to a public repository is not something to do + as a side effect of documenting a runbook. + """ + offenders = [] + for doc in (*sorted(_REPO.rglob("*.md")), *sorted(_PKG.rglob("*.py"))): + if ".git" in doc.parts: + continue + for number, line in enumerate(doc.read_text(errors="ignore").splitlines(), 1): + if "@fao.org" in line.lower(): + offenders.append(f"{doc.relative_to(_REPO)}:{number}") + assert not offenders, ( + f"partner contact addresses appear in this public repository: {offenders}. " + "Keep them in the project materials; reference the decision, not the address." ) - assert "UN FAO" in text and "supersede" in text From a3e95325dcd47274eae61e703dfdeaa379dda58b Mon Sep 17 00:00:00 2001 From: Polichinl Date: Sun, 2 Aug 2026 12:03:17 +0200 Subject: [PATCH 2/2] fix: the address guard fired on the sentence describing the address guard Third instance in this epic of a guard catching the prose that explains it, after S3's retired contract name and S5's ledger-schema docstring. The fix is the same each time: name the thing without spelling it. Worth keeping the note rather than silently rewording, because the alternative each time was to weaken the guard, and three times would have made it worthless. Co-Authored-By: Claude Opus 5 (1M context) --- reports/technical_risk_register.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index c26eb3b..8c60d48 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -586,7 +586,7 @@ Pinned by three checks in `tests/test_doc_accuracy.py`: the document exists and - **Treatment of an affected delivery:** the intended policy is **withdrawal**. What is *implemented* is **supersession**, and the procedure now says so explicitly — supersession is in force because it is what the wire does, not because it was chosen. Withdrawal needs an ADR-013 amendment plus views-faoapi work, and whether that is worth building depends on FAO's answer about audit requirements. - **Put to FAO** as **Pre-Release Note 07, Topic B** (Decision Points B.1 and B.2), which also records the interim defaults in force as placeholders rather than policy. -Recipients are deliberately **not in this repository**, which is public; naming a responsible person on our side is one thing, publishing an external organisation's individual email addresses is another. `tests/test_doc_accuracy.py` now refuses `@fao.org` anywhere in the repo, mutation-proven. +Recipients are deliberately **not in this repository**, which is public; naming a responsible person on our side is one thing, publishing an external organisation's individual email addresses is another. `tests/test_doc_accuracy.py` now refuses partner address strings anywhere in the repo, mutation-proven — and fired on the first draft of this very sentence, which spelled the pattern out. Third time in this epic that a guard has caught the prose explaining it (after S3's retired contract name and S5's ledger-schema docstring); the fix is the same each time — name the thing without spelling it. The original residual, kept for the record: