Skip to content

service-automation: restoreConsumedSuspension cannot reach a nested run — a stranded child's cascade-failed ancestors are consumed without a snapshot, so restoring the child continues into a dead parent #15222

Description

@os-warren

Found while delivering the services half of #13937 (branch claude/issue-13937-stranded-run-operator-verb). ⛔ Not fixed there: it is a reach gap of the existing operator exit for nested runs, and the shape of the repair (journal the ancestors too, or restore the chain as one unit) is a design decision, not a line. Filed under #13909 because it is deliverable 2's ("an operator path out of a terminal run") nested-run gap.

The reading, on origin/main 50d6c92

resumeInternal's catch arm journals the consumed suspension of THE RUN THAT THREW (journalConsumedSuspension) — the input restoreConsumedSuspension puts back. For a nested run, the ancestors are then handled on both paths without any journal:

  • Up-bubble path (skipBubble === false): failAncestors walks $parentRunId and calls failSuspendedRun on each suspended ancestor.
  • Delegation path (parent resumed first, child resumed with skipBubble === true): the parent frame sees !childRes.success with no retryable code and calls failSuspendedRun(run, ...) on itself.

failSuspendedRun is forgetSuspendedRun(run, 'failed') plus a failed log record — it journals NOTHING. So after a child strands:

  • the child is stranded and restorable (status: 'stranded' on its result; restoreConsumedSuspension(CHILD_RUN_ID) answers restored: true);
  • every ancestor is recorded failed with its pause consumed and no snapshot: restoreConsumedSuspension(PARENT_RUN_ID) answers NO_CONSUMED_SUSPENSION, and the parent's own resume result (delegation path) carries no status at all — it is neither stranded nor repairable, and must not be stamped stranded (nothing can re-arm it);
  • restoring and resuming the child completes the child, whose bubbleToParent finds no parent suspension and logs a warning: the parent flow never continues.

⇒ For a nested run the operator exit re-arms the leaf and loses the tree. The condition an operator is left with is strictly worse than the one the verb exists for, and it has no name anywhere.

What a fix has to decide

Related

#13909 (deliverable 2, the operator exit — this is its nested-run gap) · #13937 (shape-4 ruling; services half in flight) · #14379 (the delegated child's retryable refusal, a neighbouring seam on the same frame)

Activity

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

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions