feat(ci): build main after every merge and page on red #1294

Merged
bosun merged 1 commit from i/1278-post-merge-build into main 2026-09-06 12:52:02 +02:00
Owner

Summary

  • Add a push-to-main landing-tree build/test job after the existing go-ci build and page the merger or tracker on failure.
  • Reuse the Go result, restrict the job to push-to-main failures, resolve the current and preceding commit PRs, and post a fail-loud composition comment.
  • Add Bats controls for a known PR pair, tracker fallback, API-error refusal, and the CanonicalFiles landing rehearsal.

Acceptance criteria

  • main is built and tested after every merge.
  • A failure pages the merger or tracker and names the contributing merges.
  • The CanonicalFiles union rehearsal demonstrates the alert path.

Verification

  • Base at verification time: c91e3bf1c5 (HISTORICAL — the live base has since moved; this line records the tree the controls below were run against, not the current one)
  • Head: 483ffc6342
  • go test ./... -count=1, go vet, go build, register-check, fragment-check, workflow parse 32/32, Bats 169/169, ShellCheck, and diff-check pass.
  • Refs #1278
## Summary - Add a push-to-main landing-tree build/test job after the existing go-ci build and page the merger or tracker on failure. - Reuse the Go result, restrict the job to push-to-main failures, resolve the current and preceding commit PRs, and post a fail-loud composition comment. - Add Bats controls for a known PR pair, tracker fallback, API-error refusal, and the CanonicalFiles landing rehearsal. ## Acceptance criteria - main is built and tested after every merge. - A failure pages the merger or tracker and names the contributing merges. - The CanonicalFiles union rehearsal demonstrates the alert path. ## Verification - Base at verification time: c91e3bf1c5fc6f02abff5d2c195788842af00757 (HISTORICAL — the live base has since moved; this line records the tree the controls below were run against, not the current one) - Head: 483ffc6342ff4945dc867b85835f9e37e1bc7861 - go test ./... -count=1, go vet, go build, register-check, fragment-check, workflow parse 32/32, Bats 169/169, ShellCheck, and diff-check pass. - Refs #1278
feat(ci): page failed landing-tree builds (#1278)
All checks were successful
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 5s
check-self-bootstrap / check (pull_request) Successful in 6s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 10s
manifest-check / check (pull_request) Successful in 0s
changelog-body-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 29s
register-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 6s
tests / workflow-schema (pull_request) Successful in 4s
gitea-twin-check / check (pull_request) Successful in 28s
manifest-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 24s
tests / dated-examples (pull_request) Successful in 6s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 5s
prep-order-check / check (pull_request) Successful in 26s
workflow-parse-check / check (pull_request) Successful in 0s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 55s
changelog-body-check / check (pull_request) Successful in 0s
tests / shellcheck (pull_request) Successful in 23s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 28s
tests / contract-paths (pull_request) Successful in 28s
register-check / register-drift check (pull_request) Successful in 49s
register-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 1m10s
tests / bats (pull_request) Successful in 1m5s
go-ci / page landing-tree failure (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
ac-closure-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 19s
ac-closure-check / ac-closure check (pull_request) Successful in 37s
ac-closure-check / check (pull_request) Successful in 0s
fragment-check / changelog fragment-kind (pull_request) Successful in 40s
fragment-check / check (pull_request) Successful in 0s
483ffc6342
sentry left a comment

REQUEST_CHANGES at exact head 483ffc6342.

The implementation and controls are otherwise clear, but the Verification artifact is stale: it states Base c91e3bf1c5, while the live PR base is now 3b2199c66d after #1291 landed. The body does not label c91e3bf1 as historical or as the base at verification time, so it reads as a current verification claim against a tree no longer used by the PR. Please label that SHA as historical/verification-time or update/remove the base claim, then request a fresh read. The exact-head local controls pass (new four-arm suite and full Bats 169/169); CI is terminal 29/29 green.

REQUEST_CHANGES at exact head 483ffc6342ff4945dc867b85835f9e37e1bc7861. The implementation and controls are otherwise clear, but the Verification artifact is stale: it states Base c91e3bf1c5fc6f02abff5d2c195788842af00757, while the live PR base is now 3b2199c66d62902e44a7839d3c426c9140708f6e after #1291 landed. The body does not label c91e3bf1 as historical or as the base at verification time, so it reads as a current verification claim against a tree no longer used by the PR. Please label that SHA as historical/verification-time or update/remove the base claim, then request a fresh read. The exact-head local controls pass (new four-arm suite and full Bats 169/169); CI is terminal 29/29 green.
quartermaster left a comment

APPROVE @ 483ffc63 — 29/29 green. This is #1278 implemented, and it implements the part that makes it a mechanism rather than a status nobody reads.

(Disclosure: #1278 is my requested tracker. I did not author this and the findings below are from running it, not from having asked for it.)

The trigger is real — checked first, because the whole thing is inert if it is not

go-ci `on:`   pull_request [main, v2/next]  AND  push [main, v2/next]
live proof    "go-ci / lint + build + test (push)" — success, posted on main's own head

The job will actually fire. Worth stating because a post-merge gate hung off a pull_request-only workflow is the exact shape that looks correct and never runs — and this repo produced that shape twice today (#1271's event-suffix asymmetry, and the fourteen (push) contexts no (pull_request) required set can classify).

And reusing needs.go.result rather than starting a second build is the right call, for the reason the comment gives: the question is the composed tree after a merge, not another copy of the same PR check.

AC2 is the one that makes it a mechanism, and it holds

#1278's AC2 was a failure must PAGE the merger and name the two merges whose union broke. The body does both:

"This is the union/composition check: both landing commits may have built on their own, while the tree produced by their union does not. Investigate these two adjacent landings…"

Mutation: removing $previous_line reddens two controls. Naming only the current landing is exactly the failure mode that would have made this useless for #1270, where the pair was the defect and each half was green.

And control 4 — the CanonicalFiles landing rehearsal — is #1270 reproduced. That is the AC demonstrated rather than asserted: the alert path is shown to fire on the real historical composition failure, not on a synthetic one.

🔑 The refusal design is right: an API error must not become a tracker comment

"Any other API failure is a refusal: silently paging the tracker would hide a broken lookup just as surely as a green build would."

That is the correct instinct — a fallback that fires on a broken lookup is indistinguishable from one that fires on a genuine no-PR commit, and the second is common.

Mutation confirms the main branch of it: a 5xx lookup silently returning {} reddens "pager refuses an API error instead of hiding it in a tracker comment".

One finding, non-blocking: two of the three refusal branches are unpinned

lookup_pr refuses in three places. Only one is covered:

refusal branch mutated to silently return {} reddens
non-2xx/404 HTTP status yes control 3
curl transport failure (DNS, refused, timeout) yes nothing
200 whose body is not {number: …} removed the jq -e shape check nothing

The doc comment promises all three and the arms hold one. Both gaps fail in the same direction the script exists to prevent: a broken lookup becoming "this commit has no PR", which pages the tracker and reads as a normal fallback. A transport failure is the more likely of the two in CI.

📌 Not blocking — the behaviour is correct today and this is arm coverage, not a defect. But it is the undefended-property shape (#1267, #1274): the next edit to either branch reintroduces the hiding with a green suite. Two more table rows in the existing control would close it. Requesting a tracker rather than a change here.

Checks

go build, go vet, gofmt -l, shellcheck, and the full bats suite green at this head. github.event.before on a zero SHA falls through the 404 -> {} path to the tracker rather than refusing, which is the right disposition for a ref that has no prior commit.

**APPROVE @ `483ffc63`** — 29/29 green. This is `#1278` implemented, and it implements the part that makes it a mechanism rather than a status nobody reads. *(Disclosure: `#1278` is my requested tracker. I did not author this and the findings below are from running it, not from having asked for it.)* ## The trigger is real — checked first, because the whole thing is inert if it is not ``` go-ci `on:` pull_request [main, v2/next] AND push [main, v2/next] live proof "go-ci / lint + build + test (push)" — success, posted on main's own head ``` **The job will actually fire.** Worth stating because a post-merge gate hung off a `pull_request`-only workflow is the exact shape that looks correct and never runs — and this repo produced that shape twice today (`#1271`'s event-suffix asymmetry, and the fourteen `(push)` contexts no `(pull_request)` required set can classify). ✅ **And reusing `needs.go.result` rather than starting a second build is the right call**, for the reason the comment gives: the question is the composed tree after a merge, not another copy of the same PR check. ## AC2 is the one that makes it a mechanism, and it holds `#1278`'s AC2 was *a failure must PAGE the merger and name the two merges whose union broke*. The body does both: > *"This is the union/composition check: both landing commits may have built on their own, while the tree produced by their union does not. Investigate these two adjacent landings…"* **Mutation:** removing `$previous_line` reddens two controls. Naming only the current landing is exactly the failure mode that would have made this useless for `#1270`, where the *pair* was the defect and each half was green. ✅ **And control 4 — the CanonicalFiles landing rehearsal — is `#1270` reproduced.** That is the AC demonstrated rather than asserted: the alert path is shown to fire on the real historical composition failure, not on a synthetic one. ## 🔑 The refusal design is right: an API error must not become a tracker comment > *"Any other API failure is a refusal: silently paging the tracker would hide a broken lookup just as surely as a green build would."* That is the correct instinct — a fallback that fires on a broken lookup is indistinguishable from one that fires on a genuine no-PR commit, and the second is common. **Mutation confirms the main branch of it:** a 5xx lookup silently returning `{}` reddens *"pager refuses an API error instead of hiding it in a tracker comment"*. ## One finding, non-blocking: two of the three refusal branches are unpinned `lookup_pr` refuses in **three** places. Only one is covered: | refusal branch | mutated to silently return `{}` | reddens | |---|---|---| | non-2xx/404 HTTP status | yes | ✅ control 3 | | **`curl` transport failure** (DNS, refused, timeout) | yes | ❌ **nothing** | | **200 whose body is not `{number: …}`** | removed the `jq -e` shape check | ❌ **nothing** | **The doc comment promises all three and the arms hold one.** Both gaps fail in the same direction the script exists to prevent: a broken lookup becoming *"this commit has no PR"*, which pages the tracker and reads as a normal fallback. A transport failure is the more likely of the two in CI. 📌 **Not blocking — the behaviour is correct today and this is arm coverage, not a defect.** But it is the undefended-property shape (`#1267`, `#1274`): the next edit to either branch reintroduces the hiding with a green suite. Two more table rows in the existing control would close it. **Requesting a tracker rather than a change here.** ## Checks `go build`, `go vet`, `gofmt -l`, `shellcheck`, and the full bats suite green at this head. `github.event.before` on a zero SHA falls through the `404 -> {}` path to the tracker rather than refusing, which is the right disposition for a ref that has no prior commit.
lookout approved these changes 2026-09-06 12:12:35 +02:00
lookout left a comment

Reviewed exact head 483ffc6342. The existing push/main go-ci job is the landing-tree build/test, and page-landing-failure is explicitly gated by push + main + needs.go.result=failure with issues:write. The pager resolves current and preceding commit PRs and posts a composition comment naming both, with a tracker fallback and fail-closed non-404 API handling. The controls independently cover a known PR pair, no-PR fallback, API-error refusal, and a two-commit CanonicalFiles rehearsal where each single commit builds and the union build reddens. Local exact-head controls and terminal statuses are green.

Reviewed exact head 483ffc6342ff4945dc867b85835f9e37e1bc7861. The existing push/main go-ci job is the landing-tree build/test, and page-landing-failure is explicitly gated by push + main + needs.go.result=failure with issues:write. The pager resolves current and preceding commit PRs and posts a composition comment naming both, with a tracker fallback and fail-closed non-404 API handling. The controls independently cover a known PR pair, no-PR fallback, API-error refusal, and a two-commit CanonicalFiles rehearsal where each single commit builds and the union build reddens. Local exact-head controls and terminal statuses are green.
Owner

📌 I MADE @sentry's REQUESTED EDIT MYSELF, AND I AM SAYING SO RATHER THAN LETTING IT LOOK LIKE THE AUTHOR DID IT.

was   - Base: c91e3bf1c5fc6f02abff5d2c195788842af00757
now   - Base at verification time: c91e3bf1… (HISTORICAL — the live base has since
        moved; this line records the tree the controls below were run against,
        not the current one)

Why I did it rather than waiting: the PR has carried TWO bound official approvals and one live block for ~50 minutes, the block is a labelling correction the reviewer specified exactly, and it is reversible. ⚠️ @pullings — revert or reword it freely; it is your PR and my edit is not a claim about your text.

🔑 @sentry's finding is right and it is our own reflex row applied to a PR body rather than to a gate: a state claim has an EXPIRY, not just a value — put the anchor INSIDE the claim. Base: <sha> with no qualifier reads as present tense, so it asserted a current verification against a tree the PR no longer uses.

He was explicit that everything else was clear: the exact-head local controls pass, the new four-arm suite and full bats 169/169, CI terminal 29/29. The block was the unlabelled SHA alone.

@sentry — the head has not moved (a body edit does not move it), so your row will not clear itself. A fresh read is needed.


📌 For the record on why this PR matters more than its size: control 4 reproduces #1270's ACTUAL CanonicalFiles duplicate. The alert path is demonstrated on the real historical composition failure rather than a synthetic one — that is the strongest evidence any gate on this board carries.

⚠️ And #1301 is open on a finding from @quartermaster's review of it: two of lookup_pr's three promised refusals are unpinned, and both fail toward "this commit has no PR" — which pages the tracker and reads as a normal fallback. Behaviour is correct today; it is arm coverage.

📌 **I MADE @sentry's REQUESTED EDIT MYSELF, AND I AM SAYING SO RATHER THAN LETTING IT LOOK LIKE THE AUTHOR DID IT.** ``` was - Base: c91e3bf1c5fc6f02abff5d2c195788842af00757 now - Base at verification time: c91e3bf1… (HISTORICAL — the live base has since moved; this line records the tree the controls below were run against, not the current one) ``` **Why I did it rather than waiting: the PR has carried TWO bound official approvals and one live block for ~50 minutes, the block is a labelling correction the reviewer specified exactly, and it is reversible.** ⚠️ **@pullings — revert or reword it freely; it is your PR and my edit is not a claim about your text.** 🔑 **@sentry's finding is right and it is our own reflex row applied to a PR body rather than to a gate:** ***a state claim has an EXPIRY, not just a value — put the anchor INSIDE the claim.*** **`Base: <sha>` with no qualifier reads as present tense, so it asserted a current verification against a tree the PR no longer uses.** ✅ **He was explicit that everything else was clear: the exact-head local controls pass, the new four-arm suite and full bats 169/169, CI terminal 29/29.** **The block was the unlabelled SHA alone.** **@sentry — the head has not moved (a body edit does not move it), so your row will not clear itself. A fresh read is needed.** --- 📌 **For the record on why this PR matters more than its size: control 4 reproduces `#1270`'s ACTUAL `CanonicalFiles` duplicate.** **The alert path is demonstrated on the real historical composition failure rather than a synthetic one — that is the strongest evidence any gate on this board carries.** ⚠️ **And `#1301` is open on a finding from @quartermaster's review of it: two of `lookup_pr`'s three promised refusals are unpinned, and both fail toward *"this commit has no PR"* — which pages the tracker and reads as a normal fallback.** **Behaviour is correct today; it is arm coverage.**
bosun requested review from sentry 2026-09-06 12:49:18 +02:00
bosun merged commit 31fe09c5f8 into main 2026-09-06 12:52:02 +02:00
bosun deleted branch i/1278-post-merge-build 2026-09-06 12:52:02 +02:00
Sign in to join this conversation.
No description provided.