From d6f190207f101c38e074cbae5fc2507a76781f6a Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Thu, 30 Jul 2026 22:52:57 +0800
Subject: [PATCH 1/3] test(ui): cover component state transitions
---
docs/outstanding-issues.md | 2 +-
.../document-search-record-fault.dom.test.tsx | 52 +++++++++++
tests/mode-action-popup.dom.test.tsx | 91 +++++++++++++++++++
3 files changed, 144 insertions(+), 1 deletion(-)
create mode 100644 tests/mode-action-popup.dom.test.tsx
diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md
index 447970d839..d652400446 100644
--- a/docs/outstanding-issues.md
+++ b/docs/outstanding-issues.md
@@ -141,7 +141,6 @@ removed after current-main verification; it is not missing recommended work.
| #103 | P2 | issue | Wide table-facts trigram index missing from `schema.sql` | **Outcome:** the migration chain and `schema.sql` agree on `document_table_facts` trigram indexes. `supabase/migrations/20260714190000_document_table_facts_trgm_idx.sql` creates a wide 5-column trigram index that is **absent from `supabase/schema.sql`**, so local replay and the live database can diverge. Distinct from #102: different owner and verification path. **Next:** confirm whether the wide index exists live, then take one of exactly two routes — **retained:** mirror `document_table_facts_text_trgm_idx` into `supabase/schema.sql` beside the narrow one and regenerate `drift-manifest.json`; **redundant:** drop it through a new forward migration, never by deleting `20260714190000`. **`drift-allowlist.json` is NOT a third option** (PR #1377 review): its own header scopes it to _"Known live-vs-`schema.sql` divergence"_, so it can silence a live drift finding but cannot reconcile the migration chain with the mirror — a fresh `supabase db reset` still runs `20260714190000` and creates the index while `schema.sql` still omits it, leaving this row's stated outcome unmet. **No offline gate catches this today:** the migration↔`schema.sql` parity test (`tests/drift-detection.test.ts:59-68`) only asserts one migration's `schema_drift_snapshot` function definition, not an index inventory — which is why this sits open rather than red in CI, and why a replay-to-schema inventory comparison is the check that would have caught it. Note the narrow `document_table_facts_title_row_param_trgm_idx` (`schema.sql:6425`) is the one the effective RPC expression (`:6726`) actually matches, so the wide index may be genuinely redundant — do not drop it without live scan evidence, per the monitored-not-auto-fixed index policy. | `docs/audit/latency-audit-2026-07-28.md` limitations; `npm run check:drift` | 2026-07-29 |
| #105 | P3 | task | Verify the `#017`-exempt client latency wins in a browser | **Outcome:** the two zero-payload client fixes are confirmed in a real browser. `#017` gates _payload_ decisions (#012/#013/#016 are all byte-count items); a `loading` fallback ships zero bytes and a resource hint ships ~60, so neither can be justified or refuted by a Lighthouse number — that is why these were not held behind #017. **Implementation shipped 2026-07-29; browser verification still PENDING:** 10 of 11 `ssr:false` dashboard surfaces had NO `loading` fallback and rendered nothing between HTML arrival and chunk execution — all now use the shared `LoadingPanel` (`role="status"` + accessible label); Supabase `preconnect`/`dns-prefetch` added, since `AuthProvider` awaits a cross-origin `getUser()` on mount that every auth-gated fetch queues behind and there were no resource hints anywhere in `src/`. Shipped with `verify:cheap` + `verify:pr-local` only. **Next:** run `npm run verify:ui` once the heavy-run lock is free, and confirm the preconnect appears in `
` on a live page. **Stop:** the two sidebar dialogs are intentionally excluded — they mount on open, so a fallback would render into a closed dialog. | `docs/audit/latency-audit-2026-07-28.md` L3-4/L3-5; `src/components/clinical-dashboard/clinical-dashboard-lazy.tsx` | 2026-07-29 |
| #106 | P2 | rec | Ingestion worker and indexing agent are verified by grepping their own source | **Outcome:** the ingestion worker and indexing agent are verified by executing code, not by asserting on their own source text. **Detail:** measured 2026-07-29 via `npm run test:coverage` — `worker/main.ts` (2,015 lines) and `supabase/functions/indexing-v3-agent/index.ts` (1,966 lines) each report **0% executed lines**; no test imports either module. Both are covered only by `readFileSync` + `toContain` assertions in `worker-safe-logging.test.ts`, `worker-visual-capture.test.ts` and `document-metadata-merge.test.ts`, which pass whenever a string is present and break on harmless refactors; `document-metadata-merge.test.ts` additionally reimplements the SQL deep-merge in TypeScript and tests the reimplementation rather than the worker. Area totals: `worker/` 18.6% lines, `supabase/functions/` 4.5%. **Next:** continue the extraction pattern that already works here — `indexing-v3-agent/behavior.ts` (167 lines, 96%) and `ingestion-worker/auth.ts` (30 lines, 90%) — pulling the highest-risk decision points out of `worker/main.ts` (job claim/retry, generation commit, failure classification) into importable modules with executing tests, retiring the matching source-text assertion as each lands. Roughly cost-neutral: each extracted test replaces a grep assertion. **Stop:** do not try to make the 2,000-line entrypoint importable in one pass; extract incrementally and keep each step green. | session 2026-07-29 test-coverage analysis | 2026-07-29 |
-| #107 | P2 | rec | Component state matrices are the largest untested surface | **Outcome:** loading / empty / error / disabled states on interactive components are covered by executing tests, not only by E2E happy paths. **Detail:** measured 2026-07-29 — production components (excluding mockups) sit at **38.2% lines / 22.8% branch** across 12,602 lines, with **83 of 208 files at zero executed lines**; there are 51 `.dom.test.tsx` files against 195 components. Playwright does visit these routes, so they are smoke-covered, but branch coverage is where the state matrix lives and smoke journeys rarely reach it. Worst by uncovered lines: `global-search-shell.tsx` (7%), `mode-action-popup.tsx` (21%), `answer-content.tsx` (27%), `document-search-results.tsx` (32%), `universal-search-command-surface.tsx` (39%), `master-search-header.tsx` (43%). A concrete first target with clinical meaning: `calculator-ui.tsx` now covers all exported scoring logic, but `seedCheckboxDefaults`, `toggleCheckboxAnswer` and `selectOptionAnswer` stay uncovered because they are module-private and only reachable through React event handlers — `seedCheckboxDefaults` is what makes an all-negative CAGE / SAD PERSONS screen read as a valid 0 rather than incomplete, so a regression there is a false-negative risk. **Next:** treat as a per-PR convention rather than a backfill push — `docs/testing.md` already prescribes the state matrix, so the gap is enforcement. Start with `global-search-shell.tsx`, which `docs/search-chrome-behaviour.md` treats as a contract surface. Keep additions in the jsdom tier (measured ~0.54s per file) instead of new Playwright journeys (~231 production journeys already run serially at `workers: 1` against a 45-minute CI budget). **Stop:** do not chase the coverage percentage by backfilling low-risk components; the re-ratcheted broad floor in `vitest.config.mts` holds the line. | session 2026-07-29 test-coverage analysis | 2026-07-29 |
| #110 | P3 | task | Design-system project token manifest lags its stylesheet | **Outcome:** the claude.ai/design token panel matches the shipped stylesheet. **Detail:** PR #1375 pushed a recompiled `_ds_bundle.css` (Clinical Sky, `--e0`–`--e4`, 4px radius grid, `--tracking-eyebrow`/`--leading-display`/`--leading-prose`) plus the four changed guideline docs to project `08d6f126`, but `_ds_manifest.json` is converter-generated and still advertises `--text-4xs: 0.5rem`, the old `--radius-lg/xl/2xl` values, and `--tw-leading`/`--tw-tracking` entries scoped to the retired `.leading-[…]` / `.tracking-[0.08em]` utilities. Rendering is correct; only the token inventory lags. Hand-editing was rejected — `kind`/`scope`/`annotation` are converter heuristics and a wrong panel is worse than a stale one. **Next:** in a session with the `/design-sync` skill, `npm ci`, then `npm install --prefix .ds-sync --no-save --package-lock=false esbuild ts-morph @types/react @tailwindcss/cli geist`, read `.design-sync/NOTES.md`, and run `resync.mjs --remote` so bundle and manifest regenerate together. **Stop:** do not hand-author `_ds_manifest.json`; the converter is not a published npm package and ships with the skill. | PR #1375; `.design-sync/NOTES.md`; project `08d6f126` (`_ds_needs_recompile` marker present) | 2026-07-29 |
| #121 | P3 | issue | Container Playwright browser build lags the pinned client | **Outcome:** browser gates run in remote sessions without hand-patching. **Evidence 2026-07-30:** the repo's Playwright client resolves headless-shell build `1234`; the container image provides `1194` at `/opt/pw-browsers`, so every browser test fails at launch. Worked around in-session by symlinking `chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell` to the `1194` `headless_shell` binary plus its sibling resources — container-local, nothing committed, and it disappears with the session. `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` means the mismatch cannot self-heal. **Next:** decide whether the image pins the browser build or the repo pins a client matching the image; until then any remote session claiming browser proof must state which it used. | `docs/testing.md`; container `/opt/pw-browsers` | 2026-07-30 |
| #122 | P2 | issue | `ci/circleci: verify` fails on every branch and its log needs operator access | **Outcome:** the CircleCI status is trustworthy signal again, or it stops reporting. **Evidence 2026-07-30:** `ci/circleci: verify` was `failure` on every open PR sampled — #1396, #1407, #1405, and #1400, which is a **docs-only** `AGENTS.md` change — plus #1403's head. It is sharply bounded in time: #1393's head **passed** at build 638 (03:57), and builds 645 (04:09) onward all failed. The job's entire contents were mirrored locally on PR #1396's exact tip and every part is green — `format:check` clean, `lint` exit 0, `typecheck` exit 0, `npm run test` `432 passed (432)` / `4473 passed \| 4 skipped`, and the PyMuPDF-gated `tests/pdf-extractor.test.ts` (the repo's only `process.env.CI`-gated tests) `6 passed (6)` under a locally built `PyMuPDF==1.28.0` venv with `PYTHON_BIN` set exactly as `.circleci/config.yml` does. So the failure is in the job's **environment**, not repo code. Around 40 builds fired in ~40 minutes across 8 open PRs in that window, so credit/quota exhaustion is the leading hypothesis — **explicitly unverified**: the CircleCI project is private and no CircleCI token is available to any agent session, and `api/v1.1/project/gh/BigSimmo/Database/` returns `Build not found` unauthenticated. **Next:** an operator opens one failing build and reads the failing step; if it is quota, either raise it or remove the CircleCI status so it stops masking real reds. **Stop:** do not chase this from a PR branch — it is not branch-specific, and no agent can read the log. Do not go looking for a CircleCI token. | `.circleci/config.yml`; PR #1396 session 2026-07-30 | 2026-07-30 |
@@ -174,6 +173,7 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th
| ID | Type | Summary | Outcome | Resolved |
| ---- | ----- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
+| #107 | rec | Component state matrices are the largest untested surface | RESOLVED 2026-07-30. The state-matrix convention is enforced through executing jsdom coverage on two previously weak, high-branch interactive surfaces rather than a low-value percentage backfill. `DocumentSearchResultsPanel` now proves loading → settled-empty transition, empty-query home actions, unavailable/error reporting, registry loading/error/unauthorized states, and successful governed results. `ModeActionPopup` now proves closed → open → action-close, keyboard entry/navigation/Escape focus restoration, and disabled mode selection. Together with the existing `docs/testing.md` per-PR state-matrix requirement, the re-ratcheted broad component coverage floor, and the source-preview lifecycle suite, this closes the recommendation without adding serial Playwright journeys or chasing low-risk components. | 2026-07-30 |
| #095 | issue | `PR required` reports failure for concurrency-cancelled jobs | RESOLVED 2026-07-30. The `pr-required` aggregate now distinguishes a cancelled job from a failed one. `require_success` / `require_skipped_or_success` are thin wrappers over one `record()` collector that reads **each job's own `result`** and appends to a `failures` or `cancellations` array; both arrays are filled before anything is reported. **Genuine failures win:** every failure is emitted as its own `::error::` and a concurrent cancellation is demoted to a `::warning::`, so a run that is cancelled AND broken cannot read as an excuse (refinement reported by Codex on PR #1409). Cancelled with nothing failing stays **RED**, and the message states the two possibilities rather than asserting supersession: it points the reader at a newer `PR required` run on the PR's current head SHA, and says that if there is none the run was cancelled by hand and must be re-run rather than merged past. It deliberately does NOT read the workflow-level cancelled status function — an earlier revision passed that through an `env:` value, which is invalid because GitHub allows those functions only in `if:` conditions, so the whole file failed to parse, ran as `.github/workflows/ci.yml` instead of `CI`, and created zero jobs; valid YAML but invalid Actions schema, so prettier and every local gate passed it. **The tempting fix was rejected as unsafe:** treating `cancelled` as neutral, or skipping the aggregate via a not-cancelled condition, would make the red disappear, but GitHub counts a SKIPPED required check as PASSING, so a hand-cancelled run on the current head would become mergeable with nothing verified — `if: always()` is therefore deliberate. Guarded by ten cases in `tests/ci-cache-safety.test.ts` that EXECUTE the extracted aggregate script under synthetic job results rather than grepping the YAML. **Corrected in review (Codex, PR #1428):** the first version of this record described a shared `cancelled_error` helper that never existed and claimed the error names the newest run; both were carried over from an obsolete sentence in the open row and neither matched the shipped implementation. Source: PR #1316 runs 30340972329 / 30341225585; PR #1409 | 2026-07-30 |
| #096 | task | PR #1316 review follow-ups — adoption-gate coverage closed | RESOLVED 2026-07-30. Every sub-item is dispositioned. The band adoption gate's root-path gap closed on PR #1394 — root and href-less modes now resolve to `src/app/(search-app)/page.tsx` — and closing it surfaced two further defects in the same gate the original finding did not name: the hand-rolled walk was capped at two import hops where the root route's real chain is four, and it followed neither `layout.tsx` (where that route's band actually comes from) nor `dynamic(() => import(...))` (how the dashboard code-splits its mode workspaces). All three were fixed together with a bounded BFS, each verified load-bearing by reverting it and watching the gate fail. Four findings — favourites hub counts, the document-search status derivation, the 401 session-expiry path, the record-path duplicate notice — were already fixed independently. The Therapy Compass retry-waiter finding was corrected to NOT a live defect: `retryWaitersRef` is genuinely unscoped but no caller observes it. The seven Codex follow-up SHAs remain unreachable and were never pushed; the PR #1316 review threads are the durable source. Archived by the 2026-07-30 triage pass. Source: PR #1316 review sweep | 2026-07-30 |
| #104 | rec | Worker's triple image read is deliberate, not debt | NOT DEBT — archived so a fourth audit does not re-file it. The 2026-07-28 latency audit listed L4-2 (`worker/main.ts` reads each extracted image up to 3x per document — hash, caption on cache miss, upload) as "CONFIRMED with no fix evidence", carried forward from the 2026-07-01 audit's L11. **That was wrong.** The 2026-07-01 disposition table already recorded it as a deliberate peak-memory trade-off, and the rationale is documented in place at `worker/main.ts:866-869`: holding every extracted image Buffer for a document with hundreds of multi-MB page images would multiply the worker's peak memory, and disk I/O is the cheaper resource for a background pipeline. The three reads (`:872`, `:1034`, `:1129`) are real but accepted. This row carried no next action, so it is archived rather than left open — revisit only if ingestion throughput becomes a measured complaint AND a bounded-buffer design is proposed. **Stop:** do not re-file this as debt. Source: `docs/audit/repo-audit-2026-07-01.md` L11 + disposition table | 2026-07-30 |
diff --git a/tests/document-search-record-fault.dom.test.tsx b/tests/document-search-record-fault.dom.test.tsx
index 8b22e78783..a8f70c1250 100644
--- a/tests/document-search-record-fault.dom.test.tsx
+++ b/tests/document-search-record-fault.dom.test.tsx
@@ -1,4 +1,5 @@
import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { DocumentSearchResultsPanel } from "@/components/clinical-dashboard/document-search-results";
@@ -123,3 +124,54 @@ describe("document search record path fault reporting", () => {
expect(screen.getByRole("button", { name: `Answer from ${lithiumMatch.title}` })).toBeInTheDocument();
});
});
+
+describe("document search state matrix", () => {
+ const documentProps = {
+ ...baseProps,
+ showRecordMatches: false,
+ recordMatches: [],
+ };
+
+ it("announces loading and replaces it with the empty result after the request settles", () => {
+ const { rerender } = render();
+
+ const loadingLabel = screen.getByText("Finding matching documents");
+ expect(loadingLabel.closest('[role="status"]')).toBeInTheDocument();
+ expect(screen.queryByText("No matching documents")).not.toBeInTheDocument();
+
+ rerender();
+ expect(screen.queryByText("Finding matching documents")).not.toBeInTheDocument();
+ expect(screen.getByText("No matching documents")).toBeInTheDocument();
+ });
+
+ it("renders the document home for an empty query and wires every escape action", async () => {
+ const user = userEvent.setup();
+ const onOpenRecentDocuments = vi.fn();
+ const onOpenLibrary = vi.fn();
+ const onOpenSourcePdf = vi.fn();
+ render(
+ ,
+ );
+
+ expect(screen.getByTestId("document-search-empty-state")).toBeInTheDocument();
+ await user.click(screen.getByRole("button", { name: /Recent documents/i }));
+ await user.click(screen.getByRole("button", { name: /Browse sources/i }));
+ await user.click(screen.getByRole("button", { name: /Open a source PDF/i }));
+ expect(onOpenRecentDocuments).toHaveBeenCalledTimes(1);
+ expect(onOpenLibrary).toHaveBeenCalledTimes(1);
+ expect(onOpenSourcePdf).toHaveBeenCalledTimes(1);
+ });
+
+ it("reports an unavailable document search alongside the empty-result guidance", () => {
+ render();
+
+ expect(screen.getByRole("alert")).toBeInTheDocument();
+ expect(screen.getByText(/No matching documents/)).toBeInTheDocument();
+ });
+});
diff --git a/tests/mode-action-popup.dom.test.tsx b/tests/mode-action-popup.dom.test.tsx
new file mode 100644
index 0000000000..92d74192d7
--- /dev/null
+++ b/tests/mode-action-popup.dom.test.tsx
@@ -0,0 +1,91 @@
+import { useState } from "react";
+
+import { render, screen, waitFor } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { Search, UploadCloud } from "lucide-react";
+import { describe, expect, it, vi } from "vitest";
+
+import {
+ ModeActionPopup,
+ modeActionItemsFor,
+ type ModeActionModeOption,
+} from "@/components/clinical-dashboard/mode-action-popup";
+
+const modeOptions: ModeActionModeOption[] = [
+ { id: "documents", label: "Documents", icon: Search },
+ { id: "upload", label: "Upload", icon: UploadCloud, disabled: true },
+];
+
+function Harness({ onAction = vi.fn(), onModeSelect = vi.fn() }) {
+ const [open, setOpen] = useState(false);
+
+ return (
+
+ );
+}
+
+describe("ModeActionPopup state transitions", () => {
+ it("starts closed, opens its action menu, and closes after an action", async () => {
+ const user = userEvent.setup();
+ const onAction = vi.fn();
+ render();
+
+ const trigger = screen.getByRole("button", { name: "Open document actions" });
+ expect(trigger).toHaveAttribute("aria-expanded", "false");
+ expect(screen.queryByRole("menu", { name: "Documents" })).not.toBeInTheDocument();
+
+ await user.click(trigger);
+ expect(trigger).toHaveAttribute("aria-expanded", "true");
+ expect(screen.getByRole("menu", { name: "Documents" })).toBeInTheDocument();
+
+ await user.click(screen.getByRole("menuitem", { name: "Upload PDF" }));
+ expect(onAction).toHaveBeenCalledWith("documents-upload");
+ expect(screen.queryByRole("menu", { name: "Documents" })).not.toBeInTheDocument();
+ expect(trigger).toHaveAttribute("aria-expanded", "false");
+ });
+
+ it("opens from the keyboard and moves focus through the action list", async () => {
+ const user = userEvent.setup();
+ render();
+
+ const trigger = screen.getByRole("button", { name: "Open document actions" });
+ trigger.focus();
+ await user.keyboard("{ArrowDown}");
+
+ const first = screen.getByRole("menuitem", { name: "Upload PDF" });
+ await waitFor(() => expect(first).toHaveFocus());
+ await user.keyboard("{End}");
+ expect(screen.getByRole("menuitem", { name: "Open source PDF" })).toHaveFocus();
+ await user.keyboard("{Escape}");
+ expect(screen.queryByRole("menu", { name: "Documents" })).not.toBeInTheDocument();
+ await waitFor(() => expect(trigger).toHaveFocus());
+ });
+
+ it("exposes disabled modes but never selects them", async () => {
+ const user = userEvent.setup();
+ const onModeSelect = vi.fn();
+ render();
+
+ await user.click(screen.getByRole("button", { name: "Open document actions" }));
+ const modeTrigger = screen.getByRole("button", { name: "Documents" });
+ await user.click(modeTrigger);
+
+ expect(screen.getByRole("menu", { name: "Choose search mode" })).toBeInTheDocument();
+ expect(screen.getByRole("menuitemradio", { name: "Documents" })).toHaveAttribute("aria-checked", "true");
+ const disabledMode = screen.getByRole("menuitemradio", { name: "Upload" });
+ expect(disabledMode).toBeDisabled();
+ await user.click(disabledMode);
+ expect(onModeSelect).not.toHaveBeenCalled();
+ });
+});
From 482728dd9803184230e8a8ed7e9883b5231a0fa0 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Fri, 31 Jul 2026 01:31:59 +0800
Subject: [PATCH 2/3] test(ui): cover enabled mode selection
---
tests/mode-action-popup.dom.test.tsx | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tests/mode-action-popup.dom.test.tsx b/tests/mode-action-popup.dom.test.tsx
index 92d74192d7..d9dcad1193 100644
--- a/tests/mode-action-popup.dom.test.tsx
+++ b/tests/mode-action-popup.dom.test.tsx
@@ -13,6 +13,7 @@ import {
const modeOptions: ModeActionModeOption[] = [
{ id: "documents", label: "Documents", icon: Search },
+ { id: "answer", label: "Answer", icon: Search },
{ id: "upload", label: "Upload", icon: UploadCloud, disabled: true },
];
@@ -72,7 +73,7 @@ describe("ModeActionPopup state transitions", () => {
await waitFor(() => expect(trigger).toHaveFocus());
});
- it("exposes disabled modes but never selects them", async () => {
+ it("selects enabled modes and exposes disabled modes without selecting them", async () => {
const user = userEvent.setup();
const onModeSelect = vi.fn();
render();
@@ -87,5 +88,10 @@ describe("ModeActionPopup state transitions", () => {
expect(disabledMode).toBeDisabled();
await user.click(disabledMode);
expect(onModeSelect).not.toHaveBeenCalled();
+
+ await user.click(screen.getByRole("menuitemradio", { name: "Answer" }));
+ expect(onModeSelect).toHaveBeenCalledWith("answer");
+ expect(screen.queryByRole("menu", { name: "Choose search mode" })).not.toBeInTheDocument();
+ await waitFor(() => expect(modeTrigger).toHaveFocus());
});
});
From a727fa5f4357fecb2fdf24afd4f53dd1c59f1aae Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Fri, 31 Jul 2026 02:13:33 +0800
Subject: [PATCH 3/3] docs: record PR #1469 review
---
docs/branch-review-ledger.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md
index 52016f8894..ff3bef69f2 100644
--- a/docs/branch-review-ledger.md
+++ b/docs/branch-review-ledger.md
@@ -252,3 +252,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie
| 2026-07-30 | PR #1477 | 26d713922006c1af8187994edfa76669dc14cd46 | PR #1477 fork-safe Codex autofix routing | Fixed fork routing to the PR head repository, added fail-closed metadata handling, reconciled current main, and found no remaining actionable defects. | check:codex-autofix-workflow; check:github-actions; check:pr-policy; check:outstanding-issues; check:branch-review-ledger; docs:check-inventory; docs:check-links; docs:check-scripts; typecheck; focused Vitest 53 passed; Prettier |
| 2026-07-30 | PR #1477 | 20f795da2d9d0adafa6cb3117429ab3665129c0d | PR #1477 fork-safe Codex autofix routing | Refreshed onto current main after #1465; issue and ledger reconciliation remained clean and no new actionable defects were introduced. | check:outstanding-issues; check:branch-review-ledger; check:codex-autofix-workflow; focused Vitest 53 passed |
| 2026-07-30 | PR #1480 | 6c1e76f53aee87be8408cebc295744fbdce05367 | PR #1480 bounded outstanding reliability fixes | Fixed both review findings: documented the dark accent role and added partial favourites retry without hiding valid counts; no other actionable defects found. | focused Vitest 119 passed; docs index; issue and ledger guards; Actions and Codex workflow guards; Prettier; diff check; typecheck coordinator-blocked |
+| 2026-07-30 | PR-1469 | 02108d5424f8a3ab50f45808a6cc3cbd872e7555 | PR #1469 component state matrix coverage | PASS after current-main sync; tests execute enabled and disabled popup transitions plus document search loading, empty, and fault states | focused Vitest 2 files, 10 tests passed; outstanding-issues and branch-review-ledger guards passed; no unresolved review threads |