feat(execute-phase): classify quota/rate-limit failures across runtimes (#3095) (#3490)

* feat(execute-phase): classify quota/rate-limit failures across runtimes (#3095)

Dispatched executor subagents that die from provider quota or rate-limit
errors currently look identical to a crashed agent to the orchestrator —
so step 7's recovery prompt offers "retry now" when the right action is
"wait for reset and resume". This adds a runtime-agnostic classifier and
wires execute-phase step 7 to it.

- `agent.classify-failure` SDK query returns
  `{class: 'quota-exceeded' | 'classify-handoff-bug' | 'unknown-failure',
    sentinel?, retryAfterSeconds?}`. Sentinels cover Claude Code
  (`usage limit`, `429`), Copilot CLI (`rate_limit`,
  `user_weekly_rate_limited`), Codex (`usage_limit_reached`,
  `too many requests`), and Gemini (`RESOURCE_EXHAUSTED`,
  `exceeded your`).
- `execute-phase.md` step 7 now branches on the class. Quota-exceeded
  presents a wait-for-reset prompt and points at the safe-resume gate
  landing in #3212 instead of re-dispatching a fresh executor.
- `docs/research/provider-rate-limit-signals.md` records the proactive
  (header / SDK event) signals each provider exposes and the upstream
  Claude Code / Copilot / Codex issues blocking hook-side detection —
  the forward path once host runtimes surface them.

Resume-from-partial-worktree and context-load metrics from the original
report are deliberately out of scope; they overlap #3212's
`state.verify-against-disk` work already in flight.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(execute): render quota retry hint and refresh alias artifacts

* fix(workflow): restore slash namespace and execute-phase size budget

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-05-14 07:46:56 -04:00
committed by GitHub
parent 8b679959cc
commit 49e7c48007
9 changed files with 374 additions and 32 deletions

View File

@@ -1005,46 +1005,46 @@ increases monotonically across waves. `{status}` is `complete` (success),
---
```
- Bad: "Wave 2 complete. Proceeding to Wave 3."
- Good: "Terrain system complete — 3 biome types, height-based texturing, physics collision meshes. Vehicle physics (Wave 3) can now reference ground surfaces."
7. **Handle failures:**
**Known Claude Code bug (classifyHandoffIfNeeded):** If an agent reports "failed" with error containing `classifyHandoffIfNeeded is not defined`, this is a Claude Code runtime bug — not a GSD or agent issue. The error fires in the completion handler AFTER all tool calls finish. In this case: run the same spot-checks as step 5 (SUMMARY.md exists, git commits present, no Self-Check: FAILED). If spot-checks PASS → treat as **successful**. If spot-checks FAIL → treat as real failure below.
For real failures: report which plan failed → ask "Continue?" or "Stop?" → if continue, dependent plans may also fail. If stop, partial completion report.
**Step 7.0 — classify before branching (#3095):**
```bash
CLASS_JSON=$(gsd-sdk query agent.classify-failure -- "$AGENT_RETURN_BODY")
CLASS=$(echo "$CLASS_JSON" | jq -r '.class')
SENTINEL=$(echo "$CLASS_JSON" | jq -r '.sentinel // empty')
RETRY_AFTER=$(echo "$CLASS_JSON" | jq -r '.retryAfterSeconds // empty')
if [ -n "$RETRY_AFTER" ]; then RETRY_HINT=" Provider hinted retry-after: ${RETRY_AFTER}s"; else RETRY_HINT=""; fi
```
One classifier branch handles sentinels across Claude/Copilot/Codex/Gemini. Reference: `docs/research/provider-rate-limit-signals.md`.
**Step 7.1 — `class == "quota-exceeded"`:**
Do not offer "retry now". Run step-5 spot-check first; if SUMMARY.md is missing but commits exist, route to safe-resume (`state.verify-against-disk`) instead of immediate redispatch.
```text
⚠ Plan {plan_id} terminated by provider quota / rate limit
Runtime sentinel: {SENTINEL}
{RETRY_HINT}
Partial commits on worktree branch: {N}
SUMMARY.md present: {yes|no}
1. Wait for quota reset, then resume (recommended)
2. Switch to a different runtime / model and resume
3. Abort phase and report partial state
```
Re-run `/gsd:execute-phase` after quota reset for Option 1.
**Step 7.2 — `class == "classify-handoff-bug"`:**
If error contains `classifyHandoffIfNeeded is not defined`, treat as Claude runtime bug. Run the same step-5 spot-checks; PASS => treat as success, FAIL => fall through.
**Step 7.3 — `class == "unknown-failure"`:**
Report failed plan and ask Continue/Stop; continuing may cascade into dependent plan failures.
7b. **Pre-wave dependency check (waves 2+ only):**
Before spawning wave N+1, for each plan in the upcoming wave:
```bash
gsd-sdk query verify.key-links {phase_dir}/{plan}-PLAN.md
```
If any key-link from a PRIOR wave's artifact fails verification:
## Cross-Plan Wiring Gap
| Plan | Link | From | Expected Pattern | Status |
|------|------|------|-----------------|--------|
| {plan} | {via} | {from} | {pattern} | NOT FOUND |
Wave {N} artifacts may not be properly wired. Options:
1. Investigate and fix before continuing
2. Continue (may cause cascading failures in wave {N+1})
Key-links referencing files in the CURRENT (upcoming) wave are skipped.
Before wave N+1, run `gsd-sdk query verify.key-links {phase_dir}/{plan}-PLAN.md` for each upcoming plan.
If any PRIOR-wave artifact link fails, present:
- `## Cross-Plan Wiring Gap` with plan/link/from/pattern rows
- Options: investigate+fix before continue, or continue with cascade risk
Skip key-links that reference files in the CURRENT (upcoming) wave.
8. **Execute checkpoint plans between waves** — see `<checkpoint_handling>`.
9. **Proceed to next wave.**
</step>
<step name="checkpoint_handling">
Plans with `autonomous: false` require user interaction.
**Auto-mode checkpoint handling:**
Read auto-advance config (chain flag OR user preference — same boolean as `check.auto-mode`):
```bash
AUTO_MODE=$(gsd-sdk query check auto-mode --pick active 2>/dev/null || echo "false")
@@ -1785,6 +1785,7 @@ For 1M+ context models, consider:
</context_efficiency>
<failure_handling>
- **Quota / rate-limit (any runtime — #3095):** Agent return body contains a sentinel like `usage limit`, `rate limit`, `429`, `too many requests`, `RESOURCE_EXHAUSTED`, `usage_limit_reached`. Route via `gsd-sdk query agent.classify-failure` → `class: "quota-exceeded"`. Do not offer retry-now; the right action is wait-for-reset and resume.
- **classifyHandoffIfNeeded false failure:** Agent reports "failed" but error is `classifyHandoffIfNeeded is not defined` → Claude Code bug, not GSD. Spot-check (SUMMARY exists, commits present) → if pass, treat as success
- **Agent fails mid-plan:** Missing SUMMARY.md → report, ask user how to proceed
- **Dependency chain breaks:** Wave 1 fails → Wave 2 dependents likely fail → user chooses attempt or skip