feat(differentials): query-lit Diagnoses stream with related clusters - #1757
Conversation
…sters Wire the Diagnoses catalogue to search ranking, match jump controls, related-family highlighting, multi-select compare, and urgency/presentation browse chapters so the stream matches its promised safety-ordering UX. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in:40 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe diagnoses route now builds a serializable stream model and renders it through a client workspace. The workspace supports focus navigation, grouping, filtering, safety shelves, diagnosis selection, and comparison. Routes preserve focus and selected diagnosis IDs. ChangesDifferential stream experience
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Route
participant DifferentialStreamPage
participant buildDifferentialStreamModel
participant DifferentialStreamWorkspace
Route->>DifferentialStreamPage: pass query and focus
DifferentialStreamPage->>buildDifferentialStreamModel: build stream model
buildDifferentialStreamModel-->>DifferentialStreamPage: return serializable model
DifferentialStreamPage->>DifferentialStreamWorkspace: pass model and initialFocus
DifferentialStreamWorkspace->>DifferentialStreamWorkspace: match, group, filter, and select diagnoses
DifferentialStreamWorkspace-->>Route: navigate with focus or selected diagnosis IDs
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Append the branch-review ledger row for PR #1757 at the shipped head. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Point the differentials stream ledger record at the current PR head. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:952015872c
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
CI triageCI failed on this PR. Automated classification of the 3 failed job(s):
Compared with main CI run #9284 (cancelled). Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger. |
…y mode Address Codex review on the query-lit Diagnoses stream: seed compare ticks only for co-workflow pairs, keep every selected id through the presentations redirect, require two selections for the mobile compare CTA, and treat punctuation-only queries as browse mode via normalizeSearchText.
Keep known compare ids across the presentations redirect while dropping unknown/cased junk, and replace arbitrary text-[0.65rem] with text-3xs.
…query-lit-stream-8bc0
Drop border+ring on selected cards and replace legacy shadow-soft with shadow-inset so Static PR checks stay within contract budgets.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (12)
src/lib/differential-stream.ts (4)
274-277: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAlign
safetyShelfIdswith the diagnoses branch.For presentations,
safetyShelfIdsis derived from allitems, including non-matches, when a query is present. The diagnoses branch (Lines 310-318) restricts the shelf tomatchedItemswhenhasQueryis true. The workspace currently renders the shelf only whenhasQueryis false, so the difference is not visible today. Make the two branches consistent so a later UI change that shows the shelf during a query does not surface non-matching emergent items.🤖 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/differential-stream.ts` around lines 274 - 277, Update the presentations branch’s safetyShelfIds construction to use matchedItems when hasQuery is true, matching the diagnoses branch behavior, while preserving all-items filtering when no query is present.
200-265: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract one
presentationItemFromWorkflowhelper.The same 14-field
DifferentialStreamItemliteral appears three times: matched items, unmatched items, and browse items. OnlymatchReasons,isMatch, andscorediffer. The diagnoses branch already usesdiagnosisItemFromRecordfor this. Mirror that pattern here so a new field cannot be added to two of the three copies.♻️ Proposed refactor sketch
+function presentationItemFromWorkflow(+ workflow: ReturnType<typeof differentialPresentations>[number],+ match?: { score: number; reasons: string[] },+): DifferentialStreamItem {+ return {+ id: `presentation-${workflow.id}`,+ slug: workflow.id,+ title: workflow.title,+ description: workflow.subtitle,+ examples: workflow.safetySnapshot.tags.slice(0, 3),+ href: `/differentials/presentations/${workflow.id}`,+ status: workflow.status,+ matchReasons: match?.reasons ?? [],+ isMatch: Boolean(match),+ score: match?.score ?? 0,+ related: [],+ exclusionPreview: null,+ chapterId: `status-${workflow.status}`,+ chapterTitle: statusChapterCopy[workflow.status].title,+ };+}🤖 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/differential-stream.ts` around lines 200 - 265, Extract a shared presentationItemFromWorkflow helper for constructing DifferentialStreamItem from a workflow, accepting the varying matchReasons, isMatch, and score values. Replace the duplicated item literals in the matchedItems, unmatchedItems, and browse-items mappings with this helper, preserving their existing values and ordering behavior.
61-68: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winPrecompute the slug→presentation index once per build.
presentationChapterForSlugcallsdifferentialPresentations()and scans every presentation and candidate for each record.diagnosisItemFromRecordruns it for every record, and the diagnoses path builds items twice (matchedItems/unmatchedItemsplusbrowseItems). The cost is O(records × presentations × candidates) on each server render.Build one
Map<slug, {id,title}>at module scope or insidebuildDifferentialStreamModel, then pass it down.♻️ Proposed refactor
-function presentationChapterForSlug(slug: string): { id: string; title: string } | null {- for (const presentation of differentialPresentations()) {- if (presentation.candidates.some((candidate) => candidate.slug === slug)) {- return { id: presentation.id, title: presentation.title };- }- }- return null;-}+function presentationChapterIndex(): Map<string, { id: string; title: string }> {+ const index = new Map<string, { id: string; title: string }>();+ for (const presentation of differentialPresentations()) {+ for (const candidate of presentation.candidates) {+ if (!index.has(candidate.slug)) {+ index.set(candidate.slug, { id: presentation.id, title: presentation.title });+ }+ }+ }+ return index;+}Then thread the index through
diagnosisItemFromRecordinstead of calling the lookup per record.🤖 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/differential-stream.ts` around lines 61 - 68, Replace the per-record scan in presentationChapterForSlug with a precomputed slug-to-presentation Map built once per build, preferably in buildDifferentialStreamModel using differentialPresentations(). Thread this index through diagnosisItemFromRecord and all callers, including matchedItems, unmatchedItems, and browseItems, so each record performs constant-time slug lookup without rebuilding or rescanning presentations.
293-301: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winDo not build
browseItemswhen a query is present.
browseItemsmaps every record throughdiagnosisItemFromRecord. WhenhasQueryis true, the value feeds onlypresentationChapters(browseItems)and the browsesafetyShelfIds, and both are gated on!hasQuery. The whole array is then discarded. Each mapped record also runs the presentation scan noted at Lines 61-68, so the waste doubles the per-request cost of the query path.♻️ Proposed fix
- const browseItems = [...differentialRecords]- .sort(- (left, right) =>- statusChapterOrder.indexOf(left.status) - statusChapterOrder.indexOf(right.status) ||- left.title.localeCompare(right.title),- )- .map((record) => diagnosisItemFromRecord(record, knownSlugs));-- const items = hasQuery ? [...matchedItems, ...unmatchedItems] : browseItems;+ const browseItems = hasQuery+ ? []+ : [...differentialRecords]+ .sort(+ (left, right) =>+ statusChapterOrder.indexOf(left.status) - statusChapterOrder.indexOf(right.status) ||+ left.title.localeCompare(right.title),+ )+ .map((record) => diagnosisItemFromRecord(record, knownSlugs));++ const items = hasQuery ? [...matchedItems, ...unmatchedItems] : browseItems;🤖 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/differential-stream.ts` around lines 293 - 301, Only construct and map `browseItems` when `hasQuery` is false, while preserving the existing sorted `diagnosisItemFromRecord` transformation for browse requests. Ensure the query path uses `matchedItems` and `unmatchedItems` without evaluating the unnecessary browse mapping or its presentation scan.tests/differential-stream.test.ts (3)
60-72: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueImport
getPresentationWorkflowSelectionForDiagnosisIdsstatically.
@/lib/differentialsis already imported at Line 5. The dynamicawait importinside the test adds no isolation, because the module instance is shared. Add the symbol to the static import and drop theasynckeyword.♻️ Proposed change
-import { differentialRecords, getDifferentialRecord } from "`@/lib/differentials`";+import {+ differentialRecords,+ getDifferentialRecord,+ getPresentationWorkflowSelectionForDiagnosisIds,+} from "`@/lib/differentials`";- it("auto-seeds compare ticks only for diagnosis pairs that share a presentation workflow", async () => {- const { getPresentationWorkflowSelectionForDiagnosisIds } = await import("`@/lib/differentials`");- const model = buildDifferentialStreamModel("diagnoses", "pain");+ it("auto-seeds compare ticks only for diagnosis pairs that share a presentation workflow", () => {+ const model = buildDifferentialStreamModel("diagnoses", "pain");🤖 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/differential-stream.test.ts` around lines 60 - 72, Update the static import from "`@/lib/differentials`" to include getPresentationWorkflowSelectionForDiagnosisIds, remove the dynamic await import inside the test, and make the test callback synchronous by removing async.
8-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the presentations stream branch.
Every test in this suite calls
buildDifferentialStreamModel("diagnoses", …). Thepresentationsbranch at Lines 196-280 ofsrc/lib/differential-stream.tshas no coverage, and it holds three duplicated item literals plus asafetyShelfIdsrule that differs from the diagnoses branch.Add at least one query case and one browse case for
"presentations". I can generate those tests if you want them.🤖 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/differential-stream.test.ts` around lines 8 - 81, Extend the differential stream model tests with at least one query case and one empty-query browse case using buildDifferentialStreamModel("presentations", ...). Assert presentation-specific results, including matching/ranking behavior for the query and browse metadata such as chapters, presets, or safetyShelfIds, so the presentations branch and its distinct safetyShelfIds rule are covered.
103-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert compare behavior, not source text.
This test matches exact source substrings, including
const canCompare = selectedCount >= 2. Any rename, reformat, or extraction of the threshold into a named constant fails the test while the behavior stays correct. A Prettier reflow of that line is enough to break it.The boundary test at Lines 90-100 is different: it guards an architectural invariant that has no runtime surface, so source inspection is appropriate there. The compare threshold does have a runtime surface. The ledger records existing DOM tests for compare selection, so the infrastructure is available.
Render
DifferentialStreamWorkspace, then assert thatdifferentials-stream-compare-mobileis a link at two selections and is not a link at one.🤖 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/differential-stream.test.ts` around lines 103 - 113, Replace the source-text assertions in “differential stream compare CTA contracts” with a rendered DifferentialStreamWorkspace test. Use the existing DOM-testing infrastructure to verify that “differentials-stream-compare-mobile” renders as a link with two selections and does not render as a link with one selection; keep the separate architectural boundary source-inspection test unchanged.src/components/differentials/differential-stream-workspace.tsx (3)
359-364: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSmooth scrolling ignores
prefers-reduced-motionin both scroll paths. Two call sites hardcodebehavior: "smooth". The shared root cause is one missing reduced-motion check.scrollToSlugserves the match rail, the safety shelf, andjumpMatch, so a check placed there covers every user-started jump; the auto-jump effect duplicates the scroll inline and needs the same treatment.
src/components/differentials/differential-stream-workspace.tsx#L359-L364: readwindow.matchMedia("(prefers-reduced-motion: reduce)").matchesinscrollToSlugand passbehavior: "auto"when it is true.src/components/differentials/differential-stream-workspace.tsx#L373-L378: reuse the same helper inside therequestAnimationFramecallback instead of repeatingscrollIntoViewwith a hardcodedbehavior.♻️ Proposed change
+ function scrollBehavior(): ScrollBehavior {+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth";+ }+ function scrollToSlug(slug: string) { const node = document.getElementById(`differential-stream-card-${slug}`); if (!node) return; - node.scrollIntoView({ behavior: "smooth", block: "center" });+ node.scrollIntoView({ behavior: scrollBehavior(), block: "center" }); setFocusedSlug(slug); }const frame = window.requestAnimationFrame(() => { - const node = document.getElementById(`differential-stream-card-${target}`);- if (!node) return;- node.scrollIntoView({ behavior: "smooth", block: "center" });- setFocusedSlug(target);+ scrollToSlug(target); });🤖 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-stream-workspace.tsx` around lines 359 - 364, Update scrollToSlug in src/components/differentials/differential-stream-workspace.tsx at lines 359-364 to check prefers-reduced-motion and use auto behavior when enabled, preserving smooth scrolling otherwise. Update the requestAnimationFrame callback at lines 373-378 to reuse scrollToSlug or the same reduced-motion behavior instead of hardcoding smooth scrolling.
86-99: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winNarrow the MutationObserver scope.
The observer watches
document.bodywithsubtree: trueandchildList: true, sosyncruns for every DOM insertion or removal anywhere in the application. The workspace itself mutates the DOM on focus changes, family filtering, and grouping switches, and the shell mutates chrome during scroll hide/reveal. Each callback runs amatchMediacheck and agetElementByIdlookup on a phone render path that also performs smooth scrolling.
setHostbails out when the node is unchanged, so this is a cost concern rather than a render loop. Observe the smallest container that can contain the addon slot, or debouncesyncwithrequestAnimationFrame.♻️ Proposed change
sync(); phoneMediaQuery.addEventListener("change", sync); - const observer = new MutationObserver(sync);- observer.observe(document.body, { childList: true, subtree: true });+ let frame = 0;+ const scheduleSync = () => {+ if (frame) return;+ frame = window.requestAnimationFrame(() => {+ frame = 0;+ sync();+ });+ };+ const observer = new MutationObserver(scheduleSync);+ observer.observe(document.body, { childList: true, subtree: true }); return () => { phoneMediaQuery.removeEventListener("change", sync); observer.disconnect(); + if (frame) window.cancelAnimationFrame(frame); };🤖 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-stream-workspace.tsx` around lines 86 - 99, In the useEffect containing sync and MutationObserver, stop observing document.body globally. Scope the observer to the smallest stable container that can contain differentialsMobileCompareAddonSlotId, or coalesce mutations through requestAnimationFrame when no narrower container is available, while preserving media-query synchronization and cleanup behavior.
658-662: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winReplace the nested
visibleItems.somescan with a Set lookup.This branch runs only when
hasQueryis false andfamilyModeis false. In that statevisibleItemsreturnsmodel.itemsunchanged at Line 345, so thesomepredicate always passes. The code still performs O(chapterItems × visibleItems) comparisons for every chapter on every render.Build one
Setof visible ids outside the map, or drop the filter in this branch.♻️ Proposed change
+ const visibleIds = useMemo(() => new Set(visibleItems.map((item) => item.id)), [visibleItems]);const chapterItems = chapter.itemIds .map((id) => itemById.get(id)) .filter((item): item is DifferentialStreamItem => Boolean(item)) - .filter((item) => visibleItems.some((visible) => visible.id === item.id));+ .filter((item) => visibleIds.has(item.id));🤖 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-stream-workspace.tsx` around lines 658 - 662, In the chapter-item construction around chapterItems, remove the redundant visibleItems.some filter for the no-query, non-family branch, or replace it with a single Set of visible item IDs created outside the chapter map and use constant-time membership checks. Preserve the existing filtering by itemById and the empty-chapter null return.tests/differentials-navigation.test.ts (1)
72-81: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFold the id assertions into the existing presentation-redirect test.
This test calls
resolveDifferentialCompareHandoffwith the same two diagnosis ids as the test at Lines 36-47. Only the query casing differs, and no assertion covers that difference. The new value is the per-idtoContaincheck, which is stronger than the existingtoContain("ids=").Move Lines 79-80 into the earlier test and delete this one, or assert the casing behavior here so the second case has a distinct purpose.
🤖 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/differentials-navigation.test.ts` around lines 72 - 81, Remove the duplicate presentation-redirect test around resolveDifferentialCompareHandoff and move its per-diagnosis href assertions into the existing test covering the same diagnosis ids, or change this test’s query input to a distinct casing scenario and assert the expected casing behavior so it has unique coverage.src/lib/differentials-navigation.ts (1)
7-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider an options object for the trailing parameters.
differentialRouteWithQuerynow takes four positional parameters. Callers that need onlyfocusmust passundefinedforselectedIds, astests/differential-stream.test.tsLine 85 shows. A trailing options object would remove the placeholder and leave room for the next parameter.The current signature is backward compatible and correct, so treat this as optional.
🤖 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-navigation.ts` around lines 7 - 19, Optionally refactor differentialRouteWithQuery to replace the positional selectedIds and focus parameters with a trailing options object, allowing callers to provide focus without an undefined placeholder and leaving room for future options. Preserve the existing query, ids, and focus trimming and serialization behavior, while retaining backward compatibility if the project requires it.
🤖 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/differentials/differential-stream-workspace.tsx`:
- Around line 595-604: Update the differential stream workspace component’s
existing useId setup near matchJumpRegionId to create a unique ID for the
“Select at least two diagnoses” description, then use that generated value for
both aria-describedby and the hidden span’s id instead of the hardcoded
stream-compare-need-two.
- Around line 394-399: Update jumpMatch to navigate through the rendered
jumpItems collection rather than all matchItems, so family mode only targets
visible matches; preserve the existing wraparound behavior and slug scrolling.
Use jumpItems.length for the disabled prop on both previous and next controls.
- Around line 197-199: Update the dimmed highlight styling in the differential
stream workspace so clinical text remains at least 4.5:1 contrast in both
themes. Replace the opacity-45 treatment or scope dimming only to decorative
elements, while preserving the existing border, surface, and inset-shadow
styling.
In `@src/lib/differential-stream.ts`:
- Around line 167-191: Move the existing JSDoc block from above
compareSeedIdsForMatches to directly above the buildDifferentialStreamModel
declaration, leaving the helper function and documentation text unchanged.
---
Nitpick comments:
In `@src/components/differentials/differential-stream-workspace.tsx`:
- Around line 359-364: Update scrollToSlug in
src/components/differentials/differential-stream-workspace.tsx at lines 359-364
to check prefers-reduced-motion and use auto behavior when enabled, preserving
smooth scrolling otherwise. Update the requestAnimationFrame callback at lines
373-378 to reuse scrollToSlug or the same reduced-motion behavior instead of
hardcoding smooth scrolling.
- Around line 86-99: In the useEffect containing sync and MutationObserver, stop
observing document.body globally. Scope the observer to the smallest stable
container that can contain differentialsMobileCompareAddonSlotId, or coalesce
mutations through requestAnimationFrame when no narrower container is available,
while preserving media-query synchronization and cleanup behavior.
- Around line 658-662: In the chapter-item construction around chapterItems,
remove the redundant visibleItems.some filter for the no-query, non-family
branch, or replace it with a single Set of visible item IDs created outside the
chapter map and use constant-time membership checks. Preserve the existing
filtering by itemById and the empty-chapter null return.
In `@src/lib/differential-stream.ts`:
- Around line 274-277: Update the presentations branch’s safetyShelfIds
construction to use matchedItems when hasQuery is true, matching the diagnoses
branch behavior, while preserving all-items filtering when no query is present.
- Around line 200-265: Extract a shared presentationItemFromWorkflow helper for
constructing DifferentialStreamItem from a workflow, accepting the varying
matchReasons, isMatch, and score values. Replace the duplicated item literals in
the matchedItems, unmatchedItems, and browse-items mappings with this helper,
preserving their existing values and ordering behavior.
- Around line 61-68: Replace the per-record scan in presentationChapterForSlug
with a precomputed slug-to-presentation Map built once per build, preferably in
buildDifferentialStreamModel using differentialPresentations(). Thread this
index through diagnosisItemFromRecord and all callers, including matchedItems,
unmatchedItems, and browseItems, so each record performs constant-time slug
lookup without rebuilding or rescanning presentations.
- Around line 293-301: Only construct and map `browseItems` when `hasQuery` is
false, while preserving the existing sorted `diagnosisItemFromRecord`
transformation for browse requests. Ensure the query path uses `matchedItems`
and `unmatchedItems` without evaluating the unnecessary browse mapping or its
presentation scan.
In `@src/lib/differentials-navigation.ts`:
- Around line 7-19: Optionally refactor differentialRouteWithQuery to replace
the positional selectedIds and focus parameters with a trailing options object,
allowing callers to provide focus without an undefined placeholder and leaving
room for future options. Preserve the existing query, ids, and focus trimming
and serialization behavior, while retaining backward compatibility if the
project requires it.
In `@tests/differential-stream.test.ts`:
- Around line 60-72: Update the static import from "`@/lib/differentials`" to
include getPresentationWorkflowSelectionForDiagnosisIds, remove the dynamic
await import inside the test, and make the test callback synchronous by removing
async.
- Around line 8-81: Extend the differential stream model tests with at least one
query case and one empty-query browse case using
buildDifferentialStreamModel("presentations", ...). Assert presentation-specific
results, including matching/ranking behavior for the query and browse metadata
such as chapters, presets, or safetyShelfIds, so the presentations branch and
its distinct safetyShelfIds rule are covered.
- Around line 103-113: Replace the source-text assertions in “differential
stream compare CTA contracts” with a rendered DifferentialStreamWorkspace test.
Use the existing DOM-testing infrastructure to verify that
“differentials-stream-compare-mobile” renders as a link with two selections and
does not render as a link with one selection; keep the separate architectural
boundary source-inspection test unchanged.
In `@tests/differentials-navigation.test.ts`:
- Around line 72-81: Remove the duplicate presentation-redirect test around
resolveDifferentialCompareHandoff and move its per-diagnosis href assertions
into the existing test covering the same diagnosis ids, or change this test’s
query input to a distinct casing scenario and assert the expected casing
behavior so it has unique coverage.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4df50cf8-5a0d-43e1-a097-dafb6912c275
📒 Files selected for processing (11)
docs/branch-review-ledger.mdsrc/app/(search-app)/differentials/diagnoses/page.tsxsrc/components/clinical-dashboard/global-search-shell.tsxsrc/components/differentials/differential-stream-page.tsxsrc/components/differentials/differential-stream-workspace.tsxsrc/lib/differential-stream-model.tssrc/lib/differential-stream.tssrc/lib/differentials-navigation.tstests/differential-stream.test.tstests/differentials-navigation.test.tstests/mobile-interaction-regressions.test.ts
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Workspace uses useRouter; assert presentation entries as buttons in the query-lit stream UI so Unit coverage can pass. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Summary
q: reuse catalogue ranking, show match count, light matches, and dim non-matches instead of ignoring the query.focus=) match, and related-cluster highlighting from each diagnosisrelated[]graph, plus a Show family filter./differentials/diagnoses).Verification
npm run test -- tests/differential-stream.test.ts tests/mobile-interaction-regressions.test.ts tests/differentials-navigation.test.ts→ 16 passednpm run lint(changed surfaces) → passnpm run typecheck→ passnpm run test→ 5711 passed | 4 skippednpm run verify:pr-local— dry-run selected lint/typecheck/test/build; lint+typecheck+test run above; fullverify:pr-local/ build not re-run as a stacked gatenpm run verify:ui/ phone-chrome not executed in this session (no Playwright proof yet)Risk and rollout
rankDifferentialRecordsand compare redirect. No snapshot clinical prose edits and no RAG ranking changes.Notes
differential-stream-model.ts); ranking still runs on the server inbuildDifferentialStreamModel./differentials/diagnoses?q=Pain&focus=acute-dystoniabed84986; branch tip includes ledger docs commits.Summary by CodeRabbit
New Features
Bug Fixes