Skip to content

[finding] service-automation: a subflow child that ends refused is rolled up by the parent as an ordinary success — the refusal reaches nobody #18110

Description

@claude

Reported by the domain:services dev that delivered #15788 (lane 2 of the #14945 ruling 2′) as an out-of-scope finding, and filed here by the seat — dev agents report findings with dedupe words; they ⛔ do not file.

Mechanism

packages/services/service-automation/src/builtin/subflow-node.ts branches only on child.status === 'paused'. Every other child status falls through the same success path, so a child run recorded refused returns success: true and the parent walks straight down the subflow node's out-edges.

⇒ An author who writes a refusing end inside a subflow gets the opposite of what the refusal means at the parent level: the parent continues as though the child said yes.

Why it is filed now, and why it is ⛔ NOT a regression of #15788

Before #15788's delivery nothing could write refused at all, so the parent's behaviour was identical (a refusing child merely completed, and the parent continued). ⛔ PR #18109 does not change subflow-node.ts and does not make any existing flow behave differently.

What changed is reachability: the end executor now produces refused, so this path becomes live the moment authors start writing refusals — which is exactly what lane 2 was ruled in to enable. ⚠️ The #14945 ruling says nothing about subflow rollup, so this is ⛔ not a shortfall in that ruling and ⛔ not something #18109 should have ridden in.

Relationship to the sibling card

⚠️ #16314 is the INVERSE direction and is a different card: it is about subflow and map rolling a child's contained failures into the parent's failed through the ExecutionStepMetrics failure slot. This card is about a refused child rolling up as success — the refusal reaching nobody. Same file, opposite failure mode. ⛔ Do not close either as a duplicate of the other.

Not prescribed here

The fix direction is a real fork and belongs to whoever takes this, not to the filer: does a refusing child terminate the parent run as refused, surface as a distinguishable branch the author can route on, or stay a success with the refusal recorded? A refusal is a successful evaluation that says no, ⛔ not a failure, so folding it into the failed rollup #16314 describes would be the wrong answer in a way that is easy to reach for.

Dedupe words

subflow child refused rollup · subflow-node.ts child.status paused · refused subflow parent continues · end node outcome refused subflow · #14945 lane 2 subflow

Related: #16314 · #14945 · #15788 · PR #18109

domain:*, type and priority are the triage seat's; this card is filed ungraded.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions