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
1 change: 1 addition & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- Ran Stage 12 US national simulations as 20 region-group calls each, and built an independent, immutable Stage 12 cache of three annual US datasets and 60 current-law baselines before each deployment, publishing it only after a recorded determinism check and recording its release in the v2 version manifest under a new key, while the existing key keeps a copy that earlier readers accept
- Made the budget refuse a household count outside its country's plausible range (above 200 million in the US or 60 million in the UK, negative or not finite), raising ImplausibleEntityCountError instead of publishing a count like #767's
changed:
- Enforced the file-length rule sim.quality.python-file-length, now that every tracked Python file is within its limit: its empty baseline and its downgrade section are gone, and its tier is metric rather than mechanical, because it measures a number against a threshold, with the at-the-limit and one-over fixtures as its below and above proof
- Moved the entrypoint's backend calls, response telemetry and comparison dispatch out of app.py (925 lines, now 583) into entry_runtime.py, which app.py re-exports and which still logs under the policyengine_simulation_entry.app logger; the routes stay in app.py, so FastAPI and ruff read their annotations as before; the 2,251-line deployment asset test file is split into seven test files and a support module named test_deployment_assets_support.py, the rules registry names each moved test by its new file, and its guard-test discovery now covers tests/test_deployment_assets*.py
- Moved the worker's credential setup, request year and policy parsing, and region resolution out of simulation_runtime.py (1,111 lines, now 651) into gcp_credentials.py, request_policy.py and region_resolution.py; simulation_runtime.py keeps dataset selection and loading, simulation building, local-area breakdowns and the run itself, which tests replace by name there, and re-exports the moved names, so every caller still imports them from simulation_runtime; the shared helpers _parse_year, _normalise_policy, _compile_interval_policy, _runtime_policy (now wrapper_policy), _resolve_region, _resolve_dataset_reference and _require_local_area_breakdown are now public, and the credential messages still log under the simulation_runtime logger; the 3,048-line output builder test file is split into seven test files and a module of shared fakes, all named test_simulation_output_*.py so the weighted-frame rule still checks them
- Split the artifact precompute (precompute.py, 931 lines) and the Stage 12 cache precompute (stage12_cache/precompute.py, 844 lines) each into planning, dataset-building and baseline modules, with the manifest and run_precompute left in the original module, which re-exports every public name, so the Modal precompute apps and their tests import from the same paths; the cache precompute's _scoped_planned_simulation and _baseline_identity are now public, and the worker validation imports scoped_planned_simulation from its new module; the 1,835-line precompute test file is split into four, and the cache precompute tests patch each name in the module whose code reads it
Expand Down
15 changes: 6 additions & 9 deletions docs/engineering/skills/file-length.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,14 @@ complexity, branch, statement and argument rules each measure one function, so
a module can keep growing while every function in it passes. A long module
usually holds several responsibilities, and is hard to review.

## The baseline
## No exceptions

While the rule is ratcheted, `rules/baselines/python-file-length.txt` lists the
files that were already over their limit when the rule was added. The guard
fails on a file over its limit that the baseline doesn't list, and on a listed
file that is now within its limit or no longer tracked. The pull request that
splits a listed file therefore removes its entry and lowers the rule's baseline
`count`.
The rule is enforced: every tracked Python file is within its limit, and there
is no baseline of tolerated files. A file that grows past its limit fails the
`quality-guards` job, so split it in the same pull request.

Never add an entry, or move a file out of the rule's scope: that weakens the
rule, which only a person may decide (see
Never add a baseline or an exception, or move a file out of the rule's scope:
that weakens the rule, which only a person may decide (see
[the registry's baselines](rules-registry.md#baselines)). Split the file
instead.

Expand Down
9 changes: 4 additions & 5 deletions docs/generated/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Every rule that governs `policyengine-sim-api` is listed here with how it is enf

| Tier | enforced | ratcheted | advisory | doc-only | Total |
| --- | ---: | ---: | ---: | ---: | ---: |
| mechanical | 53 | 3 | 0 | 14 | 70 |
| metric | 0 | 0 | 0 | 0 | 0 |
| mechanical | 53 | 2 | 0 | 14 | 69 |
| metric | 1 | 0 | 0 | 0 | 1 |
| judgment | 0 | 0 | 0 | 0 | 0 |

**Blocking:** 56 of 70 rules.
Expand Down Expand Up @@ -66,7 +66,6 @@ Every rule that governs `policyengine-sim-api` is listed here with how it is enf

- `sim.ci.multiline-run-is-script`: 13 remaining in `rules/baselines/ci-multiline-run.txt`
- `sim.microdata.weight-stripping-confined`: 0 remaining in `rules/baselines/sim-microdata-weight-stripping.txt`
- `sim.quality.python-file-length`: 0 remaining in `rules/baselines/python-file-length.txt`

### Exceptions and review dates

Expand Down Expand Up @@ -312,7 +311,7 @@ flowchart LR
| `sim.quality.pyright-executor` | The simulation executor has no Pyright errors, checked with the pinned Pyright version in the executor's own locked environment. | mechanical | enforced | yes | pyright in `projects/policyengine-simulation-executor` | `pr.yml:pyright` | yes |
| `sim.quality.pyright-gateway` | The simulation gateway has no Pyright errors, checked with the pinned Pyright version in the gateway's own locked environment. | mechanical | enforced | yes | pyright in `projects/policyengine-simulation-gateway` | `pr.yml:pyright` | yes |
| `sim.quality.pyright-libraries-and-entry` | The contract, observability, Stage 12 persistence and fastapi libraries and the simulation entry have no Pyright errors, checked with the pinned Pyright version in each package's own locked environment. | mechanical | enforced | yes | pyright in `libs/policyengine-simulation-contract`, `libs/policyengine-simulation-observability`, `libs/policyengine-stage12-persistence`, `libs/policyengine-fastapi`, `projects/policyengine-simulation-entry` | `pr.yml:pyright` | yes |
| `sim.quality.python-file-length` | No tracked Python file has more than 800 lines, or more than 1,500 lines if it is test code: a file under a tests or fixtures directory, or a conftest.py. | mechanical | ratcheted | yes | quality guard `file-length` | `pr.yml:quality-guards` | yes |
| `sim.quality.python-file-length` | No tracked Python file has more than 800 lines, or more than 1,500 lines if it is test code: a file under a tests or fixtures directory, or a conftest.py. | metric | enforced | yes | quality guard `file-length` | `pr.yml:quality-guards` | yes |
| `sim.quality.ruff-format-src` | Source code under projects/*/src and libs/*/src is formatted with ruff format. | mechanical | enforced | yes | ruff format | `pr.yml:lint` | — |
| `sim.quality.ruff-format-tests` | Test code is formatted with ruff format too; CI currently checks only src directories. | mechanical | doc-only | no | nothing | — | — |
| `sim.quality.ruff-lint` | All Python code passes ruff check with every ruff rule selected (ALL), with no ratchet, except the ignores in ruff.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` | — |
Expand Down Expand Up @@ -345,7 +344,7 @@ flowchart LR
e_pyright_in_projects_policyengine_simulation_gateway["pyright in projects/policyengine-simulation-gateway"]
r_sim_quality_pyright_libraries_and_entry("sim.quality.pyright-libraries-and-entry (enforced)")
e_pyright_in_libs_policyengine_simulation_contract__libs_policyengine_simulation_observability__libs_policyengine_stage12_persistence__libs_policyengine_fastapi__projects_policyengine_simulation_entry["pyright in libs/policyengine-simulation-contract, libs/policyengine-simulation-observability, libs/policyengine-stage12-persistence, libs/policyengine-fastapi, projects/policyengine-simulation-entry"]
r_sim_quality_python_file_length("sim.quality.python-file-length (ratcheted)")
r_sim_quality_python_file_length("sim.quality.python-file-length (enforced)")
e_quality_guard_file_length["quality guard file-length"]
r_sim_quality_ruff_format_src("sim.quality.ruff-format-src (enforced)")
e_ruff_format["ruff format"]
Expand Down
6 changes: 0 additions & 6 deletions rules/baselines/python-file-length.txt

This file was deleted.

17 changes: 7 additions & 10 deletions rules/registry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1178,8 +1178,8 @@ category = "quality"
statement = "No tracked Python file has more than 800 lines, or more than 1,500 lines if it is test code: a file under a tests or fixtures directory, or a conftest.py."
rationale = "docs/engineering/skills/file-length.md#the-rule"
scope = ["**/*.py"]
tier = "mechanical"
status = "ratcheted"
tier = "metric"
status = "enforced"
autofix = false
[rule.enforcer]
kind = "quality-guard"
Expand All @@ -1188,19 +1188,16 @@ name = "file-length"
workflow = ".github/workflows/pr.yml"
job = "quality-guards"
invocation = "run_quality_guards.py"
[rule.baseline]
file = "rules/baselines/python-file-length.txt"
count = 0
[rule.proof]
bad_fixture = "rules/fixtures/sim.quality.python-file-length/bad_files.toml"
good_fixture = "rules/fixtures/sim.quality.python-file-length/good_files.toml"
# Files at their limit (below) pass, and files one line over it (above) fail.
below_fixture = "rules/fixtures/sim.quality.python-file-length/good_files.toml"
above_fixture = "rules/fixtures/sim.quality.python-file-length/bad_files.toml"
# The source limit; both fixtures also cover the 1,500-line limit for test code.
threshold = 800
test = "scripts/rules/tests/test_file_length.py::TestFileLengthProof::test_rule_proof_python_file_length"
workflow = ".github/workflows/pr.yml"
job = "quality-guards"
invocation = "unittest discover -s scripts/rules/tests"
[rule.downgrade]
reason = "Authorized by Anthony (anth-volk) on 2026-10-27: the rule starts ratcheted with the 14 files that were over their limit on main when it was added, and the pull requests stacked on #920 split them and shrink the baseline to empty."
issue = 921

# --- Dependency locking -----------------------------------------------------

Expand Down
Loading