TLDR: Running autonomous agent cycles in the same session means every new cycle has read the prior ones. By Cycle 5 they stop catching bugs and start agreeing with themselves. Fresh subprocess per cycle fixes this completely.
The Setup
I've been building /council — a 10-agent autonomous dev council (Designer, FE Dev, BE Dev, UX Auditor, Senior Engineer, Security Auditor, and more) that reviews a codebase, votes on the highest-value fix, and ships it as a commit.
No hand-holding. You point it at a project. It runs.
I pointed it at Apollo's background Python security daemon that processes items through a Sonnet-then-Opus collector-then-judge pipeline. Weeks of accumulated tech debt. Time to let the council clean it up.
What I Tried First
The obvious play: /loop 30m /council. Fire and forget.
It worked. Until it didn't.
By Cycle 5 the agents were nudging instead of finding. The Senior Engineer would note "retry logic looks solid now." The Security Auditor would say "injection vector patched in Cycle 2." Both true. Neither useful.
I started calling this leading the witness.
Not hallucination. Not bugs. Just agents that had read their own prior work and started agreeing with it. Every cycle inherits the full conversation from the cycles before it. By Cycle 5 you don't have 5 independent reviewers — you have 5 reviewers who watched the same 4 episodes and know how the story ends.
So what does that mean in practice?
They stop catching fresh bugs because they've already explained the risk away.
The Fix That Actually Worked
/council-headless spawns each cycle as a fresh claude -p subprocess (Claude CLI running in non-interactive print mode). Workers boot cold — they read a mission brief and a backlog file from disk, and that's all they know. Zero conversation history. Zero prior context.
The original session stays alive as an orchestrator. It reads diffs after each cycle, detects cross-cycle patterns, saves memories, manages halt conditions. The continuity lives there — not in the workers.
First cycle with headless, the Security Auditor found a content-fence bypass three prior cycles had walked straight past.
(Obvious in retrospect. It always is.)
What Nine Cycles Actually Shipped
~3 hours. 2:13 AM halt by unanimous council vote. 24 fixes, 36 tests, 599 lines changed across 13 files:
- Security: AppleScript injection fix, stdin prompt passing to prevent MCP (model context protocol) token exposure in
psoutput, enum guards, content fencing - Reliability: atomic state writes, 3-attempt retry with backoff via a shared CLI runner, per-item Pydantic validation, rotating log handler
- Architecture: a unified JSON parser — Sonnet collector and Opus judge both run through the same parser now, same retry logic, no more fragile per-caller workarounds
At Cycle 8, the Senior Engineer reviewed the full list and declared the daemon stable.
Cycle 9 was the council itself voting to halt — zero items worth shipping. Time to stop.
Why This Matters to Me
The lesson is dumb-simple once you see it: accumulated context is a bias engine.
An agent that's read its own prior 4 cycles will drift toward validating those decisions, not questioning them. Fresh context per iteration doesn't sacrifice continuity — the orchestrator holds the thread. What it buys you is reviewers who can still be surprised.
I'm being honest about one gap: /council-headless isn't cleared for overnight client runs yet. It commits to whatever branch is checked out — branch isolation isn't built. But for first-party hardening? It's a DIFFERENT GAME.
P.S. Twenty backlog items remain — all features and polish, nothing critical. The council called it correctly. Sometimes the right engineering decision is knowing when to stop.