diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yaml similarity index 98% rename from .github/workflows/ci.yml rename to .github/workflows/ci.yaml index f4aa07322..b736ee2a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yaml @@ -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: # @@ -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 @@ -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 @@ -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. @@ -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 diff --git a/.github/workflows/native-kernels.yml b/.github/workflows/native-kernels.yaml similarity index 93% rename from .github/workflows/native-kernels.yml rename to .github/workflows/native-kernels.yaml index c4114b8b8..251f1a128 100644 --- a/.github/workflows/native-kernels.yml +++ b/.github/workflows/native-kernels.yaml @@ -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 }} diff --git a/.github/workflows/proto-contract.yml b/.github/workflows/proto-contract.yaml similarity index 89% rename from .github/workflows/proto-contract.yml rename to .github/workflows/proto-contract.yaml index 535f0e5f8..54f5e0f5b 100644 --- a/.github/workflows/proto-contract.yml +++ b/.github/workflows/proto-contract.yaml @@ -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 @@ -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 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yaml similarity index 96% rename from .github/workflows/publish.yml rename to .github/workflows/publish.yaml index 5aaf96eb7..98b93e805 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yaml @@ -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 @@ -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. diff --git a/Taskfile.yml b/Taskfile.yaml similarity index 97% rename from Taskfile.yml rename to Taskfile.yaml index c2c1a4a7b..5c73f88fb 100644 --- a/Taskfile.yml +++ b/Taskfile.yaml @@ -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. # @@ -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 @@ -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"}}' diff --git a/docs/releasing.md b/docs/releasing.md index bb6d9aa28..18863fbb3 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -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 @@ -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 @@ -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. @@ -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 @@ -201,8 +201,8 @@ git merge-base --is-ancestor HEAD # per must-ride git rev-list --count v..HEAD # and COUNT it; do not estimate # 3. green CI on this exact tree, then tag and push -gh workflow run ci.yml --ref -git tag -s v -m "..." && git push origin v # tag push triggers publish.yml +gh workflow run ci.yaml --ref +git tag -s v -m "..." && git push origin v # tag push triggers publish.yaml ``` **Tags are SSH-SIGNED** (`git tag -s`; the repo sets `gpg.format=ssh` + `user.signingkey`). Check @@ -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 ` for a reason: a `workflow_dispatch` (or `push`) run +Step 3 is `gh workflow run ci.yaml --ref ` 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//merge`, your head +run does not** — `ci.yaml` checks out with no `ref:`, so GitHub builds `refs/pull//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: diff --git a/scripts/assert_ci_proof.py b/scripts/assert_ci_proof.py index a7a89b7d2..3fdf7e8d7 100644 --- a/scripts/assert_ci_proof.py +++ b/scripts/assert_ci_proof.py @@ -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//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 @@ -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 @@ -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] @@ -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 # or the branch you will tag + gh workflow run ci.yaml --ref # or the branch you will tag then re-run this publish job once it is green. diff --git a/scripts/lint_mypy_ratchet.py b/scripts/lint_mypy_ratchet.py index 7c5542ada..c53f2144e 100644 --- a/scripts/lint_mypy_ratchet.py +++ b/scripts/lint_mypy_ratchet.py @@ -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() diff --git a/scripts/skip_census.txt b/scripts/skip_census.txt index 1f35c9314..fbd42e14d 100644 --- a/scripts/skip_census.txt +++ b/scripts/skip_census.txt @@ -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. diff --git a/tests/test_publish_proof_gate_pgw1191.py b/tests/test_publish_proof_gate_pgw1191.py index b6b157660..bf6d665b3 100644 --- a/tests/test_publish_proof_gate_pgw1191.py +++ b/tests/test_publish_proof_gate_pgw1191.py @@ -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" @@ -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 # -------------------------------------------------------------------------- @@ -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: @@ -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" )