DEV Community

Sam Yang
Sam Yang

Posted on

The Second Run Was a Different Machine: A Myth-Busting FAQ

A pull request arrived just after lunch with a green local script and a commit message that claimed the suite had been rerun. The reviewer opened the job log and found a hostname that did not match the laptop that sent the patch. The same prompt had been submitted twice, yet the second transcript talked about files the first run had already deleted. That mismatch is the subject of this FAQ, because agent loops make the story easy to misread.

Teams now split a coding task across a laptop, a CI runner, and a complimentary remote workspace without recording which host answered. Free model access makes another attempt feel inexpensive, so the loop continues after the working tree has already changed underfoot. The corrected mental model is blunt: a transcript is a story about some machine, not a proof about yours. The checks below turn that sentence into commands you can run before anyone merges.

When the loop runs against a free server option, the host identity problem becomes ordinary rather than exotic. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is an open-source coding assistant that provides free model access and a free server option for these agent loops. Remove the product name and the receipt workflow still has to exist, because the failure mode is about identity, not branding.

Myth: the same prompt means the same machine

This claim shows up whenever someone reruns a failed agent pass and reads the second transcript as a sequel. Continuity of text is not continuity of disk, clock, installed compilers, or even repository HEAD. A free server can clone a repo at a different commit, drop you in /workspace, and still quote your original relative paths with complete confidence. Ask the process to print a receipt before it is allowed to describe a file as missing, patched, or tested.

The proposed commands are intentionally boring, because boring facts are what the chat window tends to skip. Run them on the host that will execute tools, not on the laptop that composed the prompt if those hosts differ. Save the JSON beside the transcript so a reviewer can see whether "again" meant replay or relocation.

python3 session_receipt.py --label before --out /tmp/receipt-before.json
git rev-parse --show-toplevel
git rev-parse HEAD
git status --porcelain=v1
git rev-parse --abbrev-ref @{upstream} 2>/dev/null || true
uname -n
pwd
Enter fullscreen mode Exit fullscreen mode

Myth: a tool result stays true until the thread ends

Chat UIs present every tool payload as a still photograph that remains hanging on the wall. Filesystems are not galleries; they are kitchens during dinner service, and later turns keep moving the pans. An agent may list routes.py in turn two, delete it in turn five, and still argue from the old listing. The corrected model is that a tool result is a timestamped observation, and observations expire when any later write can touch the same path.

A practical analogy is a restaurant ticket that still says "table four, two soups" after table four has paid and left. The ticket is authentic, and it is still about a world that no longer exists. If you need the listing, hash the file again and compare it to the earlier receipt rather than asking the model. Memory in a thread is a compression of prior tokens, not a lock on the working tree.

#!/usr/bin/env python3
"""Proposed session receipt helper. Labelled example; not a production service."""
from __future__ import annotations

import argparse
import hashlib
import json
import os
import socket
import subprocess
import time
from pathlib import Path

SKIP_PARTS = {".git", "node_modules", ".venv", "dist", "__pycache__"}


def git(args: list[str]) -> str:
    result = subprocess.run(
        ["git", *args],
        check=False,
        capture_output=True,
        text=True,
    )
    return result.stdout.strip()


def hash_tree(root: Path) -> str:
    digest = hashlib.sha256()
    files = sorted(
        p for p in root.rglob("*")
        if p.is_file() and SKIP_PARTS.isdisjoint(p.parts)
    )
    for path in files:
        rel = path.relative_to(root).as_posix()
        digest.update(rel.encode())
        digest.update(b"\0")
        digest.update(path.read_bytes())
    return digest.hexdigest()


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--label", required=True)
    parser.add_argument("--out", required=True)
    args = parser.parse_args()
    top = git(["rev-parse", "--show-toplevel"]) or os.getcwd()
    root = Path(top)
    payload = {
        "label": args.label,
        "captured_at_unix": int(time.time()),
        "hostname": socket.gethostname(),
        "cwd": os.getcwd(),
        "git_head": git(["rev-parse", "HEAD"]),
        "git_branch": git(["branch", "--show-current"]),
        "git_status": git(["status", "--porcelain=v1"]),
        "upstream": git(["rev-parse", "--abbrev-ref", "@{upstream}"]),
        "tree_sha256": hash_tree(root),
    }
    Path(args.out).write_text(json.dumps(payload, indent=2) + "\n")
    print(args.out)


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Myth: a clean status in chat means origin moved

Agents often paraphrase git status into English that sounds like a completed delivery to production. Working tree clean is a statement about one index on one clone, not a promise that origin moved. On a free server the clone may be a shallow fetch with no remote you can push toward. The reviewer still has to compare HEAD, the upstream ref, and the reflog before trusting a summary.

python3 session_receipt.py --label after --out /tmp/receipt-after.json
git log --oneline -5 --decorate
git reflog -5
git status -sb
git rev-list --left-right --count origin/HEAD...HEAD 2>/dev/null || echo "no-origin-count"
python3 - <<'PY'
import json
from pathlib import Path
before = json.loads(Path("/tmp/receipt-before.json").read_text())
after = json.loads(Path("/tmp/receipt-after.json").read_text())
keys = ("hostname", "cwd", "git_head", "tree_sha256")
for key in keys:
    left, right = before.get(key), after.get(key)
    state = "match" if left == right else "DIVERGED"
    print(f"{key}: {left!r} -> {right!r} ({state})")
PY
Enter fullscreen mode Exit fullscreen mode

If hostname or cwd diverges, the second run is not a retry; it is a different laboratory that happened to receive similar English. If git_head matches and tree_sha256 diverges, the commit name is stable while the files are not, which is a classic dirty-or-ignored-file surprise. If both hashes match and origin has not advanced, the agent may have produced a locally clean tree that nobody else can see. None of those outcomes should be blamed on model quality until the two receipts have been compared.

Myth: cheap retries replace a pinned fixture

Free model access tempts teams to treat another generation as cheaper than writing a fixture that would make the claim testable. The cost that actually grows is reviewer time, because each retry can change host and HEAD while the prompt stays frozen. A pinned fixture is a receipt plus an assertion of hostname, cwd, and HEAD, or a note that those values may change. Without that assertion, another try is not an experiment, and it remains only a new anecdote.

The small matrix below is the whole method, and you should refuse the agent claim when a check fails. This is a proposed review gate, not a measured benchmark from a production program.

Agent claim Required check If it fails, the claim is
Same retry as last turn hostname and cwd equal in both receipts a different machine telling a sequel
Tests were run here tree hash plus a runner exit code on that host prose about a runner you cannot name
Ready to merge HEAD equals the reviewed commit and origin has the same SHA a clean island
File still missing a fresh listing after the last write nostalgia for turn two

What this approach is not

Do not send receipts, source, or env files to a free server if your organization forbids third-party execution of that tree. The receipt proves identity, and it does not prove confidentiality, isolation, or supply-chain hygiene on its own. People working on regulated code, unpublished keys, or customer data should keep the loop on hosts they already control. They should not treat a complimentary workspace as a harmless substitute for that operational control.

The script hashes file bytes and can become slow on huge monorepos, so scope it to the package the agent was asked to touch. It also does not detect lies inside a test runner, skipped tests, or network stubs that return theatrical success. Pair it with the tests you already trust, and distrust any summary that cannot point at a receipt pair. Treat every unmatched hostname as a new investigation rather than as extra creativity from the model.

If you already run these loops on MonkeyCode's free server option, attach the before and after JSON before you accept a second-pass summary. The useful habit is the same on any host: name the machine, name the commit, and only then argue about what the model understood. A transcript without those names is still useful as a draft, and it is not yet evidence. Reviewers can reject a green-sounding story when the two receipt files cannot be produced.

Top comments (0)