feat(review-loop): log agent output so a failed run can be diagnosed - #21
Conversation
Greptile SummaryThe PR records each review agent’s combined output and exit status in persistent, per-run log directories.
|
| Filename | Overview |
|---|---|
| bin/code-review-loop | Integrates per-step logs, atomically claimed run directories, retention, failure-path log reporting, and resolved-path staging exclusion; the previously reported path and collision issues are fixed. |
| lib/lib-review-loop | Adds centralized output mirroring with correct agent exit propagation plus atomic run-directory claiming and suffix-aware pruning. |
| test/code-review-loop.bats | Exercises successful log production and non-staging for relative, trailing-slash, project-root, and shared-root configurations. |
| test/lib-review-loop.bats | Covers output capture, stderr capture, exit-code preservation, directory creation, concurrent uniqueness, and retention behavior. |
| README.md | Documents log locations, contents, retention, failure behavior, and configuration variables. |
Reviews (3): Last reviewed commit: "fix(review-loop): claim run directories ..." | Re-trigger Greptile
|
Pushed 8d25b89 covering two follow-ups. Retention. Logs outlived their run by design, which also meant nothing ever removed them: one directory per run, forever, in a cache directory. They are now pruned at startup by age rather than count, since the loop gets run several times in a sitting and what you come back for is today's failure. Ten runs in an afternoon should not push out yesterday's, and a quiet week should not keep last month's. One day by default, A bug the end-to-end test caught. Codex artifact cleanup sweeps files that appeared during a reviewer run, and when the log directory sits inside the repo, which A log is a record of the run, not something the agent made, so README now documents where the logs are, what each holds, and that an agent failing does not stop the loop, so the log is often the only sign a step went wrong. Environment variables are listed too. 64 tests pass. The six new ones cover retention and log preservation, and each was confirmed to fail against the previous behaviour. |
|
@greptileai review |
|
@greptileai review |
A run that dies leaves nothing behind. Agent output went to the terminal and nowhere else, so a loop that stopped mid-refinement showed a bare "Execution error" with no way to tell a crash from a timeout from an external kill, and no record to read afterwards. A run started in the background does not even have the scrollback. run_agent mirrors the agent's combined output to AGENT_LOG when set, stamped with the agent, its tools, and the exit code. Dispatch moves to _dispatch_agent so the wrapper lives in one place. It returns PIPESTATUS[0] rather than the pipeline status, since tee succeeds even when the agent does not and the caller's `|| local_exit=$?` has to see the agent's own code. code-review-loop claims a directory per run and names a log per step, printed in the banner and again whenever an agent fails. The directory sits outside TMPDIR_REVIEW, which the EXIT trap wipes, and outside the project, where the staging and artifact-cleanup passes would otherwise pick the logs up as agent output. CODE_REVIEW_LOOP_LOG_DIR moves the root; each run still gets its own timestamped directory beneath it, claimed with mkdir so two loops starting in the same second cannot share one. is_inside_dir backs both exclusions. Resolving the paths rather than trimming a prefix is what makes a relative, trailing-slashed, or symlinked log directory compare correctly.
Run logs outlive their run so a failure can still be read afterwards, which also meant nothing ever removed them: one directory per run, forever, in a cache directory. Old runs are pruned at startup, by age rather than by count. The loop gets run several times in a sitting and what you come back for is today's failure, so ten runs in an afternoon should not push out yesterday's, and a quiet week should not keep last month's. A day by default, REVIEW_LOOP_LOG_DAYS to change it. Two limits on what pruning touches. It only matches the YYYYmmdd-HHMMSS directories the loops create, so anything kept alongside them survives, and setting CODE_REVIEW_LOOP_LOG_DIR opts out entirely, since a directory the user named is theirs to manage. The README now says where the logs are, what each one holds, and that an agent failing does not stop the loop, so a log is often the only sign a step went wrong.
1cffaa7 to
3061227
Compare
The problem
A review loop that dies leaves nothing behind. Agent output went to the terminal and
nowhere else, so when a run stopped mid-refinement all it showed was:
No way to tell a crash from a timeout from an external kill, and no way to recover it
afterwards. A run started in the background does not even have the scrollback.
The change
run_agentmirrors the agent's combined output toAGENT_LOGwhen set, stamped with theagent, the tools it was given, and the exit code. Dispatch moves to
_dispatch_agentsothe wrapper lives in one place rather than being repeated per runner.
It returns
PIPESTATUS[0], not the pipeline status.teesucceeds even when the agentdoes not, and the caller's
|| local_exit=$?has to see the agent's real code, otherwiseevery failure silently reads as success.
code-review-loopcreates a per-run directory and names a log per step:printed in the banner and again on any agent failure. It sits outside two places on
purpose:
TMPDIR_REVIEW, which theEXITtrap wipes, taking the most interesting run with itstage_review_changeswould offer the logs up for commitThat second point is also why staging now skips anything under the log directory.
CODE_REVIEW_LOOP_LOG_DIRcan point inside a repo, and without the skip the first runthere staged its own logs. That was caught by testing the change rather than by reading it.
Tests
Five tests in
test/lib-review-loop.batscover the wrapper with a stubbed agent onPATH:AGENT_LOGis unsettee'sThe two that matter were confirmed to fail against
rc=$?before the fix, so they arenot vacuous:
58 tests pass, shellcheck clean,
pre-commit rungreen.Not included
No timeout.
claudehas no timeout flag andgtimeoutis not present on macOS bydefault, so anything here would have been invented.
run_antigravityalready carries--print-timeout 30m; the others do not.Worth noting the original failure turned out not to be a bug in these scripts. The
tolerated-failure path works: with a stubbed agent that exits 1, the loop logs the warning
and continues through every remaining step. The run had been killed externally. The defect
was that this was not knowable, which is what this PR fixes.