You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two holes in the #3801 gate, both demonstrated with a repro before fixing.
1. The chain walk missed `map:`. `resumeInternal` handles the two linked-run
correlations oppositely — `subflow:` DELEGATES the signal to the child,
`map:` RE-RUNS the map node — and the gate followed only the first. A run
parked on a `map` node was therefore judged on `map` itself
(resumeAuthority 'any') and let through even while the item it was waiting
on sat on an `approval`.
`map` is the batch-approval shape and the map parent's run id is the one a
launcher holds. Since `$mapState.started` is advanced past the in-flight
item before the suspend, and the re-entry records a result only when
`$mapItemDone` is set, an empty-body resume of the parent skipped that
item's approval outright and orphaned its still-pending request; a later
real decision then bubbled into a parent already waiting on the next item,
cascading the misalignment.
The walk now follows both prefixes. The unifying rule: a linked-run pause is
waiting on a CHILD, so the child's node carries the authority — read the
item, not the loop. They differ only in what continuing would mean (lands on
the child vs. advances past it), which the refusal message now says.
2. Resume `inputs` could write the engine's `$` namespace. They are applied as
bare flow variables, so a caller could set the exact handoff keys
bubbleToParent uses (`<nodeId>.$mapItemDone` / `$mapItemOutput`) and have the
map record a per-item result for a decision nobody made — the node id is
readable from GET /automation/:name. The same reached `$runId`, which
approval/wait nodes use to correlate external state back to a run.
The route now answers 400 for a reserved name (`$…` or a `.$` segment).
Enforced at the transport, not in the engine, so the in-process bubble keeps
working — the same trust split the gate itself uses. Refuse rather than
silently strip, so a mis-authored screen input fails at the door.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gvN32u1EiuvY9uQEMJiMR
0 commit comments