koala73 / koala73/worldmonitor
[P3] CI: 'proto-freshness' runs but does not gate — path-filtered workflow cannot join the deploy gate's required list as-is
- Dominant language
- TypeScript
- Stars
- 86.6k
- Forks
- 13.1k
- Avg merge
- 8h 4m
- Merged PRs (30d)
- 825
Description
## Summary
`proto-check.yml`'s `proto-freshness` job verifies that `make generate` output is committed — if `src/generated/` or `docs/api/` is stale, it fails. It reports on the PR, but a red result does not block merge.
Branch protection on `main` requires exactly `biome`, `typecheck`, `unit`, `gate`. `gate` (`deploy-gate.yml`) aggregates only the four workflows named in its `workflow_run` trigger: `Test`, `Typecheck`, `Lint Code`, `Security Audit`. `Proto Generation Check` is not among them, so `proto-freshness` is advisory.
## Verified
- `deploy-gate.yml:17` — `workflows: ["Test", "Typecheck", "Lint Code", "Security Audit"]`; `Proto Generation Check` absent.
- `deploy-gate.yml:36` — `required` list contains no `proto-freshness`.
- `gh api repos/koala73/worldmonitor/branches/main/protection` — required contexts are `biome`, `typecheck`, `unit`, `gate`.
- `proto-freshness` does publish a check run on PR head SHAs (present on `ac4fce5075502fd9e149f2dcb22a1649cdffe0a6`).
## Why the #5402 fix did not cover this
The drift guard added for #5402 (`tests/ci-workflow-coverage.test.mts`, "requires every job of every workflow the deploy gate aggregates") only walks workflows the gate already claims. Adding `Proto Generation Check` to that claim is not a one-line change, because of how the gate treats an absent check run:
`proto-check.yml` is **path-filtered** (`paths: proto/**, src/generated/**, docs/api/**, Makefile, .github/workflows/proto-check.yml`). On a PR that touches none of those, the workflow never runs and publishes **no check run at all**. The gate maps a missing check run to `pending` (`deploy-gate.yml:92-94`), and pending never clears — so naively adding `proto-freshness` to `required` would hold every unrelated PR at "Waiting for required PR gates" forever.
This is a different failure mode from an `if:`-skipped job, which *does* publish a check run with conclusion `skipped` and is correctly treated as passing.
The job is also `if: github.event.pull_request.head.repo.full_name == github.repository`, so fork PRs skip it — that half is fine (a skipped job publishes a check run).
## Options
1. Drop the `paths:` filter so `Proto Generation Check` runs on every PR, add it to the gate's `workflow_run` list and `required`. Costs a Go toolchain + buf install on every PR.
2. Keep the filter but add a cheap always-running companion job in the same workflow that publishes a stable check run, gating the expensive step behind the path condition (the `docs-stats` / `public-docs` shape — always-on, no `needs: changes`).
3. Teach the gate to distinguish "workflow not triggered for this SHA" from "job not finished yet". This is the general fix but needs a real not-applicable signal; getting it wrong fails open.
Option 2 matches how the repo already keeps always-on checks cheap.
Contributor guide
Research direction
Start with .github/workflows/proto-check.yml and .github/workflows/deploy-gate.yml, then read tests/ci-workflow-coverage.test.mts and the existing docs-stats/public-docs workflow shape. Determine how to publish a stable check for unrelated pull requests while gating proto freshness when relevant paths change, update the gate configuration, and run the CI workflow coverage tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github-actions, typescript
- Domain
- ci-cd, devops
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100