diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 99d863913..185d036a2 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -623,3 +623,4 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-19 | main / `codex/supabase-database-review` | 4034d2e60ebb6616130ff17bf3cb69368f36f8f6 + reviewed working diff | live `Clinical KB Database` security, migration, schema-drift, integrity, and performance review against current repo | Confirmed and remediated a P1 privacy defect: 601 private-document title-vocabulary rows were reachable by the service-role query corrector; the live public-only sync/backfill now reports zero private or out-of-scope rows. Applied the committed retrieval-count bound, audit-metadata minimization, registry cleanup/index, public-title corrector, and atomic summary-rate-limit migrations. The missing FK and registry indexes are present and no invalid indexes remain. A second P1 was found in the untracked live `ingestion-worker`: gateway JWT verification accepted any project JWT before privileged direct-Postgres job processing. Recovered the deployed source into the repo, restricted it to POST plus a gateway-verified `service_role` claim, expanded the Deno checker to every tracked Edge Function, and deployed exact-matching v13 with JWT verification enabled. Review also exposed a repo mirror/test gap: the count-clamp migration was not reflected in `schema.sql`; the branch now mirrors it and locks both sources in the focused test. Remaining hosted blocker: `postgres` cannot assume managed `supabase_admin`, so the fail-closed default-ACL migrations and final title-word constraint/trigger migration remain unapplied; the intentional service-role-only table still produces one INFO no-policy advisor. | Supabase connector project identity, migration and Edge Function inventory, full drift snapshot comparison, security/performance advisors, catalog integrity/ACL/index queries, Vault JWT-role compatibility check, post-apply invariants, exact deployed-source hashes, and unauthenticated live rejection (401); focused retrieval/schema/drift Vitest 82/83 with only manifest freshness failing; Edge/retrieval auth 9/9; Deno check for both functions; offline RAG 36 cases / 294 tests; function-grant guard; scoped ESLint, Prettier, and `git diff --check`. `check:supabase-project` was attempted but stopped before provider contact because local project env vars are unset. `drift:manifest` was blocked because Docker Desktop could not start and was cleaned up. `verify:cheap`, `verify:pr-local`, production-readiness, OpenAI, hosted CI, broader deployment, and commit/push were not run. | | 2026-07-19 | cursor/safari-edge-to-edge-f46b (PR #933) | 15061964dd2fdf9665f72b7282f5cc81c736e57f | final Safari edge-to-edge / phone dock reserve review + merge readiness | No high-confidence P0-P1. Confirmed implementation: shared reserve module collapses to 0.75rem when dock hides; shell uses block scrollport + inner mobile-composer-reserve-pad so clearance contributes to scrollHeight; child dock-sized env(safe-area) pads removed; DocumentViewer owns its dock pad. Review polish: formulation/specifier max-sm:min-h-0 alignment, document-route ownership simplification, hidden-pad CSS token guard. Residual P2/P3 only: differentials compare zero-inset backdrop margin, idle 2rem vs max(2rem,safe-area) ~2px, unused-looking #main-content padding transition still needed by ClinicalDashboard. Merge-ready. | Local: format/lint/typecheck/knip/budgets pass; unit 2952 passed with only pre-existing pdf-extraction-budget (python ENOENT, also fails on clean main); production build + client-bundle secret scan pass; focused Chromium composer suite 6/6 (forms hide, tablet/desktop clearance, differentials compare, service-detail endpoint, document-viewer hide, long-answer dock). Hosted CI on prior head fully green including Production UI; polish head re-checked before merge. No OpenAI/live Supabase/provider calls. | | 2026-07-19 | cursor/pr-policy-body-cleanup-f46b (PR #942) + PR #933 closeout | 7c8e6aadf0890b143372fb96f13d9de47a416db9 | post-merge CI triage for #933 PR-policy red check | PR #933 product merge (`bd864de0`) already on main with green post-merge main CI (Static/Unit/Build/Production UI/SAST/Docker). Sole remaining red check on #933 was post-ready PR policy against a stale synced body with unchecked governance boxes (from leftover `PR_POLICY_BODY.md` introduced by #932). Token cannot edit merged PR bodies (403). Removed the stale template via #942 so Sync PR policy body no longer reapplies unchecked governance. Local composer regression 6/6 on main; reserve unit 11/11. No product regression. | Hosted #933 pre-merge + main push green; #942 required checks green then squash-merged; focused Chromium composer 6/6; reserve Vitest 11/11. No OpenAI/Supabase provider calls. | +| 2026-07-19 | cursor/documents-search-header-3eab / PR #936 | a7feaa3033180b672cfafaaaf75dc75088ebf052 | documents search header redesign final review + merge readiness | No remaining high-confidence P0-P1. Implemented identity-first results chrome, unified Sort/type-filter/Library toolbar, removed documents Also-in-library strip, relocated ScopeAndGovernanceNotice under controls, fixed Prettier CI failure and memo-busting empty warnings default, synced accurate PR policy body then removed the stale leftover, and repeatedly merged origin/main so squash auto-merge is not blocked behind/dirty. Hosted required checks including Production UI passed on the integrated head. | Local: typecheck/lint/format; focused Playwright documents `@critical` + deferred source/admin + forms sort persistence; design-system/icon-scale/maintainability; build + RAG fixtures; verify:pr-local units with known pdf-extraction-budget env artifact also on clean main. Hosted: PR policy, Static, Unit, Build, Production UI, Advisory UI, PR required green. No OpenAI/live Supabase writes. | diff --git a/src/components/ClinicalDashboard.tsx b/src/components/ClinicalDashboard.tsx index 90ba50213..cea973079 100644 --- a/src/components/ClinicalDashboard.tsx +++ b/src/components/ClinicalDashboard.tsx @@ -52,7 +52,6 @@ import { } from "@/components/ui-primitives"; import { useAuthSession } from "@/lib/supabase/client"; import { AccountSetupDialog } from "@/components/clinical-dashboard/account-setup-dialog"; -import { CrossModeLinksSection } from "@/components/clinical-dashboard/cross-mode-links"; import { useEventCallback } from "@/components/clinical-dashboard/use-event-callback"; import { AuthPanel } from "@/components/clinical-dashboard/auth-panel"; import { buildMobileSectionFabState, MobileSectionFab, ToolsHub } from "@/components/clinical-dashboard/dashboard-nav"; @@ -3875,9 +3874,8 @@ export function ClinicalDashboard({ /> ) : ( <> - - {searchMode === "documents" && modeSearchSubmitted && ( - + {searchMode === "documents" ? null : ( + )} void; + showSort?: boolean; }) { return ( -
+
@@ -396,60 +402,83 @@ function SearchResultsHeader({
- + {showSort ? : null}
); } -function DocumentResultsOverview({ - documentCount, - displayedCount, - matchCount, - activeFacetCount, - trimmedQuery, +function DocumentResultsControls({ + resultTabs, + activeResultType, + onResultTypeChange, + sortValue, + onSortChange, onOpenLibrary, }: { - documentCount: number; - displayedCount: number; - matchCount: number; - activeFacetCount: number; - trimmedQuery: string; + resultTabs: Array<{ key: ResultTypeFilter; label: string; count: number }>; + activeResultType: ResultTypeFilter; + onResultTypeChange: (value: ResultTypeFilter) => void; + sortValue: ResultSortValue; + onSortChange: (value: ResultSortValue) => void; onOpenLibrary: () => void; }) { + const showTypeFilters = resultTabs.length > 1; + return (
-
-

Documents overview

-
- - {documentCount.toLocaleString()} indexed - - - {matchCount.toLocaleString()} match{matchCount === 1 ? "" : "es"} - - {activeFacetCount > 0 ? ( - - {displayedCount.toLocaleString()} after filters - - ) : null} - {trimmedQuery ? ( - - {trimmedQuery} - - ) : null} + {showTypeFilters ? ( +
+ {resultTabs.map((tab) => { + const active = tab.key === activeResultType; + return ( + + ); + })}
+ ) : ( +
); } @@ -782,6 +811,8 @@ function DocumentSearchResultsPanelImpl({ apiUnavailable, setupWarning, facets: _facets, + searchScope = null, + sourceGovernanceWarnings = EMPTY_SOURCE_GOVERNANCE_WARNINGS, onScopeDocument, onAnswerFromDocument, onOpenRecentDocuments, @@ -805,6 +836,8 @@ function DocumentSearchResultsPanelImpl({ apiUnavailable: boolean; setupWarning: string | null; facets?: SearchFacets | null; + searchScope?: SearchScopeSummary | null; + sourceGovernanceWarnings?: SourceGovernanceWarning[]; onScopeDocument: (documentId: string) => void; onAnswerFromDocument: (documentId: string) => void; onOpenRecentDocuments: () => void; @@ -882,32 +915,34 @@ function DocumentSearchResultsPanelImpl({ if (trimmedQuery) return "No matching documents"; return `${documentCount} document${documentCount === 1 ? "" : "s"}`; })(); + const showResultsControls = matches.length > 0 && !loading; + const showIdentityHeader = + recordMatchCount > 0 || + matches.length > 0 || + (trimmedQuery && !shouldShowHome) || + loading || + (unavailableMessage && !shouldShowHome); + return (
- {trimmedQuery && !shouldShowHome ? ( - <> -
- -
- - ) : null} - {recordMatchCount > 0 || - matches.length > 0 || - (trimmedQuery && !shouldShowHome) || - loading || - (unavailableMessage && !shouldShowHome) ? ( + {showIdentityHeader ? ( + ) : null} + + {showResultsControls ? ( + ) : null} @@ -920,6 +955,10 @@ function DocumentSearchResultsPanelImpl({
) : null} + {!showRecordMatches && trimmedQuery && !shouldShowHome ? ( + + ) : null} + {showRecordMatches ? ( <> @@ -953,38 +992,6 @@ function DocumentSearchResultsPanelImpl({ ) ) : ( <> - - {resultTabs.length > 1 ? ( -
- {resultTabs.map((tab) => { - const active = tab.key === effectiveResultType; - return ( - - ); - })} -
- ) : null} {activeFacetKeys.length > 0 ? ( void; className?: string; + /** Hide the visual "Sort" label on narrow viewports; the select keeps its accessible name. */ + compact?: boolean; }) { return ( diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts index b1bb3d829..063bc76ff 100644 --- a/tests/ui-smoke.spec.ts +++ b/tests/ui-smoke.spec.ts @@ -2932,10 +2932,36 @@ test.describe("Clinical KB UI smoke coverage", () => { await submitDocumentSearch(page); await expect(page).toHaveURL(/\/documents\/search\?.*q=lithium\+monitoring/); + const documentWorkspace = page.getByTestId("document-search-workspace"); + await expect(documentWorkspace.getByRole("heading", { name: /document/i }).first()).toBeVisible(); + await expect(documentWorkspace.getByTestId("document-results-controls")).toBeVisible(); + const resultsControls = documentWorkspace.getByTestId("document-results-controls"); + await expect(resultsControls.getByLabel("Sort results")).toBeVisible(); + await expect(resultsControls.getByRole("button", { name: "Open document library" })).toBeVisible(); + await expect(documentWorkspace.getByText("Documents overview")).toHaveCount(0); + await expect(documentWorkspace.getByRole("button", { name: /Browse library/i })).toHaveCount(0); + await expect(page.getByTestId("cross-mode-links")).toHaveCount(0); + await expect(page.getByText(/Also in your library/i)).toHaveCount(0); + const documentResults = page.getByRole("article").filter({ hasText: "Synthetic Lithium Monitoring Protocol" }); await expect(documentResults).toBeVisible(); await expect(documentResults).toContainText("Best match"); await expect(documentResults).toContainText("1 table"); + + const typeFilters = resultsControls.getByLabel("Filter by result type"); + if ((await typeFilters.count()) > 0) { + const tablesFilter = typeFilters.getByRole("button", { name: /Tables/i }); + await expect(tablesFilter).toBeVisible(); + await tablesFilter.click(); + await expect(tablesFilter).toHaveAttribute("aria-pressed", "true"); + await expect(documentResults).toBeVisible(); + await typeFilters.getByRole("button", { name: /^All/i }).click(); + } + + await resultsControls.getByLabel("Sort results").selectOption("alpha"); + await expect(page).toHaveURL(/[?&]sort=alpha/); + await resultsControls.getByLabel("Sort results").selectOption("relevance"); + const openDocumentLink = documentResults .getByRole("link", { name: /Open Synthetic lithium monitoring protocol/i }) .last(); @@ -2949,6 +2975,12 @@ test.describe("Clinical KB UI smoke coverage", () => { await expect(page.getByRole("complementary", { name: "Selected document evidence" })).toBeVisible(); await expectNoPageHorizontalOverflow(page); + await resultsControls.getByRole("button", { name: "Open document library" }).click(); + const resultsLibraryDialog = page.getByRole("dialog", { name: "Source library" }); + await expect(resultsLibraryDialog).toBeVisible(); + await page.keyboard.press("Escape"); + await expect(resultsLibraryDialog).toHaveCount(0); + await page.reload({ waitUntil: "domcontentloaded" }); await expect(documentResults).toBeVisible(); await expect(documentResults).toContainText("Best match");