Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/branch-review-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
8 changes: 4 additions & 4 deletions src/components/ClinicalDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -3875,9 +3874,8 @@ export function ClinicalDashboard({
/>
) : (
<>
<ScopeAndGovernanceNotice scope={searchScope} warnings={sourceGovernanceWarnings} />
{searchMode === "documents" && modeSearchSubmitted && (
<CrossModeLinksSection queries={[query]} onModeSearch={crossModeSearch} />
{searchMode === "documents" ? null : (
<ScopeAndGovernanceNotice scope={searchScope} warnings={sourceGovernanceWarnings} />
)}
<DocumentSearchResultsPanel
matches={documentMatches}
Expand All @@ -3894,6 +3892,8 @@ export function ClinicalDashboard({
apiUnavailable={apiUnavailable}
setupWarning={setupWarning}
facets={searchFacets}
searchScope={searchMode === "documents" ? searchScope : null}
sourceGovernanceWarnings={searchMode === "documents" ? sourceGovernanceWarnings : undefined}
onScopeDocument={handleScopeDocument}
onAnswerFromDocument={handleAnswerFromDocument}
onOpenRecentDocuments={handleOpenRecentDocuments}
Expand Down
197 changes: 102 additions & 95 deletions src/components/clinical-dashboard/document-search-results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import { DocumentTagCloud } from "@/components/DocumentTagCloud";
import { documentDisplayTitle } from "@/components/DocumentOrganizationBadges";
import { isDeployedClinicalKb } from "@/lib/deployed-app";
import { ModeHomeTemplate, ModeHomeVerificationFooter } from "@/components/mode-home-template";
import { ResultSortControl, SearchResultsHeaderBand } from "@/components/clinical-dashboard/search-results-header-band";
import { ScopeAndGovernanceNotice } from "@/components/clinical-dashboard/answer-content";
import { ResultSortControl } from "@/components/clinical-dashboard/search-results-header-band";
import { UniversalSearchAlsoMatches } from "@/components/clinical-dashboard/universal-search-also-matches";
import { useResultSort } from "@/components/use-result-sort";
import { SafeBoldText } from "@/components/SafeBoldText";
Expand Down Expand Up @@ -57,9 +58,10 @@ import {
type SmartDocumentTagFacet,
type SmartDocumentTagGroup,
} from "@/lib/document-tags";
import type { SourceGovernanceWarning } from "@/lib/source-governance";
import type { ServiceSearchMatch } from "@/lib/services";
import type { FormSearchMatch } from "@/lib/forms";
import type { ClinicalDocument, DocumentMatch, SearchResult } from "@/lib/types";
import type { ClinicalDocument, DocumentMatch, SearchResult, SearchScopeSummary } from "@/lib/types";
import type { RegistryRequestStatus } from "@/lib/use-registry-records";
import { sortResultItems, type ResultSortValue } from "@/lib/result-sort";
import { documentRelevancePercent } from "./relevance-score";
Expand Down Expand Up @@ -88,6 +90,8 @@ export type SearchFacets = {
type SearchRecordMode = "services" | "forms";
type SearchRecordMatch = ServiceSearchMatch | FormSearchMatch;

const EMPTY_SOURCE_GOVERNANCE_WARNINGS: SourceGovernanceWarning[] = [];

const searchRecordConfig: Record<
SearchRecordMode,
{
Expand Down Expand Up @@ -370,14 +374,16 @@ function SearchResultsHeader({
trimmedQuery,
sortValue,
onSortChange,
showSort = true,
}: {
resultLabel: string;
trimmedQuery: string;
sortValue: ResultSortValue;
onSortChange: (value: ResultSortValue) => void;
showSort?: boolean;
}) {
return (
<section className="flex items-start justify-between gap-3">
<section className="flex items-start justify-between gap-3" aria-label="Document search results">
<div className="min-w-0">
<div className="flex items-center gap-3">
<span className="grid h-11 w-11 shrink-0 place-items-center rounded-lg bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)]">
Expand All @@ -396,60 +402,83 @@ function SearchResultsHeader({
</div>
</div>
</div>
<ResultSortControl value={sortValue} onChange={onSortChange} className="min-h-tap shrink-0 lg:hidden" />
{showSort ? <ResultSortControl value={sortValue} onChange={onSortChange} className="min-h-tap shrink-0" /> : null}
</section>
);
}

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 (
<section
aria-label="Documents overview"
className="grid gap-3 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-subtle)] p-3 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center"
aria-label="Sort and filter documents"
data-testid="document-results-controls"
className="flex flex-nowrap items-center gap-1.5 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-raised)] p-1 shadow-[var(--shadow-inset)]"
>
<div className="min-w-0">
<p className="text-sm font-semibold text-[color:var(--text-heading)]">Documents overview</p>
<div className="mt-2 flex flex-wrap gap-1.5">
<DocumentBadge variant="best" icon={FileText} className="min-h-7 rounded-lg px-2.5 text-2xs">
{documentCount.toLocaleString()} indexed
</DocumentBadge>
<DocumentBadge variant="neutral" icon={Target} className="min-h-7 rounded-lg px-2.5 text-2xs">
{matchCount.toLocaleString()} match{matchCount === 1 ? "" : "es"}
</DocumentBadge>
{activeFacetCount > 0 ? (
<DocumentBadge variant="relevant" icon={Filter} className="min-h-7 rounded-lg px-2.5 text-2xs">
{displayedCount.toLocaleString()} after filters
</DocumentBadge>
) : null}
{trimmedQuery ? (
<DocumentBadge variant="neutral" icon={BookOpen} className="min-h-7 max-w-full rounded-lg px-2.5 text-2xs">
<span className="truncate">{trimmedQuery}</span>
</DocumentBadge>
) : null}
{showTypeFilters ? (
<div
role="group"
aria-label="Filter by result type"
className="polished-scroll flex min-w-0 flex-1 items-center gap-1 overflow-x-auto"
>
{resultTabs.map((tab) => {
const active = tab.key === activeResultType;
return (
<button
key={tab.key}
type="button"
aria-pressed={active}
onClick={() => onResultTypeChange(tab.key)}
className={cn(
"inline-flex min-h-tap shrink-0 items-center gap-1.5 rounded-md px-2.5 text-2xs font-bold transition motion-reduce:transition-none",
"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]",
active
? "bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]"
: "text-[color:var(--text-muted)] hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)]",
)}
>
{tab.label}
<span className="nums opacity-75">{tab.count}</span>
</button>
);
})}
</div>
) : (
<div className="min-w-0 flex-1" aria-hidden="true" />
)}
<div className="flex shrink-0 items-center gap-1">
<ResultSortControl
value={sortValue}
onChange={onSortChange}
compact
className="min-h-tap border-[color:var(--border)] bg-[color:var(--surface)]"
/>
<button
type="button"
onClick={onOpenLibrary}
aria-label="Open document library"
title="Open document library"
className={cn(floatingControl, "min-h-tap min-w-tap gap-1.5 rounded-lg px-2.5 text-xs sm:px-3")}
>
<FolderOpen aria-hidden="true" className="size-icon-md shrink-0" />
<span className="hidden sm:inline">Library</span>
</button>
</div>
<button
type="button"
onClick={onOpenLibrary}
className={cn(floatingControl, "min-h-9 w-full rounded-lg px-3 text-xs sm:w-auto")}
>
<FolderOpen aria-hidden="true" className="h-4 w-4" />
Browse library
</button>
</section>
);
}
Expand Down Expand Up @@ -782,6 +811,8 @@ function DocumentSearchResultsPanelImpl({
apiUnavailable,
setupWarning,
facets: _facets,
searchScope = null,
sourceGovernanceWarnings = EMPTY_SOURCE_GOVERNANCE_WARNINGS,
onScopeDocument,
onAnswerFromDocument,
onOpenRecentDocuments,
Expand All @@ -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;
Expand Down Expand Up @@ -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 (
<div data-testid="document-search-workspace" className="w-full space-y-3">
{trimmedQuery && !shouldShowHome ? (
<>
<div className="hidden lg:block">
<SearchResultsHeaderBand
modeId="documents"
query={trimmedQuery}
matchCount={sortedMatches.length}
loading={loading}
sortValue={sortValue}
onSortChange={setSortValue}
/>
</div>
</>
) : null}
{recordMatchCount > 0 ||
matches.length > 0 ||
(trimmedQuery && !shouldShowHome) ||
loading ||
(unavailableMessage && !shouldShowHome) ? (
{showIdentityHeader ? (
<SearchResultsHeader
resultLabel={resultLabel}
trimmedQuery={trimmedQuery}
sortValue={sortValue}
onSortChange={setSortValue}
showSort={!showResultsControls}
/>
) : null}

{showResultsControls ? (
<DocumentResultsControls
resultTabs={resultTabs}
activeResultType={effectiveResultType}
onResultTypeChange={setActiveResultType}
sortValue={sortValue}
onSortChange={setSortValue}
onOpenLibrary={onOpenLibrary}
/>
) : null}

Expand All @@ -920,6 +955,10 @@ function DocumentSearchResultsPanelImpl({
</div>
) : null}

{!showRecordMatches && trimmedQuery && !shouldShowHome ? (
<ScopeAndGovernanceNotice scope={searchScope} warnings={sourceGovernanceWarnings} />
) : null}

{showRecordMatches ? (
<>
<RecordRegistryNotice status={recordStatus} mode={recordMode} />
Expand Down Expand Up @@ -953,38 +992,6 @@ function DocumentSearchResultsPanelImpl({
)
) : (
<>
<DocumentResultsOverview
documentCount={Math.max(documentCount, matches.length)}
displayedCount={sortedMatches.length}
matchCount={matches.length}
activeFacetCount={activeFacetKeys.length}
trimmedQuery={trimmedQuery}
onOpenLibrary={onOpenLibrary}
/>
{resultTabs.length > 1 ? (
<div className="flex max-w-full items-center gap-1 overflow-x-auto rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-raised)] p-1 shadow-[var(--shadow-inset)]">
{resultTabs.map((tab) => {
const active = tab.key === effectiveResultType;
return (
<button
key={tab.key}
type="button"
aria-pressed={active}
onClick={() => setActiveResultType(tab.key)}
className={cn(
"inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-md px-2.5 text-2xs font-bold transition",
active
? "bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]"
: "text-[color:var(--text-muted)] hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)]",
)}
>
{tab.label}
<span className="nums opacity-75">{tab.count}</span>
</button>
);
})}
</div>
) : null}
{activeFacetKeys.length > 0 ? (
<DocumentTagFacetRail
groups={tagFacetGroups}
Expand Down
Loading