Skip to content

ci: run on pull_request and add the "CI passed" aggregator - #4

Merged
hunterdsp merged 1 commit into
mainfrom
ci/pull-request-trigger
Jul 31, 2026
Merged

ci: run on pull_request and add the "CI passed" aggregator#4
hunterdsp merged 1 commit into
mainfrom
ci/pull-request-trigger

Conversation

@hunterdsp

Copy link
Copy Markdown
Contributor

Fixes the deadlock found while reviewing #3: every pull request to this repo
is currently unmergeable.

The problem

main ruleset            requires status check "CI passed"
only workflow           check-readme.yml
its triggers            push(main) / schedule / workflow_dispatch  -- no pull_request
its job name            check                                      -- not "CI passed"
check-runs on a PR head (none)  ->  mergeStateStatus: BLOCKED, forever

The required check cannot be reported on a pull request, so the merge box waits
on something that will never arrive.

It looked fine because both merged PRs (#1, #2) landed 2026-06-29, and the
ruleset was created 2026-07-29 — every merge in this repo predates the rule
by a month. #3 is simply the first PR to meet it.

CI passed is doppler's aggregator job name
(doppler/.github/workflows/ci.yml:620), so the ruleset looks copied from
doppler's without the workflow that makes the name true.

The fix

  • pull_request trigger, with no path filter — deliberately. The required
    check must report on every PR, so the job must run on every PR. A path
    filter would skip it on an unrelated change and reproduce exactly the same
    deadlock, just less often and more confusingly.
  • A ci-passed job named "CI passed", gating on every other job in the
    file, mirroring doppler's aggregator so the org has one shape. if: always() so it still reports when a dependency fails — skipped would make
    a red PR indistinguishable from an unmergeable one.

Self-bootstrapping

pull_request events run workflows from the merge ref, so this workflow runs
on its own PR
. The PR that adds the required check is the first to be gated by
it — no chicken-and-egg, and no admin override needed here or on #3 afterwards.

Verification

  • YAML parses; triggers are push, pull_request, schedule, workflow_dispatch;
    jobs are check and ci-passed; the aggregator's name: is exactly
    CI passed, matching the ruleset string.
  • The repo's own pre-commit hooks pass on the file.
  • The real check is this PR's own merge box: if CI passed appears here, the
    fix works.

Note the irony for the plan in #3: this is the same drift class one level up —
a config copied into a second repo without the thing it depends on, unnoticed
until it fires.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XeNc2g4pM4Sq2Dhds5zT6j

The `main` ruleset (created 2026-07-29) requires a status check named
"CI passed". No workflow in this repo produced it, and the only workflow here
had no `pull_request` trigger at all — so nothing ran on a PR and the required
check could never arrive. Every pull request was permanently unmergeable.

It went unnoticed because both merged PRs (#1, #2) landed on 2026-06-29, a
month before the ruleset existed; #3 is the first PR to meet the rule.
"CI passed" is doppler's aggregator job name, so the ruleset appears to have
been copied from doppler's without the workflow that makes the name true.

Two changes:

- `pull_request` trigger, with NO path filter. The required check must report
  on every PR, so the job has to run on every PR — a path filter would skip it
  on an unrelated change and reproduce the same deadlock.
- a `ci-passed` job named "CI passed", gating on every other job, mirroring
  doppler's ci.yml aggregator so the org has one shape. `if: always()` so it
  still reports when a dependency fails, rather than being skipped and leaving
  a red PR indistinguishable from an unmergeable one.

This workflow runs on its own PR (pull_request events use the merge ref), so
the fix is self-bootstrapping: the PR that adds the check is the first to be
gated by it.
@hunterdsp
hunterdsp merged commit 9ba411b into main Jul 31, 2026
2 checks passed
hunterdsp added a commit to just-buildit/just-buildit.github.io that referenced this pull request Jul 31, 2026
…s it

Two defects found while validating this repo against the process it now
documents.

**1. Every PR here is unmergeable.** The `main` ruleset requires a status check
named `CI passed`, and the only workflow (`mirror.yml`) has no `pull_request`
trigger and reports no such check — so the required check can never arrive.
The single merge path is an org-admin bypass, which is a required check that
gates nothing: this project's own thesis pointed at its own infrastructure.
That matters more here than anywhere else, because canonical `standard.mk`
lives in this repo, so *every future change to the standard* has to come
through a PR that cannot merge.

Same shape, and same fix, as just-buildit/.github#4. Self-bootstrapping:
`pull_request` workflows run from the merge ref, so the PR adding this check
is the first one gated by it.

**2. The new README contradicted the standard on where local targets live.**
It said repo-only targets "go in `local.mk`", but `standard.mk`'s own comment
says "in the Makefile or local.mk", the plan's criterion 1 explicitly permits
"config + genuinely local targets" in the Makefile, and the reference adopter
puts all 26 of its own targets there and passes every gate. Corrected to say
what actually matters: naming them in `LOCAL_TARGETS`.

**The CI is not a rubber stamp.** A broken publish here does not fail here — it
fails in every adopter at once, the moment they re-vendor, because their
`make lint` fetches this file with no cache. So the job checks the three things
that would cost that:

* the file still fails CLEANLY with the config surface unset (asserting on the
  message, since a syntax error and a deliberate `$(error)` look alike to make);
* every feature group still defines its targets, with all nine flags on (>=38);
* its own gates pass on a clean tree AND fail on a sabotaged one — a
  ghost-check that cannot catch a rule-less `.PHONY` is exactly the failure
  this standard exists to prevent.

Plus, on `main` only, that Pages serves what is committed — the drift gate's
question asked from the other side.

All four verified locally against the published file before committing: the
unset-config case errors naming `TEST_CMD`, all-flags-on lists 40 targets, the
clean tree passes both gates, and the sabotaged tree fails naming `ghosty`.
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