Supported OS / Python Compatibility Matrix
Source of truth. This document explains which operating systems and Python versions the
repository supports. config/python-support.json is its machine-readable counterpart and the two
are checked together. The policy was added by
issue #333.
The rule of thumb is “supported = CI-enforced.” A combination is supported only if the project’s CI actively exercises it on a runner. Combinations that are declared but not CI-tested are called best-effort and are kept deliberately distinct so no one reads best-effort as equivalent to supported.
Runtime promise
pyproject.toml declares:
requires-python = ">=3.10,<3.14"
Python 3.10 is the root-project floor and 3.13 is the ceiling. The upper bound is intentional: it makes
the promise closed rather than open-ended, so a future 3.14 cannot silently ship as “supported”
without the CI matrix being extended first. A consequence of the <3.14 bound is that uv sync
on a 3.14 interpreter will reject the project unless you pass --no-install-project (the pattern CI
already uses), so local development on a newer interpreter stays possible while the supported
runtime remains bounded.
Root and standalone boundaries
config/python-support.json is the machine-readable boundary. The shared root project and its
uv.lock support Python 3.10 through 3.13. Packaged skills remain source-compatible with Python
3.9 when their own requirements.txt permits it. Python 3.9 clean-room jobs must not sync the root
lock or install the root project; they validate only standalone artifacts. Ruff therefore retains a
py39 syntax target across skills/ and shared standalone scripts.
Supported (CI-enforced)
Actively tested in CI:
| OS | Python | Where |
|---|---|---|
Linux (ubuntu-latest) |
3.10 | test / coverage / workflow-replay jobs |
Linux (ubuntu-latest) |
3.9 | standalone market-calendar-compat clean room |
Linux (ubuntu-latest) |
3.11 | existing lint / metadata / security / supply-chain jobs |
Linux (ubuntu-latest) |
3.13 | compat-smoke (PR) |
Windows (windows-latest) |
3.13 | compat-smoke (PR) |
Windows (windows-latest) |
3.10 | compat-nightly (daily) |
macOS (macos-latest) |
3.13 | compat-smoke (PR) |
macOS (macos-latest) |
3.10 | compat-nightly (daily) |
The macOS runner uses the macos-latest label. actions/setup-python provisions the requested
Python version independently of the base runner image, so the 3.10 leg is available on that label.
Best-effort (NOT CI-enforced)
Any combination whose Python version satisfies >=3.10,<3.14 on
ubuntu-latest / windows-latest / macos-latest, except the CI-enforced rows above.
Defined as the set-complement of the supported rows, so the two tiers never overlap. Nothing in this tier is claimed to “just work” — it is simply not ruled out at the package level.
Policy
Every job in every workflow under .github/workflows/ must satisfy the following (validated
statically by scripts/check_compat_matrix.py):
- Its OS is in
{ubuntu-latest, windows-latest, macos-latest}. - Root-project jobs use Python in
[3.10, 3.14). Explicit standalone clean-room jobs may use the separately declared 3.9 floor.
The drift guard globs the whole workflow directory rather than a hard-coded allowlist, so a
brand-new workflow that runs a job on an unsupported OS / out-of-range Python is caught and not
silently skipped. dependency-review is a third-party-action job with no setup-python step, so it
is OS-checked only; single-runner jobs without a matrix axis are only OS-checked because no Python
version can be pinned. Compatibility-defining jobs (compat-smoke, compat-nightly) are also
checked against the documented axis mapping below.
Job → (os, python) mapping
The compatibility-defining jobs are declared here and must match the actual workflow YAML:
compat_matrix:
python: ">=3.10,<3.14"
supported_os: [ubuntu-latest, windows-latest, macos-latest]
jobs:
compat-smoke:
os: [ubuntu-latest, windows-latest, macos-latest]
python: ["3.13"]
compat-nightly:
os: [windows-latest, macos-latest]
python: ["3.10"]
scripts/check_compat_matrix.py parses this block and compares it to the axis sets it infers from
ci.yml / compat-nightly.yml. Workflow environments are not (and cannot be) exercised by the
static checker.
Enforcement scope
The issue title asks to define and enforce the matrix. Enforcement here is static and has two parts:
- A drift guard (
scripts/check_compat_matrix.py, run in themetadataCI job and as a pre-commit hook) that fails a change if the documented matrix diverges from the workflow YAML, or if any workflow job leaves the supported OS set / Python range. - Declaring only supported combinations in the workflow itself.
A runtime rejection of “this unsupported combination reached a runner” is out of scope for a static checker: a static tool cannot observe the live runner. This is a deliberate, documented reduction so the gap between “enforce” and the current implementation is explicit.
Why this matrix
Two past failure classes motivated bounding the promise and testing cross-platform:
- #64 — a Windows default-encoding Markdown failure that would not have surfaced if the suite had run on a Windows runner.
- #311 — a standalone dependency gap discovered by running the risk/state/navigator skills
outside the main project venv. The
compat-smoke/compat-nightlyjobs therefore run an extra isolated packaged-dependency import step: each core skill’s declaredrequirements.txtis installed into a throwaway environment (uv run --isolated --no-project --with <reqs>) and the skill’s top-level imports are exercised there, per OS and per Python version. This catches a missing dependency declaration that the shared dev environment would otherwise mask via packages installed transitively.
The cross-platform jobs (compat-smoke, compat-nightly) exercise the compatibility-sensitive
suites — position-sizer, futures-position-sizer, trader-memory-core,
drawdown-circuit-breaker, trading-skills-navigator — plus the UTF-8 / path / line-ending /
temp-dir / subprocess-quoting regressions in scripts/tests/test_compat_regressions.py, so these
classes no longer ship silently.
Verification record
Local verification on a macOS (aarch64) host, using uv sync --locked:
- Python 3.10: resolves the locked environment successfully after the security-exception retirement.
- Python 3.13: resolves the locked environment successfully (
scipy==1.17.1,statsmodels==0.14.6). - Result: the
>=3.10,<3.14range is viable for--extra dev --extra cion this machine.
Windows and macOS runner outcomes are validated by the PR’s GitHub Actions; they cannot be reproduced locally from a macOS host.