Repository navigation
Deliver to matching-surface Codex idle instead of treating it as errored - #1067
Conversation
Watch officer-route refused delivery when Codex Assess returned errored on an independently idle composer. Treat matching-surface Codex errored like Unknown: the empty-composer proof decides. Selected Codex idle-cleared counts as that proof. Codex Assess and detector status are unchanged so a genuine Codex dead-end stays errored.
There was a problem hiding this comment.
All reported issues were addressed across 2 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
#1067) A SessionUncooperative Codex pane can still render ComposerCleared (provider- stopped idle composer). Driver-name-only Errored forgiveness would type-in to a dead desk. Require the pane PID to be running and the Codex session store to hold an open rollout for that PID before treating matching-surface Codex errored as an idle-proof gap. Assess and detector status are unchanged.
There was a problem hiding this comment.
1 issue found across 7 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cmd/flotilla/officer_route.go">
<violation number="1" location="cmd/flotilla/officer_route.go:230">
P3: The session-liveness probe runs for every errored idle sample, so proveOfficerIdle invokes officerCodexSessionLive twice per proof (two tmux pane_pid/pane_current_path subprocess calls plus a full procfs tree/rollout scan each). Nothing about the 750ms settle makes liveness meaningfully re-verifiable across the two samples, and the probe also runs before the cheaper idempotent per-sample refusals (cursor/empty proof, capture match) that may already reject the sample. Hoist the liveness check to once per proof, or reorder it after the other per-sample checks.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| return officerIdleProof{}, fmt.Errorf("idle proof sample %d reported %s", i+1, sample.state) | ||
| } | ||
| if sample.inMode || (!sample.visible && !selectedGrokComposerProof(d, sample.emptyProof)) { | ||
| if sample.state == surface.StateErrored { |
There was a problem hiding this comment.
P3: The session-liveness probe runs for every errored idle sample, so proveOfficerIdle invokes officerCodexSessionLive twice per proof (two tmux pane_pid/pane_current_path subprocess calls plus a full procfs tree/rollout scan each). Nothing about the 750ms settle makes liveness meaningfully re-verifiable across the two samples, and the probe also runs before the cheaper idempotent per-sample refusals (cursor/empty proof, capture match) that may already reject the sample. Hoist the liveness check to once per proof, or reorder it after the other per-sample checks.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/flotilla/officer_route.go, line 230:
<comment>The session-liveness probe runs for every errored idle sample, so proveOfficerIdle invokes officerCodexSessionLive twice per proof (two tmux pane_pid/pane_current_path subprocess calls plus a full procfs tree/rollout scan each). Nothing about the 750ms settle makes liveness meaningfully re-verifiable across the two samples, and the probe also runs before the cheaper idempotent per-sample refusals (cursor/empty proof, capture match) that may already reject the sample. Hoist the liveness check to once per proof, or reorder it after the other per-sample checks.</comment>
<file context>
@@ -216,11 +223,19 @@ func proveOfficerIdle(d surface.Driver, pane, expectedCaptureSHA string, cleanCo
if !officerSampleStateAllowed(d, sample.state) {
return officerIdleProof{}, fmt.Errorf("idle proof sample %d reported %s", i+1, sample.state)
}
+ if sample.state == surface.StateErrored {
+ if deps.live == nil {
+ return officerIdleProof{}, fmt.Errorf("idle proof sample %d reported errored without a session-liveness probe", i+1)
</file context>
There was a problem hiding this comment.
Disposition: REJECT WITH EVIDENCE. Do not hoist liveness to once-per-proof.
officerIdleSettle is 750ms so a session that dies between the two idle samples refuses. Each errored sample must re-prove PID + open rollout; a single probe would authorize type-in after the process/session died in that window.
Kept: per-sample deps.live in proveOfficerIdle (officer_route.go after the StateErrored check). Mechanical coverage: TestOfficerRouteMatchingSurfaceCodexErroredReprovesLiveEachSample — first sample live, second sample pane pid not running → refuse idle proof sample 2 reported errored without a live session; live calls=2.
No single-probe path restored.
Watch inherits the unit environment. A nonempty CODEX_HOME is the Codex config root (same rule as codextrust.ConfigPath); default remains ~/.codex. Each errored idle-proof sample still re-proves PID + open rollout across the 750ms settle so a session that dies between samples refuses.
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
officerCodexHome re-implemented ConfigPath's CODEX_HOME / ~/.codex rule. codextrust.Home is the single lookup; ConfigPath and officerCodexHome call it so a set CODEX_HOME cannot drift between trust seeding and session liveness.
Operator summary
Before
Watch could see a Codex desk that was alive and sitting at an empty prompt, then still refuse to deliver. The fallback path treated that idle Codex session as broken, so messages queued instead of landing.
Change
When the live surface is Codex and the empty prompt is independently proven, that idle session is no longer treated as broken. A different Codex desk that really is in a dead-end error stays reported as errored. Overlay-first driver selection is unchanged.
After
Matching-surface Codex idle can receive watch delivery. Genuine Codex error seats stay errored. No live install in this change.
Identifiers
agent/officer-route-codex-idleSummary
Watch officer-route (the fallback that delivers after confirmed delivery returns a transient classifier miss) refused matching-surface Codex idle because idle-proof samples treated
Assess=erroredas not-idle. Confirmed delivery already mapserroredtoErrTransientand then asks officer-route to prove the pane independently idle; officer-route vetoed the sameerroredsample.This change:
StateErroredlikeStateUnknownin idle-proof: neither positive-idle evidence nor a veto. The stable cursor/capture plus empty-main-composer proof decides.erroreddoes not veto that proof; foreign Working / awaiting / shell still veto.ComposerClearedat the final disposition gate when the proof is selected Codex.Assessor detector status, so a sibling Codex seat that is genuinelyerroredstayserrored/codex. Detector idle-override remainsUnknown-only.Test plan
go test ./cmd/flotilla/ -count=1in isolate (includes new officer-route cases)errored+ idle-cleared composer deliverserroredwithout cleared composer refuseserroredstill refuseserroreddoes not veto selected Codex proofSummary by cubic
Fixes watch delivery to a live, idle Codex desk that previously got treated as errored. Matching-surface Codex
StateErroredis now treated likeStateUnknownin the idle proof only when a PID/session-store liveness check passes, so the empty-composer proof decides. Genuine Codex error seats still stay errored, and other driver behavior is unchanged.Idle+ComposerClearedcounts as empty-composer proof, mirroring Grok.StateErroredas an idle-proof gap.codextrust.Home, defaulting to~/.codexwhen unset.erroredno longer veto that proof; foreign Working / awaiting / shell still veto.ComposerClearedwhen the proof is selected Codex.Assessand detector status are untouched.Written for commit 322beae. Summary will update on new commits.