Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
170 commits
Select commit Hold shift + click to select a range
f5bd2ba
test: certify CallContext promotion chain shares one bundle
OmarAlJarrah Jul 13, 2026
0e6cb15
chore: enforce namespace, licence-header, and py.typed packaging inva…
OmarAlJarrah Jul 13, 2026
a65d5bb
fix: bound the logging policy's request-body preview to 8 KiB
OmarAlJarrah Jul 13, 2026
6eeaa36
fix(pipeline): guard singleton stages and make staged-builder edits a…
OmarAlJarrah Jul 13, 2026
6229647
feat(serde): add TypeRef witnesses with eager validation
OmarAlJarrah Jul 13, 2026
54b3ccf
feat: add requirement-ID traceability markers and audit tooling
OmarAlJarrah Jul 13, 2026
762600e
feat: add AST-normalized sync/async parity gate
OmarAlJarrah Jul 13, 2026
b4d3dc7
chore: simplify policies path constant in parity-check test
OmarAlJarrah Jul 13, 2026
c8b3860
feat(http): add outbound-strict Headers path and harden name folding
OmarAlJarrah Jul 13, 2026
b4b7740
test: add hypothesis property suites for HTTP parsers and codecs
OmarAlJarrah Jul 13, 2026
72ccdb6
feat(config): add property seam, raw accessor, derivation, and build …
OmarAlJarrah Jul 13, 2026
e12a31d
fix: give each call a unique ContextStore key to prevent context loss…
OmarAlJarrah Jul 13, 2026
f21e86b
feat(instrumentation): add structured event-builder API to ClientLogger
OmarAlJarrah Jul 13, 2026
85a0713
test: add golden wire-exactness byte corpus for HTTP codecs
OmarAlJarrah Jul 13, 2026
2853934
feat(auth): single-flight token cache and three-zone async bearer ref…
OmarAlJarrah Jul 13, 2026
b29054d
fix: harden shared URL redactor and route all emitters through it
OmarAlJarrah Jul 13, 2026
c1c546b
fix: make Status lookup total over all integer codes
OmarAlJarrah Jul 13, 2026
231b2f1
chore: hold MediaType's normalized parts behind read-only accessors
OmarAlJarrah Jul 13, 2026
c547f41
test: expand conformance batteries for the HTTP value types
OmarAlJarrah Jul 13, 2026
70fa01a
merge: PY-M0-01 packaging/namespace/header guardrails
OmarAlJarrah Jul 13, 2026
abc037f
merge: PY-M0-03 golden wire-exactness byte corpus
OmarAlJarrah Jul 13, 2026
664543a
merge: PY-M0-04 sync/async parity gate
OmarAlJarrah Jul 13, 2026
56ac170
merge: PY-M0-05 requirement-ID traceability skeleton
OmarAlJarrah Jul 13, 2026
5d902fa
merge: PY-M1-01 fix ContextStore key for concurrent same-trace calls …
OmarAlJarrah Jul 13, 2026
a0b7295
merge: PY-M1-03 StagedPipelineBuilder occupancy/order/atomicity guards
OmarAlJarrah Jul 13, 2026
9a78865
merge: PY-M1-04 bound logging policy preview to 8 KiB
OmarAlJarrah Jul 13, 2026
e5bad72
merge: PY-M2-01 Headers conformance battery (outbound-strict path)
OmarAlJarrah Jul 13, 2026
9735db0
merge: PY-M2-03 MediaType/Status/ETag/HttpRange/Conditions batteries
OmarAlJarrah Jul 13, 2026
f0656e0
merge: PY-M3-01 certify CallContext promotion chain
OmarAlJarrah Jul 13, 2026
eeb6057
merge: PY-M4-05 TokenCache single-flight + async three-zone bearer re…
OmarAlJarrah Jul 13, 2026
f1d65d7
merge: PY-M5-03 type witnesses + TypeRef eager rejection
OmarAlJarrah Jul 13, 2026
5ab9e3e
merge: PY-M7-01 client_logger structured event-builder + emit-once latch
OmarAlJarrah Jul 13, 2026
5774084
merge: PY-M7-02 certify shared url_redactor battery
OmarAlJarrah Jul 13, 2026
24a42b3
merge: PY-M7-05 Configuration layered lookup, accessors, clock seam
OmarAlJarrah Jul 13, 2026
fdea729
merge: PY-M10-03 hypothesis property suites for existing parsers/codecs
OmarAlJarrah Jul 13, 2026
18475e3
fix: make single-use body consumption race-safe with a lock-protected…
OmarAlJarrah Jul 13, 2026
4fcbc81
chore: enforce architecture boundaries with import-linter and depende…
OmarAlJarrah Jul 13, 2026
f18d00a
fix: reconcile wave-1 merge fallout across MediaType, Status, and tra…
OmarAlJarrah Jul 13, 2026
70e719f
merge: PY-M0-02 import-linter contracts (retry)
OmarAlJarrah Jul 13, 2026
550b050
merge: PY-M2-05 single-use body semantics with lock-protected once-gu…
OmarAlJarrah Jul 13, 2026
1e7567b
test: add golden wire-exactness byte corpus for HTTP codecs
OmarAlJarrah Jul 13, 2026
8dfd849
feat: propagate replayability through multipart bodies via shared fra…
OmarAlJarrah Jul 13, 2026
210d497
fix: harden SSE retry-field parsing against non-ASCII digits and over…
OmarAlJarrah Jul 13, 2026
f211109
test: certify the SSE parser against the golden byte corpus
OmarAlJarrah Jul 13, 2026
dba2515
test: cover pipeline fork/re-drive and response lifecycle on both dri…
OmarAlJarrah Jul 13, 2026
2a53071
fix(auth): enforce HTTPS and reject blank secrets across credential p…
OmarAlJarrah Jul 13, 2026
68efcb9
test: prove ordering-sensitive semantics of auxiliary pipeline policies
OmarAlJarrah Jul 13, 2026
88aa03d
test: pin the public API contract and add an advisory pyright lane
OmarAlJarrah Jul 13, 2026
bdced3b
fix: don't follow redirects by default on the async pipeline
OmarAlJarrah Jul 13, 2026
452322e
feat(context): bound ContextStore and add identity-conditional removal
OmarAlJarrah Jul 13, 2026
bea7065
ci: enforce coverage, warnings, and dependency-budget gates
OmarAlJarrah Jul 13, 2026
b50bcbe
fix(http): render and parse URL queries with a pure RFC 3986 codec
OmarAlJarrah Jul 13, 2026
0a569ee
feat(serde): strict JSON binding and a rooted serde failure taxonomy
OmarAlJarrah Jul 13, 2026
187e126
feat(auth): gate 401 challenge replay on body replayability
OmarAlJarrah Jul 13, 2026
233a726
feat: harden proxy model credential safety and env resolution
OmarAlJarrah Jul 13, 2026
93fe443
feat(instrumentation): finish tracing/metrics SPIs, W3C id generators…
OmarAlJarrah Jul 13, 2026
e53e3ae
feat(http): validate exchange models and add a memoizing response parse
OmarAlJarrah Jul 13, 2026
92d0610
feat(retry): extract shared pure resilience modules (classifier, back…
OmarAlJarrah Jul 14, 2026
07624be
merge: PY-M4-01 shared pure resilience modules (classifier, backoff, …
OmarAlJarrah Jul 14, 2026
344297c
merge: PY-M2-02 pure RFC 3986 query codec, furl sealed for wire-exact…
OmarAlJarrah Jul 14, 2026
7c20342
merge: PY-M10-05 enforce coverage, warnings, and dependency-budget gates
OmarAlJarrah Jul 14, 2026
a580a3d
merge: PY-M3-02 harden ContextStore (bounded drain, identity eviction…
OmarAlJarrah Jul 14, 2026
659cec9
merge: PY-M1-02 gate async redirect twin behind the full REDIR battery
OmarAlJarrah Jul 14, 2026
7e8a710
merge: PY-M2-06 multipart/form bodies with shared framing
OmarAlJarrah Jul 14, 2026
fb0094c
merge: PY-M2-04 Request/Response hardening (validation, parse latch, …
OmarAlJarrah Jul 14, 2026
1df16a8
merge: PY-M2-04 Request/Response hardening (validation, parse latch, …
OmarAlJarrah Jul 14, 2026
b560e1a
merge: PY-M3-03 pipeline fork/re-drive + PIPE-40 lifecycle battery
OmarAlJarrah Jul 14, 2026
294a8d2
merge: PY-M5-04 strict JSON binder and rooted Serde failure taxonomy
OmarAlJarrah Jul 14, 2026
123fece
merge: PY-M6-04 certify SSE parser grammar over raw bytes
OmarAlJarrah Jul 14, 2026
09c5435
merge: PY-M7-03 tracing/metrics SPIs, W3C ids, contextvars bridge
OmarAlJarrah Jul 14, 2026
8ea92ed
feat: two-regime drain for LoggableResponseBody
OmarAlJarrah Jul 14, 2026
f216f4d
fix: isolate genuine per-call leaks from one-time allocator overhead …
OmarAlJarrah Jul 14, 2026
854cef1
merge: PY-M2-07 LoggableResponseBody two-regime drain + request tap
OmarAlJarrah Jul 14, 2026
72fc21b
merge: PY-M7-06 proxy model with compiled globs and opt-in env resolu…
OmarAlJarrah Jul 14, 2026
da265f2
merge: PY-M7-07 certify idempotency/set-date/client-identity ordering…
OmarAlJarrah Jul 14, 2026
980d0b5
merge: PY-M10-04 API snapshots, typing conformance, advisory pyright …
OmarAlJarrah Jul 14, 2026
262ae30
merge: PY-M4-06 gate 401 challenge replay identically on both pillars
OmarAlJarrah Jul 14, 2026
e0e5f26
merge: PY-M4-06 gate 401 challenge replay identically on both pillars
OmarAlJarrah Jul 14, 2026
63b732e
merge: PY-M4-08 enforce credential hygiene across every auth policy
OmarAlJarrah Jul 14, 2026
c5d4a75
docs: record standing deviations in the deviations ledger
OmarAlJarrah Jul 14, 2026
bbca3ba
feat(serde): reject Present(None) and certify Tristate wiring
OmarAlJarrah Jul 14, 2026
7c75b68
test: certify sync redirect policy against the full redirect battery
OmarAlJarrah Jul 14, 2026
085783b
test: certify default pipeline order, options threading, and ownershi…
OmarAlJarrah Jul 14, 2026
12ba9f4
feat(codegen): add declarative Operation model and assemble()
OmarAlJarrah Jul 14, 2026
a62f563
feat(auth): parse multi-line challenges and harden Digest qop negotia…
OmarAlJarrah Jul 14, 2026
875be25
feat: complete retry pacing parser with strict decimal screen and clamp
OmarAlJarrah Jul 14, 2026
d1ffe99
feat(streaming): release the underlying body when jsonl/chunked-frame…
OmarAlJarrah Jul 14, 2026
b69443f
feat(errors): add NetworkError(OSError) branch and bounded replayable…
OmarAlJarrah Jul 14, 2026
ae5df84
feat(pipeline): add recovery fold with Success|Failure outcome and ca…
OmarAlJarrah Jul 14, 2026
4aff9f1
feat(pipeline): add async wire logging and per-attempt tracing policies
OmarAlJarrah Jul 14, 2026
9297cdb
fix(sse): make connection close race-free and single-pass
OmarAlJarrah Jul 14, 2026
5e7203c
feat(sse): add typed adapter over SSE event streams
OmarAlJarrah Jul 14, 2026
6b587cd
merge: PY-M1-05 extend deviations ledger with 5 more entries
OmarAlJarrah Jul 14, 2026
85ae13f
merge: PY-M9-01 Operation/OperationInput declaration layer and assemb…
OmarAlJarrah Jul 14, 2026
a6db6c0
merge: PY-M5-01 finalize error taxonomy (NetworkError/OSError, bounde…
OmarAlJarrah Jul 14, 2026
43c9fde
merge: PY-M5-05 ship Tristate — reject Present(None), certify wiring
OmarAlJarrah Jul 14, 2026
2bbd305
merge: PY-M3-04 recovery chain (Outcome sum, CancelledError immunity)
OmarAlJarrah Jul 14, 2026
bbbb6bc
merge: PY-M4-02 total pacing parser with strict decimal screen
OmarAlJarrah Jul 14, 2026
5a386f3
merge: PY-M6-06 harden jsonl/chunked-frame decoders + webhook regress…
OmarAlJarrah Jul 14, 2026
481a874
merge: PY-M6-05 SSE connection facade + typed adapter
OmarAlJarrah Jul 14, 2026
2418559
merge: PY-M4-07 Digest RFC 7616 + ChallengeHandler family
OmarAlJarrah Jul 14, 2026
324820f
merge: PY-M4-04 certify sync redirect policy against the full REDIR b…
OmarAlJarrah Jul 14, 2026
81b844c
docs: clarify the redirect-battery gaps are shared-core, not async-only
OmarAlJarrah Jul 14, 2026
f6a8687
merge: PY-M3-05 certify default pipeline order, options threading, ow…
OmarAlJarrah Jul 14, 2026
a684028
merge: PY-M7-04 add async logging policy + per-attempt tracing twin
OmarAlJarrah Jul 14, 2026
c0d177e
test: cover configurable retryable-status set end to end
OmarAlJarrah Jul 14, 2026
92577ab
feat: add StatusErrorMap for data-driven status-to-error mapping
OmarAlJarrah Jul 14, 2026
0fc2d98
test: enforce requirement-traceability coverage end-to-end
OmarAlJarrah Jul 14, 2026
a3fd80f
feat(retry): complete retry loops with cancellation, resend gate, and…
OmarAlJarrah Jul 14, 2026
5c67a8e
feat(pagination): harden sync Paginator page lifecycle and certify it
OmarAlJarrah Jul 14, 2026
70e82f8
test: add shared transport-adapter conformance battery
OmarAlJarrah Jul 14, 2026
00d6656
feat(client): add sync/async transport bridges
OmarAlJarrah Jul 14, 2026
44a08c8
merge: PY-M9-03 StatusErrorMap and typed service-error rules
OmarAlJarrah Jul 14, 2026
3007cac
merge: PY-M6-01 certify ItemPaged/Paginator sync battery
OmarAlJarrah Jul 14, 2026
ccfe635
merge: PY-M5-02 wire configurable retryable-status set end to end
OmarAlJarrah Jul 14, 2026
9823d59
merge: PY-M4-03 complete both retry loops (budgets, cancellation, att…
OmarAlJarrah Jul 14, 2026
2934e1d
merge: PY-M3-06 sync/async transport bridges with explicit-executor rule
OmarAlJarrah Jul 14, 2026
ff36a2a
merge: PY-M8-01 shared transport adapter conformance battery
OmarAlJarrah Jul 14, 2026
496c01b
merge: PY-M10-02 traceability matrix, ledger N/A bridge, requirement …
OmarAlJarrah Jul 14, 2026
e8e963d
feat(serde): add response handlers over a pure status classifier
OmarAlJarrah Jul 14, 2026
692401e
feat: wire-exact raw-query splice for pagination next-request rewriting
OmarAlJarrah Jul 14, 2026
ca28577
merge: PY-M5-06 response handlers (decode_body, decode_success)
OmarAlJarrah Jul 14, 2026
16320d8
feat(pagination): certify AsyncItemPaged (cancellation, close ledger,…
OmarAlJarrah Jul 14, 2026
93f0b7d
merge: PY-M6-02 certify AsyncItemPaged (cancellation, close ledger, t…
OmarAlJarrah Jul 14, 2026
3acffea
merge: PY-M6-03 paging strategies + wire-exact verbatim splice
OmarAlJarrah Jul 14, 2026
5b54028
feat(transport): certify the httpx adapter against the shared conform…
OmarAlJarrah Jul 14, 2026
c6a8b8e
merge: PY-M8-03 certify httpx adapter against shared conformance battery
OmarAlJarrah Jul 14, 2026
5921d58
feat(codegen): add ServiceCore/AsyncServiceCore executors over pure p…
OmarAlJarrah Jul 14, 2026
b09210a
merge: PY-M9-02 ServiceCore/AsyncServiceCore executors over pure plan…
OmarAlJarrah Jul 14, 2026
992758a
feat(http-requests): certify the requests adapter against the shared …
OmarAlJarrah Jul 14, 2026
28076c4
merge: PY-M8-05 certify requests adapter against shared conformance b…
OmarAlJarrah Jul 14, 2026
7034dee
fix(mypy): exclude transport-adapter conftest shims from the module n…
OmarAlJarrah Jul 14, 2026
0f31e28
feat(codegen): add ServiceDefaults and merge_config pipeline flattening
OmarAlJarrah Jul 14, 2026
d5ae54f
merge: PY-M9-04 ServiceDefaults and merge_config pipeline flattening
OmarAlJarrah Jul 14, 2026
5f3233a
feat(codegen): add tiered auth-requirement resolver over the auth pol…
OmarAlJarrah Jul 14, 2026
d2c3d36
merge: PY-M9-05 tiered auth-requirement resolver over the auth policies
OmarAlJarrah Jul 14, 2026
a256c7d
test(aiohttp): certify AiohttpHttpClient against the shared async tra…
OmarAlJarrah Jul 14, 2026
352cfc9
merge: PY-M8-04 certify aiohttp adapter against shared async conforma…
OmarAlJarrah Jul 14, 2026
3aa8e28
feat(examples): add the petstore codegen canary with a regen and pari…
OmarAlJarrah Jul 14, 2026
e974395
merge: PY-M9-06 petstore codegen canary with regen and parity gates
OmarAlJarrah Jul 14, 2026
e47da25
feat(transport): certify stdlib transports against conformance battery
OmarAlJarrah Jul 14, 2026
6a6b600
merge: PY-M8-02 certify stdlib transports against shared conformance …
OmarAlJarrah Jul 14, 2026
08b2af5
refactor(tck): move stdlib conformance harness into its own package
OmarAlJarrah Jul 14, 2026
74bb573
feat(tck): extract the conformance kit into a dedicated dexpace-sdk-t…
OmarAlJarrah Jul 14, 2026
75c2c4c
merge: PY-M10-01 extract the conformance kit into dexpace-sdk-tck (6t…
OmarAlJarrah Jul 14, 2026
c12003a
fix(tck): make the httpx malformed-header facet honest on Python 3.14
OmarAlJarrah Jul 14, 2026
1c193b8
ci: add release train, version-skew matrix, and single-version-source…
OmarAlJarrah Jul 14, 2026
25e1524
merge: PY-M11-02 release train, version-skew CI matrix, and single-ve…
OmarAlJarrah Jul 14, 2026
a3685aa
docs: add seam extension guides, codegen target, and refresh the docs…
OmarAlJarrah Jul 14, 2026
70defe9
merge: PY-M11-01 docs, seam extension guides, and codegen target
OmarAlJarrah Jul 14, 2026
eeb86eb
docs: correct TracingPolicy docstring to note its async twin
OmarAlJarrah Jul 14, 2026
57d5f75
feat: load the real 645-requirement index into the traceability audit
OmarAlJarrah Jul 14, 2026
24d3b12
test: annotate tests with normative requirement-id markers
OmarAlJarrah Jul 14, 2026
530b60b
feat: ledger not-applicable and deviated requirements for the traceab…
OmarAlJarrah Jul 14, 2026
8730582
fix(sse): align the parser with WHATWG dispatch and lifecycle semantics
OmarAlJarrah Jul 15, 2026
58efd22
fix: apply retry-safety uniformly and complete pacing/classifier cove…
OmarAlJarrah Jul 15, 2026
92feebd
feat(redirect): enforce credential hygiene and add downgrade/predicat…
OmarAlJarrah Jul 15, 2026
74ec4e3
feat(observability): add header redaction, log detail levels, and tra…
OmarAlJarrah Jul 15, 2026
9def0c4
fix(http): tighten construction-time validation across the HTTP models
OmarAlJarrah Jul 15, 2026
206954c
fix(bodies): detect a truncated file transfer as a short write
OmarAlJarrah Jul 15, 2026
2bb8b2c
feat(config): add override removal and NO_PROXY escape parsing
OmarAlJarrah Jul 15, 2026
47edbcb
feat(pipeline): add batch policy adds and same-instance pillar idempo…
OmarAlJarrah Jul 15, 2026
9673ec2
chore: regenerate public API surface baseline for new conformance APIs
OmarAlJarrah Jul 15, 2026
7c5afa3
ci: enforce the requirement-traceability gate across all subsystems
OmarAlJarrah Jul 15, 2026
da6471a
docs: archive the conformance ledger and cross-link the deviations doc
OmarAlJarrah Jul 15, 2026
b71e6f7
fix(observability): default HTTP log detail level to NONE
OmarAlJarrah Jul 15, 2026
bc748ae
fix(aiohttp): require a Python 3.14-compatible aiohttp floor
OmarAlJarrah Jul 15, 2026
c7dd962
test: skip the no-op allocation check under a coverage tracer
OmarAlJarrah Jul 15, 2026
700e004
fix(aiohttp): raise the aiohttp floor to 3.10 for the timeout-error s…
OmarAlJarrah Jul 15, 2026
2eee180
test: narrow a type-ignore code to match the too-many-args error
OmarAlJarrah Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
242 changes: 242 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,28 @@ jobs:
- name: Run pytest
run: uv run pytest -q

# Blocking requirement-traceability audit. Collection-only run: the
# TraceabilityPlugin (registered in the root conftest) collects every
# @pytest.mark.req(...) marker, writes the matrix artifact, and — because
# DEXPACE_TRACEABILITY_GATE is set — fails the run if any MUST-level
# requirement in an enabled subsystem is neither covered by a marker nor
# listed N/A in docs/traceability-na-ids.txt (the deviations-ledger bridge).
- name: Run traceability audit (blocking)
env:
DEXPACE_TRACEABILITY_ENABLE: "seams,http,bodies,io,context,pipeline,recovery,redirect,retry,auth,xcut,serde,pagination,sse,observability,config,transport,async,nfr"
DEXPACE_TRACEABILITY_NA: docs/traceability-na-ids.txt
DEXPACE_TRACEABILITY_MATRIX: build/traceability-matrix.json
DEXPACE_TRACEABILITY_GATE: "1"
run: uv run pytest -q --co

- name: Upload traceability matrix
if: always()
uses: actions/upload-artifact@v4
with:
name: traceability-matrix-py${{ matrix.python-version }}
path: build/traceability-matrix.json
if-no-files-found: error

- name: Run mypy
run: uv run mypy --strict

Expand All @@ -45,3 +67,223 @@ jobs:

- name: Run ruff format --check
run: uv run ruff format --check

- name: Run import-linter
run: uv run lint-imports

packaging:
name: packaging invariants
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
python-version: "3.12"

- name: Check namespace layout
run: uv run --no-project python tools/check_namespace_packages.py

- name: Check MIT headers
run: uv run --no-project python tools/check_mit_headers.py

- name: Check runtime dependency budget
run: uv run --no-project python tools/check_dependency_audit.py

- name: Build core + stdlib wheels
run: |
uv build --package dexpace-sdk-core --wheel -o dist
uv build --package dexpace-sdk-http-stdlib --wheel -o dist

- name: Smoke-import wheels in a fresh venv
run: |
uv venv /tmp/wheel-smoke
uv pip install --python /tmp/wheel-smoke \
dist/dexpace_sdk_core-*.whl \
dist/dexpace_sdk_http_stdlib-*.whl
/tmp/wheel-smoke/bin/python tools/smoke_wheel_import.py

coverage:
name: coverage gate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
python-version: "3.12"

- name: Sync workspace
run: uv sync

# Blocking aggregate line+branch coverage gate. The 80% floor lives in
# [tool.coverage.report].fail_under in pyproject.toml; pytest-cov exits
# non-zero when the total drops below it.
- name: Run coverage gate
run: uv run pytest -q --cov --cov-report=term-missing

typecheck-divergence:
name: typecheck divergence (pyright, non-blocking)
runs-on: ubuntu-latest
# Advisory early-warning lane: pyright cross-checks mypy's verdict so a
# divergence between the two checkers surfaces early. This is NOT a gate —
# `mypy --strict` in the `test` job remains the sole blocking type check.
# `continue-on-error` at both the job and step level keeps a red result
# (e.g. pyright's stricter completeness scoring) from ever failing CI.
# pyright is layered in per-run via `uv run --with`, so it stays out of the
# committed dependency set and uv.lock.
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
python-version: "3.14"

- name: Sync workspace
run: uv sync

- name: pyright --verifytypes (advisory)
continue-on-error: true
run: uv run --with pyright pyright --verifytypes dexpace.sdk.core --ignoreexternal

- name: pyright typing-conformance corpus (advisory)
continue-on-error: true
run: uv run --with pyright pyright packages/dexpace-sdk-core/tests/typing_conformance.py

version-skew:
name: version skew (Python ${{ matrix.python-version }}, ${{ matrix.resolution }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.12", "3.13", "3.14"]
resolution: ["highest", "lowest-direct"]
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
python-version: ${{ matrix.python-version }}

# Resolve the workspace at either edge of its declared dependency window.
# `highest` exercises the newest adapters against head core (the standard
# release runtime). `lowest-direct` pins every *direct* dependency to its
# declared floor: core to the compatible-release lower bound each adapter
# and the TCK pin (dexpace-sdk-core>=0.1,<0.2) and each transport to its
# minimum (httpx>=0.27, aiohttp>=3.9, requests>=2.32, furl>=2.1.3). Running
# both edges across Python 3.12-3.14 turns the forward-compatibility
# promise -- newest adapters and the generated petstore canary run against
# the oldest supported core -- into a passing pipeline, not a prose claim.
- name: Sync workspace (${{ matrix.resolution }} resolution)
run: uv sync --resolution ${{ matrix.resolution }}

# Newest adapters (built from HEAD) plus the TCK conformance battery, run
# against the resolved core. `--no-sync` keeps the environment exactly as
# the resolution step left it rather than re-resolving to the default edge.
- name: Adapters + TCK conformance
run: >
uv run --no-sync pytest -q
packages/dexpace-sdk-http-stdlib/tests
packages/dexpace-sdk-http-httpx/tests
packages/dexpace-sdk-http-aiohttp/tests
packages/dexpace-sdk-http-requests/tests
packages/dexpace-sdk-tck/src/dexpace/sdk/tck

# The petstore canary is the "generated at vN.M runs on vN.x" proof: a
# generated SDK driven end to end over the certified core. Running it at
# both dependency edges proves the generated output keeps working against
# both head core and the oldest supported core.
- name: Petstore canary
run: uv run --no-sync pytest -q examples/petstore/tests

free-threaded:
name: free-threaded CPython (${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.13t", "3.14t"]
# Exercises the codebase's explicit-lock claims (no GIL assumption) on a real
# GIL-disabled interpreter, not just under code review. Only the
# lock-sensitive core suites run here, and each spins real threads: the
# single-use body once-guards, the single-flight token cache, the
# ContextStore cap-drain, the emit-once logger latch, and the SSE
# cross-thread close. Core is pure Python (only furl), so the environment
# holds core plus the test runner alone -- no C-extension transports, which
# do not yet ship free-threaded wheels.
env:
# Fail loudly if an imported extension would force the GIL back on rather
# than silently re-enabling it and voiding the exercise.
PYTHON_GIL: "0"
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true

- name: Install free-threaded interpreter
run: uv python install ${{ matrix.python-version }}

- name: Create free-threaded environment
run: uv venv --python ${{ matrix.python-version }} .venv-ft

# `--only-binary hypothesis` uses its universal wheel instead of building
# its sdist, whose build pulls a Rust/PyO3 toolchain with no free-threaded
# support yet. hypothesis is imported by the core test conftest.
- name: Install core + test runner
run: >
uv pip install --python .venv-ft --only-binary hypothesis
./packages/dexpace-sdk-core
pytest pytest-asyncio hypothesis

- name: Verify the interpreter is GIL-disabled
run: .venv-ft/bin/python -c "import sys; assert not sys._is_gil_enabled(), 'GIL is enabled'; print(sys.version)"

- name: Run lock-sensitive suites (GIL disabled)
run: >
.venv-ft/bin/python -m pytest -q
packages/dexpace-sdk-core/tests/http/test_single_use_bodies.py
packages/dexpace-sdk-core/tests/auth/test_token_cache.py
packages/dexpace-sdk-core/tests/context/test_context_store.py
packages/dexpace-sdk-core/tests/instrumentation/test_logger.py
packages/dexpace-sdk-core/tests/sse/test_connection_lifecycle.py

version-source:
name: single version train
runs-on: ubuntu-latest
# One version train for every published package (NFR-14): the version lives
# once in each pyproject and is mirrored in uv.lock, and a built wheel
# carries that real version -- never a placeholder such as 0.0.0 or
# "unknown" (NFR-15). A bump touches all published packages together or this
# gate fails.
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
python-version: "3.12"

- name: Build a wheel to inspect its metadata version
run: uv build --package dexpace-sdk-core --wheel -o dist

- name: Verify one version train (pyproject + uv.lock + wheel)
run: uv run --no-project python tools/check_version_train.py --wheel-dir dist
105 changes: 105 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Release

# Publishes the whole version train to PyPI via trusted publishing (OIDC), with
# PEP 740 attestations and reproducible, byte-stable wheels. A tag push builds
# and publishes; a manual run defaults to a dry run that builds and verifies the
# artifacts without uploading anything.
on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
dry_run:
description: "Build and verify only; skip the PyPI upload."
type: boolean
default: true

permissions:
contents: read

jobs:
build:
name: build + verify artifacts
runs-on: ubuntu-latest
steps:
- name: Checkout
# Full history so the commit timestamp is available for a reproducible
# SOURCE_DATE_EPOCH.
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
python-version: "3.12"

# Reproducible builds (NFR-12): pin every archive timestamp to the tagged
# commit's authored date and fix the hash seed so the build carries no
# run-to-run entropy. hatchling honours SOURCE_DATE_EPOCH and writes wheel
# entries in a stable order, so identical inputs yield byte-identical
# wheels.
- name: Pin reproducible-build environment
run: |
echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> "$GITHUB_ENV"
echo "PYTHONHASHSEED=0" >> "$GITHUB_ENV"

# Build each published distribution explicitly so the unpublished
# workspace root never lands in dist/. Sdists ship alongside wheels.
- name: Build wheels + sdists
run: |
for pkg in \
dexpace-sdk-core \
dexpace-sdk-http-stdlib \
dexpace-sdk-http-httpx \
dexpace-sdk-http-aiohttp \
dexpace-sdk-http-requests \
dexpace-sdk-tck; do
uv build --package "$pkg" --out-dir dist
done

# NFR-14/15: every artifact carries the single, real train version -- never
# a placeholder. Blocks the upload if any wheel disagrees.
- name: Verify one version train (pyproject + uv.lock + wheels)
run: uv run --no-project python tools/check_version_train.py --wheel-dir dist

- name: List artifacts
run: ls -1 dist | sort

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: release-dist
path: dist/
if-no-files-found: error

publish:
name: publish to PyPI (trusted publishing)
needs: build
runs-on: ubuntu-latest
# A tag push publishes; a manual run publishes only when dry_run is turned
# off. The default manual run is a build-and-verify dry run.
if: >-
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false')
environment:
name: pypi
url: https://pypi.org/project/dexpace-sdk-core/
permissions:
# OIDC token for PyPI trusted publishing; no long-lived API token needed.
id-token: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: release-dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
# PEP 740 provenance attestations, signed with the OIDC identity
# (NFR-16).
attestations: true
print-hash: true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,6 @@ dmypy.json

# Cython debug symbols
cython_debug/

# Subagent-driven-development scratch (progress ledger, task briefs/reports)
.superpowers/
Loading
Loading