Did that build actually execute on a machine? Or did the model only describe a build?
I keep seeing this mix-up in agent logs. People paste a command. Then they treat stdout like a host.
That leap is the bug. This FAQ names it.
Why this FAQ exists
Agent transcripts collapse three different planes. The model plane proposes tokens. The tool plane dispatches a call. The host plane runs a process.
Which plane failed last time? Can you prove it from the log alone?
I cannot. Not without a receipt from the host.
The corrected mental model
Keep these three names on a sticky note.
- Model plane: text, including fake command output
- Tool plane: a broker accepted a function call
- Host plane: a kernel started a process
A green story in chat can stop at plane one. A tool JSON can stop at plane two. Only plane three has a hostname.
Ask one rude question after every agent run. Which host executed that argv?
If you cannot answer, you do not have a run. You have a story with punctuation.
Myth 1: The model endpoint is the machine
The claim I keep hearing
The free model "ran" the test suite. People say the endpoint executed bash. Some even say the API is the CI box.
Does that sound familiar? I hear it after every demo.
What the claim ignores
A model endpoint predicts tokens. It does not spawn your compiler. Token success is not process success. HTTP 200 is not wait(2).
The client may live on your laptop. The model may live elsewhere. The command may never start.
Evidence you can collect today
Compare two strings after a run. The model identifier in the client. The hostname in a receipt file.
If those strings match, something is very wrong. A language model is not a Unix host.
# proposed: this names the CLIENT, not the execution host
printf 'client_host=%s\n' "$(hostname)"
printf 'client_user=%s\n' "$(whoami)"
printf 'client_pwd=%s\n' "$(pwd)"
That block only names the client box. It says nothing about the runner. Label it that way in the log.
Corrected model
Treat the model as a proposer. Treat the host as the only runner. Never fuse those identities in a report.
Would you merge a PR because a chatbot described make? I would not.
Myth 2: A free remote server clones your laptop
The claim I keep hearing
Spin a free remote box. Then assume PATH matches home. Assume Node matches home. Assume .env.local boarded the plane.
Why would it? Did you copy those files on purpose?
What the claim ignores
Remote images start clean. Your ~/.zshrc never traveled. Neither did your SSH agent. Neither did your private npm token.
A missing binary looks like an agent failure. It is often just an empty PATH.
Evidence you can collect today
Print the environment the process actually saw. Do it on the execution host. Not in the chat window.
#!/usr/bin/env bash
# proposed: env_delta.sh — run on the EXECUTION host
set -euo pipefail
printf 'hostname=%s\n' "$(hostname)"
printf 'uname=%s\n' "$(uname -srm)"
printf 'shell=%s\n' "${SHELL:-EMPTY}"
printf 'node=%s\n' "$(command -v node || echo MISSING)"
printf 'python=%s\n' "$(command -v python3 || echo MISSING)"
printf 'make=%s\n' "$(command -v make || echo MISSING)"
printf 'HOME=%s\n' "${HOME:-EMPTY}"
printf 'has_env_file=%s\n' "$(test -f .env && echo yes || echo no)"
env | awk -F= '{print $1}' | sort
Did node resolve? Did .env even exist? If both are missing, the transcript lied by omission.
Corrected model
A remote server is a different computer. Copy only what a checklist names. Do not copy by vibes.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. I use that proposer-host split on purpose now. MonkeyCode's free model access and free server option keep the proposer off my laptop. The remote host is still not my laptop. I still print PATH on that host.
Myth 3: A successful tool call means the process finished
The claim I keep hearing
The agent showed a tool result. So the process exited zero. So the suite passed. So we ship.
Who recorded the status byte? The model? The broker? Nobody?
What the claim ignores
Brokers can return truncated stdout. Timeouts can look like answers. Wrappers can swallow SIGPIPE. "Tool ok" is not a POSIX status.
A tool payload can even be prose. Some agents narrate a command. They never invoke it.
Evidence you can collect today
Wrap the argv. Persist the exit code beside the host. Keep the file after the chat ends.
#!/usr/bin/env bash
# proposed: run_receipt.sh — unexecuted until you run it
set -u
hash_cmd() {
if command -v sha256sum >/dev/null 2>&1; then
printf '%s' "$1" | sha256sum | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
printf '%s' "$1" | shasum -a 256 | awk '{print $1}'
else
echo UNAVAILABLE
fi
}
receipt_id="$(date -u +%Y%m%dT%H%M%SZ)-$$"
receipt="/tmp/receipt-${receipt_id}.txt"
cmd_hash="$(hash_cmd "$*")"
{
echo "receipt_id=${receipt_id}"
echo "utc=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "hostname=$(hostname)"
echo "whoami=$(whoami)"
echo "pwd=$(pwd)"
echo "uname=$(uname -srm)"
echo "cmd=$*"
echo "cmd_sha256=${cmd_hash}"
echo "git_head=$(git rev-parse HEAD 2>/dev/null || echo NONE)"
} > "$receipt"
set +e
"$@"
status=$?
set -e
echo "exit=${status}" >> "$receipt"
echo "receipt=${receipt}"
exit "$status"
Usage looks boring. That is the point.
chmod +x run_receipt.sh
./run_receipt.sh true
./run_receipt.sh false
cat /tmp/receipt-*.txt
Did the file contain exit=? If not, you only have a story. Did true record 0? Did false record 1?
If both receipts show exit=0, your wrapper is broken. Fix that before you blame the model.
Corrected model
No exit code, no claim. Tool JSON is a letter. The receipt is the postmark.
I refuse screenshots of chat as proof. I want a file the host wrote.
Myth 4: pwd in the transcript is the deploy directory
The claim I keep hearing
The log printed a path. Shipping that path is safe. The agent "was in the repo."
Was it? Which inode? Which worktree?
What the claim ignores
Agents cd into worktrees. Then they forget to cd back. Symlinks lie. Containers rewrite /. The path you read may be gone.
A logical path can differ from the physical path. Deploying the logical one ships the wrong tree.
Evidence you can collect today
Resolve the path. Then hash a known file. Do both on the same host.
#!/usr/bin/env bash
# proposed: pin_cwd.sh — run on the EXECUTION host
set -euo pipefail
real="$(pwd -P)"
echo "logical=$(pwd)"
echo "physical=${real}"
if git rev-parse --show-toplevel >/dev/null 2>&1; then
echo "toplevel=$(git rev-parse --show-toplevel)"
echo "head=$(git rev-parse HEAD)"
echo "dirty_lines=$(git status --porcelain | wc -l | tr -d ' ')"
fi
if test -f README.md; then
if command -v sha256sum >/dev/null 2>&1; then
echo "readme_sha=$(sha256sum README.md)"
else
echo "readme_sha=$(shasum -a 256 README.md)"
fi
fi
Does logical match physical? Does README still hash the same? If not, stop deploying.
Is dirty_lines zero? If not, the host has uncommitted edits. Chat will not tell you that.
Corrected model
Paths are claims until resolved. Pin the physical directory in the receipt. Pin a content hash too.
A fake transcript, annotated
Here is a composite log I refuse to trust. I annotated each line. None of this is a real customer run.
Agent: I ran npm test and everything passed.
# model plane only — no host, no exit
Tool result: {"ok": true, "stdout": "5 passing"}
# tool plane — broker payload, still no wait(2)
Agent: We are on main in /app.
# path claim — unresolved, no pwd -P, no HEAD
What would I need instead? Three boring lines from the host.
hostname=devbox-7
exit=0
head=4f2c1a9...
Until those exist, I treat the log as fiction. Harsh? Yes. Mergeable? No.
Decision table I actually use
I keep this table next to the script. It is a filter, not a vibe.
| Claim in the transcript | Need before you believe it | Fail closed if missing |
|---|---|---|
| "I ran the command" | hostname + argv hash + exit | No receipt file |
| "Tests passed" | exit 0 from the test runner | Only model prose |
| "We're on main" |
git rev-parse HEAD on that host |
Branch name in chat |
| "Secrets were available" | explicit key names, never values | Assumed .env
|
| "Same as local" |
uname plus tool versions |
Empty remote PATH |
Print the table. Then refuse the merge. Drama drops fast.
What never belongs in the receipt? Token values. Passwords. Private keys. Dump names, not secrets.
# proposed: prove a name exists without printing the value
if test -n "${DATABASE_URL:-}"; then echo DATABASE_URL=set; else echo DATABASE_URL=missing; fi
A one-hour drill
Do not tune prompts first. Drill the host split.
- Open a clean remote shell.
- Clone a tiny repo you control.
- Drop
run_receipt.shinto the repo. - Ask the agent to run
./run_receipt.sh true. - Ask the agent to run
./run_receipt.sh false. - Cat both receipts yourself. Do not ask the agent to summarize them.
- Confirm hostnames match each other.
- Confirm exits are
0then1.
Did the agent skip the wrapper? That is your real defect. Fix the wrapper habit before you fix the model.
Did the agent invent a receipt path? Check /tmp yourself. If the file is missing, the tool plane never reached the host.
Limitations
This receipt is not cryptography. A process can fake hostname. A container can fake uname. Root on that box can edit /tmp.
It also ignores network egress. You still do not know which API the app called. Pair this with scoped tokens. Do not treat a receipt as an audit log.
Clock skew exists. date -u is a hint. It is not a ledger.
Windows hosts need a different script. This one assumes a POSIX shell.
I also do not claim the free server is durable. Persistence is a separate question. This FAQ only pins one process.
Who should not use this
Skip this if you already have hermetic CI. Skip this if every command is a reviewed workflow file. Skip this if you cannot write files on the execution host.
Also skip it for production deploys. A /tmp receipt is a teaching tool. It is not a release control.
If you need multi-tenant isolation, this script will not save you. Get a real sandbox team.
If your agent cannot execute shell at all, stop here. The model plane cannot grow a kernel.
What I want you to steal
Steal the question. Not the branding.
Which host ran that argv? If you cannot answer, you do not have a run. You have a story.
I keep the wrapper small on purpose. Short files get used. Long policy docs do not.
Top comments (0)