kaxil commented on code in PR #70805:
URL: https://github.com/apache/airflow/pull/70805#discussion_r3690030816


##########
task-sdk/src/airflow/sdk/coordinators/_subprocess.py:
##########
@@ -414,18 +539,20 @@ def execute_task(
         subprocess_logs_to_stdout: bool,
         **kwargs,
     ) -> BaseCoordinator.ExecutionResult:
-        command, subprocess_schema_version = 
self._build_execute_task_command(what=what)
-        process = _PopenActivitySubprocess.start(
-            what=what,
-            dag_rel_path=dag_rel_path,
-            bundle_info=bundle_info,
-            client=client,
-            logger=logger,
-            subprocess_logs_to_stdout=subprocess_logs_to_stdout,
-            sentry_integration=sentry_integration,
-            command=command,
-            subprocess_schema_version=subprocess_schema_version,
-            startup_timeout=self.task_startup_timeout,
-        )
-        exit_code = process.wait()
-        return self.ExecutionResult(exit_code, process.final_state)
+        with self._set_current_bundle(bundle_info):

Review Comment:
   The Python path holds `BundleVersionLock` around the whole run 
(`task_runner.py:2375`), but that lock lives in `main()`, not in `parse()`, so 
extracting `initialize_ti_bundle` gives this path the materialization without 
the protection.
   
   Two consequences. `BundleUsageTrackingManager._remove_stale_bundle` 
(`bundles/base.py:150-177`) takes `flock(LOCK_EX|LOCK_NB)` and `rmtree`s the 
version directory, backing off only on `BlockingIOError`, and Celery workers 
run that cleanup loop in their own subprocess (`celery_command.py:131-138`) on 
the same host as the JVM. In a mixed Python + language deployment a version 
that already has a tracking file from an earlier Python task can be deleted 
while a JVM is still lazily loading classes off that classpath. Separately, on 
a coordinator-only worker no tracking file is ever written, so 
`_find_all_tracking_files` returns nothing and every version cloned here is 
invisible to cleanup and never reclaimed.
   
   Could `_init_root_source` return the bundle rather than just the path, and 
this block hold `BundleVersionLock(bundle_name=bundle.name, 
bundle_version=bundle.version)` across `start()` and `wait()`? Making 
`_set_current_bundle` an `ExitStack` that also enters the lock would mirror 
`task_runner.main()`.



##########
task-sdk/src/airflow/sdk/coordinators/_subprocess.py:
##########
@@ -385,23 +399,134 @@ class SubprocessCoordinator(BaseCoordinator):
     :param task_startup_timeout: Maximum time the coordinator waits for the
         subprocess to connect to both servers, in seconds. The default is 10
         seconds.
+    :param dag_bundle_name: Locate artifacts through a configured Dag bundle 
rather
+        than an explicit root. Mutually exclusive with the subclass's explicit 
root;
+        if neither is set, the task's own bundle is used. A named bundle 
resolves to
+        its latest version; the task's own bundle is pinned to the run's 
version.
     """
 
     task_startup_timeout: float = 10.0
+    dag_bundle_name: str | None = None
+
+    # Classified once at construction by :meth:`_classify_artifact_source` and
+    # dispatched on by :meth:`_init_root_source` at execute time.
+    _artifact_source: _ArtifactSource | None = attrs.field(init=False, 
default=None)
+    # The subclass's explicit root, recorded at construction so the base can
+    # resolve roots without knowing the subclass field name.
+    _configured_roots: list[pathlib.Path] = attrs.field(init=False, 
factory=list)
+    # The task's own bundle, bound for the duration of a single 
:meth:`execute_task`
+    # call by :meth:`_set_current_bundle` so :meth:`_init_root_source` can 
resolve
+    # co-located artifacts.
+    _active_bundle_info: BundleInfo | None = attrs.field(init=False, 
default=None)
+
+    def _classify_artifact_source(self, configured: Sequence[pathlib.Path], *, 
root_kwarg: str) -> None:
+        """
+        Classify and validate how this coordinator locates artifacts 
(construction time).
+
+        Subclasses call this from ``__attrs_post_init__`` with their own root
+        field. It rejects setting both an explicit root and 
``dag_bundle_name``,
+        fails fast when ``dag_bundle_name`` names a bundle that is not 
configured,
+        and records the resulting :class:`_ArtifactSource` and explicit root.
+        """
+        if configured and self.dag_bundle_name is not None:
+            raise ValueError(
+                f"Set at most one of {root_kwarg!r} or 'dag_bundle_name': 
{root_kwarg!r} for an "
+                f"explicit path, 'dag_bundle_name' for a configured Dag 
bundle, or leave both "
+                f"unset to scan the task's own bundle."
+            )
+        if configured:
+            source = _ArtifactSource.EXPLICIT_ROOT
+            self._configured_roots = list(configured)
+        elif self.dag_bundle_name is not None:
+            source = _ArtifactSource.NAMED_BUNDLE
+            from airflow.dag_processing.bundles.manager import 
DagBundlesManager  # noqa: SDK002
+
+            if not 
DagBundlesManager.is_bundle_configured(self.dag_bundle_name):
+                raise ValueError(
+                    f"Coordinator 'dag_bundle_name' references unconfigured 
Dag bundle "
+                    f"{self.dag_bundle_name!r}."
+                )
+        else:
+            source = _ArtifactSource.TASK_BUNDLE
+
+        self._artifact_source = source
+        details: dict[str, str | list[str]] = {"mode": source.name}
+        if self.dag_bundle_name is not None:
+            details["dag_bundle_name"] = self.dag_bundle_name
+        if self._configured_roots:
+            details["configured_roots"] = [str(root) for root in 
self._configured_roots]
+        log.debug("Coordinator artifact source selected", **details)
+
+    def _init_root_source(self) -> list[pathlib.Path]:
+        """
+        Resolve the directories to scan for artifacts for the current task.
+
+        Dispatches on the :class:`_ArtifactSource` classified at construction.
+        An explicit root is returned as-is (no Dag bundle is resolved); 
otherwise
+        the root is a Dag bundle's materialized path — the named
+        ``dag_bundle_name`` bundle at its latest version, or the task's own
+        bundle pinned to the run's version. Called by :meth:`execute_task`, 
which
+        forwards the result to :meth:`_build_execute_task_command`.
+        """
+        if self._artifact_source is _ArtifactSource.EXPLICIT_ROOT:
+            return self._configured_roots
+
+        if self._artifact_source is _ArtifactSource.NAMED_BUNDLE:
+            from airflow.sdk.api.datamodels._generated import BundleInfo
+
+            # NAMED_BUNDLE implies dag_bundle_name is set.
+            target = BundleInfo(name=cast("str", self.dag_bundle_name))

Review Comment:
   `BundleInfo` with no version resolves to the shared mutable checkout rather 
than a per-version tree. `GitDagBundle.__init__` sets `repo_path = base_dir / 
"tracking_repo"` when version is falsy (`providers/git/.../git.py:84-87`), and 
`_initialize()` falls through to `refresh()`, which fetches and then does 
`head.reset(target, index=True, working_tree=True)`.
   
   With `worker_concurrency > 1`, task B's startup hard-resets the tree that 
task A's JVM is reading its JARs from. `bundle.lock()` covers the refresh 
itself but not the subsequent use, and `BundleVersionLock.acquire()` returns 
early when version is falsy (`base.py:492`), so this mode cannot be protected 
by the existing mechanism at all. The same exposure applies to the task-bundle 
mode whenever the run's `bundle_version` is None.
   
   Would resolving `get_current_version()` once and re-resolving with that 
concrete version work? That gives a `versions/<sha>` tree a lock can actually 
protect. It also pulls the two network fetches out of this path, which today 
run before `_PopenActivitySubprocess.start` and so are not covered by 
`task_startup_timeout`.



##########
task-sdk/src/airflow/sdk/coordinators/_subprocess.py:
##########
@@ -385,23 +399,134 @@ class SubprocessCoordinator(BaseCoordinator):
     :param task_startup_timeout: Maximum time the coordinator waits for the
         subprocess to connect to both servers, in seconds. The default is 10
         seconds.
+    :param dag_bundle_name: Locate artifacts through a configured Dag bundle 
rather
+        than an explicit root. Mutually exclusive with the subclass's explicit 
root;
+        if neither is set, the task's own bundle is used. A named bundle 
resolves to
+        its latest version; the task's own bundle is pinned to the run's 
version.
     """
 
     task_startup_timeout: float = 10.0
+    dag_bundle_name: str | None = None
+
+    # Classified once at construction by :meth:`_classify_artifact_source` and
+    # dispatched on by :meth:`_init_root_source` at execute time.
+    _artifact_source: _ArtifactSource | None = attrs.field(init=False, 
default=None)
+    # The subclass's explicit root, recorded at construction so the base can
+    # resolve roots without knowing the subclass field name.
+    _configured_roots: list[pathlib.Path] = attrs.field(init=False, 
factory=list)
+    # The task's own bundle, bound for the duration of a single 
:meth:`execute_task`
+    # call by :meth:`_set_current_bundle` so :meth:`_init_root_source` can 
resolve
+    # co-located artifacts.
+    _active_bundle_info: BundleInfo | None = attrs.field(init=False, 
default=None)
+
+    def _classify_artifact_source(self, configured: Sequence[pathlib.Path], *, 
root_kwarg: str) -> None:
+        """
+        Classify and validate how this coordinator locates artifacts 
(construction time).
+
+        Subclasses call this from ``__attrs_post_init__`` with their own root
+        field. It rejects setting both an explicit root and 
``dag_bundle_name``,
+        fails fast when ``dag_bundle_name`` names a bundle that is not 
configured,
+        and records the resulting :class:`_ArtifactSource` and explicit root.
+        """
+        if configured and self.dag_bundle_name is not None:
+            raise ValueError(
+                f"Set at most one of {root_kwarg!r} or 'dag_bundle_name': 
{root_kwarg!r} for an "
+                f"explicit path, 'dag_bundle_name' for a configured Dag 
bundle, or leave both "
+                f"unset to scan the task's own bundle."
+            )
+        if configured:
+            source = _ArtifactSource.EXPLICIT_ROOT
+            self._configured_roots = list(configured)
+        elif self.dag_bundle_name is not None:
+            source = _ArtifactSource.NAMED_BUNDLE
+            from airflow.dag_processing.bundles.manager import 
DagBundlesManager  # noqa: SDK002
+
+            if not 
DagBundlesManager.is_bundle_configured(self.dag_bundle_name):
+                raise ValueError(
+                    f"Coordinator 'dag_bundle_name' references unconfigured 
Dag bundle "
+                    f"{self.dag_bundle_name!r}."
+                )
+        else:
+            source = _ArtifactSource.TASK_BUNDLE
+
+        self._artifact_source = source
+        details: dict[str, str | list[str]] = {"mode": source.name}
+        if self.dag_bundle_name is not None:
+            details["dag_bundle_name"] = self.dag_bundle_name
+        if self._configured_roots:
+            details["configured_roots"] = [str(root) for root in 
self._configured_roots]
+        log.debug("Coordinator artifact source selected", **details)
+
+    def _init_root_source(self) -> list[pathlib.Path]:
+        """
+        Resolve the directories to scan for artifacts for the current task.
+
+        Dispatches on the :class:`_ArtifactSource` classified at construction.
+        An explicit root is returned as-is (no Dag bundle is resolved); 
otherwise
+        the root is a Dag bundle's materialized path — the named
+        ``dag_bundle_name`` bundle at its latest version, or the task's own
+        bundle pinned to the run's version. Called by :meth:`execute_task`, 
which
+        forwards the result to :meth:`_build_execute_task_command`.
+        """
+        if self._artifact_source is _ArtifactSource.EXPLICIT_ROOT:
+            return self._configured_roots
+
+        if self._artifact_source is _ArtifactSource.NAMED_BUNDLE:
+            from airflow.sdk.api.datamodels._generated import BundleInfo
+
+            # NAMED_BUNDLE implies dag_bundle_name is set.
+            target = BundleInfo(name=cast("str", self.dag_bundle_name))
+        elif self._artifact_source is _ArtifactSource.TASK_BUNDLE:
+            if self._active_bundle_info is None:
+                raise RuntimeError("_init_root_source requires an active task; 
call it during execute_task.")
+            target = self._active_bundle_info
+        else:
+            raise RuntimeError(

Review Comment:
   Classification is opt-in per subclass, so a third-party subclass that 
defines `__attrs_post_init__` for its own reasons and does not call 
`_classify_artifact_source` constructs fine and then dies here on every task.
   
   Could the base define `__attrs_post_init__` itself, calling an overridable 
`_explicit_artifact_roots()` that defaults to empty, plus a `_root_kwarg` 
ClassVar? A subclass that overrides nothing would then land on the task-bundle 
mode, which is the documented default, instead of a runtime error.



##########
task-sdk/src/airflow/sdk/coordinators/_subprocess.py:
##########
@@ -385,23 +399,134 @@ class SubprocessCoordinator(BaseCoordinator):
     :param task_startup_timeout: Maximum time the coordinator waits for the
         subprocess to connect to both servers, in seconds. The default is 10
         seconds.
+    :param dag_bundle_name: Locate artifacts through a configured Dag bundle 
rather
+        than an explicit root. Mutually exclusive with the subclass's explicit 
root;
+        if neither is set, the task's own bundle is used. A named bundle 
resolves to
+        its latest version; the task's own bundle is pinned to the run's 
version.
     """
 
     task_startup_timeout: float = 10.0
+    dag_bundle_name: str | None = None

Review Comment:
   `dag_bundle_name` does not appear in any `.rst`, and the three config tables 
still list the root parameter as *(required)*: `java.rst:665`, `go.rst:427`, 
`typescript.rst:272`. Those become wrong on merge. The three-mode semantics 
live only in this docstring, so there is nowhere a user can read when to pick 
co-located over named-bundle over explicit root.
   
   `contributing-docs/30_new_language_sdk.rst:107` also documents the old hook 
signature verbatim (`def _build_execute_task_command(self, *, what: 
TaskInstanceDTO) -> tuple[list[str], str]`), which this PR changes. task-sdk 
1.3.0 shipped that signature, though the docs pages do carry `|experimental|` 
(java.rst:23), so this is more about whether you want to call out the break 
than a blocker.



##########
task-sdk/tests/task_sdk/coordinators/executable/test_coordinator.py:
##########
@@ -370,13 +370,42 @@ def test_executables_root_accepts_list(self, tmp_path):
         coordinator = ExecutableCoordinator(executables_root=[str(tmp_path), 
other])
         assert coordinator.executables_root == [tmp_path, other]
 
-    def test_executables_root_required(self):
-        with pytest.raises(TypeError, match="executables_root"):
-            ExecutableCoordinator()
-
-    def test_executables_root_must_be_non_empty(self):
-        with pytest.raises(ValueError, match="executables_root"):
-            ExecutableCoordinator(executables_root=None)
+    def test_executables_root_optional_defaults_to_empty(self):
+        # Neither an explicit root nor dag_bundle_name: co-located mode, valid.
+        coordinator = ExecutableCoordinator()
+        assert coordinator.executables_root == []
+        assert coordinator.dag_bundle_name is None
+
+    def test_none_executables_root_normalized_to_empty(self):
+        coordinator = ExecutableCoordinator(executables_root=None)
+        assert coordinator.executables_root == []
+
+    def test_root_and_dag_bundle_name_are_mutually_exclusive(self, tmp_path):
+        with pytest.raises(ValueError, match="at most one of 
'executables_root' or 'dag_bundle_name'"):
+            ExecutableCoordinator(executables_root=[tmp_path], 
dag_bundle_name="artifacts")
+
+    @patch("airflow.dag_processing.bundles.manager.DagBundlesManager")
+    def test_unconfigured_dag_bundle_name_raises(self, mock_manager):
+        mock_manager.is_bundle_configured.return_value = False
+        with pytest.raises(ValueError, match="unconfigured Dag bundle 
'ghost'"):
+            ExecutableCoordinator(dag_bundle_name="ghost")
+
+    @patch("airflow.dag_processing.bundles.manager.DagBundlesManager")
+    def test_configured_dag_bundle_name_accepted(self, mock_manager):
+        mock_manager.is_bundle_configured.return_value = True
+        coordinator = ExecutableCoordinator(dag_bundle_name="artifacts")
+        assert coordinator.dag_bundle_name == "artifacts"
+        mock_manager.is_bundle_configured.assert_called_once_with("artifacts")
+
+    def test_build_command_scans_given_roots(self, tmp_path):

Review Comment:
   This builds the coordinator with `executables_root=[tmp_path]` and then 
hands it `roots=[tmp_path]`, so it passes whether `_build_execute_task_command` 
consumes `roots` or ignores it and reads `self.executables_root`. It passes 
against main-shaped code too, so the comment above it asserts something the 
test does not check. Constructing in co-located mode 
(`ExecutableCoordinator()`) and passing the populated directory as `roots` 
would make it fail without the change. Same shape in the java and node copies.
   
   Separately, nothing exercises the task-bundle mode through `execute_task`. 
`_StubSubprocessCoordinator` is pinned to the explicit-root mode and ignores 
`roots`, and the `TestInitRootSource` tests set `_artifact_source` directly, so 
the wiring that binds `bundle_info` and forwards resolved roots is not covered.



##########
task-sdk/src/airflow/sdk/coordinators/java/coordinator.py:
##########
@@ -200,16 +201,21 @@ class JavaCoordinator(SubprocessCoordinator):
     jvm_args: list[str] = attrs.field(factory=list)
     jars_root: list[pathlib.Path] = attrs.field(
         converter=convert_roots,
-        validator=attrs.validators.min_len(1),
+        factory=list,

Review Comment:
   Dropping `min_len(1)` means a deployment whose `jars_root` templates to 
empty or None stops failing at construction and silently switches to scanning 
the whole Dag bundle.
   
   For Java that is worse than a slow scan. With `main_class` unset, 
`_JarInfo.find` takes the first JAR carrying a `Main-Class` in walk order and 
`_calculate_classpath` puts everything it found on the classpath. 
`java.rst:678` already warns the result is non-deterministic when multiple 
executable JARs are present, and co-located mode makes that the normal case. 
Requiring `main_class` when there is no explicit root would cover the Java side.
   
   More broadly, is the implicit default worth it? An empty root now means both 
"use the task's bundle" and "my config rendered empty". Something like 
`use_task_bundle=True` would keep the new mode while letting a broken config 
keep failing fast.



##########
airflow-core/src/airflow/dag_processing/bundles/manager.py:
##########
@@ -427,6 +427,11 @@ def get_bundle(
             name=name, version=version, version_data=version_data, 
**cfg_bundle.kwargs
         )
 
+    @classmethod
+    def is_bundle_configured(cls, name: str) -> bool:
+        """Return whether *name* is a configured Dag bundle, without 
constructing the bundle."""
+        return name in cls()._bundle_config

Review Comment:
   `cls()` runs a full `parse_config()` on every call: `__init__` always calls 
it (`manager.py:228-231`) and the `if self._bundle_config: return` guard cannot 
fire on a fresh instance. That `import_string`s every configured bundle class, 
and with `[core] load_examples` on it also walks `ProvidersManager()`.
   
   The part that bites is the error surface. `parse_config` raises 
`AirflowConfigException` for a duplicate or reserved name and lets 
`ImportError` escape, out of the coordinator's `__attrs_post_init__`. 
`CoordinatorManager.for_queue` catches `ImportError` and re-raises 
`InvalidCoordinatorError("Cannot import coordinator ...")` 
(`execution_time/coordinator.py:289`), pointing the operator at the wrong 
config key. Worth catching both here and re-raising with the bundle name so 
"bundle not configured" stays distinguishable from "the bundle config list does 
not load".



##########
task-sdk/src/airflow/sdk/coordinators/_subprocess.py:
##########
@@ -385,23 +399,134 @@ class SubprocessCoordinator(BaseCoordinator):
     :param task_startup_timeout: Maximum time the coordinator waits for the
         subprocess to connect to both servers, in seconds. The default is 10
         seconds.
+    :param dag_bundle_name: Locate artifacts through a configured Dag bundle 
rather
+        than an explicit root. Mutually exclusive with the subclass's explicit 
root;
+        if neither is set, the task's own bundle is used. A named bundle 
resolves to
+        its latest version; the task's own bundle is pinned to the run's 
version.
     """
 
     task_startup_timeout: float = 10.0
+    dag_bundle_name: str | None = None
+
+    # Classified once at construction by :meth:`_classify_artifact_source` and
+    # dispatched on by :meth:`_init_root_source` at execute time.
+    _artifact_source: _ArtifactSource | None = attrs.field(init=False, 
default=None)
+    # The subclass's explicit root, recorded at construction so the base can
+    # resolve roots without knowing the subclass field name.
+    _configured_roots: list[pathlib.Path] = attrs.field(init=False, 
factory=list)
+    # The task's own bundle, bound for the duration of a single 
:meth:`execute_task`
+    # call by :meth:`_set_current_bundle` so :meth:`_init_root_source` can 
resolve
+    # co-located artifacts.
+    _active_bundle_info: BundleInfo | None = attrs.field(init=False, 
default=None)
+
+    def _classify_artifact_source(self, configured: Sequence[pathlib.Path], *, 
root_kwarg: str) -> None:
+        """
+        Classify and validate how this coordinator locates artifacts 
(construction time).
+
+        Subclasses call this from ``__attrs_post_init__`` with their own root
+        field. It rejects setting both an explicit root and 
``dag_bundle_name``,
+        fails fast when ``dag_bundle_name`` names a bundle that is not 
configured,
+        and records the resulting :class:`_ArtifactSource` and explicit root.
+        """
+        if configured and self.dag_bundle_name is not None:
+            raise ValueError(
+                f"Set at most one of {root_kwarg!r} or 'dag_bundle_name': 
{root_kwarg!r} for an "
+                f"explicit path, 'dag_bundle_name' for a configured Dag 
bundle, or leave both "
+                f"unset to scan the task's own bundle."
+            )
+        if configured:
+            source = _ArtifactSource.EXPLICIT_ROOT
+            self._configured_roots = list(configured)
+        elif self.dag_bundle_name is not None:
+            source = _ArtifactSource.NAMED_BUNDLE
+            from airflow.dag_processing.bundles.manager import 
DagBundlesManager  # noqa: SDK002
+
+            if not 
DagBundlesManager.is_bundle_configured(self.dag_bundle_name):
+                raise ValueError(
+                    f"Coordinator 'dag_bundle_name' references unconfigured 
Dag bundle "
+                    f"{self.dag_bundle_name!r}."
+                )
+        else:
+            source = _ArtifactSource.TASK_BUNDLE
+
+        self._artifact_source = source
+        details: dict[str, str | list[str]] = {"mode": source.name}
+        if self.dag_bundle_name is not None:
+            details["dag_bundle_name"] = self.dag_bundle_name
+        if self._configured_roots:
+            details["configured_roots"] = [str(root) for root in 
self._configured_roots]
+        log.debug("Coordinator artifact source selected", **details)
+
+    def _init_root_source(self) -> list[pathlib.Path]:
+        """
+        Resolve the directories to scan for artifacts for the current task.
+
+        Dispatches on the :class:`_ArtifactSource` classified at construction.
+        An explicit root is returned as-is (no Dag bundle is resolved); 
otherwise
+        the root is a Dag bundle's materialized path — the named
+        ``dag_bundle_name`` bundle at its latest version, or the task's own
+        bundle pinned to the run's version. Called by :meth:`execute_task`, 
which
+        forwards the result to :meth:`_build_execute_task_command`.
+        """
+        if self._artifact_source is _ArtifactSource.EXPLICIT_ROOT:
+            return self._configured_roots
+
+        if self._artifact_source is _ArtifactSource.NAMED_BUNDLE:
+            from airflow.sdk.api.datamodels._generated import BundleInfo
+
+            # NAMED_BUNDLE implies dag_bundle_name is set.
+            target = BundleInfo(name=cast("str", self.dag_bundle_name))
+        elif self._artifact_source is _ArtifactSource.TASK_BUNDLE:
+            if self._active_bundle_info is None:
+                raise RuntimeError("_init_root_source requires an active task; 
call it during execute_task.")
+            target = self._active_bundle_info
+        else:
+            raise RuntimeError(
+                "Coordinator artifact source was not classified; call 
_classify_artifact_source first."
+            )
+
+        # Lazy import: task_runner is a heavy module and importing it at module
+        # load would risk an import cycle through the supervisor.
+        from airflow.sdk.execution_time.task_runner import initialize_ti_bundle
 
-    def _build_execute_task_command(self, *, what: TaskInstance) -> 
tuple[list[str], str | None]:
+        bundle = initialize_ti_bundle(target, log)

Review Comment:
   This passes the module logger, so bundle resolution failures land in the 
worker log and never in the task log. `logger` is in scope in `execute_task` 
and already threaded into `_PopenActivitySubprocess.start`, so `logger or log` 
here would fix it.
   
   The ordering also flipped. The TI goes RUNNING in `_on_child_started` via 
`client.task_instances.start` (`supervisor.py:1388`), which now happens after 
bundle materialization, so a first-time clone of a large repo runs while the TI 
still shows QUEUED and counts against `[scheduler] task_queued_timeout`. The 
Python path is the other way round: RUNNING first, then `parse()` clones in the 
child where output is captured to the task log.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to