Test-Iterate Loop — Autonomous Bug Fix Cycle
Autonomous loop: run tests → root-cause failures → apply minimal fix → retry. Bounded by iteration cap and same-error-repeat detector. Never commits — leaves clean working tree + markdown report. Generic across Python (pytest), R (testthat), Julia (Pkg.test), and HPC pipelines (mock-Avon Docker).
Hard Rules
Existential — block proceed
- Never
git commit, git push, or modify .git/ state. All changes go into the working tree only. The user reviews the final clean tree + report and decides what to commit. Enforced via the standard forbid-list per subagent-write-guard.md if dispatching sub-agents.
- Bounded iteration: max 10 iterations OR 3-same-error-repeat — whichever first. Hardcoded ceiling. Past that, stop and summarise. Don't ask "continue?" — exhaustion is the signal.
- Each iteration must change ≥1 file. A no-op iteration (Claude couldn't propose a fix) counts as a same-error-repeat.
- Memory bug detector: flag any iteration that adds a model load, env var change, or version pin without a corresponding test — these are common failure-cause patterns from past HPC sessions.
Format — catch in review
- Every iteration logged to
log/test-iterate/<project>-YYYY-MM-DD-HHMM.md with: hypothesis, fix applied, result.
- Final report at the same path summarises iterations, terminal state, and remaining failures (if any).
- Use
TaskCreate/TaskUpdate for live progress tracking — the user can see what iteration is running and why.
When to Use
- A pipeline / library has failing tests and you want them fixed autonomously
- Pre-flight before submitting an HPC job (catch torch/transformers/CUDA mismatches in mock-Avon Docker before the real job queues)
- Refactor cycles: change → run tests → fix breaks → repeat
- Reproducing a failure on a fresh checkout
When NOT to Use
- The test suite itself is broken (fix the tests first, or you'll loop trying to fix code to match wrong tests)
- The failure root cause is external (network, third-party API, hardware) — the active agent cannot fix those
- You want to write tests, not fix them — use
computational-experiments or direct work
- The fix requires research / design decisions, not just code adjustment
Modes
| Invocation |
Behaviour |
test-iterate-loop |
Auto-detects test runner; iterates up to 10 |
test-iterate-loop --max-iter 5 |
Lower iteration cap |
test-iterate-loop --mock-hpc |
Run tests inside the mock-Avon Docker container (catches HPC-specific torch/CUDA bugs pre-submission) |
test-iterate-loop --container <image> |
Custom Docker image |
test-iterate-loop --no-fix |
Run tests once, root-cause failures, stop without applying fixes (diagnostic mode) |
Architecture
Phase 1 (detect) → which test runner? pytest, testthat, julia, custom?
Phase 2 (initial) → run tests, capture full failure log
Phase 3 (loop) → for each iteration: hypothesis → fix → re-run → log
Phase 4 (terminate) → all-pass | iter-cap | same-error-3x → final report
Phase 1: Detect test runner
Heuristics in priority order:
| Signal |
Runner |
pyproject.toml with [tool.pytest] or tests/ dir + *.py |
uv run pytest --maxfail=1 -x |
package.json with "scripts": {"test": ...} |
npm test |
DESCRIPTION (R package) + tests/testthat/ |
Rscript -e 'devtools::test()' |
Project.toml (Julia) |
julia --project -e 'using Pkg; Pkg.test()' |
Makefile with test: target |
make test |
noxfile.py or tox.ini |
nox / tox |
Custom runner specified by user (--runner '<cmd>') |
use that |
If multiple match, ask the user which.
Phase 2: Initial run
Run the test command. Capture:
- Exit code
- Full stdout + stderr to
/tmp/test-iterate-<run-id>.log
- Failure count, error types, first few failing test names
If exit 0: print "All green ✓" and exit. No iteration needed.
Phase 3: Iteration loop
Per iteration:
Hypothesis — read the failure log; identify the root cause. Use the Read tool on the test file + the implementation file to confirm. Write the hypothesis to the iteration log:
Iteration 3 — 2026-05-10 14:23
Hypothesis: pytest fails on test_inventory_split because the function
returns a list when it should return a dict (matches old API).
Fix — make the minimal edit to address the hypothesis. Document the file + line range in the iteration log.
Re-run — same test command.
Log result — pass / new failure / same failure:
- All pass → exit loop, terminal state PASS.
- New failure → fresh hypothesis next iteration.
- Same failure (3rd time consecutively) → exit loop, terminal state STUCK.
Memory-bug check — if this iteration's fix added a from <model> import, a .cuda() call, an os.environ[] set, or a version pin (torch==X.Y), flag in the log with [MEMORY-BUG-RISK]. These are the patterns that bit past Avon runs.
Phase 4: Termination + report
Final report at log/test-iterate/<project>-YYYY-MM-DD-HHMM.md:
# Test-Iterate Loop — <project>
**Started:** YYYY-MM-DD HH:MM
**Ended:** YYYY-MM-DD HH:MM
**Terminal state:** PASS | STUCK (same error 3x) | EXHAUSTED (hit iter cap)
## Iterations
| # | Hypothesis | Fix | Result |
|---|---|---|---|
| 1 | ... | ... | new failure |
| 2 | ... | ... | new failure |
| 3 | ... | ... | PASS |
## Final test output
Files modified
src/foo.py (lines 42-58) — fix iteration 1
tests/conftest.py (lines 12-15) — fix iteration 2
- ...
Working tree
Clean (no commits made). User decides whether to commit, what to amend, or what to revert.
Memory-bug flags
- Iteration 2:
[MEMORY-BUG-RISK] — added torch.cuda.empty_cache() without a paired test
## HPC mock mode (`--mock-hpc`)
Use when iterating before submitting to [HPC cluster]. Runs inside a Docker container that mirrors Avon's environment:
- Default image: `user/avon-mock:latest` (built from `scripts/hpc/Dockerfile.avon-mock`)
- Container has same CUDA, torch, transformers, slurm-mock as Avon
- Test command runs inside the container; failures bubble out to the iteration log
**Hard rule:** never run `--mock-hpc` against a project whose data lives outside the project directory — the Docker mount won't see it. Verify data paths first.
If `user/avon-mock:latest` doesn't exist on the current machine, print the build instructions and exit.
## Standard forbid-list (when dispatching sub-agents)
If for very large iteration loops (>5 files modified concurrently) the orchestrator decides to dispatch sub-agents, each gets:
This sub-agent has a narrow scope. It does NOT inherit the orchestrator's
authorisation for any other action.
- Do NOT run
git add, git commit, git push, or any other git write command.
- Do NOT modify files outside the scope I've named below.
- Do NOT run latexmk, pip install, uv add, or any package-management command.
- Do NOT modify CI configs or pyproject.toml dependencies.
- Read-only access except for the explicit edit scope.
Scope:
Task: <hypothesis + fix>
Report your changes as a diff. The orchestrator runs the tests.
## Cross-References
| Skill / Rule | Relationship |
|---|---|
| `subagent-write-guard.md` | Sub-agent dispatch (when needed) follows this rule |
| the `code-review` agent | Run AFTER test-iterate-loop terminates PASS — quality scorecard |
| `computational-experiments` | The skill that *writes* the tests this loop iterates on |
| `code-paper-auditor` agent | Code-paper consistency check — orthogonal concern |
## Anti-Patterns
- **Don't** loop without a same-error-repeat detector — an agent can spin on the same issue forever.
- **Don't** auto-commit even on PASS — leave the clean tree for the user to review and stage as they want.
- **Don't** treat warnings as failures — only test exit code 0 means PASS. Warnings get logged but don't trigger iterations.
- **Don't** apply fixes to test files — that's a smell that you're fitting tests to broken code. Fix the code.
- **Don't** skip the memory-bug flag — past HPC sessions had `torch.cuda` and `TRANSFORMERS_OFFLINE` bugs that would have been caught with this signal.
1---2name: test-iterate-loop3description: "Autonomously diagnose a codebase, apply minimal fixes, and rerun tests until they pass or a real blocker is reached. Use when the user explicitly requests an iterative fix-until-green loop across Python, R, Julia, or HPC workflows."4allowed-tools: Read, Write, Edit, Glob, Grep, Bash(uv*), Bash(pytest*), Bash(Rscript*), Bash(julia*), Bash(docker*), Bash(make*), Bash(git*), TaskCreate, TaskUpdate, AskUserQuestion5argument-hint: "[project-path] [--max-iter N] [--mock-hpc] [--container <image>]"6skill-dependencies: [computational-experiments]7---89# Test-Iterate Loop — Autonomous Bug Fix Cycle1011> Autonomous loop: run tests → root-cause failures → apply minimal fix → retry. Bounded by iteration cap and same-error-repeat detector. Never commits — leaves clean working tree + markdown report. Generic across Python (pytest), R (testthat), Julia (Pkg.test), and HPC pipelines (mock-Avon Docker).1213## Hard Rules1415### Existential — block proceed16171. **Never `git commit`, `git push`, or modify `.git/` state.** All changes go into the working tree only. The user reviews the final clean tree + report and decides what to commit. Enforced via the standard forbid-list per `subagent-write-guard.md` if dispatching sub-agents.182. **Bounded iteration: max 10 iterations OR 3-same-error-repeat — whichever first.** Hardcoded ceiling. Past that, stop and summarise. Don't ask "continue?" — exhaustion is the signal.193. **Each iteration must change ≥1 file.** A no-op iteration (Claude couldn't propose a fix) counts as a same-error-repeat.204. **Memory bug detector**: flag any iteration that adds a model load, env var change, or version pin without a corresponding test — these are common failure-cause patterns from past HPC sessions.2122### Format — catch in review23245. Every iteration logged to `log/test-iterate/<project>-YYYY-MM-DD-HHMM.md` with: hypothesis, fix applied, result.256. Final report at the same path summarises iterations, terminal state, and remaining failures (if any).267. Use `TaskCreate`/`TaskUpdate` for live progress tracking — the user can see what iteration is running and why.2728## When to Use2930- A pipeline / library has failing tests and you want them fixed autonomously31- Pre-flight before submitting an HPC job (catch torch/transformers/CUDA mismatches in mock-Avon Docker before the real job queues)32- Refactor cycles: change → run tests → fix breaks → repeat33- Reproducing a failure on a fresh checkout3435## When NOT to Use3637- The test suite itself is broken (fix the tests first, or you'll loop trying to fix code to match wrong tests)38- The failure root cause is *external* (network, third-party API, hardware) — the active agent cannot fix those39- You want to *write* tests, not fix them — use `computational-experiments` or direct work40- The fix requires research / design decisions, not just code adjustment4142## Modes4344| Invocation | Behaviour |45|---|---|46| `test-iterate-loop` | Auto-detects test runner; iterates up to 10 |47| `test-iterate-loop --max-iter 5` | Lower iteration cap |48| `test-iterate-loop --mock-hpc` | Run tests inside the mock-Avon Docker container (catches HPC-specific torch/CUDA bugs pre-submission) |49| `test-iterate-loop --container <image>` | Custom Docker image |50| `test-iterate-loop --no-fix` | Run tests once, root-cause failures, stop without applying fixes (diagnostic mode) |5152## Architecture5354```55Phase 1 (detect) → which test runner? pytest, testthat, julia, custom?56Phase 2 (initial) → run tests, capture full failure log57Phase 3 (loop) → for each iteration: hypothesis → fix → re-run → log58Phase 4 (terminate) → all-pass | iter-cap | same-error-3x → final report59```6061## Phase 1: Detect test runner6263Heuristics in priority order:6465| Signal | Runner |66|---|---|67| `pyproject.toml` with `[tool.pytest]` or `tests/` dir + `*.py` | `uv run pytest --maxfail=1 -x` |68| `package.json` with `"scripts": {"test": ...}` | `npm test` |69| `DESCRIPTION` (R package) + `tests/testthat/` | `Rscript -e 'devtools::test()'` |70| `Project.toml` (Julia) | `julia --project -e 'using Pkg; Pkg.test()'` |71| `Makefile` with `test:` target | `make test` |72| `noxfile.py` or `tox.ini` | `nox` / `tox` |73| Custom runner specified by user (`--runner '<cmd>'`) | use that |7475If multiple match, ask the user which.7677## Phase 2: Initial run7879Run the test command. Capture:80- Exit code81- Full stdout + stderr to `/tmp/test-iterate-<run-id>.log`82- Failure count, error types, first few failing test names8384If exit 0: print "All green ✓" and exit. No iteration needed.8586## Phase 3: Iteration loop8788Per iteration:89901. **Hypothesis** — read the failure log; identify the root cause. Use the `Read` tool on the test file + the implementation file to confirm. Write the hypothesis to the iteration log:9192 ```93 Iteration 3 — 2026-05-10 14:2394 Hypothesis: pytest fails on test_inventory_split because the function95 returns a list when it should return a dict (matches old API).96 ```97982. **Fix** — make the minimal edit to address the hypothesis. Document the file + line range in the iteration log.991003. **Re-run** — same test command.1011024. **Log result** — pass / new failure / same failure:103104 - **All pass** → exit loop, terminal state PASS.105 - **New failure** → fresh hypothesis next iteration.106 - **Same failure (3rd time consecutively)** → exit loop, terminal state STUCK.1071085. **Memory-bug check** — if this iteration's fix added a `from <model>` import, a `.cuda()` call, an `os.environ[]` set, or a version pin (`torch==X.Y`), flag in the log with `[MEMORY-BUG-RISK]`. These are the patterns that bit past Avon runs.109110## Phase 4: Termination + report111112Final report at `log/test-iterate/<project>-YYYY-MM-DD-HHMM.md`:113114```markdown115# Test-Iterate Loop — <project>116117**Started:** YYYY-MM-DD HH:MM118**Ended:** YYYY-MM-DD HH:MM119**Terminal state:** PASS | STUCK (same error 3x) | EXHAUSTED (hit iter cap)120121## Iterations122123| # | Hypothesis | Fix | Result |124|---|---|---|---|125| 1 | ... | ... | new failure |126| 2 | ... | ... | new failure |127| 3 | ... | ... | PASS |128129## Final test output130131```132<paste of last test run>133```134135## Files modified136137- `src/foo.py` (lines 42-58) — fix iteration 1138- `tests/conftest.py` (lines 12-15) — fix iteration 2139- ...140141## Working tree142143Clean (no commits made). User decides whether to commit, what to amend, or what to revert.144145## Memory-bug flags146147- Iteration 2: `[MEMORY-BUG-RISK]` — added `torch.cuda.empty_cache()` without a paired test148```149150## HPC mock mode (`--mock-hpc`)151152Use when iterating before submitting to [HPC cluster]. Runs inside a Docker container that mirrors Avon's environment:153154- Default image: `user/avon-mock:latest` (built from `scripts/hpc/Dockerfile.avon-mock`)155- Container has same CUDA, torch, transformers, slurm-mock as Avon156- Test command runs inside the container; failures bubble out to the iteration log157158**Hard rule:** never run `--mock-hpc` against a project whose data lives outside the project directory — the Docker mount won't see it. Verify data paths first.159160If `user/avon-mock:latest` doesn't exist on the current machine, print the build instructions and exit.161162## Standard forbid-list (when dispatching sub-agents)163164If for very large iteration loops (>5 files modified concurrently) the orchestrator decides to dispatch sub-agents, each gets:165166```167This sub-agent has a narrow scope. It does NOT inherit the orchestrator's168authorisation for any other action.169170- Do NOT run `git add`, `git commit`, `git push`, or any other git write command.171- Do NOT modify files outside the scope I've named below.172- Do NOT run latexmk, pip install, uv add, or any package-management command.173- Do NOT modify CI configs or pyproject.toml dependencies.174- Read-only access except for the explicit edit scope.175176Scope: <file paths>177Task: <hypothesis + fix>178179Report your changes as a diff. The orchestrator runs the tests.180```181182## Cross-References183184| Skill / Rule | Relationship |185|---|---|186| `subagent-write-guard.md` | Sub-agent dispatch (when needed) follows this rule |187| the `code-review` agent | Run AFTER test-iterate-loop terminates PASS — quality scorecard |188| `computational-experiments` | The skill that *writes* the tests this loop iterates on |189| `code-paper-auditor` agent | Code-paper consistency check — orthogonal concern |190191## Anti-Patterns192193- **Don't** loop without a same-error-repeat detector — an agent can spin on the same issue forever.194- **Don't** auto-commit even on PASS — leave the clean tree for the user to review and stage as they want.195- **Don't** treat warnings as failures — only test exit code 0 means PASS. Warnings get logged but don't trigger iterations.196- **Don't** apply fixes to test files — that's a smell that you're fitting tests to broken code. Fix the code.197- **Don't** skip the memory-bug flag — past HPC sessions had `torch.cuda` and `TRANSFORMERS_OFFLINE` bugs that would have been caught with this signal.