Skip to content

[finding] a durably-PAUSED child that fails after resume rolls NO totals to its parent — the parent's steps are frozen at suspend time, so #15617's own "a failing child is counted" control does not hold on that path #16666

Description

@os-trump

Found by the at-tier Clause-② contract review of PR #16635 (card #15617, the spec half of decision batch #55) while verifying that PR's new roll-up statements against the engine. ⛔ Not a defect in that PR — filed out of it because it is engine behaviour, and because it falsifies a control reading the card rests on.

⛔ No domain:* set — routing is triage's. It lands in service-automation (engine.ts).

Measured on origin/main 0a61db1f5d

A subflow / map child that is durably paused and then fails after resume terminally fails the parent through a different path from the ordinary one:

engine.ts:5549-5552  →  failSuspendedRun (:6358-6372)  →  up-bubble failAncestors (:7001)

That path fails the parent with steps: run.steps frozen at suspend time. ⇒ No total of any kind rides up — not selected, not acted, not unmeasuredEffect, and (once #16314 lands) not failures either.

Contrast the ordinary path, where a failed child DOES contribute: subflow-node.ts:144-147 spreads the same rolled object onto the failure return, and map-node.ts:197-203 adds the failed item's summary.acted.

Why it is worth a card rather than a footnote

⚠️ It falsifies a control that #15617's evidence base rests on. That card's measurement was reported as a matched pair:

Control, in the same run: when the child fails rather than contains, the parent's subflow node failure is counted (call: {runs:5, failures:1}, parent failed=1). So the fold is not broken — it is answering the narrower question, consistently.

On the durably-paused path that control does not hold: the parent is failed, but nothing is counted, because its step list never advanced past the suspend. The control is true of the path it was measured on and silently untrue of this one — which is exactly the class of gap the reviewer was looking for, arriving one path over.

⭐ It is also the third roll-up site, and the one the spec half's PR body does not name. AutomationEngine.creditChildRun (engine.ts:5351-5371, call sites :5485 up-bubble and :5560 down-delegation) is the only path by which a durably-paused child that later completes reaches the parent's step. The failure twin of that path is this card.

Scope note for whoever takes it

Not a rider on #16314 — but #16314 is the card that will make it matter. Once the ruled failures slot folds contained child failures into the run-level failed, a delegating parent whose child paused and then failed will read failed: 0 while its child lost rows, which is the exact misreading #13681's visibility rider exists to prevent and the reason #15617 was filed at all. ⇒ Sequence it with #16314, or at minimum make #16314's implementer read this first.

⚠️ Deliberately NOT prejudged here: whether the right fix is to credit the frozen steps at failSuspendedRun time, to carry the child's summary onto the parent's step before freezing, or to declare this path's silence explicitly. All three are contract-visible and none is this seat's to rule.

Related but distinct (checked, none covers this): #15222 (restoreConsumedSuspension cannot reach a nested run — restoration, not totals) · #14379 (closed; a retryable child refusal treated as terminal) · #16314 (the engine half of the roll-up) · #15617 · #13681.

Refs: contract review verdict on #15617 (5573894010) · PR #16635.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions