Skip to content

# fix(639): the migrated model picker — veo-lite-lp, [Lower Priority] ambiguity, and the overlay a switch leaves behind - #669

Merged
ffroliva merged 5 commits into
ffroliva:developfrom
kittinan:fix/migrated-lite-lp-model
Sep 5, 2026
Merged

ffroliva merged 5 commits into
ffroliva:developfrom
kittinan:fix/migrated-lite-lp-model

Conversation

@kittinan

@kittinan kittinan commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Three defects in the migrated flow.google.com model picker (#639). The first is the
reported one; the other two were found while fixing it and are the more serious pair —
one bills the wrong tier, one breaks the next run outright.

1. --model veo-lite-lp could not be selected at all. On an account Google has
already moved, _select_model refused with ConfigurationError (exit 11, "not available
on the migrated Flow host"
) before it ever opened the picker. The omission was
deliberate: the migrated map is keyed by product label, and that tier had no captured
label — the 2026-08-14 two-account capability matrix, #650's duration capture and
v0.61.0's refusal A/B all recorded a picker MISS. It is now matched the way the labs
driver has since #539: by the [Lower Priority] tag alone.

The entry has since been captured — a migrated account renders
Veo 3.1 - Lite [Lower Priority], and its picker was already defaulted to that tier,
which is the likely reason earlier captures missed it (they were taken on accounts Flow
was not throttling). Matching stays on the tag rather than the now-known label: one
account's rendering, the labs driver keys off the same tag, and a tag Flow appends to
whichever tier it throttles survives that tier changing.

2. --model veo-lite could silently generate on the throttled tier. Entries were
matched by case-insensitive substring with .first, and the same substring ran against
the picker button's read-back. Against the real button text:

"Veo 3.1 - Lite [Lower Priority] arrow_drop_down".lower().startswith("veo 3.1 - lite")
# -> True

So --model veo-lite returned "already selected", never opened the menu, and generated
on the throttled tier — which Flow bills. This is the ambiguity #539 fixed on labs.google
(whose selectors carry :not(:has-text('[Lower Priority]'))); the port had dropped it.

3. Switching --model broke the next run. Reported from the field: previous run on
veo-lite-lp; run 1 on omni-flash dies, run 2 on omni-flash works, run 3 on
veo-lite-lp dies. Picking from the menu leaves Angular with two stacked overlays
(the settings pane and the menu opened over it) and each Escape dismisses exactly one, so
_close_pane's single press left the settings pane covering the composer. send_prompt's
click then failed actionability and surfaced ~5 s later as a bare TimeoutError naming
[contenteditable='true'], pointing nowhere near the pane. A repeat run binds the model
at the button read-back, never opens the menu, and never stacks the second overlay — which
is exactly why re-running the same model worked.

The click's message blames stability, and that is a red herring. Sampling every 100 ms for
12 s after a switch:

composer bounding box .cdk-overlay-pane:visible
after a switch identical for 12 s 1, for the whole window
after a repeat identical 0

migrated.pane_still_open does fire on the pre-fix source — confirmed by replaying it
against the live host, not assumed. It was a warning the run ignored; only the consequence
was missing, and it is now the failure itself.

What changed

  • ModelMenuMatcher(contains, excludes) replaces the bare label lookup and drives both
    the button read-back and the menu scan, so the two cannot drift.
  • The menu is matched in Python over all_text_contents() rather than a has_text filter:
    an exclusion is not expressible as has_text, and more than one hit must refuse
    (naming every candidate) instead of resolving .first.
  • _close_pane escapes until no overlay is visible (bounded at 4) and raises pre-submit at
    $0 if one will not close.
  • New log events migrated.model_already_selected (the read-back short-circuit previously
    emitted nothing at all) and a counted migrated.pane_still_open.
  • Routing deliberately unchanged: migrated_can_serve still gates on
    VIDEO_MODEL_MENU_LABELS, so veo-lite-lp does not pull an unmoved account onto the
    new host. A moved account is routed by its URL and never reaches that question.

Refs #639. Not filed as its own issue — reported directly against v0.67.0.

Lifecycle

  • Issue triaged with issue-assessment — not run. This arrived as a direct
    field report against v0.67.0, not a GitHub issue. Surfaces: CLI only — no MCP or CLI
    flag surface is touched (--model veo-lite-lp already existed and already validated).
  • predict verdict recorded — not run, and this does touch selectors. It is a
    pre-implementation gate and the work began as a one-line map addition; defects 2 and 3
    were then found by measurement rather than design. Running it retroactively would be
    theatre — say the word and I will run it against the diff instead.
  • scenario + plan under docs/superpowers/plans/ — not written, same
    reason. The investigation is written up instead as
    docs/superpowers/spikes/2026-09-05-migrated-model-menu-lower-priority.md, with the
    captures, the measurements and an explicit "recorded, not asserted" section.
  • check — components run individually, not the skill, and the step 1b CLI↔MCP
    mirror sweep was not run as such. What did run is listed under Validation, plus the full
    MCP suite (tests/mcp/, 168 passed).
  • MCP twin — not applicable. git diff origin/develop...HEAD -- src/gflow_cli/mcp/ src/gflow_cli/cli_video.py src/gflow_cli/api/video.py is empty; this changes only
    the migrated transport's internals. No new CLI surface to mirror.
  • Live-verified against real Flow. Details below, including what could NOT be
    verified.
  • Council review — not run. Happy to run branch-review before this is looked
    at if you would rather see it first.

Live verification

Instrument: scripts/dev/capture_migrated_model_menu.py (new). Selection happens entirely
before submit — what made v0.61.0's and v0.62.1's refusal matrices free — so all of this
is $0 except where noted.

--drive, the shipped _select_model for all five tiers, reading the picker back after
each switch (the only evidence separating "clicked the entry the user asked for" from
"returned success"):

requested outcome button read back after
omni_flash selected Omni 1.1 Flash
veo_3_1_lite selected Veo 3.1 - Lite
veo_3_1_fast selected Veo 3.1 - Fast
veo_3_1_quality selected Veo 3.1 - Quality
veo_3_1_lite_lower_priority selected Veo 3.1 - Lite [Lower Priority]

Both directions of the exclusion are exercised: plain Lite lands on the plain entry with
the throttled sibling in the same menu, and the throttled tier is reached through the
menu.

The reported sequence replayed after the fix — omni-flash (switch) / omni-flash
(repeat) / veo-lite-lp (switch): all three apply settings and type the prompt, with
visible_overlays_after_settings: 0.

Two real generations (not $0): one on veo-lite-lp, and one end-to-end on the switch
path (veo-lite-lp → omni-flash, migrated.model_selected in the timeline) that
submitted, generated and downloaded a 2.6 MB clip, exit 0.

Could NOT be verified:

  • One account, one locale (en). Whether Flow serves the [Lower Priority] entry to
    accounts it is not throttling is untested, and every earlier capture suggests it does
    not. The routing gate is left conservative for exactly this reason.
  • The ambiguity refusal has no live case. This account's menu yields exactly one hit
    per matcher, so the >1 branch is covered offline only.
  • _open_pane cannot be called twice on one page load — re-opening the settings pane
    leaves a detached overlay that still contains radiogroups and .last resolves to it
    ("0 option groups"; reproduced twice while building the probe, which now loads a fresh
    document per tier). Production opens the pane once per run, so no shipped path is known
    to hit it. Recorded in the spike, not filed.

Validation

  • Focused tests added — 9 new, of which 8 fail against origin/develop's
    source
    and pass here. The ninth
    (test_repeating_the_same_model_never_stacks_the_second_overlay) passes on both by
    design
    : it pins the working half of the reported asymmetry. The fake DOM now models
    what the live host does — a menu pick leaves an overlay that eats the next Escape,
    :visible excludes the detached pane, and a covered composer is not clickable.
  • Documentation updated — CHANGELOG.md, KNOWN_ISSUES.md +
    website/docs/KNOWN_ISSUES.md, and the new spike.
  • uv run python scripts/ci/check_doc_links.py → All links resolved across 29 files.
  • uv run ruff check src tests → All checks passed! (plus ruff format --check,
    426 files already formatted)
  • uv run pyright src → 0 errors, 0 warnings, 0 informations
  • Relevant pytest commands:
    • uv run pytest -q tests/api/transports/test_migrated_composer.py → 34 passed
    • uv run pytest -q tests/mcp/ → 168 passed
    • full suite → 3820 passed, 12 skipped

One unrelated pre-existing failure, reproduced on an untouched origin/main worktree:
tests/features/test_auth_steps.py::test_status_of_a_profile_with_a_live_flow_session
asserts "experiments" in output, but Rich wraps the long basetemp path mid-word
(profile_expe\nriments). Environment/path-length artifact, not from this branch.

Worth flagging for review: both the offline suite and the --drive matrix missed
defect 3, because neither touched the composer after a real switch. The new switch-path
tests close that gap, but the matrix alone is not sufficient coverage for this driver.

Contribution Checklist

  • This PR targets develop
  • Commits use a real Git identity (Kittinan <tunxedge@gmail.com>)
  • Signed-off-by: is missing. This is an external contribution and the three
    commits carry no DCO trailer. Needs
    git rebase --signoff origin/develop (or a re-commit with -s) before merge.
  • No secrets, cookies, tokens or signed URLs. Scanned the new files; what the spike
    and probe do contain is a profile name, project ID and media IDs from the capture —
    the same class of identifier existing LIVE_VERIFICATION_*.md docs carry, but they are
    account data, so redact them if you would rather they were not in the repo.
  • AI-assisted changes reviewed before submitting — author to confirm. These commits
    were written by Claude Code with live measurement; the diff, the spike's claims and the
    two real generations should be read before this is opened.

@kittinan
kittinan requested a review from ffroliva as a code owner September 5, 2026 11:29
@github-actions github-actions Bot added external-contribution Pull request from a non-maintainer or fork needs-copilot-review Ask GitHub Copilot for advisory code review needs-maintainer-review Requires maintainer review before merge labels Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Thanks for the contribution. This PR has been marked as an external contribution and routed for maintainer review.

Before merge, please make sure:

  • The PR targets develop.
  • Commits use a real Git identity or GitHub noreply email.
  • External commits include a DCO sign-off: git commit -s.
  • The PR does not include secrets, cookies, account tokens, signed URLs, or private captured data.
  • Focused tests/docs are included for behavior changes.

GitHub Copilot code review may be requested as an advisory first pass, but maintainer approval is still required.

kittinan and others added 3 commits September 5, 2026 18:40
…ority] binding the wrong tier

`--model veo-lite-lp` could not be selected at all on an account Google has
already moved to flow.google.com: VIDEO_MODEL_MENU_LABELS carried only the four
tiers whose product names have been read back from a live menu, so
`_select_model` refused with ConfigurationError (exit 11, "not available on the
migrated Flow host") before it ever opened the picker.

The omission was deliberate, not an oversight — the tier's full menu label is
unknown. Flow has never rendered the entry on any account gflow has driven: the
2026-08-14 two-account capability matrix, ffroliva#650's duration capture and v0.61.0's
refusal A/B all recorded a picker MISS. The migrated map is keyed by label, so
there was nothing to key it with.

Match it the way the labs driver has since ffroliva#539 — by the `[Lower Priority]` tag
alone. No label is invented, and nothing is asserted about an entry no capture
has produced.

That exposed a second defect the port had inherited from nowhere: entries were
matched by case-insensitive *substring* with `.first`. `Veo 3.1 - Lite` is a
substring of `Veo 3.1 - Lite [Lower Priority]`, so on an account whose menu
carries the sibling, `--model veo-lite` bound whichever Angular listed first and
Flow billed it. The same substring ran against the picker button's read-back,
where a pane already showing the lower-priority tier satisfied
`startswith("Veo 3.1 - Lite")`, returned "already selected", and silently kept
it. labs.google has carried `:not(:has-text('[Lower Priority]'))` against
exactly this since ffroliva#539.

- ModelMenuMatcher(contains, excludes) replaces the bare label lookup and is
  used for both the button read-back and the menu scan, so the two cannot drift.
  Ordinary tiers exclude the tag; the lower-priority tier is the tag.
- The menu is matched in Python over `all_text_contents()` rather than through a
  `has_text` filter: the text is read back for the refusal diagnostic anyway, an
  exclusion is not expressible as `has_text`, and more than one hit must refuse
  (naming every candidate) instead of resolving `.first`.
- Routing is unchanged on purpose. `migrated_can_serve` still gates on
  VIDEO_MODEL_MENU_LABELS, so veo-lite-lp does not pull an unmoved account onto
  the new host — that would trade a working driver for an unverified one. A
  moved account is routed by its URL and never asks the question.

All five new tests fail on the pre-fix source and pass after.

Not live-verified: no account available here renders the lower-priority entry,
which is the same blocker the labs side has had since August. The tag is
inherited from a shipped, live-verified labs selector rather than guessed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MK6VHisb4JifQPAmvm724e
Signed-off-by: Kittinan <tunxedge@gmail.com>
…ead-back short-circuit

Follow-up to 85b87b1, which shipped the matcher unverified because no account
here was known to render the lower-priority entry. One does — this one — so the
change is now measured rather than argued.

The entry has been captured for the first time: the migrated menu renders
`Veo 3.1 - Lite [Lower Priority]`, and the account's picker was already
*defaulted* to it. That is the likely reason every earlier capture recorded a
picker MISS (2026-08-14's two-account matrix, ffroliva#650, v0.61.0): those accounts were
not being throttled, so Flow did not serve them the entry. Matching stays on the
`[Lower Priority]` tag rather than moving to the now-known label — one account's
rendering, the labs driver keys off the same tag, and a tag Flow appends to
whichever tier it throttles survives that tier changing.

Verified at $0 by driving the shipped `_select_model` for all five tiers and
reading the picker back after each switch (selection happens before submit, which
is what made v0.61.0's and v0.62.1's matrices free):

    omni_flash                   -> Omni 1.1 Flash
    veo_3_1_lite                 -> Veo 3.1 - Lite
    veo_3_1_fast                 -> Veo 3.1 - Fast
    veo_3_1_quality              -> Veo 3.1 - Quality
    veo_3_1_lite_lower_priority  -> Veo 3.1 - Lite [Lower Priority]

Both directions of the exclusion are exercised: plain Lite lands on the plain
entry with the throttled sibling in the same menu, and the throttled tier is
reached through the menu. The read-back defect is confirmed against the real
button text — `"Veo 3.1 - Lite [Lower Priority] arrow_drop_down"` satisfies the
pre-fix `startswith("veo 3.1 - lite")`, so `--model veo-lite` on this account
generated on the throttled tier without ever opening the menu.

Also closes the observability gap the verification ran into. A run that
short-circuits at the button read-back emitted no model event at all, so the
timeline could not distinguish "bound the tier you asked for" from "never touched
the picker"; answering it needed a separate probe. It now logs
`migrated.model_already_selected` with the observed button text.

- scripts/dev/capture_migrated_model_menu.py: the $0 instrument (`--drive` for
  the matrix). Loads a fresh document per tier: re-opening the settings pane on
  one page load leaves a detached overlay that still contains radiogroups, and
  `_open_pane`'s `.last` then resolves to it. Production opens the pane once per
  run, so no shipped path is known to hit that — recorded in the spike, not filed.
- docs/superpowers/spikes/2026-09-05-migrated-model-menu-lower-priority.md: the
  capture, the matrix, and what is NOT established (one account, one locale;
  whether an unthrottled account is ever served the entry stays untested).

One 8 s generation was spent reproducing the report before the $0 probe was
reached for; the spike records that as a misstep rather than a cost of the work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MK6VHisb4JifQPAmvm724e
Signed-off-by: Kittinan <tunxedge@gmail.com>
…r breaks the next run

Reported: with a previous run on veo-lite-lp, run 1 on omni-flash dies, run 2 on
omni-flash works, run 3 on veo-lite-lp dies. A *switch* fails; a *repeat*
succeeds.

Reproduced at $0 and measured rather than guessed. Selecting from the model menu
leaves Angular with TWO stacked overlays — the settings pane and the menu opened
over it — and each Escape dismisses exactly one. `_close_pane` pressed it once,
so the settings pane stayed open, covering the composer. `send_prompt`'s click
then failed Playwright's actionability check and surfaced ~5 s later as a bare
`TimeoutError` naming `[contenteditable='true']`, with nothing pointing at the
pane.

The click's own message blames stability, and that is a red herring. Sampling
every 100 ms for 12 s after a switch: the composer's bounding box is IDENTICAL
throughout — it was never unstable — while `.cdk-overlay-pane:visible` sits at 1
for the whole window. One more Escape takes it to 0 and the prompt types.

A repeat run binds the model at the button read-back, never opens the menu and
never stacks the second overlay, which is exactly why re-running the same model
worked and why the offline suite and the `--drive` matrix both missed it: neither
touched the composer after a real switch.

`_close_pane` now escapes until no overlay is visible (bounded at 4), and raises
`UiSelectorDriftError` pre-submit at $0 if one will not close, instead of leaving
the composer click to report it as an unattributable timeout. The overlay count
is re-queried each pass rather than held in one locator.

The signal was already there: `migrated.pane_still_open` DOES fire on the pre-fix
source — confirmed by replaying it against the live host, not assumed. It was a
warning the run ignored. Only the consequence was missing; it is now the failure.

Tests model what the live host does: a menu pick leaves an overlay that eats the
next Escape, `:visible` excludes the detached pane, and a covered composer is not
clickable. The two switch-path tests fail on the pre-fix source; the repeat-path
test passes on both, which is the reported asymmetry.

Verified live after the fix, all $0: the reported sequence — omni-flash (switch),
omni-flash (repeat), veo-lite-lp (switch) — applies settings and types the prompt
in all three, with 0 visible overlays left. One authorised end-to-end run on the
switch path (veo-lite-lp -> omni-flash, `migrated.model_selected` in the timeline)
submitted, generated and downloaded a 2.6 MB clip, exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MK6VHisb4JifQPAmvm724e
Signed-off-by: Kittinan <tunxedge@gmail.com>
@kittinan
kittinan force-pushed the fix/migrated-lite-lp-model branch from 90bd60c to 1901d85 Compare September 5, 2026 11:41

@ffroliva ffroliva left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

PR #669 — Council Review Verdict

Reviewed at 1901d85 (pinned). No SHA drift during review. Protocol: skills/pr-council-review/SKILL.md v2.1.

Consensus: 🔴 RED — two must-fix items, both small

Let me say up front that the evidence quality here is the best I have reviewed on this repo: a $0 instrument, a read-back matrix that separates "clicked what was asked" from "returned success", a measured refutation of the stability red herring, and an explicit "could NOT be verified" section. Defects 2 and 3 are real, well-diagnosed and worth more than the reported one. The blockers below are narrow and none of them touch the core fix.

Dim Verdict Headline
D0 CI-mechanical 🟢 All six gates re-run at 1901d85 + ruff/pyright/34 tests. 14/14 GitHub checks green.
D1 Correctness 🟡 _close_pane raising inside finally masks the precise error (reproduced).
D2 Code quality 🟡 _close_pane widens a selector that _open_pane deliberately narrows.
D3 Security 🟡 Profile name + project UUID in the spike; the PII gate does not cover that path.
D4 Tests 🟢 9 new, 8 red pre-fix. Affected surface exercised.
D5 Memory hygiene 🔴 Two memory files still record the claim this PR disproves.
D6 UI / live-verify 🟡 Live evidence strong; [Lower Priority] remains an English prose anchor.
D9 Docs drift 🟢 CHANGELOG + KNOWN_ISSUES + mirror consistent; links and mirror gates pass.
D13 Scripts 🟢 New probe follows the _spike_common sibling convention.
D14 YAGNI 🟢 One nit: PANE_CLOSE_ESCAPES = 4 where 2 is the measured need.
D15 Surface parity 🟢 Your "MCP twin not applicable" claim verified — diff on mcp/, cli_video.py, api/video.py is empty.

Must-fix (2)

1. 🔴 D5 — the memory store still says this tier is a picker MISS

docs/superpowers/memory/video-model-capability-matrix.md records, as current state:

  • :103 — | veo_3_1_lite_lower_priority | **PICKER MISS** (still) | — | — |
  • :144 — "What is NOT settled: … whether lower_priority's miss is selector or absence."

and flow-capabilities-are-cohort-dependent.md carries "(lower_priority missed its picker, so it stays unmeasured)".

This PR settles that open question: the entry exists, renders as Veo 3.1 - Lite [Lower Priority], and the miss was cohort/absence — not selector drift — because Flow serves it to accounts it is actively throttling. That is exactly the fact the memory store exists to carry, and right now it points the next agent the wrong way. The spike is the capture; memory is the routing layer, and only the spike was updated.

Concretely: update the :103 row, retire the "not settled" clause at :144, and amend the cohort file's "stays unmeasured" parenthetical. check_council_memory.py stays green either way (47 files, all cited) — this is a content edit, not a wiring one.

2. 🟡→blocking D1 — _close_pane raising in finally replaces the real error

apply_video_settings calls _close_pane in a finally: (migrated_composer.py:224-225). It previously could only warn; it can now raise. When the try body already failed, the new raise discards the actionable diagnostic. Reproduced against your own fake DOM:

RAISED : UiSelectorDriftError — "2 overlay(s) still visible after 4 Escape presses" (exit 23)
CONTEXT: ConfigurationError — "model 'veo_3_1_quality' is not offered on this account's
         migrated Flow host; offered: Omni 1.1 Flash, Veo 3.1 - Fast" (exit 11)

The user gets exit 23 ("selectors drifted, file a bug") instead of exit 11 ("pass a different --model"), and the offered-models list is gone. __context__ does not survive into the Problem Details output, so it is invisible. Suggested shape: only raise from _close_pane when no exception is in flight — or let apply_video_settings log-and-swallow the close failure when the body already raised.


Nice-to-have (3)

  1. _close_pane is broader than the bug requires (:279-297). It aborts on any visible .cdk-overlay-pane, while _open_pane (:236) deliberately narrows the same selector with .filter(has=RADIOGROUP) — the file already knows the class is not unique to the settings pane. .cdk-overlay-pane is the generic Angular CDK class (snackbars, tooltips, dialogs), and Escape does not dismiss a snackbar. A transient toast would now hard-abort a run that used to succeed. Cost is $0 and pre-submit, so this is a false-abort risk rather than a credit risk — but scoping the count to panes containing RADIOGROUP/MENU_ITEM would keep the fix and drop the blast radius.

  2. PII in the spike — docs/superpowers/spikes/2026-09-05-…md:3-4 carries profile kittinansr2 and project 66324c59-…. You flagged this yourself. Worth noting the gate gap: check_website_docs_pii.py scans only website/docs/ (25 files), so docs/superpowers/ is unscanned and CI is green regardless. Maintainer's call, but the redaction is two lines.

  3. PANE_CLOSE_ESCAPES = 4 where the measurement says 2. The comment calls the headroom deliberate, which is fair — flagging only because D14 asks.


Correcting one thing in the PR body

Signed-off-by: is missing. … Needs git rebase --signoff origin/develop before merge.

This is not the case — all three commits already carry the trailer:

1901d85  Signed-off-by: Kittinan <tunxedge@gmail.com>
7baa566  Signed-off-by: Kittinan <tunxedge@gmail.com>
ea1f8c0  Signed-off-by: Kittinan <tunxedge@gmail.com>

No rebase needed. Worth fixing the checkbox so it does not cost anyone a round trip.


Confirmed-good

  • D0 green at the reviewed SHA, run in a detached worktree, not inherited from your log: ruff check (all passed), ruff format --check (428 files), pyright src (0/0/0), check_doc_links (29 files), check_repo_hygiene (947 files), check_website_docs_pii, check_council_memory (47 files), generate_website_docs --check (mirror in sync), and pytest tests/api/transports/test_migrated_composer.py → 34 passed.
  • The .first → refuse change is right, and matches labs. ui_automation_video.py:265-291 reasons through this exact hazard and reaches the same conclusion; the port now carries the :not(:has-text('[Lower Priority]')) semantics it had dropped, on both the menu scan and the button read-back. Driving both from one ModelMenuMatcher is the correct structural fix — the two paths drifting is what caused defect 2.
  • Matching on the tag rather than the newly-captured label is the right call, and for the reason you give. VEO_3_1_LITE_LOWER_PRIORITY keys off the same tag on labs (ui_automation_video.py:291), so the two drivers stay symmetric.
  • Routing left alone is the conservative choice and is well argued: migrated_can_serve still gates on VIDEO_MODEL_MENU_LABELS, so one account's capture cannot pull an unmoved account onto the new host.
  • migrated.model_already_selected closes a real observability hole — a silent short-circuit on a billing-relevant path was worth a log line.
  • The test fake now models the mechanism, not the symptom: one overlay dismissed per Escape, :visible excluding the detached pane, and a covered composer that is genuinely unclickable. That is why 8 of the 9 fail pre-fix.

Note on gates

SonarCloud analysis is skipped, not green — expected on a fork PR, so it is maintainer-checked here rather than a signal from this PR. Per AGENTS.md it cannot be called merge-ready on the Sonar axis until checked from a maintainer branch.

On the skipped lifecycle phases

Your accounting of what was and was not run is honest and I would not ask for retroactive predict/scenario theatre. The one gap with teeth is check step 1b — but D15 above verifies the mirror axes hold by inspection, so it is satisfied in substance.


Generated by Claude Code

… settle the memory row

Review feedback on ffroliva#669. Both must-fix items and all three nice-to-haves.

D1 (blocking) — `_close_pane` in a bare `finally` replaced the error already in
flight. A `--model` Flow does not offer raises ConfigurationError (exit 11)
naming what IS offered; a pane that then refused to close overwrote it with
UiSelectorDriftError (exit 23) and the offered-models list was gone. The close is
now `strict=False` on the exception path (warn, never raise) and `strict=True`
only when the settings pass succeeded, so a stuck pane can still fail a run that
would otherwise have typed a prompt into a covered composer — without ever
standing in front of a better error. This was a defect introduced by this branch,
not a pre-existing one.

D5 (blocking) — the memory store still said PICKER MISS. Worse, it twice
concluded from that miss that the `:not()` selector was broken. It never was: the
entry is simply not served to accounts Flow is not throttling, and a throttled
account renders it. Updated the 2026-08-14 row, the "CONFIRMED on a 2nd account"
paragraph that drew the wrong conclusion, the 2026-09-03 row, and the "What is
NOT settled" clause, and appended the settling capture.
`flow-capabilities-are-cohort-dependent` gains the generalisation: an ABSENT menu
entry is cohort evidence exactly like a present one, and "missing on N accounts"
never by itself indicts the selector. Duration and credits for the tier stay
recorded as unmeasured — it has only been selected at $0, never generated on.

D2 — `_close_pane` targeted every `.cdk-overlay-pane`, but those classes are
generic CDK and Flow mounts snackbars and tooltips in the same container. It now
narrows to overlays containing a radiogroup or a menuitem, the two that are ours
and the only two that answer to Escape.

D14 — PANE_CLOSE_ESCAPES 4 -> 2, the measured stack depth. The headroom was
hope; a third stacked overlay is now a named failure instead of something
absorbed quietly.

PII — the spike named a profile and a project id. Both removed; what mattered was
never the identity but that Flow was throttling the account, which is what the
doc says now. The gate not covering `docs/superpowers/` is left alone
deliberately: it exists to verify the anonymized `website/docs/` mirror, and 65
files in the canonical `docs/` tree legitimately carry the tokens it forbids, so
widening its scope would fail CI on all of them. Raised in the PR instead.

Three new tests, all failing on the pre-review source: the stuck pane no longer
masks the caller's error, an unrelated overlay is not ours to dismiss, and the
constant matches the measurement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MK6VHisb4JifQPAmvm724e
Signed-off-by: Kittinan <tunxedge@gmail.com>
@ffroliva

ffroliva commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Re-review at 2731f4d — RED cleared. Merged develop onto your branch to resolve the CHANGELOG conflict.

Both must-fix items and all three nice-to-haves from the earlier council are addressed, verified against the SHA rather than the commit message:

Finding State at 2731f4d
D5 memory rows still said PICKER MISS video-model-capability-matrix.md rows at :17/:106 now read "absence, not a broken selector; settled 2026-09-05", the "NOT settled" clause is struck, and the cohort file carries the generalisation.
D1 _close_pane raising in finally masked the real error apply_video_settings now catches BaseException, closes with strict=False (warn only) and re-raises; strict=True only on the success path.
D2 selector broader than _open_pane _blocking_overlays filters .cdk-overlay-pane:visible to panes containing a radiogroup or menuitem.
D14 PANE_CLOSE_ESCAPES = 4 Now 2.
D3 PII in the spike Profile and project id removed; the header says so.

Mechanical gate re-run in a detached worktree at 2731f4d: ruff, format, doc-links, hygiene, website PII, council-memory, website mirror, pyright src (0/0/0), test_migrated_composer.py → 37 passed. After the merge with develop: 39 passed, all doc gates green.

What I pushed to your branch (9da2502, using the maintainer-edit permission you granted): a merge of origin/develop — v0.68.0 shipped an hour ago and its back-merge emptied [Unreleased], so your ### Added and ### Fixed entries are re-homed under the new [Unreleased] section and [0.68.0] keeps only what shipped. No code changes beyond git's clean auto-merge of migrated_composer.py and its tests (your _close_pane work and #672's submit-enable poll coexist). If you had local commits on top of 2731f4d, pull before pushing again.

Evidence bar, forward-looking only. This PR carries live-verify evidence (two Veo generations, the $0 read-back matrix) and meets the current bar. Open PR #675 will additionally require a tests/e2e/ test, run or written, for every behavior change. It is not applied retroactively here; flagged for the maintainer, not held against this PR.

Merging once CI reports on 9da2502. SonarCloud stays skipped on fork PRs and is maintainer-checked.

@ffroliva
ffroliva merged commit 8287a51 into ffroliva:develop Sep 5, 2026
15 checks passed
ffroliva added a commit that referenced this pull request Sep 5, 2026
…er clause to the merge

Council review of #677 found the substitution still reachable -- not through
the wording, but through the verdict colour. D4 said "must-fix" while scoring
YELLOW, and a YELLOW is dismissable under section 5 step 8 with a one-line
justification. "The live runs in the PR body are good enough" is precisely the
justification someone writes, which reopens the ledger-for-e2e substitution
through the escape valve rather than through the text.

- Absent with no reason given now scores D4 RED, forcing consensus RED under
  section 5 step 3, and the skill says why it is not YELLOW so nobody softens
  it back later.
- Absent WITH a stated reason stays YELLOW, blocked on a maintainer run -- that
  is the documented exemption, not an evasion.
- Present is now verified rather than trusted: cite the tests/e2e file:line,
  confirm it exists at REVIEWED_SHA, confirm it exercises the changed surface.
  A passing e2e test that never touches the changed path is
  pr-must-verify-on-affected-surface wearing an e2e label.
- The grandfather clause said "PRs opened before 2026-09-05", which excluded
  the only two PRs it exists for: #669 opened 11:28Z and #671 at 13:38Z, both
  hours before #675 merged at 19:15Z the same day. Now anchored to the merge
  commit, not the calendar day.

The published slug carries the same RED wording, so the memory a future
reviewer loads cannot disagree with the skill that cites it.
ziyedbe pushed a commit to ziyedbe/gflow-cli-fork that referenced this pull request Sep 5, 2026
PR ffroliva#675 put the rule in the contributor-facing docs, but the review skills
still scored by the old bar: D4 treated a missing surface test as
"automatically YELLOW", and nothing told a reviewer that a PR body full of
live runs does not close it. A rule the reviewer does not apply is not a rule.

- New published slug `e2e-evidence-is-a-contributor-deliverable`, cited from
  the D4 mandatory-memory row so `check_council_memory.py` enforces the round
  trip both ways.
- pr-council-review D4: missing e2e evidence on a Flow surface is a must-fix,
  with the three substitutions that must be refused named explicitly -- a
  live-verify ledger, a BDD .feature file, and demanding an impossible run from
  a docs-only change. PRs opened before 2026-09-05 are flagged forward-looking
  rather than held to it.
- pr-council-review section 9: autonomous mode reports e2e evidence, never runs
  it -- the sandbox has no Flow auth and must not spend credits, so GREEN there
  means "evidence present and plausible", never "e2e passed".
- live-verify Notes: this skill is not the e2e suite and does not satisfy the
  rule; a Flow-surface PR owes both.

The substitution the D4 text refuses was proposed on ffroliva#669 within an hour of
ffroliva#675 landing, which is why it is written down rather than left to judgement.
@kittinan kittinan mentioned this pull request Sep 6, 2026
13 of 19 tasks
@ffroliva ffroliva mentioned this pull request Sep 6, 2026
ffroliva pushed a commit that referenced this pull request Sep 6, 2026
…echanism found, gesture not reliable

The account owner supplied the human gesture (ArrowDown + Enter; a mouse click
also works by hand), which explained every earlier failure: a synthetic Playwright
click reads as a click-away and dismisses the overlay. ArrowDown alone behaves —
the @ survives, the active asset stays active, UpteDb fires for its detail load.
Enter after it still cleared the composer, with and without waiting for that load.

What did attach was typing a query after the @:

    <span class="mention-chip"
          data-mention-id="c68767a5-..."
          data-reference-type="entity"
          data-entity-id="c68767a5-..."
          contenteditable="false">aloha</span>

Checked rather than assumed: a clean editor load reads chips: [] with an empty
placeholder composer, so the chip was created by the typing and was not left over
from an earlier session.

That settles the SHAPE. A reference is a chip in the prompt document carrying an
entity id, which is consistent with the previous commit's finding that no rpc
fires on attach. A port builds a prompt containing chips and can verify them by
reading `.mention-chip[data-entity-id]` before submitting.

It is NOT reliable and must not be built on as-is: the chip reproduced ONCE, and
two later runs with the same query produced chips: 0, each failing the same way —
the picker stayed open and the next composer click died on a `.cdk-overlay-backdrop`
intercepting pointer events. So the insert is timing- or query-dependent in a way
that is not characterised, and no run has yet produced both a chip and a submit.
Whether the YhhmEf payload carries the entity id therefore remains open.

Side finding worth a look when the port lands: `_close_pane` counts
`.cdk-overlay-pane` only, and a `.cdk-overlay-backdrop` can outlive it and still
intercept pointer events — measured twice here. Same class as the #669 overlay
bug, same symptom. No shipped t2v path opens a picker, so it is not known to be
reachable in production.

No production code touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MK6VHisb4JifQPAmvm724e
Signed-off-by: Kittinan <tunxedge@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contribution Pull request from a non-maintainer or fork needs-copilot-review Ask GitHub Copilot for advisory code review needs-maintainer-review Requires maintainer review before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants