Skip to content

Image-gen reliability hardening: cohort-lock UX, multi-candidate ambiguity, ref re-upload, click humanization, HAR/debug, Camoufox eval #311

Closed

Description

@ffroliva

Consolidated findings from a 2026-07-14/15 production session (Compiled Growth content/business-parable/protocol — cooking story s007 via gen_scenes_resumable.sh on gflow-cli 0.32.1/0.35.0). Eight dimensions, all confirmed live against real Flow projects. No account/token details included below.

1. Agentic cohort lock has no forced-flip, only detect-and-abort

--ui-mode classic (0.34.0+) correctly detects when Flow's server has pinned a profile to the agentic arm and aborts before submitting (UiModeUnavailableError, exit 28) — this is good, safe behavior. But there's no way to request a flip back to classic; the only lever is blind retry. In this session both test accounts (denon82, ffroliva) got stuck agentic-only for extended periods (one scene needed 13 consecutive retry attempts spanning ~20 minutes before landing classic again). A gflow auth refresh-cohort (force a fresh page load / new tab context) or documented guidance on what actually un-sticks the arm would help — right now the remediation hint ("cohort flaps per page load, RETRY") doesn't hold when it's pinned for tens of minutes.

2. Agentic-mode multi-candidate ambiguity burns real attempts before failing

agentic.py's _compose_directive sends a natural-language "Make me a picture of X" (singular for count=1) rather than a hard UI count control. Google's agentic assistant can ignore this and generate multiple candidates; await_images' ambiguity guard (issue #281 already referenced in the code) correctly refuses to guess and raises MediaAttributionError — but only after the generation already ran. Confirmed live: two consecutive attempts under --ui-mode auto both hit this path with zero usable output. (Image gen itself is free/credit-less on this account tier, so the cost here is wall-clock time, not money — but that may not be universal.) Would be good if agentic mode's count enforcement had a harder guarantee, or if await_images could download-and-let-caller-pick instead of hard-failing when it's unambiguous which is "first"/newest.

3. "Poisoned" character entity smuggles referenceEntities into unrelated calls

Repro: gflow character create --body-prompt ... where the body-triptych step 400s (WireFormatError) still persists the entity (face image done, persist-before-spend saga worked as designed — good). But afterward, plain gflow image i2i --ref <path-or-uuid> calls in the same project — never passing --reference-entity — started including referenceEntities: [{entityId: <broken-entity>}] in the batchGenerateImages request body, which then 400s. Confirmed via the ui_automation.batch_request_body debug log's summary.reference_fields. gflow character rm --id <id> -y immediately fixed it (mentions_reference_entities back to false). Something in local media-tracking is associating a plain uploaded/selected image with an unrelated character entity in the same project — worth tracking down since it's a silent, hard-to-diagnose failure mode for anyone who's ever run character create with a body-prompt that failed.

4. Systemic duplicate uploads — media_refs.py's UUID-avoidance could be replaced by picker name-search

Not a gflow-cli bug per se (this monorepo's own media_refs.py deliberately avoids UUID refs for canonicals + multi-ref scenes, citing two 2026-07-10 findings: a fresh canonical's UUID can be unpickable, and the picker can only select the first UUID ref in a multi-ref call). Owner visually confirmed 8+ duplicate son.jpg uploads in one project's media library as a result. Flow's own picker UI has a search-by-filename field (type "son.jpg" → filtered results sorted "Most Used" → "Add to Prompt") that gflow-cli's automation doesn't use at all today — it only ever fresh-uploads or attempts raw-UUID picker selection. A gflow image i2i --ref-search "<name>" (search picker by name, select top/oldest match, click "Add to Prompt") could dedupe reliably without gflow needing to persist/trust a UUID across runs — worth prototyping in both classic and agentic mode (agentic's chat surface may have no picker-search equivalent at all, needs recon).

5. Zero click/interaction humanization

Every .click()/.fill()/keyboard.insert_text() in ui_automation.py is followed by a hardcoded wait_for_timeout(N) — same literal ms every call site, never randomized. Prompt entry is one atomic insert_text() paste of the whole prompt (a deliberate reliability choice per the code comment — insert_text avoids Slate's fill() incompatibility — but the least human-like way to enter text). No playwright-stealth, no webdriver-property patching beyond --disable-blink-features=AutomationControlled, no mouse-movement paths anywhere. Bonus: _persistent_context_kwargs() hardcodes viewport 1280x720 and locale en-US identically for every launch on every machine — a fixed cross-install fingerprint independent of click timing. Given how much of this issue is plausibly bot-detection-driven (cohort pinning, an "Action required" account-verification flag observed mid-session), jittering the existing timeouts and varying viewport/locale seem like a cheap first experiment.

6. No HAR capture, no traceback-unlock — and the hash-only errors are deliberate, not a bug

Grepped the whole package: no record_har, no context.tracing, no --har/--trace flag anywhere. --verbose only surfaces the structured info-level event stream (ui_automation.* events) — it does not unlock full exception detail. observability.py's emit_unhandled_event() is intentionally hash-only (message_hash/stack_hash, never raw message/traceback) with an explicit privacy-safety docstring about frame locals potentially containing signed URLs/tokens. The CLI's own stdout hint ("Re-run with --verbose to capture details") is misleading for this error class — there is no flag that does what it implies. Two independent, non-conflicting additions would help: (a) a --har <path> flag wired to Playwright's built-in record_har_path context option (the kwargs-builder is already documented as an "overridable seam" for exactly this) — no new dependency; (b) a GFLOW_CLI_DEBUG_TRACEBACK=1 env var that bypasses the redaction with a loud "may leak tokens, don't share this log" warning, for local debugging only.

7. Camoufox — feasible but not a drop-in swap

Browser engine is fully hardcoded at the actual launch call site (self._pw.chromium.launch_persistent_context(...) in client.py) — no engine-selection abstraction exists to plug into (the kwargs-builder "seam" only affects launch options, not engine choice). Bigger blocker: the cookie/session machinery (_preread_flow_session_cookies, Chrome cookie-DB decryption) is Chrome-cookie-format-specific end to end. A Camoufox (Firefox-based) path would need its own fresh interactive login building a new Firefox-format profile from scratch — none of the existing profile_<name> Chrome directories would carry over. Real but contained scope: new engine branch + new auth flow for that engine, evaluated independently before any reliability comparison is meaningful.

8. gflow image batch looks underused for reliability, but adding refs is genuinely harder than "add a field"

gflow image batch mounts one Flow project/editor session for up to 5 prompts ("stay-mounted editor") instead of relaunching per-prompt — this alone would cut the number of "roll the cohort-lock dice" attempts roughly 5x for any multi-scene job (we ran 11 separate t2i/i2i invocations for s007's stills, each independently re-navigating/re-binding UI mode). But BatchPromptItem (image_batch.py) has no ref/reference_entity field at all — batch is t2i-only, unusable for our i2i character-consistency scenes as-is.

Adding --ref support to batch is more than a schema field, though: a batch's later rows may want to reference an image generated by an earlier row in that same batch (e.g. scene 2 referencing scene 1's freshly-generated still), not just a pre-existing canonical. That means the batch executor needs intra-batch dependency ordering (row N can't resolve ref: "row:2" until row 2's media UUID exists), not just "attach a fixed ref to every row independently." Worth scoping as its own design question rather than a quick addition — happy to help think through the ordering/dependency model against our real multi-scene manifests if useful.


Happy to help prototype any of these (picker name-search resolution in particular) against our real production scenes if useful test cases.

Activity

  1. ffroliva commented on Jul 15, 2026

    @ffroliva
    OwnerAuthor
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