THE ROOT CAUSE of the djsim_phonewall run, and it was never Flow.
promptEchoed() answered "has this prompt already been submitted?" by searching
the chat pane for prompt.slice(0, 60). All 15 calendar prompts open with the
same 60 characters:
"The illustrated top picture-panel of a cheap 1979 promotiona"
So once Alien's prompt was echoed in the chat, every subsequent calendar looked
already-submitted. The driver logged "Prompt already echoed in the session —
not re-typing", typed nothing, and waited out the full 3-minute image timeout.
Thirteen times, at a metronomic 3m30s apart. Flow was working the whole time.
No fixed slice is safe: djsim_phonewall needs 160 chars of PREFIX to
disambiguate its prompts; djsim_batch3 needs 190 chars of SUFFIX. So match the
whole normalized prompt, or (if the DOM mangles the middle) require both ends
in the same element. Whitespace-normalized, with the container guard kept.
test_prompt_echo.mjs pins it: asserts the old impl DOES false-positive, the new
one doesn't, that it still fires as the "it stuck" signal, survives DOM
whitespace reflow, ignores a page-sized container, and that every prompt in
every bank is distinguishable from all its neighbours (0 missed, 0 cross-matched).
The circuit breaker from the previous commit stands: it would have stopped this
after 3 instead of 13. But the driver should never have been silent in the
first place.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two bugs, both found by autopsy of the djsim_phonewall run (2026-07-08):
15 calendar prompts queued, 2 generated, then THIRTEEN consecutive
"Timed out — no new asset" at a metronomic 3m30s apart. 45 minutes of the
driver politely feeding prompts into a Flow that had stopped generating.
1. NO CIRCUIT BREAKER. Each timeout threw, was reported failed, and the
loop moved straight on. When Flow goes quiet it does not spontaneously
start again. Now: 3 consecutive failures -> clear isRunning, log a loud
system message, leave the queue intact for a hand-resume. (The abuse-flag
path already had consecutiveRateLimits; failures had nothing.)
2. done_ids() COUNTED 'ratelimited' AS DONE. handleRateLimit() writes that
status with the explicit comment "report the task back as pending" — and
the server then binned the task forever. Now excluded. 'failed' is still
counted: a timeout may have burned credits invisibly, so nothing retries
automatically.
requeue.py: the deliberate way to retry. --list / --dry / by id or prefix /
--failed. Writes a .bak, never re-runs a success. Used it to make the 13
cal79 prompts pending again.
Note for whoever reads this next: the LIVE server + queue live in
~/Library/Application Support/flowrinse/, not this repo. HERE is derived
from the script's own dir, so running the repo copy silently serves the
stale Jul-6 queue. Copy the script over, run it from there.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reported symptom: harvest opens the More menu then moves on without ever hovering
Download. Cause: the size flyout's text can glue onto the Download row (textContent
"Download1K Original size2K Upscaled..."), so the old /^download\b/ match — which needs a
word boundary right after "download" — returned null and the whole size step was skipped.
Fix: findDownloadRow() matches "download" anywhere and takes the SHORTEST visible
candidate (the row itself, not a wrapper that swallowed the flyout). Stage-3 hover now
moves in two steps (approach from the left, then settle on centre) to fire a real
mouseenter that unfurls the submenu, and re-queries. Added per-stage logging (Download
row text+coords, chosen size option) so a copied log shows exactly where any future miss
happens. Popup gains a Copy button (whole activity log to clipboard) and log retention is
raised 100 -> 300 so a full harvest pass is capturable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adversarial review (21 agents, verify layer refuted the false positives) confirmed 4:
CRITICAL — double-generation on requeue (content.js): submitEditor commits 15 credits,
but if the visual stuck-signals lag under load the task requeue+reloads and re-submits
(promptEchoed unreliable across reload) → up to 4×15=60 credits for one task. Fix:
durable per-task 'submitted_<id>' marker persisted BEFORE the stuck-check, checked on
entry, cleared only on terminal status (centralized in reportCompletion). A rare
genuine submit-failure now times out and fails (retryable) instead of paying twice.
HIGH — transient harvest miss permanently blacklisted the asset: every miss path did
seen.add()+persist, so a hover/menu race wrote a never-downloaded tile to storage and
the seen-guard skipped it forever. Fix: transient misses go to an in-memory
missedThisPass set (retried next run); ONLY confirmed downloads are persisted.
MEDIUM — Try-again re-ran succeeded gens: a spurious 'Something went wrong' banner
co-occurring with a lagging-but-real asset auto-clicked Try-again (15cr each, ×3).
Fix: settle + re-check assets AND new completion-message before clicking; cap 3→1.
MEDIUM — seen-list slice(-3000) evicted oldest done URLs on big libraries → re-download.
Fix: cap raised to 20000 (and only confirmed downloads accumulate now).
+ belt-and-suspenders: findTileMenuButton fallback can never land on Animate/Like/♥/redo
(a stray Animate click = paid image→video gen). Proven by unit test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Video download flyout differs from images: 270p GIF / 720p Original Size / 1080p
Upscaled / 4K Upscaled·50 credits. Old finder matched '1k' (image-only) and would
have missed video originals. New findSizeOption() keys on 'original size' (common
to both 1K and 720p rows) and hard-rejects any 'upscal'/'credit' row — the 4K video
upscale costs 50 credits, so a stray click there spends real money. Proven against
all flyout string variants (spaced + newline-collapsed).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- DL Mode button: one click harvests everything on screen (click ⋮ -> hover
Download -> click 1K, verified per tile), click again to stop after current
tile; no queue involved, no server reporting for manual runs
- Prompt Mode = the existing queue runner, relabelled
- harvestActive synced to storage so the popup button reflects state
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- countErrMsgs baseline: lingering 'Something went wrong' chat history no longer
fails every task (the 20:33-21:01 massacre — gens succeeded, all marked failed)
- Try-again only clicked when a NEW error appears (old buttons re-ran old prompts)
- kind:'harvest' task = automated John-ritual per tile: trusted CDP hover -> ⋮ ->
Download -> 1K; background routes downloads to flowrinse/harvest/; seen-list in
storage makes it resumable; paced + capped at 500/pass
- queued harvest_all_001 to recover tonight's generated-but-not-downloaded batch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- snapshotAssets also keys on /project/…/edit/<uuid> tile anchors: stable https,
survive reloads (video blob srcs don't) — the reliable new-asset signal
- harvestFresh splits diff into direct media (downloaded) vs tile links (reported
as assets when no media url — credits not lost, recoverable from the library)
- gridCensus appended to every miss so failures describe the actual harvest surface
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- countDoneMsgs baseline before submit: lingering 'has been scheduled' chatter from
the PREVIOUS gen no longer triggers an instant post-gen reload (the self-reload bug)
- promptEchoed: chat echoing the prompt = strongest it-stuck signal, added to the
post-Enter check; and on retry it means the earlier submit registered -> skip
re-typing entirely (no double generation / double credits)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- video path: wait for done signal (grid asset OR agent completion message incl.
'scheduled/waiting in the queue' which usually means DONE), settle 2s, persist
harvest state, hard refresh — never re-submit (double-bills credits)
- runPostReloadHarvest: on load with pendingHarvest, diff fresh grid vs persisted
pre-submit snapshot (https urls only — blobs don't survive reloads), download,
report; up to 2 extra refreshes if the asset hasn't surfaced; 30-min staleness cap
- fixes the 'Timed out — no new asset' failure class in results.jsonl
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- waitForAgentReady: after (re)load, wait for the sidebar to hydrate before typing
- post-submit success check = square stop button (or emptied box); no square after
Enter + submit fallback -> hard refresh (bypassCache via background) and requeue
- 'Something went wrong' surviving Try-again -> hard refresh + requeue (was: fail)
- requeue attempts capped at 3 per task (chrome.storage, survives reloads)
- server: 'requeue' status keeps task pending; INFLIGHT entries expire after 15 min
so a reloaded tab gets its task re-served
- banks/djsim_batch3.jsonl: 18-task 90sDJsim asset bank (queued live)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flow throws "Something went wrong. Please try again" constantly under load; the
driver treated it as terminal (task FAILED, moves on). Now it clicks the Try
again button — same prompt re-runs — up to 3x with a 15s cooldown so a lingering
banner doesn't burn all retries. Gives up only after 3 shots or an error banner
with no retry button.
queue.jsonl: retarget the 2 unsupported aspect ratios (16:5, 21:9) to 16:9 —
Nano Banana 2 only supports 16:9/1:1/3:4/4:3/9:16, otherwise the agent asks a
clarifying question and produces no asset (driver times out).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DOM automation for Google Labs Flow — background worker injects trusted input
via the DevTools Protocol (Flow's editor ignores synthetic events), polls a local
queue, harvests generated asset URLs. Includes the generated game+promo prompt bank.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>