ci: hausfold.co fixed what rule 3's paragraph still calls out - #587
Merged
Merged
Conversation
`docs/ci.md`'s hausfold.co paragraph closed on two sentences that named its drift jobs as "the place rules 2 and 3 are least applied (`push:` with `paths:` and no `branches:`, and no `concurrency` at all), which is a thing to fix there rather than a thing to copy". hausfold.co#338 fixed exactly that, and a standard that assigns work to another repo is the one document nobody re-reads when the work lands. Verified at hausfold.co `a45e661`, all 11 workflows: 11 of 11 carry a `concurrency:` block, all 9 `push:` triggers carry `branches: [main]`, and 10 of 11 carry `workflow_dispatch`. The four drift jobs are now the fullest statement of both rules in the family, not the thinnest. So the two sentences say what those jobs are now worth copying FOR. Each of the four checks the site's copy against a checkout of `hausfold/haus` (`check-bar-tables`, `check-rice-bindings`, `gen-options --check`, `check-rooms`), and a `paths:` filter cannot see a change made in another repo. Each pairs the filter with a Monday `schedule:`: the filter keeps the gate quiet while only this site moves, the cron catches an upstream that moved without it. That is the answer to the blind spot rule 3 just spent a paragraph recommending. The endorsement stops at the pairing — `deploy` and `dns` put non-PR runs in shared groups, deliberately and for reasons of their own, and rule 3 still reads them the way it reads anyone. The one real gap is `preview.yml`, `pull_request`-only and the single workflow in the family without `workflow_dispatch` outside the release workflows rule 2 already exempts. It stays that way, and rule 2 now says why rather than leaving it to be re-opened: it deploys a preview Worker named from `github.event.pull_request.number`, keys its concurrency group on the same number, and gates both jobs on `head.repo.full_name`, so a branch with no PR has nothing there for it to check. Adding the line would hand someone a button whose run groups as `preview-` with both jobs skipped, which is worse than no button. The exemption is written at the width of its evidence — triggered by `pull_request` alone, so it narrowed no `push` and took nothing away to hand back — and not at the width of "a workflow about pull requests", which `preview-sweep.yml` also is while correctly carrying the line. That exemption falsified a clause 120 lines down, in *The Nix store cache*: "a `workflow_dispatch` — which rule 2 requires of every workflow here". It now reads "of every workflow whose `push` was narrowed to `main`", the same formulation as the exemption. The reasoning downstream is unaffected; only the universal phrasing was. The first half of the hausfold.co paragraph is untouched: it is still the family's one instance of the `paths:` rule, and still the place to look for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UMUHVTxRfgADnACNXzc6Ef
JulienMartel
added a commit
that referenced
this pull request
Sep 13, 2026
Rule 3 states an absolute — "**Only a PR run is ever in a shared group**" —
and the family has three standing counterexamples, all in `hausfold.co`:
`deploy.yml`, `dns.yml` and `preview-sweep.yml`, each keying every run into
one constant group named for the workflow, each on purpose, and the doc said
nothing about any of them. An unstated exemption is what the next PR "fixes".
Census over the org's public repos at their `origin/main`: 27 workflows, 21
carrying a `concurrency:` block, exactly 3 with a constant top-level group.
The near misses are `scruff`'s `release.yml` (`release-${{ github.ref }}`) and
hausfold.co's `preview.yml` (keyed on the PR number), both correctly keyed.
`ops` is private and outside the sweep; it runs a scheduled `scoreboard` and
nothing on a push.
The property, written so the count is not load-bearing: what a gate loses when
an intermediate run is dropped is that commit's answer, which nothing else
will produce. These three converge something outside the repo instead — the
deployed site, the DNS zone, this repo's own preview Workers — and a converge
has no answer to lose, because the surviving run redoes the whole job from the
current state. For them the constant group is the point of the line, not a
cost of it: what it serializes is the resource.
The guard against reading that wider than it is: check for a `pull_request`
trigger first. None of the three has one, and `preview.yml` is the standing
example of a workflow that converges something external AND has a PR run, and
keys on the PR number anyway. The one other constant group in the family,
`pounce`'s job-level `bump-pin`, is in a release workflow and off this list by
the doc's own line; a reader who greps finds four and now knows why.
`cancel-in-progress` is a second and separate question, about what a stopped
run leaves behind, and the three do not share one reason. `deploy` cancels, a
newer build superseding an older outright. `dns` does not, because a converge
stopped between two tables leaves the zone half-way. The preview sweep does
not for a different reason entirely: its deletes are independent and a Worker
already gone counts as success, so nothing is half-written — there is just
nothing for a later run to supersede, and a `dry_run` dispatch would otherwise
take out a live sweep.
And the cost is stated rather than papered over. `dns`'s scheduled `check`
shares `group: dns` with `publish`, a *pending* run is cancelled outright when
a newer one joins whatever `cancel-in-progress` says, so a Monday check queued
behind a running converge can be dropped and that week's drift answer waits
seven days. One writer on the zone is worth more than one week's report. It is
a trade made once, for a converge, and not a reason to key a gate this way.
Follow-up to #587, which narrowed rule 3's endorsement of `hausfold.co` so the
paragraph could not be read as blessing these three, but left the exemption
itself unwritten.
Claude-Session: https://claude.ai/code/session_01UMUHVTxRfgADnACNXzc6Ef
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What
docs/ci.md, three edits, docs only.hausfold.coparagraph — the last two sentences went. They named its drift jobs as "the place rules 2 and 3 are least applied (push:withpaths:and nobranches:, and noconcurrencyat all), which is a thing to fix there rather than a thing to copy". They now say what those jobs are worth copying for. The first half of the paragraph is untouched.preview.ymlexemption, stated at the width of its evidence.pushwas narrowed tomain" — the new exemption falsified the universal phrasing 120 lines down.Why
hausfold.co#338 fixed the thing rule 3 was still calling out. A standard that assigns work to another repo is the one document nobody re-reads when the work lands, so the criticism outlived its subject and the doc was recommending a fix that was already in.
What replaces it is the reason those four jobs are the family's best statement of rules 2 and 3: each checks the site's copy against a checkout of
hausfold/haus, and apaths:filter cannot see a change made in another repo. Each pairs the filter with a Mondayschedule:— the filter keeps the gate quiet while only the site moves, the cron catches an upstream that moved without it. That pairing is the answer to the blind spot rule 3 had just spent a paragraph recommending.The endorsement stops at the pairing.
deployanddnsput non-PR runs in shared groups, deliberately and for their own reasons, and rule 3 reads them the way it reads anyone.The one real gap is
preview.yml,pull_request-only and the only workflow in the family withoutworkflow_dispatchoutside the release workflows rule 2 already exempts. It stays that way and rule 2 now says why: the workflow deploys a preview Worker named fromgithub.event.pull_request.number, keys its concurrency group on the same number, and gates both jobs onhead.repo.full_name. A branch with no PR has nothing there for it to check, so adding the line would hand someone a button whose run groups aspreview-with both jobs skipped — worse than no button. No hausfold.co PR needed.Verify
Every claim checked against
hausfold/hausfold.coata45e661(git show origin/main:<path>, read-only), all 11 workflows:concurrency:push:carriesbranches: [main]workflow_dispatchpresentpreview.ymlthe only gap*-drift.ymlcheck outhausfold/hauspath: .haus0,15,15,30past 9 on Mon)preview.ymlgroup keyed on the PR numberpreview-${{ github.event.pull_request.number }}preview.ymljobs gate onhead.repo.full_namepreviewandcleanupbothA dispatched
preview.ymlrun:head.repo.full_nameis null, sopreview'siffails;actionis absent, socleanup'saction == 'closed'fails. Both skipped, grouppreview-.Widened past hausfold.co: the only other dispatch-less workflows anywhere in the family are the five
release.ymls, already out of scope atdocs/ci.md's release-workflow paragraph. "The family's only one" holds.Pre-PR assurance pass run on
git diff main...HEAD+AGENTS.md; nothing landed at 4 or 5, and every finding at 2 or 3 is fixed in this commit (the falsified clause at line 201, the over-wide exemption lede, the unstated concurrency-group fact, the paragraph order that left "that block" pointing two paragraphs back, and the endorsement's scope).No test in this repo asserts on
docs/ci.md.Watch out
docs/ci.mdis hot — CI: one run per commit, nothing from apt, and the rules written down #578, ci: haus is five ubuntu jobs, the shell half four of them #580, A branch with no PR can still be checked, and path filters are a no we wrote down #581, ci: what the store cache actually costs, and the number the ceiling counts #582, ci: the nix half is three jobs on haus, and a key per job is why #583, ci: the store cache's save guard reads the ref, because workflow_dispatch exists #584 and ci: a fifth rule — group parallel jobs by need, not by step count #585 all landed in it today. Rebase ontomain, never mergemainin.bench overlap, verbatim:The
⚠is on a file this branch does not touch.add-homebrew-formuladoes touchdocs/ci.md, but only at the gate table near the top (@@ -23,12 +23,15 @@), nowhere near the three edits here.Rule 3's stated absolute — "Only a PR run is ever in a shared group" — has two live, deliberate counterexamples in
hausfold.co(group: deploycancelling,group: dnsnot). Both are commented in their own files and neither is a speed measure. This PR narrows the endorsement so nobody reads the paragraph as blessing them, but does not write a rule 3 exemption. That is a separate call.🤖 Generated with Claude Code
https://claude.ai/code/session_01UMUHVTxRfgADnACNXzc6Ef