Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a3b9d3b
Fix differentials results top clipped by ModeHomeMain centering
cursoragent Jul 19, 2026
2df3181
Harden ModeHomeMain alignment API against flex-center regressions
cursoragent Jul 19, 2026
300f8b0
Record PR #938 ModeHomeMain alignment review in ledger
cursoragent Jul 19, 2026
74c370d
Polish ModeHomeMain alignment guards and restore PR #938 body
cursoragent Jul 19, 2026
ceb3505
Record PR #938 follow-up alignment polish in review ledger
cursoragent Jul 19, 2026
b62d414
Satisfy PR policy UI verification evidence for #938
cursoragent Jul 19, 2026
44c839f
Record PR #938 merge-readiness after hosted UI green
cursoragent Jul 19, 2026
4c59b51
Center forms/services ModeHomeMain when registry is empty
cursoragent Jul 19, 2026
f6682cc
Clarify forms/services seeded alignment in PR #938 body
cursoragent Jul 19, 2026
d1045d7
Record PR #938 final review and auto-merge handoff
cursoragent Jul 19, 2026
e177b5e
Merge origin/main into differentials ModeHomeMain fix branch
cursoragent Jul 19, 2026
892f843
Record PR #938 main-merge revalidation in review ledger
cursoragent Jul 19, 2026
3a775d4
Merge origin/main to keep PR #938 mergeable for auto-merge
cursoragent Jul 19, 2026
0723c96
Record fresh PR #938 final review after main sync
cursoragent Jul 19, 2026
302449d
Merge origin/main; stop shipping PR_POLICY_BODY.md
cursoragent Jul 19, 2026
76189c3
Merge origin/main (#936) into ModeHomeMain branch
cursoragent Jul 19, 2026
cf5aeeb
Fix conflict markers left in branch-review ledger
cursoragent Jul 19, 2026
efe472f
Merge origin/main into differentials results-top fix
cursoragent Jul 19, 2026
80c033f
Merge origin/main (#941 Specifiers UI) into results-top fix
cursoragent Jul 19, 2026
8bb0ed3
Merge origin/main (#944 overlap header settle)
cursoragent Jul 19, 2026
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
7 changes: 7 additions & 0 deletions docs/branch-review-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,10 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD
| 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. |
| 2026-07-19 | cursor/specifiers-results-ui-7850 (PR #941) | ef9bcf23bb93cbaddddeda3aedf08e6d037e2f18 | Specifiers results UI elevation final review + merge readiness | No high-confidence P0-P1. Ranking blurb removed; solid Aegean subnav kept vs soft family chips; diagnosis control content-sized with min-h-tap; match cards single Open hit target with motion-reduce and guarded typical language. Re-synced origin/main after #936 ledger append conflict. Residual risk: hosted Production UI must finish green on exact head; full verify:cheap still hits known container-only pdf-extraction-budget failures also seen on main. | Local: format:changed; lint/typecheck on touched files; design-system + icon-scale; vitest specifiers+reserve 10/10; Playwright ui-specifiers 5/5 including filter interactions; production build + client-bundle secret scan; offline RAG fixtures 36/21. PR marked ready for review. No OpenAI/live Supabase provider calls. |
| 2026-07-19 | PR #938 / `cursor/fix-differentials-results-top-d760` | 2df3181859345339d777ac814908b3095cea36fd | ModeHomeMain contentAlign fix for differentials mobile clipping | No high-confidence P0–P2. Fix correctly introduces exclusive `contentAlign` (`center` \| `start` \| `startOnPhone`), migrates therapy/formulation/specifiers off broken className justify overrides, top-aligns differentials results via `autoRunSearch`, and guards with static + Playwright fold assertions. Residual: DSM/forms/services still default-center; justify-strip leaves variant prefixes; Playwright `0.55*vh` upper bound may flake if header band grows; Chip compact overrides still dual-utility via `cn()`. | Static diff vs `origin/main`; inventory of all `ModeHomeMain` call sites; justify-strip regex probe; `npx vitest run tests/mode-home-main-align.test.ts` 4/4. Not run: Playwright/`verify:ui` (browser), `verify:cheap`/`verify:pr-local`, provider-backed checks. |
| 2026-07-19 | PR #938 / `cursor/fix-differentials-results-top-d760` | 74c370d81342dd729398dc2b40ba3158ea30f1db | follow-up review + residual polish (policy body, align API, UI flake) | Prior residuals closed: `PR_POLICY_BODY.md` rewritten for #938 (Sync PR policy body was overwriting with stale #932 text); `withoutJustifyUtilities` strips prefixed utilities; Chip uses exclusive `density` type scale; DSM/forms/services use `startOnPhone`; Best Answer fold bound uses header+240px; `ui-overlap` waits for a single `header#search`. No remaining high-confidence P0–P2 in the ModeHomeMain/differentials mobile scope. | `npx vitest run tests/mode-home-main-align.test.ts` 5/5; Prettier on touched files. Playwright focused rerun and hosted Production UI expected after push. |
| 2026-07-19 | PR #938 / `cursor/fix-differentials-results-top-d760` | b62d414ca9001fbc1ac0d50b315450e107751d67 | merge-readiness after policy + hosted UI | No remaining high-confidence P0–P2. PR description sync + `verify:ui` evidence keep PR policy green; hosted Production UI / PR required green on exact head. | Local: align Vitest 5/5; ui-overlap 12/12; differentials fold Playwright 1/1. Hosted: Production UI, Advisory UI, Static, Unit, Build, PR policy, PR required, Sync PR policy body all pass. |
| 2026-07-19 | PR #938 / `cursor/fix-differentials-results-top-d760` | f6682cc08eb0fcc0b8dd170d5c9358b3fedc0c0d | final meticulous review + safe-merge handoff | No high-confidence P0–P2. Forms/services now centre when unseeded/loading and use `startOnPhone` only when seeded (avoids sparse empty homes). CodeRabbit risk-line comment dispositioned as stale #932 body confusion. Residual: branch protection still requires a human approving review before squash auto-merge can land. | Local: align Vitest 5/5; focused Chromium overlap+fold 13/13; verify:cheap/pr-local unit stages 2953/2955 with only known container `pdf-extraction-budget` Python ENOENT failures; format/lint/typecheck/RAG fixtures pass; local `next build` blocked by running ensure server (hosted Build green). Hosted exact-head: Production UI, PR required, Unit, Static, Build, PR policy all SUCCESS. Enabled squash `--auto` merge. |
| 2026-07-19 | PR #938 / `cursor/fix-differentials-results-top-d760` | e177b5e877aa2f834b3a601fa37679fa3e77b0e3 | merge main (#933) + post-merge revalidation | Merged `origin/main` (Safari edge-to-edge #933) with ledger conflict resolved by keeping both review rows. No product conflicts; `contentAlign`, Chip density, and Best Answer fold asserts intact. No new P0–P2. Still blocked only by required human approving review; squash auto-merge remains enabled. | Local after merge: align+composer-reserve Vitest 9/9; focused Chromium dock-hide + fold + compare-dock 3/3. Hosted on merge head: Production UI, PR required, Unit, Static, Build, PR policy all SUCCESS. |
| 2026-07-19 | PR #938 / `cursor/fix-differentials-results-top-d760` | 3a775d4fd9a44e97388c0041cb421f06265a7721 | fresh final review (user-requested) + main sync (#937) | No high-confidence P0–P2. Product delta unchanged vs prior merge-ready head; #933 reserve + #938 contentAlign remain complementary. Merged `origin/main` (#937) cleanly. Follow-up: dropped `PR_POLICY_BODY.md` when syncing #942 so this PR does not reintroduce the stale template; live PR description already correct. Residual: required human approving review. | Local: align+composer-reserve Vitest 9/9; focused Chromium overlap+fold+compare 14/14. Hosted CI green on prior tip. |
| 2026-07-19 | PR #938 / `cursor/fix-differentials-results-top-d760` | efe472fac3489473e2465da2d92980508c20fd81 | merge main (#934 favourites) + #941 sync | Merged favourites guest-gate (#934) then Specifiers results UI (#941). Product conflicts none: kept `contentAlign="startOnPhone"` on Specifiers home while taking #941 results chrome. `ui-overlap` gotoHome wait conflict resolved with settled single-header short-circuit. No new P0–P2. Residual: human approving review required for squash auto-merge. | Local: align Vitest; favourites+app-modes+tools 42 tests; Chromium ui-overlap+ui-tools 95/95; verify:cheap unit stage green. Hosted CI green on pre-#941 tip; re-running after this merge. |
1 change: 1 addition & 0 deletions docs/process-hardening.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This document turns the current process review into phased, durable repo practic

## Repository cleanup follow-ups (2026-07-19)

- **ModeHomeMain vertical alignment (fixed in #938):** commit `39d14a51` made standalone mode homes a `flex-1 justify-center` shell. That centres short empty homes correctly, but when differentials (and any tall results view) reused the same wrapper, the top of the content was clipped above the phone scrollport — white gap under the header, Best Answer unreachable, first visible list card looking like rank “2”. Prefer `ModeHomeMain`’s `contentAlign` prop (`center` | `start` | `startOnPhone`); never pass `justify-*` via `className` (`cn()` does not merge Tailwind). Guards: `tests/mode-home-main-align.test.ts` plus the narrow-viewport Best Answer fold assertion in `tests/ui-tools.spec.ts`.
- **High-priority local process ownership:** `scripts/run-eval-safe.mjs` still scans for and terminates residual repository processes through `cleanupResidualEvaluationProcesses()`. A superseded RAG safety worktree contained a narrower child-owned `terminateOwnedProcessTree(child.pid)` approach plus a regression test proving unrelated Vitest, Playwright, and Next processes remain untouched. Do not cherry-pick that stale worktree wholesale; isolate this process-ownership fix on current `main`, then verify it statically without starting a provider-backed evaluation. Modifying or exercising the eval workflow remains approval-gated.
- **Provider-gated RAG safety ideas:** the same stale worktree contained conservative answer-quality thresholds, an evaluation cost-cap preflight, production-safety validation, deep-health assessment, and citation/vector proof tests. Its 754-line retrieval migration and route changes conflict with the later public-title privacy and migration chain and must not be replayed. If explicitly approved, rescope only the still-relevant preflight utilities and tests against current `main`; keep live OpenAI/Supabase validation separate.
- **Semantic reranking rollout debt:** PR #901 keeps `RAG_SEMANTIC_RERANK_ENABLED=false`. Do not enable it until the provider-backed 36/36 retrieval-quality gate and an ambiguity-focused canary are explicitly approved and recorded.
Expand Down
40 changes: 32 additions & 8 deletions src/components/clinical-dashboard/differentials-home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,23 @@ function MatchBadge({ label }: { label: string }) {
);
}

function Chip({ children, className }: { children: string; className?: string }) {
function Chip({
children,
className,
density = "default",
}: {
children: string;
className?: string;
/** Comfortable chips use a single exclusive type scale — never dual text-* via cn(). */
density?: "default" | "comfortable";
}) {
return (
<span
className={cn(
"inline-flex min-h-6 min-w-0 max-w-full items-center rounded-md bg-[color:var(--surface-subtle)] px-2 text-2xs font-bold leading-none text-[color:var(--text-muted)]",
"inline-flex min-h-6 min-w-0 max-w-full items-center rounded-md bg-[color:var(--surface-subtle)] text-[color:var(--text-muted)]",
density === "comfortable"
? "px-2.5 py-1 text-xs font-semibold leading-snug"
: "px-2 text-2xs font-bold leading-none",
className,
)}
>
Expand Down Expand Up @@ -489,12 +501,12 @@ function MobileResultCard({
</div>
<div className="flex min-w-0 max-w-full flex-wrap gap-1.5">
{result.tags.slice(0, 2).map((tag) => (
<Chip key={`${result.id}-${tag}`} className="max-w-full px-2.5 py-1 text-xs font-semibold leading-snug">
<Chip key={`${result.id}-${tag}`} density="comfortable" className="max-w-full">
{tag}
</Chip>
))}
{result.tags.length > 2 ? (
<Chip className="shrink-0 px-2.5 py-1 text-xs font-semibold leading-snug">{`+${result.tags.length - 2}`}</Chip>
<Chip density="comfortable" className="shrink-0">{`+${result.tags.length - 2}`}</Chip>
) : null}
</div>
</article>
Expand Down Expand Up @@ -526,6 +538,7 @@ function BestAnswerCard({

return (
<section
data-testid={compact ? "differential-best-answer" : undefined}
className={cn("rounded-lg border shadow-[var(--shadow-inset)]", compact ? "p-3.5" : "p-4")}
style={{
borderColor: `color-mix(in srgb, ${cardBorderColor}, transparent)`,
Expand Down Expand Up @@ -570,14 +583,25 @@ function BestAnswerCard({
</div>
{onToggle ? <SelectionToggle selected={Boolean(selected)} onClick={onToggle} label={best.title} /> : null}
</div>
<p className={cn("text-sm font-medium leading-6 text-[color:var(--text-muted)]", compact ? "mt-2.5" : "mt-3")}>
<p
className={cn(
"min-w-0 text-sm font-medium leading-6 text-[color:var(--text-muted)]",
compact ? "mt-2.5 line-clamp-3" : "mt-3",
)}
>
{best.subtitle}
</p>
<div className={cn("flex flex-wrap gap-1.5", compact ? "mt-2.5" : "mt-3")}>
<div className={cn("flex min-w-0 max-w-full flex-wrap gap-1.5", compact ? "mt-2.5" : "mt-3")}>
{visibleTags.map((tag) => (
<Chip key={tag}>{tag}</Chip>
<Chip key={tag} density={compact ? "comfortable" : "default"} className={compact ? "max-w-full" : undefined}>
{tag}
</Chip>
))}
{hiddenTagCount > 0 ? <Chip>{`+${hiddenTagCount}`}</Chip> : null}
{hiddenTagCount > 0 ? (
<Chip density={compact ? "comfortable" : "default"} className={compact ? "shrink-0" : undefined}>
{`+${hiddenTagCount}`}
</Chip>
) : null}
</div>
</section>
);
Expand Down
7 changes: 6 additions & 1 deletion src/components/differentials/differentials-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,13 @@ export function DifferentialsHomePage({ query = "", autoRunSearch = false }: Dif
[router, routedSearchContext.queryMode, routedSearchContext.scopeFilters],
);

// `autoRunSearch` is true on /differentials?q=…&run=1 — that mounts the tall
// SearchResultsView. Empty homes stay centred; results must top-align or the
// Best Answer / query band are clipped above the phone scrollport.
const showingResults = autoRunSearch;

return (
<ModeHomeMain>
<ModeHomeMain contentAlign={showingResults ? "start" : "center"}>
<DifferentialsHome
query={query}
loading={loading}
Expand Down
2 changes: 1 addition & 1 deletion src/components/dsm/dsm-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const featuredCategories = dsmCategories

export function DsmHomePage() {
return (
<ModeHomeMain testId="dsm-home-main">
<ModeHomeMain testId="dsm-home-main" contentAlign="startOnPhone">
<ModeHomeTemplate
testId="dsm-home"
title="DSM-5 Diagnosis"
Expand Down
7 changes: 6 additions & 1 deletion src/components/forms/forms-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,12 @@ export function FormsHomePage() {
) : null;

return (
<ModeHomeMain testId="forms-home">
<ModeHomeMain
testId="forms-home"
// Seeded homes are content-rich and can clip when centered on phone;
// loading/empty notices stay short — keep those vertically centred.
contentAlign={hasRegistryRecords ? "startOnPhone" : "center"}
>
<ModeHomeTemplate
testId="forms-home-template"
title="Forms"
Expand Down
2 changes: 1 addition & 1 deletion src/components/formulation/formulation-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function FormulationThreadStrip() {

function FormulationHome() {
return (
<ModeHomeMain testId="formulation-home" className="justify-start sm:justify-center">
<ModeHomeMain testId="formulation-home" contentAlign="startOnPhone">
<ModeHomeTemplate
testId="formulation"
title="How can I help with the formulation?"
Expand Down
45 changes: 40 additions & 5 deletions src/components/mode-home-template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,25 +95,60 @@ export function ModeHomeHero({
}

/**
* Standalone-route wrapper that mirrors the dashboard's vertically centred
* mode homes: full-height, centred content. The shell reserves composer
* clearance via --mobile-composer-reserve on #main-content.
* Vertical alignment for standalone mode-home shells.
*
* Introduced as always-`justify-center` flex in 39d14a51 (edge-to-edge mobile
* shell). That works for short empty homes, but centering a child taller than
* the phone scrollport clips the top — unreachable at scrollTop 0.
*
* Prefer this prop over className `justify-*` overrides: `cn()` concatenates
* and does not resolve Tailwind conflicts, so dual justify utilities are
* non-deterministic. Alignment classes are applied last and any stray
* `justify-*` tokens in `className` are stripped.
*/
export type ModeHomeMainAlign = "center" | "start" | "startOnPhone";

const MODE_HOME_MAIN_ALIGN_CLASS: Record<ModeHomeMainAlign, string> = {
// Short empty homes — centre in the visible canvas.
center: "justify-center pt-[clamp(1.25rem,4vh,2.25rem)] sm:pt-[clamp(1.75rem,5vh,3.25rem)]",
// Tall results / content — keep the top reachable on every breakpoint.
start: "justify-start pt-3 sm:pt-4",
// Content-rich homes that still fit after sm — top-align on phone only.
startOnPhone: "justify-start pt-3 sm:justify-center sm:pt-[clamp(1.75rem,5vh,3.25rem)]",
};

/** Strip bare and prefixed justify utilities (`sm:justify-center`, `max-sm:justify-start`, …). */
function withoutJustifyUtilities(className?: string) {
if (!className) return undefined;
const cleaned = className
.replace(/(?:^|\s)(?:[\w-]+:)*justify-(?:normal|start|end|center|between|around|evenly|stretch)(?=\s|$)/g, " ")
.replace(/\s+/g, " ")
.trim();
return cleaned || undefined;
}

/**
* Standalone-route wrapper that mirrors the dashboard mode homes. The shell
* reserves composer clearance via --mobile-composer-reserve on #main-content.
*/
export function ModeHomeMain({
testId,
children,
className,
contentAlign = "center",
}: {
testId?: string;
children: ReactNode;
className?: string;
contentAlign?: ModeHomeMainAlign;
}) {
return (
<main
data-testid={testId}
className={cn(
"flex min-h-0 w-full flex-1 flex-col items-center justify-center bg-[color:var(--background)] px-0 pt-[clamp(1.25rem,4vh,2.25rem)] pb-4 text-[color:var(--text)] sm:min-h-[calc(100dvh-4rem)] sm:px-6 sm:pb-[clamp(1.75rem,5vh,3.25rem)] sm:pt-[clamp(1.75rem,5vh,3.25rem)] lg:px-8",
className,
"flex min-h-0 w-full flex-1 flex-col items-center bg-[color:var(--background)] px-0 pb-4 text-[color:var(--text)] sm:min-h-[calc(100dvh-4rem)] sm:px-6 sm:pb-[clamp(1.75rem,5vh,3.25rem)] lg:px-8",
withoutJustifyUtilities(className),
MODE_HOME_MAIN_ALIGN_CLASS[contentAlign],
)}
>
{children}
Expand Down
7 changes: 6 additions & 1 deletion src/components/services/services-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,12 @@ export function ServicesHomePage({ defaultServiceSlug = null }: { defaultService
) : null;

return (
<ModeHomeMain testId="services-home">
<ModeHomeMain
testId="services-home"
// Seeded homes are content-rich and can clip when centered on phone;
// loading/empty notices stay short — keep those vertically centred.
contentAlign={hasRegistryRecords ? "startOnPhone" : "center"}
>
<ModeHomeTemplate
testId="services-home-template"
title="Find a service"
Expand Down
2 changes: 1 addition & 1 deletion src/components/specifiers/specifiers-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function SpecifierPathwayStrip() {

function SpecifiersHome() {
return (
<ModeHomeMain testId="specifiers-home" className="justify-start sm:justify-center">
<ModeHomeMain testId="specifiers-home" contentAlign="startOnPhone">
<ModeHomeTemplate
testId="specifiers"
title="Refine the diagnosis with the right specifier"
Expand Down
2 changes: 1 addition & 1 deletion src/components/therapy-compass/screens/home-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function HomeScreen() {
: `Search ${b.therapies.length} source-grounded therapy ${b.therapies.length === 1 ? "record" : "records"} by problem, symptom, skill or population — or jump into a clinical pathway.`;

return (
<ModeHomeMain testId="therapy-compass-home" className="justify-start sm:justify-center">
<ModeHomeMain testId="therapy-compass-home" contentAlign="startOnPhone">
<ModeHomeTemplate
testId="therapy-compass"
title="What therapy are you looking for?"
Expand Down
Loading