Repository navigation
feat(ui-automation): robust aria-pressed agentic→classic mode control - #332
Merged
Merged
Conversation
Replace the blind "click the Agent pill once and give up" recovery with a clean, reusable, state-aware component. Flow's Agent toggle is a button[aria-pressed]:has(span.content) whose aria-pressed IS the mode (false=classic media, true=agent) — locale-invariant, unlike the button text or the apps_spark_2 "Tools" icon (present in BOTH modes; a false forced-agentic signal that caused spurious "not recoverable" aborts). New leaf module `mode_control` (read_mode + ensure_media_mode): reads aria-pressed, closes the expanded chat sidebar (X) first, then toggles OFF only when actually on — never a blind click, idempotent, bounded loop. `_dismiss_agent_affordances` now delegates to `ensure_media_mode`, so every caller (image + video mode-switch, driver detection) gets the robust recovery. (The classic→agentic direction already has the robust `_force_agent_mode`.) Live-validated: `scripts/dev/spike_mode_roundtrip.py` drove a full classic→agent→sidebar→close→toggle-off→classic cycle on real Flow, asserting aria-pressed + crop_* at every step. Unit tests for the component + updated _exit_agent_mode integration tests. Removed the now-dead _AGENT_* loop constants. Recon: `scripts/dev/spike_mode_switch_agentic_to_classic.py` (apps_spark_2=Tools). Unblocks #299/#314/#183/#174. Follow-up: adopt read_mode + drop apps_spark_2/tune from AGENTIC_UI_INDICATORS (still a last-resort escalation signal today).
This was referenced Jul 17, 2026
Merged
15990166686ll-crypto
pushed a commit
to 15990166686ll-crypto/gflow-cli
that referenced
this pull request
Aug 24, 2026
…ffroliva#332) Replace the blind "click the Agent pill once and give up" recovery with a clean, reusable, state-aware component. Flow's Agent toggle is a button[aria-pressed]:has(span.content) whose aria-pressed IS the mode (false=classic media, true=agent) — locale-invariant, unlike the button text or the apps_spark_2 "Tools" icon (present in BOTH modes; a false forced-agentic signal that caused spurious "not recoverable" aborts). New leaf module `mode_control` (read_mode + ensure_media_mode): reads aria-pressed, closes the expanded chat sidebar (X) first, then toggles OFF only when actually on — never a blind click, idempotent, bounded loop. `_dismiss_agent_affordances` now delegates to `ensure_media_mode`, so every caller (image + video mode-switch, driver detection) gets the robust recovery. (The classic→agentic direction already has the robust `_force_agent_mode`.) Live-validated: `scripts/dev/spike_mode_roundtrip.py` drove a full classic→agent→sidebar→close→toggle-off→classic cycle on real Flow, asserting aria-pressed + crop_* at every step. Unit tests for the component + updated _exit_agent_mode integration tests. Removed the now-dead _AGENT_* loop constants. Recon: `scripts/dev/spike_mode_switch_agentic_to_classic.py` (apps_spark_2=Tools). Unblocks ffroliva#299/ffroliva#314/ffroliva#183/ffroliva#174. Follow-up: adopt read_mode + drop apps_spark_2/tune from AGENTIC_UI_INDICATORS (still a last-resort escalation signal today).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the root cause behind the spurious "forced agentic — classic media panel not recoverable" aborts (blocking #299/#314/#183/#174): the recovery clicked the Agent pill once, blindly, and treated
apps_spark_2(the always-present Tools nav icon) as proof of an unrecoverable agentic cohort.New leaf component
mode_control—read_mode+ensure_media_mode. Flow's Agent toggle isbutton[aria-pressed]:has(span.content); itsaria-pressedis the mode (false=classic,true=agent) — locale-invariant.ensure_media_modecloses the expanded chat sidebar (X) first, then toggles OFF only when actually on — never a blind click, idempotent, bounded._dismiss_agent_affordancesdelegates to it, so every caller (image + video mode-switch, driver detection) gets robust recovery. The classic→agentic direction already has the robust_force_agent_mode.Evidence (owner-provided live DOM + validation)
The mechanism was confirmed from owner-provided agentic-mode DOM/screenshots (PT locale) and then live-validated end-to-end by
scripts/dev/spike_mode_roundtrip.py, which drove a full classic → agent-on → sidebar → close → toggle-off → classic cycle on real Flow, assertingaria-pressedandcrop_*at every step:Test plan
mode_controlunit tests (state-machine fake: noop / agent→media / sidebar→agent→media / stuck)._exit_agent_modeintegration tests (fake now modelsaria-pressed; the stuck case still escalates toFlowAgentUiError)./gflow:check: ruff clean, pyright src 0/0/0, hygiene + doc-links clean; 257 transport/driver tests pass.ensure_agent_mode(duplicated the superior_force_agent_mode); kept the cohesiveread_mode+ensure_media_modepair.Follow-up (documented, not in this PR)
Adopt
read_modeinfactory.detect_ui_modeand dropapps_spark_2/tunefromAGENTIC_UI_INDICATORS(still used as a last-resort escalation signal today; the robust recovery now handles the common case first).🤖 Generated with Claude Code