Repository navigation
The CD ledger closes itself, and the parser-delta gate reaches the merge queue - #3549
Merged
Merged
Conversation
Two CI signals that could not mean what they claimed. Both are placement, not logic. #3176 — the heal comment ended "Close this issue if nothing else is outstanding" and closed nothing, while `gate` and `alert-on-failure` both append to whichever `ci-failure` issue is OPEN. The first one ever filed therefore absorbed every attempt and every heal: 324 comments over four days and a dozen unrelated causes, under a title that stopped being true on day one. That destroys the only two things an alert says — its EXISTENCE ("delivery is broken right now", which an always-open issue cannot mean) and its AGE. `verify-images` now closes it; the next failure files a fresh issue, so a title names its own cause and an age is its own outage's. The comment stays fatal, the close is a warning: a lost comment hides the delivery record, a failed close hides nothing (the next heal closes it), and a fatal close would red a delivery leg — an alert about the alerting, on a run that shipped. The step gained `id: heal` and takes every value through `env:`, so `test-cd-steps.py` extracts and EXECUTES it against a stubbed `gh`. Its cases assert on the recorded `gh` invocations, never stdout: a step that prints "closing" and calls no `gh issue close` is the defect, and the two are identical in a log. The harness refuses to run at all if the step stops calling it (negative control: removing the call fails the harness, exit 1). #3508 — a gate added to `main` reaches NEITHER build of a pull request already green. Its own `pull_request` run executes the workflow file as it stood when the branch was cut, and its `merge_group` run skipped the whole `cross-repo-pair` job, whose `if:` is pull_request only. Measured on the queue run for #3546 (34093021863): `Public surface (binary compatibility)` = success, `Cross-repo pair (public surface)` = skipped. Exit (1) is structural. Exit (2) is placement, so the parser-delta control moves into `record-signatures`, which already runs on `pull_request` AND `merge_group` and carries no fork/dependabot clause — closing those two exits as well. The strict/declaring split is written on the EVENT, the only exemption AGENTS.md sanctions and the same shape the two gates beside it use for `--pr`: on `pull_request` the body is supplied so a declared decrease is honoured; on `merge_group` it is not, and any decrease is refused. Every build gets exactly one of the two; there is no path on which neither runs. Falsified by executing the step's own `run:` block, extracted from the workflow: clean tree/merge_group = exit 0 on the byte-identical proof; a planted blindness in the detector's git-tree reader = exit 1, publicTypesAtBase 1975 -> 1710 (-265) plus -1817 / -11 / -46; the same plant on pull_request with an empty body = exit 1; with a body declaring all four counters = exit 0, so the declaring arm is not lost. Both fixes are written up (ContinuousDeliveryContract, CrossRepoPairGate) with a What's New entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LzrcSMRzD2R5UcGDk5TKU
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The workflow behavior changes are internally consistent, avoid skip-trapdoors, and are backed by an extracted-step harness that would fail if the key side effects stopped happening.
Pull request overview
Updates MeshWeaver’s CI and its documentation so two previously misleading signals become mechanically truthful: CD “heal” alerts now actually resolve themselves, and the parser-delta gate is enforced in the merge queue (so it can’t miss in-flight PRs).
Changes:
- Close
ci-failureissues automatically on a verified successful CD heal (while keeping “close failed” non-fatal, per the alerting contract). - Move the parser-delta control into a job that runs on both
pull_requestandmerge_group, enforcing the strict rule in the merge queue. - Extend the CD workflow-step harness to execute and assert the new “heal + close” behavior, and document both placement fixes.
File summaries
| File | Description |
|---|---|
.github/workflows/main-cd.yml |
Adds a tested “heal” step that comments and then closes the open ci-failure ledger, with non-fatal close failure behavior. |
.github/workflows/dotnet-test.yml |
Relocates the parser-delta control into record-signatures so it runs on merge_group and can’t be skipped in the merge queue. |
.github/scripts/test-cd-steps.py |
Extends the extracted-step test harness to cover the new heal/close behavior by asserting recorded gh invocations. |
src/MeshWeaver.Documentation/Data/Architecture/ContinuousDeliveryContract.md |
Documents the “ledger must close itself” contract and why close failures are warnings. |
src/MeshWeaver.Documentation/Data/Architecture/CrossRepoPairGate.md |
Documents the “gate added to main misses already-green PRs” placement failure and the merge-queue enforcement strategy. |
src/MeshWeaver.Documentation/Data/WhatsNew/2026-09-07-a-delivery-alert-closes-itself-and-a-new-gate-reaches-the-queue.md |
Adds a What’s New entry describing the two placement fixes as user-visible reliability improvements. |
Review details
- Files reviewed: 6/6 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
This was referenced Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3176
Closes #3508
Two CI signals that could not mean what they claimed. Both are placement, not logic — and both are the same shape: a mechanism that cannot express the state it exists to report.
#3176 — the CD alert could never be resolved, so it stopped being an alert
verify-imagesreports a successful heal onto whicheverci-failureissue is open, and the comment ended "Close this issue if nothing else is outstanding" — advice, to nobody in particular, from a bot — and closed nothing.Nobody ever did. Because
gateandalert-on-failureboth append to whicheverci-failureissue is open, the first one ever filed absorbed every attempt and every heal from then on: #3176 reached 324 comments across four days and a dozen unrelated causes, under a title describing one commit's incomplete image set that had stopped being true on its first day. The maintainer's own root-cause comment on it had to open by restating the issue's premise, because the premise had expired.That takes away both of the things an alert says:
The heal now closes it. Nothing is lost — the thread and every comment stay exactly where they were, searchable — and the next failure files a fresh issue (both
gateandalert-on-failurecreate one when none is open), so an alert's title names its own cause and its age is its own outage's age. Closing also hands the reconciler a fresh heal budget, the same effect the🛑 Automatic healing STOPPEDcomment asks a human for.🚨 The comment is fatal, the close is a warning, and the asymmetry is AGENTS.md's own test — what does a failure here hide? A lost heal comment hides the delivery record. A failed close hides nothing: the issue stays open (the behaviour that shipped for months) and the very next successful heal closes it, so a transient 403 is self-correcting. A fatal close would instead red
verify-images, whichdelivery-verdictandalert-on-failureboth read as a broken delivery — an alert about the alerting, on a run that delivered.How it is falsified
The step gained
id: healand takes every value throughenv:, because.github/scripts/test-cd-steps.pyextracts it from the workflow and executes it against a stubbedgh. Its cases assert on the recordedghinvocations, never on stdout — a step that prints the word closing and calls nogh issue closeis the defect, and the two are identical in a log.gh issue comment 3176andgh issue close 3176were both called::warning::says sogh issue close::error::+ exit 1Negative control, run: deleting the
gh issue closeline makes the harness exit 1 naming it; restoring it returns exit 0,all cases passed … steps release + decide + verdict + heal (extracted, not copied).#3508 — a gate added to
mainreached neither build of the one PR it was written forA pull request has two builds, and a gate added afterwards misses both:
pull_requestrun executes the workflow file as it stood on the merge ref when it ran — a step added tomainlater is simply not in that file, and re-running re-runs the same file;merge_grouprun — the only build that sees the change at the moment it lands — skipped the wholecross-repo-pairjob, whoseif:ispull_requestonly (correctly: its other input is the PR body).Measured on the queue run for #3546 (
gh-readonly-queue/main/pr-3546-…, run 34093021863):merge_groupPublic surface (binary compatibility)(record-signatures)completed/successCross-repo pair (public surface)completed/skippedExit (1) is structural and cannot be closed. Exit (2) is placement. So the parser-delta control moves into
record-signatures, which already runs onpull_requestandmerge_group— and carries no fork or dependabot clause, so the move closes those two exits as well.The strict/declaring split is written on the EVENT — the only exemption AGENTS.md sanctions, and the same shape the two gates beside it already use for
--pr:pull_request--pr-body-filesuppliedmerge_groupEvery build gets exactly one of the two; there is no path on which neither runs, which is the property #3508 is about. No new skip that reads as a pass.
🚦 The one trade, stated rather than hidden: a declared decrease is honoured on the pull request and refused in the queue, so a genuine parser tightening lands only if it does not shrink a published counter. That is deliberate — the queue is where the change becomes
main, and a declaration read there would have to be attributed across a multi-entry group carrying several pull requests' diffs at once. It is written intoDoc/Architecture/CrossRepoPairGatebeside the fix.How it is falsified
The acceptance test the issue names is the point of the issue, so each row below executed the step's own
run:block extracted fromdotnet-test.yml, not a restatement of it:EVENT=merge_groupMeshWeaver.Layout/skipped),merge_grouppublicTypesAtBase1975 → 1710(−265), and −1817 / −11 / −46 on the other threepull_request, empty bodypull_request, body declaring all four counters↓ DECLARED in the pull-request body, so the declaring arm is not lostThe extraction also prints where the step now lives, which is the other half of the claim:
Cost: 3 s when the detector changed, and a printed proof when it did not.
Also verified locally
check-workflow-timeouts.py(71 jobs, 0 violations, no new job) ·check-workflow-shell.py+ its self-test ·actionlint -shellcheck= -pyflakes=(the flags CI uses) — clean on both workflows ·check-parser-delta.py --self-test(13 cases) ·check-type-forwards.py --self-test(76) ·check-cross-repo-pair.py --self-test(28) ·check-pr-secret-preflight.py(no secret added) ·MeshWeaver.Documentation.TestWhatsNewEntryIntegrity+DocumentationLinkIntegrity—0 Warning(s), 0 Error(s), 2 passed.No
.cschanged.Pairs-with: none — this diff removes no public type or member from
src/; it touches two workflows, one CI harness and three docs.🤖 Generated with Claude Code
https://claude.ai/code/session_018LzrcSMRzD2R5UcGDk5TKU