- Fix source_ref mismatch: update to workspace HEAD on first owner run
so change detection compares against the correct repo
- Treat hasNewChanges === null as "no changes" at finalize to prevent
infinite re-review when source_ref is unresolvable
- Add AGENT_LANGUAGE env var: when set, appends language instruction to
all paired room prompts (owner, reviewer, arbiter)
Previously unknown verdicts caused arbiter_escalated (task terminated).
Now falls back to proceed so the loop continues — a parse failure
should not kill the task.
- Owner finalize with concerns now checks deadlock threshold before
looping back — prevents merge_ready ↔ active infinite oscillation
- Arbiter verdict resets round_trip to threshold-1 instead of 0,
giving agents one round before re-triggering arbiter
BLOCKED and NEEDS_CONTEXT verdicts went straight to completed,
bypassing the arbiter entirely. Now when arbiter is enabled, these
verdicts trigger arbiter intervention first — the arbiter may
resolve the block or decide to escalate to the user (ESCALATE).
The counter was being reset to 0 on every owner turn, including
bot-only ping-pong turns. This prevented the 3-round-trip echo
loop breaker from ever triggering. Now only human messages reset
the counter.
When both owner and reviewer keep echoing DONE_WITH_CONCERNS without
making progress, the system now stops the ping-pong after 3 round
trips and sets the task to completed, escalating to the user.
The finalize turn (auto-triggered after reviewer approval) was being
treated as a new working turn because merge_ready was unconditionally
reset to active. Now merge_ready is only reset when a human message
is in the batch, preserving the finalize flow for bot-only turns.
When a user sends a new message while the task is in merge_ready,
review_ready, or in_review status, the owner's turn was incorrectly
treated as a finalize turn. This caused premature task completion
(DONE → completed) even when the user wanted to continue working.
Now resets status to active alongside round_trip_count, ensuring
the owner gets a fresh working turn instead of a finalize turn.
When the owner registers a CI watcher and completes the turn, the
review loop no longer triggers. The watcher result will be posted
via the reviewer bot, which the owner picks up and acts on, then
the normal review loop resumes.
CLAUDE_CONFIG_DIR included .claude suffix, but the container mounts
the parent directory at /home/node/.claude. This caused double nesting:
/home/node/.claude/.claude/CLAUDE.md — SDK never found the prompts.
When the reviewer approves (merge_ready) and the owner finalizes,
the system previously only checked for code changes. If the owner
raised concerns (DONE_WITH_CONCERNS, BLOCKED, NEEDS_CONTEXT) during
finalize — e.g. push failed due to detached HEAD — the system
ignored it and auto-completed the task.
Now the owner's verdict is checked first. If the owner raises
concerns, the task goes back to active and re-triggers the reviewer
so the issue can be addressed collaboratively.
Owner worktree path was per-task (data/workspaces/{folder}/tasks/{taskId}/owner/),
causing Claude SDK to treat each new task as a different project and lose session
context. Now uses a stable per-channel path (data/workspaces/{folder}/owner/).
- Channel isolation preserved (different folders get different worktrees)
- Session persists across tasks (same path = same Claude SDK project)
- Reviewer snapshot still reads from owner worktree correctly
- Fix stale approvedHead reference from cherry-pick conflict
- Replace commit hash comparison with git tree hash (HEAD^{tree}) to
avoid false re-review triggers on commit-only operations
- Use git diff --quiet for actual file change detection
- Update source_ref on failed reviewer execution with done verdict
- Add tests for empty-commit finalize and code-change re-review
- Document Codex reviewer bash mutation gap in reviewer-runtime
Reviewer was picking up owner's cached session from the shared
.claude directory, causing "No conversation found" errors. Now uses
data/sessions/{folder}-reviewer/.claude/ as a separate config dir.
- Compare git HEAD at reviewer approval vs owner finalize to detect
new code changes after DONE verdict. Re-trigger reviewer if changed,
complete if unchanged (commit-only is fine).
- Record approved HEAD in source_ref when reviewer says DONE
- Add pnpm store mount, pre-flight checks, idle timeout for containers
- Clean up orphaned containers on shutdown
- Remove UNIFIED_MODE legacy: single service manages all 3 Discord bots
- Add OWNER_AGENT_TYPE / REVIEWER_AGENT_TYPE env vars for configurable agent selection
- Fix Discord channel routing: reviewer output goes through correct bot (discord/discord-review)
- Fix channel name assignment: explicit names prevent agentTypeFilter collision
- Remove silent output suppression system: harness-level protections replace prompt workarounds
- Add reviewer approval detection: DONE marker on first line stops ping-pong
- Include user's original message in reviewer prompt for context
- Fix round_trip_count auto-reset on new user message
- Fix session ID conflict: reviewer doesn't reuse owner's session
- Fix pending progress text flush in runner on close sentinel
- Promote buffered intermediate text to final result when result event has no text
- Remove legacy files: .env.codex.example, .env.codex-review.example, migrate-unify.cjs
- Update CLAUDE.md: server-side build deployment, unified architecture docs