e2e: share the run-capture/deadline-wrap block across agent runners · Entire

e2e: share the run-capture/deadline-wrap block across agent runners

e2659f1·

Soph·2w ago·4 files·+26 added/-41 removed

codex, copilot-cli, and gemini repeated the exit-code extraction and prompt-deadline wrapping after cmd.Run(); extract runCapture into agents/agent.go. The other runners don't take a prompt context and never wrapped the deadline, so they keep their direct handling. Verified with the Vogon canary suite (59/59).

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

Sessions

ab0d1ee8ae62View transcript

[?
/goal simplify this repo, make sure to de-slop it continue until you are happy, make sure to backfill tests and validate any significant change. The output of this should be a PR with green CI, reviewClaude Code·Fable 5·1 step](/content/gh/entireio/cli/session/1c940797-e8a8-42af-af74-e4b69e3ff567#timeline-ab0d1ee8ae62/index.html)

Changes

4

1 unmodified line

2
3
4
5
6
7
8
9
10
11
96 unmodified lines

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

1 unmodified line

import (
    "context"
    "errors"
    "fmt"
    "os"
    "os/exec"
    "strconv"
    "strings"
    "time"
96 unmodified lines

return registry
}

// runCapture runs cmd and returns its exit code (-1 when the process
// didn't produce one) and the run error. When promptCtx hit its deadline
// the error wraps context.DeadlineExceeded so IsTransientError can detect
// it — cmd.Run reports "signal: killed" in that case, not the context error.
func runCapture(cmd *exec.Cmd, promptCtx context.Context) (int, error) {
    err := cmd.Run()
    if err == nil {
        return 0, nil
    }

exitCode := -1
    exitErr := &exec.ExitError{}
    if errors.As(err, &exitErr) {
        exitCode = exitErr.ExitCode()
    }
    if errors.Is(promptCtx.Err(), context.DeadlineExceeded) {
        err = fmt.Errorf("%w: %w", err, context.DeadlineExceeded)
    }
    return exitCode, err
}

// filterEnv returns env with entries matching any of the given variable names
// removed. Used to strip test-only overrides (e.g. ENTIRE_TEST_TTY) from agent
// processes so they exercise real detection paths.

Me2e/agents/agent.go+23

129 unmodified lines

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
133
134
135
136

129 unmodified lines

cmd.Stdout = &stdout
    cmd.Stderr = &stderr

err = cmd.Run()
    exitCode := 0
    if err != nil {
        exitErr := &exec.ExitError{}
        if errors.As(err, &exitErr) {
            exitCode = exitErr.ExitCode()
        } else {
            exitCode = -1
        }
        if promptCtx.Err() == context.DeadlineExceeded {
            err = fmt.Errorf("%w: %w", err, context.DeadlineExceeded)
        }
    }
    exitCode, err := runCapture(cmd, promptCtx)

return Output{
        Command:  c.Binary() + " " + strings.Join(args[:len(args)-1], " ") + " " + fmt.Sprintf("%q", prompt),

Me2e/agents/codex.go+1/-13

86 unmodified lines

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
90
91
92
93

86 unmodified lines

cmd.Stdout = &stdout
    cmd.Stderr = &stderr

err := cmd.Run()
    exitCode := 0
    if err != nil {
        exitErr := &exec.ExitError{}
        if errors.As(err, &exitErr) {
        exitCode = exitErr.ExitCode()
        } else {
        exitCode = -1
        }
        if promptCtx.Err() == context.DeadlineExceeded {
            err = fmt.Errorf("%w: %w", err, context.DeadlineExceeded)
        }
    }
    exitCode, err := runCapture(cmd, promptCtx)

out := Output{
        Command:  c.Binary() + " " + strings.Join(displayArgs, " "),

Me2e/agents/copilot-cli.go+1/-13

119 unmodified lines

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
123
124
125
126

119 unmodified lines

cmd.Stdout = &stdout
    cmd.Stderr = &stderr

err := cmd.Run()
    exitCode := 0
    if err != nil {
        exitErr := &exec.ExitError{}
        if errors.As(err, &exitErr) {
        exitCode = exitErr.ExitCode()
        } else {
        exitCode = -1
        }
        // Wrap the prompt-level deadline so IsTransientError can detect it.
        // cmd.Run() returns "signal: killed", not the context error.
        if promptCtx.Err() == context.DeadlineExceeded {
            err = fmt.Errorf("%w: %w", err, context.DeadlineExceeded)
        }
    }
    exitCode, err := runCapture(cmd, promptCtx)

// gemini-cli can abort a turn server-side (empty/malformed model response)
    // yet still exit 0 with empty stdout. Surface it as an error so the

Me2e/agents/gemini.go+1/-15