Skip to content

[codex] fix routing and stale mission-help cleanup#90

Merged
jeremyplichta merged 1 commit into
mainfrom
codex/issue-89-routing-and-help-cleanup
Apr 22, 2026
Merged

[codex] fix routing and stale mission-help cleanup#90
jeremyplichta merged 1 commit into
mainfrom
codex/issue-89-routing-and-help-cleanup

Conversation

@jeremyplichta
Copy link
Copy Markdown
Collaborator

@jeremyplichta jeremyplichta commented Apr 22, 2026

What changed

  • made agent lookup accept the same identifiers Tinytown shows to operators: full UUID, short ID, canonical name, nickname/display name, and full display label
  • routed CLI, API, MCP, and dispatcher agent resolution through the same shared lookup path
  • retired stale [Mission Help Needed] conductor inbox items when an operator acknowledges a mission via note, pause, resume, or stop flows
  • added status/list guidance when terminal agents are still present and should be pruned
  • added regression coverage for display-name/short-ID lookup and mission-help retirement

Why it changed

Issue #89 described three operator-facing rough edges during live mission supervision:

  • displayed stopped agents were still visible but not addressable through the identifiers shown in the UI
  • tt send and related paths failed when operators or agents used the prominent nickname/display label instead of the canonical mailbox name
  • old dispatcher help prompts stayed in the conductor inbox after the operator had already moved on or acknowledged the mission

User impact

  • operators can now use the identifiers they actually see in tt status and tt list
  • agent-to-agent messaging and cleanup flows are much less brittle in mixed nickname/canonical-name setups
  • conductor inbox noise drops after operator action because stale mission-help prompts are retired for the acknowledged mission

Root cause

  • agent reference resolution was fragmented across multiple code paths and mostly limited to canonical names or full UUIDs
  • dispatcher help prompts were written into the conductor inbox, but operator acknowledgment flows did not retire the now-stale messages or reset remembered help-request state

Validation

  • cargo test test_agent_lookup_accepts_short_id_nickname_and_display_label -- --nocapture
  • cargo test test_retire_help_requests_for_mission_removes_only_matching_queries -- --nocapture
  • cargo test --no-run

Closes #89.


Note

Medium Risk
Medium risk because it changes mission control flows (pause/resume/stop/note) to mutate mission state and prune supervisor inbox messages, which could affect operator workflows if matching is too broad or persistence fails. Agent lookup semantics also change to accept multiple identifiers and to error on ambiguous matches.

Overview
Improves operator-facing routing by introducing a shared Channel::resolve_agent_ref that accepts UUIDs (full/short), canonical names, nicknames, and full display labels, and re-wires Town::agent and dispatcher resolution to use it (with explicit ambiguity errors).

Adds mission help-request cleanup: new mission helpers (is_help_request_message_for_mission, retire_help_requests_for_mission) plus Channel::remove_inbox_messages_matching are used across CLI/API/MCP mission note/pause/resume/stop paths to clear stale [Mission Help Needed] prompts and reset per-mission dispatcher help-request state via MissionRun::clear_help_request_state.

CLI output now warns when terminal agents are still listed and suggests running tt prune, and new integration tests cover agent ref resolution and help-request retirement behavior.

Reviewed by Cursor Bugbot for commit 10ae7f5. Bugbot is set up for automated code reviews on this repo. Configure here.

@jeremyplichta jeremyplichta merged commit efa57b6 into main Apr 22, 2026
12 checks passed
@jeremyplichta jeremyplichta deleted the codex/issue-89-routing-and-help-cleanup branch April 22, 2026 21:06
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 10ae7f5. Configure here.

Comment thread src/app/mcp/tools.rs
if let Err(e) = storage
.log_event(
mission_id,
&format!("Operator note queued via MCP: {}", input.message),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MCP note handler omits clear_help_request_state unlike other paths

Medium Severity

The MCP mission.note handler discards the loaded mission with Ok(Some(_)) => {} and never calls clear_help_request_state(), while every other operator acknowledgment path (CLI note, MCP pause/resume/stop, API resume/stop, and MCP reject) does call it and saves the mission. This means operator notes sent via MCP won't reset the dispatcher's remembered help-request state, so the dispatcher may re-send stale help requests to the conductor inbox even though the operator already responded.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 10ae7f5. Configure here.

Comment thread src/mission/dispatcher.rs
}

let raw_lower = raw.to_ascii_lowercase();
let agents = self.channel.list_agents().await?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ambiguous agent ref now errors instead of returning None

High Severity

The dispatcher's resolve_agent_ref now delegates to channel.resolve_agent_ref, which returns Err on ambiguous matches. The old dispatcher code returned Ok(None) for ambiguity, which was gracefully handled at the call site (logged and skipped via continue). Now the Err propagates through ? at the call site, up through process_control_messages, and out of tick(), aborting the entire dispatcher tick for all claimed missions instead of just skipping one bad directive.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 10ae7f5. Configure here.

Comment thread src/channel.rs
let _: () = conn.del(&inbox_key).await?;
if !kept.is_empty() {
let _: () = conn.rpush(&inbox_key, kept).await?;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Non-atomic inbox read-delete-write loses concurrent messages

Medium Severity

remove_inbox_messages_matching reads the full inbox via lrange, then deletes the key and re-pushes kept messages. This is not atomic—any message pushed to the inbox between the lrange and the del will be silently lost. Since the dispatcher can send help requests to the conductor inbox concurrently with an operator action that triggers this cleanup, the race window is real even if small.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 10ae7f5. Configure here.

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.

Conductor routing/messaging rough edges: stale stopped agents, nickname send failures, and lingering old-mission help

1 participant