Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
859415f
Import test modules with from-imports instead of aliases
anth-volk Oct 14, 2026
1bf2704
Group test helper parameters into composite fixtures and option objects
anth-volk Oct 14, 2026
4f97160
Pin contract and persistence validation behaviour before refactoring
anth-volk Oct 14, 2026
160b129
Split the comparison report record's state validation into report and…
anth-volk Oct 14, 2026
501b87b
Name the contract's URI, version and name-length limits
anth-volk Oct 14, 2026
a3d1b0c
Split the live column type check into enum and plain type checks
anth-volk Oct 14, 2026
f38b9c3
Keep the observability initializer's keyword-only identity arguments
anth-volk Oct 14, 2026
4a1d344
Pin the order and messages of entry settings validation
anth-volk Oct 14, 2026
3338bd5
Split entry settings validation into caller auth, old gateway and Sta…
anth-volk Oct 14, 2026
fa33a5d
Pin the Stage 12 adapter's complete report input and check order
anth-volk Oct 14, 2026
255c136
Split the Stage 12 annual-comparison adapter into eligibility checks …
anth-volk Oct 14, 2026
6adf4af
Pin the Stage 12 dispatch calls, acknowledged parent and skip event
anth-volk Oct 14, 2026
31e29a8
Group Stage 12 dispatch inputs into a prepared report, origin and wor…
anth-volk Oct 14, 2026
6783d51
Pin entry app telemetry, Stage 12 route failures and backend readiness
anth-volk Oct 14, 2026
0917e9e
Split the entry app factory into a shared runtime and route registrat…
anth-volk Oct 14, 2026
6cbf1b2
Compare the old gateway's health status with HTTPStatus.OK
anth-volk Oct 14, 2026
d23ce57
Pin gateway route resolution from the active routing state
anth-volk Oct 14, 2026
4dd8648
Split gateway active-state route resolution into pinned, latest and r…
anth-volk Oct 14, 2026
1f62102
Pass the resolved region to _build_simulation instead of its code and…
anth-volk Oct 14, 2026
e249f0d
Pass artifact key inputs to the dataset and baseline key builders as …
anth-volk Oct 14, 2026
36c2bb3
Pass baseline identity facts as one request and split out the artifac…
anth-volk Oct 14, 2026
78637d3
Pass the precompute's Modal function handles to run_precompute as one…
anth-volk Oct 14, 2026
6fdcbc0
Pin every input the national reduce passes to the output builder
anth-volk Oct 14, 2026
2797939
Pass the national reduce's run facts to build_national_output as one …
anth-volk Oct 14, 2026
455d12f
Pass fan-out poll timing to the batch and segmented runners as one ba…
anth-volk Oct 14, 2026
774df5d
Pin the Stage 12 bundle schema version check
anth-volk Oct 14, 2026
933c051
Name the supported Stage 12 bundle schema version
anth-volk Oct 14, 2026
baaaaba
Pin every Stage 12 parity mismatch code, path and comparison order
anth-volk Oct 14, 2026
e7673bf
Split Stage 12 parity comparisons into entity, column and report-valu…
anth-volk Oct 14, 2026
a95c3e0
Pin parity qualification receipts, check order and default runner lif…
anth-volk Oct 14, 2026
f291c28
Split parity qualification into existing-run, provenance, calculation…
anth-volk Oct 14, 2026
d07a904
Pin Stage 12 coordination validation, claims, failures, stages and ag…
anth-volk Oct 14, 2026
690cc7d
Pass each role's retained frames and descriptor to the aggregate buil…
anth-volk Oct 14, 2026
81b14b4
Split Stage 12 report coordination into claim, plan, dispatch, wait a…
anth-volk Oct 19, 2026
73fb56a
Pin how the Modal child invoker looks up, spawns and restores children
anth-volk Oct 14, 2026
8d24e4f
Pass the child invoker one spawn request instead of six keywords
anth-volk Oct 14, 2026
723bac7
Pin result comparison logs, spans and the failed state-update path
anth-volk Oct 14, 2026
dbd076e
Pass the result comparison its coordinator services as one group
anth-volk Oct 14, 2026
7eb8467
Pin the Stage 12 worker's reuse path and calculator timing
anth-volk Oct 14, 2026
cafe7d0
Pass the single-simulation worker its seams as one dependencies group
anth-volk Oct 14, 2026
5656d86
Pin Stage 12 worker validation results, order and failures
anth-volk Oct 14, 2026
26baf26
Pass Stage 12 worker validation its environment and checks as one group
anth-volk Oct 14, 2026
791512b
Pin routing state validation messages and check order
anth-volk Oct 14, 2026
c9bc5ca
Group a deployed app's versions in the routing publisher and split it…
anth-volk Oct 14, 2026
696586a
Pin the artifact fetch layer's receipt, credential and download edge …
anth-volk Oct 14, 2026
bb3c79a
Split the image artifact fetch into freshness, credential and downloa…
anth-volk Oct 14, 2026
0c24074
Pin the release sentinel's polling, retry statuses and problem order
anth-volk Oct 14, 2026
91074de
Split the release sentinel's deployment check into submit, poll and c…
anth-volk Oct 14, 2026
1c48044
Pin the routing publisher CLI's mode order, outputs and argument errors
anth-volk Oct 14, 2026
cbca984
Split the routing publisher CLI into one command per mode
anth-volk Oct 14, 2026
872c2fd
Pin the package update helper's bundle-versions and verify-dispatch o…
anth-volk Oct 14, 2026
56551ce
Split the package update helper's bundle and dispatch checks into hel…
anth-volk Oct 14, 2026
9d774e4
Pin the Stage 12 connection retry's delay sequence and budget cut
anth-volk Oct 14, 2026
232ac85
Pass the Stage 12 connection retry its clock, sleep and jitter as one…
anth-volk Oct 14, 2026
e75b13d
Pin the workflow expression evaluator and routing command discovery
anth-volk Oct 14, 2026
f3df4dd
Split the workflow expression evaluator into a tokenizer and a parser
anth-volk Oct 14, 2026
148853f
Split routing command discovery into script, array and per-command ch…
anth-volk Oct 14, 2026
c3b40b7
Group the routing and package-update test parameters into cases
anth-volk Oct 14, 2026
748864b
Take the Public API's group 8b rules tool refactor
anth-volk Oct 14, 2026
1fbddba
Select every ruff rule (ALL)
anth-volk Oct 14, 2026
9cf48e9
Pin the poll, deadline and budget timing the segmented runner #795 ex…
anth-volk Oct 15, 2026
382f6f1
Pass the segmented runner the poll and deadline timing #795 added as …
anth-volk Oct 15, 2026
aef062b
Set the fake child call's cancel failures #795 added after construction
anth-volk Oct 15, 2026
d71004a
Pin how the tracked_stage #791 added scopes and records each stage
anth-volk Oct 15, 2026
aebab35
Pass the tracked_stage #791 added its stage plan, progress and runtim…
anth-volk Oct 15, 2026
aad75cc
Compare the word count in the redaction helper #791 added without a m…
anth-volk Oct 15, 2026
5f3d835
Parametrize the permission tests #758 added by one route case
anth-volk Oct 15, 2026
13750ab
Pin the runtime and job progress the segmented runner #781 reports to
anth-volk Oct 15, 2026
4b4b1ce
Pass the segmented runner its runtime and the job progress #781 added…
anth-volk Oct 15, 2026
8fd0200
Name the numbers #712's segmentation and cache code compares against
anth-volk Oct 19, 2026
758ffd5
Observe the children the #712 coordinator waits for as a coordination…
anth-volk Oct 19, 2026
7fe7193
Pass the failure reports #712 added where each failure was caught as …
anth-volk Oct 19, 2026
c1460ba
Pass the segment runner #712 added its lookup and polling seams as on…
anth-volk Oct 19, 2026
a411d8b
Pass the Stage 12 simulation builder #712 added its resolved inputs a…
anth-volk Oct 19, 2026
963d452
Pass the cache precompute #712 added its Modal function handles as on…
anth-volk Oct 19, 2026
a17e7c7
Pass the scripted child call #712's tests added its outcome as one sc…
anth-volk Oct 19, 2026
b5f8f8c
Pin which simulations the Stage 12 cache identity #712 added qualifies
anth-volk Oct 19, 2026
fbd4c80
Split the Stage 12 cache eligibility #712 added into baseline, region…
anth-volk Oct 19, 2026
ec7cd27
Pin the frame comparison and probe checks of the determinism check #7…
anth-volk Oct 19, 2026
eeeaebd
Compare the determinism check's frames #712 added in their own function
anth-volk Oct 19, 2026
c97865e
Pin every check of the segment merge #712 added
anth-volk Oct 19, 2026
df237d7
Split the segment merge #712 added into indexing, decoding and entity…
anth-volk Oct 19, 2026
94dcb42
Pin the claim and cancellation-check paths of the worker #712 extended
anth-volk Oct 19, 2026
562401e
Split the Stage 12 worker #712 extended into claim, calculate and rec…
anth-volk Oct 19, 2026
84313cc
Pin every check of the image-build cache fetch #712 added
anth-volk Oct 19, 2026
09e5c39
Split the image-build cache fetch #712 added into manifest, client an…
anth-volk Oct 19, 2026
9a5f100
Drop init_simulation_observability's never-passed service_version
anth-volk Oct 20, 2026
a30e925
Pass create_initial_batch_state its route as one BatchRoute
anth-volk Oct 20, 2026
e31d044
Pass resolve_spm_selection the route's versions as one SPMRouteVersions
anth-volk Oct 20, 2026
50b52c5
Call Stage 12 runtime functions through v2_app's module-level import
anth-volk Oct 20, 2026
1edd612
Import the image setup module's standard-library modules at module level
anth-volk Oct 20, 2026
567836d
List google.oauth2 and policyengine_uk as lazily loaded packages
anth-volk Oct 20, 2026
4330d43
Update the changelog entry for the removed PLR0913 suppressions
anth-volk Oct 20, 2026
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
203 changes: 122 additions & 81 deletions .github/scripts/update_policyengine_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import tempfile
from collections.abc import Mapping
from pathlib import Path
from typing import Any
from typing import Any, NamedTuple

import tomllib

Expand Down Expand Up @@ -201,6 +201,56 @@ def latest_version(package: str) -> None:
_write_result(version)


def _package_version(packages: Mapping[str, Any], name: str) -> str:
version = _mapping(packages.get(name)).get("version")
if not isinstance(version, str) or not version:
msg = f"Bundle has no version for {name}"
raise SystemExit(msg)
return version


def _data_release_version(release: Mapping[str, Any], country: str) -> str:
version = release.get("version") or _mapping(release.get("data_package")).get(
"version"
)
if not isinstance(version, str) or not version:
msg = f"Bundle has no data release version for {country}"
raise SystemExit(msg)
return version


def _data_package_version(release: Mapping[str, Any]) -> str:
version = _mapping(release.get("data_package")).get("version")
return version if isinstance(version, str) else ""


def _dataset_sha256(release: Mapping[str, Any], country: str) -> str:
"""Return the default dataset's digest, preferring the certified artifact's."""
default = release.get("default_dataset")
certified = _mapping(release.get("certified_data_artifact"))
if certified.get("dataset") == default and certified.get("sha256"):
value = certified["sha256"]
else:
reference = _mapping(_mapping(release.get("datasets")).get(default))
value = reference.get("sha256") or ""
digest = str(value).strip().lower()
if digest and not re.fullmatch(r"[0-9a-f]{64}", digest):
msg = f"Bundle records a malformed {country} dataset SHA-256"
raise SystemExit(msg)
return digest


def _certificate_sha256(release: object) -> str:
# Receipt-v1 certificate digest; older wrappers lack the helper.
try:
from policyengine.provenance.receipt import (
certificate_sha256 as digest,
)
except ImportError:
return ""
return digest(release) if isinstance(release, Mapping) else ""


def bundle_versions() -> None:
"""Print the package and data versions selected by PolicyEngine.py."""
from policyengine.bundle import (
Expand All @@ -210,67 +260,20 @@ def bundle_versions() -> None:
bundle = _mapping(get_current_bundle())
packages = _mapping(bundle.get("packages"))
data_releases = _mapping(bundle.get("data_releases"))

def package_version(name: str) -> str:
version = _mapping(packages.get(name)).get("version")
if not isinstance(version, str) or not version:
msg = f"Bundle has no version for {name}"
raise SystemExit(msg)
return version

def data_release_version(country: str) -> str:
release = _mapping(data_releases.get(country))
version = release.get("version") or _mapping(release.get("data_package")).get(
"version"
)
if not isinstance(version, str) or not version:
msg = f"Bundle has no data release version for {country}"
raise SystemExit(msg)
return version

def data_package_version(country: str) -> str:
release = _mapping(data_releases.get(country))
version = _mapping(release.get("data_package")).get("version")
return version if isinstance(version, str) else ""

def dataset_sha256(country: str) -> str:
release = _mapping(data_releases.get(country))
default = release.get("default_dataset")
certified = _mapping(release.get("certified_data_artifact"))
if certified.get("dataset") == default and certified.get("sha256"):
value = certified["sha256"]
else:
reference = _mapping(_mapping(release.get("datasets")).get(default))
value = reference.get("sha256") or ""
digest = str(value).strip().lower()
if digest and not re.fullmatch(r"[0-9a-f]{64}", digest):
msg = f"Bundle records a malformed {country} dataset SHA-256"
raise SystemExit(msg)
return digest

def certificate_sha256(country: str) -> str:
# Receipt-v1 certificate digest; older wrappers lack the helper.
try:
from policyengine.provenance.receipt import (
certificate_sha256 as digest,
)
except ImportError:
return ""
release = data_releases.get(country)
return digest(release) if isinstance(release, Mapping) else ""

us_release = _mapping(data_releases.get("us"))
uk_release = _mapping(data_releases.get("uk"))
outputs = {
"policyengine_version": package_version("policyengine"),
"policyengine_core_version": package_version("policyengine-core"),
"spm_calculator_version": package_version("spm-calculator"),
"us_version": package_version("policyengine-us"),
"us_data_version": data_release_version("us"),
"uk_version": package_version("policyengine-uk"),
"uk_data_version": data_release_version("uk"),
"uk_data_package_version": data_package_version("uk"),
"us_dataset_sha256": dataset_sha256("us"),
"uk_dataset_sha256": dataset_sha256("uk"),
"uk_certificate_sha256": certificate_sha256("uk"),
"policyengine_version": _package_version(packages, "policyengine"),
"policyengine_core_version": _package_version(packages, "policyengine-core"),
"spm_calculator_version": _package_version(packages, "spm-calculator"),
"us_version": _package_version(packages, "policyengine-us"),
"us_data_version": _data_release_version(us_release, "us"),
"uk_version": _package_version(packages, "policyengine-uk"),
"uk_data_version": _data_release_version(uk_release, "uk"),
"uk_data_package_version": _data_package_version(uk_release),
"us_dataset_sha256": _dataset_sha256(us_release, "us"),
"uk_dataset_sha256": _dataset_sha256(uk_release, "uk"),
"uk_certificate_sha256": _certificate_sha256(data_releases.get("uk")),
}
for name, version in outputs.items():
_write_result(f"{name}={version}")
Expand All @@ -293,25 +296,30 @@ def certificate_sha256(country: str) -> str:
PROMOTABLE_VERDICTS = frozenset({"qualified", "needs_review"})


def verify_dispatch() -> None:
"""Check a policyengine-release payload against the installed bundle.
class _DispatchComparison(NamedTuple):
"""What comparing a dispatch with the installed bundle found.

Reads the dispatch ``client_payload`` JSON from ``RELEASE_PAYLOAD`` and
``bundle-versions`` output from stdin. A payload without ``bundle.uk``
(older releases, manual runs) is accepted. A field the payload names
must equal the installed bundle's, and an installed bundle that cannot
report it is a mismatch, except for the certificate digest, which older
wrappers cannot compute. A qualification that says the pair was not
promotable is refused. ``dispatch_verified`` is true only when at least
one field was compared. Prints digests for the PR body.
``mismatches`` lists the fields that disagree; ``compared`` counts the
fields both the dispatch and the installed bundle stated.
"""
raw = os.environ.get("RELEASE_PAYLOAD", "").strip()
payload = json.loads(raw) if raw and raw != "null" else {}

mismatches: list[str]
compared: int


def _installed_fields(bundle_output: str) -> dict[str, str]:
"""Read ``bundle-versions`` output: one ``key=value`` line per field."""
installed = {}
for line in sys.stdin.read().splitlines():
for line in bundle_output.splitlines():
key, _, value = line.partition("=")
if key:
installed[key.strip()] = value.strip()
return installed


def _require_dispatched_wrapper(
payload: Mapping[str, Any], installed: Mapping[str, str]
) -> None:
if payload.get("version") and payload["version"] != installed.get(
"policyengine_version"
):
Expand All @@ -320,7 +328,12 @@ def verify_dispatch() -> None:
f"{installed.get('policyengine_version')} is installed."
)
raise SystemExit(msg)
expected = _mapping(_mapping(payload.get("bundle")).get("uk"))


def _compare_dispatch_bundle(
expected: Mapping[str, Any], installed: Mapping[str, str]
) -> _DispatchComparison:
"""Compare each field the dispatch names with the installed bundle's value."""
mismatches = []
compared = 0
for payload_key, installed_key in DISPATCH_CHECKS:
Expand All @@ -341,11 +354,11 @@ def verify_dispatch() -> None:
claimed_text = claimed_text.lower()
if claimed_text != actual:
mismatches.append(f"{payload_key}: dispatch {claimed}, installed {actual}")
if mismatches:
raise SystemExit(
"The release dispatch names a different UK bundle from the installed "
"wheel: " + "; ".join(mismatches)
)
return _DispatchComparison(mismatches, compared)


def _promotable_qualification(payload: Mapping[str, Any]) -> tuple[str, str]:
"""Return the UK verdict and receipt digest, refusing malformed or rejected ones."""
qualification = _mapping(_mapping(payload.get("qualification")).get("uk"))
verdict = qualification.get("verdict") or ""
receipt_sha256 = qualification.get("receipt_sha256") or ""
Expand All @@ -362,7 +375,35 @@ def verify_dispatch() -> None:
):
msg = f"The release dispatch says the UK pair was not promotable ({verdict})"
raise SystemExit(msg)
_write_result(f"dispatch_verified={'true' if compared else 'false'}")
return verdict, receipt_sha256


def verify_dispatch() -> None:
"""Check a policyengine-release payload against the installed bundle.

Reads the dispatch ``client_payload`` JSON from ``RELEASE_PAYLOAD`` and
``bundle-versions`` output from stdin. A payload without ``bundle.uk``
(older releases, manual runs) is accepted. A field the payload names
must equal the installed bundle's, and an installed bundle that cannot
report it is a mismatch, except for the certificate digest, which older
wrappers cannot compute. A qualification that says the pair was not
promotable is refused. ``dispatch_verified`` is true only when at least
one field was compared. Prints digests for the PR body.
"""
raw = os.environ.get("RELEASE_PAYLOAD", "").strip()
payload = json.loads(raw) if raw and raw != "null" else {}
installed = _installed_fields(sys.stdin.read())
_require_dispatched_wrapper(payload, installed)
comparison = _compare_dispatch_bundle(
_mapping(_mapping(payload.get("bundle")).get("uk")), installed
)
if comparison.mismatches:
raise SystemExit(
"The release dispatch names a different UK bundle from the installed "
"wheel: " + "; ".join(comparison.mismatches)
)
verdict, receipt_sha256 = _promotable_qualification(payload)
_write_result(f"dispatch_verified={'true' if comparison.compared else 'false'}")
_write_result(f"uk_qualification_verdict={verdict}")
_write_result(f"uk_qualification_receipt_sha256={receipt_sha256}")

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ To add or change a rule, read `docs/engineering/skills/rules-registry.md`.
- `sim.quality.every-project-tested`: Every project and library with unit tests runs them in the PR test matrix. (doc-only, not checked: follow it yourself)
- `sim.quality.ruff-format-src`: Source code under projects/*/src and libs/*/src is formatted with ruff format. (blocking: ruff format)
- `sim.quality.ruff-format-tests`: Test code is formatted with ruff format too; CI currently checks only src directories. (doc-only, not checked: follow it yourself)
- `sim.quality.ruff-lint`: All Python code passes ruff check with every rule family selected in ruff.toml, with no ratchet; families are added one themed group at a time until the selection is ALL. (blocking: ruff check (A, ANN, ARG, ASYNC, B, BLE, C4, D, DTZ, E, E4, E7, E9, EM, ERA, EXE, F, FAST, FBT, FIX, FLY, FURB, G, I, INP, LOG, N, PD, PERF, PGH, PIE, PLC, PLW, PT, PTH, PYI, RET, RSE, RUF, S, SIM, SLF, T20, TC, TD, TID253, TRY, UP, W))
- `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. (blocking: ruff check (ALL))
- `sim.quality.runtime-evaluated-models-resolve`: Every Pydantic model is runtime-evaluated for ruff: one of its bases, read through the imports of the model's own module, is listed in ruff.toml's runtime-evaluated-base-classes or is a runtime-evaluated class in that same module; and every Pydantic model resolves its annotations once its package is imported. (blocking: pytest `projects/policyengine-simulation-entry/tests/test_runtime_evaluated_annotations.py`, `projects/policyengine-simulation-executor/tests/test_runtime_evaluated_annotations.py`)
- `sim.quality.src-programs-ruff-exemptions`: Programs under src (the Modal deployment utilities, and the OpenAPI and Stage 12 infrastructure commands) may print their output (T201), open URLs from their arguments (S310) and, for the Modal utilities run with python -m, live in a directory that isn't a package (INP001); every other ruff rule applies to them. (blocking: ruff per-file ignore of INP001, S310, T201 for `projects/*/src/{modal/utils/*,policyengine_simulation_entry/generate_openapi,policyengine_simulation_entry/stage12_infrastructure,policyengine_simulation_gateway/generate_openapi}.py`)
- `sim.quality.tests-pass`: Each project's unit tests pass in its own locked environment. (blocking: CI job `test`)
Expand Down
1 change: 1 addition & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Added a rules registry (rules/registry.toml) with a standard-library tool that validates it, checks every rule is enforced where it claims, and renders docs/generated/rules.md and the Rules block in AGENTS.md
- 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
changed:
- Enabled every ruff rule (select ALL), ignoring only COM812 and ISC001, which conflict with ruff format, and the D203 and D213 halves of the mutually exclusive docstring pairs; complex functions, long parameter lists and magic numbers were split into smaller helpers and grouped parameters, each behind new characterization tests, across the contract and persistence validation, entry settings, app factory and Stage 12 adapter, gateway route resolution, Stage 12 coordination, workers, parity and qualification, the precompute and national reduce, the routing publisher and its command line, the release sentinel, the package update helper and the shared rules tool; Modal function, FastAPI route, Pydantic validator and command-line signatures and every OpenAPI document are unchanged; the contract library's batch-state builder and SPM selection resolver take their route values as one grouped argument, the simulation observability initializer drops a service_version parameter no caller passed, and no PLR0913 suppression is needed
- Enabled ruff's argument, idiom and pytest-style rules (ARG, ASYNC, B, C4, FBT, PD, PERF, PGH, PLW, PT, PTH, RUF, SIM); unused arguments are removed, underscored or explained, FastAPI auth dependencies use Annotated, re-raised errors keep their cause, and boolean options of in-repo helpers are keyword-only; OpenAPI documents are unchanged, and the shared rules tool's unittest tests are exempt from pytest-style rules; tests take the unused-argument rules (ARG001, ARG002, ARG005) as file-level exceptions, since their fakes and stubs mirror real signatures; best-effort telemetry and cleanup calls that swallow any error use contextlib.suppress, with the reason as a comment
- Enabled ruff's structure and import rules (INP, PLC, SLF, N, A, FAST, E); long lines are wrapped, deferred imports that Modal images, test seams, import cycles or heavy dependencies need carry a reason, script directories are declared to ruff as namespace packages, four internal exceptions gained an Error suffix, and published field names, OpenAPI documents and logged error types are unchanged; tests are also exempt from SLF001, INP001 and PLC0415; heavy or optional packages that load only inside functions are listed in ruff's banned-module-level-imports, which keeps them out of module-level imports (TID253) and lets their deferred imports go without a suppression, and the Modal utility programs take INP001 file-wide
- Enabled ruff's security, logging and debugging rules (S, T20, LOG, G, DTZ, ERA, TD, FIX, EXE); asserts outside tests became explicit checks, command-line scripts keep their printed output, test subprocess calls go through helpers, and every deliberate exception carries a reasoned suppression; the gateway's auth misconfiguration log no longer repeats its message, and the deployed-service tests share the unit-test ruff exemptions; tests, CI/CD scripts and command-line scripts take their deliberate exceptions as registered file-level ruff ignores instead of inline suppressions, and the rules guard rejects file-level ignores anywhere else
Expand Down
11 changes: 7 additions & 4 deletions docs/engineering/skills/rules-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,13 @@ fail the guard yet.

## Baselines

- **Ruff is never ratcheted.** Adopting a ruff rule family is one standalone PR
that changes the select list and fixes every resulting violation. Don't use
`--add-noqa`. A finding that is kept gets an inline `# noqa: CODE # reason`,
except in the files below.
- **Ruff is never ratcheted.** `ruff.toml` selects `ALL`, so every ruff rule
applies except the four listed in `ignore` (two formatter conflicts and one
of each mutually exclusive docstring pair) and the registered
[file-level exceptions](#file-level-exceptions). When a ruff upgrade adds
rules, the upgrade PR fixes every new violation. Don't use `--add-noqa`.
Outside the files that allow file-level exceptions, a justified exception is
an inline `# noqa: CODE # reason`.
- Other enforcers (mypy, Pyright, Import Linter, custom guards) may start
`ratcheted` with a baseline file of tolerated violations. Baselines may only
shrink: the guard compares counts against the base branch.
Expand Down
Loading
Loading