Summary
The claude-code reviewer never completes a review autonomously: it does not post comments/verdict to the PR on GitHub, and it cannot record the result with ao review submit. The review_run row stays status: running forever. There are three independent defects in the reviewer launch + flow; all three must be fixed for the feature to work end-to-end.
Source: local repro on saturday-reads-api PR #4 | Reported by: @aditi1178 | Analyzed against: a678421e2b9b26d2fea0aba231140778d3f6878d
Confidence: High (exact code paths traced)
Reproduction
- Spawn a worker, let it open a PR (branch
ao/<session-id>).
- Trigger a review (
POST /sessions/{id}/reviews/trigger).
- Observe: reviewer pane launches, but the run stays
running with empty verdict/body; the PR has no review (gh pr view <n> --json reviews → []).
Defect 1 — reviewer launches in default permission mode and stalls
ReviewCommand (internal/adapters/reviewer/claudecode/claudecode.go:36) builds the launch config without Permissions:
argv, err := r.agent.GetLaunchCommand(ctx, ports.LaunchConfig{
SessionID: inv.ReviewerID,
WorkspacePath: inv.WorkspacePath,
Prompt: inv.Prompt,
SystemPrompt: inv.SystemPrompt,
// Permissions unset
})
With Permissions == "", appendPermissionFlags (internal/adapters/agent/claudecode/claudecode.go:303) emits no --permission-mode flag, so Claude Code starts in its default interactive mode and prompts for every Bash/tool call. The reviewer runs as a headless pane with no human to approve, so it blocks on the first tool prompt — never runs gh or ao. (Read-only is enforced by the prompt, not a sandbox, so granting autonomy is safe.)
Fix:
Permissions: ports.PermissionModeBypassPermissions,
(Alternative: add a configurable Permissions field to ReviewInvocation/LaunchSpec, defaulting to bypassPermissions.)
Defect 2 — reviewer pane has no pinned PATH / AO env, so ao review submit hits the wrong binary
Worker/orchestrator sessions are created with Env: m.runtimeEnv(...) (internal/session_manager/manager.go:275), which prepends the daemon's own dir to PATH (hookPATH) so bare ao resolves to the real Go CLI, and sets AO_SESSION_ID/AO_DATA_DIR.
The reviewer pane is created by the review launcher (internal/review/launcher.go:96) with Env: cmd.Env, but ReviewCommand returns ReviewCommandSpec{Argv: argv} and never sets Env (it is nil). With no PATH pin, the reviewer's ao resolves via the login-shell PATH to the legacy npm @aoagents/ao CLI, which has no review command → ao review submit … fails with unknown command 'review'. So even past Defect 1, the verdict is never recorded.
Fix: populate ReviewCommandSpec.Env in the launcher with the same PATH-pin + AO env the session manager builds in runtimeEnv/hookPATH (factor the PATH-pin helper so both call sites share it).
Defect 3 — prompt does not enforce "post to GitHub first, then record"
The reviewer prompt (internal/review/prompt.go) says to post the review "using the available review tooling" and, "when done… record the result with ao review submit." The ordering is only implied, so the agent may run ao review submit first or skip the GitHub post entirely.
Fix: make the prompt explicit and ordered: (1) post the review on the PR via gh (request changes / approve, with inline comments), then (2) run ao review submit … to record the verdict; only fall back to submit-without-posting if the provider post genuinely fails.
Impact
- Reviewer never posts review comments/verdict to GitHub.
ao review submit fails → review runs stuck running forever; the Reviews panel never resolves.
- Out of the box, the review feature does not work at all.
Related
- #242 — concurrent Trigger double-spawns reviewers (same subsystem)
- #236 — Review V2 multi-reviewer cockpit
- #192 — introduced the review backend
Minor note — self-approval on single-account setups
When the reviewer runs under the same GitHub account that opened the PR (common in single-user/local setups), GitHub will not accept an --approve — you can't approve your own PR. This is a small edge in the approved path only; request changes and inline comments work normally, and AO still records the verdict via ao review submit regardless. Easy mitigations: have the reviewer fall back to posting the approval as a regular comment when the provider rejects self-approval, or run reviewers under a separate bot account. Not a blocker for the fixes above.
Summary
The claude-code reviewer never completes a review autonomously: it does not post comments/verdict to the PR on GitHub, and it cannot record the result with
ao review submit. Thereview_runrow staysstatus: runningforever. There are three independent defects in the reviewer launch + flow; all three must be fixed for the feature to work end-to-end.Source: local repro on
saturday-reads-apiPR #4 | Reported by: @aditi1178 | Analyzed against:a678421e2b9b26d2fea0aba231140778d3f6878dConfidence: High (exact code paths traced)
Reproduction
ao/<session-id>).POST /sessions/{id}/reviews/trigger).runningwith emptyverdict/body; the PR has no review (gh pr view <n> --json reviews→[]).Defect 1 — reviewer launches in default permission mode and stalls
ReviewCommand(internal/adapters/reviewer/claudecode/claudecode.go:36) builds the launch config withoutPermissions:With
Permissions == "",appendPermissionFlags(internal/adapters/agent/claudecode/claudecode.go:303) emits no--permission-modeflag, so Claude Code starts in its default interactive mode and prompts for every Bash/tool call. The reviewer runs as a headless pane with no human to approve, so it blocks on the first tool prompt — never runsghorao. (Read-only is enforced by the prompt, not a sandbox, so granting autonomy is safe.)Fix:
(Alternative: add a configurable
Permissionsfield toReviewInvocation/LaunchSpec, defaulting tobypassPermissions.)Defect 2 — reviewer pane has no pinned PATH / AO env, so
ao review submithits the wrong binaryWorker/orchestrator sessions are created with
Env: m.runtimeEnv(...)(internal/session_manager/manager.go:275), which prepends the daemon's own dir toPATH(hookPATH) so bareaoresolves to the real Go CLI, and setsAO_SESSION_ID/AO_DATA_DIR.The reviewer pane is created by the review launcher (
internal/review/launcher.go:96) withEnv: cmd.Env, butReviewCommandreturnsReviewCommandSpec{Argv: argv}and never setsEnv(it isnil). With no PATH pin, the reviewer'saoresolves via the login-shell PATH to the legacy npm@aoagents/aoCLI, which has noreviewcommand →ao review submit …fails withunknown command 'review'. So even past Defect 1, the verdict is never recorded.Fix: populate
ReviewCommandSpec.Envin the launcher with the same PATH-pin + AO env the session manager builds inruntimeEnv/hookPATH(factor the PATH-pin helper so both call sites share it).Defect 3 — prompt does not enforce "post to GitHub first, then record"
The reviewer prompt (
internal/review/prompt.go) says to post the review "using the available review tooling" and, "when done… record the result withao review submit." The ordering is only implied, so the agent may runao review submitfirst or skip the GitHub post entirely.Fix: make the prompt explicit and ordered: (1) post the review on the PR via
gh(request changes / approve, with inline comments), then (2) runao review submit …to record the verdict; only fall back to submit-without-posting if the provider post genuinely fails.Impact
ao review submitfails → review runs stuckrunningforever; the Reviews panel never resolves.Related
Minor note — self-approval on single-account setups
When the reviewer runs under the same GitHub account that opened the PR (common in single-user/local setups), GitHub will not accept an
--approve— you can't approve your own PR. This is a small edge in theapprovedpath only;request changesand inline comments work normally, and AO still records the verdict viaao review submitregardless. Easy mitigations: have the reviewer fall back to posting the approval as a regular comment when the provider rejects self-approval, or run reviewers under a separate bot account. Not a blocker for the fixes above.