From ee88caffd841b4a546fc1d3826df7addf0501378 Mon Sep 17 00:00:00 2001 From: Krzysztof Macewicz Date: Sun, 27 Sep 2026 09:39:09 +0200 Subject: [PATCH] wip: Python 3.14 and Node 24 and 26, tested and claimed Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/rulesets/main.json | 9 +++++++++ .github/workflows/ci.yml | 6 +++--- .github/workflows/release.yml | 2 +- docs/github-security.md | 16 ++++++++-------- docs/implementations.md | 4 ++-- docs/platforms.md | 12 ++++++------ docs/publishing.md | 2 +- docs/test-toolchain.md | 2 +- docs/weather-starter.md | 2 +- python/pyproject.toml | 3 ++- typescript/README.md | 4 ++-- typescript/package-lock.json | 2 +- typescript/package.json | 2 +- 13 files changed, 38 insertions(+), 28 deletions(-) diff --git a/.github/rulesets/main.json b/.github/rulesets/main.json index 9b32a92..07ee17e 100644 --- a/.github/rulesets/main.json +++ b/.github/rulesets/main.json @@ -50,6 +50,9 @@ { "context": "python (3.13)" }, + { + "context": "python (3.14)" + }, { "context": "typescript (18)" }, @@ -59,6 +62,12 @@ { "context": "typescript (22)" }, + { + "context": "typescript (24)" + }, + { + "context": "typescript (26)" + }, { "context": "contract" }, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84ce1bb..77e5622 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: # The floor is 3.11 because the code uses `X | None` at runtime in annotations resolved by # get_type_hints. The ceiling is whatever is current: a canonical encoding that drifts with a # Python release is exactly the failure the contract exists to prevent, so both ends get run. - python: ["3.11", "3.12", "3.13"] + python: ["3.11", "3.12", "3.13", "3.14"] services: postgres: @@ -141,14 +141,14 @@ jobs: strategy: fail-fast: false matrix: - node: ["18", "20", "22"] + node: ["18", "20", "22", "24", "26"] # The same two engines the Python job gets, and for the same reason: this library reached Tier 2 # on 6 September 2026, so "it renders DDL and takes part in a migration" is now a claim about # real servers. Running the vectors without them would leave the adapters - two of the eight # integrations requirement 17.5 is about - checked by nothing. # - # All three Node versions, deliberately. The ClickHouse adapter exists because the official + # All five Node versions, deliberately. The ClickHouse adapter exists because the official # client requires Node 20, and a matrix that only exercised the engines on one version would be # unable to notice the day that stops being the reason. services: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff769f8..5b2ae07 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,7 +63,7 @@ jobs: git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main if ! git merge-base --is-ancestor "$GITHUB_SHA" refs/remotes/origin/main; then echo "::error::commit $GITHUB_SHA is not an ancestor of main, so it never passed the" - echo "::error::eleven required checks. The ruleset protects main; it does not stop a tag" + echo "::error::required checks. The ruleset protects main; it does not stop a tag" echo "::error::from being pointed at any commit in the repository, including one on a" echo "::error::branch that was never reviewed. Merge first, then tag the merge." exit 1 diff --git a/docs/github-security.md b/docs/github-security.md index a5848ea..4f4e834 100644 --- a/docs/github-security.md +++ b/docs/github-security.md @@ -27,7 +27,7 @@ they differ, the difference is called out rather than left to be noticed. - **Releasing is a tag and holds no credential** ✅ — both registries authenticate the workflow over OIDC, so there is no publishing token in this repository, in its Actions secrets or on a laptop. See §4.2, which was a list of intentions until 12 September. -- **Two languages, so two analyses and eleven required checks**, not four (§1). The count is derived from +- **Two languages, so two analyses and fourteen required checks**, not four (§1). The count is derived from `main.json` by a test, because it was wrong here by one until 12 September and nothing counted it. - **The default branch is `main`, not `master`.** The rulesets differ in that one string, and a ruleset targeting the wrong ref is silently inert. @@ -49,13 +49,13 @@ A ruleset named `main` is active on `refs/heads/main` with **no bypass actors**, - `required_linear_history` — no merge commits, so `git log main` stays readable - `pull_request` — direct pushes are blocked, with `required_review_thread_resolution: true` so an unresolved comment cannot be merged past, and `allowed_merge_methods: [squash, rebase]` -- `required_status_checks` with `strict: true` and **eleven contexts** +- `required_status_checks` with `strict: true` and **fourteen contexts** -The eleven are the whole matrix, not a representative sample: +The fourteen are the whole matrix, not a representative sample: ``` -python (3.11) python (3.12) python (3.13) -typescript (18) typescript (20) typescript (22) +python (3.11) python (3.12) python (3.13) python (3.14) +typescript (18) typescript (20) typescript (22) typescript (24) typescript (26) contract analyze (python) analyze (javascript-typescript) analyze (actions) CodeQL @@ -67,13 +67,13 @@ scanning integration and is the one that fails when the analysis has produced a Requiring the three without it would require that the scan ran, not that it found nothing. There are zero open alerts today, so requiring it costs nothing to adopt — which is the only moment it is cheap. -Requiring all six language-version cells rather than one is specific to this repository and worth the +Requiring all nine language-version cells rather than one is specific to this repository and worth the minute it costs. The product's guarantee is that four implementations encode a model to identical bytes; a canonical encoding that drifts with a Python release or a Node release is precisely the failure the byte contract exists to prevent, and a matrix cell that runs but blocks nothing is how that drift would arrive looking green. -`contract` is the cheapest and most valuable of the eleven: it rebuilds the wheel and checks the PEP 561 +`contract` is the cheapest and most valuable of the fourteen: it rebuilds the wheel and checks the PEP 561 marker is inside it, re-derives every committed hashing digest with `openssl`, and refuses a change to the one hand-written conformance vector without a deliberate edit to the workflow. @@ -462,7 +462,7 @@ distribution name is the one an attacker needs no access at all to exploit. ✅ pull_request_template.md and issue templates ✅ rulesets kept as JSON in .github/rulesets/ ✅ branch ruleset on main: PR required, no force push, no deletion, linear history, no bypass actors -✅ branch ruleset: all eleven status checks required, strict +✅ branch ruleset: all fourteen status checks required, strict ✅ tag ruleset on refs/tags/v* ✅ secret scanning + push protection ✅ Dependabot alerts + security updates diff --git a/docs/implementations.md b/docs/implementations.md index cdefb80..1bee15d 100644 --- a/docs/implementations.md +++ b/docs/implementations.md @@ -22,8 +22,8 @@ implementation that does not pass the Tier 0 vectors is not an SDE library, whoe | Library | Language | Tier | Hashing (§2a) | IR contract | Map contract | Engines | |---|---|---|---|---|---|---| -| `smart-data-engine-sdk` | Python 3.11–3.13 | 2 | yes | 1 | 1–5 | `clickhouse`, `orderbook`, `postgres` | -| `@smart-data-engines/sde` | TypeScript / Node 18–22 | 2 | yes | 1 | 1–5 | `clickhouse`, `postgres` | +| `smart-data-engine-sdk` | Python 3.11–3.14 | 2 | yes | 1 | 1–5 | `clickhouse`, `orderbook`, `postgres` | +| `@smart-data-engines/sde` | TypeScript / Node 18–26 | 2 | yes | 1 | 1–5 | `clickhouse`, `postgres` | The engines column carries **dialect identifiers**, not product names: they are what a hand-written layout and `schema_statements(dialect=...)` take, so they are the spelling a client actually types. diff --git a/docs/platforms.md b/docs/platforms.md index 4b45f47..7836de2 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -8,9 +8,9 @@ workflow, and fails if the three disagree. | | | |---|---| -| Supported | **3.11, 3.12, 3.13** | -| Tested in CI | 3.11, 3.12, 3.13 — every one, on every pull request | -| Declared in `python/pyproject.toml` | `>=3.11,<3.14` | +| Supported | **3.11, 3.12, 3.13, 3.14** | +| Tested in CI | 3.11, 3.12, 3.13, 3.14 — every one, on every pull request | +| Declared in `python/pyproject.toml` | `>=3.11,<3.15` | The floor is 3.11 because the code uses `X | None` in annotations that are resolved at runtime by `get_type_hints`. @@ -33,9 +33,9 @@ one action instead of two. | | | |---|---| -| Supported | **18, 20, 22** | -| Tested in CI | 18, 20, 22 — every one, on every pull request | -| Declared in `typescript/package.json` | `>=18 <23` | +| Supported | **18, 20, 22, 24, 26** | +| Tested in CI | 18, 20, 22, 24, 26 — every one, on every pull request | +| Declared in `typescript/package.json` | `>=18 <27` | Same reasoning, same closed ceiling. The TypeScript implementation has to agree with the Python one byte for byte on the shared conformance vectors, so an untested runtime is an untested encoder. diff --git a/docs/publishing.md b/docs/publishing.md index 8208d8b..0f809a7 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -568,7 +568,7 @@ upload: |---|---| | a bare `v0.1.0` tag | A tag that triggers nothing is a release that **looks done**: the tag is in the repository, protected, and nothing was published. The workflow triggers on `v*` purely so this can be said out loud, with both correct forms named. | | the tag disagrees with the manifest | Publishing the manifest's version under the tag's name. Neither half is correctable: the registry will not reuse a version number and the ruleset will not move the tag. | -| the tagged commit is not on `main` | The ruleset protects `main` with eleven required checks; it does **not** stop a tag being pointed at any commit in the repository, including one on a branch nobody reviewed. Ancestry is what makes "the published artefact passed CI" a fact rather than an assumption. | +| the tagged commit is not on `main` | The ruleset protects `main` with the required checks; it does **not** stop a tag being pointed at any commit in the repository, including one on a branch nobody reviewed. Ancestry is what makes "the published artefact passed CI" a fact rather than an assumption. | | the artefact is missing a licence, `py.typed`, or `dist/` | All three have actually been missing, on 8 September, and none of it was visible from a green suite — the suite runs the source tree and a user runs the artefact. The worst would have put an importable-looking package with no code in it under our own scope. | | the artefact records a version other than the tag's | The gate agreeing with the manifest does not prove the *build* used it, and what a user installs is the number inside the file. | diff --git a/docs/test-toolchain.md b/docs/test-toolchain.md index 55fbe25..b9c7d95 100644 --- a/docs/test-toolchain.md +++ b/docs/test-toolchain.md @@ -1,6 +1,6 @@ # TypeScript test tools -The SDK supports Node 18, 20 and 22. The test toolchain uses Vitest 3.2.7 and an explicit +The SDK supports Node 18, 20, 22, 24 and 26. The test toolchain uses Vitest 3.2.7 and an explicit Vite 6.4.3 dependency range so that a new installation does not silently choose Vite 7, which has a different Node requirement. Both selected packages declare Node 18 support. This changes development tools only; neither package is an SDK runtime dependency. diff --git a/docs/weather-starter.md b/docs/weather-starter.md index 09c0f7f..9501ea9 100644 --- a/docs/weather-starter.md +++ b/docs/weather-starter.md @@ -10,7 +10,7 @@ to try the demo. [Publishing](publishing.md) describes the separate release proc ## Install artifacts and obtain trusted metadata -Use Python 3.11-3.13, Node 18-22 and a local POSIX filesystem. An operator supplies the reviewed +Use Python 3.11-3.14, Node 18-26 and a local POSIX filesystem. An operator supplies the reviewed wheel and npm tarball. In fresh application directories: ```sh diff --git a/python/pyproject.toml b/python/pyproject.toml index 31e22b6..83711c1 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -24,7 +24,7 @@ name = "smart-data-engine-sdk" dynamic = ["version"] description = "Smart Data Engine client library: declare a data model, we place it and move it" readme = "README.md" -requires-python = ">=3.11,<3.14" +requires-python = ">=3.11,<3.15" license = "Apache-2.0" # Apache-2.0 section 4 asks that the licence travel with the work, and section 4(d) says the same # about a NOTICE file where one exists. Both were missing from the wheel and the sdist until this @@ -47,6 +47,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Database", # The wheel carries `py.typed`, so this row is a fact the artefact can be checked against. "Typing :: Typed", diff --git a/typescript/README.md b/typescript/README.md index 175e054..bb8f476 100644 --- a/typescript/README.md +++ b/typescript/README.md @@ -55,7 +55,7 @@ check reads a table and a constructor cannot await. You therefore cannot hold a has not run. **The ClickHouse adapter has no driver dependency.** There is an official Node client and it requires -Node 20 or newer; this package supports Node 18 to 22 and its CI runs all three. Dropping Node 18 +Node 20 or newer; this package supports Node 18 to 26 and its CI runs all five majors. Dropping Node 18 would narrow a published claim to gain a dependency, and pinning a superseded version of the client is the conflict the zero-dependency rule exists to avoid - so neither. ClickHouse's HTTP interface needs no client, and this adapter therefore owns both of its timeouts instead of inheriting a @@ -146,7 +146,7 @@ It includes local setup, telemetry, operator handoffs and an ownership-checked r ## Test-tool dependencies -The Node 18/20/22 matrix uses the tool versions and supported invocation described in +The Node 18/20/22/24/26 matrix uses the tool versions and supported invocation described in [the test-toolchain record](../docs/test-toolchain.md). That record distinguishes the SDK runtime from development dependencies and documents the remaining development-server advisory. diff --git a/typescript/package-lock.json b/typescript/package-lock.json index 150e3aa..c9e3826 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -20,7 +20,7 @@ "vitest": "^3.2.7" }, "engines": { - "node": ">=18 <23" + "node": ">=18 <27" }, "peerDependencies": { "pg": ">=8" diff --git a/typescript/package.json b/typescript/package.json index dbffbe2..b667317 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -55,7 +55,7 @@ "bin" ], "engines": { - "node": ">=18 <23" + "node": ">=18 <27" }, "scripts": { "prepack": "npm run build",