diff --git a/00-META/how-we-build.md b/00-META/how-we-build.md index ddeed6b..4f1c98f 100644 --- a/00-META/how-we-build.md +++ b/00-META/how-we-build.md @@ -41,7 +41,7 @@ incident behind it is not written down, and the fix is to write it down, not to | **Never bypass the pipeline** | No manual database edit, no manual restart as a workaround. Fix the cause and deploy. A workaround that works is a workaround that is never removed, and the next person cannot tell the node from its declaration. | | **Never create a symlink** | A hand-made link caused production data loss through container volume resolution, and the judgement needed to make a safe exception is exactly the judgement unavailable at the moment it matters. **The mesh creates none at all** ([ADR 0018](../02-DECISIONS/0018-the-mesh-creates-no-symlinks.md), which supersedes [ADR 0011](../02-DECISIONS/0011-the-installer-owns-linking.md)). The links the installer still reconciles are a migration, not a permission. | | **Never push directly to the main branch** | Branch, push, review, merge. Every merge is a human checkpoint, without exception — **including in this repository**. A documentation repository is not a lower tier of care; a decision record lands the same way a service does. | -| **One change per pull request, and never merge your own** | Unrelated improvements bundled together cannot be reviewed or reverted separately. Self-merging removes the checkpoint that is the entire point. | +| **One change per pull request, and never merge unapproved work** | Unrelated improvements bundled together cannot be reviewed or reverted separately. And the checkpoint is **a person deciding, not a person clicking** — work may be merged by whoever wrote it once a human has explicitly approved *that merge*, and never on a standing permission, an instruction to do the work, silence, or the author's own judgement that it is ready. [ADR 0042](../02-DECISIONS/0042-approval-is-the-checkpoint.md) | | **Never open a pull request unprompted** | A permissions list saying it is allowed is not a request. | | **A failed step fails the job** | A sequence that continues past a failure does the next thing in the wrong place. Gate each step on the last. [ADR 0008](../02-DECISIONS/0008-a-failed-step-fails-the-job.md), and §5. | diff --git a/02-DECISIONS/0042-approval-is-the-checkpoint.md b/02-DECISIONS/0042-approval-is-the-checkpoint.md new file mode 100644 index 0000000..17628f0 --- /dev/null +++ b/02-DECISIONS/0042-approval-is-the-checkpoint.md @@ -0,0 +1,78 @@ +--- +status: accepted +date: 2026-08-26 +deciders: jochen +reconstructed: false +--- + +# 42. The approval is the checkpoint, not the second pair of hands + +## Context + +[§2](../00-META/how-we-build.md) reads *"one change per pull request, and never merge your +own"*, and gives the reason: *"self-merging removes the checkpoint that is the entire point."* + +The rule was written for people. Applied literally to an agent it produces a contradiction +that surfaced immediately: an agent asked to merge cannot merge, because it authored what it +is being asked to merge. Every merge this repository has seen was therefore either performed +by the thing that wrote it, or not performed at all. + +Stated by the operator: *"you are allowed to self-merge, after I've given you explicit +approval."* + +## Considered options + +1. **Read it literally.** An agent never merges; the operator merges by hand every time. + Rejected: it makes the operator a mechanical step rather than a reviewer, and a step + performed dozens of times without reading is not a checkpoint — it is the *shape* of one, + which is worse, because the record then claims a review that did not happen. +2. **Drop the rule for agents.** Rejected: the rule is right, and the failure it prevents — + work merged with nobody having looked — is not one an agent is less prone to. +3. **Name what the checkpoint actually is.** Chosen. + +## Decision + +**The checkpoint is a person deciding, not a person clicking.** + +An agent may merge its own work **when a human has explicitly approved that merge**. The +approval is the review; the merge is bookkeeping that follows it. + +Without an explicit approval, nothing changes: the agent does not merge, and +[§2](../00-META/how-we-build.md)'s *never open a pull request unprompted* continues to mean +that a permissions list is not a request. + +**What "explicit" excludes**, because this is the half that can rot: + +- A standing permission granted once and cited forever. +- An instruction to do the work, read as approval to merge it. +- Silence. +- The agent's own judgement that the work is ready. + +## Consequences + +- **The record must show which it was.** A merge performed on approval and a merge performed + unilaterally look identical afterwards, and the difference is the whole rule. Until the forge + can record an approval, the merge commit says so — which is weaker than a recorded review and + is stated here rather than left to be discovered. +- **Branch protection would make this structural instead of textual.** Required approvals turn + the rule into something the forge enforces. It cannot be set through the API on the forge + version in use, so this remains a rule held by intention — the condition + [§5](../00-META/how-we-build.md) exists to name. +- **The one-change-per-pull-request half is untouched**, and was broken twice on 2026-08-26. + Both breaches are recorded in the pull requests themselves rather than in a conversation. +- **This narrows a rule rather than relaxing one.** The obligation moves from *who performs the + merge* to *whether a person decided*, which is a higher bar in the case the original wording + actually permits: a reviewer who merges someone else's work without reading it satisfies the + old rule and fails this one. + +## Sync + +Run and verified 2026-08-26. §2 in the enforced page now reads *never merge unapproved work*, +confirmed by reading the rule back out of the live page rather than by trusting the publish — +the previous sync reported success and changed nothing. + +## References + +- [`how-we-build.md`](../00-META/how-we-build.md) §2 — the rule, now carrying this. +- [ADR 0008](0008-a-failed-step-fails-the-job.md) — the standard a checkpoint is held to: a + step that reports success without doing anything is the fault, not the shortcut.