This is an automated email from the ASF dual-hosted git repository.
jason810496 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 906aab3c2e4 Add TypeScript SDK capability manifest and compatibility
matrix (#71754)
906aab3c2e4 is described below
commit 906aab3c2e4a564fdbff7dc3897a6c2a8a507475
Author: Jason(Zhe-You) Liu <[email protected]>
AuthorDate: Wed Aug 26 15:49:31 2026 +0800
Add TypeScript SDK capability manifest and compatibility matrix (#71754)
* Add TypeScript SDK capability manifest and compatibility matrix
Co-authored-by: Phani Kumar <[email protected]>
* Report a missing TypeScript supervisor schema constant separately
read_ts_schema_version returned None both when capabilities.yaml disagreed
with the generated runtime constant and when the constant could not be read
at all, so a missing or renamed SUPERVISOR_API_VERSION sent the contributor
to capabilities.yaml, which cannot fix either case. The unreadable case now
names src/generated/supervisor.ts and the generator instead. Reading the
file
also no longer raises when it is absent, so that path reaches the new
message.
---------
Co-authored-by: Phani Kumar <[email protected]>
---
.pre-commit-config.yaml | 14 +++
scripts/ci/prek/update_ts_sdk_readme_matrix.py | 113 +++++++++++++++++++
.../ci/prek/test_update_ts_sdk_readme_matrix.py | 73 +++++++++++++
ts-sdk/README.md | 52 +++++++++
ts-sdk/capabilities.yaml | 119 +++++++++++++++++++++
5 files changed, 371 insertions(+)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5278a66716e..5f575d25296 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -295,6 +295,20 @@ repos:
additional_dependencies: ['PyYAML>=6.0', 'rich>=13.6.0']
pass_filenames: false
require_serial: true
+ - id: update-ts-sdk-readme-matrix
+ name: Update the TypeScript SDK compatibility matrix in
ts-sdk/README.md
+ entry: ./scripts/ci/prek/update_ts_sdk_readme_matrix.py
+ language: python
+ files: >
+ (?x)
+ ^ts-sdk/capabilities\.yaml$|
+ ^ts-sdk/src/generated/supervisor\.ts$|
+ ^ts-sdk/README\.md$|
+ ^scripts/ci/prek/lang_sdk_compat_matrix\.py$|
+ ^scripts/ci/prek/update_ts_sdk_readme_matrix\.py$
+ additional_dependencies: ['PyYAML>=6.0', 'rich>=13.6.0']
+ pass_filenames: false
+ require_serial: true
- id: check-go-version-in-sync
name: Check Go toolchain version is consistent across build files
entry: ./scripts/ci/prek/check_go_version_in_sync.py
diff --git a/scripts/ci/prek/update_ts_sdk_readme_matrix.py
b/scripts/ci/prek/update_ts_sdk_readme_matrix.py
new file mode 100644
index 00000000000..837ffa59dbe
--- /dev/null
+++ b/scripts/ci/prek/update_ts_sdk_readme_matrix.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# /// script
+# requires-python = ">=3.10,<3.11"
+# dependencies = ["PyYAML>=6.0", "rich>=13.6.0"]
+# ///
+"""Regenerate the TypeScript SDK compatibility table in ``ts-sdk/README.md``.
+
+Renders the Markdown matrix from ``ts-sdk/capabilities.yaml`` between the
AUTO-GENERATED markers.
+Exits non-zero when the file changed so the contributor re-stages it. The hook
also verifies that
+the manifest's supervisor schema version matches the generated TypeScript
runtime constant.
+"""
+
+from __future__ import annotations
+
+import re
+import sys
+from pathlib import Path
+
+sys.path.insert(0, str(Path(__file__).resolve().parent))
+
+from common_prek_utils import console, insert_documentation
+from lang_sdk_compat_matrix import (
+ AIRFLOW_ROOT_PATH,
+ LANG_SDKS,
+ README_MATRIX_FOOTER,
+ README_MATRIX_HEADER,
+ CapabilitiesDoc,
+ load_capabilities,
+ render_markdown_table,
+)
+
+SDK_ID = "ts"
+TS_SUPERVISOR = AIRFLOW_ROOT_PATH / "ts-sdk" / "src" / "generated" /
"supervisor.ts"
+SCHEMA_VERSION_PATTERN = re.compile(r'^export const SUPERVISOR_API_VERSION =
"(?P<version>[^"]+)" as const;$')
+
+
+def read_ts_schema_version() -> str | None:
+ """Read ``SUPERVISOR_API_VERSION`` from the generated TypeScript runtime
source."""
+ if not TS_SUPERVISOR.exists():
+ return None
+ for line in TS_SUPERVISOR.read_text().splitlines():
+ if match := SCHEMA_VERSION_PATTERN.fullmatch(line):
+ return match["version"]
+ return None
+
+
+def check_schema_version(doc: CapabilitiesDoc) -> bool:
+ """Whether the manifest agrees with the generated TypeScript supervisor
schema version.
+
+ Unlike the Java sibling hook (which treats an unreadable gradle property
as an implicit pass),
+ an unreadable constant here is deliberately treated as a failure: silently
skipping the check
+ would let capabilities.yaml drift from the runtime undetected. It is
reported separately from a
+ genuine mismatch because editing capabilities.yaml cannot fix a missing
constant.
+ """
+ ts_version = read_ts_schema_version()
+ declared = doc["supervisor_schema_version"]
+ if ts_version is None:
+ console.print(
+ "[red]Could not read SUPERVISOR_API_VERSION from
ts-sdk/src/generated/supervisor.ts: "
+ "the file is missing, or the generated declaration no longer
matches this hook's "
+ "pattern. Regenerate it with 'pnpm run generate:supervisor' in
ts-sdk/ (or update "
+ "SCHEMA_VERSION_PATTERN if scripts/generate-supervisor.mjs changed
its output). "
+ "Editing ts-sdk/capabilities.yaml cannot fix this.[/]"
+ )
+ return False
+ if ts_version == declared:
+ return True
+ console.print(
+ f"[red]ts-sdk/capabilities.yaml declares supervisor_schema_version
{declared!r} but "
+ f"src/generated/supervisor.ts SUPERVISOR_API_VERSION is
{ts_version!r}. "
+ "Update capabilities.yaml to match.[/]"
+ )
+ return False
+
+
+def main() -> int:
+ sdk = next(entry for entry in LANG_SDKS if entry["id"] == SDK_ID)
+ doc = load_capabilities(sdk["capabilities_yaml"], expected_sdk=SDK_ID)
+ if not check_schema_version(doc):
+ return 1
+ changed = insert_documentation(
+ sdk["readme"],
+ render_markdown_table(doc),
+ README_MATRIX_HEADER,
+ README_MATRIX_FOOTER,
+ extra_information="the TypeScript SDK compatibility matrix",
+ )
+ if changed:
+ console.print(
+ "[yellow]Regenerated the TypeScript SDK compatibility matrix in
ts-sdk/README.md; re-stage it.[/]"
+ )
+ return 1
+ return 0
+
+
+if __name__ in ("__main__", "__mp_main__"):
+ raise SystemExit(main())
diff --git a/scripts/tests/ci/prek/test_update_ts_sdk_readme_matrix.py
b/scripts/tests/ci/prek/test_update_ts_sdk_readme_matrix.py
new file mode 100644
index 00000000000..57f84edb94f
--- /dev/null
+++ b/scripts/tests/ci/prek/test_update_ts_sdk_readme_matrix.py
@@ -0,0 +1,73 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import pytest
+import yaml
+from ci.prek import lang_sdk_compat_matrix as matrix,
update_ts_sdk_readme_matrix as hook
+
+SCHEMA_VERSION = "2026-06-16"
+
+
+def _doc() -> dict:
+ def entry(supported: bool) -> dict:
+ return {"supported": supported, "since": "3.3" if supported else None,
"note": ""}
+
+ return {
+ "sdk": "ts",
+ "supervisor_schema_version": SCHEMA_VERSION,
+ "min_airflow_version": "3.3",
+ "states": {state: entry(True) for state, _ in matrix.STATE_DIMENSIONS},
+ "capabilities": {cap.name: entry(True) for cap in
matrix.CAPABILITY_DIMENSIONS},
+ }
+
+
+class TestMain:
+ @pytest.fixture
+ def wired(self, tmp_path, monkeypatch):
+ capabilities_yaml = tmp_path / "capabilities.yaml"
+ capabilities_yaml.write_text(yaml.safe_dump(_doc()))
+ readme = tmp_path / "README.md"
+
readme.write_text(f"intro\n\n{matrix.README_MATRIX_HEADER}\n{matrix.README_MATRIX_FOOTER}\n\noutro\n")
+ ts_supervisor = tmp_path / "supervisor.ts"
+ ts_supervisor.write_text(f'export const SUPERVISOR_API_VERSION =
"{SCHEMA_VERSION}" as const;\n')
+ monkeypatch.setattr(
+ hook,
+ "LANG_SDKS",
+ [{"id": "ts", "capabilities_yaml": capabilities_yaml, "readme":
readme}],
+ )
+ monkeypatch.setattr(hook, "TS_SUPERVISOR", ts_supervisor)
+ return readme
+
+ def test_generates_target_then_is_idempotent(self, wired):
+ assert hook.main() == 1
+ content = wired.read_text()
+ assert "| Dimension | Tier | Supported | Since | Notes |" in content
+ assert matrix.SUPPORTED_MARK in content
+ assert content.startswith("intro\n") and content.endswith("outro\n")
+
+ assert hook.main() == 0
+
+ def
test_schema_version_disagreeing_with_ts_source_fails_without_writing(self,
wired):
+ hook.TS_SUPERVISOR.write_text('export const SUPERVISOR_API_VERSION =
"2020-01-01" as const;\n')
+ assert hook.main() == 1
+ assert matrix.README_MATRIX_HEADER + "\n" +
matrix.README_MATRIX_FOOTER in wired.read_text()
+
+ def test_schema_version_matches_ts_source(self):
+ sdk = next(entry for entry in matrix.LANG_SDKS if entry["id"] ==
hook.SDK_ID)
+ doc = matrix.load_capabilities(sdk["capabilities_yaml"],
expected_sdk=hook.SDK_ID)
+ assert doc["supervisor_schema_version"] ==
hook.read_ts_schema_version()
diff --git a/ts-sdk/README.md b/ts-sdk/README.md
index 5d90568fc38..65568a5e877 100644
--- a/ts-sdk/README.md
+++ b/ts-sdk/README.md
@@ -214,6 +214,58 @@ current task context when omitted.
accepts an abort signal so tasks can clean up cooperatively when Airflow
terminates the task subprocess with SIGTERM or SIGINT.
+## Compatibility matrix
+
+Which Airflow TaskInstance states and capabilities this SDK supports. This
table is generated from
+[`capabilities.yaml`](capabilities.yaml); the conformance dimensions are
defined in the
+[Language SDK conformance
spec](https://github.com/apache/airflow/blob/main/contributing-docs/30_new_language_sdk.rst).
+Do not edit the table by hand — update the manifest and run the
+`update-ts-sdk-readme-matrix` prek hook.
+
+<!-- BEGIN AUTO-GENERATED LANG-SDK COMPAT MATRIX -->
+
+*Min. Airflow version: 3.4 · supervisor schema: 2026-10-30*
+
+| Dimension | Tier | Supported | Since | Notes |
+|---|---|---|---|---|
+| **TaskInstance states** | | | | |
+| state: `success` | MUST | ✓ | 3.4 | |
+| state: `failed` | MUST | ✓ | 3.4 | |
+| state: `up_for_retry` | MUST | ✓ | 3.4 | RetryTask |
+| state: `skipped` | SHOULD | ✗ | – | runtime does not emit TaskState skipped
yet |
+| state: `deferred` | MAY | ✗ | – | runtime does not emit DeferTask yet |
+| state: `up_for_reschedule` | MAY | ✗ | – | runtime does not emit
RescheduleTask yet |
+| state: `awaiting_input` | MAY | ✗ | – | runtime does not emit AwaitInputTask
yet |
+| state: `removed` | MAY | ✓ | 3.4 | |
+| **Runtime capabilities** | | | | |
+| capability: `mixed-lang-stub-target` | MUST | ✓ | 3.4 | @task.stub |
+| capability: `task-logging` | MUST | ✓ | 3.4 | structured records over the
log socket |
+| capability: `xcom-read-write` | MUST | ✓ | 3.4 | getXCom / setXCom |
+| capability: `connection-read` | MUST | ✓ | 3.4 | getConnection |
+| capability: `variable-read-write` | MUST | ✗ | – | getVariable only; no
write over the comm socket yet |
+| capability: `self-contained-bundle` | MUST | ✓ | 3.4 | Airflow metadata
embedded in the bundle |
+| capability: `retry-policy` | MAY | ✗ | – | no task-facing retry-policy API
yet |
+| capability: `task-state-store` | MAY | ✗ | – | no task-facing state-store
API yet |
+| capability: `asset-state-store` | MAY | ✗ | – | no task-facing state-store
API yet |
+| capability: `asset-event-emit` | MAY | ✗ | – | runtime does not emit asset
events yet |
+| capability: `asset-event-read` | MAY | ✗ | – | no task-facing asset-event
API yet |
+| **Native-Dag authoring** | | | | |
+| capability: `native-dag-authoring` | SHOULD | ✗ | – | native Dag authoring
not implemented yet |
+| capability: `task-args` | MUST † | n/a | – | |
+| capability: `dag-params` | MUST † | n/a | – | |
+| capability: `taskflow-dependencies` | MUST † | n/a | – | |
+| capability: `branching` | SHOULD † | n/a | – | |
+| capability: `dag-test` | SHOULD † | n/a | – | |
+| capability: `task-group` | MAY † | n/a | – | |
+| capability: `dynamic-task-mapping` | MAY † | n/a | – | |
+| capability: `asset-inlets-outlets` | MAY † | n/a | – | |
+| capability: `asset-scheduling` | MAY † | n/a | – | |
+| capability: `object-store` | MAY † | n/a | – | no object-storage API yet |
+
+*Marks: ✓ supported · ✗ not supported · n/a not applicable. A tier marked †
applies only when `native-dag-authoring` is supported.*
+
+<!-- END AUTO-GENERATED LANG-SDK COMPAT MATRIX -->
+
## Development
```bash
diff --git a/ts-sdk/capabilities.yaml b/ts-sdk/capabilities.yaml
new file mode 100644
index 00000000000..4972a07fa11
--- /dev/null
+++ b/ts-sdk/capabilities.yaml
@@ -0,0 +1,119 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+---
+sdk: ts
+
+min_airflow_version: "3.4"
+
+# Keep in sync with SUPERVISOR_API_VERSION in src/generated/supervisor.ts. The
render hook fails
+# if the two disagree.
+supervisor_schema_version: "2026-10-30"
+
+# The runtime terminates a task with SucceedTask, RetryTask, or TaskState
(failed/removed); it does
+# not yet emit skipped, DeferTask, RescheduleTask, or AwaitInputTask.
+states:
+ success:
+ supported: true
+ since: "3.4"
+ failed:
+ supported: true
+ since: "3.4"
+ up_for_retry:
+ supported: true
+ since: "3.4"
+ note: "RetryTask"
+ skipped:
+ supported: false
+ note: "runtime does not emit TaskState skipped yet"
+ deferred:
+ supported: false
+ note: "runtime does not emit DeferTask yet"
+ up_for_reschedule:
+ supported: false
+ note: "runtime does not emit RescheduleTask yet"
+ awaiting_input:
+ supported: false
+ note: "runtime does not emit AwaitInputTask yet"
+ removed:
+ supported: true
+ since: "3.4"
+
+# Runtime capabilities reflect the task-facing coordinator client surface;
native-Dag authoring is
+# not implemented yet, so every native capability is unsupported.
+capabilities:
+ mixed-lang-stub-target:
+ supported: true
+ since: "3.4"
+ note: "@task.stub"
+ task-logging:
+ supported: true
+ since: "3.4"
+ note: "structured records over the log socket"
+ xcom-read-write:
+ supported: true
+ since: "3.4"
+ note: "getXCom / setXCom"
+ connection-read:
+ supported: true
+ since: "3.4"
+ note: "getConnection"
+ variable-read-write:
+ supported: false
+ note: "getVariable only; no write over the comm socket yet"
+ self-contained-bundle:
+ supported: true
+ since: "3.4"
+ note: "Airflow metadata embedded in the bundle"
+ retry-policy:
+ supported: false
+ note: "no task-facing retry-policy API yet"
+ task-state-store:
+ supported: false
+ note: "no task-facing state-store API yet"
+ asset-state-store:
+ supported: false
+ note: "no task-facing state-store API yet"
+ asset-event-emit:
+ supported: false
+ note: "runtime does not emit asset events yet"
+ asset-event-read:
+ supported: false
+ note: "no task-facing asset-event API yet"
+ native-dag-authoring:
+ supported: false
+ note: "native Dag authoring not implemented yet"
+ task-args:
+ supported: false
+ dag-params:
+ supported: false
+ taskflow-dependencies:
+ supported: false
+ branching:
+ supported: false
+ dag-test:
+ supported: false
+ task-group:
+ supported: false
+ dynamic-task-mapping:
+ supported: false
+ asset-inlets-outlets:
+ supported: false
+ asset-scheduling:
+ supported: false
+ object-store:
+ supported: false
+ note: "no object-storage API yet"