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
4 changes: 4 additions & 0 deletions docs/branch-review-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -961,5 +961,9 @@ This file is append-only. Never rewrite or delete an existing review record; app
| 2026-07-25 | execute-audit-code-remediation (PR #1162) | b9b56c140eb14cbba5a2c2230e3fa28d3a791add | CI unblock after bot sync | Tip 96188eca had PR required SUCCESS (Static/Safety/Unit/Build/Migration/Production UI). Hosted pr-branch-sync then merged main (a420b86b/b9b56c14), leaving CI action_required for bot-authored runs. Pushing agent commit to re-trigger non-bot CI. | Prior tip 96188eca hosted CI green; local services referral Playwright PASS; no provider-backed checks. |
| 2026-07-25 | PR supersede #1186 / `cursor/pr1186-audit-remediation-c94c` | `a38e83860510a4229d5658960657cd7448aff278` | Clean main-based port of intentional #1186 audit fixes | SUPERSEDE #1186 (do not merge old PR). Ported intentional 16-file delta onto current main; dropped conflicted checkpoint tree and placeholder skills. Fixed eval single results binding; async run-heavy so lock heartbeat fires; branch:cleanup dry-run default + argv-safe deletes; skill-create interface YAML. Close #1186. | Focused Vitest tooling+lock 6/6; check:skills 33; prettier on touched files; no provider/live eval runs. |
| 2026-07-25 | `cursor/ledger-066-067-519b` | f04392a408eceee14215c15169cbd6b70ac2041c | Close stale ledger #066/#067 (+ drop resolved #030/#075 from queue) | READY. #066 proven on main via #1174; #067 already fixed in #1191 in-process preflight. Docs-only ledger sync; no code change. | Local proof: `git show`/`log` for #1174/#1191; preflight test already in-process on main; ledger integrity asserts. No providers. |
| 2026-07-25 | PR #1190 / `remediate-dark-mode-audit` | `00eca49b9b0d7e5fbfa5703a15e9e930963984a6` | Cursor review+Bugbot+prlanded+debug (fresh pass, same HEAD) | DO NOT MERGE; NOT LANDED (OPEN, mergeable=CONFLICTING/DIRTY, 468 behind / 2 ahead). Reconfirmed P0: conflict resolution deleted `trustGatedAnswerForClinicalNotes` (0 hits on head; main L584/632/659/1075) — Clinical Notes consumes ungated answer. P1: `src/app/api/answer/route.ts:6` imports nonexistent `@/lib/rag` (tsc TS2307; stream correctly uses `@/lib/rag/rag`). P1: merge-tree conflicts on answer/upload/evidence-panels + 10 paths; literal `<<<<<<<` in docs audit plan; migration timestamp collision risk vs main. Intentional dark-mode delta is only commit `363672602` (~10 files). Salvage: `cursor/pr1190-dark-mode-salvage-f453` cherry-picks that commit onto current main, restores unused-manifest-import cleanup, keeps clinical gate. Close #1190 after salvage lands. | Bugbot; `git grep` gate/import/markers; merge-tree; `tsc` TS2307 proof; gh pr view/checks (PR policy fail). Salvage: tsc clean; visual-evidence+overlay tests 13/13; eslint on changed files. No provider/UI matrix. |
| 2026-07-25 | execute-audit-remediation-plan (PR #1188) | 8b8639113925601e1687bfe4f1f29c44a4308b61 | Bugbot/diff-review: maintainability remediation tip | BLOCK: tip tree carries unresolved conflict markers (answer/upload APIs, clinical-dashboard, services, tests); invalid `async export function sha256Hex` in indexing-v3 utils; ClinicalDashboard still calls removed `renderSystemNotice`; merge-tree vs main conflicts in check-github-action-pins.mjs + ui-primitives.tsx. Intended notices extraction/dynamic imports look mostly sound; search-scope/migration not in three-dot product delta. | `git grep` conflict markers on tip (none on origin/main); `git show` for ClinicalDashboard:3824 + utils.ts:191; `git merge-tree --write-tree origin/main 8b8639113`; no provider-backed checks. |
| 2026-07-25 | PR #1209 / `cursor/pr1186-audit-remediation-c94c` | `4cb22e45dfe46b1975fa15ddd028c7e14ceb5fab` | Close #1186 + babysit #1209 CI | DONE. Closed #1186 as superseded. Synced origin/main (MERGEABLE/CLEAN). Fixed PR-policy RAG impact line. Hosted PR required SUCCESS (Static/Safety/Unit/Build/Production UI/Advisory UI/containers) on pre-ledger tip; docs-only follow-up pushed. Ready to merge; auto-merge not enabled. | gh pr checks; local policy ok; no provider/eval runs. |
| 2026-07-25 | cursor/pr1190-dark-mode-salvage-f453 (PR #1214) | pending-ci-retrigger | CI unblock after bot sync | Hosted pr-branch-sync merged main onto tip (`68d0ceaab`), leaving CI `action_required` for bot-authored runs. Pushing agent commit to re-trigger non-bot CI before squash-merge; then close #1190. | Local pwa-manifest 8/8; trust gate present; prior unit failure fixed. No provider-backed checks. |

| 2026-07-25 | cursor/pr1190-dark-mode-salvage-f453 (PR #1214) | pending-ci-retrigger-2 | CI unblock + skip-branch-sync | Repeated pr-branch-sync bot merges left CI `action_required`. Applied `skip-branch-sync` label and agent retrigger so required checks can finish for squash-merge; then close #1190. | Hosted CI pending on tip; no provider-backed checks. |
41 changes: 41 additions & 0 deletions eslint-rules/no-hardcoded-hex.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Local ESLint rule: forbid hardcoded hex Tailwind colour utilities such as
* `bg-[#…]`, `text-[#…]`, and `border-[#…]` so themes must use semantic CSS
* variables (e.g. `var(--surface)`).
*/

const rule = {
meta: {
type: "problem",
docs: {
description: "Prevent hardcoded hex color utilities in Tailwind classNames",
},
schema: [],
},
create(context) {
const hexPattern = /\b(bg|text|border)-\[#[0-9a-fA-F]{3,8}\]/;

return {
Literal(node) {
if (typeof node.value === "string" && hexPattern.test(node.value)) {
context.report({
node,
message:
"Hardcoded hex color utilities (bg-[#...], text-[#...], border-[#...]) are forbidden. Use semantic CSS variables (e.g. var(--surface)) to support theming.",
});
}
},
TemplateElement(node) {
if (node.value && node.value.raw && hexPattern.test(node.value.raw)) {
context.report({
node,
message:
"Hardcoded hex color utilities (bg-[#...], text-[#...], border-[#...]) are forbidden. Use semantic CSS variables (e.g. var(--surface)) to support theming.",
});
}
},
};
},
};

export default rule;
3 changes: 3 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import nextTs from "eslint-config-next/typescript";
import requireLucideIconAria from "./eslint-rules/require-lucide-icon-aria.mjs";
import requireButtonWiring from "./eslint-rules/require-button-wiring.mjs";
import restrictSuppressHydrationWarning from "./eslint-rules/restrict-suppress-hydration-warning.mjs";
import noHardcodedHex from "./eslint-rules/no-hardcoded-hex.mjs";

// Shared `local` plugin object. ESLint flat config requires every config block
// that references a plugin namespace to point at the *same* object, so the two
Expand All @@ -14,6 +15,7 @@ const localRulesPlugin = {
"require-lucide-icon-aria": requireLucideIconAria,
"require-button-wiring": requireButtonWiring,
"restrict-suppress-hydration-warning": restrictSuppressHydrationWarning,
"no-hardcoded-hex": noHardcodedHex,
},
};

Expand Down Expand Up @@ -66,6 +68,7 @@ const eslintConfig = defineConfig([
plugins: { local: localRulesPlugin },
rules: {
"local/require-button-wiring": "error",
"local/no-hardcoded-hex": "error",
},
},
{
Expand Down
3 changes: 3 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { defineConfig, devices } from "playwright/test";
import { stableProjectPort } from "./src/lib/local-server-utils.mjs";
import { getPlaywrightBaseUrl } from "./scripts/playwright-base-url";

process.env.PORT = process.env.PORT || String(stableProjectPort(process.cwd()));

const baseURL = getPlaywrightBaseUrl({ allowEnsure: false });

// Sandboxed CI/cloud containers often ship a preinstalled Chromium and block
Expand Down
30 changes: 30 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2690,3 +2690,33 @@ html[data-motion="reduced"] .source-capsule-hit[aria-expanded="true"]:hover .sou
transition: none;
}
}

/* Theme switch micro-interaction (Improvement 2.2) */
html.theme-transitioning,
html.theme-transitioning *,
html.theme-transitioning *:before,
html.theme-transitioning *:after {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke !important;
transition-duration: 200ms !important;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
}

@media (prefers-reduced-motion: reduce) {
html.theme-transitioning,
html.theme-transitioning *,
html.theme-transitioning *:before,
html.theme-transitioning *:after {
transition: none !important;
}
}

/* Windows High Contrast / Forced Colors Compatibility (Improvement 2.3) */
@media (forced-colors: active) {
:root,
.dark {
--border: CanvasText;
--border-lux: CanvasText;
--border-strong: CanvasText;
--focus: Highlight;
}
}
8 changes: 3 additions & 5 deletions src/app/manifest.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { MetadataRoute } from "next";
import { APP_THEME_COLORS, DEFAULT_THEME } from "@/lib/theme";

// PWA manifest — makes the app installable with a proper icon. Icons derive from
// the single brand-mark source: the SVG for modern browsers, plus generated PNG
// "any" and "maskable" sets from app/icons/[variant]. Colours match the light
// default of viewport.themeColor in app/layout.tsx.
// "any" and "maskable" sets from app/icons/[variant]. Theme colours stay on
// viewport.themeColor / meta theme-color (see app/layout.tsx and use-theme.ts)
// so light/dark can update without a static PWA manifest colour lock.
export default function manifest(): MetadataRoute.Manifest {
return {
name: "Clinical KB",
Expand All @@ -22,8 +22,6 @@ export default function manifest(): MetadataRoute.Manifest {
// Focus the already-open app window on launch instead of spawning a second
// instance; "auto" lets platforms without the capability use their default.
launch_handler: { client_mode: ["navigate-existing", "auto"] },
background_color: APP_THEME_COLORS[DEFAULT_THEME],
theme_color: APP_THEME_COLORS[DEFAULT_THEME],
categories: ["medical", "productivity", "utilities"],
prefer_related_applications: false,
icons: [
Expand Down
2 changes: 1 addition & 1 deletion src/components/clinical-dashboard/ClinicalSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ export function ClinicalSidebarContent({
className="mt-2 flex w-full items-center gap-3 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-2 text-left shadow-[var(--shadow-inset)] transition hover:border-[color:var(--clinical-accent-border)] hover:bg-[color:var(--clinical-accent-soft)]/40 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]"
aria-label={accountLabel}
>
<span className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-[color:var(--clinical-accent-soft)] text-xs font-bold text-[color:var(--clinical-accent)]">
<span className="grid size-tap shrink-0 place-items-center rounded-full bg-[color:var(--clinical-accent-soft)] text-xs font-bold text-[color:var(--clinical-accent)]">
{identity.initials}
</span>
<span className="min-w-0 flex-1">
Expand Down
11 changes: 6 additions & 5 deletions src/components/clinical-dashboard/provider-brand-icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ export function ProviderBrandMark({ provider }: { provider: SsoProvider }) {
className="grid h-7 w-7 shrink-0 grid-cols-2 gap-0.5 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] p-1 shadow-[var(--shadow-inset)]"
aria-hidden="true"
>
<span className="bg-[#f25022]" />
<span className="bg-[#7fba00]" />
<span className="bg-[#00a4ef]" />
<span className="bg-[#ffb900]" />
<span style={{ backgroundColor: "#f25022" }} />
<span style={{ backgroundColor: "#7fba00" }} />
<span style={{ backgroundColor: "#00a4ef" }} />
<span style={{ backgroundColor: "#ffb900" }} />
</span>
);
}
Expand All @@ -116,8 +116,9 @@ export function ProviderBrandMark({ provider }: { provider: SsoProvider }) {
aria-hidden="true"
className={cn(
"grid h-7 w-7 shrink-0 place-items-center rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] text-base font-bold leading-none shadow-[var(--shadow-inset)]",
provider === "Apple" ? "text-[color:var(--text-heading)]" : "text-[#4285f4]",
provider === "Apple" ? "text-[color:var(--text-heading)]" : "",
)}
style={provider === "Google" ? { color: "#4285f4" } : undefined}
>
{provider === "Apple" ? "A" : "G"}
</span>
Expand Down
8 changes: 4 additions & 4 deletions src/components/clinical-dashboard/settings-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ export function SettingsDialog({
type="button"
onClick={onClose}
aria-label="Close settings"
className="grid h-9 w-9 shrink-0 place-items-center rounded-full border border-[color:var(--border)] bg-[color:var(--surface)]/70 text-[color:var(--text-muted)] shadow-[var(--shadow-inset)] transition hover:bg-[color:var(--surface)] hover:text-[color:var(--text-heading)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] lg:h-10 lg:w-10 lg:border-transparent lg:bg-transparent lg:shadow-none"
className="grid size-tap shrink-0 place-items-center rounded-full border border-[color:var(--border)] bg-[color:var(--surface)]/70 text-[color:var(--text-muted)] shadow-[var(--shadow-inset)] transition hover:bg-[color:var(--surface)] hover:text-[color:var(--text-heading)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] lg:h-10 lg:w-10 lg:border-transparent lg:bg-transparent lg:shadow-none"
>
<X aria-hidden="true" className="size-icon-lg" />
</button>
Expand Down Expand Up @@ -848,7 +848,7 @@ function SettingsGroup({ children }: { children: ReactNode }) {

function IconBadge({ icon: Icon }: { icon: LucideIcon }) {
return (
<span className="grid h-8 w-8 shrink-0 place-items-center rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-lux)] text-[color:var(--text-muted)] shadow-[var(--shadow-inset)]">
<span className="grid size-tap shrink-0 place-items-center rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-lux)] text-[color:var(--text-muted)] shadow-[var(--shadow-inset)]">
<Icon aria-hidden="true" className="h-4 w-4" />
</span>
);
Expand Down Expand Up @@ -971,7 +971,7 @@ function SegmentedControl<T extends string>({
aria-checked={checked}
onClick={() => onChange(option.value)}
className={cn(
"flex min-h-8 flex-auto items-center justify-center gap-1.5 whitespace-nowrap rounded-full px-3 text-xs font-semibold leading-none transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[color:var(--focus)]",
"flex min-h-tap flex-auto items-center justify-center gap-1.5 whitespace-nowrap rounded-full px-3 text-xs font-semibold leading-none transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[color:var(--focus)]",
checked
? "bg-[color:var(--clinical-accent)] text-[color:var(--clinical-accent-contrast)] shadow-[var(--shadow-tight)] forced-colors:outline forced-colors:outline-2 forced-colors:[outline-color:Highlight]"
: "text-[color:var(--text-muted)] hover:text-[color:var(--text-heading)]",
Expand Down Expand Up @@ -1208,7 +1208,7 @@ function SettingsProviderRow({

function SettingsClinicalContextStrip({ jurisdictionShort }: { jurisdictionShort: string }) {
return (
<div className="mt-2.5 flex min-h-8 items-center gap-2 rounded-full border border-[color:var(--clinical-accent)]/14 bg-[color:var(--clinical-accent-soft)]/60 px-3 text-xs font-semibold leading-none text-[color:var(--clinical-accent)] lg:hidden">
<div className="mt-2.5 flex min-h-tap items-center gap-2 rounded-full border border-[color:var(--clinical-accent)]/14 bg-[color:var(--clinical-accent-soft)]/60 px-3 text-xs font-semibold leading-none text-[color:var(--clinical-accent)] lg:hidden">
<ShieldCheck aria-hidden="true" className="h-3.5 w-3.5 shrink-0" />
<span className="min-w-0 truncate">
Private<span className="hidden min-[360px]:inline"> workspace</span>{" "}
Expand Down
15 changes: 13 additions & 2 deletions src/components/clinical-dashboard/use-theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,19 @@ function syncThemeColorMetadata(theme: ResolvedTheme) {
}

function applyResolvedTheme(theme: ResolvedTheme) {
document.documentElement.classList.toggle("dark", theme === "dark");
syncThemeColorMetadata(theme);
const isCurrentlyDark = document.documentElement.classList.contains("dark");
const willBeDark = theme === "dark";

if (isCurrentlyDark !== willBeDark) {
document.documentElement.classList.add("theme-transitioning");
document.documentElement.classList.toggle("dark", willBeDark);
syncThemeColorMetadata(theme);
window.setTimeout(() => {
document.documentElement.classList.remove("theme-transitioning");
}, 200);
} else {
syncThemeColorMetadata(theme);
}
}

function subscribeTheme(onStoreChange: () => void) {
Expand Down
Loading
Loading