From cceffed20eefa1c9139660e38d0a74d93bcae686 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Tue, 27 Oct 2026 17:24:46 +0400 Subject: [PATCH 1/2] Enforce the file-length rule and delete its empty baseline No Python file is over its api.quality.file-length limit any more, so the rule moves from ratcheted to enforced, its [rule.baseline] table and the empty rules/baselines/file-length.txt are removed, and the skills doc says the rule has no baseline. The rule has no downgrade record to remove: the rules tool needs one only when a rule is weakened. Co-Authored-By: Claude Opus 5.5 --- changelog.d/file-length-enforce.changed.md | 1 + docs/engineering/skills/file-length.md | 14 +++++--------- docs/generated/rules.md | 8 ++++---- rules/baselines/file-length.txt | 6 ------ rules/registry.toml | 7 +------ 5 files changed, 11 insertions(+), 25 deletions(-) create mode 100644 changelog.d/file-length-enforce.changed.md delete mode 100644 rules/baselines/file-length.txt diff --git a/changelog.d/file-length-enforce.changed.md b/changelog.d/file-length-enforce.changed.md new file mode 100644 index 000000000..0f3448406 --- /dev/null +++ b/changelog.d/file-length-enforce.changed.md @@ -0,0 +1 @@ +Enforced the file-length rule now that no Python file is over its limit, and deleted its empty baseline. diff --git a/docs/engineering/skills/file-length.md b/docs/engineering/skills/file-length.md index f7bdab303..d8b242e43 100644 --- a/docs/engineering/skills/file-length.md +++ b/docs/engineering/skills/file-length.md @@ -47,12 +47,8 @@ where that is natural, and keep every one under the limit. no longer reaches that call, and the test silently stops using its fake. Patch the module whose code calls the name. -## The baseline - -The rule is ratcheted: `rules/baselines/file-length.txt` lists the files that -were over their limit when the rule was added, and the guard tolerates only -those. It fails on an oversized file the baseline doesn't list, and on a listed -file that is now within its limit or no longer exists, so the list only -shrinks. When a split brings a file under its limit, remove its entry and lower -the rule's `baseline.count` in the same pull request. Never add an entry: split -the file instead. +## Enforcement + +The rule is enforced, with no baseline of tolerated files: the guard fails on +any measured file over its limit. When a change would take a file over, split +the file by responsibility, as above, in the same pull request. diff --git a/docs/generated/rules.md b/docs/generated/rules.md index 1d52745a2..b236119d5 100644 --- a/docs/generated/rules.md +++ b/docs/generated/rules.md @@ -8,7 +8,7 @@ Every rule that governs `policyengine-api` is listed here with how it is enforce | Tier | enforced | ratcheted | advisory | doc-only | Total | | --- | ---: | ---: | ---: | ---: | ---: | -| mechanical | 59 | 1 | 1 | 13 | 74 | +| mechanical | 60 | 0 | 1 | 13 | 74 | | metric | 0 | 0 | 0 | 0 | 0 | | judgment | 0 | 0 | 0 | 1 | 1 | @@ -66,7 +66,7 @@ Every rule that governs `policyengine-api` is listed here with how it is enforce ### Ratcheted (existing violations still tolerated) -- `api.quality.file-length`: 0 remaining in `rules/baselines/file-length.txt` +- None. ### Exceptions and review dates @@ -333,7 +333,7 @@ flowchart LR | `api.quality.command-line-modules-ruff-exemptions` | Application modules that also run as command-line programs (if __name__ == "__main__") may print their output (T201); every other ruff rule applies to them. | mechanical | enforced | yes | ruff per-file ignore of T201 for `policyengine_api/{data/v2/catalog/initialization,data/v2/household_migration_qualification,data/v2/policy_migration_qualification,data/v2/privilege_reconciliation,data/v2/user_association_migration_qualification,worker_spm_release}.py` | `pr.yml:lint` | — | | `api.quality.command-line-scripts-ruff-exemptions` | Command-line scripts under scripts/ may print their output, run other programs, fetch fixed URLs, stamp today's date and pick random samples (T201, S603, S607, S310, DTZ011, S311); every other ruff rule applies to them. | mechanical | enforced | yes | ruff per-file ignore of DTZ011, S310, S311, S603, S607, T201 for `scripts/**` | `pr.yml:lint` | — | | `api.quality.environment-variables-load` | Required environment variables and credentials load in a deployed-like environment. | mechanical | enforced | yes | CI job `test_env_vars` | `pr.yml:test_env_vars` | — | -| `api.quality.file-length` | A tracked Python file has at most 800 physical lines, or at most 1,500 if it is test code (a file under a tests or fixtures directory, or a conftest.py); a longer file is split by responsibility into modules, and keeps its public import path. | mechanical | ratcheted | yes | quality guard `file-length` | `pr.yml:quality-guards` | yes | +| `api.quality.file-length` | A tracked Python file has at most 800 physical lines, or at most 1,500 if it is test code (a file under a tests or fixtures directory, or a conftest.py); a longer file is split by responsibility into modules, and keeps its public import path. | mechanical | enforced | yes | quality guard `file-length` | `pr.yml:quality-guards` | yes | | `api.quality.mypy-v2` | Shared query models, FastAPI dependencies, v2 routes and services, and the user sign-in and account-deletion libraries pass mypy with untyped definitions disallowed. | mechanical | enforced | yes | mypy | `pr.yml:typecheck-v2` | — | | `api.quality.ruff-format` | All Python files are formatted with ruff format. | mechanical | enforced | yes | ruff format | `pr.yml:lint` | — | | `api.quality.ruff-lint` | All Python code passes ruff check with every ruff rule selected (ALL), with no ratchet, except the ignores in pyproject.toml: COM812 and ISC001, which conflict with ruff format, and D203 and D213, which conflict with the kept D211 and D212. | mechanical | enforced | yes | ruff check (ALL) | `pr.yml:lint` | — | @@ -356,7 +356,7 @@ flowchart LR r_api_quality_environment_variables_load("api.quality.environment-variables-load (enforced)") e_CI_job_test_env_vars["CI job test_env_vars"] j_CI_pr_yml_test_env_vars["CI pr.yml:test_env_vars"] - r_api_quality_file_length("api.quality.file-length (ratcheted)") + r_api_quality_file_length("api.quality.file-length (enforced)") e_quality_guard_file_length["quality guard file-length"] j_CI_pr_yml_quality_guards["CI pr.yml:quality-guards"] r_api_quality_mypy_v2("api.quality.mypy-v2 (enforced)") diff --git a/rules/baselines/file-length.txt b/rules/baselines/file-length.txt deleted file mode 100644 index 3eb243549..000000000 --- a/rules/baselines/file-length.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Python files tolerated over their api.quality.file-length limit (800 lines, -# or 1,500 for test code). This list may only shrink: split a file, then remove -# it here and lower the rule's baseline count in rules/registry.toml. -# -# Anthony Volk authorized this starting baseline on 2026-10-27. When it is -# empty, #4053 makes the rule enforced and deletes this file. diff --git a/rules/registry.toml b/rules/registry.toml index d2a50d7a6..e9470f729 100644 --- a/rules/registry.toml +++ b/rules/registry.toml @@ -1097,9 +1097,7 @@ statement = "A tracked Python file has at most 800 physical lines, or at most 1, rationale = "docs/engineering/skills/file-length.md" scope = ["**/*.py"] tier = "mechanical" -# Ratcheted with the files over their limit on 2026-10-27, as Anthony Volk -# decided that day; #4053 enforces the rule once the baseline is empty. -status = "ratcheted" +status = "enforced" autofix = false [rule.enforcer] kind = "quality-guard" @@ -1108,9 +1106,6 @@ name = "file-length" workflow = ".github/workflows/pr.yml" job = "quality-guards" invocation = "run_quality_guards.py" -[rule.baseline] -file = "rules/baselines/file-length.txt" -count = 0 [rule.proof] bad_fixture = "rules/fixtures/api.quality.file-length/bad_tree.toml" good_fixture = "rules/fixtures/api.quality.file-length/good_tree.toml" From 4963752d51c9a52a8f635675608482066de37fc3 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Tue, 27 Oct 2026 18:13:17 +0400 Subject: [PATCH 2/2] Register the file-length rule in the metric tier The rule fails when a file's line count crosses a threshold, which is the registry's metric tier (rules-registry.md: "A number crosses a threshold"), not the mechanical tier it was added in. Anthony decided on 2026-10-27 to move it. Its proof already tests both sides of the limit: a file exactly at its limit passes and one a line over fails. The proof now names those fixtures as below_fixture and above_fixture, with threshold = 800, the source-file limit; the same fixtures also cover the 1,500-line limit for test code. The fixtures themselves are unchanged. Co-Authored-By: Claude Opus 5.5 --- changelog.d/file-length-enforce.changed.md | 2 +- docs/generated/rules.md | 6 +++--- rules/registry.toml | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/changelog.d/file-length-enforce.changed.md b/changelog.d/file-length-enforce.changed.md index 0f3448406..5ab871fa5 100644 --- a/changelog.d/file-length-enforce.changed.md +++ b/changelog.d/file-length-enforce.changed.md @@ -1 +1 @@ -Enforced the file-length rule now that no Python file is over its limit, and deleted its empty baseline. +Enforced the file-length rule now that no Python file is over its limit, deleted its empty baseline, and registered it in the metric tier, the tier for a number crossing a threshold. diff --git a/docs/generated/rules.md b/docs/generated/rules.md index b236119d5..5acfc789d 100644 --- a/docs/generated/rules.md +++ b/docs/generated/rules.md @@ -8,8 +8,8 @@ Every rule that governs `policyengine-api` is listed here with how it is enforce | Tier | enforced | ratcheted | advisory | doc-only | Total | | --- | ---: | ---: | ---: | ---: | ---: | -| mechanical | 60 | 0 | 1 | 13 | 74 | -| metric | 0 | 0 | 0 | 0 | 0 | +| mechanical | 59 | 0 | 1 | 13 | 73 | +| metric | 1 | 0 | 0 | 0 | 1 | | judgment | 0 | 0 | 0 | 1 | 1 | **Blocking:** 60 of 75 rules. @@ -333,7 +333,7 @@ flowchart LR | `api.quality.command-line-modules-ruff-exemptions` | Application modules that also run as command-line programs (if __name__ == "__main__") may print their output (T201); every other ruff rule applies to them. | mechanical | enforced | yes | ruff per-file ignore of T201 for `policyengine_api/{data/v2/catalog/initialization,data/v2/household_migration_qualification,data/v2/policy_migration_qualification,data/v2/privilege_reconciliation,data/v2/user_association_migration_qualification,worker_spm_release}.py` | `pr.yml:lint` | — | | `api.quality.command-line-scripts-ruff-exemptions` | Command-line scripts under scripts/ may print their output, run other programs, fetch fixed URLs, stamp today's date and pick random samples (T201, S603, S607, S310, DTZ011, S311); every other ruff rule applies to them. | mechanical | enforced | yes | ruff per-file ignore of DTZ011, S310, S311, S603, S607, T201 for `scripts/**` | `pr.yml:lint` | — | | `api.quality.environment-variables-load` | Required environment variables and credentials load in a deployed-like environment. | mechanical | enforced | yes | CI job `test_env_vars` | `pr.yml:test_env_vars` | — | -| `api.quality.file-length` | A tracked Python file has at most 800 physical lines, or at most 1,500 if it is test code (a file under a tests or fixtures directory, or a conftest.py); a longer file is split by responsibility into modules, and keeps its public import path. | mechanical | enforced | yes | quality guard `file-length` | `pr.yml:quality-guards` | yes | +| `api.quality.file-length` | A tracked Python file has at most 800 physical lines, or at most 1,500 if it is test code (a file under a tests or fixtures directory, or a conftest.py); a longer file is split by responsibility into modules, and keeps its public import path. | metric | enforced | yes | quality guard `file-length` | `pr.yml:quality-guards` | yes | | `api.quality.mypy-v2` | Shared query models, FastAPI dependencies, v2 routes and services, and the user sign-in and account-deletion libraries pass mypy with untyped definitions disallowed. | mechanical | enforced | yes | mypy | `pr.yml:typecheck-v2` | — | | `api.quality.ruff-format` | All Python files are formatted with ruff format. | mechanical | enforced | yes | ruff format | `pr.yml:lint` | — | | `api.quality.ruff-lint` | All Python code passes ruff check with every ruff rule selected (ALL), with no ratchet, except the ignores in pyproject.toml: COM812 and ISC001, which conflict with ruff format, and D203 and D213, which conflict with the kept D211 and D212. | mechanical | enforced | yes | ruff check (ALL) | `pr.yml:lint` | — | diff --git a/rules/registry.toml b/rules/registry.toml index e9470f729..a10ccd0ba 100644 --- a/rules/registry.toml +++ b/rules/registry.toml @@ -1096,7 +1096,7 @@ category = "quality" statement = "A tracked Python file has at most 800 physical lines, or at most 1,500 if it is test code (a file under a tests or fixtures directory, or a conftest.py); a longer file is split by responsibility into modules, and keeps its public import path." rationale = "docs/engineering/skills/file-length.md" scope = ["**/*.py"] -tier = "mechanical" +tier = "metric" status = "enforced" autofix = false [rule.enforcer] @@ -1107,8 +1107,9 @@ workflow = ".github/workflows/pr.yml" job = "quality-guards" invocation = "run_quality_guards.py" [rule.proof] -bad_fixture = "rules/fixtures/api.quality.file-length/bad_tree.toml" -good_fixture = "rules/fixtures/api.quality.file-length/good_tree.toml" +below_fixture = "rules/fixtures/api.quality.file-length/good_tree.toml" +above_fixture = "rules/fixtures/api.quality.file-length/bad_tree.toml" +threshold = 800 test = "scripts/rules/tests/test_file_length.py::TestFileLengthProof::test_rule_proof_file_length" workflow = ".github/workflows/pr.yml" job = "quality-guards"