Skip to content

feat(differentials): query-lit Diagnoses stream with related clusters - #1757

Merged
BigSimmo merged 11 commits into
mainfrom
cursor/differentials-query-lit-stream-8bc0
Aug 8, 2026
Merged

feat(differentials): query-lit Diagnoses stream with related clusters#1757
BigSimmo merged 11 commits into
mainfrom
cursor/differentials-query-lit-stream-8bc0

Conversation

@BigSimmo

@BigSimmoBigSimmo commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Make the Differentials Diagnoses stream respect q: reuse catalogue ranking, show match count, light matches, and dim non-matches instead of ignoring the query.
  • Add sticky prev/next match controls, a match title rail, auto-jump to the first (or focus=) match, and related-cluster highlighting from each diagnosis related[] graph, plus a Show family filter.
  • Add multi-select → Compare from the stream (seeds top two matches; desktop CTA + phone composer compare addon on /differentials/diagnoses).
  • Empty-query browse now uses urgency chapters, optional presentation-family grouping, an emergent safety shelf, and scenario preset chips.

Verification

  • Focused unit coverage: npm run test -- tests/differential-stream.test.ts tests/mobile-interaction-regressions.test.ts tests/differentials-navigation.test.ts → 16 passed
  • npm run lint (changed surfaces) → pass
  • npm run typecheck → pass
  • npm run test → 5711 passed | 4 skipped
  • npm run verify:pr-local — dry-run selected lint/typecheck/test/build; lint+typecheck+test run above; full verify:pr-local / build not re-run as a stacked gate
  • UI verification not run: npm run verify:ui / phone-chrome not executed in this session (no Playwright proof yet)

Risk and rollout

  • Risk: Diagnoses stream UX/navigation only; reuses existing rankDifferentialRecords and compare redirect. No snapshot clinical prose edits and no RAG ranking changes.
  • Rollback: revert this PR; stream returns to the prior flat unfiltered catalogue.
  • Provider or production effects: None

Notes

  • Client workspace imports only fixture-free model types (differential-stream-model.ts); ranking still runs on the server in buildDifferentialStreamModel.
  • Deep-link: /differentials/diagnoses?q=Pain&focus=acute-dystonia
  • Feature commit: bed84986; branch tip includes ledger docs commits.
Open in WebOpen in Cursor

Summary by CodeRabbit

  • New Features

    • Added a unified differential diagnosis and presentation workspace.
    • Search results now support relevance matching, focused-result navigation, filtering, urgency grouping, presets, safety guidance, and related references.
    • Added diagnosis selection and comparison actions across desktop and mobile views.
    • Deep links can preserve a focused result and selected diagnoses.
  • Bug Fixes

    • Presentation handoffs now retain all selected diagnoses.
    • Improved mobile interaction reliability and comparison requirements.

…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>
@supabase

supabaseBot commented Aug 8, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitaiBot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 26460c86-0b44-4953-b0d5-80af3f196026

📥 Commits

Reviewing files that changed from the base of the PR and between 3635306 and 9987c21.

📒 Files selected for processing (2)
  • docs/branch-review-ledger.md
  • tests/differential-stream-page.dom.test.tsx
📝 Walkthrough

Walkthrough

The 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.

Changes

Differential stream experience

Layer / File(s)Summary
Stream model contracts and builders
src/lib/differential-stream-model.ts, src/lib/differential-stream.ts
Added serializable stream types and builders for ranked matches, chapters, safety shelves, presets, related references, and comparison seeds.
Workspace rendering and interaction
src/components/differentials/differential-stream-page.tsx, src/components/differentials/differential-stream-workspace.tsx
Replaced the inline page UI with a shared client workspace. Added matching, focus navigation, grouping, filtering, selection, safety shelves, and comparison controls.
Focus-aware route and addon wiring
src/app/(search-app)/differentials/diagnoses/page.tsx, src/lib/differentials-navigation.ts, src/components/clinical-dashboard/global-search-shell.tsx
Added optional focus handling, focus-aware differential routes, and compare-addon activation for diagnosis routes.
Regression coverage and ledger updates
tests/differential-stream.test.ts, tests/differentials-navigation.test.ts, tests/mobile-interaction-regressions.test.ts, docs/branch-review-ledger.md
Added coverage for stream behavior, deep links, comparison thresholds, selected-ID preservation, client boundaries, and workspace list keys. Updated review records.

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
Loading

Possibly related PRs

Suggested labels:codex

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Description check⚠️ WarningThe description is detailed but omits the required Clinical Governance Preflight and retrieval-quality evaluation evidence for ranking changes.Add the Clinical Governance Preflight section and run or explicitly document the required retrieval-quality evaluation and other applicable readiness checks.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly identifies the main change: a query-enabled Diagnoses stream with related clusters.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/differentials-query-lit-stream-8bc0

Comment @coderabbitai help to get the list of available commands.

cursoragentand others added 2 commits August 8, 2026 18:11
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>
@BigSimmo
BigSimmo marked this pull request as ready for review August 8, 2026 18:11
@BigSimmo
BigSimmo enabled auto-merge (squash) August 8, 2026 18:12

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment threadsrc/components/differentials/differential-stream-workspace.tsx Outdated
Comment threadsrc/components/differentials/differential-stream-workspace.tsx Outdated
@github-actions

github-actionsBot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

CI triage

CI failed on this PR. Automated classification of the 3 failed job(s):

  • Static PR checksneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.
  • Unit coverageneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.
  • PR requiredneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.

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.
Drop border+ring on selected cards and replace legacy shadow-soft with
shadow-inset so Static PR checks stay within contract budgets.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (12)
src/lib/differential-stream.ts (4)

274-277: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Align safetyShelfIds with the diagnoses branch.

For presentations, safetyShelfIds is derived from all items, including non-matches, when a query is present. The diagnoses branch (Lines 310-318) restricts the shelf to matchedItems when hasQuery is true. The workspace currently renders the shelf only when hasQuery is 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 win

Extract one presentationItemFromWorkflow helper.

The same 14-field DifferentialStreamItem literal appears three times: matched items, unmatched items, and browse items. Only matchReasons, isMatch, and score differ. The diagnoses branch already uses diagnosisItemFromRecord for 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 win

Precompute the slug→presentation index once per build.

presentationChapterForSlug calls differentialPresentations() and scans every presentation and candidate for each record. diagnosisItemFromRecord runs it for every record, and the diagnoses path builds items twice (matchedItems/unmatchedItems plus browseItems). The cost is O(records × presentations × candidates) on each server render.

Build one Map<slug, {id,title}> at module scope or inside buildDifferentialStreamModel, 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 diagnosisItemFromRecord instead 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 win

Do not build browseItems when a query is present.

browseItems maps every record through diagnosisItemFromRecord. When hasQuery is true, the value feeds only presentationChapters(browseItems) and the browse safetyShelfIds, 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 value

Import getPresentationWorkflowSelectionForDiagnosisIds statically.

@/lib/differentials is already imported at Line 5. The dynamic await import inside the test adds no isolation, because the module instance is shared. Add the symbol to the static import and drop the async keyword.

♻️ 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 win

Add coverage for the presentations stream branch.

Every test in this suite calls buildDifferentialStreamModel("diagnoses", …). The presentations branch at Lines 196-280 of src/lib/differential-stream.ts has no coverage, and it holds three duplicated item literals plus a safetyShelfIds rule 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 win

Assert 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 that differentials-stream-compare-mobile is 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 win

Smooth scrolling ignores prefers-reduced-motion in both scroll paths. Two call sites hardcode behavior: "smooth". The shared root cause is one missing reduced-motion check. scrollToSlug serves the match rail, the safety shelf, and jumpMatch, 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: read window.matchMedia("(prefers-reduced-motion: reduce)").matches in scrollToSlug and pass behavior: "auto" when it is true.
  • src/components/differentials/differential-stream-workspace.tsx#L373-L378: reuse the same helper inside the requestAnimationFrame callback instead of repeating scrollIntoView with a hardcoded behavior.
♻️ 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 win

Narrow the MutationObserver scope.

The observer watches document.body with subtree: true and childList: true, so sync runs 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 a matchMedia check and a getElementById lookup on a phone render path that also performs smooth scrolling.

setHost bails 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 debounce sync with requestAnimationFrame.

♻️ 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 win

Replace the nested visibleItems.some scan with a Set lookup.

This branch runs only when hasQuery is false and familyMode is false. In that state visibleItems returns model.items unchanged at Line 345, so the some predicate always passes. The code still performs O(chapterItems × visibleItems) comparisons for every chapter on every render.

Build one Set of 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 value

Fold the id assertions into the existing presentation-redirect test.

This test calls resolveDifferentialCompareHandoff with 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-id toContain check, which is stronger than the existing toContain("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 value

Consider an options object for the trailing parameters.

differentialRouteWithQuery now takes four positional parameters. Callers that need only focus must pass undefined for selectedIds, as tests/differential-stream.test.ts Line 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

📥 Commits

Reviewing files that changed from the base of the PR and between b877421 and 3635306.

📒 Files selected for processing (11)
  • docs/branch-review-ledger.md
  • src/app/(search-app)/differentials/diagnoses/page.tsx
  • src/components/clinical-dashboard/global-search-shell.tsx
  • src/components/differentials/differential-stream-page.tsx
  • src/components/differentials/differential-stream-workspace.tsx
  • src/lib/differential-stream-model.ts
  • src/lib/differential-stream.ts
  • src/lib/differentials-navigation.ts
  • tests/differential-stream.test.ts
  • tests/differentials-navigation.test.ts
  • tests/mobile-interaction-regressions.test.ts

Comment threadsrc/lib/differential-stream.ts
cursoragentand others added 2 commits August 8, 2026 23:01
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>
@BigSimmo
BigSimmo merged commit 2b757d2 into mainAug 8, 2026
26 checks passed
@BigSimmo
BigSimmo deleted the cursor/differentials-query-lit-stream-8bc0 branch August 8, 2026 23:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@BigSimmo@cursoragent