From 679e26d67e579e4f7e7c37bd4a846fb2c1642fcf Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Mon, 24 Aug 2026 16:34:34 +0200 Subject: [PATCH] fix(e2e): honor E2E_TIMEOUT for cursor-cli and raise the default to 4m MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This runner hardcoded a 90s per-prompt ceiling and never read E2E_TIMEOUT, which CLAUDE.md documents as the per-prompt timeout for every agent. Only the hardcoded default and a per-test override applied, so the Cursor leg could not be widened from the environment at all. 90s is independently too tight for current Cursor. On a healthy account with no quota error, one TestSingleSessionManualCommit turn measured 188.7s and passed only once the ceiling was lifted. At 90s the harness tears down tmux mid-turn, so the `stop` hook never fires and the checkpoint is silently absent — and the assertion names the timeout, not the missing hook, which makes a harness limit read as agent flakiness. Two changes: - Read E2E_TIMEOUT. Precedence now matches opencode.go: default < E2E_TIMEOUT < per-test. - Raise the default 90s -> 4m (~2x the measured turn). No workflow sets E2E_TIMEOUT, so the env knob alone would leave the default in force. A too-high default only costs wall clock when an agent genuinely hangs. This does NOT fix the currently red cursor-cli leg. That job's failures are quota: the captured pane carries "You're out of usage. Switch to Auto or Composer 2.5, or ask your admin to increase your limit to continue.", 66 times alongside 68 timeouts in the same run. The agent stops responding and the harness then times out, so the timeout is the downstream symptom and raising it will not help a rate-limited account. Not addressed here, deliberately. The runner passes no --model, so the account's Auto routing picks the model per run and turn duration is non-deterministic by construction; an E2E_CURSOR_MODEL knob mirroring E2E_CLAUDE_MODEL would fix that. Several per-test overrides are also below the measured turn duration -- edge_cases_test.go:138 (120s), split_commits_test.go:69,82 (2m), multi_session_test.go (3m). Those are agent-agnostic, so widening them changes every agent's run time. Co-Authored-By: Claude Opus 5 (1M context) Entire-Checkpoint: 01M0T37WDH4WWCNJEYT8ADHV6A --- e2e/agents/cursor_cli.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/e2e/agents/cursor_cli.go b/e2e/agents/cursor_cli.go index 03b05fa213..55a8334f3a 100644 --- a/e2e/agents/cursor_cli.go +++ b/e2e/agents/cursor_cli.go @@ -103,7 +103,26 @@ func (a *CursorCLI) RunPrompt(ctx context.Context, dir string, prompt string, op o(cfg) } - timeout := 90 * time.Second + // E2E_TIMEOUT is documented as the per-prompt timeout for every agent, but + // this runner never read it — only the hardcoded default and a per-test + // override applied. That made the Cursor leg impossible to widen from the + // environment, which matters more here than for other agents: with no + // --model flag the account's Auto routing picks the model, so turn duration + // varies per run and a fixed 90s ceiling tears down tmux mid-turn. The + // `stop` hook then never fires and the checkpoint is silently absent. + // Mirrors the precedence in opencode.go: default < E2E_TIMEOUT < per-test. + // 90s was the original default and no longer matches current Cursor: with + // Auto routing a single turn measured 188s locally, and CI's cursor-cli leg + // fails en masse on exactly this ceiling (~10 tests, all "timed out waiting + // for \"Add a follow-up\" after 1m29.99s"). 4m is ~2x the observed turn. + // The cost of a too-high default is only paid when an agent genuinely hangs. + timeout := 4 * time.Minute + if envTimeout := os.Getenv("E2E_TIMEOUT"); envTimeout != "" { + if parsed, err := time.ParseDuration(envTimeout); err == nil { + timeout = parsed + } + } + // Per-prompt timeout is the most specific override. if cfg.PromptTimeout > 0 { timeout = cfg.PromptTimeout }