Skip to content

Surface the allowed_tools × selected_mechs (mech×tool) pin conflict instead of stalling ToolSelectionRound #991

Description

@LOCKhart07

Problem

allowed_tools and selected_mechs are persistent ChatUI pins, but the set of actually-selectable tools is dynamic. A pin valid when made can become unsatisfiable at runtime, with no fault on the user's part. When _candidate_tools returns an empty set with a cause, the round emits Event.NONE and the FSM self-loops (ToolSelectionRound, NONE): ToolSelectionRound — the agent never advances to bet placement, so trading fully halts until the user manually changes the pin. The only signal is a per-round WARNING; the ChatUI surfaces nothing.

What #990 already fixed

The allowed_tools-vs-suitability slice is closed in #990:

  • Write side: the ChatUI now validates/displays pins against the published suitable set (available_prediction_tools).
  • Read side: _candidate_tools re-validates allowed_tools every round and non-destructively ignores entries that are no longer selectable (drift / cold-start window / IPFS outage), relaxing to the full selectable set instead of self-looping. The stored pin and the policy accuracy_store are left intact.

So a stale/invalid tool pin no longer stalls the round.

What remains (this issue)

The allowed_tools × selected_mechs (mech×tool) conflict, plus observability:

  1. Mech×tool conflict. allowed_tools=[Y] is suitable (so fix(chatui): show and validate tool pins against the suitable set #990's read-side revalidation keeps it), but the pinned selected_mechs don't serve Y. The selected_mechs intersection runs after the allowed_tools revalidation and empties candidatecause="selected_mechs"Event.NONE self-loop. Reachable whenever mechs register heterogeneous tool sets (supported; Omen especially — not today's Polystrat, where all mechs share one service/tool set, but that's a data coincidence). Flagged by @OjusWiZard on fix(chatui): show and validate tool pins against the suitable set #990.
  2. selected_mechs pinning a mech with zero relevant_tools → empty candidate → same stall.
  3. Surfacing. Whatever the resolution, the stuck/conflict state should be surfaced to the ChatUI status surface rather than only logged.

Why this is separate from the #990 read-side fix

#990 relaxes a stale tool pin because a tool that has dropped out of the selectable set is structurally unusable — dropping it for the round is unambiguous. A mech×tool conflict is two individually-valid pins that disagree; silently relaxing one would pick a winner between explicit user intents. That deserves a deliberate product decision (surface the conflict; halt-but-visible vs relax-and-notify) rather than a quiet relax.

Proposed direction

  • Surface the conflict to the ChatUI (e.g. "your tool pin and mech pin don't overlap — adjust one").
  • Decide halt-but-visible vs relax-and-notify for the mech-pin case.
  • Optionally, bidirectional pin-time validation in the handler (check allowed_tools ∩ tools-served-by-selected_mechs on either pin update) to catch the knowable-at-pin-time subset early.

Context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions