Skip to content

feat(favourites): search-led workspace, no ModeHome (#164) - #1983

Merged
BigSimmo merged 5 commits into
mainfrom
claude/favourites-hybrid-164
Aug 15, 2026
Merged

feat(favourites): search-led workspace, no ModeHome (#164)#1983
BigSimmo merged 5 commits into
mainfrom
claude/favourites-hybrid-164

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • /favourites becomes one dashboard + search workspace, direction B from the comps. No ModeHome is reintroduced and no separate Favourites home route is created — product had already rejected that treatment for this route.
  • Retired the marketing lockup. The heart icon tile, the "Favourites command library" H1 and a sentence explaining the page to someone already standing on it cost roughly 90px of fold and said nothing the nav had not. The heading is now Favourites with the item count beside it as plain text, never a heading.
  • In-place filtering is real, not just same-surface. The page reads the shared composer's live draft through useSearchCommand, seeded from the route's submitted ?q= so a hard load still server-renders the exact list. Typing filters the table without navigating and without a second input — the same pattern tools-search-results-page.tsx already uses — so the one-composer contract in docs/search-chrome-behaviour.md is untouched and the route keeps the shell's hero composer.
  • Collapsed the redundant half of the "dual search" the ledger row names: filteredItems and the table's own tableRows were computed independently from identical inputs, so the band's match count and the table's count were two answers to one question. The page now derives the rows once and passes them down.
  • One chip rail replaces three navigation surfaces — FavouritesSidebar, FavouritesMobileQuickViews and FavouritesMobileBrowseRail. Sets, quick views and types become chips with counts, so browse reads identically at every width instead of three components disagreeing about what it means. Each chip still toggles its own dimension, so a set and a type compose the way the sidebar allowed; "All" is the only chip that clears everything. favourites-library-nav.tsx had no remaining callers afterwards and is deleted rather than left as 529 lines of unreachable UI.
  • Empty query shows Continue, then Recent and Your sets side by side (stacked on phones). A typed query demotes that band to a collapsed disclosure and filters the table in place beneath it, with the table header switching to "N matches for …".

Not done, deliberately, and worth knowing before review. Continue and Recent render from the existing derivation, and no new store was added. lastUsedByItemId and pinnedItemIds are still hard-coded five-entry literals keyed to demo slugs; real registry items fall back to the literal string "Saved". Direction B leads with both surfaces, so they genuinely want a per-item last-opened timestamp — but that is a data-layer change, not a layout one, and building it inside this PR would have quietly rescoped the row. It is queued as its own ledger row instead, and this PR makes the gap more visible than it was, which is the honest trade.

Verification

  • npm run verify:pr-local
 Test Files 607 passed (607)
Tests 6584 passed | 4 skipped (6588)
PR-local verification summary:
- completed: check:runtime, check:installed-lock-parity, format:changed, lint, typecheck, test, build, check:rag:fixtures, check:medication-interactions, check:medication-lexicon-report
- failed: (none)
- not reached: (none)

Design-system contract, run because this deletes a component and adds three:

Design-system contract passed (733 production files; raw colors 2; literal shadows 0; legacy tap classes 0; edge conflicts 24; 1px shadow spreads 2).

edgeOwnershipConflicts drops 25 → 24 because the deleted favourites-library-nav.tsx carried one. That is a real paydown, not a baseline edit — the ratchet treats an absent path as zero, so the baseline is left alone here.

UI verification not run: the container ships chromium-1194 while the lock pins revision 1234, so check:playwright-browser-revision fails closed and no local Playwright run is possible; pointing PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH at the stale shell is forbidden by AGENTS.md. Browser proof rests on the CI Production UI job. The ui-smoke favourites journeys were updated in this PR and remain the coverage for the phone/desktop breakpoint contract.

Risk and rollout

  • Risk: this is the largest of the three changes and it deletes a navigation module, so the thing most worth a second opinion is whether the chip rail is an adequate replacement for the sidebar at desktop widths — the sidebar showed sets, quick views and types as three labelled groups, and the rail shows them as one undifferentiated row of chips. Secondary risk is the live-query wiring: if useSearchCommand were ever absent the page falls back to the route's ?q= prop, which is the pre-change behaviour, so the failure mode is "filtering needs a submit" rather than a blank page.
  • Rollback: two commits, revertable together. Nothing else depends on them. favourites-library-nav.tsx returns with the revert.
  • Provider or production effects: None. No endpoint, schema, or provider call changed; the page reads the same useSavedRegistryFavourites data as before.

Clinical Governance Preflight

The classifier returns clinicalRisk: false for these paths, so this section is not required — completed anyway because favourites are a saved-clinical-content surface and the page's fault behaviour was touched.

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

The conservative-failure behaviour is preserved exactly: favouritesRegistryStatus still folds a failed or unauthorized registry read to partial whenever items exist, so a failed load can never render as "0 matches" and read as "you have no saved favourites". The empty-state branch still fires only on a ready status. The demo boundary is unchanged — prototype favourites are still spread only under demoMode, and tests/favourites-demo-boundary.test.ts still pins it.

Notes

  • RAG impact: no retrieval behaviour change — this PR touches only the favourites presentation layer and its tests. No retrieval, ranking, selection, chunking, or scoring code is involved; the page reads already-saved account favourites and filters them client-side with the same filterAndSortItems` helper as before.
  • Two source-text test pins were adjusted for reasons unrelated to their intent, and both are worth a look in review. tests/favourites-demo-boundary.test.ts sliced the mobile card's source from function FavouriteMobileCard to function FavouritesTable, so any component added between the two was pulled into the window and failed its aria-pressed assertion for code that is not the mobile card; it now ends at the function's own closing brace. tests/audit-navigation-auth-regressions.test.ts pinned the exact query={query} expression on the favourites also-matches mount, when the test is about which surface owns that block; it now pins the mount, not the prop.

Generated by Claude Code

Ledger #164: /favourites becomes one dashboard + search page. Direction B from
the comps — persistent search, sets as chips, Continue + Recent + sets on an
empty query, in-place filter on a typed one. Product had already rejected a
ModeHome treatment for this route, and none is reintroduced.
- Retire the marketing lockup. The heart icon tile, the "Favourites command
library" H1 and a sentence explaining the page to someone already standing
on it cost roughly 90px of fold and said nothing the nav had not. The
heading is now "Favourites" with the item count beside it as plain text,
never a heading.
- Real in-place filtering. The page reads the shared composer's live draft
through useSearchCommand, seeded from the route's submitted ?q= so hard
loads still server-render the exact list. Typing now filters the table
without navigating and without a second input — the same pattern the tools
results page already uses, so the one-composer contract in
docs/search-chrome-behaviour.md is untouched and the route keeps the shell's
hero composer.
- Collapse the duplicated filter. filteredItems and the table's own tableRows
were computed independently from identical inputs, so the band's match count
and the table's count were two answers to one question. The page now derives
the rows once and passes them down.
- One chip rail replaces three navigation surfaces: FavouritesSidebar,
FavouritesMobileQuickViews and FavouritesMobileBrowseRail. Sets, quick views
and types are chips with counts, so browse reads identically at every width
instead of three components disagreeing about what it means. Each chip still
toggles its own dimension, so a set and a type compose as the sidebar
allowed; "All" is the only chip that clears everything. favourites-library-nav
had no remaining callers afterwards and is deleted rather than left dead.
- Empty query shows Continue, then Recent and Your sets side by side. A typed
query demotes that band to a collapsed disclosure and filters the table in
place beneath it, with the table header switching to "N matches for …".
Continue and Recent are rendered from the existing derivation and no new store
was added. Worth stating plainly: lastUsedByItemId and pinnedItemIds are still
hard-coded five-entry literals for demo slugs, and real registry items fall
back to the string "Saved". Direction B leads with both surfaces, so they want
a genuine per-item last-opened timestamp — that is a data-layer change, not a
layout one, and inventing it here would have quietly changed the scope of this
row. Captured as its own ledger row instead.
Test updates land with the change rather than after it: the retired heading was
asserted in six places, and two source-text pins needed adjusting for reasons
unrelated to their intent — one sliced the mobile card's source to "wherever
FavouritesTable happens to be" and so swallowed any component added between
them, the other pinned the also-matches query expression when the test is about
which surface owns that block.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYphQZmsBBeqnidpSnAtjE
Assembled before the first push this time, so the PR head does not change
under an in-flight CI run.
- Branch review record for 0267232.
- issues:done for #164, recording the delivered scope and the one part left
deliberately undone.
- issues:add for the Continue/Recent data gap that redesign exposed: both
surfaces are now prominent but still ride hard-coded demo timestamps, so
real saved items tie at one score and order arbitrarily.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYphQZmsBBeqnidpSnAtjE
@supabase

supabaseBot commented Aug 15, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitaiBot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:38 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 066159cd-30fc-4be7-978c-4e679c17f81c

📥 Commits

Reviewing files that changed from the base of the PR and between 7e91545 and 13cca3f.

📒 Files selected for processing (9)
  • docs/branch-review-records/ccf620aa5c3871b3787e29f50511d79a9a9ba43bc3591b8d0e1bd117ed930ea5.record.md
  • docs/outstanding-issues-inbox/0a052268-97e1-4d4a-9ad7-033003aa486c.json
  • docs/outstanding-issues-inbox/5cf244c3-1b68-4214-8097-767b11a49e13.json
  • src/components/clinical-dashboard/favourites-command-library-page.tsx
  • src/components/clinical-dashboard/favourites-library-nav.tsx
  • tests/audit-navigation-auth-regressions.test.ts
  • tests/favourites-auth-gate.dom.test.tsx
  • tests/favourites-demo-boundary.test.ts
  • tests/ui-smoke.spec.ts

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo enabled auto-merge (squash) August 15, 2026 12:55
@BigSimmo
BigSimmo merged commit 3dec7b3 into mainAug 15, 2026
26 checks passed
@BigSimmo
BigSimmo deleted the claude/favourites-hybrid-164 branch August 15, 2026 13:15
@BigSimmoChatGPT Codex Connector

Copy link
Copy Markdown
OwnerAuthor

Final Codex review snapshot

Head: 13cca3f7c1a189ae5c15caa7963b3555dc80c9c2
Base at merge: 7e91545e8d96aca8ea4aff521ce2881b47354180
Merge commit: 3dec7b3e67e8d26a40596a44848f242e1a8dccac

  • The branch contained the latest base and had a clean merge tree. The squash merge tree exactly matches the reviewed PR tree (3f8eb794a012f68960ac27ccdae334ef64f0873e).
  • No review threads existed. A fresh-context adversarial pass completed after the existing auto-merge and found no P0/P1, but did identify one residual P2: the new header renders an unqualified 0 items while an authenticated favourites read is loading or failed (and an unqualified lower-bound count for partial data). This was PR-introduced, but the PR had already merged externally before the finding completed, so no post-merge branch change was attempted.
  • Decisive local checks on the exact head passed: focused favourites/auth/navigation tests (31/31), a separate adversarial set (106/106), typecheck, format:changed, git diff --check, branch-review-ledger, outstanding-issues, and ledger write-discipline.
  • Exact-head required CI is green: PR required, Static PR checks, Unit coverage, Production UI critical, Production UI shards, Build, Safety/config, SAST, and Secret Scan all succeeded. CI Lighthouse also succeeded; Codex did not run Lighthouse locally. Advisory Visual baselines and Advisory UI were skipped as out of scope.
  • Residual risk is limited to the truthful-count P2 above; no provider-backed local checks were run.

The PR was merged by its pre-existing auto-merge workflow. Codex did not merge it or alter auto-merge.

Sign up for freeto 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.

2 participants

@BigSimmo@claude