Skip to content

Add a TypeScript SDK ADR for TaskFlow and native Dag authoring - #72047

Merged
jason810496 merged 6 commits into
apache:mainfrom
jason810496:docs/ts-sdk/adr-taskflow-native-dag
Sep 10, 2026
Merged

jason810496 merged 6 commits into
apache:mainfrom
jason810496:docs/ts-sdk/adr-taskflow-native-dag

Conversation

@jason810496

@jason810496 jason810496 commented Aug 25, 2026 •

Copy link
Copy Markdown
Member

Write the design for TaskFlow and native Dag for the TS-SDK up front, to settle the design early and surface as much context as possible for reviewers on the actual implementation PRs.


Was generative AI tooling used to co-author this PR?

…ge and native Dags

The cross-SDK decisions on argument binding and the TaskFlow Dag DSL were
settled against Go and Java, both of which can recover a task function's
parameter list, Go with reflect at run time, Java with an annotation
processor at compile time. TypeScript can do neither: its idiomatic handler
takes a single destructured object whose parameter positions do not survive
to run time. That constraint changes the answer to several questions the
earlier records treat as settled, so the reasoning needs to live somewhere
reviewers and future SDK authors can find it.

The mixed-language argument-binding surface and native-Dag authoring are
different enough problems to warrant separate ADRs. ADR-0001 covers wiring
a TypeScript task body under a Python-declared Dag (MixedLangDag); ADR-0002
covers a Dag whose graph and task bodies are both TypeScript (Dag). Each
leads with the call-site code a user actually writes, and each records the
ctx/client-as-getters question as open rather than settled, since resolving
it would also reshape the binding mechanism and open the door to positional
wiring.
@jason810496 jason810496 self-assigned this Aug 25, 2026
@jason810496 jason810496 added the type:doc-only Changelog: Doc Only label Aug 25, 2026

@pierrejeambrun pierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the well though documents.

Just a few questions / suggestions.

Comment thread ts-sdk/adr/0002-native-dag-interface.md Resolved
Comment thread ts-sdk/adr/0002-native-dag-interface.md Resolved
Comment thread ts-sdk/adr/0002-native-dag-interface.md Resolved
@jason810496
jason810496 marked this pull request as draft September 7, 2026 02:14
jason810496 added a commit to jason810496/airflow that referenced this pull request Sep 8, 2026
The review on apache#72047 found three things these ADRs described but should
not have. The SDK's context and client shared a handler's single argument
object, so every typed handler had to intersect its own data type with the
SDK's. A mixed-language Dag was authored through a second Dag class, when
TypeScript declares no Dag at all in that mode. And a graph whose every
edge carried data had no way to express an edge that carries none, which
left a fan-in of order-only dependencies unanswerable and pushed the
examples into nesting that stops reading well before a Dag grows large.

Recording what the review arrived at keeps these documents a reference for
the interface the SDK will ship, before either interface reaches users.
The review on apache#72047 found three things these ADRs described but should
not have. The SDK's context and client shared a handler's single argument
object, so every typed handler had to intersect its own data type with the
SDK's. A mixed-language Dag was authored through a second Dag class, when
TypeScript declares no Dag at all in that mode. And a graph whose every
edge carried data had no way to express an edge that carries none, which
left a fan-in of order-only dependencies unanswerable and pushed the
examples into nesting that stops reading well before a Dag grows large.

Recording what the review arrived at keeps these documents a reference for
the interface the SDK will ship, before either interface reaches users.
@jason810496
jason810496 force-pushed the docs/ts-sdk/adr-taskflow-native-dag branch from ffb564e to de939e2 Compare September 8, 2026 07:02

@jason810496 jason810496 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks @pierrejeambrun for the review.

Comment thread ts-sdk/adr/0002-native-dag-interface.md Resolved
Comment thread ts-sdk/adr/0002-native-dag-interface.md Resolved
@jason810496
jason810496 marked this pull request as ready for review September 8, 2026 07:25
@jason810496
jason810496 marked this pull request as draft September 9, 2026 05:27
A bundle usually provides both native Dags and bodies for tasks Python
declares, and the earlier draft made it describe itself twice: one verb
per kind, with a collector named after only one of them. Naming that
collector Bundle, letting a single register call take either kind, and
hanging serve off the same object leaves an author with one thing to
hold and one place to look.

StubHandler says what a mixed-language registration is - a body for a
Python @task.stub task - instead of borrowing the word Dag for something
TypeScript does not own.

Both records also open with their decision now, with the
AsyncLocalStorage dispatch scope, the name-folding proxy, and the rest
of the mechanics moved to an appendix, so a reviewer can see what was
decided without reading how it works first.
The example declared a bare Dag and bare tasks, which reads as a more
verbose way to write the mixed-language case. Carrying a schedule on the
Dag and retries on a task shows the reason the native mode exists: the
Dag file owns the configuration a Python stub Dag would otherwise own,
and a handler for a Python-declared task can carry neither.

The fields are recorded as forward-looking. DagSpec and TaskSpec accept
only the empty object today, so the record says where an author writes
this configuration without claiming to choose the fields themselves.
@jason810496
jason810496 marked this pull request as ready for review September 9, 2026 08:24
The name should say what the SDK registers, and what it registers is a
task: Python declares it with @task.stub, gives it a task_id, and the SDK
supplies only its body. "Handler" described the callable rather than the
thing it stands for, leaving these docs using a word no other part of
Airflow uses for a task.

@pierrejeambrun pierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

Python declares the task; an SDK supplies only its body. Naming the SDK
object after the task overloads a word the native surface already uses for
something the user creates with dag.Task, so it names the role the SDK
actually plays instead.
@jason810496
jason810496 merged commit 1952520 into apache:main Sep 10, 2026
65 checks passed
imrichardwu pushed a commit to imrichardwu/airflow that referenced this pull request Sep 11, 2026
…e#72047)

* Add TypeScript SDK ADRs for TaskFlow argument binding on mixed-language and native Dags

The cross-SDK decisions on argument binding and the TaskFlow Dag DSL were
settled against Go and Java, both of which can recover a task function's
parameter list, Go with reflect at run time, Java with an annotation
processor at compile time. TypeScript can do neither: its idiomatic handler
takes a single destructured object whose parameter positions do not survive
to run time. That constraint changes the answer to several questions the
earlier records treat as settled, so the reasoning needs to live somewhere
reviewers and future SDK authors can find it.

The mixed-language argument-binding surface and native-Dag authoring are
different enough problems to warrant separate ADRs. ADR-0001 covers wiring
a TypeScript task body under a Python-declared Dag (MixedLangDag); ADR-0002
covers a Dag whose graph and task bodies are both TypeScript (Dag). Each
leads with the call-site code a user actually writes, and each records the
ctx/client-as-getters question as open rather than settled, since resolving
it would also reshape the binding mechanism and open the door to positional
wiring.

* Record the reviewed TypeScript SDK task and Dag authoring interfaces

The review on apache#72047 found three things these ADRs described but should
not have. The SDK's context and client shared a handler's single argument
object, so every typed handler had to intersect its own data type with the
SDK's. A mixed-language Dag was authored through a second Dag class, when
TypeScript declares no Dag at all in that mode. And a graph whose every
edge carried data had no way to express an edge that carries none, which
left a fan-in of order-only dependencies unanswerable and pushed the
examples into nesting that stops reading well before a Dag grows large.

Recording what the review arrived at keeps these documents a reference for
the interface the SDK will ship, before either interface reaches users.

* Collapse the TypeScript SDK's registration surface onto the bundle

A bundle usually provides both native Dags and bodies for tasks Python
declares, and the earlier draft made it describe itself twice: one verb
per kind, with a collector named after only one of them. Naming that
collector Bundle, letting a single register call take either kind, and
hanging serve off the same object leaves an author with one thing to
hold and one place to look.

StubHandler says what a mixed-language registration is - a body for a
Python @task.stub task - instead of borrowing the word Dag for something
TypeScript does not own.

Both records also open with their decision now, with the
AsyncLocalStorage dispatch scope, the name-folding proxy, and the rest
of the mechanics moved to an appendix, so a reviewer can see what was
decided without reading how it works first.

* Show what a native TypeScript Dag owns that a stub handler cannot

The example declared a bare Dag and bare tasks, which reads as a more
verbose way to write the mixed-language case. Carrying a schedule on the
Dag and retries on a task shows the reason the native mode exists: the
Dag file owns the configuration a Python stub Dag would otherwise own,
and a handler for a Python-declared task can carry neither.

The fields are recorded as forward-looking. DagSpec and TaskSpec accept
only the empty object today, so the record says where an author writes
this configuration without claiming to choose the fields themselves.

* Rename StubHandler to StubTask in the TypeScript SDK ADRs

The name should say what the SDK registers, and what it registers is a
task: Python declares it with @task.stub, gives it a task_id, and the SDK
supplies only its body. "Handler" described the callable rather than the
thing it stands for, leaving these docs using a word no other part of
Airflow uses for a task.

* Rename StubTask to TaskHandler in the TypeScript SDK ADRs

Python declares the task; an SDK supplies only its body. Naming the SDK
object after the task overloads a word the native surface already uses for
something the user creates with dag.Task, so it names the role the SDK
actually plays instead.
xvega pushed a commit to xvega/airflow that referenced this pull request Sep 13, 2026
…e#72047)

* Add TypeScript SDK ADRs for TaskFlow argument binding on mixed-language and native Dags

The cross-SDK decisions on argument binding and the TaskFlow Dag DSL were
settled against Go and Java, both of which can recover a task function's
parameter list, Go with reflect at run time, Java with an annotation
processor at compile time. TypeScript can do neither: its idiomatic handler
takes a single destructured object whose parameter positions do not survive
to run time. That constraint changes the answer to several questions the
earlier records treat as settled, so the reasoning needs to live somewhere
reviewers and future SDK authors can find it.

The mixed-language argument-binding surface and native-Dag authoring are
different enough problems to warrant separate ADRs. ADR-0001 covers wiring
a TypeScript task body under a Python-declared Dag (MixedLangDag); ADR-0002
covers a Dag whose graph and task bodies are both TypeScript (Dag). Each
leads with the call-site code a user actually writes, and each records the
ctx/client-as-getters question as open rather than settled, since resolving
it would also reshape the binding mechanism and open the door to positional
wiring.

* Record the reviewed TypeScript SDK task and Dag authoring interfaces

The review on apache#72047 found three things these ADRs described but should
not have. The SDK's context and client shared a handler's single argument
object, so every typed handler had to intersect its own data type with the
SDK's. A mixed-language Dag was authored through a second Dag class, when
TypeScript declares no Dag at all in that mode. And a graph whose every
edge carried data had no way to express an edge that carries none, which
left a fan-in of order-only dependencies unanswerable and pushed the
examples into nesting that stops reading well before a Dag grows large.

Recording what the review arrived at keeps these documents a reference for
the interface the SDK will ship, before either interface reaches users.

* Collapse the TypeScript SDK's registration surface onto the bundle

A bundle usually provides both native Dags and bodies for tasks Python
declares, and the earlier draft made it describe itself twice: one verb
per kind, with a collector named after only one of them. Naming that
collector Bundle, letting a single register call take either kind, and
hanging serve off the same object leaves an author with one thing to
hold and one place to look.

StubHandler says what a mixed-language registration is - a body for a
Python @task.stub task - instead of borrowing the word Dag for something
TypeScript does not own.

Both records also open with their decision now, with the
AsyncLocalStorage dispatch scope, the name-folding proxy, and the rest
of the mechanics moved to an appendix, so a reviewer can see what was
decided without reading how it works first.

* Show what a native TypeScript Dag owns that a stub handler cannot

The example declared a bare Dag and bare tasks, which reads as a more
verbose way to write the mixed-language case. Carrying a schedule on the
Dag and retries on a task shows the reason the native mode exists: the
Dag file owns the configuration a Python stub Dag would otherwise own,
and a handler for a Python-declared task can carry neither.

The fields are recorded as forward-looking. DagSpec and TaskSpec accept
only the empty object today, so the record says where an author writes
this configuration without claiming to choose the fields themselves.

* Rename StubHandler to StubTask in the TypeScript SDK ADRs

The name should say what the SDK registers, and what it registers is a
task: Python declares it with @task.stub, gives it a task_id, and the SDK
supplies only its body. "Handler" described the callable rather than the
thing it stands for, leaving these docs using a word no other part of
Airflow uses for a task.

* Rename StubTask to TaskHandler in the TypeScript SDK ADRs

Python declares the task; an SDK supplies only its body. Naming the SDK
object after the task overloads a word the native surface already uses for
something the user creates with dag.Task, so it names the role the SDK
actually plays instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ts-sdk type:doc-only Changelog: Doc Only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants