Summary
Some failures only surface on the Windows / non-default-encoding legs of the CI matrix and are fixed after merge, because the local validation commands and the lint/format/mypy CI legs all run on a single platform (ubuntu / Python 3.10). Add a lint guard for the most common cause — unqualified text I/O that relies on the platform default encoding — so these are caught locally and on the canonical lane.
Why this matters
The test matrix runs across ubuntu/windows/macos × Python 3.10–3.14 (15 combinations), but ruff check, ruff format --check, and mypy run only on ubuntu-latest / 3.10 (see .github/workflows/ci.yml lines 38–48). A contributor running the documented four commands on Linux/macOS gets a green local run, then discovers a Windows-only failure in CI. That is a slow, frustrating feedback loop and a recurring source of post-merge fix commits.
Evidence
test(evals): read the eval report with explicit UTF-8 (Windows CI fix) — an encoding bug that passed locally and on Linux CI but broke on Windows (where the default open() encoding is not UTF-8).
fix: make public_api_snapshot test version-aware for cross-Python CI — another "passed on the canonical lane, broke on another matrix leg" fix.
- These landed as follow-up commits rather than being caught pre-merge.
Proposed work
- Enable a ruff rule that flags
open() / Path.read_text / Path.write_text calls without an explicit encoding= (e.g. flake8-encodings-style / PLW1514 / W605-adjacent rules already available in ruff), scoped to chainweaver/ tests/ examples/.
- Fix any existing violations surfaced by the rule.
- Document in
CONTRIBUTING.md / AGENTS.md that text I/O must pass encoding="utf-8" explicitly, with a one-line rationale (Windows default is not UTF-8).
- Optional: run the lint/format/mypy leg on Windows as well, or add a small "encoding + cross-platform smoke" job, so the gating signal isn't single-platform.
Acceptance criteria
- A new or existing ruff rule rejects unqualified-encoding text I/O in the project source paths, and the rule runs as part of the existing blocking lint step.
- Existing violations are fixed; CI is green.
- The encoding expectation is documented next to the other code-style rules.
Notes
Summary
Some failures only surface on the Windows / non-default-encoding legs of the CI matrix and are fixed after merge, because the local validation commands and the lint/format/mypy CI legs all run on a single platform (ubuntu / Python 3.10). Add a lint guard for the most common cause — unqualified text I/O that relies on the platform default encoding — so these are caught locally and on the canonical lane.
Why this matters
The test matrix runs across
ubuntu/windows/macos × Python 3.10–3.14(15 combinations), butruff check,ruff format --check, andmypyrun only onubuntu-latest/3.10(see.github/workflows/ci.ymllines 38–48). A contributor running the documented four commands on Linux/macOS gets a green local run, then discovers a Windows-only failure in CI. That is a slow, frustrating feedback loop and a recurring source of post-merge fix commits.Evidence
test(evals): read the eval report with explicit UTF-8 (Windows CI fix)— an encoding bug that passed locally and on Linux CI but broke on Windows (where the defaultopen()encoding is not UTF-8).fix: make public_api_snapshot test version-aware for cross-Python CI— another "passed on the canonical lane, broke on another matrix leg" fix.Proposed work
open()/Path.read_text/Path.write_textcalls without an explicitencoding=(e.g. flake8-encodings-style /PLW1514/W605-adjacent rules already available in ruff), scoped tochainweaver/ tests/ examples/.CONTRIBUTING.md/AGENTS.mdthat text I/O must passencoding="utf-8"explicitly, with a one-line rationale (Windows default is not UTF-8).Acceptance criteria
Notes
chainweaver/ tests/ examples/) so local and CI behavior match (lessons-learned rule on "commands that don't match CI exactly").