Skip to content

Commit c915191

Browse files
committed
fix(spec): Flow.errorHandling's maxAttempts refusal says a bare rename runs one attempt more, not fewer
maxAttempts counts the first attempt and maxRetries does not, so maxAttempts: 3 is 3 runs and a bare rename to maxRetries: 3 is 4. The prescription (maxRetries one lower) was right; the consequence clause was inverted. Pinned on both surfaces: the message says "one attempt more" and never "fewer". Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
1 parent b7fdd50 commit c915191

4 files changed

Lines changed: 13 additions & 4 deletions

File tree

‎.changeset/22343-retry-policy-try-catch-undeclared-keys-refused.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Clause-②: yes (narrowing: an undeclared key on the shared retry policy is refu
2525
- A pre-17 `retryDelayMs` alone: still renamed to `backoffMs` before the judge at every door that converts first (`defineStack`, `os validate`, `os compile`, `registerFlow`, the save door). Met by a direct `FlowSchema.parse` or `defineFlow()`, it meets its tombstone, like every other retired spelling.
2626
- `Flow.errorHandling`, which carries the same policy keys, was already closed; a `script` node's retired keys keep the scope they had.
2727

28+
**Also corrected:** `Flow.errorHandling`'s refusal of a `maxAttempts` key said a bare rename to `maxRetries` would run one attempt fewer than asked for; it runs one more (`maxAttempts: 3` is 3 runs, `maxRetries: 3` is 4), and the message now says so — the prescription, `maxRetries` one lower, is unchanged.
29+
2830
## FROM → TO
2931

3032
| you wrote | write instead |

‎packages/spec/src/automation/flow.test.ts‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,10 +1988,15 @@ describe('unknown keys are rejected, not stripped', () => {
19881988
...minimalFlow,
19891989
errorHandling: { strategy: 'retry', maxAttempts: 3 },
19901990
});
1991-
// Renaming alone would silently run one attempt FEWER than asked for:
1992-
// RetryConfig's `maxAttempts` counts the first try, `maxRetries` does not.
1991+
// Renaming alone would silently run one attempt MORE than asked for:
1992+
// RetryConfig's `maxAttempts` counts the first try, `maxRetries` does not,
1993+
// so `maxAttempts: 3` is 3 runs and a bare `maxRetries: 3` is 1 + 3 = 4.
1994+
// The consequence clause shipped inverted ("fewer") until the shared
1995+
// retry policy closed beside it with the right direction; pin both halves.
19931996
expect(issue!.message).not.toContain('`maxAttempts` → `maxRetries`');
1994-
expect(issue!.message).toContain('maxAttempts - 1');
1997+
expect(issue!.message).toContain('`maxRetries: <maxAttempts - 1>`');
1998+
expect(issue!.message).toContain('one attempt more than you asked for');
1999+
expect(issue!.message).not.toMatch(/fewer/i);
19952000
});
19962001

19972002
it('errorHandling: the `strategy: retry` refinement still runs after the block is strict', () => {

‎packages/spec/src/automation/flow.zod.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1191,7 +1191,7 @@ export const FlowSchema = lazySchema(() => strictObject(
11911191
maxAttempts:
11921192
'`maxAttempts` is the connector/RetryConfig spelling and INCLUDES the first attempt; ' +
11931193
'flow `errorHandling` counts retries AFTER it. Write `maxRetries: <maxAttempts - 1>` ' +
1194-
'— renaming the key alone would quietly run one attempt fewer than you asked for.',
1194+
'— renaming the key alone would quietly run one attempt more than you asked for.',
11951195
fallback:
11961196
'There is no fallback node on `errorHandling` (`fallbackNodeId` was removed in 17 — ' +
11971197
'the engine never read it). Draw a per-node FAULT EDGE from the failing node ' +

‎packages/spec/src/shared/retry-policy.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ describe('RetryPolicySchema refuses a key it does not declare', () => {
180180
const attempts = RetryPolicySchema.safeParse({ maxAttempts: 3 });
181181
const message = attempts.success ? '' : attempts.error.issues[0]!.message;
182182
expect(message).toContain('`maxRetries: <maxAttempts - 1>`');
183+
expect(message).toContain('one attempt more than you asked for');
184+
expect(message).not.toMatch(/fewer/i);
183185
expect(message).not.toContain('`maxAttempts` → ');
184186
});
185187

0 commit comments

Comments
 (0)