One chat window, one model, every job: the idea, the structure, the code and the final check. Sooner or later, though, the same model can quietly approve its own mistake, and a leaked key, an unmeasured number or a broken link goes out with your name on it.
What changed things for us was not a better prompt. It was a separation of powers. The model that builds never grades its own work. A second reader sees only the finished text and the rules. And the checks that must never be argued with are done by plain code at commit time. In our own test, that commit-time leak gate stops 32 of 32 planted samples and raises 0 false alarms on 17 clean look-alikes. That is a figure for planted samples, not a detection rate for real folders.
Here is the setup, piece by piece, so you can rebuild it with the models you already use.
Why one window degrades
Three things go wrong when one conversation does everything.
- Context mixing. The brainstorm, the pricing math and the deployment details share one window. Every new answer is shaped by everything before it, including the parts that were wrong.
- Self-grading. A model asked to review what it just wrote tends to agree with itself. It has read its own reasoning, so it walks the same path to the same blind spot.
- Nobody says no. There is no role whose job is to refuse. Speed wins every argument.
None of this is a model problem. It is an organisational one, and the old fix applies: split the powers.
Four roles, one decision-maker
| Role | Owns | Does not |
|---|---|---|
| Vision | the story, the market, the plan | write the final code |
| Structure | math, prices, edge cases, a second opinion | approve its own numbers |
| Execution | code, files, film, the scheduled runs | grade its own output |
| Blind audit | reading the artifact against the rules | see the maker's reasoning |
Above them sits one person who decides: execute, abort, or mutate with a stated reason. As one example of an assignment, in our setup a long-context model takes vision, a second vendor's model takes structure and, in a separate session, the blind review, and a coding agent executes. The assignment matters less than the rule that no role holds two powers.
Handoffs that keep the powers apart
Separation only works if the handoffs are clean.
- Write a brief, not a chat. Inputs, constraints, a definition of done, and what must not be touched. A brief can be read cold by a model that saw nothing else.
- Seal your own answer first. Before asking for review, the maker writes its proposal and seals it. Then the reviewers are asked in parallel, and neither sees the other's answer. Disagreement is the signal you are paying for.
- Review in an empty room. Run the reviewer in an empty temporary folder and hand it only the text to read and the rules to check against. No repository, no history, no maker's notes. A reader that never sees the reasoning is less likely to inherit its blind spot.
Deterministic gates: what AI must not judge
Some questions are not up for debate: did a secret leak, does a number have a source, does every link answer. These run as plain code, at commit time and again before release.
- Leaks: personal names, machine paths that reveal a login name, secret keys, private contacts.
- Claims: figures with no measurement behind them, and wording that overstates what was measured.
- Links: every public link must answer; a retired page counts as alive only while a real redirect exists.
- House rules: the palette, public times in UTC, loudness and frame rate for any film.
The wiring is ordinary. A sketch, with placeholder file names:
#!/bin/sh # .git/hooks/pre-commit: a non-zero exit stops the commit python3 tools/check_leaks.py --staged || exit 1 python3 tools/check_claims.py --staged || exit 1 The agent gets a seat belt too. Claude Code can run a hook before every tool call; exit code 2 refuses the call, and the message on standard error goes back to the agent. This one, from our MIT starter, refuses a commit that tries to skip the checks:
SKIP_FLAG = "--no-" + "verify" RULE = re.compile(r"git\s+(commit|push)\b[^\n;|&]*" + re.escape(SKIP_FLAG)) def main() -> int: try: data = json.loads(sys.stdin.read() or "{}") if data.get("tool_name") == "Bash": command = str((data.get("tool_input") or {}).get("command", "")) if RULE.search(command): sys.stderr.write("guard: refused. Fix the cause instead.\n") return 2 except Exception: return 0 return 0 {"hooks": {"PreToolUse": [{"matcher": "Bash", "hooks": [{"type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/hooks/guard_min.py\""}]}]}} It is a text match on the command, nothing more. Treat it as a seat belt, not a sandbox. Exceptions raised inside the hook are caught and let the call through, so a bug in its logic does not block your work.
Keeping the budget fixed
Metered API keys are the quiet way a solo studio overspends. The same hook pattern can refuse a tool call that touches a metered-key variable, so the agents work through the flat-rate tools you are already signed into. The cost follows the plan you chose rather than a per-call meter.
A rhythm that runs without you
The studio wakes on a schedule (four times a day for us), catches up once after the machine has slept, and picks up any new directive the founder leaves in a shared folder. What reaches the human is one card: what changed, what was measured, and one decision, with execute, abort or mutate.
What this does not do
- It does not replace judgment. It moves judgment to the one place it belongs.
- The gates catch only what they are written to catch. "No findings" is not proof that nothing is there.
- A blind reviewer can be wrong. Two reviewers who disagree are still cheaper than one who agrees with everything.
Try it
- The pre-release leak check runs in your browser, and the files you scan are not uploaded: https://zero-trace-ship-kit.zeus-lab-global.workers.dev/?utm_source=devto
- The MIT starter with the hook above: https://github.com/ZEUSLABZERO/zeus-harness-starter
- The full blueprint (role directives, gates, a budget ledger and a self-check), if you want it ready-made: https://zero-trace-ship-kit.zeus-lab-global.workers.dev/studio/?utm_source=devto
Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.