fix(runtime): resume --retry-failed leaves a continuing task its started consumers ran without (#1231) - #1254
Merged
Conversation
…ted consumers ran without (#1231) A started consumer admitted a failed continuing producer as an optional omission, so rerunning the producer could not reach that consumer's output. Once the rerun succeeded every planned task read as succeeded and the report said COMPLETE. `--retry-failed` now leaves such a producer failed, warns with WORKFLOW_RETRY_SKIPPED, and retries the other failed tasks. Modal no longer resumes a finished run whose only failures are such producers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p non-null assertions from its tests A property lens is an optional input to every task after the property fan-in (50 in the default topology), so the warning now names three and counts the rest. The new resume tests guard the fake command log with assert.ok instead of `!`, which lint:strict forbids. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in retained retries (#1231) Modal read a fanned-out lens's aggregate state entry, and an expanded dynamic group's entry, as a failure `--retry-failed` would rerun, so it could resume a run with nothing to retry and wait for a change that never came. readRetainedFailureStateIds now includes such an entry when every entry under it that did not succeed is retained. A task `--reset-node` resets is no longer reported as left failed in the WORKFLOW_RETRY_SKIPPED warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…PPED where the warning is built Filtering it inside runSmithersLifecycleCommand pushed that function past the lint complexity limit on the branch merged with unstable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Real-engine reproduction of the scenario this PR fixes, where the report claimed COMPLETE with 0 issues while the strategies that found the bug were never read: #1231 (latest comment). |
Resolve the CHANGELOG.md conflict by keeping both entries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment on lines
+74
to
+77
| const failedNodeIds = Object.entries(state.nodes ?? {}) | ||
| .filter(([, node]) => node.status !== undefined && CHECKPOINT_FAILED_STATUSES.has(node.status)) | ||
| .map(([nodeId]) => nodeId); | ||
| return failedNodeIds.length === 0 || failedNodeIds.some((nodeId) => !retainedFailures.has(nodeId)); |
There was a problem hiding this comment.
Skipped tasks trigger needless resume When a terminal run has a retained failed producer and a skipped downstream task, the checkpoint counts the skipped task as failed. This check therefore requests a resume, even though
--retry-failed cannot reset the skipped task and leaves the producer failed. With nothing to redo, the Modal worker waits for a state change until its watch times out instead of finalizing the partial evaluation.
Knowledge Base Used: Modal worker integration
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/modal/src/resume.ts
Line: 74-77
Comment:
**Skipped tasks trigger needless resume** When a terminal run has a retained failed producer and a skipped downstream task, the checkpoint counts the skipped task as failed. This check therefore requests a resume, even though `--retry-failed` cannot reset the skipped task and leaves the producer failed. With nothing to redo, the Modal worker waits for a state change until its watch times out instead of finalizing the partial evaluation.
**Knowledge Base Used:** [Modal worker integration](https://app.greptile.com/monad-foudnation/-/custom-context/knowledge-base/monad-developers/ultrafuzz/-/docs/modal-worker-integration.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1231.
Problem
A failed node in a
failure_policy: continuegroup (a property lens since #1198, or an optional strategy) is an optional input to its consumers in other groups, so they run without it.resume --retry-failedreran it anyway. A succeeded consumer is never finalized again, so the rerun could not reach that consumer's output. Once the rerun succeeded, every planned task read as succeeded, the report said COMPLETE, and a--require-completerun could endsucceeded.Confirmed on
unstable(a375d3b4): with the new tests and no fix,resume --retry-failedissuedtimetravel --node-id node:lens(andnode:optional-specialist) after the consumer had finished without it.Fix
Takes the direction the issue proposes:
packages/runtime/src/retry-failed-omissions.ts:failedProducersStartedConsumersOmittednames each failed producer that a started consumer lists inoptionalDependencyArtifactDirs. It has two readers:prepare:,node:orverify:node is in-progress, finished, failed or stalled.state.jsonandsmithers/tasks.json.runSmithersLifecycleCommand:--retry-failedno longer resets those producers. It still retries every other failed task.resumeRunreports each skipped producer as aWORKFLOW_RETRY_SKIPPEDwarning that names the consumers that ran without it. The report stays PARTIAL.modalDurableRunNeedsResumetakes the retained failures and no longer resumes a terminal run whose only failures are those. Before,waitForTerminalRunwould wait for a change that never came.docs/reference/cli.mdand a CHANGELOG entry.Not changed: the report-completion schema, and
--reset-node, which can still rerun such a node.Tests
runtime.test.ts) cover a lens and a strategy. Each runs with its consumer started (producer left failed, other failed task retried, warning reported) and with its consumer not started (producer retried as before).readFailedProducersStartedConsumersOmittedreportslens → [catalog].modalDurableRunNeedsResumewith retained failures.--retry-failedtests and the sync tests for optional prerequisites. Modalresume.test.ts: 32 of 32 pass. eslint and prettier are clean on the changed files. I have not run the full runtime suite locally.🤖 Generated with Claude Code
The PR is not safe to merge until Modal avoids resuming terminal runs whose only non-retained failed checkpoint entries are skipped tasks.
Fix with agent prompt
Summary
The PR keeps failed optional producers failed once consumers have started without them, reports skipped retries, and uses retained-failure state to decide whether Modal should resume a terminal evaluation.
--retry-failedcan redo.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Terminal run] --> B[Read retained producer failures] B --> C{Other failed checkpoint IDs?} C -->|Retryable task| D[Resume and wait for advancement] C -->|None| E[Finalize partial evaluation] C -->|Skipped task misclassified| DReviews (4) · Last reviewed commit: "Merge branch 'unstable' into fix/1231-re..."