Skip to content

Drop the Dag inventory from Lang-SDK bundle metadata - #74139

Draft
jason810496 wants to merge 7 commits into
jason/core-taskhandler-refactor/17-run-task-handler-artifact-by-pathfrom
jason/core-taskhandler-refactor/18-bundle-metadata-without-dags
Draft

jason810496 wants to merge 7 commits into
jason/core-taskhandler-refactor/17-run-task-handler-artifact-by-pathfrom
jason/core-taskhandler-refactor/18-bundle-metadata-without-dags

Conversation

@jason810496

@jason810496 jason810496 commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Stack (bottom to top): #73970, #73971, #73972, #73973, #73974, #73975, #73976, #73977, #74030, #74031, #74032, #74033, #74067, #74135, #74136, #74137, #74138, #74139, #74140, #74141

Why

Nothing reads the Dag and task ids that a bundle's metadata lists any more. The Dag processor asks the artifact which task handlers it registers, and the worker runs the file a stub task is bound to (ADR-0014). This PR removes the inventory and what existed only to produce it: the Go packer no longer runs the bundle binary with --airflow-metadata, and the TypeScript packer no longer embeds the handlers it checks. Spec version 1.0 stays, since older manifests still validate.

What changes

The Go packer never runs the binary it packs, so a binary built for any platform packs on any host, with no manifest to capture first:

# before: a binary built for another platform needed a manifest captured by running it there
./my-bundle --airflow-metadata > airflow-metadata.yaml
go tool airflow-go-pack --executable ./my-bundle --source main.go --airflow-metadata airflow-metadata.yaml

# after
GOOS=linux GOARCH=amd64 go build -o my-bundle ./example/bundle
go tool airflow-go-pack --executable ./my-bundle --source ./example/bundle/main.go

The manifest it writes has no dags mapping, and the packer refuses a binary built against another go-sdk than its own:

airflow_bundle_metadata_version: "1.0"
sdk:
  language: "go"
  version: "(devel)"
  supervisor_schema_version: "2026-10-30"
source: "main.go"
digests:
  integrity: "6fce067a..."
  cache: "1f86d77a..."
error: the bundle binary was built against go-sdk v0.1.0, but airflow-go-pack was built against go-sdk v0.2.0; pack with `go tool airflow-go-pack` from the module that built the binary, so both use the same go-sdk
  • Go packer. supervisor_schema_version comes from the packer's own go-sdk, and the SDK version from the binary's build information, which Go keeps in a stripped binary and for any target platform. The two go-sdk builds are accepted without a comparison when go-sdk is the main module of both, as inside the SDK module, because Go stamps the main module's version from version control. Otherwise the version and the replacement must be equal, in build mode too, so a flag after -- such as -modfile cannot change the module graph unnoticed. A binary that does not link go-sdk is refused. The code that ran the binary and decoded its output, the host build for --goos and --goarch, the Dag and task id warnings, the --airflow-metadata flag and the Dag count in the output line are gone.
  • Go bundle binary. --airflow-metadata, --format and DumpAirflowMetadata are removed, so the binary speaks only the coordinator protocol. --comm and --logs are the only names Serve reserves, so a bundle may define flags named airflow-metadata or format of its own. The manifest type loses its Dag inventory.
  • TypeScript. airflow-ts-pack still runs the bundle with --airflow-metadata, for the schema version, the source file of each Dag declared in TypeScript and finalizing those Dags. It still checks the handlers the bundle reports (it serves something, a Dag with no tasks, ids the server would reject) but no longer embeds them: the metadata line has airflow_bundle_metadata_version, sdk, entrypoint_path and dag_source_paths.
  • Schema and specs. airflow-metadata.schema.json no longer requires or describes dags. The executable bundle spec drops the field and its example, says the packer writes the manifest, and says consumers ignore a dags mapping from an older packer. The TypeScript spec shows the fields the encoder writes in place of a source field it does not write, and says an older task_handlers mapping is ignored.
  • Older bundles. A bundle packed before this PR still carries dags (Go) or task_handlers (TypeScript), and every reader accepts it. The TypeScript golden bundle is kept in both forms for the Python reader.
  • Docs. The Go and TypeScript pages, the Language SDK guide, the READMEs, the contributor guide for a new Language SDK, the Justfile and the e2e docstring no longer say the packer runs the binary or that the manifest lists Dag and task ids, and go-sdk ADRs 0001 to 0005 get a status note that --airflow-metadata is retired. ADR-0014 keeps the status Proposed and its Consequences describe what the code does. The Bundle.serve documentation says a stub task whose handler is left out fails the import of its Python Dag file, and a task that still reaches the bundle without its handler is marked removed.
  • Documentation drift fixed on the way. The TypeScript page and README documented a --source pack option that the CLI rejects with Unknown option --source, and the metadata sample of the TypeScript spec showed a source field the encoder does not write. Both are corrected. The spec's container, layout header, source section and reader steps still describe a single source region, which ADR-0015 replaced with one region per Dag file. That is left to a follow-up.
What the next PRs can rely on
  • The Go pack runs no binary. It prints Wrote bundle <path> (sdk=go/<version>), and go tool airflow-go-pack inspect <bundle> prints a manifest without dags. Packing in a module that replaces go-sdk with a local path, such as kubernetes-tests/lang_sdk/go_example, records the version (devel).
  • The TypeScript bundle's //# airflowMetadata= line has no task_handlers. airflow-ts-pack still runs the bundle and prints its pack-time warnings to stderr.
  • No supervisor message changed, so the schema snapshot and the Go models are unchanged.

How to test

(cd go-sdk && go vet ./... && go test ./...)
(cd ts-sdk && pnpm install --frozen-lockfile && pnpm typecheck && pnpm lint && pnpm format:check && pnpm test && pnpm build)
uv run --project task-sdk --with-editable shared/secrets_masker pytest task-sdk/tests/task_sdk/coordinators/ -q
uv run --project airflow-core --with-editable shared/secrets_masker pytest airflow-core/tests/unit/dag_processing/ -q
AIRFLOW_LANG_SDK_REAL_PROBE_TESTS=1 uv run --project airflow-core --with-editable shared/secrets_masker pytest airflow-core/tests/unit/dag_processing/test_task_handler_processor_go.py airflow-core/tests/unit/dag_processing/test_task_handler_processor_ts.py airflow-core/tests/unit/dag_processing/test_task_handler_processor_java.py -v
prek run check-go-example-mod-tidy generate-supervisor-schemas-snapshot check-go-sdk-generated-drift --all-files
(cd kubernetes-tests/lang_sdk/go_example && CGO_ENABLED=0 go tool airflow-go-pack --output /tmp/go_example_bundle . && go tool airflow-go-pack inspect /tmp/go_example_bundle)

Ran:

  • go vet and go test ./... in go-sdk: 13 packages pass, 32 tests in the packer's package. They include a real two-module test: a module that replaces go-sdk with a local path packs with its own go tool and is accepted, a binary built against another go-sdk version is refused with both versions in the error, so is a binary built inside go-sdk when the packer replaces it, and so is a build with -modfile pointing at another go.mod. With an empty Go build cache the packer's package tests take 9 seconds.
  • ts-sdk: pnpm install --frozen-lockfile, typecheck, lint, format check and build pass, and pnpm test passes 611 tests.
  • task-sdk coordinators/: 314 passed. airflow-core dag_processing/: 798 passed, 19 skipped (10 real probe tests, which need AIRFLOW_LANG_SDK_REAL_PROBE_TESTS=1, and 9 that need FabAuthManager, a system test module or a Postgres or MySQL backend).
  • The real probes with Node 22.22.0, pnpm, JDK 11 and Go 1.25: 10 passed (Go 4, TypeScript 4, Java 2). The Go probe validates the packed manifest against the published schema, and the TypeScript probe checks the packed metadata has no task_handlers.
  • kubernetes-tests/lang_sdk/go_example packs with go tool airflow-go-pack, also with --goos linux --goarch amd64, and inspect shows no dags. The packed bundle exits with unknown flag: --airflow-metadata when run with the removed flag. check-go-example-mod-tidy, check-go-sdk-generated-drift and generate-supervisor-schemas-snapshot pass and change nothing.
  • prek run --from-ref <parent> --stage pre-commit passed. mypy-airflow-core, mypy-task-sdk and mypy-airflow-e2e-tests passed with --all-files.
  • The compose e2e modes and the Kubernetes test ran once, on the last layer of the stack, and passed (results in Add e2e tests for checking stub tasks against their task handlers #74140 and Read the k8s Java task handler bundle from S3 and test failure cases #74141). They pack with this PR's packers: the Go pack ran no binary, the Go manifests have no dags and the TypeScript metadata has no task_handlers.

The tests of each behavior change fail without it, checked by reverting the code of its commit: 13 of the Go packer's 32 tests fail against the old packer (one of them with its 4 subtests, and the 3 version tests do not compile without the new check), the 3 bundle binary flag tests fail against the old flags, the 6 TypeScript encoder and pack tests and the Python fixture test fail against the old encoder, and the 4 schema tests fail against the old schema and spec. The tests that read an older bundle, with dags or task_handlers in its manifest (inspect, the executable and Node coordinators, the Node bundle reader), pass before and after: they guard what the PR keeps.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

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

airflow-go-pack no longer runs the bundle binary to read a manifest. The manifest has no Dag inventory, so the packer needs only the SDK block: the supervisor schema version is its own go-sdk's, and the SDK version is read from the binary's build information, which Go keeps in a stripped binary and for any target platform. A binary built against another go-sdk than the packer is refused with both versions in the error, in build mode too, so a flag after -- such as -modfile cannot change the module graph unnoticed. When go-sdk is the main module of both builds, as inside the SDK module, the two are accepted, because Go stamps the main module's version from version control. A cross-built binary packs without a host build, and the --airflow-metadata flag, the Dag and task id checks and the Dag count in the output are gone. A bundle packed earlier, with its dags mapping, still prints with inspect.
Nothing reads the manifest from the bundle binary any more, so the binary speaks only the coordinator protocol. The --airflow-metadata and --format flags, DumpAirflowMetadata and the code that collected the Dag and task ids for them are gone, and the manifest type loses its Dag inventory. A bundle that defines a flag named airflow-metadata or format of its own is no longer refused, since Serve reserves only comm and logs.
airflow-ts-pack still runs the built bundle, for the schema version, the source path of each Dag declared in TypeScript and finalizing those Dags, and it still checks the task handlers the bundle reports: that it serves something, a Dag with no tasks and ids the server would reject. It no longer embeds the handlers in the bundle's metadata, since nothing reads them from the artifact. A bundle packed earlier, with task_handlers in its metadata, still reads, and the golden bundle is kept in both forms for the Python reader.
The airflow-metadata JSON Schema no longer requires or describes a dags mapping, and its description says what the manifest is for now. The executable bundle spec drops the field and its example, says the packer writes the manifest, and says a bundle packed earlier may still carry dags, which consumers ignore. The TypeScript bundle spec shows the fields the encoder writes, entrypoint_path and dag_source_paths, in place of a source field the encoder does not write, and says a task_handlers mapping from an older packer is ignored. The spec version stays 1.0, since an older manifest still validates. A new test validates the manifest without dags, the spec's own example and an older manifest with dags against the schema, and the executable coordinator tests read a bundle with and without the inventory.
The Go page and the Go README describe a packer that never runs the binary: it reads the go-sdk version from the binary's build information, refuses a binary built against another go-sdk version, and packs a binary built for any platform with --executable, so the cross-platform text no longer has the user capture a manifest with --airflow-metadata. The Go, TypeScript and Language SDK pages, the contributor guide for a new Language SDK, the Justfile and the e2e docstring no longer say the manifest holds the Dag and task ids, and the TypeScript README says the packer checks the handlers a bundle reports but does not embed them. go-sdk ADRs 0001 to 0005 get a note that the introspection flag is retired. The TypeScript page and README no longer document a --source pack option, which the CLI rejects.
The Bundle.serve documentation said what is left out of a bundle is not part of it and its tasks are marked removed at runtime. That is true only of a task that still reaches the bundle: a stub task whose handler is left out fails the import of its Python Dag file, since the Dag processor finds no handler for it.
The Consequences now say what the code does. The Go packer runs nothing: it takes the schema version from its own go-sdk and the SDK version from the binary's build information, refuses a binary built against another go-sdk, packs a cross-built binary without a host build, and the bundle binary has no --airflow-metadata flag. The TypeScript packer still runs the bundle but embeds no task_handlers, and dag_source_paths is the one place a Dag id remains in its metadata, for display. A bundle packed earlier may carry dags or task_handlers, which readers ignore, and Java never had an inventory. The status stays Proposed.

This branch has not been deployed

No deployments
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.

1 participant