DEV Community

Jordan Huang
Jordan Huang

Posted on

FAQ: Five Myths About "The Agent Session Finished"

Have you merged a change because the agent typed done?
That single word is not a merge gate.

I keep seeing the same claims in review threads.
They sound very reasonable during a late review.
They still collapse when you inspect the working tree.

This FAQ busts five of those session claims.
Each myth gets a check you can run today.

You do not need a paid cluster for this rehearsal.
A scratch box and a free model already suffice.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.
I reach for MonkeyCode when I need a free model and a free server to rehearse the gate below.
The product stays optional but the gate does not.

Why "finished" keeps lying

Agents write summaries and humans still ship patches.
Those two actions are not the same job.

A session can stop for many boring reasons.
The budget ended, the context filled, or you closed a tab.

Did any of that actually update HEAD?
Did any of that prove the original failure is gone?

The corrected mental model stays blunt on purpose.
Done means artifacts, not prose.

The artifact: session_gate.sh

Do not trust the last chat bubble at all.
Trust a small script that fails closed.

Save the script at the repository root.
Run it before you even read the recap.

#!/usr/bin/env bash
set -euo pipefail

ROOT="${1:-.}"
REPRO="${REPRO_SCRIPT:-$ROOT/repro.sh}"
MANIFEST="${CLAIMED_FILES:-$ROOT/claimed_files.txt}"

cd "$ROOT"

if [[ ! -x "$REPRO" ]]; then
  echo "FAIL: repro.sh missing or not executable"
  exit 2
fi

if ! "$REPRO"; then
  echo "FAIL: repro.sh exited non-zero"
  exit 3
fi

if [[ ! -f "$MANIFEST" ]]; then
  echo "FAIL: claimed_files.txt is missing"
  exit 4
fi

mapfile -t claimed < <(grep -Ev '^(#|$)' "$MANIFEST" | sort)
mapfile -t changed < <(git diff --name-only HEAD | sort)
mapfile -t untracked < <(git ls-files --others --exclude-standard | sort)

echo "=== claimed ==="
printf '%s\n' "${claimed[@]}"
echo "=== changed ==="
printf '%s\n' "${changed[@]}"
echo "=== untracked ==="
printf '%s\n' "${untracked[@]}"

in_tree() {
  local f="$1"
  printf '%s\n' "${changed[@]}" "${untracked[@]}" | grep -Fxq "$f"
}

claimed_has() {
  local f="$1"
  printf '%s\n' "${claimed[@]}" | grep -Fxq "$f"
}

for f in "${claimed[@]}"; do
  if ! in_tree "$f"; then
    echo "FAIL: claimed but not in the tree: $f"
    exit 5
  fi
done

for f in "${changed[@]}"; do
  if ! claimed_has "$f"; then
    echo "FAIL: changed but not claimed: $f"
    exit 6
  fi
done

for f in "${untracked[@]}"; do
  if ! claimed_has "$f"; then
    echo "FAIL: untracked but not claimed: $f"
    exit 7
  fi
done

echo "PASS: repro ran and the tree matches the claim"
Enter fullscreen mode Exit fullscreen mode

Is this elegant production code? Not even close.
Does it catch a cheerful fake finish? Yes it does.

Pair it with a tiny repro.sh file.
That file must exit zero or one.

#!/usr/bin/env bash
set -euo pipefail
# Label: example only. Point this at YOUR failure.
python -m pytest -q tests/test_repro.py
Enter fullscreen mode Exit fullscreen mode

Keep claimed paths in boring plain text.
If a path is missing here, the gate should scream.

# claimed_files.txt
src/billing/invoice.py
tests/test_repro.py
Enter fullscreen mode Exit fullscreen mode

Read the exit codes before you argue with anyone.

Exit codes

  • 2: repro.sh is missing or not executable
  • 3: the repro ran and it failed
  • 4: claimed_files.txt was never written
  • 5: a claimed path never appeared in the tree
  • 6: git diff shows a path nobody claimed
  • 7: an untracked file is sitting outside the claim
  • 8: optional freeze diff, environment drifted

A recap cannot return those numbers.
A shell can. Prefer the shell.

Myth 1: "The session ended, so the work ended"

Who told you the work actually ended there?
The model did, in a paragraph, after it stopped.

A stop is not a valid ship signal.
It is a stop, and nothing more than that.

Ask a colder question before you open the PR.
Did repro.sh pass on this exact tree?

If you need the chat log to answer, stop now.
The log is commentary. The script is state.

Myth 2: "The recap lists files, so those files changed"

Summaries invent paths with a straight face.
Have you grepped a recap path that never existed on disk?

I have, and the working tree did not contain it.
The recap still bragged like a release note.

Run these three commands after every agent session.

git status --porcelain
git diff --name-only
git ls-files --others --exclude-standard
Enter fullscreen mode Exit fullscreen mode

Diff that output against claimed_files.txt by hand.
Any mismatch means the recap is fiction, period.

Do not debate the model about filenames tonight.
Fix the tree, update the claim, then re-run the gate.

Myth 3: "Tests from the same session prove the patch"

Same-session tests are not independent evidence at all.
The agent wrote both sides of the story in one loop.

Can those tests still fail on the old bug?
Often they cannot, because they encode the new code.

So what is the corrected model for this?
Pin a failing check before any edit starts.

Keep that check in repro.sh from the first minute.
If the agent also authors tests, treat them as suspects.

A cheap smell test from a cold shell looks like this.

git diff --name-only HEAD | grep -E '^(src|lib|tests)/' || true
Enter fullscreen mode Exit fullscreen mode

If production code and tests only appear together, pause.
What failed before this session even began?

No prior failure means you still lack a repro.
You have a staged demo, and demos are not gates.

Myth 4: "The scratch host is close enough, so skip pins"

A borrowed host is not close enough for silent trust.
Base images drift, caches cheat, and runtimes move under you.

You borrowed a box so a loop could run.
That does not freeze pip, npm, or apt.

Pin what you can inside the repro itself.

#!/usr/bin/env bash
set -euo pipefail
python --version
pip freeze > /tmp/session-freeze.txt
if [[ -f constraints.txt ]]; then
  if ! diff -u constraints.txt /tmp/session-freeze.txt; then
    echo "WARN: environment drifted. Repro may not travel."
    exit 8
  fi
fi
Enter fullscreen mode Exit fullscreen mode

Must every drift block the merge today?
Not always. Silent drift is the real bug.

Free servers are rehearsal rooms for the gate.
They are not production witnesses and never were.

Myth 5: "The same loop can certify its side effects"

The same loop cannot certify side effects honestly.
It can print "migration applied" as ordinary text.

Who counted the rows besides that same loop?
Who queried the table from another process afterward?

Corrected model: side effects need an outside observer.
Use another process, another query, or another user.

The next snippet is a sketch. Replace the probe.

#!/usr/bin/env bash
set -euo pipefail
before="$(sqlite3 app.db 'select count(*) from invoices;')"
./repro.sh
after="$(sqlite3 app.db 'select count(*) from invoices;')"
echo "invoices before=$before after=$after"
test "$after" -ge "$before"
Enter fullscreen mode Exit fullscreen mode

If you cannot probe the effect, you do not know it.
Hope is not an observer, and logs are not tables.

Decision table

Use this table before anyone types LGTM.

Claim you heard What you run Pass means Fail means
Session finished session_gate.sh Repro plus matching tree Prose only
These files changed git diff --name-only vs manifest Claim equals tree Hallucinated paths
Tests prove it Failing check existed first Requirement pinned Self-authored green
Host is close enough interpreter version plus freeze diff Drift is visible Silent skew
Side effect landed outside probe Count or query moved The log said so

Print the table and tape it near the terminal.
Yes, I am completely serious about that habit.

A twenty-minute drill

Use a throwaway tree for this whole drill.
Do not load production secrets into that box, ever.

  1. Break one test on purpose.
  2. Commit only the failing repro.sh.
  3. Let an agent attempt a fix on a scratch host.
  4. Refuse to read the summary first.
  5. Run session_gate.sh from a cold shell.
  6. Open the diff only after a pass or a clear fail.

What did the summary overclaim this time around?
Write that sentence down. That list is yours.

I run that drill with a free model on a free server.
That pairing is enough to practice the refusal muscle.
Skip the recap. Interrogate the tree instead.

Limitations

This gate does not prove product quality by itself.
It only proves the session story matches the tree.

It will nag you about generated lockfiles constantly.
It will miss a logic bug with a matching manifest.

Who should skip this approach in practice?

  • People merging one-line docs with their own eyes
  • Teams whose CI already diffs claimed paths
  • Anyone hoping a script replaces human review
  • Experiments that pour secrets into a shared prompt

A free server is still someone else's computer.
Treat it like a hotel desk and leave the drawer empty.

Keep this mental model

Stop asking whether the agent finished the session.
Ask which artifact would still pass tomorrow morning.

If the answer is a paragraph, you are not done.
If the answer is repro.sh plus a matching tree, maybe.

Done is a git state you can check twice.
Everything else is a vibe, so treat it that way.

Top comments (0)