fix(retire): the hello-lib tombstone must report what it read #598

Merged
binjovi-bot merged 1 commit from fix/hello-lib-tombstone-diagnostics into trunk 2026-09-10 20:19:41 +00:00
Owner

The tombstone from #595 ran. It released the push mirror, proved sean/hello-lib empty at id 392, and proved the CR owns it — the identity fence worked. Then it refused four times:

ForgejoPushMirror/hello-lib delete 200
sean/hello-lib proven empty at id 392, owned by ForgejoRepository/hello-lib
ForgejoRepository/hello-lib policy did not take; refusing

It was wrong. The flip had worked. managedFields records manager curl setting deletionPolicy at 19:28:06, which is the first attempt's own finish time, and attempts 2, 3 and 4 re-read an object that already said Delete and refused anyway.

The defect is that the check could not say why

It threw the PATCH response away with -o /dev/null, so a rejected PATCH and an accepted one looked the same. It then tested the GET body with grep -q, which answers only yes or no and discards the evidence. An absent substring carries no information. These are one answer:

  • the field says Retain
  • this is an error Status object
  • the read failed

Replaying the exact line against the live object shows it does match, so the bytes the Job received at that step were not the object — and the Job had no way to report that.

Both policy checks now capture the HTTP status, read .spec.deletionPolicy as a field with jq, and print the body when they refuse.

The mirror check pointed the dangerous way

It asked whether the body contained Delete and refused if it did. For the mirror the two readings point opposite ways: an error body contains no Delete, so a failed read looked like consent and the mirror would be deleted with its guard never running. It now requires deletionPolicy to equal Retain.

The repository CR is also read once instead of twice — the old code called status_of for the status, then call GET again for the body.

State right now

ForgejoPushMirror/hello-lib gone
ForgejoRepository/hello-lib present, deletionPolicy: Delete
sean/hello-lib present, id 392, empty: true, size 0

One act is left — delete the CR. If the next run still refuses, it will say what it saw.

Guard

Four new properties pinned, plus a refusal of any policy decision made by substring on an executable line (comments keep the history). Five mutations confirmed red: the field read removed, the PATCH status discarded, the read status discarded, the mirror check no longer requiring Retain, and a substring policy decision added back.

https://claude.ai/code/session_01TdW5FSvRJW8CtGnpd29Xp8

The tombstone from #595 ran. It released the push mirror, proved `sean/hello-lib` empty at id 392, and proved the CR owns it — the identity fence worked. Then it refused four times: ``` ForgejoPushMirror/hello-lib delete 200 sean/hello-lib proven empty at id 392, owned by ForgejoRepository/hello-lib ForgejoRepository/hello-lib policy did not take; refusing ``` **It was wrong.** The flip had worked. `managedFields` records manager `curl` setting `deletionPolicy` at `19:28:06`, which is the first attempt's own finish time, and attempts 2, 3 and 4 re-read an object that already said `Delete` and refused anyway. ## The defect is that the check could not say why It threw the PATCH response away with `-o /dev/null`, so a rejected PATCH and an accepted one looked the same. It then tested the GET body with `grep -q`, which answers only yes or no and discards the evidence. **An absent substring carries no information.** These are one answer: - the field says `Retain` - this is an error `Status` object - the read failed Replaying the exact line against the live object shows it *does* match, so the bytes the Job received at that step were not the object — and the Job had no way to report that. Both policy checks now capture the HTTP status, read `.spec.deletionPolicy` as a **field** with `jq`, and print the body when they refuse. ## The mirror check pointed the dangerous way It asked whether the body contained `Delete` and refused if it did. For the mirror the two readings point **opposite** ways: an error body contains no `Delete`, so a failed read looked like consent and the mirror would be deleted with its guard never running. It now **requires** `deletionPolicy` to equal `Retain`. The repository CR is also read once instead of twice — the old code called `status_of` for the status, then `call GET` again for the body. ## State right now | | | |---|---| | `ForgejoPushMirror/hello-lib` | gone | | `ForgejoRepository/hello-lib` | present, `deletionPolicy: Delete` | | `sean/hello-lib` | present, id 392, `empty: true`, size 0 | One act is left — delete the CR. If the next run still refuses, it will say what it saw. ## Guard Four new properties pinned, plus a refusal of any policy decision made by substring on an executable line (comments keep the history). Five mutations confirmed red: the field read removed, the PATCH status discarded, the read status discarded, the mirror check no longer requiring `Retain`, and a substring policy decision added back. https://claude.ai/code/session_01TdW5FSvRJW8CtGnpd29Xp8
fix(retire): the hello-lib tombstone must report what it read
All checks were successful
binjovi/ci Binjovi completed the frozen plan
7578548f74
The Job ran and refused four times: "ForgejoRepository/hello-lib policy did not
take". The flip had in fact worked. `managedFields` records manager `curl`
setting `deletionPolicy` at 19:28:06, which is the first attempt, and the three
later attempts re-read an object that already said `Delete` and refused anyway.

The check could not report any of that, and that is the defect. It threw the
PATCH response away with `-o /dev/null`, so a rejected PATCH and an accepted one
looked the same. It then tested the GET body with `grep -q`, which answers only
yes or no and discards the evidence. An absent substring carries no information:
"the field says Retain", "this is an error Status object" and "the read failed"
are one answer. Replaying the line against the live object shows it matches, so
the bytes the Job received at that step were not the object -- and the Job had no
way to say so.

Both policy checks now capture the HTTP status, read `.spec.deletionPolicy` as a
field with jq, and print the body when they refuse.

The mirror check changes direction as well. It asked whether the body contained
"Delete" and refused if it did. For the mirror the two readings point OPPOSITE
ways: an error body contains no "Delete", so a failed read looked like consent
and the mirror would be deleted with its guard never running. It now REQUIRES
`deletionPolicy` to equal `Retain`.

The repository CR is also read once instead of twice. The old code called
`status_of` for the status and `call GET` again for the body.

State when this was written: the mirror CR is gone, the repository CR remains
with `deletionPolicy: Delete`, and `sean/hello-lib` still exists at id 392. The
next run has one act left -- delete the CR -- and if it still refuses it will now
say what it saw.

The guard pins the four new properties and refuses any policy decision made by
substring on an executable line. Five mutations confirmed red: the field read
removed, the PATCH status discarded, the read status discarded, the mirror check
no longer requiring Retain, and a substring policy decision added back.

Claude-Session: https://claude.ai/code/session_01TdW5FSvRJW8CtGnpd29Xp8
binjovi-bot deleted branch fix/hello-lib-tombstone-diagnostics 2026-09-10 20:19:42 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sean/seanfarm!598
No description provided.