Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml → .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ name: CI
#
# Until pgw#952 a lane PR in this repository could run NO CI AT ALL. Verified on
# PR #466 (`950-legacy-hardcut`, 215+/178-): `statusCheckRollup` was `[]`. Not a
# check that passed — no check ran. At the time the cause was `ci.yml` naming
# check that passed — no check ran. At the time the cause was `ci.yaml` naming
# only `master` while lanes targeted `dev`; the branch names have since swapped
# roles, and the invariant that matters is the one to preserve:
#
Expand Down Expand Up @@ -95,7 +95,7 @@ name: CI
# ─── NO `paths:` / `paths-ignore:` FILTER ──────────────────────────────────────
#
# The old `paths-ignore: ["**.md"]` is gone, and no `paths:` filter replaces it.
# A path filter is precisely what made proto-contract.yml silently absent from
# A path filter is precisely what made proto-contract.yaml silently absent from
# #466, and three of the gates below scan the WHOLE TREE:
# `lint_unreached_surface.py` walks the public surface for unreached symbols,
# `lint_config_reads.py` censuses every `os.environ` access against
Expand All @@ -115,13 +115,13 @@ name: CI
# device path here runs through fakes, skips, or CPU fallbacks. A regression
# that only manifests on a real card is NOT caught, and green here is not
# evidence about the fleet. That is the nightly GPU lane's job.
# native-kernels.yml proves the kernels COMPILE; it never runs one.
# native-kernels.yaml proves the kernels COMPILE; it never runs one.
# * LINT. `Lint` is `continue-on-error` (pre-existing debt) — findings are
# visible in the log and block nothing.
# * DEPENDENCY-RESOLUTION DRIFT beyond `--locked`. `uv sync --locked` proves the
# lock is internally consistent, not that its pins are current.
# * CROSS-REPO CONTRACT DRIFT against tensorhub's canonical proto. That is
# proto-contract.yml layer 1 here and layer 2 in tensorhub, by design.
# proto-contract.yaml layer 1 here and layer 2 in tensorhub, by design.
#
# The list above used to be the only place a reader could learn what a green run
# did not measure, and it could only ever name CLASSES. pgw#966 makes the actual
Expand Down Expand Up @@ -220,7 +220,7 @@ jobs:
run: uv python install 3.12

# --locked (pgw#524 item 6): resolve exactly what uv.lock pins — the
# same resolution publish.yml uses on tag push, so PR-CI green
# same resolution publish.yaml uses on tag push, so PR-CI green
# actually implies publish green (fails loudly instead if uv.lock
# drifts from pyproject.toml, the 0.18.0 silent-publish-failure root
# cause). Both jobs use this exact line; keep them identical.
Expand Down Expand Up @@ -404,7 +404,7 @@ jobs:
run: uv run python scripts/assemble_changelog.py --check

# Cheap (2s) and it belongs with the gates, not behind the suite: a
# broken sdist/wheel build is a publish-time failure, and publish.yml
# broken sdist/wheel build is a publish-time failure, and publish.yaml
# gates on a green run of THIS workflow.
- name: Build package
run: uv build
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ on:
# pgw#977: `dev` is retired; lanes PR straight to `master`, which is where
# pgw#952's intent now lives — a csrc change is compiled at the PR that
# introduces it, not downstream where the next lane pays for it.
# The `paths:` filter STAYS, unlike ci.yml's: this job compiles one
# The `paths:` filter STAYS, unlike ci.yaml's: this job compiles one
# directory and its verdict is a property of that directory alone — it is
# not tree-scanning, so nothing can invalidate it collaterally.
branches: [master]
paths:
- "csrc/**"
- "scripts/native/**"
- ".github/workflows/native-kernels.yml"
- ".github/workflows/native-kernels.yaml"

concurrency:
group: native-kernels-${{ github.ref }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ name: proto contract
# copy of tensorhub's canonical copy. Until th#1457/#1488 makes the schema
# generated, the two are maintained by hand and this gate keeps them identical.
#
# SEPARATE FROM ci.yml on purpose — but NOT for the reason first written here.
# The original text said ci.yml was master-only "because CI spend is metered
# SEPARATE FROM ci.yaml on purpose — but NOT for the reason first written here.
# The original text said ci.yaml was master-only "because CI spend is metered
# (WORKSPACE-GIT-POLICY.md)". pgw#952 measured that premise and it is false for
# THIS repo: python-gen-worker is PUBLIC, so standard-runner minutes are free and
# unmetered. ci.yml now runs on `[dev, master]` like this workflow does.
# unmetered. ci.yaml now runs on `[dev, master]` like this workflow does.
#
# What still justifies a separate file is COST SHAPE, not spend: this job is a
# sha256sum plus one codegen (~40s, no llama-server, no pytest), so it returns a
# contract verdict in well under a minute instead of behind ci.yml's 15m suite,
# and keeping it out of ci.yml is what guarantees it stays that cheap.
# contract verdict in well under a minute instead of behind ci.yaml's 15m suite,
# and keeping it out of ci.yaml is what guarantees it stays that cheap.
#
# This side runs LAYER 1 ONLY (local copy vs the shared PROTO_DIGEST). Layer 2 —
# comparing against tensorhub's copy — cannot run here: tensorhub is a PRIVATE
Expand All @@ -30,7 +30,7 @@ on:
- "scripts/proto-drift-check.sh"
- "scripts/vendor-proto.sh"
- "src/gen_worker/pb/**"
- ".github/workflows/proto-contract.yml"
- ".github/workflows/proto-contract.yaml"
# pgw#952: the second step REGENERATES the bindings with grpc_tools.protoc
# and diffs them, so the generator's own version is an input to this
# gate's verdict. A `grpcio-tools` bump moves the generated header and the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

# --locked (pgw#524 item 6): fail loudly if uv.lock drifts from
# pyproject.toml instead of silently re-resolving newer dependency
# versions than what PR CI (ci.yml, also --locked) validated — the
# versions than what PR CI (ci.yaml, also --locked) validated — the
# 0.18.0 root cause: a tag's publish run resolved different versions
# than the merged PR did and failed AFTER the tag was already cut.
- name: Install dependencies
Expand All @@ -40,11 +40,11 @@ jobs:
# re-derive it.
#
# This step used to be `pytest tests/` — the same suite, on the same tree,
# with the same `--locked` resolution that ci.yml had already run green.
# with the same `--locked` resolution that ci.yaml had already run green.
# A second identical run cannot produce new information; its only possible
# novel outcomes are infrastructure flake and timing flake, and it produced
# three of those in a row for v0.78.0 at ~22 minutes each, on top of a 4.6h
# scheduling stall, blocking three downstream lanes. So: ci.yml's full
# scheduling stall, blocking three downstream lanes. So: ci.yaml's full
# suite at -n 4 is THE test gate, and this job runs only what is
# artifact-facing (build + the pgw#740 wheel contract) once the tree it is
# about to ship is known to have passed it.
Expand Down
6 changes: 3 additions & 3 deletions Taskfile.yml → Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ tasks:
- uv build

# THERE IS NO `task publish`, deliberately (pgw#1140, 2026-08-12). PyPI
# publication is the TAG PUSH: `.github/workflows/publish.yml` refuses to ship
# publication is the TAG PUSH: `.github/workflows/publish.yaml` refuses to ship
# a tree no CI run has proven, and a local `uv publish` walks around that gate.
# One procedure, and it lives in docs/releasing.md. Do not re-add this task.
#
Expand Down Expand Up @@ -132,7 +132,7 @@ tasks:
pgw#944 — fail if the VENDORED contract was hand-edited: the local
proto/worker_scheduler.proto must hash to proto/PROTO_DIGEST, the same
digest tensorhub commits. Gated in CI by
.github/workflows/proto-contract.yml on PRs into dev AND master.
.github/workflows/proto-contract.yaml on PRs into dev AND master.
cmds:
- PROTO_SKIP_PEER=1 scripts/proto-drift-check.sh

Expand Down Expand Up @@ -220,7 +220,7 @@ tasks:
# module/session fixture and everything that shares its process stay
# together. Override with `task test WORKERS=1` to debug serially.
#
# pgw#808: `tests_v2/` runs as its own step, mirroring ci.yml — a suite CI
# pgw#808: `tests_v2/` runs as its own step, mirroring ci.yaml — a suite CI
# gates on but `task test` skips is the same dead weight in a slower form.
vars:
WORKERS: '{{.WORKERS | default "4"}}'
Expand Down
24 changes: 12 additions & 12 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Releasing gen-worker

**This file is the ONLY release procedure.** Publication is the **tag push** — `publish.yml` refuses
to ship a tree no CI run has proven. `Taskfile.yml` deliberately has no `publish` task: a local
**This file is the ONLY release procedure.** Publication is the **tag push** — `publish.yaml` refuses
to ship a tree no CI run has proven. `Taskfile.yaml` deliberately has no `publish` task: a local
`uv publish` walks around that gate, and a second written procedure is how a cutter ends up
following the stale one. **No local mint is a release gate** — the `rig:*` tasks are development
vehicles, they run real inductor/AOTI compiles, and Paul's 2026-08-10 hard cut puts every mint on a
Expand Down Expand Up @@ -92,9 +92,9 @@ one version deliberately; do not reach for a global exact pin to get it.

## The gate — what actually has to be green

`ci.yml`. **A local `pytest` run is not the gate** and will let you push a red.
`ci.yaml`. **A local `pytest` run is not the gate** and will let you push a red.

Since pgw#952 it is TWO PARALLEL JOBS, and both must be green — `publish.yml` keys on
Since pgw#952 it is TWO PARALLEL JOBS, and both must be green — `publish.yaml` keys on
the run's conclusion, which is success only when both are. It runs on **every PR into `master`**
(pgw#977 narrowed the trigger back to `[master]` alone when `dev` was deleted, so that IS the
every-lane trigger), plus `workflow_dispatch` — so a cut no longer inherits a pile of unproven lane
Expand All @@ -116,11 +116,11 @@ merges. A third context, `drift`, also gates.
3. `pytest tests/ -n 4 --dist loadfile`
4. `pytest tests_v2/ -n 4 --dist loadfile` — **`testpaths = ["tests"]`, so a bare `pytest` skips this.**

**`ruff check src/gen_worker` is NOT a gate.** `ci.yml` runs it with
**`ruff check src/gen_worker` is NOT a gate.** `ci.yaml` runs it with
`continue-on-error: true` (lint debt, mostly `worker.py`) — it is visible in
the logs and blocks nothing. Do not spend cut time on red ruff output.

**Publishing additionally requires a green CI run carrying this exact TREE** (`publish.yml` compares
**Publishing additionally requires a green CI run carrying this exact TREE** (`publish.yaml` compares
tree SHAs, not commit SHAs). Tag a commit CI has already proven, or dispatch CI on the tag and re-run
publish.

Expand All @@ -135,7 +135,7 @@ Measured on the 0.90.6 cut: three reds, none authored by the cutter — another
stale pgw#849 baseline entry, and **two tests that had never passed CI in their lives** (established
by `git merge-base --is-ancestor` against the last green run).

**Since pgw#952** every lane PR is gated, so the debt stopped accruing: `ci.yml` had been
**Since pgw#952** every lane PR is gated, so the debt stopped accruing: `ci.yaml` had been
`branches: [master]` while lanes targeted `dev`, and such a PR ran NO CI AT ALL (PR #466's
`statusCheckRollup` was literally `[]`). `dev` has since been deleted and pgw#977 pointed the
trigger back at `master`, which is now where every lane PRs — so a red is refused at the lane PR
Expand Down Expand Up @@ -201,8 +201,8 @@ git merge-base --is-ancestor <commit> HEAD # per must-ride
git rev-list --count v<prev>..HEAD # and COUNT it; do not estimate

# 3. green CI on this exact tree, then tag and push
gh workflow run ci.yml --ref <branch>
git tag -s v<X.Y.Z> -m "..." && git push origin v<X.Y.Z> # tag push triggers publish.yml
gh workflow run ci.yaml --ref <branch>
git tag -s v<X.Y.Z> -m "..." && git push origin v<X.Y.Z> # tag push triggers publish.yaml
```

**Tags are SSH-SIGNED** (`git tag -s`; the repo sets `gpg.format=ssh` + `user.signingkey`). Check
Expand All @@ -211,13 +211,13 @@ release marker, which v0.110.0 is.

### Only a DISPATCHED run proves a tree — the gate enforces this, you do not have to remember it

Step 3 is `gh workflow run ci.yml --ref <branch>` for a reason: a `workflow_dispatch` (or `push`) run
Step 3 is `gh workflow run ci.yaml --ref <branch>` for a reason: a `workflow_dispatch` (or `push`) run
checks out the ref it names, so its `head_sha` genuinely names the tree it built. **A `pull_request`
run does not** — `ci.yml` checks out with no `ref:`, so GitHub builds `refs/pull/<n>/merge`, your head
run does not** — `ci.yaml` checks out with no `ref:`, so GitHub builds `refs/pull/<n>/merge`, your head
merged with whatever `master` is at that moment, while still recording your branch head as
`head_sha`.

**`publish.yml` refuses a `pull_request` run as proof** (pgw#1191, `scripts/assert_ci_proof.py`), so
**`publish.yaml` refuses a `pull_request` run as proof** (pgw#1191, `scripts/assert_ci_proof.py`), so
this is a rule the gate holds rather than a step you can forget. If you see it, the refusal tells you
which problem you have:

Expand Down
10 changes: 5 additions & 5 deletions scripts/assert_ci_proof.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
"""A tag publishes only against a CI run that BUILT the tagged tree.

Matching the tag's tree against a green `ci.yml` run by `head_sha` is not
enough. `ci.yml` checks out with `actions/checkout@v4` and no `ref:`, so on a
Matching the tag's tree against a green `ci.yaml` run by `head_sha` is not
enough. `ci.yaml` checks out with `actions/checkout@v4` and no `ref:`, so on a
`pull_request` event GitHub builds `refs/pull/<n>/merge` -- the PR head merged
with whatever `master` is at that moment -- while still recording the PR BRANCH
HEAD as the run's `head_sha`. A green PR run therefore names a commit it never
Expand Down Expand Up @@ -42,7 +42,7 @@

@dataclass(frozen=True)
class Run:
"""One `ci.yml` run, as the Actions API reports it."""
"""One `ci.yaml` run, as the Actions API reports it."""

id: int
head_sha: str
Expand Down Expand Up @@ -118,7 +118,7 @@ def _gh_json(args: Sequence[str]) -> object:

def _api_runs(repo: str, per_page: int) -> list[Run]:
payload = _gh_json(
["api", f"repos/{repo}/actions/workflows/ci.yml/runs?per_page={per_page}"])
["api", f"repos/{repo}/actions/workflows/ci.yaml/runs?per_page={per_page}"])
rows = (payload or {}).get("workflow_runs", []) if isinstance(payload, dict) else []
return [Run.from_api(r) for r in rows]

Expand Down Expand Up @@ -150,7 +150,7 @@ class of hole pgw#795 closed for v0.78.0).

The fix is one CI run:

gh workflow run ci.yml --ref <this-tag> # or the branch you will tag
gh workflow run ci.yaml --ref <this-tag> # or the branch you will tag

then re-run this publish job once it is green.

Expand Down
2 changes: 1 addition & 1 deletion scripts/lint_mypy_ratchet.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def check(pyproject: Path) -> List[str]:
# `tests tests_v2` from the invocation would return the suite to zero static
# coverage and every check would stay green — the exact shape of failure
# this repo keeps paying for, so it is asserted rather than trusted.
workflow = pyproject.parent / ".github" / "workflows" / "ci.yml"
workflow = pyproject.parent / ".github" / "workflows" / "ci.yaml"
if workflow.exists():
invocations = [
line.strip() for line in workflow.read_text().splitlines()
Expand Down
2 changes: 1 addition & 1 deletion scripts/skip_census.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
# other side. The suite reaches exactly three third parties, all of which now
# degrade to a counted skip below rather than to a red:
#
# github.com the pinned llama-server tarball (ci.yml, continue-on-error).
# github.com the pinned llama-server tarball (ci.yaml, continue-on-error).
# Docker Hub `ubuntu:24.04` for the pgw#858 root parent (exit 125 -> skip).
# PyPI `uv build` + `uv pip install` in the pgw#740 wheel row — the
# same PyPI `uv sync --locked` already required in step 5.
Expand Down
10 changes: 5 additions & 5 deletions tests/test_publish_proof_gate_pgw1191.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

REPO = Path(__file__).resolve().parent.parent
SCRIPT = REPO / "scripts" / "assert_ci_proof.py"
PUBLISH_YML = REPO / ".github" / "workflows" / "publish.yml"
PUBLISH_YAML = REPO / ".github" / "workflows" / "publish.yaml"

TAG_TREE = "aaaa111122223333444455556666777788889999"
OTHER_TREE = "bbbb111122223333444455556666777788889999"
Expand Down Expand Up @@ -147,7 +147,7 @@ def test_failed_and_unresolvable_runs_are_ignored_not_trusted() -> None:


# --------------------------------------------------------------------------
# the CLI, driven exactly as publish.yml drives it
# the CLI, driven exactly as publish.yaml drives it
# --------------------------------------------------------------------------


Expand All @@ -174,7 +174,7 @@ def test_cli_exits_nonzero_and_explains_itself_on_a_pr_only_proof(tmp_path: Path
assert "::error::" in proc.stderr
assert gate.ONLY_PULL_REQUEST_PROOF in proc.stderr
# It must tell the releaser the ONE command that fixes it.
assert "gh workflow run ci.yml" in proc.stderr
assert "gh workflow run ci.yaml" in proc.stderr


def test_cli_exits_zero_on_a_dispatched_proof(tmp_path: Path) -> None:
Expand All @@ -196,10 +196,10 @@ def test_cli_exits_zero_on_a_dispatched_proof(tmp_path: Path) -> None:
def test_publish_workflow_calls_the_gate_and_keeps_no_inline_matcher() -> None:
"""A gate that can be quietly replaced by the shell loop it fixed is not a
gate. This is the row that goes red if someone inlines the match again."""
text = PUBLISH_YML.read_text()
text = PUBLISH_YAML.read_text()

assert "scripts/assert_ci_proof.py" in text
assert 'select(.conclusion=="success")' not in text, (
"publish.yml matches CI runs inline again — the pgw#1191 rule "
"publish.yaml matches CI runs inline again — the pgw#1191 rule "
"(a pull_request run proves the MERGE tree, not the head) is bypassed"
)
Loading