diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 65d57120e..3da1d205e 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -647,3 +647,4 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-20 | claude/clinical-kb-pwa-review-asi3wb (restarted; PR: ci.yml concurrency) | 79aaacf04e64f753c480dd957a81ac9be6acbb43 | CI workflow concurrency: stop main churn cancelling dispatch/schedule runs | One-line group expression: workflow_dispatch/schedule events now get a per-run concurrency group (github.run_id) while push/PR keep the shared ref group with cancel-in-progress — fixes the release-browser-matrix livelock (cancelled twice on 2026-07-19/20 by main merges mid-run; the weekly Sunday 18:00 UTC scheduled run was subject to the same cancellation). release-browser-matrix is not a required branch-protection check; pin/scope checkers do not constrain the concurrency block. Accepted side effect: deliberate runs can overlap push runs. Rollback: plain revert. | check:github-actions PASS; check:ci-scope PASS; format:check PASS (repo files; local-only .claude/settings.local.json warning is gitignored) | | 2026-07-20 | claude/clinical-kb-pwa-review-asi3wb (restarted; PR: #901 rerank regression closeout) | fd5dcd1582e1c7aa8bf92ff097575e0c68aa930d | #901 rerank regression: baseline re-eval + offline guard net + saturated-tie fix | Baseline eval-canary dispatch on remediated main b3ae061 (run 29731533081) PASSED — golden retrieval 36/36 + answer-quality green, confirming #913-#926 closed the live 4/36 regression (July 19 failing run tested the raw #901 state; fixes landed 1-8h after it, never re-evaled until now). Residual defect found by new offline repro and fixed: with byte-identical imputed fast-path primaries, selection's clamped keys tie exactly and the chunk-id fallback decides, which secondStageScore's position adjustment launders into releaseRankScore (buried the CIWA answer doc in repro); fix = contentRankScore carried on RetrievalCandidate as tie-break between clamped rerankScore and chunk id, never added to scores. Amended #901's own never-live-validated saturated-tie pin to content-rank-then-id. Guards: tests/rag-fast-path-ordering.test.ts (4 end-to-end eval-shape repros) + ranking-tuning gates (4 golden-mapped hard negatives at production weights + full-snapshot high-risk). Fixture aliases NOT changed (Step C not triggered — identity cases pass live). | Targeted 4-suite vitest 37/37; npm run test 2981 passed (sole fail = container-only pdf-extraction-budget artifact); verify:cheap green through all 18 pre-test checks incl. lint+typecheck; check:production-readiness PASS on substantive checks (2 FAILs = documented missing-secret class in this container); build + client-bundle scan + check:rag:fixtures PASS | | 2026-07-20 | claude/clinical-kb-pwa-review-asi3wb (restarted; PR: coverage tie-break follow-up) | 57ec880b306a8e2b31c5f20dacc47256fc93b4e2 | Post-merge live-eval finding on #982 + corrective fix: saturated-tie key rankScore → query-term coverage | Post-#982 golden dispatch (eval-canary run #50, 29735004222, main b9057f0 + deps) came back 35/36: the three verifiable July-19 failures (lithium-therapy-monitoring, clozapine-anc-threshold, patient-safety-plan-include) all PASS live, but alcohol-ciwa-threshold flipped pass→FAIL vs the same-morning pre-#982 run #49 (29731533081, 36/36) — failing top-3 ordered by descending rankScore (1.85/1.75/1.53, all finalScore-saturated, releaseRankScore 1.09/1.086/1.07), i.e. #982's tie-break let generic clinicalSignalBoost stacking outvote the ciwa/score/threshold-bearing chunk; #982 is the only retrieval-path delta in the window. Fix: contentRankScore → contentCoverageScore sourced from lexicalCoverageScore (query-term coverage, immune to boost stacking; ties still fall to chunk id); saturated-tie contract test re-pinned so coverage beats a HIGHER rankScore (discriminating — old key fails it); fast-path CIWA guard gains the run-#50 screening-chunk shape + content-term assertion. Live validation: tonight's 18:00 UTC scheduled canary (dispatch cap 2/2 spent ≈$2-4). Separately: ci.yml dispatch 4012 survived 30+ min of main churn under #979's per-run concurrency group (fix working); duplicate dispatch 4017 cancelled. | Targeted vitest 38/38; npm run test 3012 passed / 1 known container-only pdf-budget artifact; verify:cheap green to the same artifact; build + client-bundle scan + check:rag:fixtures PASS; check:production-readiness expected missing-secret FAILs only (no secrets in container) | +| 2026-07-20 | claude/clinical-kb-pwa-review-asi3wb (restarted; PR: WebKit matrix diagnosis) | caa298972846ef52d472bfbff98c90fc26481ed8 | First completed release-browser-matrix in weeks (run 4012, main b9057f0, 38 min uncancelled under #979) — full triage of 28 failures + fix for the dominant cluster | Run 4012: 716 passed / 28 failed (25 webkit, 3 firefox) / 4 skipped. Dominant cluster (root-caused, FIXED here): all 8 ui-universal-search webkit failures share one signature — typeahead content never enters the DOM — because commandDropdownCanDisplay (added 42a3e3c 2026-07-17, AFTER the last completed matrix; never ran on WebKit until 4012) requires fine-pointer OR zero-touch; headless browsers fail the (hover:hover)+(pointer:fine) query (proven by the zero-touch escape existing for CI at all), Chromium/Firefox pass via maxTouchPoints===0, and Playwright's Linux WebKit build advertises phantom touch points — flunking both branches and disabling useUniversalSearch entirely. Fix: beforeEach addInitScript in ui-universal-search/ui-smoke/ui-tools specs stubbing Navigator.prototype.maxTouchPoints to the runner's true 0 (inert on Chromium/Firefox; product gate + tests/search-command-surface.test.ts pins untouched). Local chromium runs of ui-universal-search fail 16/20 IDENTICALLY on unmodified main (container artifact — hosted CI chromium green in 4012 is authoritative; verified by stash/run/pop baseline). Expected delta next matrix: ≥8 webkit failures clear; candidates ui-tools:1244 + several ui-smoke answer-flow cases (same surface). NOT yet root-caused (triaged remainder, hosted-matrix-only reproduction): webkit ui-stress overflow 330/409 (360px overflow at mobile), webkit ui-accessibility 195/252 (focus dismissal; forced-colors labels), webkit ui-formulation 132, webkit ui-smoke copy-table/retry/recovery/recent-searches/source-only/differential-context/viewer-hydration/document-questions, webkit ui-tools 2087 (goto interrupted by ?q= navigation), webkit ui-universal-search 183 strict-mode duplicate options (fallback-surface rendering; likely clears with the gate fix), firefox ui-smoke 946/2206/2932 (@critical document search fails on BOTH firefox and webkit — cross-browser, highest-priority remainder). | prettier PASS; eslint PASS; typecheck PASS; chromium stash-baseline no-delta (16 fail pre AND post — container artifact); webkit validation lands via tonight's 18:00 UTC scheduled matrix | diff --git a/tests/ui-accessibility.spec.ts b/tests/ui-accessibility.spec.ts index e74fae50f..9c642b719 100644 --- a/tests/ui-accessibility.spec.ts +++ b/tests/ui-accessibility.spec.ts @@ -178,6 +178,16 @@ async function expectNoBlockingAxeViolations(page: Page, testInfo: TestInfo, opt expect(summary, "axe found critical/serious WCAG A/AA violations").toEqual([]); } +// Playwright's Linux WebKit build advertises phantom touch points on the touch-free CI +// runner, tripping the fine-pointer/zero-touch gate on the search command surface +// (commandDropdownCanDisplay) that Chromium and Firefox pass via the zero-touch fallback. +// Report the runner's real capability so WebKit exercises the same desktop surfaces. +test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(Navigator.prototype, "maxTouchPoints", { configurable: true, get: () => 0 }); + }); +}); + test.describe("Clinical KB accessibility coverage", () => { test.describe.configure({ timeout: 60_000 }); diff --git a/tests/ui-formulation.spec.ts b/tests/ui-formulation.spec.ts index 2101eaf01..d5f6d5871 100644 --- a/tests/ui-formulation.spec.ts +++ b/tests/ui-formulation.spec.ts @@ -50,6 +50,13 @@ async function expectNoBlockingAxeViolations(page: Page, testInfo: TestInfo) { test.beforeEach(async ({ page }) => { await blockExternalRequests(page); + // Playwright's Linux WebKit build advertises phantom touch points on the touch-free CI + // runner, tripping the fine-pointer/zero-touch gate on the search command surface + // (commandDropdownCanDisplay) that Chromium and Firefox pass via the zero-touch fallback. + // Report the runner's real capability so WebKit exercises the same desktop surfaces. + await page.addInitScript(() => { + Object.defineProperty(Navigator.prototype, "maxTouchPoints", { configurable: true, get: () => 0 }); + }); }); test("searches patient language, opens a mechanism guide, and carries it into the builder", async ({ diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts index 8dd6d723a..2950034c1 100644 --- a/tests/ui-smoke.spec.ts +++ b/tests/ui-smoke.spec.ts @@ -873,6 +873,16 @@ async function openDailyActions(page: Page, triggerName: string | RegExp = /^Ope return menu; } +// Playwright's Linux WebKit build advertises phantom touch points on the touch-free CI +// runner, tripping the fine-pointer/zero-touch gate on the search command surface +// (commandDropdownCanDisplay) that Chromium and Firefox pass via the zero-touch fallback. +// Report the runner's real capability so WebKit exercises the same desktop surfaces. +test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(Navigator.prototype, "maxTouchPoints", { configurable: true, get: () => 0 }); + }); +}); + test.describe("Clinical KB UI smoke coverage", () => { test.describe.configure({ timeout: 60000 }); diff --git a/tests/ui-stress.spec.ts b/tests/ui-stress.spec.ts index c39d247ac..b3ef03f7d 100644 --- a/tests/ui-stress.spec.ts +++ b/tests/ui-stress.spec.ts @@ -321,6 +321,16 @@ async function openScopeControl(page: Page) { }).toPass({ timeout: 20_000 }); } +// Playwright's Linux WebKit build advertises phantom touch points on the touch-free CI +// runner, tripping the fine-pointer/zero-touch gate on the search command surface +// (commandDropdownCanDisplay) that Chromium and Firefox pass via the zero-touch fallback. +// Report the runner's real capability so WebKit exercises the same desktop surfaces. +test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(Navigator.prototype, "maxTouchPoints", { configurable: true, get: () => 0 }); + }); +}); + test.describe("Clinical KB long-content stress coverage", () => { for (const viewport of [ { name: "mobile", width: 320, height: 740 }, diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts index 7812a36b8..1d6e7765d 100644 --- a/tests/ui-tools.spec.ts +++ b/tests/ui-tools.spec.ts @@ -350,6 +350,16 @@ async function expectVerticalSeparation(page: Page, upperSelector: string, lower expect((metrics?.lowerTop ?? 0) - (metrics?.upperBottom ?? 0)).toBeGreaterThanOrEqual(minimumGap); } +// Playwright's Linux WebKit build advertises phantom touch points on the touch-free CI +// runner, tripping the fine-pointer/zero-touch gate on the search command surface +// (commandDropdownCanDisplay) that Chromium and Firefox pass via the zero-touch fallback. +// Report the runner's real capability so WebKit exercises the same desktop surfaces. +test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(Navigator.prototype, "maxTouchPoints", { configurable: true, get: () => 0 }); + }); +}); + test.describe("Clinical KB tools launcher", () => { test.describe.configure({ timeout: 60_000 }); diff --git a/tests/ui-universal-search.spec.ts b/tests/ui-universal-search.spec.ts index 689c3c09f..88b253b8e 100644 --- a/tests/ui-universal-search.spec.ts +++ b/tests/ui-universal-search.spec.ts @@ -115,6 +115,18 @@ async function openComposer(page: Page, href = "/?mode=documents&focus=1") { return input; } +// Playwright's Linux WebKit build advertises phantom touch points on the touch-free CI +// runner. That trips the deliberate fine-pointer/zero-touch gate on the search command +// surface (commandDropdownCanDisplay: headless browsers fail the fine-pointer media query +// and rely on the zero-touch fallback), silently hiding the desktop dropdown that Chromium +// and Firefox render — every typeahead assertion then fails with "element not found". +// Report the runner's real capability; the product gate and its unit pins stay unchanged. +test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(Navigator.prototype, "maxTouchPoints", { configurable: true, get: () => 0 }); + }); +}); + test.describe("universal search typeahead", () => { test("shows grouped cross-entity results while typing", async ({ page }) => { await mockUniversalSearch(page);