From e5bbd79ef70faeb258a8c13a4fc30c3d8bd1d588 Mon Sep 17 00:00:00 2001 From: Ingwannu Date: Fri, 4 Sep 2026 21:20:40 +0000 Subject: [PATCH] docs(devlog): make CI completion check fail closed --- .../090_outcome.md | 2 +- .../091_post_merge_audit.md | 31 ++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/devlog/_fin/260905_always_on_429_failover/090_outcome.md b/devlog/_fin/260905_always_on_429_failover/090_outcome.md index 3de38c8936..07e4399881 100644 --- a/devlog/_fin/260905_always_on_429_failover/090_outcome.md +++ b/devlog/_fin/260905_always_on_429_failover/090_outcome.md @@ -18,7 +18,7 @@ the tree rather than against the plan — the plan's own criteria were satisfied Two were defects the fix itself created (#3499, #3503), three were surfaces still describing the old contract (#3517, #3520, #3523), one closed the structural gap that let this unit ship two subset-rotator loops (#3512), and one cleaned up after a collision with concurrent maintainer -work (#3526). All are recorded in `091`. +work (#3526). The runtime post-merge findings and CI lessons are recorded in `091`. ## What changed diff --git a/devlog/_fin/260905_always_on_429_failover/091_post_merge_audit.md b/devlog/_fin/260905_always_on_429_failover/091_post_merge_audit.md index 490040325d..297213b2a3 100644 --- a/devlog/_fin/260905_always_on_429_failover/091_post_merge_audit.md +++ b/devlog/_fin/260905_always_on_429_failover/091_post_merge_audit.md @@ -67,14 +67,37 @@ The post-merge run on `dev` then showed `ci failure`, which was a genuinely alar out. It turned out to be cancellation by the maintainer's next merge two minutes later, not a real failure — every job read `cancelled`, not `failure`. -**Rule:** verify with the check-runs API and require zero `null` conclusions, not a pass count: +**Rule:** use the exact head SHA, require every expected aggregate or policy gate by name, and +also require zero non-terminal check runs. A missing check is not success. Paginate before treating +the returned set as complete: ```bash -gh api repos///commits//check-runs \ - --jq '[.check_runs[] | .conclusion] | group_by(.) | map({(.[0]//"null"): length}) | add' +set -o pipefail +gh api --paginate repos///commits//check-runs \ + | jq -se ' + [.[].check_runs[]] as $runs + | ["ci", "enforce-target", "hygiene", "react-doctor"] as $expected + | ($expected - [ + $runs[] + | select(.status == "completed" and .conclusion == "success") + | .name + ]) as $missing + | [ + $runs[] + | select(.status != "completed" or .conclusion == null) + | .name + ] as $pending + | if ($missing | length) == 0 and ($pending | length) == 0 + then {ready: true, expected: $expected} + else error("missing=\($missing) pending=\($pending)") + end' ``` -A clean result looks like `{"skipped":3,"success":24}` — no `null` key at all. +A clean result is `{"ready":true,...}` with exit status 0. This does not replace review-policy +checks such as confirming the approval belongs to the same head. Every `$expected` value is an +exact Checks API `.check_runs[].name`, not a workflow title or workflow-run name. If those required +check-run names change, update this list with the policy; silently accepting an absent name +recreates the original bug. The near-miss paid for itself: sweeping `dev` afterwards found a real defect. #3511 and #3513 landed concurrently, one moving `anthropic-quorum-cache.test.ts` into `tests/routing/` and the