Skip to content

ci: ratchet python one-function-per-file to max_lines = 16 - #1040

Merged
thekevinscott merged 3 commits into
mainfrom
claude/tackle-1037-checks16
Sep 4, 2026
Merged

thekevinscott merged 3 commits into
mainfrom
claude/tackle-1037-checks16

Conversation

@thekevinbot

Copy link
Copy Markdown
Collaborator

Part of #1037 (part 1 of 2 — the internals/checks scan root; #1039 is the packages/ts half).

unit one-function-per-file fires when one file holds two module-scope functions and one of them runs longer than max_lines. At 16, internals/checks/src had exactly four violations:

internals/checks/src/checks/preflight/gate.py:137: `prepare` runs 20 lines, and `invocation` already holds this file
internals/checks/src/checks/preflight/gate.py:167: `run` runs 24 lines, and `invocation` already holds this file
internals/checks/src/checks/preflight/matrix.py:141: `discovered` runs 23 lines, and `parse_gate_matrix` already holds this file
internals/checks/src/checks/release_globs/decide.py:125: `unprechecked` runs 21 lines, and `glob_problems` already holds this file

Every one is fixed by moving a function into its own module with its tests. No exemption (the count for this gate is zero and stays there), no golfing a function under the limit.

The four splits

New module Holds Left behind
preflight/invocation.py invocation, plus package_root / e2e_flags / the Invocation record and the CLI / LAUNCHERS tables it builds from gate.py keeps run
preflight/prepare.py prepare —
preflight/discovery.py discovered and sources matrix.py keeps parse_gate_matrix, named, read_text, pairs and the model
release_globs/precheck.py unprechecked decide.py keeps glob_problems

Two of these are shaped by constraints rather than taste, so they are worth naming:

gate.py keeps run, and invocation is what moved. #1037 sketched extracting run and prepare. invocation is itself 24 lines, so it needs its own file either way, and all nine checks/* packages use gate.py for the orchestration entry point — a preflight/run.py would be the only one. Keeping run in gate.py also leaves preflight/cli.py's from .gate import default_runner, read_e2e, run untouched.

sources had to move with discovered. discovered needs parse_gate_matrix / NoGateMatrix / REUSABLE from matrix.py, so leaving its only caller behind would have made the two modules import each other. Moving sources too keeps the dependency one-way (discovery → matrix). That does change preflight/cli.py's import site, which is the one call-site edit in the PR.

REUSABLE stays in matrix.py, so discovered's NoGateMatrix message still points contributors at the right file; discovery_test.py now asserts the full preflight/matrix.py path rather than the bare matrix.py the old test matched.

release_globs/gate.py also switches from checks.release_globs.decide import ... to a relative import, per AGENTS.md.

Config

max_lines = 16 in the three python roots this PR clears:

  • internals/checks/testing-conventions.toml (clean after the splits)
  • the root testing-conventions.toml's [python] table — the file internals/distcheck reads, already clean at 16
  • packages/python/testing-conventions.toml, already clean at 16

Left for #1039: the root file's [typescript] table and packages/ts/testing-conventions.toml stay at 24. plugins/dirsql-plugin-embeddings/testing-conventions.toml also stays at 24 — it is clean at 16 but is neither PR's lane, so it wants its own change.

Neither half carries a closing keyword, since either one alone would close #1037 early. The issue is done once both land.

Tests

Relocations, not duplications. gate_test.py (339 lines) split three ways along the code it covers, matrix_test.py two ways, decide_test.py two ways. Three assertions are new rather than moved:

  • prepare_test.py pins both drift guards to the repo root (cwd == "."), which nothing asserted before.
  • release_globs/gate_test.py pins the summary line's problem count when the failure comes from the precheck half, so the two halves are not interchangeable there.
  • discovery_test.py pins the matrix.py path in the "no caller found" fix message, as above.

Red/green does not apply: this is a behaviour-preserving move plus a config tightening, so the suite stayed green throughout.

Gate verification

just preflight (the full derived matrix, all 8 scan roots): 0 failing pair(s), 3 skipped — the three skips are packaging, which needs a built artifact.

The two diff-scoped gates that judge the move, run against the pushed commits:

internals-checks [python] one-function-per-file   exit 0 at max_lines = 16
internals-checks [python] mutation                no surviving mutants (80 mutants tested)
internals-checks [python] unit-coverage           exit 0 (100% floor)
internals-checks [python] colocated-test          exit 0
internals-checks [python] unit-lint               exit 0

uv run python -m pytest -q in internals/checks: 465 passed.

Manual exercise

The changed code is just preflight and dirsql-checks release-globs, so both were run for real:

  • just preflight — derived the matrix through the moved discovery.sources, built every argv through the moved invocation, and ran the drift guards through the moved prepare.
  • dirsql-checks preflight --conventions nope.yml → preflight: --conventions nope.yml: no such workflow. … (exit 1), and the same command from /tmp → preflight: no .github/workflows directory here. … (exit 1). Those are named and discovered's error paths.
  • dirsql-checks release-globs → ok release-globs: putitoutthere.toml and .github/workflows/release-ci.yml agree on what ships. (exit 0).
  • The same against a scratch config/workflow pair with a bare packages/rust/** glob and a !packages/rust/tests/** precheck exclusion → both annotations, including the moved unprechecked's (exit 1).

E2E Verification

  • Ran e2e suites locally for every affected package
  • Python SDK e2e: N/A — no packages/python source change
  • TypeScript SDK e2e: N/A
  • Rust core e2e: N/A
  • internals/checks/e2e-attestations/claude-tackle-1037-checks16.json receipt written (just e2e-attest-internals-checks)
  • Command(s) run: uv run python -m pytest tests/e2e -x -q in internals/checks
  • Result summary: 24 passed, exit 0

Worth correcting for the next agent on this ratchet: internals/checks does owe an e2e receipt. Its lane declares e2e-verify with --scope internals/checks/src, so any source change there stales the attestation — just preflight failed exactly one pair until the receipt was written.

Changelog / Migrations

  • Not required — verified against changelog_gate/decide.py rather than assumed. _ROOTS = ("packages", "plugins"), so internals/checks/** is outside every package the gate knows about. The one file this PR touches under packages/ is packages/python/testing-conventions.toml, which _is_exempt waives by name ({pkg}/testing-conventions\.toml — gate config ships in no artifact). The root testing-conventions.toml is not under a package root at all.
  • Migration fragment: not required — no public API, no runtime behaviour change.

No PARITY.md change: no SDK public API is touched.

…ease_globs its own module

Splits four functions out of the two files that held more than one substantial
function each, so the one-function-per-file gate can be tightened:

- preflight/invocation.py -- the argv/cwd builder, with package_root and
  e2e_flags, leaving gate.py holding run.
- preflight/prepare.py -- the per-python-root drift guards.
- preflight/discovery.py -- discovered() and sources(), which had to move
  together to keep the dependency on matrix.py one-way.
- release_globs/precheck.py -- unprechecked().

Tests move with the code they cover rather than being duplicated.
release_globs/gate.py also switches its absolute import of decide to a
relative one.
internals/checks, packages/python, and the root config's [python] table (which
internals/distcheck reads). The [typescript] tables stay at 24 until the
packages/ts split lands; the embeddings plugin is clean at 16 but is left for a
separate change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: ratchet one-function-per-file from max_lines = 24 to 16

2 participants