Skip to content

A branch with no PR can still be checked, and path filters are a no we wrote down - #581

Merged
JulienMartel merged 2 commits into
mainfrom
worktree-cut-gate-protection
Sep 12, 2026
Merged

JulienMartel merged 2 commits into
mainfrom
worktree-cut-gate-protection

Conversation

@JulienMartel

@JulienMartel JulienMartel commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

What

Two leftovers from the CI pass that #578 landed.

  • .github/workflows/test.yml gets workflow_dispatch:.
  • docs/ci.md rule 2's canonical trigger block gets the same line, plus a
    sentence saying what it is for.
  • docs/ci.md's What we deliberately don't do gains a paths-ignore
    stanza, with the measurement and the method.
  • The same section's "no hosted binary cache" now says what it does not rule
    out.

hausfold/factory#23 is this PR's other half: the same one line, in the one
other repo that had the gap. It is green.

Why

The dispatch line. Narrowing push to branches: [main] stops a commit on
a PR branch being checked twice. It also takes away the only way a branch with
no PR yet was ever checked — and #578 wrote the narrowed trigger into
docs/ci.md as the family standard without the line that hands that back.
haus's check.yml has had workflow_dispatch all along, with a comment
explaining exactly this. Now the rule, workshop and factory all match haus.

The path filter. It was an open question in this project and it now has a
measured answer: no. Against a real ignore list over each repo's last 40 merged
PRs, the share that would actually skip is

repo would skip
haus 1 / 40
trill 6 / 40
scruff 10 / 40
perch 11 / 40
nebelung 12 / 40

A quarter at the top of the range, one in forty at the bottom, saving two or
three minutes each. That does not buy a second silent copy of "which files does
this job protect?" — the copy rots, which is docs/drift.md's whole subject —
and prose in this family is guarded prose (embed-skills.sh --check, the two
check-skills.sh, the generated issue forms). The sharp end is scruff:
release.yml reaches its gate through workflow_call, so a filter that skips a
job on a docs path skips it for a tag too, publishing to registries that have no
undo.

The stanza carries the method as well as the number, because the loose version
of this measurement is how the idea keeps coming back: a classifier that counts
modules/**/*.md and dist/**/README.md as docs says 80%, which for haus is
wrong by a factor of thirty.

The substituter carve-out. "No hosted binary cache" reads as a blanket no,
and its three reasons (an account, a token in nine repos, a service that can be
down) are all about a cache we run. A public read-only extra-substituters
entry for something nixpkgs doesn't carry has none of them and fails soft to
building from source. Saying so keeps the next person from reading the stanza as
a blocker.

Verify

git show HEAD:.github/workflows/test.yml \
  | ruby -ryaml -e 'p YAML.safe_load($stdin.read)[true]'
# => {"push"=>{"branches"=>["main"]}, "pull_request"=>nil, "workflow_dispatch"=>nil}

The block in docs/ci.md rule 2 is now byte-identical to that trigger. After
merge, Actions → Tests → Run workflow offers a branch picker, and a PR here
still produces one run per commit.

Re-run the measurement (about a minute):

gh pr list --repo hausfold/perch --state merged --limit 40 --json number,files

then count the PRs where every path is in the ignore list.

Watch out

  • Three repos do not yet have the trigger this doc now states as the
    trigger.
    workflow_dispatch is on haus check, pounce build, perch
    build, trill build, workshop Tests (this PR) and factory test
    (A branch with no PR can still be checked on demand factory#23). It is missing from scruff check.yml, snug
    ci.yml and nebelung check.yml, and nothing catches the gap. One line
    each; filed as its own to-do rather than widened into this PR.
  • The Nix store cache section's nebelung bullet still says whiskers "is not
    in cache.nixos.org", which stays literally true after the nebelung
    substituter PR now in flight — but the implication softens, since the store
    cache stops being the only thing between a cold run and 218s of rustc. Worth
    a sentence there once that lands, not before.
  • bench overlap is · clear, but another lane is in docs/ci.md
    right now — measure-nix-store, elsewhere in the file (and in
    docs/drift.md). merge-tree is clean against it and against origin/main.
    It looks likely to add a rule about grouping parallel jobs; if it lands
    first, this rebases without touching the same hunks.
  • factory tier says not-1 on the factory half (it touches
    .github/workflows), so the lease will not merge either of these. They need a
    go-ahead.
  • Docs-only change plus one workflow line, so this repo's own gate cannot
    exercise the dispatch button until it is on main.

🤖 Generated with Claude Code

https://claude.ai/code/session_019v1reBfZd8d4euMNyXry6B

JulienMartel and others added 2 commits September 12, 2026 04:39
…e wrote down

Two leftovers from the CI pass.

Narrowing `push` to `main` took away the only way a branch with no PR yet ever
got checked. haus's check.yml already hands that back with `workflow_dispatch`;
workshop's test.yml did not, and neither did the canonical trigger block in
docs/ci.md's rule 2. Both now match haus. factory's test.yml had the same gap
and takes the same one line in hausfold/factory#23.

The path-filter question is now measured and closed. Against a real ignore list
over each repo's last 40 merged PRs, the share that would skip is haus 1,
trill 6, scruff 10, perch 11, nebelung 12 — two or three minutes saved on a
quarter of PRs at best. Not enough to buy a second silent copy of "which files
does this job protect?", which rots; and scruff's release path reaches its gate
through `workflow_call`, so a filter that skips a job on a docs path skips it
for a tag too. It goes under "What we deliberately don't do" with the numbers
and with the method, because the loose version of that measurement (which says
80%) is how the idea keeps coming back.

The same section's "no hosted binary cache" now says what it does not rule out:
a public read-only substituter for something nixpkgs doesn't carry needs no
account and no token, and fails soft to building from source.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019v1reBfZd8d4euMNyXry6B
…easured

Assurance pass on the first commit found three things worth the rewrite.

The scruff argument was wrong for the mechanism the heading named. A
`paths-ignore:` under `on:` cannot skip anything at a tag — a `workflow_call`
invocation does not evaluate the called workflow's own triggers, so
release.yml still runs the full gate. The risk is real only for an in-job
changed-files filter, which IS evaluated on that run, so the stanza now names
that implementation and says where a filter would have to go if one were ever
worth it.

The section also contradicted the hausfold.co paragraph above it, which calls a
`paths:` filter the first thing to reach for. Allowlist and denylist were never
distinguished: a `paths:` says what one task workflow is about, a
`paths-ignore:` on the gate says what the repo merges unchecked. Only the
second is the no, and the heading now says so.

The numbers were unreproducible because the ignore list they were measured
against was nowhere in the doc. It is in the doc now, and the closing paragraph
points at it instead of at an "actual list you would ship" the reader would
have to invent.

Two smaller ones: the substituter carve-out was answering "a cache we run" when
the paragraph above rules out one we rent and push to, and fail-soft is the
property that makes the difference, so it says that; and rule 2's block now
says it is the floor rather than the whole `on:`, since scruff's gate also
needs `workflow_call`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019v1reBfZd8d4euMNyXry6B
@JulienMartel
JulienMartel merged commit 94af5ed into main Sep 12, 2026
1 check passed
@JulienMartel
JulienMartel deleted the worktree-cut-gate-protection branch September 12, 2026 09:48
JulienMartel added a commit to hausfold/nebelung that referenced this pull request Sep 12, 2026
… it (#61)

The store cache from #59 wins the HIT; this is what makes the MISS cheap.
`whiskers` is in neither nixpkgs nor cache.nixos.org, so a lock bump or an
evicted entry still meant a full rustc toolchain closure and 218 seconds to
render text files. Catppuccin publishes it to its own cachix, signed, and
the x86_64-linux path these runs need is there — so the installer trusts
that cache and a miss downloads 2.2 MB of zstd (6.9 MB unpacked) instead.
Coverage is 3 of the flake's 4 systems: x86_64-darwin 404s there, which
costs nothing while ubuntu-latest is the only thing that builds.

Read-only and public: no account and no token, which is a different trade
from the hosted binary cache the workshop's docs/ci.md rules out —
hausfold/workshop#581 is the carve-out that says so, and until it lands
this step is an exception to a rule that does not yet name it. A
substituter that is down is not a failure either: Nix falls back to
building, which is exactly what every run does today.

What it does cost is that `trusted-public-keys` is not per-path, so
catppuccin's signing key can supply any store path in this job, not only
whiskers. The repo already builds their derivation from a locked rev; this
adds their prebuilt binaries to what it will accept.

`nix_conf` rather than `extra-conf`: this repo installs Nix with
nixbuild/nix-quick-install-action, whose input writes ~/.config/nix/nix.conf
before the action appends its own access token and flake settings, so
nothing it needs is clobbered. `extra-substituters` adds to cache.nixos.org
rather than replacing it, and cachix answers at Priority 41 against
cache.nixos.org's 40, so nothing else changes where it comes from.


Claude-Session: https://claude.ai/code/session_019ds2UUkGK1mBHTWQYaopuE

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant