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

Open
#5,823 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.