Commands and files: os/exec and os.Root for an agent’s tools
Run go test so it stops when told, keeps your keys, and reports what failed; keep file tools inside one directory.
Stage 3: Run the toolsA coding agent’s most useful tools reach outside the Go process.
One runs go test on the code the model just changed; others read, search, and edit files in a workspace.
These tools are where a model’s choices become real processes and real writes, so their limits have to hold no matter what the model asks for.
Two parts of the standard library carry most of that weight.
os/exec starts a command, collects what it prints, and stops it when the run is cancelled or a deadline passes.
os.Root opens one directory and refuses any path that would lead out of it.
This page covers both, with code from the companion repository, where it is tested.
Some of the pieces are recent:
| Feature | Since |
|---|---|
exec.CommandContext |
Go 1.7 |
exec.Cmd.Cancel, exec.Cmd.WaitDelay, exec.ErrWaitDelay |
Go 1.20 |
os.Root, os.OpenRoot, os.OpenInRoot |
Go 1.24 |
root.ReadFile, root.WriteFile, root.MkdirAll, and nine more os.Root methods |
Go 1.25 |
errors.AsType |
Go 1.26 |
Go 1.26 and 1.27 added no methods to os.Root.
The companion code needs Go 1.27.
Running a command from a tool
exec.CommandContext builds a command tied to a context.
If the context ends before the command does, os/exec calls the command’s Cancel function, which CommandContext sets to kill the process; on Unix that is SIGKILL, which the process can’t catch.
The context to pass is the one the tool was given, derived from the run’s, so Ctrl-C, a shutdown, and the tool’s own deadline all reach the process the same way.
The context page covers that chain; here is the runner behind the agent’s run_tests tool:
// GoTest runs go test in dir with args, such as a package pattern, and
// returns a report the model can read. The triage tools (chapter 7)
// call it on the agent's workspace.
func GoTest(ctx context.Context, dir string, timeout time.Duration,
args ...string) (string, error) {
ctx, cancel := context.WithTimeoutCause(ctx, timeout, ErrTestsTimedOut)
defer cancel()
inner := fmt.Sprintf("-timeout=%s", timeout*3/4)
cmd := exec.CommandContext(ctx, "go", append([]string{"test", "-json",
"-count=1", inner}, args...)...)
cmd.Dir = dir
cmd.Env = testEnv()
if slices.Contains(args, "-race") {
cmd.Env = append(cmd.Env, "CGO_ENABLED=1") // the race detector needs cgo
}
cmd.WaitDelay = 5 * time.Second
var stdout, stderr bytes.Buffer
cmd.Stdout, cmd.Stderr = &stdout, &stderr
err := cmd.Run()
run_tests gives it two minutes, as a deadline with its own cause, ErrTestsTimedOut, so a timeout can be told apart from a cancelled run.
go test gets a -timeout of three quarters of that, 90 seconds, so a hung test is usually stopped by go test itself, which names the test and prints its goroutines.
WaitDelay handles a problem that has nothing to do with the command’s own exit.
When Stdout or Stderr is a Go writer, such as a bytes.Buffer, os/exec connects the child to a pipe and copies from it in a goroutine, and Run doesn’t return until that copy reaches the end of the pipe.
Any process the command started inherits the pipe, so it can keep the pipe open after the command itself is dead.
A short program shows the difference: a shell that started sleep 30 in the background was killed after half a second.
With no WaitDelay, Run returned after 30 seconds, when sleep finally exited.
With a WaitDelay of one second, Run returned after 1.5 seconds, by closing the pipe; sleep was still running.
WaitDelay gets your goroutine back, but it does nothing about that leftover process, which is the next section’s subject.
Cancel can be replaced, as long as you set it before the command starts.
A custom one might send SIGTERM to let the process exit on its own; if the process is still running when WaitDelay expires, os/exec kills it anyway.
Sometimes killing is the wrong first step altogether.
The agent’s MCP client starts a server process that it talks to over standard input and output, and the protocol asks a client to close the server’s input before anything harsher:
func Start(ctx context.Context, cmd Command,
info Implementation) (*Client, error) {
// Not exec.CommandContext: its cancel kills the process, and the
// protocol asks a client to close the server's input first. Close
// does that, then kills; ctx ending calls Close.
p := exec.Command(cmd.Path, cmd.Args...) //nolint:noctx // see above
p.Env = append([]string{}, cmd.Env...) // nil would inherit ours
p.Dir = cmd.Dir
So it uses plain exec.Command and arranges the stop itself, with context.AfterFunc, as the context page shows.
Stopping everything it started
go test is a parent: it runs the compiler, then a test binary, and test code can start processes of its own.
The default Cancel kills only the process exec started.
In a check with a test that sleeps for 40 seconds, killing the go command after four seconds left the test binary running, and Run waited out its two-second WaitDelay because the test binary still held the output pipe.
The triage agent has a hardened test runner, which it uses when its defenses are on. That runner starts the command in a group of its own and makes cancelling kill the group:
// group starts the command in a process group of its own, and makes
// cancelling it kill the whole group: go test starts a test binary, and
// the test can start more, none of which a kill of the go command alone
// would reach. A child that starts its own session leaves the group and
// escapes this; only a rung-3 sandbox (bubblewrap's --die-with-parent,
// a container) catches that.
func group(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Cancel = func() error {
return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
}
// killGroup kills whatever is left in the group after the go command
// exits, such as a test's child that was still running.
func killGroup(cmd *exec.Cmd) {
if cmd.Process != nil {
// ESRCH, the only error, means none was left.
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
}
Setpgid: true puts the child in a new group whose ID is the child’s own process ID, so -cmd.Process.Pid names the whole group.
Every process go test starts joins that group unless it asks to leave.
With this Cancel, the same check ended at four seconds with nothing left running.
killGroup covers the other way a run ends: the go command exits normally while a child of the test is still alive.
Here is where both are used, along with the output handling the next section describes:
// Run runs go test with args in the workspace, for at most timeout, and
// returns its output, cut at MaxOutput as it is read. A timeout kills
// every process the run started, not only the go command.
func (t TestRunner) Run(ctx context.Context, timeout time.Duration,
args ...string) (string, error) {
if err := t.scratch(); err != nil {
return "", err
}
ctx, cancel := context.WithTimeoutCause(ctx, timeout,
ErrTestsTimedOut)
defer cancel()
name, argv := t.command(args)
cmd := exec.CommandContext(ctx, name, argv...)
cmd.Dir = t.Dir
cmd.Env = append(t.env(), t.Env...)
if slices.Contains(args, "-race") {
// The race detector needs cgo.
cmd.Env = append(cmd.Env, "CGO_ENABLED=1")
}
out := &capped{max: cmpInt(t.MaxOutput, 1<<20)}
cmd.Stdout, cmd.Stderr = out, out
group(cmd)
cmd.WaitDelay = 2 * time.Second
err := cmd.Run()
killGroup(cmd)
return out.String(), runError(ctx, err, out.over)
}
Three limits are worth stating plainly:
- It is Unix only.
group_unix.gohas theunixbuild constraint; on other systems,group_other.goprovides two functions that do nothing, and only thegocommand is killed. On Windows the equivalent is a Job Object, which the companion code doesn’t use. - A process can leave the group. One that starts a new session with
setsidis no longer in the group, so the kill misses it. An operating system sandbox catches that: bubblewrap, used on Linux at the next rung, puts the run in a process namespace of its own and ends it with its parent. - The plain runner doesn’t do it.
GoTest, above, uses the defaultCanceland a five-secondWaitDelay. The triage tools use it too when the defenses are off, so there a test’s children can outlive a timeout.
Output the model can read
What a command prints becomes a tool result, which goes into the conversation and is sent again with every later request. So the output has to arrive in a readable order, be bounded in size, and say when it was cut.
One writer for both streams.
The runner above assigns the same *capped value to Stdout and Stderr.
When the two are the same writer and can be compared with ==, os/exec gives the child a single pipe for both, so error lines and normal output stay in the order the process wrote them, and only one goroutine calls Write at a time.
cmd.CombinedOutput does the same with a bytes.Buffer, with no limit, so a test that prints in a loop can fill memory.
A cap applied while reading.
capped keeps the first bytes it is given and drops the rest:
// capped keeps the first max bytes written to it and drops the rest, so
// a test that prints forever can't fill memory.
type capped struct {
mu sync.Mutex
buf bytes.Buffer
max int
over bool
}
func (c *capped) Write(p []byte) (int, error) {
c.mu.Lock()
defer c.mu.Unlock()
if room := c.max - c.buf.Len(); room < len(p) {
c.over = true
c.buf.Write(p[:max(room, 0)])
return len(p), nil
}
n, err := c.buf.Write(p)
if err != nil {
return n, fmt.Errorf("capture output: %w", err)
}
return n, nil
}
Write reports the whole write as taken even when it keeps none of it.
A short count would end the copy from the pipe with an error, os/exec would close its end of the pipe, and the command would fail on its next write instead of finishing.
This cap, 1 MiB by default, protects the harness’s memory.
When it is reached, the run’s error says so, with ErrOutputCapped.
A smaller cap for the model, with a note.
run_tests sends the model far less: its report keeps the output of failing tests, build errors, and each package’s result line, drops the output of passing tests, timings, and the temporary directory’s path, and then cuts the text to 8 KiB:
// limit cuts s to at most n bytes, saying how much was cut.
func limit(s string, n int) string {
if len(s) <= n {
return s
}
for n > 0 && !utf8.RuneStart(s[n]) {
n-- // don't cut a character in half
}
return s[:n] + fmt.Sprintf("\n[%d more bytes not shown]", len(s)-n)
}
A cut result that says how much is missing tells the model the output was long, so it doesn’t treat a partial result as the whole story.
The file tools follow the same rule: code_read shows at most 200 lines and ends with the line number to ask for next, as designing tools describes.
A non-zero exit is a result.
go test exits with status 1 when a test fails, and a failing test is exactly what the model is there to see.
Here is the rest of GoTest, after Run:
if ctx.Err() != nil {
err := fmt.Errorf("go test: %w", ctx.Err())
if cause := context.Cause(ctx); !errors.Is(err, cause) {
err = fmt.Errorf("%w: %w", err, cause)
}
return "", err
}
_, exited := errors.AsType[*exec.ExitError](err)
if err != nil && !exited {
return "", fmt.Errorf("run_tests: %w", err)
}
if err != nil && stdout.Len() == 0 {
return "", fmt.Errorf("run_tests: %s",
strings.TrimSpace(stderr.String()))
}
return report(&stdout, stderr.String(), dir), nil
}
The order of the checks matters:
- The context first. A command killed by
Cancelreturns an*exec.ExitErrortoo, whose message is just “signal: killed”. Checked any later, a timeout would look like a test failure. - Then the exit status.
errors.AsType[*exec.ExitError]tells “the command ran and exited non-zero” apart from “the command couldn’t run”, such as a missinggobinary, for whicherrors.Is(err, exec.ErrNotFound)is true. If you need the status itself,ExitCode()on the*exec.ExitErrorreturns it. - A failure with nothing on standard output. That means
go testrefused to start, for example over a brokengo.mod; its standard error becomes the error text. - Everything else is the report, returned with a nil error, whatever the exit status was.
The hardened runner makes the same choice in fewer lines:
// runError turns how the command ended into the run's error. A failing
// test is a result, not an error.
func runError(ctx context.Context, err error, over bool) error {
switch {
case ctx.Err() != nil:
return fmt.Errorf("run tests: %w", context.Cause(ctx))
case over:
return fmt.Errorf("run tests: %w", ErrOutputCapped)
}
var exit *exec.ExitError
switch {
case err == nil, errors.As(err, &exit):
return nil
case errors.Is(err, exec.ErrWaitDelay):
return nil // a test left a child holding the output; the group kill ended it
}
return fmt.Errorf("run tests: %w", err)
}
It adds one case: exec.ErrWaitDelay is what Run returns when the command exited successfully but something it started kept the output open until WaitDelay expired.
killGroup has already ended that process, so the run counts as finished.
The errors these functions do return still don’t stop the agent.
A tool’s error goes back to the model as the tool’s result, marked as an error, unless it wraps ErrToolFatal; the errors page shows that split, and why the agent stopped lists what does end a run.
A test run that timed out is something the model can act on, by running fewer tests or fixing a hang.
The environment it runs in
cmd.Env decides what the child’s environment holds:
nilgives the child a copy of your whole environment,OPENROUTER_API_KEYand any other secret in it included.- A non-nil slice is the child’s entire environment. An empty, non-nil slice really is empty:
envrun that way prints nothing. - A repeated key takes its last value, so appending
CGO_ENABLED=1afterCGO_ENABLED=0turns cgo on.
The code go test runs is code the model wrote or changed, so it gets an environment built from nothing:
// testEnv is the environment go test runs with: only what the go
// command needs, so the code under test can't read your API key, and
// settings that keep it offline and away from your other projects.
func testEnv() []string {
env := []string{
"GOWORK=off", "GOPROXY=off", "GOFLAGS=", "GOENV=off",
"GOTOOLCHAIN=local", "CGO_ENABLED=0",
}
for _, k := range []string{"PATH", "HOME", "TMPDIR", "GOCACHE",
"GOPATH", "GOMODCACHE", "XDG_CACHE_HOME", // Linux cache
"USERPROFILE", "LOCALAPPDATA", "TMP", "TEMP", // Windows
"GOROOT"} {
if v, ok := os.LookupEnv(k); ok {
env = append(env, k+"="+v)
}
}
return env
}
The first six entries keep go test offline and independent of your own Go setup:
| Setting | What it stops |
|---|---|
GOWORK=off |
A go.work file in a parent directory pulling other modules into the build |
GOPROXY=off |
Downloading modules; a module not already in the cache is an error |
GOFLAGS= |
Flags you set for all your go commands, such as -mod=mod, applying to this one |
GOENV=off |
Settings saved with go env -w in your Go configuration file |
GOTOOLCHAIN=local |
Downloading a newer Go when go.mod asks for one; the installed go reports “go.mod requires go >= …” instead |
CGO_ENABLED=0 |
Calling a C compiler, unless the run asked for -race |
The loop after them passes through only the variables the go command needs to find itself and its caches, each copied only if it is set.
The run_tests tests include a case named “no API key”, which sets OPENROUTER_API_KEY and runs a test that fails if it can see the key.
The hardened runner goes further and inherits nothing but PATH:
// env is the whole environment the test run gets. Nothing is inherited
// but PATH, so the agent's API key, a cloud token, or an SSH agent
// socket in the user's environment never reaches test code.
func (t TestRunner) env() []string {
s := t.Scratch
return []string{
"PATH=" + os.Getenv("PATH"),
"HOME=" + filepath.Join(s, "home"),
"GOCACHE=" + filepath.Join(s, "cache"),
"GOMODCACHE=" + filepath.Join(s, "mod"),
"GOPATH=" + filepath.Join(s, "gopath"),
"GOTMPDIR=" + filepath.Join(s, "tmp"),
"TMPDIR=" + filepath.Join(s, "tmp"),
"GOPROXY=off", "GOTOOLCHAIN=local", "GOENV=off", "GOWORK=off",
"GOFLAGS=", "CGO_ENABLED=0", "GIT_CONFIG_NOSYSTEM=1",
}
}
HOME, the build cache, the module cache, and the temporary directory all point into a private scratch directory made for the run.
A program that finds your files through $HOME, such as git reading its configuration, sees an empty home instead.
A program that names /Users/you/.ssh directly still reaches it; stopping that is the operating system sandbox’s job.
The same rule applies to every child the harness starts.
The MCP client above copies cmd.Env into a new slice, because a nil Env would hand the server your whole environment.
The triage program also removes its key variables from its own environment at startup, so a command started anywhere with a nil Env can’t inherit them.
On macOS that still leaves a gap: a process can read the starting environment of another process run by the same user, which is why the prompt injection guide keeps keys in files only you can read.
Files inside one directory: os.Root
A file tool takes a path from the model.
Joining that path to the workspace with filepath.Join and calling os.Open trusts it completely: ../../.ssh/id_ed25519 leads out, and so does a symbolic link inside the workspace that points elsewhere.
Checking the text of the path catches the first and can’t see the second.
os.OpenRoot(dir) opens the directory, on most systems as a file descriptor, so the root keeps referring to the same directory even if it is renamed while open.
The methods mirror the os functions.
Go 1.24 has Open, OpenFile, Create, Stat, Lstat, Mkdir, Remove, OpenRoot for a subdirectory, FS, Name, and Close.
Go 1.25 added ReadFile, WriteFile, MkdirAll, RemoveAll, Rename, Link, Symlink, Readlink, Chmod, Chown, Lchown, and Chtimes.
os.OpenInRoot(dir, name) opens one file in one call.
A Root is safe to use from several goroutines.
A test program on macOS opened a workspace directory, ws, as a root, put secret.txt beside it, and tried these names:
Name given to root.Open |
Result |
|---|---|
store/a.go |
Opened |
store/../store/a.go |
Opened: .. that stays inside is allowed |
../secret.txt, store/../../secret.txt |
Refused |
/etc/passwd, or any absolute path, even one inside the workspace |
Refused |
A link to ../secret.txt |
Refused |
| A link with an absolute target, even a target inside the workspace | Refused |
A link to store/a.go |
Opened |
A link to ../ws/store/a.go, which leaves the workspace and comes back |
Refused |
| A name that doesn’t exist | An error that matches fs.ErrNotExist |
Writes behave the same: root.Create("../new.txt") and root.WriteFile through an escaping link both failed, and nothing appeared outside.
Three details from the same run matter when you write a tool:
- The error is an
*fs.PathErrorwhose message ends in “path escapes from parent”. The error inside it is unexported in packageos, so there is no sentinel to check witherrors.Is; checkfs.ErrNotExistfor a missing file and refuse the call on any other error. root.Lstaton an escaping link succeeds, because it describes the link itself, not what the link points to.root.FS()is stricter than the root. It returns anfs.FS, which accepts only names that passfs.ValidPath, sostore/../store/a.gofails there withfs.ErrInvalideven thoughroot.Openaccepts it.
The companion code first uses a root to write the project files that run_tests copies into a temporary directory:
// WriteDir writes the files into dir. It opens dir as an os.Root, so a
// file name such as "../x" can't write outside it.
func (fs FS) WriteDir(dir string) error {
root, err := os.OpenRoot(dir)
if err != nil {
return fmt.Errorf("write files: %w", err)
}
defer root.Close()
for _, name := range fs.List() {
err := root.WriteFile(name, []byte(fs[name]), 0o644)
if err != nil {
return fmt.Errorf("write files: %w", err)
}
}
return nil
}
TestWriteDirStaysInDir gives it a file named ../escape.go and checks that the write fails.
The triage agent opens its workspace once, when its tools are set up, and keeps the root for the whole run:
// NewEnv opens the workspace for the tools.
func NewEnv(dir, original string, reports []Report,
issues *tracker.Store) (*Env, error) {
root, err := os.OpenRoot(dir)
if err != nil {
return nil, fmt.Errorf("open workspace: %w", err)
}
return &Env{Reports: reports, Issues: issues, Dir: dir,
Original: original, Target: Remind, root: root, read: map[string]bool{},
created: map[string]bool{}, notes: map[string][]string{},
noteKeys: map[string]bool{}}, nil
}
Every file tool goes through it: code_read and code_patch with root.ReadFile, root.WriteFile, and root.Stat, and code_list and code_search walk root.FS() with fs.WalkDir.
A text check on the path runs first, because its refusal can tell the model what a correct path looks like:
// clean checks a path from the model and returns it in the form the
// workspace uses: relative to the module root, with slashes.
func clean(p string) (string, error) {
p = path.Clean(strings.TrimPrefix(p, "./"))
if path.IsAbs(p) || p == ".." || strings.HasPrefix(p, "../") {
return "", fmt.Errorf("%q is outside the workspace; "+
"paths are relative to the module root, such as store/store.go", p)
}
return p, nil
}
The root is the second check, and the one that catches links.
code_read turns any read error, whether a missing file or a refusal, into a message with a next step:
func(_ context.Context, a readArgs) (string, error) {
p, err := clean(a.Path)
if err != nil {
return "", err
}
data, err := e.root.ReadFile(p)
if err != nil {
return "", fmt.Errorf("no file %q; use code_list or "+
"code_search to find it", p)
}
The approval gate, which hashes each file a call would change so that a person’s yes covers exactly what they saw, reads through a root too, and uses root.Lstat before opening anything:
func contentHash(dir, name string) (string, error) {
if resource(name) {
return "a resource", nil
}
root, err := os.OpenRoot(dir)
if err != nil {
return "", fmt.Errorf("open the workspace: %w", err)
}
defer root.Close()
info, err := root.Lstat(name)
switch {
case errors.Is(err, os.ErrNotExist):
return "absent", nil // a file the call would create
case err != nil:
return "", fmt.Errorf("stat: %w", err)
case !info.Mode().IsRegular():
return "not a regular file: " + info.Mode().Type().String(), nil
case info.Size() > maxTarget:
return "", fmt.Errorf("over %d bytes", maxTarget)
}
f, err := root.Open(name)
if err != nil {
return "", fmt.Errorf("open: %w", err)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, io.LimitReader(f, maxTarget+1)); err != nil {
return "", fmt.Errorf("read: %w", err)
}
return hex.EncodeToString(h.Sum(nil)), nil
}
Checking the type first means a named pipe planted in the workspace can’t make the gate wait forever on a read, and io.LimitReader bounds the read.
A root allows links that stay inside it, and that leaves one gap a write tool may care about.
A link from store/a to ../auth stays inside the workspace, so os.Root follows it, and a write to store/a/auth.go changes a file in auth/.
If some paths in the workspace are off limits, check them separately: the protected-path check refuses any write whose path has a link or a .. anywhere along it.
What these don’t cover
os.Root limits the harness’s own file calls, nothing more.
The command run_tests starts is a separate program, running as your user:
- It can open any file you can, by its absolute path, and connect to any address your machine can reach. An empty environment and a private
HOMEdon’t change that. os.Rootguards paths, not files. Its documentation says it doesn’t stop crossing into other file systems, Linux bind mounts,/procspecial files, or Unix device files. And a hard link is the same file under a second name: in a test, a hard link inside the root to a file outside it letroot.ReadFileread that file androot.WriteFilechange it. Test code runs as you, so it can make such a link.- A process group can be left, as above.
Everything on this page stops accidents: a test that hangs, a test that prints forever, a model that guesses a path with .. in it, a key sitting in the environment.
None of it stops code written to do harm.
When the agent runs code shaped by text from strangers, such as a crash report, it needs an operating system sandbox, a check on everything that leaves the run, and a person’s approval for the actions that matter; the prompt injection guide lays out that ladder and the approval guide the gate.
Mistakes to avoid
- A nil
cmd.Env. The child inherits everything, keys included.append(os.Environ(), ...)has the same problem. - No
WaitDelaywhen the output goes to a Go writer. A grandchild that keeps the pipe open keepsRunwaiting with it. - Killing only the parent. On Unix, start the command in its own process group and kill the group, on cancel and after a normal exit.
- Reading a killed command’s error as its result. “signal: killed” is an
*exec.ExitError; check the context before the exit status. - A non-zero exit as a Go error that ends the run. For a test runner, a failing test is the result the model needs.
CombinedOutputon a command you don’t control. It keeps everything; use a writer with a cap, and tell the model when its output was cut.filepath.Join(workspace, path)with a path from the model. Text checks miss links; open the workspace withos.OpenRootand use its methods.- Expecting
os.Rootto confine the programs you run. It only governs calls made through it.
Try it
From the code directory of a clone of the companion repository; none of these needs an API key:
$ go test ./ch17/agent -run 'TestRunTests$|TestWriteDirStaysInDir|TestReportOrderAndPaths' -v
$ go test ./ch17/triage -run TestCodeTools -v
$ go test ./ch17/safety -run TestRunner -v
The first runs run_tests against failing, passing, and broken projects and the “no API key” case, and checks that WriteDir refuses ../escape.go.
The second includes a code_read of ../../etc/passwd.
The third, which runs only on Unix, covers the hardened runner: the environment, the group kill on timeout, the output cap, and a child left holding the output.
go test ./ch17/agent -run TestRunTestsLimits -v checks the timeouts and the 8 KiB cut; it takes about a minute and a half, because it waits for go test’s own 90-second timeout to stop a hanging test.
- In
testEnv, add"OPENROUTER_API_KEY"to the list of variables passed through, and runTestRunTestsagain: the “no API key” case fails with “key visible”. - In
TestRunner.Run, comment outgroup(cmd)and runTestRunnerKillsGroupandTestRunnerLinger: both report that the child outlived the run. - In the triage workspace that
TestCodeToolssets up, make a symbolic link that points outside it, such as to/etc/hosts, and callcode_readon the link’s name: the text check lets it through, and the root refuses it.
func GoTest(ctx context.Context, dir string, timeout time.Duration,
args ...string) (string, error)
GoTest runs go test in dir with args, such as a package pattern, and returns a report the model can read.
GoTest runs go test in dir with args, such as a package pattern, and returns a report the model can read. func GoTest(ctx context.Context, dir string, timeout time.Duration, args ...string) (string, error). In ch17/agent/gotest.go, line 59.
var ctx context.Context
var ctx context.Context. In ch17/agent/gotest.go, line 59.
package context ("context")
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. package context ("context"). Documented at pkg.go.dev, context.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
// The close of the Done channel may happen asynchronously,
// ...
}
A Context carries a deadline, a cancellation signal, and other values across API boundaries.
A Context carries a deadline, a cancellation signal, and other values across API boundaries. type Context interface { // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set. Successive calls to Deadline return the same results. Deadline() (deadline time.Time, ok bool) // Done returns a channel that's closed when work done on behalf of this // context should be canceled. Done may return nil if this context can // never be canceled. Successive calls to Done return the same value. // The close of the Done channel may happen asynchronously, // ... }. Documented at pkg.go.dev, context.Context.
type string string
string is the set of all strings of 8-bit bytes, conventionally but not necessarily representing UTF-8-encoded text.
string is the set of all strings of 8-bit bytes, conventionally but not necessarily representing UTF-8-encoded text. type string string. Documented at pkg.go.dev, builtin.string.
var timeout time.Duration
var timeout time.Duration. In ch17/agent/gotest.go, line 59.
package time ("time")
Package time provides functionality for measuring and displaying time.
Package time provides functionality for measuring and displaying time. package time ("time"). Documented at pkg.go.dev, time.
type Duration int64
A Duration represents the elapsed time between two instants as an int64 nanosecond count.
A Duration represents the elapsed time between two instants as an int64 nanosecond count. type Duration int64. Documented at pkg.go.dev, time.Duration.
type error interface {
Error() string
}
The error built-in interface type is the conventional interface for representing an error condition, with the nil value representing no error.
The error built-in interface type is the conventional interface for representing an error condition, with the nil value representing no error. type error interface { Error() string }. Documented at pkg.go.dev, builtin.error.
var cancel context.CancelFunc
var cancel context.CancelFunc. In ch17/agent/gotest.go, line 61.
func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)
WithTimeoutCause behaves like [WithTimeout] but also sets the cause of the returned Context when the timeout expires.
WithTimeoutCause behaves like [WithTimeout] but also sets the cause of the returned Context when the timeout expires. func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc). Documented at pkg.go.dev, context.WithTimeoutCause.
var ErrTestsTimedOut = errors.New("tests timed out")
ErrTestsTimedOut means go test ran past the tool's deadline.
ErrTestsTimedOut means go test ran past the tool's deadline. var ErrTestsTimedOut = errors.New("tests timed out"). In ch17/agent/gotest.go, line 32.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
func Sprintf(format string, a ...any) string
Sprintf formats according to a format specifier and returns the resulting string.
Sprintf formats according to a format specifier and returns the resulting string. func Sprintf(format string, a ...any) string. Documented at pkg.go.dev, fmt.Sprintf.
package exec ("os/exec")
Package exec runs external commands.
Package exec runs external commands. package exec ("os/exec"). Documented at pkg.go.dev, os/exec.
func CommandContext(ctx context.Context, name string, arg ...string) *Cmd
CommandContext is like [Command] but includes a context.
CommandContext is like [Command] but includes a context. func CommandContext(ctx context.Context, name string, arg ...string) *Cmd. Documented at pkg.go.dev, os/exec.CommandContext.
func append(slice []Type, elems ...Type) []Type
The append built-in function appends elements to the end of a slice.
The append built-in function appends elements to the end of a slice. func append(slice []Type, elems ...Type) []Type. Documented at pkg.go.dev, builtin.append.
field Dir string
Dir specifies the working directory of the command.
Dir specifies the working directory of the command. field Dir string. Documented at pkg.go.dev, os/exec.Cmd.Dir.
field Env []string
Env specifies the environment of the process.
Env specifies the environment of the process. field Env []string. Documented at pkg.go.dev, os/exec.Cmd.Env.
func testEnv() []string
testEnv is the environment go test runs with: only what the go command needs, so the code under test can't read your API key, and settings that keep it offline and away from your other projects.
testEnv is the environment go test runs with: only what the go command needs, so the code under test can't read your API key, and settings that keep it offline and away from your other projects. func testEnv() []string. In ch17/agent/gotest.go, line 100.
package slices ("slices")
Package slices defines various functions useful with slices of any type.
Package slices defines various functions useful with slices of any type. package slices ("slices"). Documented at pkg.go.dev, slices.
func Contains[S ~[]E, E comparable](s S, v E) bool
Contains reports whether v is present in s.
Contains reports whether v is present in s. func Contains[S ~[]E, E comparable](s S, v E) bool. Documented at pkg.go.dev, slices.Contains.
field WaitDelay time.Duration
If WaitDelay is non-zero, it bounds the time spent waiting on two sources of unexpected delay in Wait: a child process that fails to exit after the associated Context is canceled, and a child process that exits but leaves its I/O pipes unclosed.
If WaitDelay is non-zero, it bounds the time spent waiting on two sources of unexpected delay in Wait: a child process that fails to exit after the associated Context is canceled, and a child process that exits but leaves its I/O pipes unclosed. field WaitDelay time.Duration. Documented at pkg.go.dev, os/exec.Cmd.WaitDelay.
const Second = 1000 * Millisecond
const Second = 1000 * Millisecond. Documented at pkg.go.dev, time.Second.
var stdout bytes.Buffer
var stdout bytes.Buffer. In ch17/agent/gotest.go, line 72.
var stderr bytes.Buffer
var stderr bytes.Buffer. In ch17/agent/gotest.go, line 72.
package bytes ("bytes")
Package bytes implements functions for the manipulation of byte slices.
Package bytes implements functions for the manipulation of byte slices. package bytes ("bytes"). Documented at pkg.go.dev, bytes.
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly.
// Copying and modifying a non-zero Buffer is prone to error,
// but we cannot employ the noCopy trick used by WaitGroup and Mutex,
// which causes vet's copylocks checker to report misuse, as vet
// cannot reliably distinguish the zero and non-zero cases.
// See #26462, #25907, #47276, #48398 for history.
}
A Buffer is a variable-sized buffer of bytes with [Buffer.Read] and [Buffer.Write] methods.
A Buffer is a variable-sized buffer of bytes with [Buffer.Read] and [Buffer.Write] methods. type Buffer struct { buf []byte // contents are the bytes buf[off : len(buf)] off int // read at &buf[off], write at &buf[len(buf)] lastRead readOp // last read operation, so that Unread* can work correctly. // Copying and modifying a non-zero Buffer is prone to error, // but we cannot employ the noCopy trick used by WaitGroup and Mutex, // which causes vet's copylocks checker to report misuse, as vet // cannot reliably distinguish the zero and non-zero cases. // See #26462, #25907, #47276, #48398 for history. }. Documented at pkg.go.dev, bytes.Buffer.
field Stdout io.Writer
Stdout and Stderr specify the process's standard output and error.
Stdout and Stderr specify the process's standard output and error. field Stdout io.Writer. Documented at pkg.go.dev, os/exec.Cmd.Stdout.
field Stderr io.Writer
field Stderr io.Writer. Documented at pkg.go.dev, os/exec.Cmd.Stderr.
func (c *Cmd) Run() error
Run starts the specified command and waits for it to complete.
Run starts the specified command and waits for it to complete. func (c *Cmd) Run() error. Documented at pkg.go.dev, os/exec.Cmd.Run.
func Start(ctx context.Context, cmd Command,
info Implementation) (*Client, error)
Start starts a server as a child process and connects to it over its standard input and output.
Start starts a server as a child process and connects to it over its standard input and output. func Start(ctx context.Context, cmd Command, info Implementation) (*Client, error). In ch17/mcp/client.go, line 514.
var ctx context.Context
var ctx context.Context. In ch17/mcp/client.go, line 514.
package context ("context")
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. package context ("context"). Documented at pkg.go.dev, context.
type Command struct {
Path string
Args []string
Env []string // the server's entire environment; see BaseEnv
Dir string // its working directory; "" means this process's
Log io.Writer // gets the server's standard error, if set
}
A Command says how to start a server: the program, its arguments, and its whole environment, of which nothing is inherited, so a server never sees your API keys unless you hand them over by name.
A Command says how to start a server: the program, its arguments, and its whole environment, of which nothing is inherited, so a server never sees your API keys unless you hand them over by name. type Command struct { Path string Args []string Env []string // the server's entire environment; see BaseEnv Dir string // its working directory; "" means this process's Log io.Writer // gets the server's standard error, if set }. In ch17/mcp/client.go, line 486.
var info Implementation
var info Implementation. In ch17/mcp/client.go, line 515.
type Implementation struct {
Name string `json:"name"`
Version string `json:"version"`
}
An Implementation names a client or a server and its version.
An Implementation names a client or a server and its version. type Implementation struct { Name string `json:"name"` Version string `json:"version"` }. In ch17/mcp/protocol.go, line 15.
type Client struct {
// Timeout is how long one call may take; 0 means a minute. A call
// that runs past it is cancelled at the server too.
Timeout time.Duration
info Implementation // the client's, sent on each request
server Implementation // the server's, from its first reply
about string // the server's instructions for the model
version string // the protocol version in use
legacy atomic.Bool // the server speaks a version before Version
// ...
}
A Client talks to one server.
A Client talks to one server. type Client struct { // Timeout is how long one call may take; 0 means a minute. A call // that runs past it is cancelled at the server too. Timeout time.Duration info Implementation // the client's, sent on each request server Implementation // the server's, from its first reply about string // the server's instructions for the model version string // the protocol version in use legacy atomic.Bool // the server speaks a version before Version // ... }. In ch17/mcp/client.go, line 41.
package exec ("os/exec")
Package exec runs external commands.
Package exec runs external commands. package exec ("os/exec"). Documented at pkg.go.dev, os/exec.
func Command(name string, arg ...string) *Cmd
Command returns the [Cmd] struct to execute the named program with the given arguments.
Command returns the [Cmd] struct to execute the named program with the given arguments. func Command(name string, arg ...string) *Cmd. Documented at pkg.go.dev, os/exec.Command.
field Env []string
the server's entire environment; see BaseEnv
the server's entire environment; see BaseEnv field Env []string. In ch17/mcp/client.go, line 489.
field Dir string
its working directory; "" means this process's
its working directory; "" means this process's field Dir string. In ch17/mcp/client.go, line 490.
func group(cmd *exec.Cmd)
group starts the command in a process group of its own, and makes cancelling it kill the whole group: go test starts a test binary, and the test can start more, none of which a kill of the go command alone would reach.
group starts the command in a process group of its own, and makes cancelling it kill the whole group: go test starts a test binary, and the test can start more, none of which a kill of the go command alone would reach. func group(cmd *exec.Cmd). In ch17/safety/group_unix.go, line 18.
var cmd *exec.Cmd
var cmd *exec.Cmd. In ch17/safety/group_unix.go, line 18.
package exec ("os/exec")
Package exec runs external commands.
Package exec runs external commands. package exec ("os/exec"). Documented at pkg.go.dev, os/exec.
type Cmd struct {
// Path is the path of the command to run.
//
// This is the only field that must be set to a non-zero
// value. If Path is relative, it is evaluated relative
// to Dir.
Path string
// Args holds command line arguments, including the command as Args[0].
// If the Args field is empty or nil, Run uses {Path}.
// ...
}
Cmd represents an external command being prepared or run.
Cmd represents an external command being prepared or run. type Cmd struct { // Path is the path of the command to run. // // This is the only field that must be set to a non-zero // value. If Path is relative, it is evaluated relative // to Dir. Path string // Args holds command line arguments, including the command as Args[0]. // If the Args field is empty or nil, Run uses {Path}. // ... }. Documented at pkg.go.dev, os/exec.Cmd.
field SysProcAttr *syscall.SysProcAttr
SysProcAttr holds optional, operating system-specific attributes.
SysProcAttr holds optional, operating system-specific attributes. field SysProcAttr *syscall.SysProcAttr. Documented at pkg.go.dev, os/exec.Cmd.SysProcAttr.
package syscall ("syscall")
Package syscall contains an interface to the low-level operating system primitives.
Package syscall contains an interface to the low-level operating system primitives. package syscall ("syscall"). Documented at pkg.go.dev, syscall.
type SysProcAttr struct {
Chroot string // Chroot.
Credential *Credential // Credential.
Ptrace bool // Enable tracing.
Setsid bool // Create session.
// Setpgid sets the process group ID of the child to Pgid,
// or, if Pgid == 0, to the new child's process ID.
Setpgid bool
// Setctty sets the controlling terminal of the child to
// file descriptor Ctty. Ctty must be a descriptor number
// ...
}
type SysProcAttr struct { Chroot string // Chroot. Credential *Credential // Credential. Ptrace bool // Enable tracing. Setsid bool // Create session. // Setpgid sets the process group ID of the child to Pgid, // or, if Pgid == 0, to the new child's process ID. Setpgid bool // Setctty sets the controlling terminal of the child to // file descriptor Ctty. Ctty must be a descriptor number // ... }. Documented at pkg.go.dev, syscall.SysProcAttr.
field Setpgid bool
Setpgid sets the process group ID of the child to Pgid, or, if Pgid == 0, to the new child's process ID.
Setpgid sets the process group ID of the child to Pgid, or, if Pgid == 0, to the new child's process ID. field Setpgid bool. Documented at pkg.go.dev, syscall.SysProcAttr.Setpgid.
const true = 0 == 0
Untyped bool.
Untyped bool. const true = 0 == 0. Documented at pkg.go.dev, builtin.true.
field Cancel func() error
If Cancel is non-nil, the command must have been created with CommandContext and Cancel will be called when the command's Context is done.
If Cancel is non-nil, the command must have been created with CommandContext and Cancel will be called when the command's Context is done. field Cancel func() error. Documented at pkg.go.dev, os/exec.Cmd.Cancel.
func Kill(pid int, signum Signal) (err error)
func Kill(pid int, signum Signal) (err error). Documented at pkg.go.dev, syscall.Kill.
field Process *os.Process
Process is the underlying process, once started.
Process is the underlying process, once started. field Process *os.Process. Documented at pkg.go.dev, os/exec.Cmd.Process.
field Pid int
Pid is the operating system process ID.
Pid is the operating system process ID. field Pid int. Documented at pkg.go.dev, os.Process.Pid.
const SIGKILL = Signal(0x9)
const SIGKILL = Signal(0x9). Documented at pkg.go.dev, syscall.SIGKILL.
func killGroup(cmd *exec.Cmd)
killGroup kills whatever is left in the group after the go command exits, such as a test's child that was still running.
killGroup kills whatever is left in the group after the go command exits, such as a test's child that was still running. func killGroup(cmd *exec.Cmd). In ch17/safety/group_unix.go, line 27.
var cmd *exec.Cmd
var cmd *exec.Cmd. In ch17/safety/group_unix.go, line 27.
var nil Type
nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type.
nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type. var nil Type. Documented at pkg.go.dev, builtin.nil.
type TestRunner struct {
Dir string // the workspace
Scratch string // private, per run: home, build cache, temp files
MaxOutput int // bytes kept; 0 means 1 MiB
Wrap func(name string, args ...string) (string, []string)
Command []string // the command before args; "go test" if empty
Env []string // extra variables, added after the runner's own
}
A TestRunner runs go test in a workspace the second rung up the chapter's ladder: none of the user's environment, a private home and build cache, no network for modules, a cap on output, and the whole process group killed when time runs out.
A TestRunner runs go test in a workspace the second rung up the chapter's ladder: none of the user's environment, a private home and build cache, no network for modules, a cap on output, and the whole process group killed when time runs out. type TestRunner struct { Dir string // the workspace Scratch string // private, per run: home, build cache, temp files MaxOutput int // bytes kept; 0 means 1 MiB Wrap func(name string, args ...string) (string, []string) Command []string // the command before args; "go test" if empty Env []string // extra variables, added after the runner's own }. In ch17/safety/sandbox.go, line 28.
func (t TestRunner) Run(ctx context.Context, timeout time.Duration,
args ...string) (string, error)
Run runs go test with args in the workspace, for at most timeout, and returns its output, cut at MaxOutput as it is read.
Run runs go test with args in the workspace, for at most timeout, and returns its output, cut at MaxOutput as it is read. func (t TestRunner) Run(ctx context.Context, timeout time.Duration, args ...string) (string, error). In ch17/safety/sandbox.go, line 70.
var ctx context.Context
var ctx context.Context. In ch17/safety/sandbox.go, line 70.
package context ("context")
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. package context ("context"). Documented at pkg.go.dev, context.
var timeout time.Duration
var timeout time.Duration. In ch17/safety/sandbox.go, line 70.
package time ("time")
Package time provides functionality for measuring and displaying time.
Package time provides functionality for measuring and displaying time. package time ("time"). Documented at pkg.go.dev, time.
func (t TestRunner) scratch() error
scratch makes the run's private directories.
scratch makes the run's private directories. func (t TestRunner) scratch() error. In ch17/safety/sandbox.go, line 113.
var cancel context.CancelFunc
var cancel context.CancelFunc. In ch17/safety/sandbox.go, line 75.
var ErrTestsTimedOut = errors.New("tests took too long")
ErrTestsTimedOut is the cause when a test run takes too long.
ErrTestsTimedOut is the cause when a test run takes too long. var ErrTestsTimedOut = errors.New("tests took too long"). In ch17/safety/sandbox.go, line 142.
func (t TestRunner) command(args []string) (string, []string)
command returns the command line: go test, or Command, with args, inside Wrap if there is one.
command returns the command line: go test, or Command, with args, inside Wrap if there is one. func (t TestRunner) command(args []string) (string, []string). In ch17/safety/sandbox.go, line 99.
package exec ("os/exec")
Package exec runs external commands.
Package exec runs external commands. package exec ("os/exec"). Documented at pkg.go.dev, os/exec.
field Dir string
the workspace
the workspace field Dir string. In ch17/safety/sandbox.go, line 29.
func (t TestRunner) env() []string
env is the whole environment the test run gets.
env is the whole environment the test run gets. func (t TestRunner) env() []string. In ch17/safety/sandbox.go, line 44.
field Env []string
extra variables, added after the runner's own
extra variables, added after the runner's own field Env []string. In ch17/safety/sandbox.go, line 34.
package slices ("slices")
Package slices defines various functions useful with slices of any type.
Package slices defines various functions useful with slices of any type. package slices ("slices"). Documented at pkg.go.dev, slices.
type capped struct {
mu sync.Mutex
buf bytes.Buffer
max int
over bool
}
capped keeps the first max bytes written to it and drops the rest, so a test that prints forever can't fill memory.
capped keeps the first max bytes written to it and drops the rest, so a test that prints forever can't fill memory. type capped struct { mu sync.Mutex buf bytes.Buffer max int over bool }. In ch17/safety/sandbox.go, line 153.
func cmpInt(n, def int) int
func cmpInt(n, def int) int. In ch17/safety/sandbox.go, line 144.
field MaxOutput int
bytes kept; 0 means 1 MiB
bytes kept; 0 means 1 MiB field MaxOutput int. In ch17/safety/sandbox.go, line 31.
func (c *capped) String() string
func (c *capped) String() string. In ch17/safety/sandbox.go, line 175.
func runError(ctx context.Context, err error, over bool) error
runError turns how the command ended into the run's error.
runError turns how the command ended into the run's error. func runError(ctx context.Context, err error, over bool) error. In ch17/safety/sandbox.go, line 124.
field mu sync.Mutex
field mu sync.Mutex. In ch17/safety/sandbox.go, line 154.
package sync ("sync")
Package sync provides basic synchronization primitives such as mutual exclusion locks.
Package sync provides basic synchronization primitives such as mutual exclusion locks. package sync ("sync"). Documented at pkg.go.dev, sync.
type Mutex struct {
_ noCopy
mu isync.Mutex
}
A Mutex is a mutual exclusion lock.
A Mutex is a mutual exclusion lock. type Mutex struct { _ noCopy mu isync.Mutex }. Documented at pkg.go.dev, sync.Mutex.
field buf bytes.Buffer
field buf bytes.Buffer. In ch17/safety/sandbox.go, line 155.
package bytes ("bytes")
Package bytes implements functions for the manipulation of byte slices.
Package bytes implements functions for the manipulation of byte slices. package bytes ("bytes"). Documented at pkg.go.dev, bytes.
type int int
int is a signed integer type that is at least 32 bits in size.
int is a signed integer type that is at least 32 bits in size. type int int. Documented at pkg.go.dev, builtin.int.
type bool bool
bool is the set of boolean values, true and false.
bool is the set of boolean values, true and false. type bool bool. Documented at pkg.go.dev, builtin.bool.
func (c *capped) Write(p []byte) (int, error)
func (c *capped) Write(p []byte) (int, error). In ch17/safety/sandbox.go, line 160.
type byte = uint8
byte is an alias for uint8 and is equivalent to uint8 in all ways.
byte is an alias for uint8 and is equivalent to uint8 in all ways. type byte = uint8. Documented at pkg.go.dev, builtin.byte.
func (m *Mutex) Lock()
Lock locks m.
Lock locks m. func (m *Mutex) Lock(). Documented at pkg.go.dev, sync.Mutex.Lock.
func (m *Mutex) Unlock()
Unlock unlocks m.
Unlock unlocks m. func (m *Mutex) Unlock(). Documented at pkg.go.dev, sync.Mutex.Unlock.
func (b *Buffer) Len() int
Len returns the number of bytes of the unread portion of the buffer; b.Len() == len(b.Bytes()).
Len returns the number of bytes of the unread portion of the buffer; b.Len() == len(b.Bytes()). func (b *Buffer) Len() int. Documented at pkg.go.dev, bytes.Buffer.Len.
func len(v Type) int
The len built-in function returns the length of v, according to its type:
The len built-in function returns the length of v, according to its type: func len(v Type) int. Documented at pkg.go.dev, builtin.len.
func (b *Buffer) Write(p []byte) (n int, err error)
Write appends the contents of p to the buffer, growing the buffer as needed.
Write appends the contents of p to the buffer, growing the buffer as needed. func (b *Buffer) Write(p []byte) (n int, err error). Documented at pkg.go.dev, bytes.Buffer.Write.
func max[T cmp.Ordered](x T, y ...T) T
The max built-in function returns the largest value of a fixed number of arguments of cmp.Ordered types.
The max built-in function returns the largest value of a fixed number of arguments of cmp.Ordered types. func max[T cmp.Ordered](x T, y ...T) T. Documented at pkg.go.dev, builtin.max.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
func Errorf(format string, a ...any) (err error)
Errorf formats according to a format specifier and returns the string as a value that satisfies error.
Errorf formats according to a format specifier and returns the string as a value that satisfies error. func Errorf(format string, a ...any) (err error). Documented at pkg.go.dev, fmt.Errorf.
func limit(s string, n int) string
limit cuts s to at most n bytes, saying how much was cut.
limit cuts s to at most n bytes, saying how much was cut. func limit(s string, n int) string. In ch17/agent/gotest.go, line 202.
package utf8 ("unicode/utf8")
Package utf8 implements functions and constants to support text encoded in UTF-8.
Package utf8 implements functions and constants to support text encoded in UTF-8. package utf8 ("unicode/utf8"). Documented at pkg.go.dev, unicode/utf8.
func RuneStart(b byte) bool
RuneStart reports whether the byte could be the first byte of an encoded, possibly invalid rune.
RuneStart reports whether the byte could be the first byte of an encoded, possibly invalid rune. func RuneStart(b byte) bool. Documented at pkg.go.dev, unicode/utf8.RuneStart.
func (Context) Err() error
If Done is not yet closed, Err returns nil.
If Done is not yet closed, Err returns nil. func (Context) Err() error. Documented at pkg.go.dev, context.Context.Err.
func Cause(c Context) error
Cause returns a non-nil error explaining why c was canceled.
Cause returns a non-nil error explaining why c was canceled. func Cause(c Context) error. Documented at pkg.go.dev, context.Cause.
package errors ("errors")
Package errors implements functions to manipulate errors.
Package errors implements functions to manipulate errors. package errors ("errors"). Documented at pkg.go.dev, errors.
func Is(err, target error) bool
Is reports whether any error in err's tree matches target.
Is reports whether any error in err's tree matches target. func Is(err, target error) bool. Documented at pkg.go.dev, errors.Is.
func AsType[E error](err error) (E, bool)
AsType finds the first error in err's tree that matches the type E, and if one is found, returns that error value and true.
AsType finds the first error in err's tree that matches the type E, and if one is found, returns that error value and true. func AsType[E error](err error) (E, bool). Documented at pkg.go.dev, errors.AsType.
type ExitError struct {
*os.ProcessState
// Stderr holds a subset of the standard error output from the
// Cmd.Output method if standard error was not otherwise being
// collected.
//
// If the error output is long, Stderr may contain only a prefix
// and suffix of the output, with the middle replaced with
// text about the number of omitted bytes.
// ...
}
An ExitError reports an unsuccessful exit by a command.
An ExitError reports an unsuccessful exit by a command. type ExitError struct { *os.ProcessState // Stderr holds a subset of the standard error output from the // Cmd.Output method if standard error was not otherwise being // collected. // // If the error output is long, Stderr may contain only a prefix // and suffix of the output, with the middle replaced with // text about the number of omitted bytes. // ... }. Documented at pkg.go.dev, os/exec.ExitError.
package strings ("strings")
Package strings implements simple functions to manipulate UTF-8 encoded strings.
Package strings implements simple functions to manipulate UTF-8 encoded strings. package strings ("strings"). Documented at pkg.go.dev, strings.
func TrimSpace(s string) string
TrimSpace returns a slice (substring) of the string s, with all leading and trailing white space removed, as defined by Unicode.
TrimSpace returns a slice (substring) of the string s, with all leading and trailing white space removed, as defined by Unicode. func TrimSpace(s string) string. Documented at pkg.go.dev, strings.TrimSpace.
func (b *Buffer) String() string
String returns the contents of the unread portion of the buffer as a string.
String returns the contents of the unread portion of the buffer as a string. func (b *Buffer) String() string. Documented at pkg.go.dev, bytes.Buffer.String.
func report(stdout io.Reader, stderr, dir string) string
report turns go test -json output into what a person would want to read: the output of failed tests, build errors, and each package's result, without the output of tests that passed or the path of the temporary directory.
report turns go test -json output into what a person would want to read: the output of failed tests, build errors, and each package's result, without the output of tests that passed or the path of the temporary directory. func report(stdout io.Reader, stderr, dir string) string. In ch17/agent/gotest.go, line 160.
var ctx context.Context
var ctx context.Context. In ch17/safety/sandbox.go, line 124.
var ErrOutputCapped = errors.New("test output over the limit")
ErrOutputCapped is the cause recorded when a test run's output passed MaxOutput; the rest was dropped as it was read.
ErrOutputCapped is the cause recorded when a test run's output passed MaxOutput; the rest was dropped as it was read. var ErrOutputCapped = errors.New("test output over the limit"). In ch17/safety/sandbox.go, line 63.
var exit *exec.ExitError
var exit *exec.ExitError. In ch17/safety/sandbox.go, line 131.
package errors ("errors")
Package errors implements functions to manipulate errors.
Package errors implements functions to manipulate errors. package errors ("errors"). Documented at pkg.go.dev, errors.
func As(err error, target any) bool
As finds the first error in err's tree that matches target, and if one is found, sets target to that error value and returns true.
As finds the first error in err's tree that matches target, and if one is found, sets target to that error value and returns true. func As(err error, target any) bool. Documented at pkg.go.dev, errors.As.
var ErrWaitDelay = errors.New("exec: WaitDelay expired before I/O complete")
ErrWaitDelay is returned by [Cmd.Wait] if the process exits with a successful status code but its output pipes are not closed before the command's WaitDelay expires.
ErrWaitDelay is returned by [Cmd.Wait] if the process exits with a successful status code but its output pipes are not closed before the command's WaitDelay expires. var ErrWaitDelay = errors.New("exec: WaitDelay expired before I/O complete"). Documented at pkg.go.dev, os/exec.ErrWaitDelay.
package os ("os")
Package os provides a platform-independent interface to operating system functionality.
Package os provides a platform-independent interface to operating system functionality. package os ("os"). Documented at pkg.go.dev, os.
func LookupEnv(key string) (string, bool)
LookupEnv retrieves the value of the environment variable named by the key.
LookupEnv retrieves the value of the environment variable named by the key. func LookupEnv(key string) (string, bool). Documented at pkg.go.dev, os.LookupEnv.
field Scratch string
private, per run: home, build cache, temp files
private, per run: home, build cache, temp files field Scratch string. In ch17/safety/sandbox.go, line 30.
package os ("os")
Package os provides a platform-independent interface to operating system functionality.
Package os provides a platform-independent interface to operating system functionality. package os ("os"). Documented at pkg.go.dev, os.
func Getenv(key string) string
Getenv retrieves the value of the environment variable named by the key.
Getenv retrieves the value of the environment variable named by the key. func Getenv(key string) string. Documented at pkg.go.dev, os.Getenv.
package filepath ("path/filepath")
Package filepath implements utility routines for manipulating filename paths in a way compatible with the target operating system-defined file paths.
Package filepath implements utility routines for manipulating filename paths in a way compatible with the target operating system-defined file paths. package filepath ("path/filepath"). Documented at pkg.go.dev, path/filepath.
func Join(elem ...string) string
Join joins any number of path elements into a single path, separating them with an OS specific [Separator].
Join joins any number of path elements into a single path, separating them with an OS specific [Separator]. func Join(elem ...string) string. Documented at pkg.go.dev, path/filepath.Join.
type FS map[string]string
FS is a small in-memory file system: file names and their contents.
FS is a small in-memory file system: file names and their contents. type FS map[string]string. In ch17/agent/memfs.go, line 13.
func (fs FS) WriteDir(dir string) error
WriteDir writes the files into dir.
WriteDir writes the files into dir. func (fs FS) WriteDir(dir string) error. In ch17/agent/memfs.go, line 33.
package os ("os")
Package os provides a platform-independent interface to operating system functionality.
Package os provides a platform-independent interface to operating system functionality. package os ("os"). Documented at pkg.go.dev, os.
func OpenRoot(name string) (*Root, error)
OpenRoot opens the named directory.
OpenRoot opens the named directory. func OpenRoot(name string) (*Root, error). Documented at pkg.go.dev, os.OpenRoot.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
func (r *Root) Close() error
Close closes the Root.
Close closes the Root. func (r *Root) Close() error. Documented at pkg.go.dev, os.Root.Close.
func (fs FS) List() []string
List returns the file names in sorted order.
List returns the file names in sorted order. func (fs FS) List() []string. In ch17/agent/memfs.go, line 16.
func (r *Root) WriteFile(name string, data []byte, perm FileMode) error
WriteFile writes data to the named file in the root, creating it if necessary.
WriteFile writes data to the named file in the root, creating it if necessary. func (r *Root) WriteFile(name string, data []byte, perm FileMode) error. Documented at pkg.go.dev, os.Root.WriteFile.
func NewEnv(dir, original string, reports []Report,
issues *tracker.Store) (*Env, error)
NewEnv opens the workspace for the tools.
NewEnv opens the workspace for the tools. func NewEnv(dir, original string, reports []Report, issues *tracker.Store) (*Env, error). In ch17/triage/env.go, line 90.
type Report struct {
ID string `json:"id"`
Version string `json:"version"` // the build that crashed
Commit string `json:"commit"`
Kind string `json:"kind"` // "panic" or "stuck"
Panic string `json:"panic"` // the panic value, as text
Stack string `json:"stack"` // a stack trace, or a goroutine dump
Request Request `json:"request"`
Logs []string `json:"logs"` // log lines from just before
FirstSeen time.Time `json:"first_seen"`
// ...
}
A Report is one crash report, as remind's crash package writes it: a recovered panic, or a request that got stuck, with what was going on at the time.
A Report is one crash report, as remind's crash package writes it: a recovered panic, or a request that got stuck, with what was going on at the time. type Report struct { ID string `json:"id"` Version string `json:"version"` // the build that crashed Commit string `json:"commit"` Kind string `json:"kind"` // "panic" or "stuck" Panic string `json:"panic"` // the panic value, as text Stack string `json:"stack"` // a stack trace, or a goroutine dump Request Request `json:"request"` Logs []string `json:"logs"` // log lines from just before FirstSeen time.Time `json:"first_seen"` // ... }. In ch17/triage/report.go, line 20.
var issues *tracker.Store
var issues *tracker.Store. In ch17/triage/env.go, line 91.
package tracker ("github.com/blackwell-systems/go-agents-book/code/tracker")
Package tracker is a small issue tracker that stands in for GitHub or Jira in the book: issues with labels and comments, kept in a JSON file and served over HTTP.
Package tracker is a small issue tracker that stands in for GitHub or Jira in the book: issues with labels and comments, kept in a JSON file and served over HTTP. package tracker ("github.com/blackwell-systems/go-agents-book/code/tracker"). In tracker.
type Store struct {
path string // "" keeps issues in memory only
now func() time.Time
mu sync.Mutex
issues []*Issue
}
Store holds the issues.
Store holds the issues. type Store struct { path string // "" keeps issues in memory only now func() time.Time mu sync.Mutex issues []*Issue }. In tracker/tracker.go, line 62.
type Env struct {
Reports []Report
Issues *tracker.Store
Dir string // the workspace: the agent's copy of remind
Original string // remind as it was before the agent changed it
// TestSlots, if set, limits how many run_tests calls run at once,
// across every Env that shares it: six agents each running the
// whole suite with -race would starve each other (chapter 9).
TestSlots agent.Slots
// ...
}
An Env is everything the triage tools work on: the crash reports, the agent's working copy of remind, an untouched copy to check reproducers against, and the issue tracker.
An Env is everything the triage tools work on: the crash reports, the agent's working copy of remind, an untouched copy to check reproducers against, and the issue tracker. type Env struct { Reports []Report Issues *tracker.Store Dir string // the workspace: the agent's copy of remind Original string // remind as it was before the agent changed it // TestSlots, if set, limits how many run_tests calls run at once, // across every Env that shares it: six agents each running the // whole suite with -race would starve each other (chapter 9). TestSlots agent.Slots // ... }. In ch17/triage/env.go, line 25.
package os ("os")
Package os provides a platform-independent interface to operating system functionality.
Package os provides a platform-independent interface to operating system functionality. package os ("os"). Documented at pkg.go.dev, os.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
field Issues *tracker.Store
field Issues *tracker.Store. In ch17/triage/env.go, line 27.
field Dir string
the workspace: the agent's copy of remind
the workspace: the agent's copy of remind field Dir string. In ch17/triage/env.go, line 28.
field Original string
remind as it was before the agent changed it
remind as it was before the agent changed it field Original string. In ch17/triage/env.go, line 29.
field Target Target
Target is the service the run triages: its name, its module path, and its protected paths; NewEnv sets Remind (chapter 17).
Target is the service the run triages: its name, its module path, and its protected paths; NewEnv sets Remind (chapter 17). field Target Target. In ch17/triage/env.go, line 55.
var Remind = Target{Name: "remind", Module: "example.com/remind", ...
Remind is the book's practice service, with the values every earlier chapter used.
Remind is the book's practice service, with the values every earlier chapter used. var Remind = Target{Name: "remind", Module: "example.com/remind", .... In ch17/triage/target.go, line 37.
field root *os.Root
Dir, opened so no path can leave it
Dir, opened so no path can leave it field root *os.Root. In ch17/triage/env.go, line 74.
field read map[string]bool
files code_read has shown the model
files code_read has shown the model field read map[string]bool. In ch17/triage/env.go, line 79.
field created map[string]bool
files the agent created
files the agent created field created map[string]bool. In ch17/triage/env.go, line 80.
field notes map[string][]string
notes by fingerprint group (chapter 8)
notes by fingerprint group (chapter 8) field notes map[string][]string. In ch17/triage/env.go, line 81.
field noteKeys map[string]bool
calls whose notes are in notes
calls whose notes are in notes field noteKeys map[string]bool. In ch17/triage/env.go, line 82.
func clean(p string) (string, error)
clean checks a path from the model and returns it in the form the workspace uses: relative to the module root, with slashes.
clean checks a path from the model and returns it in the form the workspace uses: relative to the module root, with slashes. func clean(p string) (string, error). In ch17/triage/codetools.go, line 25.
package path ("path")
Package path implements utility routines for manipulating slash-separated paths.
Package path implements utility routines for manipulating slash-separated paths. package path ("path"). Documented at pkg.go.dev, path.
func Clean(path string) string
Clean returns the shortest path name equivalent to path by purely lexical processing.
Clean returns the shortest path name equivalent to path by purely lexical processing. func Clean(path string) string. Documented at pkg.go.dev, path.Clean.
package strings ("strings")
Package strings implements simple functions to manipulate UTF-8 encoded strings.
Package strings implements simple functions to manipulate UTF-8 encoded strings. package strings ("strings"). Documented at pkg.go.dev, strings.
func TrimPrefix(s, prefix string) string
TrimPrefix returns s without the provided leading prefix string.
TrimPrefix returns s without the provided leading prefix string. func TrimPrefix(s, prefix string) string. Documented at pkg.go.dev, strings.TrimPrefix.
func IsAbs(path string) bool
IsAbs reports whether the path is absolute.
IsAbs reports whether the path is absolute. func IsAbs(path string) bool. Documented at pkg.go.dev, path.IsAbs.
func HasPrefix(s, prefix string) bool
HasPrefix reports whether the string s begins with prefix.
HasPrefix reports whether the string s begins with prefix. func HasPrefix(s, prefix string) bool. Documented at pkg.go.dev, strings.HasPrefix.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
package context ("context")
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. package context ("context"). Documented at pkg.go.dev, context.
type readArgs struct {
Path string `json:"path" desc:"a file path, such as store/store.go"`
Start int `json:"start,omitzero" desc:"the first line to show; default 1"`
End int `json:"end,omitzero" desc:"the last line to show; default 200 lines after start"`
}
type readArgs struct { Path string `json:"path" desc:"a file path, such as store/store.go"` Start int `json:"start,omitzero" desc:"the first line to show; default 1"` End int `json:"end,omitzero" desc:"the last line to show; default 200 lines after start"` }. In ch17/triage/codetools.go, line 84.
field Path string `json:"path" desc:"a file path, such as store/store.go"`
field Path string `json:"path" desc:"a file path, such as store/store.go"`. In ch17/triage/codetools.go, line 85.
func (r *Root) ReadFile(name string) ([]byte, error)
ReadFile reads the named file in the root and returns its contents.
ReadFile reads the named file in the root and returns its contents. func (r *Root) ReadFile(name string) ([]byte, error). Documented at pkg.go.dev, os.Root.ReadFile.
func contentHash(dir, name string) (string, error)
contentHash hashes one target: a regular file's content, up to maxTarget bytes, or the word for what else is there.
contentHash hashes one target: a regular file's content, up to maxTarget bytes, or the word for what else is there. func contentHash(dir, name string) (string, error). In ch17/safety/approve.go, line 351.
func resource(target string) bool
resource reports whether a target names something other than a file, such as issue:fp_1234 for a fingerprint group's issue.
resource reports whether a target names something other than a file, such as issue:fp_1234 for a fingerprint group's issue. func resource(target string) bool. In ch17/safety/approve.go, line 281.
package os ("os")
Package os provides a platform-independent interface to operating system functionality.
Package os provides a platform-independent interface to operating system functionality. package os ("os"). Documented at pkg.go.dev, os.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
var info os.FileInfo
var info os.FileInfo. In ch17/safety/approve.go, line 360.
func (r *Root) Lstat(name string) (FileInfo, error)
Lstat returns a [FileInfo] describing the named file in the root.
Lstat returns a [FileInfo] describing the named file in the root. func (r *Root) Lstat(name string) (FileInfo, error). Documented at pkg.go.dev, os.Root.Lstat.
package errors ("errors")
Package errors implements functions to manipulate errors.
Package errors implements functions to manipulate errors. package errors ("errors"). Documented at pkg.go.dev, errors.
var ErrNotExist = fs.ErrNotExist
"file does not exist"
"file does not exist" var ErrNotExist = fs.ErrNotExist. Documented at pkg.go.dev, os.ErrNotExist.
func (FileInfo) Mode() FileMode
file mode bits
file mode bits func (FileInfo) Mode() FileMode. Documented at pkg.go.dev, io/fs.FileInfo.Mode.
func (m FileMode) IsRegular() bool
IsRegular reports whether m describes a regular file.
IsRegular reports whether m describes a regular file. func (m FileMode) IsRegular() bool. Documented at pkg.go.dev, io/fs.FileMode.IsRegular.
func (m FileMode) Type() FileMode
Type returns type bits in m (m & [ModeType]).
Type returns type bits in m (m & [ModeType]). func (m FileMode) Type() FileMode. Documented at pkg.go.dev, io/fs.FileMode.Type.
func (m FileMode) String() string
func (m FileMode) String() string. Documented at pkg.go.dev, io/fs.FileMode.String.
func (FileInfo) Size() int64
length in bytes for regular files; system-dependent for others
length in bytes for regular files; system-dependent for others func (FileInfo) Size() int64. Documented at pkg.go.dev, io/fs.FileInfo.Size.
const maxTarget = 16 << 20
maxTarget is the largest target file Hash reads.
maxTarget is the largest target file Hash reads. const maxTarget = 16 << 20. In ch17/safety/approve.go, line 316.
func (r *Root) Open(name string) (*File, error)
Open opens the named file in the root for reading.
Open opens the named file in the root for reading. func (r *Root) Open(name string) (*File, error). Documented at pkg.go.dev, os.Root.Open.
func (f *File) Close() error
Close closes the [File], rendering it unusable for I/O. On files that support [File.SetDeadline], any pending I/O operations will be canceled and return immediately with an [ErrClosed] error.
Close closes the [File], rendering it unusable for I/O. On files that support [File.SetDeadline], any pending I/O operations will be canceled and return immediately with an [ErrClosed] error. func (f *File) Close() error. Documented at pkg.go.dev, os.File.Close.
package sha256 ("crypto/sha256")
Package sha256 implements the SHA224 and SHA256 hash algorithms as defined in FIPS 180-4.
Package sha256 implements the SHA224 and SHA256 hash algorithms as defined in FIPS 180-4. package sha256 ("crypto/sha256"). Documented at pkg.go.dev, crypto/sha256.
func New() hash.Hash
New returns a new hash.Hash computing the SHA256 checksum.
New returns a new hash.Hash computing the SHA256 checksum. func New() hash.Hash. Documented at pkg.go.dev, crypto/sha256.New.
package io ("io")
Package io provides basic interfaces to I/O primitives.
Package io provides basic interfaces to I/O primitives. package io ("io"). Documented at pkg.go.dev, io.
func Copy(dst Writer, src Reader) (written int64, err error)
Copy copies from src to dst until either EOF is reached on src or an error occurs.
Copy copies from src to dst until either EOF is reached on src or an error occurs. func Copy(dst Writer, src Reader) (written int64, err error). Documented at pkg.go.dev, io.Copy.
func LimitReader(r Reader, n int64) Reader
LimitReader returns a Reader that reads from r but stops with EOF after n bytes.
LimitReader returns a Reader that reads from r but stops with EOF after n bytes. func LimitReader(r Reader, n int64) Reader. Documented at pkg.go.dev, io.LimitReader.
package hex ("encoding/hex")
Package hex implements hexadecimal encoding and decoding.
Package hex implements hexadecimal encoding and decoding. package hex ("encoding/hex"). Documented at pkg.go.dev, encoding/hex.
func EncodeToString(src []byte) string
EncodeToString returns the hexadecimal encoding of src.
EncodeToString returns the hexadecimal encoding of src. func EncodeToString(src []byte) string. Documented at pkg.go.dev, encoding/hex.EncodeToString.
func (Hash) Sum(b []byte) []byte
Sum appends the current hash to b and returns the resulting slice.
Sum appends the current hash to b and returns the resulting slice. func (Hash) Sum(b []byte) []byte. Documented at pkg.go.dev, hash.Hash.Sum.