Skip to content

Deliver to matching-surface Codex idle instead of treating it as errored - #1067

Merged
jim80net merged 4 commits into
mainfrom
agent/officer-route-codex-idle
Sep 8, 2026
Merged

jim80net merged 4 commits into
mainfrom
agent/officer-route-codex-idle

Conversation

@jim80net

@jim80net jim80net commented Sep 7, 2026 •

Copy link
Copy Markdown
Owner

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

Summary

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=errored as not-idle. Confirmed delivery already maps errored to ErrTransient and then asks officer-route to prove the pane independently idle; officer-route vetoed the same errored sample.

This change:

  • Treats matching-surface Codex StateErrored like StateUnknown in idle-proof: neither positive-idle evidence nor a veto. The stable cursor/capture plus empty-main-composer proof decides.
  • Accepts selected Codex idle+cleared as empty-main-composer proof (Codex analog of selected Grok). Foreign errored does not veto that proof; foreign Working / awaiting / shell still veto.
  • Requires ComposerCleared at the final disposition gate when the proof is selected Codex.
  • Does not change Codex Assess or detector status, so a sibling Codex seat that is genuinely errored stays errored/codex. Detector idle-override remains Unknown-only.

Test plan

  • go test ./cmd/flotilla/ -count=1 in isolate (includes new officer-route cases)
  • Matching-surface Codex errored + idle-cleared composer delivers
  • Codex errored without cleared composer refuses
  • Grok errored still refuses
  • Foreign Working still vetoes; foreign errored does not veto selected Codex proof

Summary by cubic

Fixes watch delivery to a live, idle Codex desk that previously got treated as errored. Matching-surface Codex StateErrored is now treated like StateUnknown in 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.

  • Selected Codex Idle + ComposerCleared counts as empty-composer proof, mirroring Grok.
  • Requires the pane PID to be running and the Codex session store to hold an open rollout for that PID before treating StateErrored as an idle-proof gap.
  • CODEX_HOME lookup is shared between trust seeding and session liveness via codextrust.Home, defaulting to ~/.codex when unset.
  • Foreign drivers reporting errored no longer veto that proof; foreign Working / awaiting / shell still veto.
  • Final disposition gate requires ComposerCleared when the proof is selected Codex.
  • No live install changes; Assess and detector status are untouched.

Written for commit 322beae. Summary will update on new commits.

Review in cubic

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread cmd/flotilla/officer_route.go Resolved
#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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread cmd/flotilla/officer_route.go Resolved
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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread cmd/flotilla/officer_route.go Resolved
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.
@jim80net
jim80net merged commit ddcff42 into main Sep 8, 2026
6 checks passed
@jim80net
jim80net deleted the agent/officer-route-codex-idle branch September 8, 2026 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant