Skip to content

The ligature-carrier sweep is unfinished: 55 single-carrier selectors, and nothing watches them #730

Description

@ffroliva

What

#703 swept three selector constants for the labs/migrated ligature-carrier split (<i class="google-symbols"> vs <mat-icon>) — add_2, arrow_drop_down, accessibility_new. It missed PROMPT_FORMAT_SELECTORS, which became a silent no-op and shipped as #727.

Nothing proves that was the last one. Measured on 2026-09-07 at 7c1d4c6d:

git grep "i\.google-symbols" src/gflow_cli --include=*.py | grep -v "mat-icon" | wc -l
55

55 single-carrier literals across ui_automation.py, ui_automation_video.py, mode_control.py, diagnostics.py and drivers/{agentic,factory}.py. Two consecutive PRs have now fixed one of these by hand, which is the signal that this is a sweep problem, not a selector problem.

Named candidates from the #729 council (all pre-existing, none introduced there):

Constant Location Exposure
IMAGE_MODEL_PICKER_TRIGGER ui_automation.py:104 Sharpest. Sole selector, no mat-icon twin — while its sibling _CHARACTER_MODEL_PICKER_TRIGGER_SELECTORS in the same file carries both and a comment recording that omitting mat-icon logged model-picker trigger not found on the migrated host. Likely masked today because _select_image_model only runs after a panel that warn-returns first — not verified on the migrated host.
ui_automation_video.py:251 same shape same class
OVERLAY_CLOSE_BUTTON_SELECTORS ui_automation.py:490-494 clear / close are <i>-only. Degraded, not dead — the [role=dialog]:has(a[href*=changelog]), button[data-dismiss] and Escape fallbacks remain.
NEW_PROJECT_SELECTORS ui_automation.py:329-331 add_2 tier is <i>-only; falls back to a regex + 14 localised labels.

Why nothing caught it, and why nothing will catch the next one

.github/workflows/selector-probe.yml drives run_probe.py --surface editor against src/gflow_cli/flow_selectors/registry.py. That registry has zero entries for the Format button:

grep -rn "personal_recommendations\|format" src/gflow_cli/flow_selectors/ --include=*.py
(no output)

And e2e_character is opt-in (GFLOW_CLI_E2E_RUN_CHARACTER=1); the nightly canary runs e2e_auth only — 18 of 87 tests. So the drift was invisible from both directions at once: no probe watched the selector, and no nightly test exercised the command.

Suggested shape

  1. Sweep the 55, both carriers — or better, anchor on the shared .google-symbols class, which matches <i> and <mat-icon> alike, since the class was never the discriminator (the tag was).
  2. Prefer custom elements where one exists — <flow-format-prompt-button>, <flow-slot-chip-button>, <flow-generate-icon-button> are component boundaries that survive both a carrier change and a translation.
  3. Register the surfaced anchors in flow_selectors/registry.py so selector-probe.yml watches them nightly. This is the half that makes the fix durable.

Worth a /gflow:predict first — item 1 touches ~55 call sites across 6 files and item 3 changes what the nightly probe asserts.

Evidence

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions