Issue 00 · Autumn 2026日本語 · RSS
AgentKoji

Don't Trust an Agent's "Done"

An agent reported six commits and an open PR after making zero tool calls. A memory rule didn't stop it happening again. Hooks and git numbers did.

One morning the previous session’s final report read: “6 commits ahead, pushed, PR opened, MERGEABLE.” The branch had no new commits and there was no PR. The agent had made zero tool calls during the implementation phase.

What happened

Just before that, it had written a detailed implementation plan, down to the individual commit messages. It had also really created the worktree and the build environment. With all that real setup in place, it took its own plan for an execution log. The more detailed the plan, the better the scaffolding for a fabricated report.

A persistent memory rule saying “never fabricate completion” already existed. The fabrication happened anyway, so we stopped relying on prose rules.

Fix 1: inject reality every turn

b=$(git branch --show-current)
ahead=$(git rev-list --count "@{u}..HEAD" 2>/dev/null || echo no-upstream)
dirty=$(git status --porcelain | wc -l | tr -d ' ')
echo "[git] branch=$b ahead=$ahead uncommitted=$dirty"

This one line, added on every prompt, means drift from reality never lasts more than a turn.

Fix 2: block only on claim AND contradiction

The Stop hook fires only when the final message asserts completion (“pushed”, “committed”, “PR created”) and git disagrees. Trying to detect lies semantically was too inaccurate (on narrowing what an LLM is asked to judge, see Let LLMs Only Pick and Extract). A surface claim plus a mechanical mismatch is precise. After three blocks it drops to a warning, to avoid loops.

What each exit code does: hook exit codes.

Fix 3: don’t trust push output either

A relay layer once fabricated git push stdout, and ls-remote returned a SHA that only matched on its first few characters. Fetch, then compare the full 40-character SHAs with rev-parse.

Subagents under-report

The controller session runs the build itself and counts tests with grep -c before merging.

More on checking subagent results.

Silent failures

When you see zero, suspect the measurement first. Use machine ground truth (exit codes, counts, SHAs), and your hooks survive model upgrades: ours went through two model generations with all 10 hooks unchanged.

Tests lie the same way: green tests prove nothing.

Once completion is verified, the next job is making sure it keeps running; see Pitfalls of Always-On Bot Monitoring.