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
e2e/agents
Magent.go+23
Mcodex.go+1/-13
Mcopilot-cli.go+1/-13
Mgemini.go+1/-15
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