The log ends mid-line. There is no traceback, no exit code from your job, and no clue about which write failed — only a half-finished record on a machine you do not administer. You re-run the same job on your laptop, it passes, and that is the moment you learn that "it works" was never the claim you needed to verify.
Agent jobs on shared hardware rarely fail because the model is wrong. They fail because the workspace turned read-only, the clock drifted far enough to expire a token, egress to one host is blocked, or two runs are interleaving writes into the same directory. None of that appears in a capability review, because these are not questions about what the box can do. They are questions about what you quietly assumed it would keep doing.
This playbook converts those assumptions into five checks, three roles, and a single wiki page you can paste before a job is allowed to start.
1. Placement is a decision, not a default
Most teams answer one question — "can this machine run the job?" — and skip the second one, "should this job live here at all?" The first question gets answered by a successful run, which is a sample of size one on a machine you do not own.
Treat placement as a written decision with a named owner. The artefact does not need to be long: a run line, five checked assumptions, and a hash of the report that proved them. What matters is that the decision is recorded somewhere a reviewer can read without opening your terminal history.
Why this lands harder on a shared or free tier
When you rent a dedicated host, the environment is boring and you stop thinking about it. When you use a free tier, the trade you accepted is exactly that the box is shared, may be recycled, and is not yours to configure. MonkeyCode offers free model access and a free server option, which drops the cost of an agent run close to zero — and that is precisely the condition under which a team stops writing down its environmental assumptions, because retrying feels cheaper than documenting.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
I am deliberately not printing a token allowance or a hardware spec here. Those numbers change, and a stale figure in a tutorial becomes a bug report three weeks later. Check the project's own page for the current limits before you plan around them.
2. The five assumptions worth checking
Each row is cheap to verify and expensive to discover late.
| Assumption | What breaks without it | Cheap check |
|---|---|---|
| The workspace is writable with headroom | a mid-run write fails, or the disk fills silently |
touch a probe file, then df -Pm
|
| The wall clock is roughly correct | token expiry, cache TTLs, and log ordering all lie | compare to an HTTP Date header |
| Every host the job needs is reachable | the call hangs until your timeout, not the server's | one HEAD request per host |
| The credential exists and is never printed | an hour lost to 401s you cannot see | length check plus a short hash |
| You are the only writer for this run | two agents interleave writes into one directory |
flock on a run lock |
Note what is absent: no performance measurement, no model comparison, no throughput claim. This is an assumption ledger, not a benchmark. If you want the capacity side, that is a separate exercise with a separate owner.
3. Three roles and two handoffs
Keep the role count low enough that a small team will actually honour it.
Run Owner. Writes the run line, owns the five assumptions, pastes the preflight report into the wiki page. Does not get to declare the job ready.
Box Steward. Owns the shared host or tier, maintains the egress allowlist, and is the only person who can sign an exception. When a check fails and the job must still run, the steward writes down which assumption is being waived and until when.
Reviewer. Reads the run line and the report hash before the work merges. The reviewer's job is not to re-run the script; it is to notice when a report is three days old or when the run line quietly grew a second egress host.
There are two handoffs. The first is Run Owner → Box Steward when any line reports FAIL. The second is Run Owner → Reviewer when the gate passes. Both handoffs are wiki edits, not messages, so the next person to touch the job inherits the reasoning.
4. Step 1 — drop the preflight script into the repo
The script below asserts the five assumptions and prints one line per check. It is intentionally boring, and it exits non-zero whenever anything fails.
#!/usr/bin/env bash
# preflight.sh — assert five assumptions before an agent job starts.
# Usage: WORKDIR=./work EGRESS_HOSTS="api.example.com" ./preflight.sh > preflight.report
set -uo pipefail
WORKDIR="${WORKDIR:-$PWD/work}"
EGRESS_HOSTS="${EGRESS_HOSTS:-api.example.com}"
SECRET_VAR="${SECRET_VAR:-MODEL_API_KEY}"
MIN_FREE_MB="${MIN_FREE_MB:-512}"
MAX_SKEW_S="${MAX_SKEW_S:-120}"
LOCK="${LOCK:-/tmp/agent-run.lock}"
fail=0
line() { printf '%-8s %-22s %s\n' "$1" "$2" "$3"; }
ok() { line OK "$1" "$2"; }
bad() { line FAIL "$1" "$2"; fail=1; }
# 1 — workspace is writable with headroom
if mkdir -p "$WORKDIR" && touch "$WORKDIR/.probe" 2>/dev/null; then
rm -f "$WORKDIR/.probe"; ok workspace.writable "$WORKDIR"
else
bad workspace.writable "$WORKDIR not writable"
fi
free_mb=$(df -Pm "$WORKDIR" 2>/dev/null | awk 'NR==2 {print $4}')
if [ -n "${free_mb:-}" ] && [ "$free_mb" -ge "$MIN_FREE_MB" ]; then
ok workspace.headroom "${free_mb}MB free"
else
bad workspace.headroom "${free_mb:-unknown}MB free, need ${MIN_FREE_MB}"
fi
# 2 — wall clock agrees with a public reference within tolerance
first_host=${EGRESS_HOSTS%% *}
local_epoch=$(date -u +%s)
remote_epoch=$(curl -sS -m 5 -I "https://$first_host" 2>/dev/null \
| awk 'BEGIN{IGNORECASE=1} /^date:/ {sub(/^[Dd]ate: +/, ""); print}' \
| tr -d '\r' | xargs -r date -u -d)
if [[ "${remote_epoch:-}" =~ ^[0-9]+$ ]]; then
skew=$(( local_epoch > remote_epoch ? local_epoch - remote_epoch : remote_epoch - local_epoch ))
if [ "$skew" -le "$MAX_SKEW_S" ]; then ok clock.skew "${skew}s"; else bad clock.skew "${skew}s over tolerance"; fi
else
bad clock.skew "no usable Date header from $first_host"
fi
# 3 — every host the job needs answers
for host in $EGRESS_HOSTS; do
code=$(curl -sS -m 8 -o /dev/null -w '%{http_code}' "https://$host" 2>/dev/null || echo 000)
if [ "$code" != "000" ]; then ok egress.$host "HTTP $code"; else bad egress.$host "unreachable"; fi
done
# 4 — credential present, never printed
value="${!SECRET_VAR:-}"
if [ "${#value}" -ge 16 ]; then
digest=$(printf '%s' "$value" | sha256sum | cut -c1-10)
ok secret.present "$SECRET_VAR sha256:$digest"
else
bad secret.present "$SECRET_VAR missing or too short"
fi
# 5 — this process is the only writer for this run
exec 9>"$LOCK"
if flock -n 9; then ok run.lock "acquired $LOCK"; else bad run.lock "$LOCK held elsewhere"; fi
if [ "$fail" -eq 0 ]; then echo "SUMMARY PASS"; else echo "SUMMARY FAIL"; fi
exit "$fail"
Two portability notes before you copy this. It assumes bash 4 or newer for the indirect expansion ${!SECRET_VAR}, plus GNU coreutils and flock; on macOS, replace the date -u -d reference parse with gdate or a small Python one-liner. Keep the script in the job repo rather than in someone's home directory, so the reviewer can read exactly what was checked.
5. Step 2 — gate the run on a fresh report
A preflight nobody is required to read is a decoration. Wrap it in a gate that refuses to start the job without a complete report.
#!/usr/bin/env bash
# gate.sh — refuse a run unless the latest preflight report is complete.
set -euo pipefail
report="${1:?usage: gate.sh <preflight.report>}"
run_id="${2:-$(date -u +%Y%m%dT%H%M%SZ)}"
if grep -q '^FAIL' "$report"; then
echo "gate: fail run=$run_id see $report" >&2
exit 1
fi
for key in workspace clock egress secret run; do
if ! grep -qE "^OK[[:space:]]+${key}" "$report"; then
echo "gate: missing OK for $key run=$run_id" >&2
exit 1
fi
done
stamp=$(sha256sum "$report" | cut -c1-12)
echo "gate: pass run=$run_id report=$stamp" | tee -a "$report"
The gate checks that each assumption reported at least one success, so a report where egress checks were skipped cannot pass on the strength of the other four. The report hash is what you paste into the wiki; it is short enough to read and long enough to spot a swapped file.
6. Step 3 — paste the one-page run into the wiki
This is the whole page. Fill it in once per job, then edit it in place forever.
# Agent run: <job-name>
Owner: @<handle> (run line, five assumptions)
Box steward: @<handle> (shared host or tier, signs exceptions)
Reviewer: @<handle> (reads report + run line before merge)
## Run line
job=<job-name> box=<host-or-tier> workdir=<path> egress=<host1,host2> \
secret=<VAR> deadline_utc=<YYYY-MM-DDTHH:MMZ>
## Steps
1. Preflight lives in the repo at `scripts/preflight.sh`. No local-only copies.
2. Run: `WORKDIR=<path> EGRESS_HOSTS="<hosts>" ./scripts/preflight.sh > preflight.report`
3. Paste the `SUMMARY` line and the report hash here.
4. Any `FAIL` → open an exception row with the steward. Do not retry first.
5. Run `scripts/gate.sh preflight.report`. Paste its output. Only then start the job.
6. After the run, append exit code, wall time, and the first line that scrolled past you.
## Exceptions
| date | assumption waived | signed by | expires |
| --- | --- | --- | --- |
| | | | |
Step 6 matters more than it looks. The first line that scrolled past is usually the one that explains the next failure, and it costs nothing to write down while the run is still fresh.
7. Step 4 — decide where the job belongs
Use the same page to argue about placement instead of relitigating it in chat.
| Job shape | Where it belongs | Why |
|---|---|---|
| Interactive debugging, minutes, one credential | your laptop | fastest loop, no shared state |
| Repeatable batch, small working set | a shared free box | cheap and disposable, and preflight keeps it honest |
| Needs a stable public IP or inbound port | a host you administer | shared egress is not yours to promise |
| Large local dataset, GPU, big temp space | a sized host you pay for | disk assumptions break quietly |
| Secret-heavy or regulated data | your own environment | five checks are not a security review |
Write the chosen row into the run line as box=. A future you reading the page will not remember why the job moved, and the table is a cheaper reminder than a postmortem.
Limitations and who should skip this
The preflight is a point-in-time assertion. It proves the environment at the second you ran it, and a shared box can change its mind ten minutes later; the gate is a speed bump, not a guarantee. It measures nothing about performance, quality, or cost. And the flock check is advisory — it stops cooperating runs, not a determined one.
Skip this playbook if configuration management or existing health checks already cover your hosts, if you run single-tenant infrastructure you fully control, or if your job starts in under a second and cannot tolerate a probe. Likewise skip it if the workload is regulated: the five checks tell you the box is usable, not that it is auditable.
If you want a target to practise on before you write the real page, the free model access and free server option in the MonkeyCode project give you a machine you can throw away while you learn what your job actually assumes — start with the project's own documentation so your run line matches the current limits.
Top comments (0)