ci: test the Python services across a 3.10–3.14 matrix (closes #192)#195
Merged
Conversation
Production runs Python 3.10 but CI only ran 3.11. That gap let a 3.11-only `from datetime import UTC` reach main in services/log_ring.py and crash both Python services at import time on deploy (see #180); the symptom was addressed separately by #191. Run both pytest lanes across a ['3.10'..'3.14'] matrix (fail-fast: false) so a version-specific API fails at PR time on the 3.10 floor instead of on deploy. Exact == pins can't span the range: no single numpy ships both a cp310 and a cp314 wheel, and onnxruntime 1.27.0 has no cp310 wheel. So float only the blocking native deps to >= lower bounds and let pip resolve a per-interpreter wheel: - image-service: numpy>=2.0.0, onnxruntime>=1.23.2 - both services: pydantic>=2.12.5 (pinned pydantic-core lacks cp314) duckdb 1.4.4 already spans cp310-cp314 and stays pinned. This moves a fresh prod install onto numpy 2.x; rationale + trade-off in ADR-028. Also carry the identical UTC -> timezone.utc change from #191 into both log_ring.py copies, so the new 3.10 cells pass on this branch. Verified: pip --dry-run resolves on 3.10-3.14; both suites pass locally on numpy 2.4.6 (image-service 97, duckdb-service 232). Docs: ADR-028 + index, ci-gates rows, chapter-11 lessons entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTpzfM7M4q7gXzuwsxxhzp
Addresses senior-review findings on the version-matrix change: - P1: image-service detection tests `importorskip` onnxruntime/cv2, so a broken floated-wheel resolution on a cell would SKIP them and pass the lane green — false confidence for exactly the deps the matrix de-risks. Add tests/test_native_runtime.py, a non-skipping guard asserting hole_detection._RUNTIME_AVAILABLE is True, so a busted install turns the cell red. - P1: correct the "prod runs 3.10" premise. The repo specifies its runtime three ways (Dockerfiles build 3.12-slim, deploy docs say 3.11, the #180 host crashed on 3.10). Reframe 3.10 as a conservative floor proven by the incident, not a flat fact, across the workflow comment, ci-gates, ADR-028, and the chapter-11 lesson; flag the inconsistency as the deeper risk. - P2: pin image-service ruff target-version py312 -> py310 so the UP (pyupgrade) rule can't rewrite timezone.utc back to the 3.11-only UTC. - P2: note the #191 overlap in ADR-028 consequences. Verified: image-service 98 passed on numpy 2.4.6; ruff check clean at py310 (no UTC rewrite suggested); check-citations clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTpzfM7M4q7gXzuwsxxhzp
Doc-only precision fixes from the second senior-review pass; no behaviour change: - ruff comment no longer implies CI enforces the py310 floor (ruff is not run in any CI lane); reword to local `ruff --fix` / editor-on-save. - Reframe the runtime drift accurately: the container path is consistently 3.12-slim (dev + prod); the disagreement is deploy-docs prose (3.11) vs the bare-metal PM2 host that crashed (3.10) — not the Dockerfiles. - onnxruntime cp314 ceiling 1.24 -> 1.24.1 (no 1.24.0 on PyPI). - ADR-028: note duckdb-service is unexposed to the pyupgrade rewrite (no ruff config -> default rules exclude UP), and ground the numpy-2.x "low-risk" claim in the existing test_hole_detection.py inference tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTpzfM7M4q7gXzuwsxxhzp
Resolve the ADR-028 numbering collision: main merged adr-028 (ML inference is server-side only, f55a840) while this branch also added an adr-028 (Python version matrix). Renumber this branch's ADR to 029: - rename adr-028-python-version-matrix-floated-pins.md -> adr-029-... - retitle the file heading ADR-028 -> ADR-029 - index README lists both 028 (ML inference, from main) and 029 (matrix) - repoint every reference to the matrix ADR (tests.yml, ci-gates.md, chapter-11, both requirements.txt, pyproject.toml, test_native_runtime.py) from ADR-028 to ADR-029; leave main's ADR-028 (ML inference) refs in CLAUDE.md and the index untouched. No code-path change; main's log_ring.py still imports datetime.UTC, so this branch's timezone.utc fix remains required (references #191, #192).
This was referenced Jun 28, 2026
cofade
added a commit
that referenced
this pull request
Jun 29, 2026
…R-029) The PR's runbook rewrite asserted onnxruntime is "pinned to 1.23.2" under a "Python 3.10 ceiling". After folding in main (#195 / ADR-029), the real requirements float numpy>=2.0.0 / onnxruntime>=1.23.2 / pydantic>=2.12.5 for a 3.10-3.14 matrix — a floor, not a pin. Rewrote the step-2 comment and the "Python 3.10 floor" paragraph to match, citing ADR-029, and noted that a pip upgrade is not reverted on rollback. Added a ch11 lessons-learned entry for the workspace-lockfile npm-ci miss this PR corrects (per CLAUDE.md's mandatory doc gate). Addresses the senior-reviewer P0/P2 findings on the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cofade
added a commit
that referenced
this pull request
Jun 29, 2026
…closes #197) (#198) The repo named its Python runtime three disagreeing ways — container python:3.12-slim, deploy-docs prose 3.11, bare-metal PM2 host 3.10 — the documented root cause behind incident #180 (a 3.11-only datetime.UTC that crashed both Python services on deploy because CI/containers ran a different version than the host). The 3.10-3.14 CI matrix from #195 (ADR-029) mitigated the symptom but left the runtime-naming drift open; this closes it. Adopt 3.10 as the authoritative floor and make it enforced, not hand-kept: - Add /.python-version (=3.10) as the single source of truth. - Add scripts/check-python-version.sh: asserts both Dockerfile.dev FROM lines, the image-service ruff floor (py310), and each CI matrix's version-aware minimum all match the anchor. Wired into make check-python-version, the husky pre-push hook, and a new python-version-consistency CI job. - Pin both Python service images python:3.12-slim -> python:3.10-slim, so the container path mirrors the live 3.10 PM2 host (verified: both images build and import the native stack on 3.10 — onnxruntime resolves its 3.10 wheel, 1.23.2, exactly as ADR-029 predicts). - Reconcile every prose surface to 3.10 (constraints, building-block view, docker-compose doc, CLAUDE.md, CONTRIBUTING, runbook, production-deployment, PR/issue templates) and correct the now-ten CI job count across the docs. - Record the reconciliation as done in ADR-029 and the chapter-11 lesson; the historical "named three ways" narrative is left intact (the guard scopes to machine-readable surfaces, so a literal-version grep can't self-trip on it). The matrix stays 3.10-3.14: 3.10 is the floor we ship, not a cap on what the code must tolerate. Requirements pins are untouched — the floated bounds already resolve real wheels on 3.10. See ADR-029 for the floor rationale. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
cofade
added a commit
that referenced
this pull request
Jun 29, 2026
…R-029) The PR's runbook rewrite asserted onnxruntime is "pinned to 1.23.2" under a "Python 3.10 ceiling". After folding in main (#195 / ADR-029), the real requirements float numpy>=2.0.0 / onnxruntime>=1.23.2 / pydantic>=2.12.5 for a 3.10-3.14 matrix — a floor, not a pin. Rewrote the step-2 comment and the "Python 3.10 floor" paragraph to match, citing ADR-029, and noted that a pip upgrade is not reverted on rollback. Added a ch11 lessons-learned entry for the workspace-lockfile npm-ci miss this PR corrects (per CLAUDE.md's mandatory doc gate). Addresses the senior-reviewer P0/P2 findings on the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
What changed
duckdb-unit,image-unit) across Python['3.10','3.11','3.12','3.13','3.14']withfail-fast: false, in.github/workflows/tests.yml. Job count stays nine; each fans into five version cells.==pins are impossible here (no singlenumpyships both a cp310 and a cp314 wheel;onnxruntime==1.27.0has no cp310 wheel at all). So:numpy>=2.0.0,onnxruntime>=1.23.2(image-service) andpydantic>=2.12.5(both services, for a cp314pydantic-coreon 3.14). Everything else stays exact-pinned (duckdb==1.4.4already spans cp310–cp314;opencv-python-headlessis abi3). pip resolves the newest interpreter-compatible wheel per cell.image-service/tests/test_native_runtime.pyassertshole_detection._RUNTIME_AVAILABLE is True. The detection testsimportorskiponnxruntime/cv2, so without this a broken floated-wheel resolution would skip them and pass the lane green — false confidence for exactly the deps the matrix de-risks.datetime.UTC→timezone.utcfix in bothservices/log_ring.pycopies (identical to fix: Python 3.10 compat — timezone.utc + onnxruntime 1.23.2 (unbreaks prod) #191) so the new 3.10 cells pass instead of failing on the still-unmerged import fix.target-version→py310inimage-service/pyproject.tomlso theUP(pyupgrade) rule can't rewritetimezone.utcback to the 3.11-onlyUTCon a dev box.ADR-028(+ index row) recording the floated-pin pin-policy and the prod numpy 1.26→2.x consequence; updatedci-gates.mdrows; new chapter-11 lessons-learned entry.Why
References #192. CI tested the Python services on only 3.11, but the runtime is specified inconsistently across the repo (container path runs
3.12-slim; deploy-docs prose says 3.11; the bare-metal host that crashed ran 3.10). That single-version gap let a 3.11-onlyfrom datetime import UTCreachmaininlog_ring.pyand crash both Python services at import time on deploy (#180) — anImportErrorondatetime.UTC(added in 3.11) proves the runtime was < 3.11. The matrix spans a conservative 3.10 floor to a 3.14 forward ceiling so a version-specific API fails at PR time instead of on deploy. Full rationale inADR-028.Note on #191: this branch carries the identical
UTC→timezone.utcswap that #191 makes to the same two files. Whichever merges second is a no-op / trivial conflict on those lines — recommend merging #191 first (my copy becomes a rebase no-op) or closing #191 in favour of this branch.How tested
pio test -e native)pytest tests/e2e)Ran both Python suites in clean venvs: image-service 98 passed (resolving numpy 2.4.6 / pydantic 2.13.4 — confirms the numpy 1.26→2.x bump is safe), duckdb-service 232 passed. Verified pip resolution on all five versions via
pip install --dry-run --python-version <v> --only-binary=:all:(e.g. 3.10 → numpy 2.2.6 / onnxruntime 1.23.2; 3.14 → numpy 2.4.6 / onnxruntime 1.27.0).ruff checkclean atpy310(noUTCrewrite).make check-citationsclean. The matrix itself runs once this PR triggers CI.Checklist
main, so it executes for the first time on this PRADR-028)Screenshots / logs (optional)
N/A — CI-config and dependency change, no UI surface.
🤖 Generated with Claude Code
https://claude.ai/code/session_01WTpzfM7M4q7gXzuwsxxhzp
Generated by Claude Code