From 10e231d94398c5f33afdfd0a549128d0892d248f Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:24:04 +0530 Subject: [PATCH 01/12] ci: classify changed surfaces --- scripts/agent/ci_changes.py | 104 ++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 scripts/agent/ci_changes.py diff --git a/scripts/agent/ci_changes.py b/scripts/agent/ci_changes.py new file mode 100644 index 0000000..2d098be --- /dev/null +++ b/scripts/agent/ci_changes.py @@ -0,0 +1,104 @@ +"""Classify changed paths for the conditional CI workflow.""" + +from __future__ import annotations + +import os +import subprocess +import sys + + +def classify(paths: list[str]) -> tuple[bool, bool, bool, bool, bool]: + """Return whether harness, docs, Python, Rust, and package checks are required.""" + harness = docs = python = rust = package = False + + for path in paths: + if path == "scripts/agent/ci_changes.py" or path.startswith( + ".github/workflows/" + ): + harness = docs = python = rust = package = True + elif path == "uv.toml": + harness = docs = python = rust = package = True + elif path == "uv.lock": + harness = docs = python = package = True + elif path == "pyproject.toml": + docs = python = package = True + elif path.startswith("rust/"): + python = rust = package = True + elif ( + path.startswith((".codex/", "scripts/agent/")) + or (path.startswith("tests/test_agent_") and path.endswith(".py")) + or path in {"tests/test_rtk_hook.py", "tests/test_codegraph_mcp.py"} + ): + harness = True + elif ( + path in { + "README.md", + "CONTRIBUTING.md", + "docs.json", + "index.mdx", + "style.css", + "site.js", + "premove-icon.png", + "scripts/check_local_links.py", + "tests/test_docs_routes.py", + } + or path.startswith(("docs/", "itn/")) + or ( + path.startswith(("benchmarks/", "eval/")) + and path.endswith((".md", ".mdx")) + ) + ): + docs = True + elif path.startswith(("src/", "tests/")): + python = True + elif path == "scripts/check_frozen_boundaries.py": + python = True + elif path == "scripts/inspect_release_artifact.py": + package = True + elif path in { + "AGENTS.md", + "CHANGELOG.md", + "LICENSE", + "THIRD_PARTY_NOTICES.md", + ".github/PULL_REQUEST_TEMPLATE.md", + } or path.startswith( + ( + "LICENSES/", + ".github/PULL_REQUEST_TEMPLATE/", + ".github/ISSUE_TEMPLATE/", + ) + ): + continue + else: + # A new surface gets the full suite until it is classified. + harness = docs = python = rust = package = True + + return harness, docs, python, rust, package + + +def main() -> int: + if len(sys.argv) != 3: + print("usage: ci_changes.py BASE HEAD", file=sys.stderr) + return 2 + + paths = ( + subprocess.check_output( + ["git", "diff", "--name-only", "-z", sys.argv[1], sys.argv[2]] + ) + .decode() + .split("\0") + ) + required = classify([path for path in paths if path]) + + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output: + for name, value in zip( + ("harness", "docs", "python", "rust", "package"), + required, + strict=True, + ): + print(f"{name}={str(value).lower()}", file=output) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From aee4f2d6be34a1c08bd84b5cdb904c5beada1cac Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:24:07 +0530 Subject: [PATCH 02/12] test: cover conditional CI routing --- tests/test_agent_ci_changes.py | 89 ++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/test_agent_ci_changes.py diff --git a/tests/test_agent_ci_changes.py b/tests/test_agent_ci_changes.py new file mode 100644 index 0000000..46c1bac --- /dev/null +++ b/tests/test_agent_ci_changes.py @@ -0,0 +1,89 @@ +"""Changed-surface routing keeps required CI checks proportional.""" + +from scripts.agent.ci_changes import classify + + +def test_policy_only_needs_no_expensive_jobs() -> None: + assert classify( + [ + "AGENTS.md", + "CHANGELOG.md", + ".github/PULL_REQUEST_TEMPLATE.md", + ".github/ISSUE_TEMPLATE/bug_report.md", + ] + ) == (False, False, False, False, False) + + +def test_harness_only_needs_harness_job() -> None: + assert classify([tests_path := "tests/test_agent_ci_changes.py"]) == ( + True, + False, + False, + False, + False, + ) + assert tests_path.endswith(".py") + assert classify( + [ + ".codex/config.toml", + "scripts/agent/start_change.py", + "tests/test_agent_check.py", + "tests/test_rtk_hook.py", + "tests/test_codegraph_mcp.py", + ] + ) == (True, False, False, False, False) + + +def test_docs_use_lightweight_docs_job() -> None: + assert classify( + [ + "README.md", + "docs/agent-harness.md", + "itn/docs/deployment.md", + "docs.json", + "style.css", + "site.js", + "tests/test_docs_routes.py", + ] + ) == (False, True, False, False, False) + + +def test_python_runtime_does_not_force_rust_or_package_jobs() -> None: + assert classify(["src/premove_itn/contextual.py"]) == ( + False, + False, + True, + False, + False, + ) + assert classify(["tests/test_candidate_scorer.py"]) == ( + False, + False, + True, + False, + False, + ) + + +def test_rust_changes_cover_cross_language_and_package_behavior() -> None: + assert classify(["rust/src/lib.rs"]) == (False, False, True, True, True) + assert classify(["rust/Cargo.toml"]) == (False, False, True, True, True) + + +def test_dependency_routes_match_consumers() -> None: + assert classify(["uv.lock"]) == (True, True, True, False, True) + assert classify(["pyproject.toml"]) == (False, True, True, False, True) + assert classify(["uv.toml"]) == (True, True, True, True, True) + + +def test_workflow_and_unknown_paths_require_all_jobs() -> None: + assert classify(["scripts/agent/ci_changes.py"]) == (True, True, True, True, True) + assert classify([".github/workflows/ci.yml"]) == (True, True, True, True, True) + assert classify(["new-surface/tool.py"]) == (True, True, True, True, True) + assert classify(["AGENTS.md", "src/premove_itn/contextual.py"]) == ( + False, + False, + True, + False, + False, + ) From 07dcdd93e390a39e1f3eb81b60df14634a11ffa9 Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:25:08 +0530 Subject: [PATCH 03/12] docs: align generic agent rules with premove --- AGENTS.md | 64 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index dd6fc14..518ef15 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,9 @@ voice-agent transcripts. Rust generates valid written candidates, a frozen DeBERTa scorer ranks them in sentence context, and an exact decoder selects compatible edits. +Code and tests define implemented behavior. Read the README only for public API, +installation, or documentation changes. + ## Boundaries - `rust/` owns deterministic realization rules and candidate generation. @@ -15,11 +18,19 @@ compatible edits. ## Change rules -- For semantic implementation changes, read the relevant implementation, - direct callers, and tests before editing. For small mechanical or - configuration changes, inspect only the target state and evidence needed to - apply and validate the requested change. -- Make the smallest coherent change and preserve unrelated work. +- For semantic changes, inspect the implementation, direct callers, consumers, + and relevant tests. +- For mechanical changes, inspect only the target and required validation. +- Use CodeGraph for structure. Use `rg` for exact text and references. +- Make the smallest coherent change. Preserve unrelated work. +- Add abstractions, options, compatibility layers, or caches only for a current + need. +- Keep one authoritative owner for each semantic rule. +- Test behavior, not edits. Add or update tests only when behavior, contracts, + or critical workflow logic changes. +- Prefer deterministic tests and replay. Use live models or downloads only when + that boundary is under test. +- Measure end-to-end latency before claiming a performance improvement. - Keep `PremoveITN` as the single implementation behind the Python API and CLI. - Do not change model, candidate, decoder, or normalization behavior in a cleanup-only change. @@ -74,12 +85,21 @@ If two evidence-driven fixes fail and the cause remains unclear, stop and return ## Git - Treat `main` as the stable release branch. -- Use one short-lived branch and one focused pull request per change. +- Create branches with `scripts/agent/start_change.py --base --branch /`. Use `main` for normal work and the parent branch for stacked work. +- Use prefixes: `feat/`, `fix/`, `refactor/`, `perf/`, `test/`, `docs/`, `ci/`, `build/`, `chore/`, `design/`, and `epic/`. +- Keep each pull request to one coherent responsibility and one primary review question. Keep its implementation, tests, and required docs together. +- Split at a new responsibility, pipeline boundary, independent invariant, or substantial runtime surface. File count is a warning, not a limit. +- Use stacked pull requests for dependent stages. Each layer targets its parent branch. +- Use `epic/` only when dependent work must stay off `main` until integrated. Child PRs target the epic; the final epic PR targets `main`. +- Do not split trivial work or combine unrelated work for PR-count or file-count reasons. - Do not push directly to `main` or force-push shared branches. -- Merge only after required checks pass. +- Merge only when explicitly requested and required checks pass. ## Verification +Keep validation proportional to the changed surface. Documentation and policy-only +changes do not require the full Python, Rust, or package suites. + During implementation, use the focused gate with the semantic tests relevant to the change: @@ -87,10 +107,6 @@ the change: uv run --locked python scripts/agent/check.py focused --pytest ``` -Prefer the smallest validation that establishes the change locally. Do not run -the complete local gate only to duplicate checks that pull-request CI runs from -a clean checkout. - Use the complete local gate when the change is cross-cutting, modifies the validation harness or CI, prepares a release, CI is unavailable, or focused evidence is insufficient: @@ -100,6 +116,7 @@ uv run --locked python scripts/agent/check.py full ``` GitHub Actions is the authoritative completion gate for pull requests. + Do not repeat an already-passing local gate only to open or integrate an unchanged, already-reviewed branch. Confirm the branch state and expected diff, then rely on the target pull request's authoritative CI unless prior validation @@ -107,21 +124,22 @@ is stale, incomplete, or the integration introduces new changes. ## Command output -RTK may compact supported shell output. When compact output omits information -needed to continue: +RTK may compact shell output. -1. Use the printed `rtk recall ` handle. -2. Use `rtk proxy ` if raw re-execution is required. -3. Use `RTK_DISABLED=1 ` only when RTK behavior itself is under - investigation. +- Use `rtk recall ` when omitted output is needed. +- Use `rtk proxy ` for raw re-execution. +- Use `RTK_DISABLED=1` only when debugging RTK. +- Do not wrap `scripts/agent/check.py` with RTK. It already verifies exact recall. - ## CodeGraph -In repositories indexed by CodeGraph (a `.codegraph/` directory exists at the repo root), reach for it BEFORE grep/find or reading files when you need to understand or locate code: +Use CodeGraph before broad grep, find, or file reads when locating or +understanding code. -- **MCP tool** (when available): `codegraph_explore` answers most code questions in one call — the relevant symbols' verbatim source plus the call paths between them, including dynamic-dispatch hops grep can't follow. Name a file or symbol in the query to read its current line-numbered source. If it's listed but deferred, load it by name via tool search. -- **Shell** (always works): `codegraph explore ""` prints the same output. +Use `rg` for exact text and references. -If there is no `.codegraph/` directory, skip CodeGraph entirely — indexing is the user's decision. - +If `.codegraph/` is missing, run: + +```bash +uv run --locked python scripts/agent/bootstrap.py +``` From ed655728d026cc349584eb328759ccfb4c403965 Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:25:11 +0530 Subject: [PATCH 04/12] ci: route checks by changed surface --- .github/workflows/ci.yml | 110 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddf2a4f..301a97a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,8 +14,87 @@ concurrency: cancel-in-progress: true jobs: + changes: + name: Changed surfaces + runs-on: ubuntu-22.04 + outputs: + harness: ${{ steps.classify.outputs.harness }} + docs: ${{ steps.classify.outputs.docs }} + python: ${{ steps.classify.outputs.python }} + rust: ${{ steps.classify.outputs.rust }} + package: ${{ steps.classify.outputs.package }} + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Classify changed paths + id: classify + env: + BASE: ${{ github.event.pull_request.base.sha || github.event.before }} + HEAD: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + python scripts/agent/ci_changes.py "$BASE" "$HEAD" + git diff --check "$BASE" "$HEAD" + + harness: + name: Agent harness + needs: changes + if: ${{ needs.changes.outputs.harness == 'true' }} + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up uv + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 + with: + enable-cache: true + version-file: uv.toml + python-version: "3.11" + + - name: Install locked harness environment + run: uv sync --locked --only-group dev --no-install-project + + - name: Lint harness + run: uv run --no-sync ruff check scripts/agent tests/test_agent_*.py tests/test_rtk_hook.py tests/test_codegraph_mcp.py + + - name: Check harness formatting + run: uv run --no-sync ruff format --check scripts/agent tests/test_agent_*.py tests/test_rtk_hook.py tests/test_codegraph_mcp.py + + - name: Test harness + run: uv run --no-sync pytest -q tests/test_agent_*.py tests/test_rtk_hook.py tests/test_codegraph_mcp.py + + docs: + name: Documentation + needs: changes + if: ${{ needs.changes.outputs.docs == 'true' }} + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up uv + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 + with: + enable-cache: true + version-file: uv.toml + python-version: "3.11" + + - name: Install locked docs test environment + run: uv sync --locked --only-group dev --no-install-project + + - name: Check local links + run: uv run --no-sync python scripts/check_local_links.py + + - name: Check documentation routes + run: uv run --no-sync pytest -q tests/test_docs_routes.py + python: name: Python / package + needs: changes + if: ${{ needs.changes.outputs.python == 'true' }} runs-on: ubuntu-22.04 steps: - name: Check out repository @@ -43,6 +122,8 @@ jobs: rust: name: Rust + needs: changes + if: ${{ needs.changes.outputs.rust == 'true' }} runs-on: ubuntu-22.04 steps: - name: Check out repository @@ -69,6 +150,8 @@ jobs: package: name: Package / release wheel + needs: changes + if: ${{ needs.changes.outputs.package == 'true' }} runs-on: ubuntu-22.04 steps: - name: Check out repository @@ -87,3 +170,30 @@ jobs: - name: Build, inspect, and smoke test release artifacts run: uv run --locked python scripts/agent/check.py package --smoke + + gate: + name: CI gate + needs: [changes, harness, docs, python, rust, package] + if: ${{ always() }} + runs-on: ubuntu-22.04 + steps: + - name: Require all selected checks + env: + CHANGES: ${{ needs.changes.result }} + HARNESS_REQUIRED: ${{ needs.changes.outputs.harness }} + HARNESS_RESULT: ${{ needs.harness.result }} + DOCS_REQUIRED: ${{ needs.changes.outputs.docs }} + DOCS_RESULT: ${{ needs.docs.result }} + PYTHON_REQUIRED: ${{ needs.changes.outputs.python }} + PYTHON_RESULT: ${{ needs.python.result }} + RUST_REQUIRED: ${{ needs.changes.outputs.rust }} + RUST_RESULT: ${{ needs.rust.result }} + PACKAGE_REQUIRED: ${{ needs.changes.outputs.package }} + PACKAGE_RESULT: ${{ needs.package.result }} + run: | + test "$CHANGES" = success + if [ "$HARNESS_REQUIRED" = true ]; then test "$HARNESS_RESULT" = success; fi + if [ "$DOCS_REQUIRED" = true ]; then test "$DOCS_RESULT" = success; fi + if [ "$PYTHON_REQUIRED" = true ]; then test "$PYTHON_RESULT" = success; fi + if [ "$RUST_REQUIRED" = true ]; then test "$RUST_RESULT" = success; fi + if [ "$PACKAGE_REQUIRED" = true ]; then test "$PACKAGE_RESULT" = success; fi From 5ce6c060aa58c5904e71169d7841ed8689bbb331 Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:25:31 +0530 Subject: [PATCH 05/12] test: simplify CI routing cases --- tests/test_agent_ci_changes.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_agent_ci_changes.py b/tests/test_agent_ci_changes.py index 46c1bac..7505c5f 100644 --- a/tests/test_agent_ci_changes.py +++ b/tests/test_agent_ci_changes.py @@ -15,14 +15,13 @@ def test_policy_only_needs_no_expensive_jobs() -> None: def test_harness_only_needs_harness_job() -> None: - assert classify([tests_path := "tests/test_agent_ci_changes.py"]) == ( + assert classify(["tests/test_agent_ci_changes.py"]) == ( True, False, False, False, False, ) - assert tests_path.endswith(".py") assert classify( [ ".codex/config.toml", From dff06188a8ab0429619b222a1e6744a72b9f73cf Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:27:17 +0530 Subject: [PATCH 06/12] ci: classify harness summary as policy --- scripts/agent/ci_changes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/agent/ci_changes.py b/scripts/agent/ci_changes.py index 2d098be..58db965 100644 --- a/scripts/agent/ci_changes.py +++ b/scripts/agent/ci_changes.py @@ -57,6 +57,7 @@ def classify(paths: list[str]) -> tuple[bool, bool, bool, bool, bool]: package = True elif path in { "AGENTS.md", + "AGENT_HARNESS.md", "CHANGELOG.md", "LICENSE", "THIRD_PARTY_NOTICES.md", From 59f8718c9148c6b6d7b61c068f584c7f307fe57f Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:27:19 +0530 Subject: [PATCH 07/12] test: align CI routing fixtures --- tests/test_agent_ci_changes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_agent_ci_changes.py b/tests/test_agent_ci_changes.py index 7505c5f..dcdea88 100644 --- a/tests/test_agent_ci_changes.py +++ b/tests/test_agent_ci_changes.py @@ -7,6 +7,7 @@ def test_policy_only_needs_no_expensive_jobs() -> None: assert classify( [ "AGENTS.md", + "AGENT_HARNESS.md", "CHANGELOG.md", ".github/PULL_REQUEST_TEMPLATE.md", ".github/ISSUE_TEMPLATE/bug_report.md", @@ -37,7 +38,7 @@ def test_docs_use_lightweight_docs_job() -> None: assert classify( [ "README.md", - "docs/agent-harness.md", + "docs/model-card.md", "itn/docs/deployment.md", "docs.json", "style.css", From 85bd18c9c7ccfe194b2213d7f2e8bc67170905d3 Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:27:21 +0530 Subject: [PATCH 08/12] docs: describe conditional CI routing --- AGENT_HARNESS.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/AGENT_HARNESS.md b/AGENT_HARNESS.md index 1bf881f..e313fad 100644 --- a/AGENT_HARNESS.md +++ b/AGENT_HARNESS.md @@ -68,7 +68,8 @@ repository's `scripts/agent/rtk_hook.py` adapter. RTK shortens supported shell output and preserves a way to recover the full output. When a compact result omits needed detail, use `rtk recall `. Use `rtk proxy ` to rerun a command with raw output. Set `RTK_DISABLED=1` only when investigating -RTK itself. +RTK itself. The repository-owned validation runner must not be outer-wrapped by +RTK because it already verifies exact recall before compacting failures. ## Validation @@ -85,3 +86,8 @@ enough: ```bash uv run --locked python scripts/agent/check.py full ``` + +Pull-request CI classifies changed paths before running expensive work. Agent +harness, documentation, Python, Rust, and package checks run only when their +surfaces require them. Unknown surfaces select the full set. A final CI gate +requires every selected job and is the authoritative pull-request result. From 8150b692e7040f37082960fc5f44c80c855950e5 Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:28:53 +0530 Subject: [PATCH 09/12] ci: satisfy classifier lint --- scripts/agent/ci_changes.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/agent/ci_changes.py b/scripts/agent/ci_changes.py index 58db965..27337c8 100644 --- a/scripts/agent/ci_changes.py +++ b/scripts/agent/ci_changes.py @@ -12,12 +12,12 @@ def classify(paths: list[str]) -> tuple[bool, bool, bool, bool, bool]: harness = docs = python = rust = package = False for path in paths: - if path == "scripts/agent/ci_changes.py" or path.startswith( - ".github/workflows/" + if ( + path == "scripts/agent/ci_changes.py" + or path.startswith(".github/workflows/") + or path == "uv.toml" ): harness = docs = python = rust = package = True - elif path == "uv.toml": - harness = docs = python = rust = package = True elif path == "uv.lock": harness = docs = python = package = True elif path == "pyproject.toml": @@ -49,9 +49,10 @@ def classify(paths: list[str]) -> tuple[bool, bool, bool, bool, bool]: ) ): docs = True - elif path.startswith(("src/", "tests/")): - python = True - elif path == "scripts/check_frozen_boundaries.py": + elif ( + path.startswith(("src/", "tests/")) + or path == "scripts/check_frozen_boundaries.py" + ): python = True elif path == "scripts/inspect_release_artifact.py": package = True From e1b31c08c0c2aa64905a3a00ee7a9447d7ddb65e Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:28:56 +0530 Subject: [PATCH 10/12] ci: remove ignored setup-uv input --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 301a97a..7ee1208 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,6 @@ jobs: uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 with: enable-cache: true - version-file: uv.toml python-version: "3.11" - name: Install locked harness environment @@ -79,7 +78,6 @@ jobs: uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 with: enable-cache: true - version-file: uv.toml python-version: "3.11" - name: Install locked docs test environment @@ -109,7 +107,6 @@ jobs: uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 with: enable-cache: true - version-file: uv.toml - name: Install locked development environment run: uv sync --locked --all-groups @@ -143,7 +140,6 @@ jobs: uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 with: enable-cache: true - version-file: uv.toml - name: Run Rust validation run: uv run --no-project python scripts/agent/check.py rust @@ -166,7 +162,6 @@ jobs: uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 with: enable-cache: true - version-file: uv.toml - name: Build, inspect, and smoke test release artifacts run: uv run --locked python scripts/agent/check.py package --smoke From 0f1c216d7b846f99012975bcbd63bb5a027eafda Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:32:16 +0530 Subject: [PATCH 11/12] ci: keep classifier formatting stable --- scripts/agent/ci_changes.py | 86 +++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/scripts/agent/ci_changes.py b/scripts/agent/ci_changes.py index 27337c8..d82edae 100644 --- a/scripts/agent/ci_changes.py +++ b/scripts/agent/ci_changes.py @@ -6,6 +6,52 @@ import subprocess import sys +_HARNESS_FILES = {"tests/test_rtk_hook.py", "tests/test_codegraph_mcp.py"} +_DOC_FILES = { + "README.md", + "CONTRIBUTING.md", + "docs.json", + "index.mdx", + "style.css", + "site.js", + "premove-icon.png", + "scripts/check_local_links.py", + "tests/test_docs_routes.py", +} +_POLICY_FILES = { + "AGENTS.md", + "AGENT_HARNESS.md", + "CHANGELOG.md", + "LICENSE", + "THIRD_PARTY_NOTICES.md", + ".github/PULL_REQUEST_TEMPLATE.md", +} + + +def _is_harness_path(path: str) -> bool: + return ( + path.startswith((".codex/", "scripts/agent/")) + or path in _HARNESS_FILES + or (path.startswith("tests/test_agent_") and path.endswith(".py")) + ) + + +def _is_docs_path(path: str) -> bool: + return ( + path in _DOC_FILES + or path.startswith(("docs/", "itn/")) + or ( + path.startswith(("benchmarks/", "eval/")) + and path.endswith((".md", ".mdx")) + ) + ) + + +def _is_policy_path(path: str) -> bool: + return path in _POLICY_FILES or path.startswith( + ("LICENSES/", ".github/PULL_REQUEST_TEMPLATE/", ".github/ISSUE_TEMPLATE/") + ) + def classify(paths: list[str]) -> tuple[bool, bool, bool, bool, bool]: """Return whether harness, docs, Python, Rust, and package checks are required.""" @@ -24,30 +70,9 @@ def classify(paths: list[str]) -> tuple[bool, bool, bool, bool, bool]: docs = python = package = True elif path.startswith("rust/"): python = rust = package = True - elif ( - path.startswith((".codex/", "scripts/agent/")) - or (path.startswith("tests/test_agent_") and path.endswith(".py")) - or path in {"tests/test_rtk_hook.py", "tests/test_codegraph_mcp.py"} - ): + elif _is_harness_path(path): harness = True - elif ( - path in { - "README.md", - "CONTRIBUTING.md", - "docs.json", - "index.mdx", - "style.css", - "site.js", - "premove-icon.png", - "scripts/check_local_links.py", - "tests/test_docs_routes.py", - } - or path.startswith(("docs/", "itn/")) - or ( - path.startswith(("benchmarks/", "eval/")) - and path.endswith((".md", ".mdx")) - ) - ): + elif _is_docs_path(path): docs = True elif ( path.startswith(("src/", "tests/")) @@ -56,20 +81,7 @@ def classify(paths: list[str]) -> tuple[bool, bool, bool, bool, bool]: python = True elif path == "scripts/inspect_release_artifact.py": package = True - elif path in { - "AGENTS.md", - "AGENT_HARNESS.md", - "CHANGELOG.md", - "LICENSE", - "THIRD_PARTY_NOTICES.md", - ".github/PULL_REQUEST_TEMPLATE.md", - } or path.startswith( - ( - "LICENSES/", - ".github/PULL_REQUEST_TEMPLATE/", - ".github/ISSUE_TEMPLATE/", - ) - ): + elif _is_policy_path(path): continue else: # A new surface gets the full suite until it is classified. From de47d79286c181ba598a2ddd544b663934d007a8 Mon Sep 17 00:00:00 2001 From: Aryaman Todkar <78498856+aryamantodkar@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:33:21 +0530 Subject: [PATCH 12/12] ci: simplify classifier predicates --- scripts/agent/ci_changes.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/scripts/agent/ci_changes.py b/scripts/agent/ci_changes.py index d82edae..c79ebdc 100644 --- a/scripts/agent/ci_changes.py +++ b/scripts/agent/ci_changes.py @@ -26,31 +26,33 @@ "THIRD_PARTY_NOTICES.md", ".github/PULL_REQUEST_TEMPLATE.md", } +_POLICY_PREFIXES = ( + "LICENSES/", + ".github/PULL_REQUEST_TEMPLATE/", + ".github/ISSUE_TEMPLATE/", +) def _is_harness_path(path: str) -> bool: - return ( - path.startswith((".codex/", "scripts/agent/")) - or path in _HARNESS_FILES - or (path.startswith("tests/test_agent_") and path.endswith(".py")) - ) + if path.startswith((".codex/", "scripts/agent/")): + return True + if path in _HARNESS_FILES: + return True + return path.startswith("tests/test_agent_") and path.endswith(".py") def _is_docs_path(path: str) -> bool: - return ( - path in _DOC_FILES - or path.startswith(("docs/", "itn/")) - or ( - path.startswith(("benchmarks/", "eval/")) - and path.endswith((".md", ".mdx")) - ) - ) + if path in _DOC_FILES: + return True + if path.startswith(("docs/", "itn/")): + return True + return path.startswith(("benchmarks/", "eval/")) and path.endswith((".md", ".mdx")) def _is_policy_path(path: str) -> bool: - return path in _POLICY_FILES or path.startswith( - ("LICENSES/", ".github/PULL_REQUEST_TEMPLATE/", ".github/ISSUE_TEMPLATE/") - ) + if path in _POLICY_FILES: + return True + return path.startswith(_POLICY_PREFIXES) def classify(paths: list[str]) -> tuple[bool, bool, bool, bool, bool]: