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
23 changes: 22 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: [main]
branches: [main, dev]
tags: ["v*"]
pull_request:
branches: [main, dev]
Expand Down Expand Up @@ -96,6 +96,27 @@ jobs:
- name: Run fuzz session
run: uv tool run --from 'nox[uv]==2026.4.10' nox -f noxfile.py -s fuzz

interpreters:
name: CPython ${{ matrix.python-version }} compatibility
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12", "3.13", "3.14"]
env:
UV_PYTHON: ${{ matrix.python-version }}
RAES_EXPECTED_PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v8
- name: Test exact interpreter and clean distribution
run: uv tool run --from 'nox[uv]==2026.4.10' nox -f noxfile.py -s python-compatibility

# Opt-in, non-blocking, runtime-gated container integration tests (RUN-314).
# Kept out of the hermetic `verify` graph; the `docker` marker tests self-skip
# when no runtime is present, and this whole job never fails the build.
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/python-free-threaded-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Python free-threaded preview

on:
schedule:
- cron: "17 8 * * 1"
workflow_dispatch:

permissions:
contents: read

jobs:
python-314t:
name: CPython 3.14t preview only
runs-on: ubuntu-latest
timeout-minutes: 30
continue-on-error: true
env:
UV_PYTHON: 3.14t
RAES_EXPECTED_PYTHON: "3.14"
RAES_EXPECT_FREE_THREADED: "1"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14t
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v8
- name: Test preview interpreter and clean distribution
run: uv tool run --from 'nox[uv]==2026.4.10' nox -f noxfile.py -s python-compatibility
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ documentation more precise and easier to validate.

Prerequisites:

- Python 3.11 or newer
- standard CPython 3.11, 3.12, 3.13, or 3.14
- [uv](https://github.com/astral-sh/uv)
- [nox](https://nox.thea.codes/) or `uvx nox`

Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ backend.

## Validate your first scenario

You need Python 3.11 or newer. OCI module extraction additionally requires
Python 3.11.4 or newer because earlier 3.11 patch releases lack the mandatory
safe tar extraction filter. Those releases fail closed instead of extracting
without it.
You need standard CPython 3.11 through 3.14. Python 3.15 is not admitted until
its post-final dependency and compatibility qualification is complete. OCI
module extraction on Python 3.11 additionally requires Python 3.11.4 or newer.
Earlier patch releases lack the mandatory safe tar extraction filter and fail
closed instead of extracting without it.

```console
python -m venv .venv
Expand Down
23 changes: 16 additions & 7 deletions docs/decisions/adrs/adr-014-nox-as-canonical-verification-graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ a session with explicit substages, run sequentially through a
- `contracts` — generated-schema drift, JSON-artifact validation
- `tests` — pytest with coverage
- `fuzz` — pytest with `-m fuzz`
- `python-compatibility` — exact-interpreter tests plus clean distribution
build, installation, import, and CLI checks for one supported CPython release
- `docs` — sphinx-build (added by AUT-805)
- `verify` — composes hygiene + policy + lint + contracts + tests + docs
- `hook-pre-commit` — staged-file hygiene + policy + scoped lint +
Expand All @@ -65,10 +67,12 @@ a session with explicit substages, run sequentially through a
- `hook-pre-push` — full hygiene + policy + lint + contracts + tests +
fuzz

`verify` is the canonical "passed locally" state. CI invokes
`verify` is the canonical single-interpreter "passed locally" state. CI invokes
`nox -s verify`; the pre-push hook invokes `nox -s hook-pre-push`; the
pre-commit hook invokes `nox -s hook-pre-commit`. All three resolve
their work through the same per-gate helpers (`_run_hygiene`,
pre-commit hook invokes `nox -s hook-pre-commit`. CI additionally invokes
`python-compatibility` once for every supported CPython feature release because
a single local interpreter cannot establish the distribution's cross-version
support claim. All invocations resolve through the same per-gate helpers (`_run_hygiene`,
`_run_policy`, `_run_lint`, `_run_contracts`, `_run_tests`,
`_run_fuzz`, `_run_docs`).

Expand All @@ -91,10 +95,14 @@ when pre-commit and CI maintained their own command lists.
### 3. CI consumes the same graph

`.github/workflows/ci.yml` runs `uv tool run --from 'nox[uv]==…' nox
-s verify` for the blocking gate, plus `nox -s fuzz` as a separate job
(fuzz is excluded from `verify` because it is property-based and slow,
not because it is optional). SonarCloud consumes the coverage XML that
the `tests` substage produces; it is not a parallel test-runner.
-s verify` for the canonical single-interpreter gate, plus `nox -s fuzz` as a
separate job (fuzz is excluded from `verify` because it is property-based and
slow, not because it is optional). It also runs `nox -s python-compatibility`
as a required-check matrix over every standard CPython feature release named
by package metadata. Those jobs assert the selected runtime, use the frozen
dependency graph, run the hermetic suite, and verify a clean installed
distribution. SonarCloud consumes the coverage XML that the `tests` substage
produces; it is not a parallel test-runner.

The `.ground-control.yaml` workflow block declares each command nox
exposes:
Expand Down Expand Up @@ -216,3 +224,4 @@ local hooks, CI, and ground-control automation.
| Date | Commit/PR | Summary |
|---|---|---|
| 2026-07-31 | #963 | Replaced the serial `verify` composition with six isolated, CPU-budgeted concurrent nox lanes, primed shared policy tooling before cold-cache lanes, batched JSON artifacts by shared schema with bounded concurrency, combined unit and integration coverage deterministically, scoped pre-commit to staged changes and directly changed tests without duplicating the mandatory full pre-push/completion regression, and separated network-dependent external-link validation into dedicated docs CI. Ground Control's completion half omits policy because its mechanically enforced policy half runs immediately afterward; direct `verify` and CI retain policy. |
| 2026-08-14 | #1134 | Added a required CI matrix that runs the canonical `python-compatibility` session for each supported CPython feature release while retaining `verify` as the reproducible single-interpreter local gate. |
5 changes: 4 additions & 1 deletion docs/decisions/adrs/adr-index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,14 @@ adrs:
summary: "Superseded §6's no-SDL-episode-syntax non-goal for the joint episode+budget design (SEM-222/DSL-120/ACT-623, #122): DSL-120/ACT-623 add an authored episode-structure surface (init, turn structure, terminal/truncation conditions, reset policy) as intent compiling to the existing ParticipantEpisode* contracts; runtime identity, state/reason/action separation, and the schema-first boundary are unchanged."
- id: ADR-014
path: docs/decisions/adrs/adr-014-nox-as-canonical-verification-graph.md
pin: 52b3bf9dfbf128658b0edc88e164114529e3e225db0360ea99e005ebc97f86bf
pin: bd8a22d378a04d61eb57ea855b99848035e9aba563339dc22a078cbd99c40b63
amendments:
- date: 2026-07-31
ref: "#963"
summary: Parallelized deterministic verification and contract batches, scoped pre-commit without duplicating full regression, and moved external links to dedicated docs CI.
- date: 2026-08-14
ref: "#1134"
summary: Added a required cross-interpreter CI matrix while retaining verify as the reproducible single-interpreter local gate.
- id: ADR-015
path: docs/decisions/adrs/adr-015-sdl-processor-layering-and-source-file-size-cap.md
pin: bc5038164e535c4d506962e27cee07e720f5c8a77cb17d018dedb3dba927936f
Expand Down
79 changes: 79 additions & 0 deletions docs/decisions/issue-1097-python-314-support-preflight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Issue 1097 Python 3.14 Support Preflight

Date: 2026-08-11

Issue: #1097. Requirement: GOV-901.

## Decision

The `raes` distribution supports standard CPython 3.11, 3.12, 3.13, and
3.14. `Requires-Python` is therefore bounded to `>=3.11,<3.15`, and the Trove
classifiers list those four tested feature releases. The lower and upper
metadata bounds are install admission; the classifiers and blocking CI matrix
identify the versions for which this repository makes a tested support claim.

Python 3.15 is outside this change. It remains pre-final until 2026-10-01 and
must not receive a classifier, compatibility job, or package-support claim
before final. A later qualification issue must refresh the governed lock,
review native-wheel coverage on every claimed platform, run clean build/install
and integration gates, and then decide whether to raise the upper bound.

Free-threaded CPython 3.14 (`3.14t`) is an early-compatibility signal, not part
of the standard-interpreter guarantee. A nonblocking scheduled/manual lane may
install and test it, but must assert that the selected interpreter is actually
free-threaded and label the result preview-only. Failures there cannot be
described as a standard 3.14 regression without reproducing them on the normal
GIL build.

## Compatibility Gates

The blocking matrix must, for each standard feature release:

1. select the requested interpreter explicitly and assert `sys.version_info`;
2. perform a frozen all-extras sync rather than silently resolving another
dependency graph;
3. run the hermetic default unit suite;
4. build both wheel and source distribution;
5. install the wheel into a fresh environment with dependencies; and
6. smoke distribution metadata, public package imports, `raes --version`, and
`raes --help` from that clean environment.

The compatibility job is separate from canonical `verify`: proof replay,
policy, Sonar coverage, and contract generation remain single-version because
their result is not interpreter-specific. This avoids multiplying proof cost
while making every advertised interpreter blocking for code and packaging.

The job must not trust its label. `UV_PYTHON`, the runtime assertion, wheel
smoke interpreter, and log output all resolve to the same matrix value. This
prevents a `.python-version`, reused environment, or hard-coded smoke command
from turning the matrix into cosmetic coverage.

Nox removes an inherited `UV_PYTHON` selector from commands by default. The
compatibility session therefore copies the admitted selector into Nox's
per-session command environment before any nested `uv` call. A tooling
regression covers that handoff, while the exact-runtime assertion remains the
independent fail-closed check.

## Dependency And Platform Boundary

The frozen lock and current native dependency set must install on standard
CPython 3.14. A source build is not automatically a defect, but the build
prerequisites and platform claim must remain honest. The compatibility matrix
is initially the repository's existing Ubuntu execution contract; macOS local
verification supplements it but does not silently create a cross-platform
support promise. Backend-specific native integration remains governed by its
own runtime and host prerequisites.

The 3.14 test change replaces deprecated `asyncio.get_event_loop()` use with
`asyncio.run()` only at synchronous test boundaries. Product async APIs retain
their existing lifecycle and do not create nested event loops.

## Nonclaims

This issue does not:

- support or preview Python 3.15;
- claim production support for free-threaded builds;
- change SDL, contract, runtime, or backend semantics;
- make every optional native backend available on every operating system; or
- weaken canonical verification, coverage, policy, or release gates.
9 changes: 6 additions & 3 deletions docs/explain/reference/coding-standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,12 @@ that makes the semantic behavior clear and testable.

## Static Analysis Alignment

The Python implementation targets Python 3.11+ and uses Ruff with pyupgrade as
the local style contract. SonarCloud is advisory but must not contradict that
contract. Rules that require Python 2 compatibility idioms, Python 3.12-only
The Python implementation supports standard CPython 3.11 through 3.14 and uses
Ruff with pyupgrade as the local style contract. `Requires-Python` is bounded
below 3.15; the blocking CI matrix and classifiers define the tested standard
interpreter set. A scheduled/manual 3.14t lane is preview-only and does not
extend the production support claim. SonarCloud is advisory but must not
contradict that contract. Rules that require Python 2 compatibility idioms, Python 3.12-only
generic syntax, or mechanical docstrings on every private helper are disabled in
`sonar-project.properties` rather than encoded as source churn.

Expand Down
11 changes: 10 additions & 1 deletion docs/requirements/GOV-901/requirement.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type: NON_FUNCTIONAL
priority: MUST
wave: 2
created_at: 2026-04-03T07:16:00.634358Z
updated_at: 2026-06-14T02:41:58.145594Z
updated_at: 2026-08-12T00:00:00.000000Z
---

# GOV-901 — Versioning And Compatibility Rules
Expand Down Expand Up @@ -41,3 +41,12 @@ Requirement inventory expansion. Compatibility claims need explicit versioning a
- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1111-gov-901-cli-version-startup-preflight.md` (Measured startup boundary and compatibility invariants)
- TESTS → TEST `implementations/python/tests/test_issue_1111_cli_version_startup.py` (Source, fallback, delegation, and startup-budget guards)
- TESTS → TEST `implementations/python/tests/test_corpus_packaging.py` (Clean installed-wheel entry-point acceptance)
- IMPLEMENTS → GITHUB_ISSUE `1097` (Standard CPython 3.14 support and bounded 3.15 admission)
- IMPLEMENTS → GITHUB_ISSUE `1100` (Fail-closed Nox option-value validation)
- IMPLEMENTS → GITHUB_ISSUE `1117` (Python 3.14-safe MCP test event-loop boundaries)
- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1097-python-314-support-preflight.md` (Blocking interpreter matrix, clean distribution smoke, and free-threaded preview boundary)
- IMPLEMENTS → CONFIG `noxfile.py` (Exact interpreter selection, hermetic suite, build, install, and smoke graph)
- IMPLEMENTS → CONFIG `.github/workflows/ci.yml` (Blocking CPython 3.11 through 3.14 matrix)
- IMPLEMENTS → CONFIG `.github/workflows/python-free-threaded-preview.yml` (Nonblocking scheduled/manual 3.14t preview)
- TESTS → TEST `implementations/python/tests/test_repo_policy_tools.py` (Selector handoff and compatibility-graph regression tests)
- TESTS → TEST `implementations/python/tests/test_mcp_server.py` (Deprecation-free synchronous MCP test boundaries)
6 changes: 4 additions & 2 deletions implementations/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ build-backend = "hatchling.build"
name = "raes"
description = "Contracts and reference tooling for reproducible agentic environments."
dynamic = ["readme", "version"]
requires-python = ">=3.11"
requires-python = ">=3.11,<3.15"
# Trove classifiers are the only source PyPI and downstream badges read for
# supported interpreter versions; `requires-python` alone leaves them blank.
# Keep this list in step with `requires-python`.
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
dependencies = [
"typer>=0.12.0",
Expand All @@ -27,7 +29,7 @@ dependencies = [
"uvicorn[standard]>=0.34.0",
"sse-starlette>=2.0.0",
"asyncssh>=2.23.0",
"mcp>=1.28.1",
"mcp>=1.28.1,<2",
"packaging>=23.0",
"rfc8785>=0.1.4,<0.2",
"blake3>=1.0.8,<2",
Expand Down
4 changes: 2 additions & 2 deletions implementations/python/tests/test_mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def _text(result) -> str:

def _call(server, tool: str, args: dict | None = None) -> str:
"""Synchronously call a tool and return its text."""
return asyncio.get_event_loop().run_until_complete(_async_call(server, tool, args or {}))
return asyncio.run(_async_call(server, tool, args or {}))


async def _async_call(server, tool: str, args: dict) -> str:
Expand Down Expand Up @@ -897,7 +897,7 @@ def test_server_has_all_tools(self):
# Using the real registration surface (rather than a hand-copied
# literal) means a drift between what the server exposes and what
# raes_tool_surface advertises cannot pass silently.
registered = asyncio.get_event_loop().run_until_complete(server.list_tools())
registered = asyncio.run(server.list_tools())
registered_names = {tool.name for tool in registered}
assert registered_names, "server registered no tools"

Expand Down
38 changes: 38 additions & 0 deletions implementations/python/tests/test_public_project_readiness.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import json
import re
import tomllib
from pathlib import Path

import yaml
Expand Down Expand Up @@ -86,3 +87,40 @@ def test_publishers_build_only_the_curated_public_source() -> None:
makefile = (REPO_ROOT / "docs" / "Makefile").read_text(encoding="utf-8")
assert "SOURCEDIR = public" in makefile
assert "BUILDDIR = _build" in makefile


def test_python_support_metadata_and_blocking_matrix_are_aligned() -> None:
pyproject = tomllib.loads((REPO_ROOT / "implementations" / "python" / "pyproject.toml").read_text(encoding="utf-8"))
project = pyproject["project"]
supported = ["3.11", "3.12", "3.13", "3.14"]

assert project["requires-python"] == ">=3.11,<3.15"
assert {
classifier.removeprefix("Programming Language :: Python :: ")
for classifier in project["classifiers"]
if re.fullmatch(r"Programming Language :: Python :: 3\.\d+", classifier)
} == set(supported)

ci = yaml.safe_load((REPO_ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8"))
interpreter_job = ci["jobs"]["interpreters"]
assert interpreter_job["strategy"]["fail-fast"] is False
assert interpreter_job["strategy"]["matrix"]["python-version"] == supported
assert interpreter_job["env"] == {
"UV_PYTHON": "${{ matrix.python-version }}",
"RAES_EXPECTED_PYTHON": "${{ matrix.python-version }}",
}

preview = yaml.safe_load(
(REPO_ROOT / ".github" / "workflows" / "python-free-threaded-preview.yml").read_text(encoding="utf-8")
)
preview_job = preview["jobs"]["python-314t"]
assert preview_job["continue-on-error"] is True
assert preview_job["env"] == {
"UV_PYTHON": "3.14t",
"RAES_EXPECTED_PYTHON": "3.14",
"RAES_EXPECT_FREE_THREADED": "1",
}

noxfile = (REPO_ROOT / "noxfile.py").read_text(encoding="utf-8")
assert 'assert is_gil_enabled() is False, "interpreter is not free-threaded"' in noxfile
assert 'assert is_gil_enabled() is True, "standard lane selected a free-threaded interpreter"' in noxfile
Loading
Loading