feat(differentials): polish search results UX and mobile compare bar#461
Conversation
- Show the query/match-count header band on phones too, not just desktop - Feature the best answer once on phones (compact card with Open page link) instead of duplicating it as the rank-1 list card - Surface the safety-first callout on phones as a slim strip - Condense the source-status warning into a single-row strip with an inline Run source search action - Replace the dead Sort and Filters buttons with a working sort (relevance / urgency first / A-Z) on phones and desktop - Fix the desktop "Compare top 3" button that actually re-ran the source search; it is now an honest Run source search action - Rebuild the phone compare action as a floating FAB-style pill anchored above the bottom search dock, with a count badge, a polite live region, and an empty-selection hint state - Reserve red for emergent status: match-quality labels now use the clinical accent, and the urgency rail no longer forces the first row to emergent - Bump selection toggles to 44px tap targets Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe differentials search UI now supports client-side sorting, updated responsive result cards, source-status states, and floating mobile comparison. Selected comparison IDs propagate through presentation routes into workflow selection. UI tests and the branch review ledger are also updated. ChangesDifferentials search experience
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SearchResultsView
participant PresentationRoute
participant WorkflowPage
SearchResultsView->>PresentationRoute: open presentation URL with ids
PresentationRoute->>WorkflowPage: pass selectedIds
WorkflowPage->>WorkflowPage: mark matching candidates selected
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error, 1 warning)
✅ Passed checks (9 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/clinical-dashboard/differentials-home.tsx`:
- Around line 153-166: The comparison navigation in the mobile action and the
corresponding action near the desktop comparison controls only forwards the
query via routeWithQuery, dropping selectedIds. Update both Link/action
destinations to serialize the selected identifiers into the URL or shared
comparison state, and ensure the comparison page reads that same representation
so selections are preserved and produce distinct destinations.
- Around line 961-971: Distinguish unsearched from searched-with-no-results in
the source evidence panel. In the component using hasSourceEvidence, derive a
separate sourcesChecked state from evidenceIsCurrent and whether documentMatches
is defined, then update the conditional UI around the source-search button and
the related section around the additional referenced lines to show the
zero-match state after a current search instead of the initial-search prompt.
- Around line 557-571: Update the “Best answer” card styling in the component
containing this section so routine results use neutral or accent border,
background, and icon colors instead of danger tokens. Reserve danger styling for
emergent status indicators such as StatusBadge, while preserving the existing
compact/non-compact layout behavior.
🪄 Autofix (Beta)
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 10487b7f-6e0b-4c4f-919e-089adc8b3bc0
📒 Files selected for processing (2)
docs/production-readiness-checklist.mdsrc/components/clinical-dashboard/differentials-home.tsx
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Fixes Applied SuccessfullyFixed 1 file(s) based on 3 unresolved review comments. Files modified:
Commit: The changes have been pushed to the Time taken: |
Fixed 1 file(s) based on 3 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
|
@codex, fix all failing CI testing |
Summary
Testing
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ff60bc5bd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
# Conflicts: # docs/branch-review-ledger.md # tests/ui-smoke.spec.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bd6642dc8a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ce84c4b22f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
All actionable threads are resolved on the current head; dismissing the stale blocking review for verified merge readiness.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/ui-smoke.spec.ts (1)
277-283: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winDo not turn invalid answer requests into valid mock requests.
src/app/api/answer/route.ts:32-80requires a trimmed, non-emptyquerywith a maximum length of 2000. Falling back to"What monitoring is required?"lets malformed client payloads receive a successful mock response, so smoke tests can miss a broken/api/answerrequest contract.Validate the query and fail the mock when it is absent, blank, or oversized.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/ui-smoke.spec.ts` around lines 277 - 283, Update the request handling around the `body.query` extraction and `onAnswerRequest` in the smoke-test route mock to validate that `query` is present, trimmed, non-empty, and no longer than 2000 characters; fail the mock for invalid values instead of substituting the default question, while preserving valid query handling.
🧹 Nitpick comments (1)
src/components/differentials/differential-presentation-workflow-page.tsx (1)
596-613: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueMinor: redundant double iteration over
candidates.
baseWorkflow.candidatesis iterated twice — once via.map(...)to setselected, once via.filter(...).lengthto computeselectedCount. Could be combined into a single pass, but with typically small candidate lists this has negligible practical impact.♻️ Optional consolidation
- const workflow = requestedIds.size - ? { - ...baseWorkflow, - candidates: baseWorkflow.candidates.map((candidate) => ({ - ...candidate, - selected: requestedIds.has(candidate.slug), - })), - selectedCount: baseWorkflow.candidates.filter((candidate) => requestedIds.has(candidate.slug)).length, - } - : baseWorkflow; + const workflow = requestedIds.size + ? (() => { + const candidates = baseWorkflow.candidates.map((candidate) => ({ + ...candidate, + selected: requestedIds.has(candidate.slug), + })); + return { + ...baseWorkflow, + candidates, + selectedCount: candidates.filter((candidate) => candidate.selected).length, + }; + })() + : baseWorkflow;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/differentials/differential-presentation-workflow-page.tsx` around lines 596 - 613, Consolidate the `baseWorkflow.candidates` processing in the workflow construction into a single pass, deriving both the mapped candidates with `selected` flags and `selectedCount` together. Preserve the existing behavior for empty `requestedIds` and the current selection matching via `requestedIds.has(candidate.slug)`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/ui-smoke.spec.ts`:
- Around line 1866-1877: Update the route fulfillment handling in the request
mock so failures are ignored only for the expected aborted first request.
Preserve the catch for the delayed response when currentRequest equals 1, but
rethrow fulfillment errors for currentRequest greater than 1 so the current
response must reach the page.
- Around line 1890-1891: Replace the fixed waitForTimeout in the source-status
test with an explicit wait for the relevant response or route-completion signal,
then assert sourceStatus contains the final “Not yet checked” state. Preserve
the existing assertion and synchronize on the actual operation completing rather
than elapsed time.
---
Outside diff comments:
In `@tests/ui-smoke.spec.ts`:
- Around line 277-283: Update the request handling around the `body.query`
extraction and `onAnswerRequest` in the smoke-test route mock to validate that
`query` is present, trimmed, non-empty, and no longer than 2000 characters; fail
the mock for invalid values instead of substituting the default question, while
preserving valid query handling.
---
Nitpick comments:
In `@src/components/differentials/differential-presentation-workflow-page.tsx`:
- Around line 596-613: Consolidate the `baseWorkflow.candidates` processing in
the workflow construction into a single pass, deriving both the mapped
candidates with `selected` flags and `selectedCount` together. Preserve the
existing behavior for empty `requestedIds` and the current selection matching
via `requestedIds.has(candidate.slug)`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d3c6ef7e-9393-461a-88f7-77f46f3b7616
📒 Files selected for processing (7)
docs/branch-review-ledger.mdsrc/app/differentials/presentations/[slug]/page.tsxsrc/app/differentials/presentations/page.tsxsrc/components/clinical-dashboard/differentials-home.tsxsrc/components/differentials/differential-presentation-workflow-page.tsxtests/ui-smoke.spec.tstests/ui-tools.spec.ts
✅ Files skipped from review due to trivial changes (1)
- docs/branch-review-ledger.md
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/clinical-dashboard/differentials-home.tsx
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/ui-smoke.spec.ts (1)
277-283: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winDo not turn invalid answer requests into valid mock requests.
src/app/api/answer/route.ts:32-80requires a trimmed, non-emptyquerywith a maximum length of 2000. Falling back to"What monitoring is required?"lets malformed client payloads receive a successful mock response, so smoke tests can miss a broken/api/answerrequest contract.Validate the query and fail the mock when it is absent, blank, or oversized.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/ui-smoke.spec.ts` around lines 277 - 283, Update the request handling around the `body.query` extraction and `onAnswerRequest` in the smoke-test route mock to validate that `query` is present, trimmed, non-empty, and no longer than 2000 characters; fail the mock for invalid values instead of substituting the default question, while preserving valid query handling.
🧹 Nitpick comments (1)
src/components/differentials/differential-presentation-workflow-page.tsx (1)
596-613: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueMinor: redundant double iteration over
candidates.
baseWorkflow.candidatesis iterated twice — once via.map(...)to setselected, once via.filter(...).lengthto computeselectedCount. Could be combined into a single pass, but with typically small candidate lists this has negligible practical impact.♻️ Optional consolidation
- const workflow = requestedIds.size - ? { - ...baseWorkflow, - candidates: baseWorkflow.candidates.map((candidate) => ({ - ...candidate, - selected: requestedIds.has(candidate.slug), - })), - selectedCount: baseWorkflow.candidates.filter((candidate) => requestedIds.has(candidate.slug)).length, - } - : baseWorkflow; + const workflow = requestedIds.size + ? (() => { + const candidates = baseWorkflow.candidates.map((candidate) => ({ + ...candidate, + selected: requestedIds.has(candidate.slug), + })); + return { + ...baseWorkflow, + candidates, + selectedCount: candidates.filter((candidate) => candidate.selected).length, + }; + })() + : baseWorkflow;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/differentials/differential-presentation-workflow-page.tsx` around lines 596 - 613, Consolidate the `baseWorkflow.candidates` processing in the workflow construction into a single pass, deriving both the mapped candidates with `selected` flags and `selectedCount` together. Preserve the existing behavior for empty `requestedIds` and the current selection matching via `requestedIds.has(candidate.slug)`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/ui-smoke.spec.ts`:
- Around line 1866-1877: Update the route fulfillment handling in the request
mock so failures are ignored only for the expected aborted first request.
Preserve the catch for the delayed response when currentRequest equals 1, but
rethrow fulfillment errors for currentRequest greater than 1 so the current
response must reach the page.
- Around line 1890-1891: Replace the fixed waitForTimeout in the source-status
test with an explicit wait for the relevant response or route-completion signal,
then assert sourceStatus contains the final “Not yet checked” state. Preserve
the existing assertion and synchronize on the actual operation completing rather
than elapsed time.
---
Outside diff comments:
In `@tests/ui-smoke.spec.ts`:
- Around line 277-283: Update the request handling around the `body.query`
extraction and `onAnswerRequest` in the smoke-test route mock to validate that
`query` is present, trimmed, non-empty, and no longer than 2000 characters; fail
the mock for invalid values instead of substituting the default question, while
preserving valid query handling.
---
Nitpick comments:
In `@src/components/differentials/differential-presentation-workflow-page.tsx`:
- Around line 596-613: Consolidate the `baseWorkflow.candidates` processing in
the workflow construction into a single pass, deriving both the mapped
candidates with `selected` flags and `selectedCount` together. Preserve the
existing behavior for empty `requestedIds` and the current selection matching
via `requestedIds.has(candidate.slug)`.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d3c6ef7e-9393-461a-88f7-77f46f3b7616
📒 Files selected for processing (7)
docs/branch-review-ledger.mdsrc/app/differentials/presentations/[slug]/page.tsxsrc/app/differentials/presentations/page.tsxsrc/components/clinical-dashboard/differentials-home.tsxsrc/components/differentials/differential-presentation-workflow-page.tsxtests/ui-smoke.spec.tstests/ui-tools.spec.ts
✅ Files skipped from review due to trivial changes (1)
- docs/branch-review-ledger.md
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/clinical-dashboard/differentials-home.tsx
🛑 Comments failed to post (2)
tests/ui-smoke.spec.ts (2)
1866-1877: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Do not swallow fulfillment failures for the current response.
The delayed first request may legitimately be aborted after the URL changes, but
.catch(() => undefined)also hides failures for later/current responses. The test can therefore pass after incrementingrequestCounteven though the response being tested never reaches the page. Catch only the expected stale-request abort and rethrow failures forcurrentRequest > 1.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/ui-smoke.spec.ts` around lines 1866 - 1877, Update the route fulfillment handling in the request mock so failures are ignored only for the expected aborted first request. Preserve the catch for the delayed response when currentRequest equals 1, but rethrow fulfillment errors for currentRequest greater than 1 so the current response must reach the page.
1890-1891: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Synchronize on response completion instead of using a fixed sleep.
waitForTimeout(600)assumes the delayed response and subsequent React/router update complete within a 100ms margin. That can remain flaky under CI load. Wait for an explicit route/response completion signal, then assert the final source-status state.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/ui-smoke.spec.ts` around lines 1890 - 1891, Replace the fixed waitForTimeout in the source-status test with an explicit wait for the relevant response or route-completion signal, then assert sourceStatus contains the final “Not yet checked” state. Preserve the existing assertion and synchronize on the actual operation completing rather than elapsed time.
# Conflicts: # docs/branch-review-ledger.md # tests/ui-smoke.spec.ts # tests/ui-tools.spec.ts
# Conflicts: # docs/branch-review-ledger.md
All actionable findings fixed in 9d9d2cb: strict answer mock validation, current-response fulfillment propagation and explicit synchronization, workflow-aware selection routing, diagnosis-only comparison controls, and restored required smoke coverage. The low-value candidate iteration was also consolidated.
|
Resolved the remaining review set in |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/differentials.ts (1)
78-95: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win"Best match" selection favors raw overlap count over specificity.
matchCountis a raw count of matched candidates with no normalization bypresentation.candidates.length. A workflow with many candidates that happens to include a couple of the requested slugs will out-rank a smaller, more precise workflow whose candidates are exactly the requested ids. Ties are also silently broken by catalog iteration order with no documented rationale. Sincesrc/app/differentials/presentations/page.tsxroutes users directly based on this result, a less relevant workflow could win.Consider weighting by match ratio (e.g.,
matchCount / presentation.candidates.length) or preferring the presentation that fully coversrequestedIdsbefore falling back to raw count.♻️ Possible refactor to prefer specificity
let bestMatch: DifferentialPresentationWorkflow | null = null; - let bestMatchCount = 0; + let bestScore = 0; for (const presentation of differentialPresentations()) { const matchCount = presentation.candidates.reduce( (count, candidate) => count + (requestedIds.has(candidate.slug) ? 1 : 0), 0, ); - if (matchCount > bestMatchCount) { + if (matchCount === 0) continue; + const score = matchCount / presentation.candidates.length; + if (score > bestScore) { bestMatch = presentation; - bestMatchCount = matchCount; + bestScore = score; } } return bestMatch;Please confirm whether raw-count ranking (vs. specificity-weighted) is the intended semantics here before deciding to change it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/differentials.ts` around lines 78 - 95, Confirm the intended ranking semantics for getPresentationWorkflowForDiagnosisIds before changing implementation. If specificity is intended, rank presentations by match ratio (matchCount divided by presentation.candidates.length) or prioritize workflows fully covering requestedIds, and define a deterministic tie-breaker instead of relying on differentialPresentations() iteration order.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/lib/differentials.ts`:
- Around line 78-95: Confirm the intended ranking semantics for
getPresentationWorkflowForDiagnosisIds before changing implementation. If
specificity is intended, rank presentations by match ratio (matchCount divided
by presentation.candidates.length) or prioritize workflows fully covering
requestedIds, and define a deterministic tie-breaker instead of relying on
differentialPresentations() iteration order.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 449bd2c2-6217-447e-b466-697f6ad0af56
📒 Files selected for processing (7)
docs/branch-review-ledger.mdsrc/app/differentials/presentations/page.tsxsrc/components/clinical-dashboard/differentials-home.tsxsrc/components/differentials/differential-presentation-workflow-page.tsxsrc/lib/differentials.tstests/differentials.test.tstests/ui-smoke.spec.ts
✅ Files skipped from review due to trivial changes (1)
- docs/branch-review-ledger.md
🚧 Files skipped from review as they are similar to previous changes (3)
- src/app/differentials/presentations/page.tsx
- src/components/differentials/differential-presentation-workflow-page.tsx
- src/components/clinical-dashboard/differentials-home.tsx
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 99432010d1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Follow-up edge case resolved in |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e34f024ab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Required-gate marker audit completed in |
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found. |
Summary
src/components/clinical-dashboard/differentials-home.tsx; structure preserved)Verification
npm run verify:pr-local— script lands on main in CI: streamline risk-scoped PR verification #454 and does not exist on this branch; rannpm run verify:cheap(vitest 1449 passed / 1 skipped, lint, typecheck, runtime, action-pins, ci-scope, sitemap all green) plusnpm run format:check(clean) insteadDuring development, use
npm run verify:cheapas the faster iteration gate before the final PR-local preflight.npm run verify:uiwhen UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed — in flight, will update this checklist with the result before merge; focused differentials Playwright specs (7 tests incl. 375px single-line-badge and horizontal-overflow checks) already pass on Chromiumnpm run verify:releasebefore release or handoff confidence claims — not a releasenpm run eval:retrieval:quality— N/A: no retrieval, ranking, selection, chunking, or scoring change (the new sort is a client-side re-ordering of already-ranked catalogue results for display only)npm run eval:rag -- --limit 15+npm run eval:quality -- --rag-only— N/A: answer generation untouchednpm run check:production-readiness— N/A: no clinical workflow, privacy, env, Supabase, governance, or deployment behavior changenpm run check:deployment-readiness— N/AClinical Governance Preflight
Presentation-only change on a clinical decision-support surface; no data, ranking, access, or governance behavior touched.
Clinical KB Database(sjrfecxgysukkwxsowpy)Notes
ui-tools.spec.ts"differentials evidence-backed search badges stay single-line on narrow viewport" failed once under parallel workers (results view mount timeout upstream of this diff) and passed cleanly in isolation🤖 Generated with Claude Code