Skip to content

Add Java SDK capability manifest and compatibility matrix - #71151

Open
jason810496 wants to merge 3 commits into
apache:mainfrom
jason810496:ci/java-sdk/compat-matrix
Open

Add Java SDK capability manifest and compatibility matrix#71151
jason810496 wants to merge 3 commits into
apache:mainfrom
jason810496:ci/java-sdk/compat-matrix

Conversation

@jason810496

Copy link
Copy Markdown
Member

Why

The Language SDK conformance spec (#70717) defines which TaskInstance states and capabilities a Language SDK may declare, but nothing lets an SDK say what it actually supports.

This PR makes that support machine-readable and generates the docs from it.

How

conformance.Capabilities            <- the only file you edit
(java-sdk/.../conformance/Capabilities.kt)
  |
  |  generate: ./gradlew :sdk:dumpCapabilities
  |  hook: check-java-sdk-capabilities-in-sync
  v
java-sdk/generated/lang-sdk/capabilities.json
  |
  |  render: scripts/ci/prek/lang_sdk_compat_matrix.py
  |  hook: update-java-sdk-readme-matrix
  |
  +--> java-sdk/README.md            (contributor-facing)
  |
  +--> java-sdk/sdk/module.md        (Dokka module doc -> the published API reference)
  |
  +--> airflow-core/docs/authoring-and-scheduling/language-sdks/index.rst
       (consolidated cross-SDK matrix — TODO, not generated yet)

Both hooks regenerate their target and exit non-zero when it was stale, so a drifted table fails the
build and the fix is to re-stage the regenerated file. A capability landing in the runtime becomes a
one-line edit to the Kotlin constant instead of three tables to update by hand.

Verification

Screenshot 2026-08-05 at 1 24 37 PM
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

The Conformance section of the Language SDK guide defines which TaskInstance
states and capabilities an SDK may declare, but nothing lets an SDK state what it
actually supports. A hand-maintained table in each SDK's docs drifts from its
runtime the moment a capability lands, and readers cannot tell whether a gap is
real or just stale documentation.

Give every Language SDK one validated schema to declare its support in, and one
renderer that turns that declaration into a table, so a published matrix is
derived from the SDK rather than transcribed from it. Conditional native-Dag
capabilities render as "not applicable" rather than unsupported when the SDK does
not author native Dags, matching how the spec gates them.
Readers of the Java SDK docs have no way to tell which parts of the Language SDK
conformance spec the runtime implements. The gaps are real and moving — native Dag
authoring, deferral, and the state stores are not there yet — so an unqualified
"see the conformance spec" overstates what a task can do today.

Let the SDK declare its own support in Kotlin next to the code that implements it,
and generate the tables in the README and the published Dokka API reference from
that declaration. Prek regenerates both and fails on drift, so the matrix cannot
go stale without CI noticing, and a capability landing in the runtime is a
one-line edit rather than three tables to update by hand.
The manifest model sat at default (public) visibility in the stable
org.apache.airflow.sdk.conformance package, so a type that exists purely to emit
a build-time JSON file shipped in the published artifact and in the Dokka API
reference. That would make every later change to the manifest schema a source and
binary compatibility question for SDK users, for no benefit — nothing outside the
Gradle dump task consumes it. Marking it internal removes it from both surfaces,
and the generically named Document becomes CapabilityManifest now that the name is
no longer public.

The rest hardens the surrounding tooling against ways it could mislead:
the sync hook now re-triggers when its own implementation changes, so editing the
checker cannot leave the manifest unverified; an unsupported dimension carrying a
"since" version is rejected rather than silently rendering as unversioned; the
manifest is decoded as the first complete JSON value instead of slicing to the
last brace, which mis-parses if Gradle ever frames the output; and the JDK gate
fails closed when CI is set but empty. The Dokka column-width rules now only raise
a minimum width rather than forbidding wrapping, so they cannot cause overflow in
unrelated tables that a Markdown-generated table gives no way to exclude.
// Keep in sync with airflowSupervisorSchemaVersion in java-sdk/gradle.properties.
private const val SUPERVISOR_SCHEMA_VERSION = "2026-06-16"

private fun yes(note: String = ""): CapabilityEntry = CapabilityEntry(supported = true, since = MIN_AIRFLOW_VERSION, note = note)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private fun yes(note: String = ""): CapabilityEntry = CapabilityEntry(supported = true, since = MIN_AIRFLOW_VERSION, note = note)
private fun yes(note: String = "", since: String = MIN_AIRFLOW_VERSION): CapabilityEntry =
CapabilityEntry(supported = true, since = since, note = note)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants