Skip to content

Stop Content Optimizer spinning forever on a failed competitor analysis - #28

Merged
ThinkingSpade merged 1 commit into
mainfrom
fix/content-optimizer-failed-analyses
Jul 31, 2026
Merged

Stop Content Optimizer spinning forever on a failed competitor analysis#28
ThinkingSpade merged 1 commit into
mainfrom
fix/content-optimizer-failed-analyses

Conversation

@ThinkingSpade

Copy link
Copy Markdown
Owner

Closes the last defect from the Lane 4 audit.

Two halves of one bug

Both because a failed per-URL request is indistinguishable from a pending
one when you only look at data.

The cells spun forever. They rendered loading dots whenever
competitorsAuthorized && analysis === undefined. A rejected query also has
data === undefined, so those cells never stopped — the page never settled, and
nothing said why.

The headline cards were worse. analysesPending is
analysisQueries.some(q => q.isLoading), which goes false when requests
settle — successfully or not. So once every analysis had failed, the target
cards read:

No data

That claims the top-ranking pages have nothing to measure. The truth was that we
couldn't measure them. On a paid feature, that's a wrong answer presented as a
finding — someone could size an article against it.

Now

  • Cells say "failed" with a tooltip instead of spinning.
  • Cards say "Could not analyze the top pages" instead of "No data".

Failures are tracked in their own Set/count rather than inferred from missing
data, so the three states — pending, failed, absent — stay distinct.

Worth noting

The surrounding code was already careful about the money: retry: 0 with a
comment explaining that react-query's retry would double the billed invocations.
The gap was reporting the failure, not causing it.

Verification

pnpm ci:check clean, 2,122 tests passing.

Not browser-verified: reproducing it needs a paid brief where a competitor
page analysis rejects.

🤖 Generated with Claude Code

Two halves of one defect, both because a FAILED per-URL request is
indistinguishable from a pending one when you only look at `data`.

The table cells rendered loading dots whenever
`competitorsAuthorized && analysis === undefined`. A rejected query also has
`data === undefined`, so those cells spun FOREVER — the page never settled,
and nothing said why.

The headline cards were worse. `analysesPending` is
`analysisQueries.some(q => q.isLoading)`, which goes false when requests
SETTLE, successfully or not. So once every analysis had failed, the target
cards read "No data" — which claims the top-ranking pages have nothing to
measure, when the truth is we could not measure them. On a paid feature,
that is a wrong answer presented as a finding.

Failures are now tracked separately: cells say "failed" with a tooltip
instead of spinning, and the cards say "Could not analyze the top pages"
instead of "No data".

Worth noting the surrounding code was already careful about the money here
— `retry: 0` with a comment explaining that react-query's retry would
double the billed invocations. The gap was reporting the failure, not
causing it.

ci:check clean, 2,122 tests passing. Not browser-verified: reproducing it
needs a paid brief where a competitor page analysis rejects.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
flyrocketseo a33591b Jul 31 2026, 06:23 AM

@ThinkingSpade
ThinkingSpade merged commit dbc5902 into main Jul 31, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant