Show which documents finished indexing and produced nothing usable - #2504
Conversation
…ced nothing usable
The hub's ingestion panel shows documents *moving* through the queue. Nothing
showed the library at rest, which is exactly where a document that finished and
produced nothing usable hides: it is not queued, it is not an error, and its row
says `indexed`.
`corpus-health.ts` reads two owner-scoped tables through the cookie-bound user
client -- `public.documents` and `public.document_index_quality`. Row-level
security (`documents owner read`, `index quality owner read`, each
`owner_id = auth.uid()`) is what does the scoping, so `createAdminClient` would
silently turn one account's library into every account's. No behavioural test can
catch that substitution, because the mocks answer either client identically, so
the module carries the same source assertion `environment-facts.ts` uses.
Every failure returns `null`, never `0`, and each read is guarded on its own. On
this panel `0` is the reassuring answer -- "nothing failed", "nothing is
unsearchable" -- so a read that did not happen must not be able to impersonate
it. The per-read guard is because the client *rejects* rather than resolving with
an `{ error }` when a request is aborted or exhausts its retries, and an
unhandled rejection would fail the whole page instead of degrading one line of
it, during exactly the outage that makes the page worth opening. An unread count
renders as "Not read"; a page that read nothing says so rather than showing a
healthy-looking wall of zeroes.
What the page refuses to imply, each pinned by a test and each above the evidence
it qualifies: every figure describes what indexing *produced*, so an empty page
means indexing ran to completion and not that the answers are good; zero chunks
is a strong signal of broken extraction rather than proof nothing is reachable;
an empty list is the absence of the worst failure rather than proof of a good
index; the failures list is current state, not history; and `quality_score`
defaults to 0 while the quality table's owner column is nullable, so a low total
there has honest explanations that are not breakage.
`resolveQualitySpread` exists for one reading in particular. A score column that
defaults to 0 and a scorer that never ran produce a perfectly tidy distribution,
so the page names a corpus where every document carries the identical score
instead of rendering it as a measurement. Whether the live library is in that
state could not be checked here and is called out on the PR.
Proven by mutation, not by a passing test: eleven load-bearing rules broken on
purpose, each watched red in its own named test, each file then restored and
confirmed byte-identical by SHA-256. Two first attempts survived because they
replaced only the opening words of a caveat and left the asserted claim standing;
both were redone as real deletions and then went red.
Gate: `npm run verify:pr-local` green -- 941 test files, 11,842 tests passed,
4 skipped; build compiled successfully in 2.4min; all 19 steps completed, none
failed. An earlier run failed `tests/clinical-hazard-controls.test.ts` and
`tests/rag-plan-package-parity.test.ts` on missing git objects; the clone was
shallow, and both pass after `git fetch --deepen`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XG7wQurapeZwWRsNhHA1PY
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d2dda0f3-943f-4f4d-a452-85d725f37fc6) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4eebf126ac
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The Codex P1 is correct, and it is wider than this PRI checked it against the schema rather than taking it on trust, and it holds. Primary source. It is the current posture, not a stale mirror. Consequence, exactly as reported. All eleven reads would fail with permission denied, every guard would convert that to This is not confined to this PR. The repository's own working pattern for this read is the sibling ingestion panel: Not pushing a fix yet, deliberately. Both viable repairs are the owner's call rather than mine:
Guessing between those two would be the wrong move on this surface, so I have put the choice to the repository owner. Everything else on the PR is green: Generated by Claude Code |
…replace PLACEHOLDER)
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_366959ae-2a86-4258-a272-6cb858373b72) |
…pshot conflict favoring corpus-health snapshot)
…iering branch Bundled into this PR rather than pushed as a separate ledger-only tip, matching how every recent review record landed (#2517, #2508, #2504). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ou4w8atP9kDKHVAXUvYPe
Summary
src/lib/developer-area/corpus-health.tsreads two owner-scoped tables through the cookie-bound user client:public.documents(status, chunk/page/image counts, error message) andpublic.document_index_quality(score, extraction label, issues). Row-level security (documents owner read,index quality owner read, eachowner_id = auth.uid()) does the scoping, so the module never sees another owner's library.indexedwith zero chunks; failures with the reason the worker recorded; the extraction-quality distribution; and the lowest-scoring documents with their recorded issues.hub-panels.ts(groupsystem, phase 1).docs/site-map.mdanddata/repo-awareness-snapshot.jsonregenerated.The two rules the module is built around
The user-session client, never the service-role admin client.
createAdminClientbypasses row-level security and would report every owner's library to whoever happened to be signed in. No behavioural test can catch that substitution — the mocks answer either client identically — so the module carries the same source assertionenvironment-facts.tsuses.Every failure returns
null, never0, and every read is guarded separately. On this panel0is the reassuring answer ("nothing failed", "nothing is unsearchable"), so a read that did not happen must never be able to impersonate it. The reads are guarded one at a time because the Supabase client rejects rather than resolving with an{ error }when a request is aborted or exhausts its network retries, and an unhandled rejection would fail the whole page rather than degrade one line of it — during exactly the outage that makes this page worth opening. An unread count renders as the words "Not read", and a page that read nothing says so instead of showing a healthy-looking wall of zeroes.What the page refuses to imply
A panel on a clinical system that implies coverage it does not have is worse than no panel, so each caveat is page content, sits above the evidence it qualifies, and is pinned by a test:
status = indexedandchunk_count = 0. Other index units and page images may still exist for those documents, so it is a strong signal of a broken extraction rather than proof that nothing about the document is reachable.quality_scoredefaults to0andextraction_qualitytounknown, so a row carrying those values may never have been scored rather than having scored badly.document_index_quality.owner_idis nullable while the read policy matches on it, so a row written without an owner is invisible here — an honest reason this total can sit below the indexed count.The uniform-score finding
I was asked to check an unverified report that every document in the library carries an identical placeholder quality score, which would make the quality half of this panel meaningless. I could not check it: a cloud container has no live database, and this repository holds no committed record of the live score distribution. So the panel is built to report that condition rather than to hide it.
resolveQualitySpreadreads both ends of the score range and distinguishes five cases — unreadable, none, a single row, every row identical, and a genuine range. When the scores are uniform the page says so in bold, states that the quality figures are not usable as a measure of any document, and adds that a uniform0is also what a corpus nobody ever scored looks like. If the live data turns out to be uniform, that is a finding to raise, not a panel to quietly ship.Verification
npm run verify:pr-local— green, all 19 steps completed, none failed:An earlier run of the same gate failed two tests, and neither was this diff:
tests/clinical-hazard-controls.test.ts(reviewedCommit does not exist 883f100…) andtests/rag-plan-package-parity.test.ts(manifest reconciledBase is unavailable locally: f3d1a3c…). The container's clone was shallow — 137 commits — so both objects were simply absent.git fetch --deepenbrought them in, both tests pass, and the run quoted above is on full history. Recorded rather than dropped, because "it was the environment" is a claim worth being able to check.npm run verify:uiwhen UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changednpm run verify:releasebefore release or handoff confidence claimsUI verification not run: the change adds one developer-hub panel route built from the existing
PanelPageShell/CountTile/PanelSectionprimitives with no change to shared chrome, tokens, or the search composer, and Chromium journeys do not cover/mockups/development/**. Its rendering is covered by a jsdom page contract test.npm run eval:retrieval:quality— not applicable; no retrieval, ranking, selection, chunking, or scoring behaviour is touched.npm run eval:rag -- --limit 15+npm run eval:quality -- --rag-only— not applicable; answer generation is untouched.npm run check:production-readiness— provider-backed, and not run. This change adds no clinical workflow, privacy, environment, Supabase, source-governance, or deployment behaviour: it is a read-only panel over two existing tables through the existing user client, and it adds no migration, no environment variable and no new access path.npm run check:deployment-readinesswhen deployment startup, hosting, or rollout behavior changedProven by mutation, not by a passing test
Each load-bearing rule was broken on purpose, the specific test watched go red, then the file restored and confirmed byte-identical by SHA-256. Eleven mutations, eleven reds, no survivors:
0instead ofnull→ "reports a missing count as unavailable rather than as zero" red.Risk and rollout
DeveloperAreaGate, plus one card on the hub. No existing surface changes behaviour. The realistic failure is a wrong or over-confident number on the panel, which the null-not-zero rule, the per-read guards and the wording tests are built to prevent.src/lib/rag/**, retrieval-selection, ranking, the eval harness or the golden fixtures is touched; this is a read-only panel over thedocumentsanddocument_index_qualitytables.Clinical Governance Preflight
Clinical KB Database(sjrfecxgysukkwxsowpy)Evidence for each, in order:
createSupabaseServerClientand carriesimport "server-only". A source assertion test fails ifsupabase/adminever appears in it, by import, dynamic import or re-export — this is the rule mutation 1 above proves is enforced.createSupabaseServerClientreturns null, no read is attempted, and the page renders the explicit "Nothing was read" notice naming the demo corpus as one of the two causes. It never renders synthetic figures as though they were the real library.Notes
ReadingTilehelper (a count tile that can say "Not read") is deliberately local to the page rather than added topanel-primitives: this is the only panel whose numbers come from a live database read and can therefore be missing. It should move there the day a second page needs it.head: truerather than by counting fetched rows, because PostgREST caps the rows it returns and a truncated fetch would under-report breakage — the one direction a panel about breakage must not fail in. A test pins that.🤖 Generated with Claude Code
https://claude.ai/code/session_01XG7wQurapeZwWRsNhHA1PY
Generated by Claude Code
Note
Medium Risk
New administrator-gated reads bypass RLS via the service role, so correctness depends on every query applying the verified
owner_idfilter; tests and source assertions target that, but a regression could expose cross-owner data.Overview
Adds a Corpus health developer-hub panel at
/mockups/development/corpus-healthplus a hub card, wired throughresolveCorpusHealth/resolveQualitySpreadincorpus-health.ts.Data access: Session client only resolves a signed-in administrator; table reads use the service-role admin client with
.eq("owner_id", …)on every query (authenticatedSELECTondocuments/document_index_qualityis not available). Failed or partial reads surface asnull/ “Not read”, not0, with per-query guards so one outage does not take down the page. Totals use Postgreshead: trueexact counts; lists are capped with explicit truncation copy.UI: Live status tiles, “finished but unsearchable” (
indexed+ zero chunks), failed jobs with error text, extraction-quality buckets, lowest-scoring samples, and copy that calls out uniform/placeholder quality scores and what the panel does not measure (answer/retrieval quality).Tests & docs: Module + page DOM contracts (owner filter, non-admin issues no queries, null-not-zero, uniform-score messaging); repo-awareness snapshot, site map, codebase index, and a branch review ledger row updated.
Reviewed by Cursor Bugbot for commit 3b1dc7c. Configure here.