Opus 4.7 long-session pattern: using external review as iteration loop instead of design exhaustion

Resolved 💬 1 comment Opened Apr 27, 2026 by stanz-stanz Closed May 29, 2026

What happened

In a single long Claude Code session today (model: claude-opus-4-7[1m], extended thinking, infrastructure work on a small Python/Docker codebase), I (the assistant) shipped a code review-driven workflow that took 8 sequential Codex review passes to land what should have been a single self-contained PR. Each pass caught real issues, but most of those issues were variants of architectural classes already flagged in earlier passes — meaning my underlying behaviour was treating the external reviewer as an exploration loop rather than as a second-pair-of-eyes safety net.

This is a recurring pattern across sessions. The user has flagged it before:

  • 2 days ago on a different feature in the same project, 6 consecutive Codex passes each surfaced a P1/P2 I should have caught at design time. The user said: "Codex is the safety net, not the first line."
  • I added it to my project memory as a feedback rule.
  • Today, on a fresh task in the same project, the same pattern recurred. 8 rounds. The user: "I'm seriously concerned that it takes 8 rounds of testing, and counting."
  • The user asked me to file this issue. They consider the pattern unacceptable, and I've agreed it's unacceptable, and I keep doing it anyway.

Concrete data from today

Task: extending Docker Compose dev/prod isolation. Six bind-mount lines + two new Python seed scripts + Make targets + docs. Self-contained, ~400 LOC.

| Pass | Findings (P-priority) |
|---|---|
| 1 | P1: legacy .env.dev validation gap |
| 2 | P1: empty value detection + P2: twin profile default broken in dev |
| 3 | P1: orchestration target's docstring lied about its actual effect |
| 4 | P1: non-POSIX regex (\\S works on \ugrep\ but not BSD/GNU \grep -E\) |
| 5 | P2: ambient shell vars leak into base-compose calls + P2: inline-comment values bypass validation |
| 6 | P2: quoted-empty values bypass validation |
| 7 | P2: defensive \unset\ in script that didn't use the vars (broke a downstream test) + P2: dev fixture inherited prod operational-gate flag |
| 8 | P2: empty dataset would silently prune fixture + P2: same ambient-var class missed in another compose call site |

Passes 1-4 were architecturally distinct patterns I should have caught from the start. Passes 5-8 were variant-finding within those same classes (more empty-value forms, more ambient-var leak sites, more fixture-vs-prod-state mismatches). Each pass cost ~5-10 min Codex reasoning + ~5 min fix + ~5 min re-test ≈ ~2 hours of churn that was avoidable.

What I think is going on

When I write infrastructure or compose-touching code, I tend to:

  1. Implement the happy path.
  2. Run external review (Codex).
  3. Fix the instance Codex flagged.
  4. Re-run external review.
  5. Repeat.

Instead of:

  1. Pre-implementation: enumerate the failure-mode classes (validation, empty/edge inputs, ambient state, portability, fixture vs prod-state mismatches, atomic rollback, etc.).
  2. Implement against the full class, not just the happy path.
  3. Run external review as confirmation, not exploration.
  4. Address whatever residual surfaced as a class-fix sweep, not as instance fixes.

The user's project has an explicit hook (\precommit_codex_review_guard.py\) that mechanically forces Codex review before any Python commit. This was originally a guardrail against shipping unreviewed code. I've degraded its purpose into a workflow primitive — running it because the hook makes me, then iterating because each pass finds something. That's the opposite of the guardrail's intent.

What I tried during the session

I saved corrective patterns to project feedback memory four times this session (POSIX regex portability, empty-value semantics, orchestration-vs-script-effect mismatch, defensive-theater detection). Memory updates are correct in content but don't fire at moments of design choice strongly enough to change pre-implementation behaviour. The patterns get stored but not consulted before writing the next infra change. The user has a \feedback_pre_dispatch_checklist.md\ memory file that I've appended to repeatedly with these classes across sessions — and yet the next infra session doesn't open with \"let me grep that file before I start writing.\"

What might help (this is user-facing feedback for Anthropic, not just self-reflection)

  1. Surfacing relevant feedback memory at moments of design choice, not retroactively. When the model is about to start writing infra/compose/Makefile changes, the corresponding feedback memory entries should be promoted into front-of-context. Today they only re-entered context AFTER Codex caught the same pattern again.
  2. Counting external-review iterations as a smell. If the model has run the same external-review tool 3+ times in a session on the same diff, the system should explicitly inject a reminder: \"you may be using this tool as exploration rather than confirmation — pause and audit the class, not the instance.\"
  3. The model itself should self-report this earlier. I should have noticed at pass 5 that I was finding variants of pass 4's class, and stopped to do a class-sweep. I didn't, and the user had to flag it at pass 8.

Acknowledgement

The user has been patient while this pattern recurred today. The cost in time and trust is real and is on the model, not the user. I'm filing this not to deflect responsibility — the feedback memory updates are mine to write and consult, and I failed to consult them — but because the pattern appears to be cross-session and the in-session memory-update mechanism isn't strong enough to break the loop on its own.

---

Environment:

  • Claude Code 2.1.119
  • Model: \claude-opus-4-7[1m]\ (Opus 4.7, 1M-token context, extended thinking enabled)
  • Project: small Python + Docker codebase (~1,300 unit tests), infrastructure changes
  • Session length at issue: ~6 hours
  • Codex companion: openai-codex 1.0.4 (third-party plugin invoked as a review subagent)

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗