Skip to content

Reconcile search_schema_health index drift on live Supabase - #266

Closed
BigSimmo wants to merge 2 commits into
mainfrom
cursor/reconcile-search-health-indexes-5f13
Closed

Reconcile search_schema_health index drift on live Supabase#266
BigSimmo wants to merge 2 commits into
mainfrom
cursor/reconcile-search-health-indexes-5f13

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes live search_schema_health() failure (ok: false, 10 missing indexes) discovered during database review.

  • Adds migration 20260705180000_reconcile_search_health_indexes.sql that creates canonical retrieval-support indexes missing on live (trgm, composite btree, partial miss-log indexes).
  • Updates search_schema_health() to accept verified functional equivalents (e.g. documents_title_search_tsv_idx for documents_title_trgm_idx) so health checks do not false-fail mid-rollout.
  • Sets search_path = public, extensions, pg_catalog in the migration so gin_trgm_ops resolves when pg_trgm lives in the extensions schema.
  • Reconciles supabase/schema.sql and adds regression coverage in tests/supabase-schema.test.ts.

Live verification (already applied)

npx supabase db push --linked --yes
select public.search_schema_health();
→ ok: true, missing: []

Checks run

  • npx vitest run tests/supabase-schema.test.ts — 37/37 passed
  • Live search_schema_health()ok: true after migration apply

Operator notes

  • Migration was applied to linked project sjrfecxgysukkwxsowpy during this change; other environments should run npx supabase db push --linked (or normal deploy migration workflow).
  • First apply attempt failed without set search_path (gin_trgm_ops not found); fixed before successful apply.
Open in Web Open in Cursor 

BigSimmo added 2 commits July 5, 2026 20:02
- Degrade invalid bearer tokens to anonymous scope instead of 401
- Allow public document read routes (list, detail, signed-url, search, images)
- Align registry routes with medications/differentials auth-signal short-circuit
- Let DocumentViewer load public sources without requiring sign-in
- Add regression tests and update access-control expectations
Create missing retrieval-support indexes (trgm, composite btree, partial
miss log) that were absent or only present under legacy names on live.
Update search_schema_health() to accept verified functional equivalents
during rollout. Set search_path for pg_trgm gin_trgm_ops in extensions.

Verified on linked project: search_schema_health() ok=true, missing=[].
@supabase

supabase Bot commented Jul 5, 2026

Copy link
Copy Markdown

Updates to Preview Branch (cursor/reconcile-search-health-indexes-5f13) ↗︎

Deployments Status Updated
Database Sun, 05 Jul 2026 12:13:39 UTC
Services Sun, 05 Jul 2026 12:13:39 UTC
APIs Sun, 05 Jul 2026 12:13:39 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Sun, 05 Jul 2026 12:13:50 UTC
Migrations Sun, 05 Jul 2026 12:14:34 UTC
Seeding Sun, 05 Jul 2026 12:14:38 UTC
Edge Functions Sun, 05 Jul 2026 12:14:39 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@BigSimmo

BigSimmo commented Jul 5, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #269 (consolidated platform fixes branch).

@BigSimmo BigSimmo closed this Jul 5, 2026
@BigSimmo
BigSimmo deleted the cursor/reconcile-search-health-indexes-5f13 branch July 8, 2026 16:26
BigSimmo added a commit that referenced this pull request Aug 8, 2026
…sured (#1719)

* docs(issues): correct the re-measured A3 scope and B1 adoption count

Replays a correction stranded in a local-only worktree when PR #1712 squash-merged
and its branch was deleted before the commit could be pushed. Both figures were
re-measured today against origin/main 2675e6e rather than copied forward, and one
claim in the stranded version turned out to be wrong.

#262: legacyShadowAliases counts SEVEN tokens, not one and not eight — the regex has
matched tight|card|soft|hover|elevated|lux|lift since PR #1616 and has never included
focus. Measured total 228 = tight 100, soft 72, elevated 17, hover 17, card 12, lux 8,
lift 2. So --shadow-tight is 100 production sites across 55 files rather than the 155
or 229 previously recorded, clearing all 100 will not zero the ratchet, and #261's
--shadow-focus does not appear in this metric at all.

#266: adoption is 23 unadopted, not 24, since Button moved via #263 — its sole
production importer is src/components/AccessibleTable.tsx.

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

* docs(design-system): mark the TextField/SearchField/Select row done, it was stale

Replays a correction stranded in a local-only worktree when PR #1712 squash-merged
and its branch was deleted before the commit could be pushed.

Re-measured today against origin/main 2675e6e: all three fold onto FormField, which
renders hint AND error unconditionally (form-field.tsx:131-132) and merges
aria-describedby caller -> hint -> error (form-field.tsx:107-109); TextField and
SearchField each take an external id and a Ref<HTMLInputElement>, Select an external id
and a Ref<HTMLSelectElement>. Landed in PR 13, not the PR 7 the row pointed at.

Section 4's 'Do: fold TextField/SearchField/Select/Checkbox/RadioGroup onto this shell
(PR 7)' was stale in a second way and is corrected too: Checkbox and RadioGroup
deliberately do NOT fold onto FormField — a group keeps fieldset/legend and composes
FieldHint/FieldError directly (choice.tsx:207-210). Left as written, that line asks a
future session to 'finish' a fold that is finished and to break one that was a
deliberate decision.

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

* docs: correct future-dated re-measurement records to 2026-08-08

Codex review on PR #1719 flagged SCOPE/COUNT RE-MEASURED and COMPONENTS.md
rows dated 2026-08-09 while the recording commits were authored on 2026-08-08.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>

* docs(design-system): separate FormField integration from product adoption, re-measure the ui-primitives row

Two review findings on PR #1719, both valid.

Section 4 said 'Select/choice controls remain separate adoption work', which
contradicted the section above it and was wrong on both axes for Select:
select.tsx consumes FormField (folded in PR 13), and adoption-manifest.json gives
Select 2 production importers, so it is adopted. The genuinely unadopted member of
the trio is SearchField, at zero, which the sentence did not mention. Rewritten to
state the two axes explicitly — integration is complete for TextField, SearchField
and Select, and deliberately declined for Checkbox/RadioGroup; adoption is measured
TextField 3, Select 2, SearchField/Checkbox/RadioGroup 0 — so a control cannot be
read as pending on one axis because it is pending on the other.

The ui-primitives row carried three stale figures. Re-measured on this head: the
module is 698 lines, not 686; 157 production files import it, not 200 — that
figure is 202 including mockups, the same mockup-inclusive mislabelling this PR
already corrects for the shadow aliases; and 30 of the 53 registered components
are adopted, not 27.

Future-dated 2026-08-09 records were already corrected to 2026-08-08 by f3a91c6
before this commit; verified none remain under docs/.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
BigSimmo pushed a commit that referenced this pull request Aug 12, 2026
…system rows

Archived as verified-delivered or duplicate: #303, #284 (dup of #296), #149,
#159, #286, #287, #288, #256, #261, #291.
Re-scoped with re-measured evidence: #210 (gate was never missing; the defect
is the .next/dev types include), #262 (aliases 228 -> 193), #266 (counts moved,
23 unadopted holds), #275 (premise 'only one implementation' is false — the
values leaked to five files).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
BigSimmo pushed a commit that referenced this pull request Aug 12, 2026
… rows

Repairs. Two main-merges silently reverted edits from this branch, and the
re-apply script missed them because it was hand-enumerated from round 1's
second commit only:
- #293 the entire min-h-tap refutation was gone (grep sm:min-h-0 returned 0);
  the row was back to asserting a defect that does not exist. Restored from
  a6bfc6f. This was the live hazard — as reverted it directed a reader to
  strip desktop step-downs and pin every control to 48px.
- #266 restored the 2026-08-12 counts (54/31/23) over main's older 53/30/23.
- #262 deliberately NOT restored: main's version is newer than mine and
  records --shadow-tight retired outright on 2026-08-10.
- #289 archived; verified fixed in round 1 and never actually closed.

Stamps. Sixteen rows were checked during the sweep, found correct, and left
unchanged — which left no trace, so they were indistinguishable from rows
nobody had opened. Each now carries VERIFIED CORRECT 2026-08-12 plus the
measurement that settled it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
BigSimmo added a commit that referenced this pull request Aug 18, 2026
* feat(ui): one category-identity registry for card glyphs and accents

Icons and category colour were spread across ten independent maps and two of
them disagreed, so the same tool rendered differently depending on which screen
reached it.

- `launcherIconById` (applications-launcher-page.tsx) carried 13 tool ids;
  `iconByToolId` (tools-search-results-page.tsx) carried 8 with a different
  fallback, so `guidelines`, `care-plans`, `safety-plan`, `calculators` and
  `monitoring` showed a real glyph on the launcher and a generic `Grid2X2` in
  search results.
- Colour diverged the same way: the launcher tinted by tool area, the results
  page painted every tile `--type-source`, so one list read as a single purple
  family while the other grouped the same tools into five.
- `ShieldCheck` was assigned to `guidelines`, to `risk-safety`, and to the
  "Source-backed" status chip — three unrelated meanings, one glyph, reachable
  on a single card.
- `appIconTone` overrode the area map per id, routing `differentials` and
  `forms` to a tone key named `differentials`, so the advertised "category
  colour" was not a category colour.

`src/lib/category-identity.ts` is now the single source of truth. It is
framework-free (string glyph keys, no lucide) following the `semantic-tone.ts`
precedent, so data and server modules can name a glyph without the render layer;
`category-identity-icons.ts` is the only place that binds keys to components,
resolving through `createElement` as `factsheets-icons.ts` does to satisfy
`react-hooks/static-components`.

`ToolCatalogRecord.id` is narrowed from `string` to a `ToolCatalogId` union, so
`Record<ToolCatalogId, …>` cannot be under-filled: adding a tool without
choosing a glyph is now a type error rather than a silent runtime fallback.
`appModeIcons` keeps its name and shape but is derived rather than
hand-maintained, so its "keep in sync" comment is now a property of the type.

Accent delivery is `data-category-accent` → `--cat-accent`/`--cat-soft`/
`--cat-border` in globals.css rather than interpolated class names, which
Tailwind's scanner cannot see, and rather than inline styles, which bypass the
theme contract. Every accent aliases an existing non-semantic triad
(`--type-*`, `--tone-*`), so light, dark and forced-colors need no new
declarations.

`risk-safety` loses its permanent danger-red tile: red asserted caution about a
route rather than about a patient, spending the loudest colour in the system on
a navigation target. Safety is carried by the now-unique shield glyph and by the
danger-toned selected state, which is a real state.

Gates: typecheck, lint, `npm run test` (643 files, 6882 passed / 4 skipped),
check:design-system-contract, check:icon-scale, check:type-scale — all green.
No provider-backed check was run and none is required; no RAG surface touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn

* fix(factsheets): stop painting content categories in semantic colours

`categoryTheme()` drew two of its four category accents from the SEMANTIC
palette: Therapies on `--success-text`/`--success-bg` and Tests & procedures on
`--warning-text`/`--warning-bg`.

Those tokens carry meaning. `src/lib/semantic-tone.ts` defines six tones where
the colour IS the claim — warning means "pause, check, adjust, review", success
means a check passed — and `docs/clinical-badge-system-guide.md` states the rule
as "meaning drives the colour, never the other way round". Applying warning-amber
to an entire category of patient handouts asserted caution about content that
nothing had reviewed, and it did so on the largest surface the factsheet has: the
hero band. It also spent a colour the badge system needs, so a genuine caution
badge had to compete with its own page chrome.

All four categories now sit on non-semantic identity triads, sourced from
`FACTSHEET_CATEGORY_IDENTITY`:

  Medications        --clinical-accent -> --type-form
  Conditions         --tone-indigo     -> --type-source
  Therapies          --success-text    -> --type-service
  Tests & procedures --warning-text    -> --type-table

Medications moves too, for a different reason: it was the same blue as every
selection state, focus ring and evidence marker on the page, so the biggest
category was the one with no identity of its own.

`FactsheetTheme`'s shape is unchanged, so the ~20 call sites passing these as
inline style values are untouched. `FactsheetCategory` is now a re-export of the
registry's union, so the accent map and the content model cannot disagree about
what the categories are.

Three guards added to tests/design-token-contract.test.ts, each mutation-verified:
  - no `[data-category-accent]` rule may reference a semantic token
    (verified: pointing "table" at --warning-text fails the test)
  - `categoryTheme` must stay derived via `categoryAccentVars` and return no
    semantic token (verified: reinstating the old --warning-* return fails it)
  - `CategoryAccent` may not declare a semantic member, which is what makes the
    mistake unrepresentable at every call site rather than caught per site

Gates: typecheck, lint, `npm run test` (643 files, 6885 passed / 4 skipped),
check:design-system-contract — all green. No provider-backed check run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn

* refactor(ui): add the shared card recipe and absorb the two private forks

Four cards that sit side by side in the same shell had drifted on every axis:

  ToolCard        rounded-lg  --shadow-card   p-4        border + tint + hover lift
  ServiceCard     rounded-xl  --shadow-inset  p-3 sm:p-4 border + ring/35
  CalculatorCard  rounded-lg  --shadow-inset  p-4        border + --shadow-soft
  Factsheet card  rounded-xl  --shadow-card   -          inline-style border-t-[3px]

Two radii, three resting elevations, and four different "this one is selected"
encodings, three of those expressed as fractional opacity on a token colour —
which is unreviewable, because the contrast an alpha lands on depends on
whatever surface happens to sit behind it in each theme.

`src/components/card-recipes.ts` holds the shared definitions: `cardSurface`,
`cardInteractive`, `cardSelected`, `cardAccentEdge`, `cardPadding`, and one
exported `focusRing` (that string is currently redeclared as a local const in a
dozen files and inline in dozens more).

Recipes rather than a registered component: COMPONENTS.md §0.4 measures 157
production importers of ui-primitives.tsx against 31 product imports across the
whole 54-component registry, and ledger #266 says adoption is demand-driven,
"never a race to 54/54". A function also sidesteps element polymorphism — these
cards are variously <article>, <button> and <Link>. A new module rather than
growing ui-primitives.tsx, which §0.4 already lists as over-budget and slated to
split.

Two deliberate departures from what the four cards did:

- `--shadow-inset` is dropped as a card elevation. It is the design-system
  bevel, and SPEC §4.7 says an inset well uses a border or inset shading, not
  both; pairing it with a border is why the services and calculator cards read
  flatter than the tool cards beside them. Resting is border + --e1.
- The recipes name `--e1`/`--e2`/`--e3` directly rather than the `--shadow-card`
  / `--shadow-soft` / `--shadow-hover` role aliases. TOKENS.md schedules those
  for retirement "inside the recipes first", and the contract ratchet counts
  every use — a new consolidating module reaching for one would move the count
  the wrong way. Caught by the gate on the first attempt (114 -> 115); now 113,
  unchanged from baseline.

`specifierCard` and `formulationCard` were byte-identical copies of the same
string; both now name the shared recipe. This moves those two modes' cards from
`--surface` to `--surface-raised`, which is the SPEC §177 correction (true-white
cards against the near-white page) and is a visible, intended change.

Consumers for `cardInteractive`/`cardSelected`/`cardAccentEdge` land in the
following commit.

Gates: typecheck, lint, `npm run test` (643 files, 6885 passed / 4 skipped),
check:design-system-contract, check:knip — all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn

* feat(tools): elevate the tool cards and merge the phone/desktop pair

The launcher rendered two components for the same content — `ToolCard` and
`MobileToolRow` — and they had already drifted: different resting elevation
(--shadow-card vs --shadow-inset), different selected tint (/50 vs /55), and a
hover lift on one but not the other. They are now one component with a `density`
prop. Both test ids are kept: `application-card-*` and `application-row-*` are
each asserted by ui-tools and ui-smoke, and ui-smoke is blocking at zero
retries. Verified in Chromium at 390px that the row renders visible and the card
hidden, with all 13 of each still in the DOM.

Craft changes, launcher and tools search results:

- The fake button is gone. `Details` was a <span> painted as a solid accent
  button INSIDE the card's own <button>: it read as a nested control, was
  announced as nothing, was the loudest element on the card, and — being
  identical on every card — distinguished nothing. The card is the control; a
  chevron on the decoration tier says so, taking the category accent on hover so
  the affordance points at the card's own family.
- On the search results page the same button is real (that card is an <article>
  and not itself clickable), so it stays — but as `floatingControl` rather than a
  filled accent. Thirteen filled primaries down one list were thirteen primary
  actions, none of them the page's actual primary action.
- Titles move from `text-base font-extrabold` to `text-lg font-semibold`. At
  extrabold they matched the section heading above them, so a grid of cards read
  as a wall of headings. Size carries the hierarchy; weight stops trying to.
  SPEC §4.6 puts card titles at --text-lg.
- "Best for:" was a bold inline run inside the body copy, giving a label the same
  emphasis as the clinical text it labels. It is a kicker, so it uses the shared
  `eyebrowText` recipe.
- The magnifier beside "Best for" on the results page is dropped: "Best for" is
  not a search, and it spent an accent-coloured glyph on a label.
- The selected rail on a results row takes the tool's own category accent rather
  than the product blue, so it agrees with the tile beside it.
- `min-h-[9.25rem]` and `min-h-[5.25rem]` are replaced by content height with a
  `min-h-tap` floor. Production tap targets stay at min-h-12.
- The local `focusRing` const in both files now imports the shared one.

`risk-safety` keeps a danger-toned SELECTED state via the new
`cardSelectedDanger` — selection is a real state, unlike the permanent red tile
retired earlier in this branch.

`legacyShadowAliases` fell 113 -> 111 as the cards moved off --shadow-card and
--shadow-inset onto the --e ladder.

Gates: typecheck, lint, `npm run test` (643 files, 6885 passed / 4 skipped),
check:design-system-contract, check:icon-scale, check:type-scale — all green.
Chromium: check:playwright-browser-revision was failing on the known #255 drift
(1194 installed against the pinned 1234); the pinned revision was installed, the
check now reports OK, and ui-tools + ui-hydration + the ui-smoke tools
assertions were run locally against it. Five ui-tools failures were investigated
and are pre-existing: all four reproduce on origin/main with this work stashed
(two /services/13yarn composer cases, two service-detail cases, and the tools
mobile detail-sheet case), and the tools one passes when driven manually, so it
is environmental rather than a regression.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn

* feat(ui): adopt the card recipe and category accents on factsheets, calculators and services

Factsheets
- The three inline `style` objects per card are gone. An inline value cannot be
  remapped by the dark or forced-colors blocks, so the old cards carried their
  light-mode tint into both. Verified in Chromium: the four accents now remap
  correctly in dark, and under forced-colors they flatten to Canvas/CanvasText
  as they should — identity colour is decoration, and the glyph and category
  chip carry the meaning in high contrast.
- The category browse pills take the same accents, so a pill and the cards it
  filters to now agree.
- Card titles move to `text-lg font-semibold`, and hover tints the title with
  the card's own category accent rather than the product blue.

Calculators
- The directory tile was grey until a card opened, so a closed directory showed
  five domains rendered identically and the domain was findable only by reading
  the chip. It now carries the domain accent at rest — which is what the chip
  beside it has always said in words.
- `CALCULATOR_DOMAIN_ACCENT` deliberately gives `risk` (suicide risk) an
  identity accent, not `--danger`. The label already says "Suicide risk", and an
  instrument is not itself a warning; a red tile on a directory row would claim
  urgency about a tool rather than about a patient.
- Open/closed states move onto `cardSelected` and the --e ladder.

Services
- Adopts `cardSurface` + `cardSelected`, retiring the fourth "this one is
  selected" encoding (`ring-1 …/35` — an alpha on a token colour, so what it
  contrasted against depended on whatever surface sat behind it per theme).
- The leading tile deliberately stays a RANK rather than becoming a category
  glyph: this is a ranked referral list, the number is what the "Best fit" pill
  refers to, and it doubles as the shortlist checkmark. Services has no single
  category axis either — records carry facets — so there would be nothing honest
  to put there.

Ratchets moved the right way: legacyShadowAliases 111 -> 107, edge conflicts
19 -> 18.

Deferred, unchanged: therapy-compass/therapy-card.tsx (own SVG icon set, own
control recipes, own IconTile, and the open rawPadding/rawGap debt from #261),
the differentials-home card family, and the forms detail cards — those are
detail-panel compositions rather than category-bearing list items.

Gates: typecheck, lint, `npm run test` (643 files, 6885 passed / 4 skipped),
check:design-system-contract — all green. Chromium inspection at 390/1440 in
light, dark and forced-colors against the pinned revision 1234.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn

---------

Co-authored-by: Claude <noreply@anthropic.com>
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