You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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:
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 candidate → cause="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.
selected_mechs pinning a mech with zero relevant_tools → empty candidate → same stall.
Surfacing. Whatever the resolution, the stuck/conflict state should be surfaced to the ChatUI status surface rather than only logged.
#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.
Problem
allowed_toolsandselected_mechsare 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_toolsreturns an empty set with acause, the round emitsEvent.NONEand 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:available_prediction_tools)._candidate_toolsre-validatesallowed_toolsevery 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 policyaccuracy_storeare 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: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 pinnedselected_mechsdon't serveY. Theselected_mechsintersection runs after theallowed_toolsrevalidation and emptiescandidate→cause="selected_mechs"→Event.NONEself-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.selected_mechspinning a mech with zerorelevant_tools→ empty candidate → same stall.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
allowed_tools∩ tools-served-by-selected_mechson either pin update) to catch the knowable-at-pin-time subset early.Context
decision_maker_abci/behaviours/tool_selection.py::_candidate_tools(selected_mechsblock) /_select_tool; FSM(ToolSelectionRound, NONE)self-loop;chatui_abcistatus surface.