Repository navigation
A usage limit whose reset has passed does not fail the run when another failed node has a later deadline #4412
Description
Problem
When a pass fails, usageLimitWait in packages/workflows/src/dag-executor.ts:12361-12403 decides whether the run waits for a provider limit to reset or fails. It checks reset_passed only against the latest deadline across all failed nodes (dag-executor.ts:12391: if (latest.resumeAt <= now) return skip(latest.nodeId, 'reset_passed')). The loop at dag-executor.ts:12380-12388 keeps only the latest resumeAt and never checks the others against now.
So if node A failed with quota_exhausted and a reported reset that has already passed, and node B in the same pass failed with a limit whose deadline is still ahead, the run pauses and waits for B's deadline instead of failing.
Two places say the run should fail in that case:
packages/docs-web/src/content/docs/reference/configuration.md(the usage-limit section under the workflows settings table): "The run waits only when every node that failed in that pass hit a usage limit with a reported reset that is still ahead, or a rate limit ... Any other failure, a usage limit without a reset time, or a usage limit whose reset has already passed fails the run as before."- The doc comment on
usageLimitWaititself (dag-executor.ts:12351-12360): "when every failed node either hit a provider usage limit whose reset the provider reported and that is still ahead, or hit a rate limit ..."
The same latest-only ranking appears in aggregateProviderFailure (packages/workflows/src/executor-shared.ts, right after providerLimitResumeAt at :90-96). It reports the latest providerLimitResumeAt among an aggregate's limits, so a loop group or fan-out child whose usage-limit reset has passed is hidden behind a sibling with a later deadline before usageLimitWait sees it.
The only test is single-node: dag-executor.test.ts:17774-17800 ('the reported reset has already passed'). No test covers a passed reset next to a later deadline.
Why it matters
A reset time in the past means the provider's limit signal can't be trusted for this node. The documented contract is that Archon fails instead of guessing. Today the code and its own doc comment disagree, and the user docs describe behavior the engine does not have. Two sites that should agree and don't are a defect under AGENTS.md.
Why now
PR #4338 just extended this wait to rate limits and touched the same function. The contract is fresh and the code is small.
Desired outcome
The engine matches its documented contract: any failed node in the pass (including a descendant reported through an aggregate) whose usage limit has a reported reset that has already passed makes the run fail, with a quota_resume_skipped event (reason: 'reset_passed') naming that node. If the implementer finds a concrete reason the current wait-for-the-latest behavior is the right one, change the docs and the doc comment instead, and say why in the PR. Do not leave the code and the docs disagreeing.
Invariants
- Only the typed provider failure class decides. Never read error text.
- A pass whose failed nodes all carry limits with deadlines still ahead still waits for the latest one.
- Rate limits keep their computed backoff when no reset is ahead. A rate limit never counts as
reset_passed. reset_unavailable,disabledandcontainer_unsupportedbehavior is unchanged.
Acceptance
- A test with two failed nodes, one
quota_exhaustedwith a past reset and one with a future deadline, shows the run fails withreset_passedon the first node (or, if the docs change instead, shows the documented behavior). - A test covers the same case inside an aggregate (loop group or fan-out), or the PR explains why the aggregate path cannot reach it.
configuration.md, theusageLimitWaitdoc comment and theaggregateProviderFailuredoc comment all describe what the code does.
Found by Archon run 26bdc2cb-a312-4b0e-90dc-852419ce19fc and verified at c47d37a by the archon-discoveries workflow.
Current scope (maintainer ruling on the build queue's question on a stopped run, 2026-10-20)
Retain latest-deadline waiting when an expired quota reset coexists with a future provider-limit deadline, including inside aggregates. Use the issue's documentation alternative, explain the preserved automatic continuation behavior, and satisfy its tests, documentation alignment and unchanged-behavior invariants.
The maintainer's words: Keep the wait. The run already has to wait for the other node's later deadline, and when it resumes the node with the expired reset retries too, so failing early gains nothing. Fix the configuration reference to describe latest-deadline waiting. Keep the change to the docs plus at most one behaviour test if one is missing; the existing aggregateProviderFailure test already covers the behaviour.
Activity