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
69f92eb
fix(sidebar): route Medication to shared home
BigSimmo Aug 26, 2026
fa3d14b
fix(ui): eliminate layout shifts, stabilize composer reserves, and st…
BigSimmo Aug 26, 2026
2357c34
fix(sidebar): restore Medication pin to /medications
cursoragent Aug 26, 2026
e80c7f6
merge(main): resolve Medication pin conflicts and review fixes
BigSimmo Aug 26, 2026
3542eb8
Merge branch 'main' into codex/ui-stability-cls-tokens
BigSimmo Aug 27, 2026
1c01ade
Merge remote-tracking branch 'origin/main' into codex/ui-stability-cl…
BigSimmo Aug 27, 2026
960d809
Merge branch 'main' into codex/ui-stability-cls-tokens
BigSimmo Aug 27, 2026
631bf42
chore: refresh outstanding-issues snapshot after main merge
BigSimmo Aug 27, 2026
390619f
Merge branch 'main' into codex/ui-stability-cls-tokens
BigSimmo Aug 27, 2026
4228b6f
merge: reconcile remote branch and refresh outstanding-issues snapshot
BigSimmo Aug 27, 2026
1d2b75c
Merge branch 'main' into codex/ui-stability-cls-tokens
BigSimmo Aug 27, 2026
aace9a7
Merge remote-tracking branch 'origin/codex/ui-stability-cls-tokens' i…
BigSimmo Aug 27, 2026
8fe8745
docs: record PR #2398 babysit sweep
BigSimmo Aug 27, 2026
0b79782
Merge branch 'main' into codex/ui-stability-cls-tokens
BigSimmo Aug 27, 2026
765bbf2
Merge remote-tracking branch 'origin/codex/ui-stability-cls-tokens' i…
BigSimmo Aug 27, 2026
8af1396
fix(ci): drop stale issues inbox done records from PR #2398
BigSimmo Aug 27, 2026
4a1b860
chore(repo-awareness): refresh snapshot after inbox cleanup
BigSimmo Aug 27, 2026
d9a2e06
Merge branch 'main' into codex/ui-stability-cls-tokens
BigSimmo Aug 27, 2026
05ee8e6
merge: sync remote branch before repo-awareness push
BigSimmo Aug 27, 2026
452ce33
merge: sync main and refresh repo-awareness snapshot for PR #2398
BigSimmo Aug 27, 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
16 changes: 12 additions & 4 deletions data/repo-awareness-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"version": "repo-awareness-snapshot-v1",
"captured_revision": {
"sha": "d2c61c8cbddfb096e7b86e4dae1983dd1266a356",
"committed_at": "2026-08-27T09:12:09+08:00"
"sha": "d9a2e0619cd30539b5dc4a433da0d97cc8f618ed",
"committed_at": "2026-08-27T10:05:30+08:00"
},
"routes": {
"modes": [
Expand Down Expand Up @@ -3805,6 +3805,14 @@
},
"review_state": {
"records": [
{
"date": "2026-08-27",
"ref": "2398",
"head": "4228b6f91a1d130b6751fd60bdee86e673cb6ddd",
"scope": "pr-babysit sweep: merge conflicts, review threads, snapshot CI fix",
"outcome": "FIXED",
"checks": "merge-main, review-replies, thread-resolve, snapshot-regen"
},
{
"date": "2026-08-26",
"ref": "claude/dev-hub-handoff-accuracy (PR #2382)",
Expand Down Expand Up @@ -24831,8 +24839,8 @@
}
],
"counts": {
"records": 2628,
"refs": 1605
"records": 2629,
"refs": 1606
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| 2026-08-27 | 2398 | 4228b6f91a1d130b6751fd60bdee86e673cb6ddd | pr-babysit sweep: merge conflicts, review threads, snapshot CI fix | FIXED | merge-main, review-replies, thread-resolve, snapshot-regen |
4 changes: 2 additions & 2 deletions src/components/clinical-dashboard/ClinicalSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ const sidebarToolItems = [
// redirects onto it, so pointing a pinned entry at `/services` or `/factsheets`
// would spend a round trip arriving at the same place.
{ id: "services", label: "Services", icon: appModeIcons.services, href: "/?mode=services" },
// Medication owns a real home: /medications is the prescribing workspace,
// not a consolidated 307 onto /?mode=prescribing (the shared empty home).
// Medication owns a real home at /medications; it is not a consolidated-mode
// redirect onto /?mode=prescribing (the shared empty home).
{ id: "prescribing", label: appModeDefinition("prescribing").label, icon: Pill, href: "/medications" },
{ id: "factsheets", label: "Factsheets", icon: appModeIcons.factsheets, href: "/?mode=factsheets" },
// PT-11: standalone /tools is the canonical entry; /?mode=tools remains a dashboard-mode alias.
Expand Down
4 changes: 2 additions & 2 deletions src/components/therapy-compass/record/key-facts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useState } from "react";
import { cn, textMuted } from "@/components/ui-primitives";
import { Sheet } from "@/components/ui/sheet";

import { therapyBtn } from "../controls";
import { interactiveRowBase } from "@/components/ui/interactive-row";
import { ProseBlock } from "../prose";
import type { Therapy } from "../data/types";
import { therapyKeyFactCards, type TherapyKeyFactCard, type TherapyKeyFactId } from "./key-fact-cards";
Expand Down Expand Up @@ -69,7 +69,7 @@ function FactCard({ card, onOpen }: { card: TherapyKeyFactCard; onOpen?: () => v
type="button"
onClick={onOpen}
aria-haspopup="dialog"
className={cn(therapyBtn, "flex min-h-12 min-w-0 flex-1 flex-col rounded-md text-left")}
className={cn(interactiveRowBase, "flex min-h-12 min-w-0 flex-1 flex-col items-start rounded-md text-left")}
aria-label={`${card.label}: ${card.face}. Open detail.`}
>
{header}
Expand Down
5 changes: 5 additions & 0 deletions src/components/ui/disclosure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,12 @@ export function Disclosure({
onOpenChange?.(next);
}

const dataState = open ? "expanded" : "collapsed";

return (
<div
data-testid="disclosure"
data-state={dataState}
className={cn(
"overflow-hidden rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)]",
// A print-expanded panel must not be clipped by the collapsed-height
Expand All @@ -84,6 +87,7 @@ export function Disclosure({
<button
type="button"
id={`${id}-trigger`}
data-state={dataState}
aria-expanded={open}
aria-controls={panelId}
onClick={toggle}
Expand Down Expand Up @@ -133,6 +137,7 @@ export function Disclosure({
role="region"
aria-labelledby={`${id}-trigger`}
data-open={open ? "true" : "false"}
data-state={dataState}
className={cn(
"px-3 py-3 print:block",
!open && "hidden",
Expand Down
4 changes: 4 additions & 0 deletions tests/forms-information-disclosure.dom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,23 @@ describe("Form information disclosures", () => {
const trigger = within(section).getByRole("button", { name: "Does not authorise" });
expect(trigger).toHaveAccessibleName("Does not authorise");
expect(trigger).toHaveAttribute("aria-expanded", "false");
expect(trigger).toHaveAttribute("data-state", "collapsed");

const panelId = trigger.getAttribute("aria-controls");
expect(panelId).toEqual(expect.any(String));
if (!panelId) return;
const panel = document.getElementById(panelId);
expect(panel).toBeTruthy();
if (!panel) return;
expect(panel).toHaveAttribute("data-state", "collapsed");
expect(panel).not.toHaveAttribute("hidden");
expect(panel).toHaveClass("hidden", "print:block");

await user.click(trigger);

expect(trigger).toHaveAttribute("aria-expanded", "true");
expect(trigger).toHaveAttribute("data-state", "expanded");
expect(panel).toHaveAttribute("data-state", "expanded");
expect(within(trigger).queryByText(fullText)).not.toBeInTheDocument();
expect(panel).not.toHaveClass("hidden");
expect(panel).not.toHaveClass("border-t");
Expand Down
133 changes: 124 additions & 9 deletions tests/ui-caring-contacts-workspace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,19 +423,17 @@ test.describe("caring-contacts patients directory", () => {
* ------------------------------------------------------------------------- */

/**
* The two widths the overlay matrix is proved at.
* The two widths the overlay matrix is proved at for pure phone and desktop modalities.
*
* 390 samples `compact` (phone modalities) and 1440 samples `wide` (desktop
* modalities), which is every branch the host's modality decision has.
*
* Ruling 60 — deliberately NOT sampled, and it must stay that way. Between 640
* and 767 the stamped modality and the shared Sheet's own geometry breakpoint
* disagree: `widthStateFor` switches compact→rail at 768, while `Sheet` switches
* to a centred dialog at Tailwind `sm:` = 640, so a `bottom-sheet` row in that
* band stamps `bottom-sheet` and renders as a dialog. That divergence is pinned
* by `tests/caring-contacts-overlay-host.dom.test.tsx` and left in place on
* purpose; adding a width here to chase it would turn an owner's design-record
* question into a red gate.
* Ruling 60 modal/sheet breakpoint contract testing: between 640 and 767 the stamped
* modality and the shared Sheet's own geometry breakpoint disagree: `widthStateFor`
* switches compact→rail at 768, while `Sheet` switches to a centred dialog at
* Tailwind `sm:` = 640, so a `bottom-sheet` row in that band stamps `bottom-sheet`
* and renders as a dialog. This divergence is explicitly verified across 640px,
* 700px, and 767px in the dedicated Ruling 60 suite below.
*/
const OVERLAY_MATRIX_WIDTHS = [390, 1440] as const;

Expand Down Expand Up @@ -713,6 +711,123 @@ test.describe("caring-contacts workspace overlays", () => {
}
});

/**
* Ruling 60 — modal vs bottom-sheet behavior across the 640px–767px band.
*
* `widthStateFor` returns "compact" up to 767px (rail breakpoint is 768px), so OverlayHost
* stamps `data-overlay-modality="bottom-sheet"`. The shared Sheet component switches from
* bottom-sheet to centred dialog modal at Tailwind `sm:` = 640px.
*
* The assertions below verify the exact contract across 640px, 700px, and 767px:
* 1. The stamped modality remains "bottom-sheet" as specified in the frozen definitions.
* 2. The rendered geometry is a centred modal dialog (constrained width <= 640px,
* not anchored to the bottom edge, drag grip hidden).
* 3. Full-screen stages remain full screen across the band (they transition at `lg:` = 1024px).
* 4. Decision control stays fully visible in the viewport and Escape dismissal is respected.
*/
const RULING_60_BAND_WIDTHS = [640, 700, 767] as const;

test.describe("caring-contacts overlay Ruling 60 modal vs bottom-sheet breakpoint contract", () => {
for (const width of RULING_60_BAND_WIDTHS) {
test(`verifies bottom-sheet rows render as centred dialog modals at ${width}px`, async ({ page }) => {
test.setTimeout(120_000);

const bottomSheetRows = WORKSPACE_OVERLAY_DEFINITIONS.filter(
(definition) => definition.phoneModality === "bottom-sheet",
);

for (const definition of bottomSheetRows) {
const label = `${definition.id} at ${width}px`;
const content = await deepLinkOverlay(page, width, definition.id);

expect(overlayParamOf(page), `${label}: URL query parameter`).toBe(definition.id);
expect(widthStateFor(width), `${label}: widthStateFor must be compact`).toBe("compact");

await expect(content, `${label}: stamped modality`).toHaveAttribute("data-overlay-modality", "bottom-sheet");
await expect(content, `${label}: stamped dismissal`).toHaveAttribute(
"data-overlay-dismissal",
definition.dismissal,
);

const sheetSurface = page.getByTestId("workspace-overlay-sheet");
const box = await sheetSurface.boundingBox();
expect(box, `${label}: the overlay surface has no box`).not.toBeNull();
expectFullyOnScreen(box!, width, label);

// Modal geometry assertions:
// Width is constrained to dialog max width and narrower than viewport width
expect(box!.width, `${label}: modal width should not exceed dialog max width`).toBeLessThanOrEqual(
DIALOG_MAX_WIDTH + EDGE_TOLERANCE,
);
expect(box!.width, `${label}: modal should be narrower than viewport width`).toBeLessThan(width);

// Centered vertically, not bottom-anchored
expect(
box!.y + box!.height,
`${label}: modal should be vertically centred, not anchored to the bottom edge`,
).toBeLessThan(VIEWPORT_HEIGHT - 4);
expect(box!.y, `${label}: modal should not touch the top edge`).toBeGreaterThan(4);

// Mobile drag grip is hidden on dialog modal
const dragGrip = sheetSurface.locator(".cursor-grab");
await expect(dragGrip, `${label}: drag handle should be hidden on modal`).toBeHidden();

// Decision action is fully in viewport
await expect(
content.getByTestId("workspace-overlay-action"),
`${label}: decision control is not fully in the viewport`,
).toBeInViewport({ ratio: 1 });

await page.keyboard.press("Escape");
if (dismissesOnEscape(definition.dismissal)) {
await expect(content, `${label}: Escape did not close it`).toHaveCount(0);
await expect
.poll(() => overlayParamOf(page), { message: `${label}: Escape left the id in the URL` })
.toBeNull();
} else {
await page.waitForTimeout(300);
await expect(content, `${label}: Escape dismissed a recovery-only overlay`).toHaveCount(1);
expect(overlayParamOf(page), `${label}: Escape cleared recovery-only id`).toBe(definition.id);
}
}
});

test(`verifies full-screen-stage rows remain fullscreen across the ${width}px band`, async ({ page }) => {
test.setTimeout(120_000);

const stageRows = WORKSPACE_OVERLAY_DEFINITIONS.filter(
(definition) => definition.phoneModality === "full-screen-stage",
);

for (const definition of stageRows) {
const label = `${definition.id} at ${width}px`;
const content = await deepLinkOverlay(page, width, definition.id);

await expect(content, `${label}: stamped modality`).toHaveAttribute(
"data-overlay-modality",
"full-screen-stage",
);
const sheetSurface = page.getByTestId("workspace-overlay-sheet");
const box = await sheetSurface.boundingBox();
expect(box, `${label}: stage surface has no box`).not.toBeNull();
expectFullyOnScreen(box!, width, label);

expect(box!.width, `${label}: stage does not fill viewport width`).toBeGreaterThanOrEqual(
width - EDGE_TOLERANCE,
);
expect(box!.height, `${label}: stage does not fill viewport height`).toBeGreaterThanOrEqual(
VIEWPORT_HEIGHT - EDGE_TOLERANCE,
);

await expect(
content.getByTestId("workspace-overlay-action"),
`${label}: decision control is not fully in the viewport`,
).toBeInViewport({ ratio: 1 });
}
});
}
});

/**
* WCAG 2.1 1.4.10 reflow, and the focus ring that has to survive it.
*
Expand Down
8 changes: 6 additions & 2 deletions tests/ui-forms-section-nav.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,17 @@ test.describe("Forms section navigation", () => {

await expect(trigger).toBeVisible({ timeout: 20_000 });
await expect(trigger).toHaveAttribute("aria-expanded", "false");
await expect(trigger).toHaveAttribute("data-state", "collapsed");
await expect(trigger.getByText(preview)).toBeVisible();
await trigger.click();

await expect(trigger).toHaveAttribute("aria-expanded", "true");
await expect(trigger).toHaveAttribute("data-state", "expanded");
const panelId = await trigger.getAttribute("aria-controls");
if (!panelId) throw new Error("Disclosure trigger is missing aria-controls");
const panel = page.locator(`#${panelId}`);
const panel = page.locator(`[id="${panelId}"]`);
await expect(panel).toHaveAttribute("data-state", "expanded");
Comment thread
BigSimmo marked this conversation as resolved.
await expect(section.locator('[data-testid="disclosure"][data-state="expanded"]')).toBeVisible();
await expect(trigger.getByText(preview)).toHaveCount(0);
await expect(panel.getByText(preview)).toBeVisible();
await expect(panel).not.toHaveClass(/border-t/);
Expand All @@ -84,7 +88,7 @@ test.describe("Forms section navigation", () => {
const panelId = await trigger.getAttribute("aria-controls");
if (!panelId) throw new Error("Disclosure trigger is missing aria-controls");

const panel = page.locator(`#${panelId}`);
const panel = page.locator(`[id="${panelId}"]`);
const disclosure = panel.locator("xpath=..");
const triggerPreview = disclosure.locator('button span[aria-hidden="true"]').filter({ hasText: preview });

Expand Down
6 changes: 3 additions & 3 deletions tests/ui-smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1176,7 +1176,7 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expect(page.locator('[data-testid="global-search-input"]:visible').first()).toBeEnabled();
});

test("Medication shortcut opens the prescribing workspace", async ({ page }) => {
test("Medication shortcut opens the standalone Medication home", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 820 });
await mockPrivateUnauthenticatedApi(page);
await gotoApp(page, "/");
Expand All @@ -1185,8 +1185,8 @@ test.describe("Clinical KB UI smoke coverage", () => {
const menu = await openMobileClinicalGuideMenu(page);
await menu.getByRole("link", { name: "Medication" }).click();

await expect(page).toHaveURL(/\/medications$/);
await expect(page.getByTestId("medication-home")).toBeVisible();
await expect.poll(() => new URL(page.url()).pathname, { timeout: 30_000 }).toBe("/medications");
await expect(page.getByTestId("medication-home").first()).toBeVisible();
});

test("mobile search focus is singular, visible, and contained at clipped edges", async ({ page }) => {
Expand Down
Loading
Loading