Skip to content

Commit bbdfa5c

Browse files
authored
ci: shard ordinary Python checks by semantic lane (#1304)
* fix(topology): preserve non-maximal facet diagnostics * test(external-sat): remove stale producer conclusions * ci: shard ordinary Python checks by semantic lane
1 parent 73c6d17 commit bbdfa5c

13 files changed

Lines changed: 88 additions & 25 deletions

File tree

.github/workflows/ci.yml

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,33 @@ jobs:
4949
path: dist/
5050

5151
python:
52-
name: python
52+
name: python (${{ matrix.lane }})
5353
runs-on: ubuntu-latest
54-
timeout-minutes: 40
54+
timeout-minutes: ${{ matrix.timeout-minutes }}
55+
strategy:
56+
fail-fast: false
57+
matrix:
58+
include:
59+
- lane: unit
60+
timeout-minutes: 10
61+
- lane: component
62+
timeout-minutes: 15
63+
- lane: domain
64+
timeout-minutes: 15
65+
- lane: composition
66+
timeout-minutes: 25
67+
- lane: e2e
68+
timeout-minutes: 15
69+
- lane: provider
70+
timeout-minutes: 25
5571
steps:
5672
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
5773
with:
5874
persist-credentials: false
5975
- uses: ./.github/actions/setup-python-tests
6076
with:
6177
python-version: "3.12"
62-
- run: make test-ordinary
78+
- run: make test-${{ matrix.lane }}
6379
env:
6480
PYTEST_ARGS: >-
6581
--junitxml=pytest.xml
@@ -69,17 +85,17 @@ jobs:
6985
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
7086
if: ${{ !cancelled() }}
7187
with:
72-
name: junit-python-3.12
88+
name: junit-python-${{ matrix.lane }}-3.12
7389
path: pytest.xml
7490
if-no-files-found: ignore
7591
retention-days: 7
7692
- if: ${{ !cancelled() }}
77-
run: mv .coverage .coverage.python
93+
run: mv .coverage .coverage.python-${{ matrix.lane }}
7894
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
7995
if: ${{ !cancelled() }}
8096
with:
81-
name: coverage-data-python
82-
path: .coverage.python
97+
name: coverage-data-python-${{ matrix.lane }}
98+
path: .coverage.python-${{ matrix.lane }}
8399
include-hidden-files: true
84100

85101
boundaries:

CONTRIBUTING.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ make check
2525

2626
Then open a pull request. `make setup` installs the locked development
2727
environment with the complete maintained Python backend stack. `make check`
28-
runs Ruff, mypy, and the same Lean-free ordinary pytest invocation CI uses.
28+
runs Ruff, mypy, and the same Lean-free ordinary test suite CI covers in fixed
29+
semantic groups.
2930
Open the PR once it is green, and add any explicitly relevant specialist
3031
validation called out below.
3132

Makefile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ ORDERING_DEFAULT_SEED := --randomly-seed=17
99
PYTEST_DIAGNOSTIC_ARGS ?= --durations=10
1010
RUFF_PATHS := src tests benchmarks
1111
PYTEST_RUNNER := $(UV_RUN) python tools/pytest_lifecycle.py
12-
# Ordinary pytest used by `make check` and the CI python job.
13-
ORDINARY_PYTEST_FLAGS := -n 4 --dist worksteal --timeout=180
12+
# Fixed semantic lanes covering the Lean-free ordinary testpaths. CI runs these
13+
# independently; `make check` runs the same lanes locally in this order.
14+
ORDINARY_TEST_LANES := unit component domain composition e2e provider
1415
PUBLIC_COMMANDS := setup quick check check-external fix
1516

1617
include make/development.mk
@@ -81,9 +82,10 @@ test-e2e: ## Complete caller-visible journeys (serial, 180s).
8182
$(if $(TESTS),$(TESTS),tests/e2e) \
8283
$(PYTEST_DIAGNOSTIC_ARGS) $(PYTEST_ARGS)
8384

84-
test-ordinary: ## Lean-free ordinary pytest (same flags as CI python).
85-
$(UV_RUN) pytest $(ORDINARY_PYTEST_FLAGS) \
86-
$(PYTEST_DIAGNOSTIC_ARGS) $(PYTEST_ARGS)
85+
test-ordinary: ## Lean-free ordinary suite in the fixed CI group order.
86+
@for lane in $(ORDINARY_TEST_LANES); do \
87+
$(MAKE) test-$$lane || exit $$?; \
88+
done
8789

8890
test-compatibility: ## Supported-version import/API compatibility smoke.
8991
$(UV_RUN) pytest -n 0 --timeout=30 --timeout-method=thread \

docs/reference/testing-strategy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ Make targets that own storage, process, MCP, and Lean isolation. `make
3131
check-external` covers Lean and maintained-provider probes when those trees
3232
change.
3333

34+
Hosted CI runs that same ordinary suite as six fixed semantic lanes: `unit`,
35+
`component`, `domain`, `composition`, `e2e`, and `provider`. These are static
36+
Make targets, not path-selected or timing-planned shards; `make check` runs all
37+
six in the same order.
38+
3439
## Test ownership
3540

3641
The filesystem is the metadata. A test under `tests/domain/` is a domain test;

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies = [
2323
"mcp-types==2.0.0",
2424
"networkx==3.6.1",
2525
"pydantic>=2.12,<3",
26+
"pydantic-core>=2.41,<3",
2627
"python-flint==0.9.0",
2728
"rfc8785==0.1.4",
2829
"sympy==1.14.0",

src/jacobian/contracts/topology.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
field_validator,
1616
model_validator,
1717
)
18+
from pydantic_core import PydanticCustomError
1819

1920
from jacobian.canonical import canonicalize_json
2021
from jacobian.contracts.certified_snf import (
@@ -109,7 +110,10 @@ def _require_request_complex(
109110
)
110111
for left, right in combinations(canonical, 2):
111112
if set(left) < set(right) or set(right) < set(left):
112-
raise ValueError("facet input must contain only maximal simplices")
113+
raise PydanticCustomError(
114+
"topology_non_maximal_facets",
115+
"facet input must contain only maximal simplices",
116+
)
113117
closure = face_closure(tuple(canonical))
114118
if sum(map(len, closure)) > MAX_TOPOLOGY_FACES:
115119
raise ValueError(

tests/boundary/providers/external_sat/startup/test_carcara.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def test_zero_hole_qf_uf_proof_is_independently_verified(
8484
produced = _produce(carcara_services, "QF_UF", "qf_uf_equality_unsat.smt2")
8585

8686
assert produced.output["contains_holes"] is False
87-
assert produced.output["conclusion"] == "UNKNOWN"
87+
assert "conclusion" not in produced.output
8888
verified = _verify(carcara_services, produced.output["proof_uri"])
8989

9090
assert verified.execution.status is ExecutionStatus.COMPLETED

tests/boundary/providers/external_sat/test_cadical_external_backend.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def test_pinned_cadical_produces_a_model_and_text_drat_proof(
4949
)
5050
assert model.execution.status is ExecutionStatus.COMPLETED
5151
assert model.output["status"] == "ASSIGNMENT_PRODUCED"
52-
assert model.output["conclusion"] == "UNKNOWN"
52+
assert "conclusion" not in model.output
5353

5454
unsatisfiable = runtime.core.sat.put_cnf(
5555
variable_names=("x",),
@@ -66,4 +66,4 @@ def test_pinned_cadical_produces_a_model_and_text_drat_proof(
6666
)
6767
assert proof.execution.status is ExecutionStatus.COMPLETED
6868
assert proof.output["status"] == "PROOF_PRODUCED"
69-
assert proof.output["conclusion"] == "UNKNOWN"
69+
assert "conclusion" not in proof.output

tests/boundary/providers/external_sat/test_sat_public_reproductions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def test_sat_public_reproductions_reach_checker_bound_results(
8282
)
8383
)
8484
assert found.execution.status is ExecutionStatus.COMPLETED
85-
assert found.output["conclusion"] == "UNKNOWN"
85+
assert "conclusion" not in found.output
8686
evidence_uri = found.output[evidence_field]
8787
assert evidence_uri is not None
8888
if case["expected_status"] == "SATISFIABLE":

tests/domain/topology/test_topology_capabilities.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,5 +481,5 @@ def test_enriched_diagnostic_still_fails_closed_for_non_digest_error(
481481

482482
assert result.execution.status is ExecutionStatus.ERROR
483483
assert result.diagnostics[0].code == "INVALID_FINITE_SIMPLICIAL_TOPOLOGY_REQUEST"
484-
assert "maximal" in (result.diagnostics[0].hint or "")
484+
assert "topology_non_maximal_facets" in (result.diagnostics[0].hint or "")
485485
assert result.artifact_uris == ()

0 commit comments

Comments
 (0)