Release Automation Is a Truth Problem, Not a Command Sequence
Jul 24, 2026
Automating the steps around a pull request looks like a sequence problem: prepare, open, verify. The real work is identity, external truth, evidence ordering, and authority. Get those wrong and you either block valid work forever or claim success you cannot defend.
The problem
The apparent task is to automate a release boundary: prepare a change, open a pull request, verify that the intended head is healthy, then stop for human acceptance before merge.
The actual problem is to build a trustworthy subsystem that can:
- act on real remote state, not a local wish
- create or reuse a pull request idempotently
- prove the pull request under verification is the intended one
- bind verification to the exact current head commit
- interpret several partially overlapping evidence surfaces from the host
- persist lifecycle truth without changing the code being verified
- advance to a human-acceptance pending state without inferring approval
- remain safe under retries, stale state, malformed data, concurrent writers, and external-service failures
A repository can already have lifecycle labels and transition ordering and still lack an executable controller path for those stages. Observational helpers are not the same as durable release authority.
What actually happened
The hardest part was not issuing the right commands. It was defining reliable external truth.
The implementation had to reconcile several kinds of truth at once: local branch state, remote branch state, persisted item metadata, pull-request identity and head commit, check runs, legacy commit statuses, deployment evidence, environment parity, and human-gate status.
Those surfaces do not always agree. In one live, checks-only topology:
- check runs were green
- legacy status contexts were empty
- combined status remained pending
- there was no deploy target
- there were zero current-head deployments
A naïve classifier could either block valid work forever or convert unknown evidence into success. The durable fix was an ordered, fail-closed evidence procedure in which a deliberately no-target, zero-deployment, non-failing case is an explicit vacuous pass — while failures, malformed targets, unavailable evidence, named targets without success, and non-empty deployment sets remain fail closed.
Several facts only showed up in live execution: CLI field contracts vary by version; checks may not retrigger after a correction; a check observer can return empty when a flow assumes checks exist; temporary environment proof can require elevated permissions; a deployment request can conflict while required checks are still running. Verification stopped at real permission boundaries rather than substituting mocks for missing proof.
The delivered result added executable stage dispatch, durable typed release metadata, idempotent preparation and opening, current-head verification, a single external-truth adapter, ordered fail-closed classification, environment and deployment evidence handling, serialised local durable writes, and a terminal human-acceptance pending stop — without automating acceptance, merge, or closeout.
One pattern mattered more than it first appeared. The verification record was intentionally local and transient. Committing it after verification would create a new head that had not itself been verified. The final record therefore identified the verified commit while explicitly prohibiting itself from being pushed to that head.
The lesson
Release automation is not a checklist of host commands. It is a truth-and-authority design.
Define the exact identity of the object being verified before you define success. Separate product-head truth from local governance records. Treat retries, stale state, ambiguous remote responses, and external unavailability as normal inputs — not exceptions. Test the repository’s real topology rather than assuming every evidence surface behaves alike. Plan live external proof early when correctness depends on API behaviour or permissions.
Most important: do not let the act of recording verification change the object you just verified. That pattern applies anywhere recording the result would alter the thing under proof.
The broader principle
Automated green evidence is not human acceptance. Verification can prove that a specific head satisfies the release checks you defined. Acceptance is a separate authority that decides whether that verified result should proceed. Merge and closeout are later actions still. Collapsing those stages into one “everything passed” claim is how false authority escalates.
External evidence must be evaluated in a deterministic order because several faults may be present at once. Named deploy targets, deliberately absent targets, and malformed targets are three different states. Absence is not inherently failure — but converting unavailability into success is.
Machine-readable durable fields must stay structurally separate from narrative annotations. Free-form commentary inside fields that should contain exact stage names or commit identities creates blockers that look like process failures and are really schema failures.
How to apply it
For similar release-boundary work:
- Start with a truth-source table covering local Git, remote Git, pull-request metadata, checks, deployments, durable state, and human gates.
- Define identity and idempotency first — pull-request identity, exact head commit, and retry rules — before implementation.
- Probe the real host topology early — checks-only versus legacy statuses, deployments present or absent, environments configured or not.
- Record minimum required permissions before live verification, not after the first permission failure.
- Model deploy targets in three states — supported name, deliberately none, corrupt or unsupported — with an explicit vacuous-pass rule only for the bounded none case.
- Keep verification output transient by design when committing it would change the verified head; include its path and hash in the acceptance evidence package instead.
- Preserve the separation between verify, human acceptance, merge, and closeout. Do not infer acceptance from green checks or a passing verification worker.
The residual maintenance risk is usually not the first implementation. It is a later change to one observer or classifier rule made without understanding its relationship to the others. Prefer reusable contracts for locking, exact identifiers, fail-closed adapters, retries, and current-head binding over re-specifying them in every item.