When a paired task ends on an arbiter ESCALATE (asking the user to choose), the
user's next reply was carried into the fresh task with the owner's earlier final
as context — not the arbiter message they were actually responding to. Generalize
the carry-forward to the latest user-facing final (owner final OR arbiter
verdict, whichever the task ended on), so the user can choose based on the last
arbiter message without the new turn re-reading the whole prior conversation.
The carried-forward guidance marker is now matched by a shared prefix so it
triggers for both. Adds tests for the arbiter-escalation carry and the prefix
guidance.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A tribunal room whose work_dir was never provisioned (null) made
resolveOwnerTaskForHumanMessage return a null task, so the owner never ran and
the room went completely silent — the user's messages got no reply at all (seen
in the tts_site room, where days of requests were dropped). ensurePairedProject
now provisions the canonical workspace on demand when work_dir is missing,
guarded to tribunal rooms so a single-mode room never gets a spurious paired
workspace. Idempotent via ensurePairedWorkspaceProvisioned. Adds tests for both
the tribunal self-heal and the single-mode no-op.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Owner continuity:
- Seed a freshly created owner task with the previous task's latest owner
final on a cold start after the previous task already closed, so a user
reply to a finalized TASK_DONE no longer produces a "no context" answer.
Activated for this deployment via PAIRED_CARRY_FORWARD_LATEST_OWNER_FINAL
(.env); carried text is injected as clearly-marked background only.
- Skip intermediate STEP_DONE outputs when picking the carry-forward anchor.
Single-mode routing:
- enforceRoomModeOnLease strips a stale reviewer/arbiter lease from a room
switched back to single, preventing single-mode messages from stalling in
the paired path on a stuck execution lease.
Session auth / credentials:
- Pre-sync Claude credentials into each session dir before the agent spawns.
- Honor CLAUDE_CREDENTIALS_PATH in setup/login.ts (per-service isolation).
- Add a relogin-required gate so a permanently logged-out claude-code room
asks the user to re-login instead of spawning a doomed agent.
Other:
- Arbiter verdicts written in the user's language (verdict keyword stays EN).
- status-dashboard chatName field; runtime-inventory credential path resolver.
Tests (make suite fully green: 1595 pass / 3 skip):
- service-routing: default owner is now the claude service and reviewer is
codex-review; update the 7 failover/default expectations accordingly.
- migrate-room-registrations: owner inferred as claude-code (configured
OWNER_AGENT_TYPE) for a dual legacy room; reviewer becomes codex.
- register: mock paired-workspace provisioning + reload signal (registration
now provisions a workspace and hot-reloads); assert RELOADED status.
- paired-execution-context: force a claude-code reviewer to exercise the
Claude read-only branch regardless of the deployment default.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The owner↔reviewer↔arbiter loop could repeat forever: when the arbiter
ruled PROCEED/REVISE/RESET it reset round_trip_count to 0, and nothing
tracked how many times the arbiter had already intervened. A re-deadlock
re-invoked the arbiter without bound.
Add a persistent arbiter_intervention_count (new column + migration 020)
that survives the round-trip reset, and a configurable cap
ARBITER_MAX_INTERVENTIONS (default 1). Once the arbiter has intervened
that many times and the loop still deadlocks, requestArbiterOrEscalate
escalates straight to the user instead of re-invoking the arbiter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- mark Codex bearer/refresh failures as terminal auth-expired states
- sync refreshed session auth back to rotation slots and revive refreshed dead_auth slots
- stop paired arbiter retry loops when Codex accounts are unavailable
- add regression coverage for rotation leases, follow-up suppression, and arbiter closure
- 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