Foundations: Product, Code and the Working Environment
The Terminal, the Local Environment and Running a Program
How to read a record of a command being run, where it ran, which process ran it, what its output streams and exit code mean, what it may have changed,…
Imagine an agent reports: "Tests passed." To judge that sentence, seeing a green tick is not enough. You need to know which command it ran, from which directory, in which shell and environment, which process actually executed it, what it printed, and what exit code it finished with. And even all that still does not prove that the finished feature meets the user's need.
The terminal is the place where a written command can turn into an action: a program can read files, change them, install dependencies, open a network port or reach into data. Once you can read an execution log, you can tell visible evidence from assumption, ask the agent a precise question, and stop a command whose side effects you do not understand.
Learning outcome
By the end of this unit the reader can read a record of a command being run and explain where it ran, which process started it, what its output streams and exit code meant, which files or resources it may have changed, and what its result does not prove. The reader can tell PowerShell, Bash (the Bourne-Again SHell) and a Linux environment on Windows apart, does not move commands between them blindly, can find possible reasons why the agent's environment and their own computer behave differently, and recognises the moments when they should ask for an explanation before an action goes ahead.
Prerequisites. This unit builds on a basic understanding of the product, code and repository from A Map of Digital Product Development and Anatomy of a Code Project, and briefly defines the terms it needs. Git is covered in detail in Git: History, Branches and Safe Changes, isolation boundaries in Worktrees and Isolated Environments, and environments in Environments. You do not need to install tools or carry out any tasks.
Context for the examples. At the time this unit was prepared, the course repository contained a README.md and the course text, but no Node.js application manifest, no test script and no installed project runtime. So the commands and results below are labelled teaching examples from other, hypothetical projects. They are not runs in the course repository.
Terminal, shell, process and agent are different things
A terminal is an interface for typing commands and displaying text results. It was once a separate physical device; today it is usually a window or a tab inside another program. A shell reads the text you type, parses it by its own rules and launches the commands it names. A CLI (command-line interface) is a way of controlling a program through commands and arguments rather than through buttons in a graphical window. A process is a running instance of a program: it has its own identifier, current directory, environment variables, open streams and permissions.
When someone types node app.js in Bash or PowerShell, the shell finds node, passes it the argument app.js and starts a process. Node.js, a runtime that executes JavaScript outside the browser, reads the file and runs it. To understand the action, the actual path of the program, its settings and its permissions also matter.
A command in a log is not the model's inner "thought". Usually the model proposes an action; the product decides whether it may run, in which space and under which permission policy; the shell parses the line; and the process reads or changes resources. That is why safety is shaped by the environment, the user the process runs as, the network, file permissions and the product's settings.
For any record, first ask:
- Which agent client, and which of its interfaces, started the action?
- Where did the command run: on the local computer, in a container, in a separate Git working tree, or on a remote runner?
- Which shell, which working directory, and what exact command?
- Which process executed it, what could it read or change, and how did it finish?
The full log in How to read an agent's log gathers these conditions into one sequence, including the process's identity, its output and the state after it ran.
"The agent ran it on my machine" is too vague. The Codex desktop app distinguishes between local, a Git worktree on this computer, and a remote cloud environment; the Codex CLI, an integrated terminal and Claude Code may have different permissions and ways of launching commands. Check the specific product surface that actually ran the command (Codex environments).
Where exactly a command runs
An environment is the combination of a computer or virtual machine, an operating system, a file system, installed programs, settings, network and permissions. A local process runs directly on the computer. In process isolation mode, a container gets its own view of some of the operating system's namespaces (for example, the file system, network ports and the process ID space), but the container's processes usually share the host's kernel. Some resources may be shared on purpose: that depends on how mounted files, the network namespace, ports or devices are configured. In Windows Hyper-V isolation mode, the container runs inside an optimised virtual machine and effectively has its own kernel. So the access boundaries depend on the platform, the runtime, the isolation mode and the shared resources that have been configured; the word "container" alone does not guarantee full isolation. See Microsoft on containers and virtual machines and Windows Containers isolation modes. A remote runner works on a different machine or in a cloud environment. Each of these may have different files, program versions, credentials and a different meaning of localhost.
localhost means "the same network host that the current process sees". For a process in a container, that may be the container, not the person's laptop. For a remote agent, it may be a cloud machine. Even Windows and a Linux environment on Windows do not always see localhost the same way: how it is reached depends on the version and the networking mode. In WSL 2 (Windows Subsystem for Linux), default port forwarding lets Windows reach a Linux server via localhost, but access in the opposite direction, and the behaviour in mirrored mode, depend on the network configuration (Microsoft documentation on WSL networking).
The current directory is the place relative to which the shell interprets short paths. An absolute path gives a location from the root or the drive; a relative path describes the way from the current directory. For example, src/app.js in the directory /workspace/data-api means /workspace/data-api/src/app.js. The same relative path from /workspace/other points to a different file.
A file extension is the last part of its name after the final dot: for example, .js in app.js or .md in README.md. It often hints at the format or at which program will open the file, but on its own it does not prove the content matches the extension.
Windows and Linux display paths differently. In PowerShell a path may look like C:\Projects\FootballStats\src\app.js; in Bash, like /home/sam/football-stats/src/app.js. In a typical WSL configuration, a Linux process sees the Windows drive as /mnt/c/Projects/FootballStats, while the Linux distribution's home directory is a separate place. Microsoft recommends keeping files where the tool mostly works: on the Windows file system for Windows programs, or on the Linux file system for Linux programs, because crossing between them has different consequences for performance and permissions (Microsoft on working across file systems).
To change to a directory and read the start of a file, match the commands to the shell. Changing directory affects only the current session; the file-reading commands shown do not change the file.
Windows PowerShell:
Set-Location 'C:\Projects\FootballStats'
Get-Location
Get-Content -LiteralPath '.\README.md' -TotalCount 20
Bash on Linux or macOS:
cd "/home/sam/football-stats"
pwd
sed -n '1,20p' README.md
Get-Content -TotalCount 20 and sed -n '1,20p' show the first 20 lines. Replace the sample paths with the verified path of your own project; do not paste these commands into a different shell without adapting them.
The table below gives examples of safe inspection. These are separate commands for specific shells, not a sequence to copy into any terminal.
| What to find out | Windows PowerShell | Linux or macOS, Bash |
|---|---|---|
| Current directory | Get-Location |
pwd |
| List ordinary and hidden files | Get-ChildItem -Force |
ls -la |
| Which Node.js will run | Get-Command node -All |
type -a node |
| Node.js version | node --version |
node --version |
| Where the chosen Node.js lives | node -p 'process.execPath' |
node -p 'process.execPath' |
In PowerShell, commands can be aliases or functions; Get-Command node -All shows the candidates it finds, taking precedence and $Env:PATH into account. Bash looks for a function, a built-in command and an executable file in $PATH; type -a node may show several matches. Check the file that was actually chosen and its path (PowerShell, Bash).
A file's name does not always tell you whether it is visible, either. In Bash, a dot at the start of a name, as in .env, usually means a hidden file; ls -la shows such files too. In PowerShell, the file system provider and the -Force flag have their own semantics. A file may exist even though an ordinary listing does not show it.
How the shell interprets a command
A command line often consists of a program name, arguments and flags. In node --test test/api.test.js, the program name is node, the --test flag switches on test-runner mode, and the last part names a file. The word "command" does not always mean a single executable: the shell may substitute a variable, expand a file pattern, redirect a stream, chain commands together or run several lines in sequence.
Spaces separate arguments unless the shell sees quotes. The path C:\Project Demo\app.js without quotes may fall apart into several pieces. Quotes keep the path as one value, but the quotes themselves can also affect variable substitution. In PowerShell, a single-quoted string is literal, while in a double-quoted one $name is replaced by the variable's value. In Bash, single quotes switch substitution off, while double quotes usually leave variable substitution on. The exact rules are more involved; check them against the shell's documentation rather than inventing extra backslashes at random (Microsoft on quoting in PowerShell and GNU Bash on quoting).
A shell variable such as $HOME or $Env:USERPROFILE can substitute the user's path, but the syntax depends on the shell. In Bash, a variable written in front of a command can change the environment for that command alone; the PowerShell environment variable $Env:APP_MODE belongs to the PowerShell process and is inherited by any child process it launches. Do not move a substitution example with a variable from one shell to another without adapting it.
Redirection changes where a program reads from or writes to. In Bash, > usually creates or overwrites a file, and >> appends to the end of it. Writing 2>&1 merges the standard error stream into standard output, in a specific order of redirections. PowerShell has numbered shell streams, and > applied to a cmdlet's output usually works like piping into Out-File; the behaviour for the standard output of external programs changed in PowerShell 7.4. These differences mean that an operator that looks the same will not always produce the same file or the same bytes. Overwriting a file is a side effect, not decoration on a command (GNU Bash on redirection and Microsoft on PowerShell streams and redirection).
Output, errors and the exit code
Many programs have a separate ordinary output stream, stdout (standard output), and stderr (standard error, the stream for diagnostic messages). Both are usually visible in the window, but the shell may process or redirect them differently. A program may write a warning, or even an error, to stderr and still return a success exit code; another program may print its normal result to stderr. So the channel alone does not determine correctness.
When it finishes, a process returns an exit code: an agreed number for its parent process. In Bash, zero means the command reported success; a non-zero value means it reported an error or some other state. If a command is not found, Bash usually returns 127. The shell keeps the current status in $?, but the next command will replace that value. In PowerShell, $? holds a short success flag for the last action, and $LASTEXITCODE holds the exit code of the last external program. You have to read the status immediately, before another command changes it. Even zero means only that a particular process reported success by its own logic; it confirms neither the business outcome nor complete test coverage (GNU Bash on exit status and Microsoft on $? and $LASTEXITCODE).
The pipeline and status models differ: Bash passes bytes and by default returns the status of the last command; pipefail preserves an earlier command's failure. PowerShell passes objects between cmdlets, and && and || arrived only in version 7. These are different rules, not interchangeable shorthands; the example below shows Bash losing an error (Bash, PowerShell).
When a log is truncated, ended in a timeout, or shows only the last line, the unknown does not equal success. Establish the full command, its working directory, the shell, the time it was allowed, the exit code, any truncation marker, and whether the process actually stopped. If the command started a background process or detached it from the shell, the end of the wait does not prove that the child server has exited. This is a conclusion drawn from the process model, not a promise about how every agent interface behaves: it has to be confirmed by observation in the same environment.
Install, build, run and checks
The words install, build, run, test, lint and format often sit side by side in a project's menu, but they mean different operations. Their exact behaviour is defined by the project's files, the parameters and the tool versions.
| Operation | Typical purpose | Possible side effects to check |
|---|---|---|
Install (install) |
Fetch the project's dependencies | Downloads from the network, changes to the dependency directory, lockfile updates, running package scripts depending on the package manager's version and settings. |
Build (build) |
Turn source into artefacts for running or distribution | Writes to dist/, caches, generated files, source maps or other products; sometimes calls to the network or to services. |
Run (run) |
Execute a program or script | Creating files, reading local data, opening a port, starting a child process or calling an external service. |
Test (test) |
Check part of the expected behaviour | Starting a test server, changing a test database, cleaning up fixtures, writing artefacts, network calls or background processes. |
Lint (lint) |
Find some errors or rule violations in the code | Usually reads files, but plugins and settings may load executable code; check whether the command runs in read-only mode. |
Format (format) |
Rewrite text according to style rules | Changes to one or many files; the diff has to be reviewed before it is accepted. |
In a JavaScript project, package.json is a JSON (JavaScript Object Notation) manifest, and its scripts section defines what npm test and npm run build do; npm gives these commands no universal meaning. The lockfile stores the chosen dependency tree. According to the npm v11 documentation, npm ci requires a lockfile, deletes any existing node_modules, does not change package.json or the lockfile, and fails if the two do not match. It installs dependencies according to the lockfile, but it may run lifecycle scripts of the project and its dependencies, depending on the version and settings. Before re-running it, check what changes to expect (npm ci, lifecycle scripts).
A lockfile improves reproducibility, but it does not make machines identical: tool versions, operating system, architecture, variables and external services can all still differ.
Versions, $PATH and environment variables
One program may have several installations. Package and version managers sometimes use a shim: an intermediary file that picks a version based on the directory or a setting. A new terminal may have a different profile and $PATH from the agent's terminal; "command not found" means that the current shell could not find it.
In the same environment, check which program and version were actually chosen, using the commands from Where exactly a command runs: node --version and node -p 'process.execPath' show Node.js's version and path. A difference is a useful lead, but on its own it does not prove the cause of a failure (Node.js on process and environment variables).
Environment variables are name-value pairs that a process receives when it starts and passes on to its children unless they are explicitly replaced or removed. They can set the application's mode, a service address or a path; the syntax differs between PowerShell ($Env:NAME) and Bash ($NAME, once exported). Whether variable names are case-sensitive, and how variables are stored, also depend on the operating system.
Secrets (keys, passwords, tokens) can live in the environment too, and stay visible to the agent or to a child process. Do not print every variable with env, printenv or Get-ChildItem Env:; for diagnosis, "set / not set" is enough, and a secret should be masked. By default, Claude Code's sandboxed commands inherit the parent process's variables, including credentials; the sandbox.credentials settings can remove or mask them. If the sandbox cannot start because of missing dependencies or an unsupported platform, Claude Code by default warns and runs commands without it; sandbox.failIfUnavailable turns that into an error. Check these modes for the specific product and configuration (Claude Code).
Local server, port and stopping a process
A server is a program that keeps waiting for requests for longer than a single command. It usually listens on a network port within a particular network space. TCP (Transmission Control Protocol) is one of the ways programs establish a connection; the port number, together with the address, helps the system route a connection to the right listener.
EADDRINUSE ("address already in use") means a process could not claim an address or port; the error itself does not establish the cause. It might be another application, a server from an earlier run, a parallel test or a child process, a different network environment, or a bug in the implementation.
The Node.js documentation defines EADDRINUSE as the error raised when the requested port, path or descriptor is already in use (Node.js on the network error). A real report in nodejs/node shows why you should not automatically blame "a forgotten server": the author described an intermittent collision between two cluster workers that created servers on a random port, released it and then tried to use it again. The error appeared more often under high CPU load. This is a specific report and reproduction, with its own Node.js and macOS context, not an explanation of every port conflict and not proof that a different failure has the same cause (nodejs/node issue #53738).
Find the port's owner in the same environment where the server runs. On Windows, Get-NetTCPConnection -LocalPort 4317 -State Listen shows the owning PID; substitute it for the example number in Get-Process -Id 4812. On Linux, ss -ltnp 'sport = :4317' shows the listeners, if the utility is available and permissions allow you to see the process. These commands only read state; next, confirm that the process belongs to the right project (Microsoft).
After a timeout or an interruption, a child process may stay alive. Do not run Stop-Process -Name node, killall node or pkill -f node just because a port error occurred: it may stop someone else's program. First check the PID, the command, the directory, the start time, the parent process and the owner; if the link to the current session is unclear, ask for permission before stopping it.
PowerShell, Bash and WSL do not share a grammar
PowerShell runs on Windows, Linux and macOS, but its shell syntax is still PowerShell. The command $Env:APP_MODE = 'test' sets an environment variable for the current PowerShell session, and Get-Location shows the current location in the file system provider. Its pipeline passes objects, most often the .NET objects produced by cmdlets. Calling an external program such as node returns text, but the PowerShell shell applies its own quoting and redirection rules before passing the arguments on. Some capabilities differ between Windows PowerShell 5.1 and PowerShell 7; for example, the pipeline chain operators && and || were added in PowerShell 7.
Bash, short for Bourne-Again SHell, is a specific shell from the Unix family. It splits text into words, expands variables and paths, and its | operator passes a byte stream to the next command. Bash is widely used on Linux, macOS and WSL, but the fact that Bash is running does not tell you the operating system: a single Windows computer may be running PowerShell, Bash in WSL and a remote Bash session. POSIX.1-2024 defines the pipefail option; a shell that conforms to that edition must support it. Older sh implementations, or shells that target earlier editions of the standard, may not support it. So check the specific shell and its version. The set -o pipefail command in the example below appears in a Bash block; before using it in some other sh, check that it is supported (GNU Bash Reference Manual, POSIX.1-2024: Shell Command Language).
WSL is a separate Linux user space and set of programs, with its own file paths and processes; it is not just a PowerShell mode. File extensions, permissions, letter case, paths, networking and installed binaries can all differ between Windows and Linux in WSL. On a Linux-style file system, access is usually described by read, write and execute bits for the owner, the group and other users; for Windows files mounted under /mnt/c, WSL may compute those bits from the actual Windows permissions unless separate metadata has been enabled. So chmod 777 in WSL cannot grant more access than the Windows account allows. Being able to see a file does not mean the process is allowed to change it (Microsoft on file permissions in WSL).
A record saying "the command ran on Windows" is not enough: find out which shell, and use that shell's syntax. Be especially careful to check commands that delete, elevate privileges or redirect output.
How to read an agent's log
A useful log lets you reconstruct the sequence of events, not just see the final sentence. For every important run, record, or ask to see:
| Field | The question it answers |
|---|---|
| Time and sequence | Which action came before which; are only selected entries being shown? |
| Working directory | Which file or package did the relative paths actually refer to? |
| Product, environment and shell | Which client launched the command; Windows, Linux, a container, WSL or a remote machine; PowerShell or Bash? |
| Command and arguments | Exactly which process was meant to start; were there quotes, variables and redirections? |
| Account and permissions | Whose identity did the command run under; did it have elevated privileges? |
| Process identity (PID and parent process, when it matters) | Which process ran the command, and does it belong to this session? |
| Versions and executable | Which Node.js, npm or other interpreters were actually chosen? |
| Output | What data and errors came back; is truncated output marked as such? |
| Exit code and state after the run | Which code was returned; were servers, child processes or changed files left behind? |
Do not confuse three statements: "the process returned code 0"; "the check covered the scenario that matters"; and "the feature is correct for the user". The first can be proven by the log of a specific run. The second requires knowing the tests and the configuration. The third may require analysing the product's behaviour, the data and the requirements.
Truncated output, a missing stderr, or only the final status of a pipeline is a reason to ask for the full log. Without the directory and the environment, the log does not prove that your files were the ones being checked; the example of a successful last command shows how a successful final process can hide an earlier error.
When to stop a run and ask for an explanation
Stop before running the command, or before further attempts, if you cannot answer these questions: exactly which paths it will touch, in which environment, under whose identity, and whether the state can be restored. Look especially closely at:
- recursive deletion, cleaning or overwriting of directories, and wildcard patterns;
- privilege elevation, running as administrator, or
sudo; - writing outside the working repository, or a command that changes system settings;
- a command that sends files or environment variables over the network, runs an unknown script, or pipes downloaded code straight into a shell;
- installing dependencies in an unfamiliar project, formatting many files, or running tests against a shared or production database;
- ending a process without confirming its PID (process identifier, the number by which the operating system tells running processes apart), its parent process and that it belongs to the current session;
- repeating a command with unknown side effects after a timeout or a partial failure.
Ask the agent to explain the command piece by piece: the shell, the directory, each argument, the variables, the redirections, the network addresses, the files it expects to touch and how to roll back. Then compare the explanation against the actual diff, the log or the process state. Permission to perform one action does not mean permission to repeat it in another environment or to use administrator rights.
Fully worked examples
Example: a test cannot open a port that is already taken
Situation. An agent claims the test stopped because of "an old server". To avoid killing some random process, we first reconstruct the facts.
Environment and log. This is a teaching model of a run of a football match data ingestion service in a Linux container: Bash, Node.js 22.x, npm 11.x, directory /workspace/football-stats-api. All the lines and numbers are invented; the commands were not run, and this is neither a test report nor a copy of the issue.
Environment: Linux container; Bash; Node.js 22.x; npm 11.x; cwd=/workspace/football-stats-api
$ npm ci
added 214 packages
process exit code: 0
$ npm test
> [email protected] test
> node --test
✖ feed ingestion service starts on configured port
Error: listen EADDRINUSE: address already in use 127.0.0.1:4317
ℹ tests 12
ℹ pass 11
ℹ fail 1
process exit code: 1
What this log proves. npm ci finished, and the test script ended with code 1: one of the 12 checks could not listen on 127.0.0.1:4317. The log does not establish who owns the port. Before re-running, check the side effects of npm ci (see Install, build, run and checks) and of the script itself: the test may change test data or leave a child server behind.
Sensible diagnosis in the same Linux environment can start by reading the port's listeners, if ss is installed and the process has sufficient permissions:
ss -ltnp 'sport = :4317'
Suppose the simulated result shows node, PID 4812, the process identifier in this environment at this moment. The number may be reused after a process exits, so also check the launch command, the directory and the parent process. The fact that a process has PID 4812 is not, by itself, grounds for ending it. You need to establish whether it belongs to the test run from /workspace/football-stats-api, how it was started, and that it is not serving another project. If its identity remains unknown, the command should stop and ask the owner to check the process. Do not use killall node or a pattern that ends every Node.js process.
Connection to a real report. In nodejs/node, issue #53738 describes macOS with Node.js 20.12.2: two cluster workers simultaneously created a server on a random port and, after closing it, tried again to bind to the numbers they had been given. The author observed an intermittent EADDRINUSE, more often under load. That report provides a real alternative to the explanation "an old process must have been left over", but it cannot be carried over automatically to our simulated Linux test. The only confirmation here is a local check that ties the port to a specific process and to how repeatable the test failure is.
Alternatives and limits. If the port belongs to a process from this same test session, check the test configuration and how the server is shut down. If the port belongs to another program, agree on an alternative port or a separate test environment. If there is no listener, the error may have been a brief race, or a different container or network namespace was being checked. A dynamic port can reduce conflicts, but on its own it does not rule out a race between parallel workers.
Example: works for the agent, fails locally
Situation. In a Codex chat running in a cloud environment, the test passed, but on the developer's computer the same command ends with a configuration error. The status "tests passed" does not confirm that the same files, shell, versions or variables were involved.
Comparison. The data below is a hypothetical example, not the results of commands that were actually run.
| Observation | Agent's cloud environment, Bash | Developer's computer, PowerShell 7 on Windows |
|---|---|---|
| Working directory | /workspace/checkout |
C:\Projects\Checkout |
| Node.js found | /usr/local/bin/node |
C:\Program Files\nodejs\node.exe |
node --version |
v22.14.0 |
v20.12.2 |
Non-secret APP_MODE |
set | not set |
| Test | passes | stops with APP_MODE is required |
Why this is a plausible explanation but not a final diagnosis. The environments differ in at least the Node.js version and one variable. The code may require a particular version of the syntax or of an API (application programming interface), and the program may switch configuration based on APP_MODE. But the table does not yet tell you which of these differences caused the failure: the file revision, dependencies, a local .env configuration, permissions, the database, the network or path encoding could also have differed.
What to check without printing secrets. In each environment, record the fields from How to read an agent's log, the required versions and whether APP_MODE is present; the commands for the directory and for which Node.js is chosen are given in Where exactly a command runs. For a secret, record only "set / not set", never the value: a full listing of the environment could expose the agent's token.
Decision. The team finds the agreed environment version in the repository, checks the manifest and the lockfile, and runs a script that uses only test data. If the team has established that the cause was the missing APP_MODE, it adds only a permitted test value in the relevant shell or test configuration, rather than changing a system variable for all users. If a different Node.js version is needed, the choice is agreed through the existing version manager or the project's documentation, not installed globally at will.
Conclusion. The same command does not mean the same execution if the environments differ. Codex's documentation explicitly distinguishes local, worktree and remote environments; Claude Code also has its own permissions, sandbox and shell behaviour. Compare controlled values, one factor at a time, without attributing the difference to the model or the product without evidence.
Example: a successful last command hides an earlier error
Situation. In a Bash script, a check searches a file for an expected word and then passes the result on to the next command:
grep -q 'ready' missing.log | cat
echo $?
Simulated Bash output:
grep: missing.log: No such file or directory
0
The first command could not open the file and returned an error. But cat, the last command in the pipeline, received empty input and finished successfully. Under Bash's default rule, a pipeline's status is the status of its last command, so the following echo showed 0. That tells you only about cat, not about a successful search. The GNU Bash documentation specifies that the pipefail option changes the pipeline's status to that of the last command that exited with a non-zero code.
The same check with the pipefail option in the current Bash session:
set -o pipefail
grep -q 'ready' missing.log | cat
echo $?
Now the pipeline's expected code is non-zero, because the grep error is no longer lost behind a successful cat. The simulated error message still goes to stderr. The set -o pipefail command affects the current Bash session and its subsequent pipelines; do not add it to a global shell profile at random.
How this relates to PowerShell. PowerShell passes objects between cmdlets and has its own success rules and an Error stream. In PowerShell 7, the && and || operators decide whether to run the next pipeline based on the status variables $? and $LASTEXITCODE; Windows PowerShell 5.1 does not have these operators. They do not switch on an equivalent of Bash's pipefail. So you cannot see a | and automatically apply Bash's rules. The log has to include the shell's name, the exact command, and the status captured immediately after execution.
What to tell the reviewer. Show the full stderr, the status of each command in the chain, the shell's name, whether pipefail was in effect, and what exactly was being checked (the log fields are listed in How to read an agent's log). The example shows a bug in a particular script, not that every successful pipeline is suspect.
Summary
- The terminal displays an exchange of text; the shell parses the command; the process actually executes it, with a particular directory, environment and permissions.
- First find out where the agent ran: locally, in a container, in WSL, in a working tree, or remotely.
localhost, paths, installed programs and permissions depend on the environment. - A command can have arguments, substitutions, redirections and chains. Quoting, streams and exit codes differ between PowerShell, Bash and a POSIX shell.
install,build,run,test,lintandformathave different purposes and possible side effects; what the scripts actually do is defined by the project.- Versions,
$PATH, environment variables and child processes explain some of the "works for the agent, fails locally" problems. Secret values are never printed into a log. - An exit code is evidence of what the process reported, not confirmation of business correctness. In a port conflict, you find the owner first rather than killing every server.
- If a path, the environment or a side effect is unclear, stop the action and ask for the command to be broken down before it runs. A log also needs the specific product surface, the exact command, the output, the exit code and the state after the run.
Sources and further reading
- Microsoft Learn, about_Pipelines (PowerShell 7.5). Confirms that PowerShell cmdlets pass objects along the pipeline and bind them to the next command's parameters. Documentation. Accessed 23 September 2026.
- Microsoft Learn, about_Quoting_Rules (PowerShell 7.5). Confirms the difference between literal single-quoted and expandable double-quoted strings, and how spaces in arguments are handled. Documentation. Accessed 23 September 2026.
- Microsoft Learn, about_Command_Precedence (PowerShell 7.5). Supports the description of command precedence,
$Env:PATH, aliases and the search for executables. Documentation. Accessed 23 September 2026. - Microsoft Learn, about_Environment_Variables (PowerShell 7.5). Confirms that child processes inherit the process environment, and the difference between Windows and Unix in the case of variable names. Documentation. Accessed 23 September 2026.
- Microsoft Learn, about_Automatic_Variables (PowerShell 7.5). Confirms the role of
$?and$LASTEXITCODE. Documentation. Accessed 23 September 2026. - Microsoft Learn, about_Redirection (PowerShell 7.5). Confirms PowerShell's streams and the change in how redirection of external programs works in PowerShell 7.4. Documentation. Accessed 23 September 2026.
- Microsoft Learn, about_Pipeline_Chain_Operators (PowerShell 7.5). Confirms that
&&and||were added in PowerShell 7 and use PowerShell's status variables. Documentation. Accessed 23 September 2026. - Microsoft Learn, Get-NetTCPConnection (Windows Server 2025 / PowerShell). Confirms reading TCP connections, the local port and the owning process's identifier. Documentation. Accessed 23 September 2026.
- GNU Project, Bash Reference Manual: the sections on quoting, redirection, pipelines, command search, the environment and exit status. Confirms Bash's specific semantics, which should not be carried over to PowerShell or to an arbitrary POSIX-compatible shell. Manual. Accessed 23 September 2026.
- Microsoft Learn, Accessing network applications with WSL, Working across file systems and File Permissions for WSL. Confirm port forwarding, networking modes, mounted paths and the limits on permissions when Windows and WSL interact. Networking, file systems, file permissions. Accessed 23 September 2026.
- Node.js, Process, Environment Variables, Child process and Net API (application programming interface). Confirm the process's path and version, environment variables, launching child processes, and the
EADDRINUSEerror; the API documentation read was version v26.10.0. Process, environment variables, child process, net. Accessed 23 September 2026. - npm, CLI v11.19.1:
npm ciand scripts. Confirms the role of the lockfile, the removal ofnode_modulesbeforenpm ci, and the script life cycle in the stated version of the documentation. npm ci, scripts. Accessed 23 September 2026. - OpenAI, Codex environments, sandboxing, agent approvals & security. Confirms the local, worktree and cloud modes of the Codex desktop app, as well as the current limits on the workspace, network and approvals for the documented clients. Environments, sandboxing, approvals and security. Accessed 23 September 2026.
- Anthropic, Claude Code: Configure permissions and Configure the sandboxed Bash tool. Confirms that permission rules are separate from the system's isolation boundary, and the details of environment inheritance, networking and falling back to running without the sandbox in Claude Code. Permissions, sandboxing. Accessed 23 September 2026.
nodejs/node, Issue #53738: EADDRINUSE error when trying to bind to port after it was closed. A real report by its author, with a reproduction on Node.js 20.12.2 and macOS, of cluster workers competing for a port; not a universal conclusion about all binding errors. Issue. Created 5 July 2024; viewed 23 September 2026.- Microsoft Learn, Containers vs. virtual machines (Windows Server 2016–2025 documentation). Explains the shared kernel typical of containers and the exception for Hyper-V isolation; the description applies to Windows Containers. Documentation. Accessed 23 September 2026.
- Microsoft Learn, Isolation modes: process isolation and Hyper-V isolation (Windows Server 2016–2025 documentation). Explains namespace and kernel isolation, and the possibility of shared access to files, ports, networking and devices; the description applies to Windows Containers. Documentation. Accessed 23 September 2026.
- The Open Group, Shell Command Language (POSIX.1-2024; Institute of Electrical and Electronics Engineers (IEEE) standard 1003.1-2024, 2024 edition). Defines the
pipefailoption and how it affects a pipeline's exit code. Standard. Accessed 23 September 2026.