On a Tuesday afternoon, a staff engineer blocked ninety minutes for one agent spike on a flaky client. The hypothesis was narrow: the HTTP layer swallowed 429 bodies, so callers never observed a Retry-After header. Twenty minutes later the assistant had renamed two packages, extracted a logger, and invented a circuit breaker. The original timeout still failed in the same way, yet the working tree already looked like a small rewrite.
That scene repeats whenever a coding agent is scored on motion instead of mapped change. Passing tests can hide a novel of unrelated edits, and a calendar alert cannot reconstruct intent from a noisy diff. A ninety-minute spike needs a kill rule that reads the diff the way a reviewer would, hunk by hunk. The protocol below treats every unmapped hunk as a reason to stop, even when the suite is green.
Time-boxed agent work fails in a particular way that ordinary review often notices only after merge. The hypothesis stays buried in a chat scroll, while the tree accumulates helpers that belong on some other ticket. A kitchen timer cannot save a recipe once every spice in the rack has been poured into the pot. The host needs a ledger that binds each unified-diff hunk to the single claim under test, then refuses to ship a mismatch.
The proposed artifact is a host-owned hunk map, kept outside the agent's write path, and checked before merge. The map records one hypothesis sentence, the allowed paths, and a tag for every hunk the host will accept. After each agent turn the host regenerates the diff, matches hunk headers to the map, and either continues or kills. Unlisted files, extra hunks, or tags that ignore the hypothesis all count as unexplained work.
Some teams run the disposable checkout on a separate machine so the laptop remains the only writer of the map. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode currently offers free model access and a free server option that can hold the isolated spike tree. The host still keeps the ledger local, and this article does not claim model names, quotas, or hardware details.
A ledger the agent cannot hold
Before the clock starts, the host freezes a baseline commit and creates two files the agent must not edit. One file states the hypothesis in a single sentence, and the other is the YAML map the checker will read. The commands below assume a clean git worktree dedicated to the spike, not the engineer's everyday branch. The workflow is a proposal with labeled scripts, not a claim that a particular team already ran these numbers.
git worktree add /tmp/spike-429 HEAD
cd /tmp/spike-429
git checkout -B spike/retry-after
mkdir -p .spike src tests
cat > .spike/hypothesis.txt <<'EOF'
HTTP client surfaces Retry-After from 429 responses to callers.
EOF
cat > .spike/hunk-map.yml <<'EOF'
hypothesis: "HTTP client surfaces Retry-After from 429 responses to callers."
deadline_minutes: 90
allowed_paths:
- src/http_client.py
- tests/test_http_client.py
hunks: []
EOF
chmod 0444 .spike/hypothesis.txt
The empty hunks list is intentional at minute zero, because no agent write has been accepted yet. The host will append tags only after reading a real diff, never in advance of the change. That delay is the difference between a plan and a receipt, since a pre-filled map invites fiction. A post-diff map forces the human to look at the hunks that actually landed in the tree.
Host-side commands and a checker
A small checker then becomes the ship-or-kill gate for the rest of the ninety minutes. The script is proposed host-side Python, run on the evidence machine, not inside the agent's tool loop. It parses unified hunks from git diff and requires each file plus header to appear in the YAML map. A tag must reuse a token from the hypothesis so a vague note like cleanup cannot launder extra work.
#!/usr/bin/env python3
# Proposed host-side checker. Label: unexecuted example for a ninety-minute spike.
import subprocess
import sys
from pathlib import Path
import yaml
def load_map(path):
data = yaml.safe_load(path.read_text())
if not data or not data.get("hypothesis"):
raise SystemExit("hunk-map.yml missing hypothesis")
return data
def git_diff():
result = subprocess.run(
["git", "diff", "--unified=3", "HEAD"],
check=True,
capture_output=True,
text=True,
)
return result.stdout
def parse_hunks(diff):
hunks = []
current = None
for line in diff.splitlines():
if line.startswith("diff --git "):
parts = line.split(" b/", 1)
current = parts[1] if len(parts) == 2 else None
continue
if line.startswith("@@ ") and current:
body = line.split("@@")[1].strip()
old_part, new_part = body.split()[:2]
old_start = int(old_part.split(",")[0].replace("-", ""))
new_start = int(new_part.split(",")[0].replace("+", ""))
hunks.append((current, old_start, new_start))
return hunks
def tokens(text):
words = set()
buf = []
for ch in text.lower() + " ":
if ch.isalnum() or ch == "-":
buf.append(ch)
else:
word = "".join(buf)
if len(word) > 3:
words.add(word)
buf = []
return words
def main():
spike_map = load_map(Path(".spike/hunk-map.yml"))
allowed = set(spike_map.get("allowed_paths") or [])
mapped = {}
for entry in spike_map.get("hunks") or []:
key = (entry["file"], int(entry["old_start"]), int(entry["new_start"]))
mapped[key] = entry
hyp_tokens = tokens(spike_map["hypothesis"])
found = parse_hunks(git_diff())
if not found:
print("KILL: no diff against HEAD; the hypothesis was not exercised")
return 2
failures = []
for file_name, old_start, new_start in found:
if file_name not in allowed:
failures.append("unallowed path " + file_name)
continue
key = (file_name, old_start, new_start)
if key not in mapped:
failures.append(
"unmapped hunk %s @@ -%s +%s @@" % (file_name, old_start, new_start)
)
continue
tag_tokens = tokens(str(mapped[key].get("tag", "")))
if not (tag_tokens & hyp_tokens):
failures.append("tag does not mention hypothesis tokens: " + file_name)
found_keys = set(found)
for key in mapped:
if key not in found_keys:
file_name, old_start, new_start = key
failures.append(
"stale map entry %s @@ -%s +%s @@" % (file_name, old_start, new_start)
)
if failures:
print("KILL: unexplained diff")
print("\n".join(failures))
return 1
print("MAP OK: every hunk is tagged to the hypothesis")
return 0
if __name__ == "__main__":
sys.exit(main())
A toy HTTP client makes the rule concrete without pretending to be a production benchmark of any model. The spike may touch only the client module and its test file, which the allowlist already named. If the agent adds a circuit breaker module, the checker fails even if every test passes. The host copies these fixtures into the worktree before starting the clock, then refuses further scope.
# src/http_client.py — baseline fixture for the proposed spike, not production code
from dataclasses import dataclass
@dataclass
class Response:
status: int
headers: dict
body: str
def handle_response(resp: Response) -> dict:
# Callers currently receive status and body only.
return {"status": resp.status, "body": resp.body}
# tests/test_http_client.py — oracle the host runs after minute seventy
from src.http_client import Response, handle_response
def test_429_surfaces_retry_after():
resp = Response(429, {"Retry-After": "12"}, "rate limited")
out = handle_response(resp)
assert out["retry_after"] == "12"
python3 -m pip install pyyaml pytest
python3 -m pytest tests/test_http_client.py -q
python3 .spike/check_hunk_map.py; echo $?
# After an agent turn, rebuild hunks from git diff --unified=3 HEAD
# then rewrite .spike/hunk-map.yml on the host laptop only.
git restore --source=HEAD --staged --worktree . # kill path: drop unexplained writes
Minute zero through seventy belong to implementation, with a map check after every agent turn that writes files. Each passing check only means the current diff is explained, not that the hypothesis is true yet. At minute seventy the host stops tool writes and spends the remaining twenty minutes on tests and the ledger. If the map still has gaps, or the 429 test still fails, the spike is killed and the branch is deleted.
Suppose the agent edits handle_response to parse Retry-After and adds one test that first fails, then passes. The host runs git diff, records two hunks, and writes tags that mention Retry-After and the 429 path. A third hunk that rewrites logging in the same file is unexplained, so the checker exits nonzero and the clock continues only after revert. Green tests never override that revert, because the suite answers correctness while the ledger answers scope.
An accepted map after a clean two-hunk turn might look like the YAML below, rebuilt from the latest headers rather than edited by guesswork. The tags repeat hypothesis tokens on purpose, which is how a cleanup story fails the set intersection in the checker. Stale line numbers from an earlier turn are treated as unexplained work, not as a paperwork nuisance.
hypothesis: "HTTP client surfaces Retry-After from 429 responses to callers."
deadline_minutes: 90
allowed_paths:
- src/http_client.py
- tests/test_http_client.py
hunks:
- file: src/http_client.py
old_start: 14
new_start: 14
tag: "surface Retry-After from 429 responses"
- file: tests/test_http_client.py
old_start: 1
new_start: 1
tag: "callers observe Retry-After on 429"
Hunk headers drift as later edits shift line numbers, so the map is rebuilt from the latest diff each turn. The host does not patch old line numbers by hand, which would hide a second change inside a previous hunk. Allowed paths stay frozen from minute zero, which is how the protocol blocks package renames and extra modules. The baseline commit remains the only rollback target if the spike is killed at minute ninety.
When the protocol should be refused
The protocol does not measure model quality, latency, or cost, and it will not replace design review on large changes. Unified diff struggles with binary files, generated bundles, and reformats that touch every line of a module. A read-only bit on the map is only a convention, so the host should keep the ledger off the agent's writable disk. Teams that need a wide refactor should not use a ninety-minute mapped spike as a theatrical substitute for planning.
On-call engineers handling a live incident should not spend the first hour building a hunk ledger around an agent. Contributors who cannot run git worktrees, or who must add dependencies across twenty packages, will find the gate hostile. The method also misfires when the true question is product direction rather than a falsifiable runtime claim. Those cases need a design note and a human schedule, not a kill switch dressed up as agent evaluation.
A mapped spike is useful when the claim is small enough to die cleanly, and the host is willing to delete the branch. The ledger turns the last git diff into evidence instead of a souvenir of the agent's personality. Teams that want a disposable machine for the checkout can try the free server option and keep the hunk map on a trusted laptop.
Top comments (0)