Skip to content

Stop failed and never-run queries claiming to be answers - #35

Open
ThinkingSpade wants to merge 1 commit into
mainfrom
fix/false-absence-states
Open

Stop failed and never-run queries claiming to be answers#35
ThinkingSpade wants to merge 1 commit into
mainfrom
fix/false-absence-states

Conversation

@ThinkingSpade

Copy link
Copy Markdown
Owner

Stop failed and never-run queries claiming to be answers

Four places where a query that FAILED, or never ran at all, was rendered as a
confident statement about the world. Found by a Codex audit of the two surfaces
reported as "fields not filled out, no error"; each is verified against the
source rather than taken on trust.

The rule applied throughout is the one resolveQueryState already encodes:
failure outranks emptiness, because a failed query has no rows because it
failed
. These call sites simply predated it.

  1. LINK GAP -- the worst of them (CompetitorsTabBody.tsx)

    rows={linkGapQuery.data?.rows ?? []} fed a table whose zero-row message is:

    "No link gap found -- every domain linking to this competitor also links
    to you."

    That is a strong, flattering claim about the user's backlink profile, and a
    comparison they never paid for could make it. Same shape for Keyword Gap's
    "No keywords found for this comparison".

  2. COMPETITOR DISCOVERY (CompetitorsTabBody.tsx / CompetitorsPage.tsx)

    competitorsQuery.data?.rows ?? restored?.result.rows ?? [] collapsed
    failure, never-run and genuine-zero into one empty array, which the table
    reported as:

    "No competitors found. Try a domain with more organic visibility."

    -- a claim about the user's site when in fact we never got an answer. Codex
    identified the trigger: hard-refreshing a URL containing target.
    Authorization is mount-session state so it resets to false, while
    auto-restore is only enabled when target is EMPTY, so neither the live
    query nor the stored run fires.

    The caller now passes what those rows MEAN (isError / isFetching /
    hasResult), because the array alone cannot say. A restored past run counts
    as a real answer.

  3. KEYWORD GAP TOTALS (KeywordGapOverview.tsx)

    Only the active mode is fetched -- correct, the other two are separately
    metered and must not auto-run. But a disabled react-query stays isPending
    forever, so the other two cards sat on loading dots indefinitely, reading as
    "still working" rather than "not run". Dots now mean isFetching; otherwise
    the card says "Select to run".

  4. PAGE EXPLORER BACKLINKS (PageExplorerService.ts, page-explorer.ts,
    PageExplorerResults.tsx)

    The backlink lookup is a separate best-effort subcall: on failure it is
    caught, logged, backlinks stays null, and the parent result still
    succeeds. Right call -- a Backlinks API hiccup should not sink the keyword
    view -- but a FAILED PAID SUBCALL and a page that genuinely has no backlink
    data both rendered as two dashes with nothing to tell them apart. This is
    the most likely thing behind the original "Backlinks and Ref. domains show
    dashes" report.

    Adds backlinksStatus: "available" | "no-data" | "error". The
    .default("no-data") is load-bearing: a required field would fail
    safeParse against every already-cached payload, and auto-restore drops a
    failed parse SILENTLY, so old runs would quietly stop restoring.

DELIBERATELY NOT "FIXED"

Several blanks are correct under the no-auto-spend rule and only needed honest
wording, never a fetch: Keyword Gap's inactive modes, Link Gap not auto-running
from URL state, and the restored Page Explorer snapshot. Nothing here starts a
metered request that did not start before -- every change is presentational or
adds a status field.

STILL OUTSTANDING from the same audit

  • useAutoRestoredRun callers ignore its isError, and a schema-mismatch
    parse failure returns null with no error state at all, so a run you performed
    can vanish silently.
  • PageExplorerPage.tsx:129 -- snapshotQuery.data ?? null, isError never
    read, so a failed on-page fetch removes the whole card.
  • RecentRunsList.tsx:31 -- query.data ?? [], and selection failures are
    swallowed by .catch(() => undefined).

VERIFICATION

pnpm ci:check clean; 2,129 tests passing across 224 files; tsc clean. Not yet
exercised in a browser against live failing queries -- the states are reachable
by construction but the wording has not been seen on screen.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Four places where a query that FAILED, or never ran at all, was rendered as a
confident statement about the world. Found by a Codex audit of the two surfaces
reported as "fields not filled out, no error"; each is verified against the
source rather than taken on trust.

The rule applied throughout is the one `resolveQueryState` already encodes:
failure outranks emptiness, because a failed query has no rows *because it
failed*. These call sites simply predated it.

1. LINK GAP -- the worst of them (CompetitorsTabBody.tsx)

   `rows={linkGapQuery.data?.rows ?? []}` fed a table whose zero-row message is:

     "No link gap found -- every domain linking to this competitor also links
      to you."

   That is a strong, flattering claim about the user's backlink profile, and a
   comparison they never paid for could make it. Same shape for Keyword Gap's
   "No keywords found for this comparison".

2. COMPETITOR DISCOVERY (CompetitorsTabBody.tsx / CompetitorsPage.tsx)

   `competitorsQuery.data?.rows ?? restored?.result.rows ?? []` collapsed
   failure, never-run and genuine-zero into one empty array, which the table
   reported as:

     "No competitors found. Try a domain with more organic visibility."

   -- a claim about the user's site when in fact we never got an answer. Codex
   identified the trigger: hard-refreshing a URL containing `target`.
   Authorization is mount-session state so it resets to false, while
   auto-restore is only enabled when `target` is EMPTY, so neither the live
   query nor the stored run fires.

   The caller now passes what those rows MEAN (`isError` / `isFetching` /
   `hasResult`), because the array alone cannot say. A restored past run counts
   as a real answer.

3. KEYWORD GAP TOTALS (KeywordGapOverview.tsx)

   Only the active mode is fetched -- correct, the other two are separately
   metered and must not auto-run. But a disabled react-query stays `isPending`
   forever, so the other two cards sat on loading dots indefinitely, reading as
   "still working" rather than "not run". Dots now mean `isFetching`; otherwise
   the card says "Select to run".

4. PAGE EXPLORER BACKLINKS (PageExplorerService.ts, page-explorer.ts,
   PageExplorerResults.tsx)

   The backlink lookup is a separate best-effort subcall: on failure it is
   caught, logged, `backlinks` stays null, and the parent result still
   succeeds. Right call -- a Backlinks API hiccup should not sink the keyword
   view -- but a FAILED PAID SUBCALL and a page that genuinely has no backlink
   data both rendered as two dashes with nothing to tell them apart. This is
   the most likely thing behind the original "Backlinks and Ref. domains show
   dashes" report.

   Adds `backlinksStatus: "available" | "no-data" | "error"`. The
   `.default("no-data")` is load-bearing: a required field would fail
   `safeParse` against every already-cached payload, and auto-restore drops a
   failed parse SILENTLY, so old runs would quietly stop restoring.

DELIBERATELY NOT "FIXED"

Several blanks are correct under the no-auto-spend rule and only needed honest
wording, never a fetch: Keyword Gap's inactive modes, Link Gap not auto-running
from URL state, and the restored Page Explorer snapshot. Nothing here starts a
metered request that did not start before -- every change is presentational or
adds a status field.

STILL OUTSTANDING from the same audit

- `useAutoRestoredRun` callers ignore its `isError`, and a schema-mismatch
  parse failure returns null with no error state at all, so a run you performed
  can vanish silently.
- `PageExplorerPage.tsx:129` -- `snapshotQuery.data ?? null`, `isError` never
  read, so a failed on-page fetch removes the whole card.
- `RecentRunsList.tsx:31` -- `query.data ?? []`, and selection failures are
  swallowed by `.catch(() => undefined)`.

VERIFICATION

pnpm ci:check clean; 2,129 tests passing across 224 files; tsc clean. Not yet
exercised in a browser against live failing queries -- the states are reachable
by construction but the wording has not been seen on screen.

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

cloudflare-workers-and-pages Bot commented Aug 1, 2026

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 31547b7 Aug 01 2026, 06:30 AM

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