Relationships
#2465 workflows: a nested workflow step should inherit the caller's placement when the child declares none
Opened by randybias · 9/24/2026
Problem
A placed workflow that calls a nested workflow (task: {type: workflow}) does not pass its placement to the child. The child's steps run with the child's own placement only: execution_service.ts:3662 at ad0aa46 (20260923.231117.0). A child without placement therefore runs on serve's loopback, inside the orchestrator process, even though its parent was placed on a worker pool.
Why this bites
Shared helper workflows are called by many parents. Ours is a substrate safety guard that 29 workflows call. The helper cannot be given placement unilaterally: a placed step fails on a plain local run ("Step requests remote placement but no worker dispatcher is active", Lab #2344). So placing the guard breaks every parent that still runs locally. Leaving it unplaced means a placed parent runs the guard in the orchestrator, which cannot reach what the guard reads. In our case that is an inventory DB on the worker host's loopback.
Migration to workers therefore has to be all-or-nothing across every caller of a shared child, or the child has to be forked into a placed copy that is kept in lockstep. We are doing the fork as a workaround.
Reproduction
A parent has workflow-level labels: {pool: x} and one step type: workflow calling a child that has no labels. The child has a model-method step. Run the parent with --server and one enrolled worker labelled pool=x. The parent's own steps dispatch to the worker; the child's step runs on the loopback executor (visible in the worker journal: no dispatch for it).
Ask
- Inherit placement into nested runs. When a child declares none, use the calling step's effective placement (workflow → job → step merged). An explicit child placement, including
labels: {}, still wins. This matches the child-wins merge the placement design already uses across workflow, job and step (placement.tsmergePlacementFields). - Optionally, a
placement: inherit | ownfield on thetype: workflowstep for callers that need the old behaviour.
Related: Lab #2344 (a serve-side default placement, declined). Inheritance is narrower: it only fills a gap the caller already declared.
Open
No activity in this phase yet.
Sign in to post a ripple.