Did your agent really sign that review last night?
A green loop is not a human signature.
It is a completion without a durable receipt.
I keep seeing the same six claims in threads.
Each claim sounds calm inside a busy standup.
Each claim dies when you try to replay the run.
This FAQ is a corrected mental model for merges.
It is not a product tour or a scoreboard.
What actually broke?
Agents now write diffs and comment on pull requests.
Teams treat a successful loop as a finished review.
Then the model identifier drifts and the tools drift.
What did you actually pin before that merge?
If the answer is the chat transcript, stop.
You do not have a durable review yet.
You have a story that will not replay.
When I need a lab, I use MonkeyCode's free model access and free server option.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
They are useful for exploration, not for merge gates.
Fail closed in five moves
- Copy the model id from the payload.
- Hash the prompt bytes that git actually stores.
- Hash the tool schema bytes you sent.
- Keep a sidecar next to the pull request.
- Put a human name on the decision file.
Skip a step and you are collecting folklore.
Folklore does not belong in a merge queue.
Myth 1: A finished loop means the model was pinned
Claim
The agent finished, so the model is known.
Why would a successful loop hide its identity?
Evidence you can check
Open the raw response JSON from that run.
Do you see a model field you did not type?
Did you store it beside the git SHA?
Corrected model
Completion is not identity.
A missing model id is a failed pin.
Fail closed, and do not guess a friendly name.
# Proposed check. Label: unexecuted example.
test -n "$AGENT_MODEL_ID" || { echo "unpinned model"; exit 1; }
test -n "$(git rev-parse HEAD)" || exit 1
printf 'model=%s sha=%s\n' "$AGENT_MODEL_ID" "$(git rev-parse HEAD)"
Did you export that id from the API payload?
If you typed it from memory, it is not a pin.
Aliases move. Memory is not a registry.
Myth 2: Tool exit zero means the plan was sound
Claim
Tests passed, so the agent reviewed the design.
The loop even left a confident comment.
Evidence you can check
List the tool calls in order, not the summary.
Which files did they touch on that SHA?
Which invariant never became a tool at all?
Corrected model
Exit zero is a local predicate.
It is not an architecture review.
A wrong plan can still compile and look tidy.
# Proposed trace. Label: unexecuted example.
jq -r '.tool_calls[] | [.name, .exit_code, .path] | @tsv' trace.json
Ask one rude question before you merge.
Which check was never encoded as a tool?
If you cannot name it, the loop did not review it.
Myth 3: Tool schemas stay still because nobody shipped
Claim
We did not deploy, so the tools are frozen.
It is only a lab file on disk, right?
Evidence you can check
Hash the tool JSON you actually sent.
Compare it to the hash on the last merged run.
Did a parameter rename sneak in through docs?
Corrected model
Schemas drift in copies, gists, and "final_v2" files.
A renamed parameter is a silent behavior change.
If you cannot hash it, you cannot replay it.
# Proposed helper fragment. Label: unexecuted example.
import hashlib, json, sys
raw = open(sys.argv[1], "rb").read()
print(hashlib.sha256(raw).hexdigest())
json.loads(raw) # fail if the schema file is not JSON
Would last week's agent even understand this schema?
If you cannot answer, you are not replaying.
You are improvising with similar looking bytes.
Myth 4: The transcript is an architecture decision record
Claim
The agent explained the tradeoff, so we have an ADR.
The prose even sounds like a design review.
Evidence you can check
Can a stranger find the decision next quarter?
Is it in git, with context, owners, and rejected options?
Or is it buried in a collapsed thread?
Corrected model
A transcript is a conversation dump.
An ADR is a dated, owned, reviewable decision.
Those are not the same artifact.
I refuse to treat a chat as the record.
Paste a short ADR next to the diff instead.
# Proposed ADR stub. Label: unexecuted example.
- Status: proposed
- Git SHA:
- Model id from the API, not from memory:
- Prompt sha256:
- Tool schema sha256:
- Decision:
- Rejected options:
- Human reviewer:
Who signs that file at merge time?
A person signs that file, not the loop.
Myth 5: The prompt in Slack is a version
Claim
We pasted the prompt, so it is versioned.
Someone can scroll up and recover it later.
Evidence you can check
Can git show the prompt at that SHA?
Can you prove whitespace and system text match?
Did anyone edit the paste before the run?
Corrected model
Slack is not an object store.
Prompts belong in the repo or another pinned store.
If you cannot git show it, you cannot replay it.
# Proposed layout. Label: unexecuted example.
mkdir -p .agent/runs
git add prompt.txt tools.json
git rev-parse HEAD:prompt.txt
git hash-object prompt.txt
Did the system prompt change under you?
Your Slack snippet will not tell you.
Scrollback is not a content address.
Myth 6: Merge queues can trust unpinned completions
Claim
The queue is automated, so the agent can approve.
Humans will skim it later if needed.
Evidence you can check
What fails the queue on a missing model id?
What fails it on a prompt hash mismatch?
If nothing fails, you built a conveyor for vibes.
Corrected model
Automation without pins is theater.
A merge gate needs identity, inputs, and a human.
Unpinned completions are exploration notes.
# Proposed CI fragment. Label: unexecuted example.
# name: agent-pin
# on: pull_request
# jobs:
# pin:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - run: python3 agent_manifest.py prompt.txt tools.json "${{ github.sha }}" run.json
Would you accept an unsigned release binary?
Then do not accept an unsigned agent review.
Queues amplify whatever you forget to pin.
A manifest you can fail closed
Here is one proposed helper for a run receipt.
It hashes the prompt and the tool schema.
It demands a model id from the environment.
Do not type a model name to make it pass.
Copy the field from the provider payload only.
This is an unexecuted example in your repo.
#!/usr/bin/env python3
"""Proposed agent-run manifest. Unexecuted example in your repo."""
from __future__ import annotations
import datetime as dt
import hashlib
import json
import os
import sys
from pathlib import Path
def sha256_file(path: Path) -> str:
data = path.read_bytes()
return hashlib.sha256(data).hexdigest()
def main() -> int:
if len(sys.argv) != 5:
print(
"usage: agent_manifest.py PROMPT tools.json git_sha out.json",
file=sys.stderr,
)
return 2
prompt_path = Path(sys.argv[1])
tools_path = Path(sys.argv[2])
git_sha = sys.argv[3]
out_path = Path(sys.argv[4])
model_id = os.environ.get("AGENT_MODEL_ID", "").strip()
manifest = {
"git_sha": git_sha,
"model_id": model_id or None,
"prompt_sha256": sha256_file(prompt_path),
"tools_sha256": sha256_file(tools_path),
"recorded_at": dt.datetime.now(dt.timezone.utc).isoformat(),
}
manifest["pinned"] = bool(manifest["model_id"] and manifest["git_sha"])
out_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
if not manifest["pinned"]:
print("FAIL: model id or git sha missing", file=sys.stderr)
return 1
print(out_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())
Wire it like this on a scratch box.
export AGENT_MODEL_ID # from the API body only
python3 agent_manifest.py prompt.txt tools.json "$(git rev-parse HEAD)" run.json
jq -e '.pinned == true' run.json
Compare two runs before you argue about "the agent said."
jq '{model_id, git_sha, prompt_sha256, tools_sha256}' run-a.json run-b.json
Same hashes? You might be talking about the same inputs.
Different hashes? You are arguing about two different labs.
Stop debating the prose until the receipts match.
When is a free lab allowed?
| Situation | Free model + scratch server | Use as merge evidence |
|---|---|---|
| Sketching a tool schema | Yes | No |
| Replaying a pinned bug | Yes, if hashes match | Only with a human |
| Customer-facing release notes | No | No |
| Teaching this checklist | Yes | No |
| Approving a production diff | No | No |
Look hard at that last table row.
Exploration can live on a free endpoint.
Approval still needs a person and a pin.
Who should not use this approach
Skip this if you need certified provenance today.
This receipt is a hash, not a legal signature.
Skip this if no human reads the ADR stub.
A loop cannot own the merge decision.
Skip this if you will invent a model name.
Invented pins are worse than missing pins.
Skip this if your tools can mutate production.
A scratch box is not your blast radius.
Limits you should say out loud
Hashes do not prove the change was good.
They only prove which bytes you claimed to run.
A model id does not freeze weights forever.
Providers can move aliases without a ceremony.
Pin what the payload returns, not a nickname.
A free server is a convenience, not a time capsule.
Do not store secrets there. Do not store customer data.
Do not treat uptime as a reproducibility guarantee.
I still want agents in the inner loop.
I do not want them on the signature line.
The corrected mental model stays simple on purpose.
Agents propose, manifests pin, and humans sign.
If you already explore on a free model endpoint, run this manifest against one open pull request.
Keep the lab a lab.
Top comments (0)