Skip to content

fix(dashboard): clearing the composer brought back two retired mode homes - #2730

Open
BigSimmo wants to merge 7 commits into
mainfrom
claude/mode-home-clear-fix
Open

fix(dashboard): clearing the composer brought back two retired mode homes#2730
BigSimmo wants to merge 7 commits into
mainfrom
claude/mode-home-clear-fix

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #2672. You asked me to root-cause seven items from that PR's audit and apply the smallest safe fix to each. Three were real, two were my own misreads, one is yours to rule on, and one was resolved by another session. All of that is set out below with evidence.

Issues 1 and 2 turned out to be a single root cause, and it is the same defect a clinician reported on Sources. Clearing the composer emptied the React query but left q and run=1 in the URL. showSharedHome reads run=1 straight off the URL (submittedUrlRunRequested), so it stayed suppressed while the mode branch beneath it, now seeing no query, fell back to the home its route had been consolidated away from. Reproduced in a browser against a running build before any change:

Route Click Rendered
/?mode=prescribing&q=…&run=1 composer clear medication-home (the Dose/Safety/Monitoring tiles)
/documents/search?q=…&run=1 composer clear document-search-empty-state (the "Start here" home)

Both are described as retired by the repository itself — medications/page.tsx says the tiles were retired, documents/page.tsx says the idle Documents view was, and tests/ui-tools.spec.ts asserts in a comment that medication-home is "retired and no longer reachable from any route". It was reachable, by that one click.

The fix is one handler. onClearQuery in ClinicalDashboard.tsx now also drops the submitted search from the URL, so the shared home renders. appModeSelectionHref rather than appModeHomeHref, because every mode reaching this handler is dashboard-owned so its home is the shared home at /, and appModeHomeHref("prescribing") still answers /medications, which only 307s back. /documents/search also has to leave its own route entirely, which the selection href does.

The pre-existing answer guard is kept and is load-bearing: a generated answer is durable state the URL does not own, so the clear button must never discard one. Verified separately in a browser that answer mode is untouched.

Issue 7 fixed. tests/privacy-readiness-contract.test.ts was the third governance suite failing on a shallow clone with reviewedCommit does not exist: <sha>, which reads as a governance breach rather than a missing object. #1M0J6D guarded the other two and left this one. It now mirrors them exactly. The gate is not weakened: on a full clone the commit resolves, checkGit stays true, and the real ancestry check runs.

Issue 6 resolved, but not here. A competing done request (4036f064) was already pending on #ZKR5YK, backed by an implementation of the mode-pill change. Two pending mutations on one row block reconciliation for everyone (#686WHW), so this branch cancels its own re-scope rather than fight over the row.

Three items deliberately not changed

  • Issue 3, the duplicate /?mode=tools launcher. Confirmed live by browser probe: /?mode=tools renders tools-home from the legacy client-only launcher while canonical /tools renders ToolsSearchResultsPage. It is real duplication, but removing it is an owner ruling plus a sizeable refactor, not a small safe fix: the mode pill deliberately always returns to the shared home, so special-casing Tools trades one inconsistency for another, and deleting the launcher means re-pointing six assertions in tests/favourites-auth-gate.dom.test.tsx that render it directly. Left for a decision.
  • Issue 4, the shared favourites-hub test id. Not a defect — my misread. The three nodes are mutually exclusive branches identifying one logical surface, and expectSingleSettledOwner (a general Next-streaming helper, not a workaround for this) asserts exactly one owner and passes.
  • Issue 5, hardcoded /documents and /medications in tools-catalog.ts. Not a defect — also my misread. These render as plain <a href> links to a mode's canonical namespace root, which redirects server-side; that is normal, not the client-side push into a 307 that made the /sources case worth changing. Changing them would break six pinned expectations across ui-tools.spec.ts and search-pins-menu.dom.test.tsx for no user-visible gain.

Flagged, not actioned

appModeHomeHref("prescribing") returns /medications for the bare case while returning /?mode=prescribing for every case that carries a query — an inconsistency inside one function, and the reason the sidebar link, the medication back link and search pins all route through a 307. Fixing it is correct but changes two pinned hrefs for no visible gain, so it is recorded here rather than bundled in.

RAG impact: none — no file under src/lib/rag/, no retrieval RPC, no ranking config, no eval harness or golden fixture is touched.

Verification

  • npm run verify:pr-local40 gates completed, 0 failed, 0 not reached, including lint, typecheck, test (the full offline suite, which now passes outright because this PR fixes the third shallow-clone suite) and build.
  • Mutation-verified. With the fix disabled (if (false && submittedUrlRunRequested)) both new browser cases fail; restored, both pass. The guards are real, not vacuous.
  • Browser proof on a production build: tests/ui-tools.spec.ts 99 passed (2.2m) and tests/ui-smoke.spec.ts 108 passed (3.5m) — 207 tests across the two specs this change touches, the two most likely to catch a dashboard regression.
  • Reproduction and confirmation done in a real browser, not inferred: counts moved medication-home 1 → 0 and shared-home-empty-state 0 → 1, with the URL landing on /?mode=prescribing&focus=1; the Documents pair moved the same way to /?mode=documents&focus=1.
  • Answer mode re-checked in a browser after the change: URL unchanged, 22 answer nodes preserved.

UI verification not run: npm run plan:browser returns level: full, escalating because src/components/ClinicalDashboard.tsx is a shared foundation, so no narrowed run of the whole suite was available and a local full verify:ui would only re-derive the verdict CI reaches. The focused browser proof above covers the changed surface; the complete Chromium suite is left to the Production UI job, which runs because this PR is opened ready for review rather than as a draft.

Risk and rollout

  • Risk: low, and confined to one event handler. The only behaviour change is that clearing the composer on a submitted dashboard search now also clears the URL, which is what makes the shared home appear instead of a retired one. Answer mode is explicitly excluded by the pre-existing guard so no generated answer can be discarded. Route-owned modes (/dsm/search, /services/search and the rest) never reach this handler and are untouched, which the 207 passing browser tests bear out.
  • Rollback: revert 9b4533a and d8367c6. The ledger commit d3e1b24 is an immutable inbox record and should be left alone. No data, schema, or configuration change, and nothing to unwind on the provider side.
  • Provider or production effects: None. No Supabase, OpenAI, migration, environment, or deployment change, and no provider-backed command was run.
  • RAG impact: none — no ranking surface is touched.

Clinical Governance Preflight

This change touches one composer event handler and one test guard. It does not alter ingestion, answer generation, search ranking, source rendering, document access, privacy, the production environment, or clinical output. What it removes is a retired page appearing where a clinician expected their search cleared, which is a small improvement to the honesty of the interface rather than a change to any clinical content. Each box below is confirmed unchanged rather than newly assured.

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

No clinical decision-support behaviour changed, so the SaMD classification is unaffected.

Notes

Branch naming: this work belongs on claude/vigilant-mayer-60ftd5, whose previous PR (#2672) was merged. The branch was correctly restarted from latest main, but publishing it needed either a force-push or a remote branch deletion, and this environment denies both. With the owner's approval it was published under claude/mode-home-clear-fix instead. The stale claude/vigilant-mayer-60ftd5 still points at ccc6a7d, whose content is already on main via #2672, and can be deleted safely.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf


Generated by Claude Code


Note

Low Risk
Single composer clear handler plus test-only shallow-clone guards; answer-present guard unchanged, so generated answers are not cleared by this URL sync.

Overview
Clearing the search composer no longer leaves run=1 (and related query params) in the URL after the React query is emptied. That mismatch had kept showSharedHome off while prescribing and documents modes fell back to retired idle UIs (medication-home, document-search-empty-state) instead of the consolidated shared home. The onClearQuery path in ClinicalDashboard now calls router.replace with appModeSelectionHref when a submitted run was URL-driven, while still bailing out when an answer is on screen.

Tests and CI: Playwright cases lock in prescribing and documents clear-to-shared-home behavior. The privacy readiness contract test gains the same shallow-clone / missing reviewedCommit handling as other governance suites so cloud CI does not fail spuriously without weakening checks on full clones.

Docs inbox: Ledger entries update and withdraw a competing outstanding-issue mutation in favor of another close request; findings are preserved in this PR narrative.

Reviewed by Cursor Bugbot for commit d8367c6. Configure here.

…retired mode homes came back

Clearing the search box emptied the React query but left `q` and `run=1` in the
URL. `showSharedHome` reads `run=1` straight off the URL, so it stayed suppressed
while the mode branch below it, now seeing no query, fell back to the home its
route had been consolidated away from. Two modes were affected and both were
reproduced in a browser against a running build before the fix:

- `/?mode=prescribing&q=…&run=1`, clear -> `medication-home` (the Dose/Safety/
  Monitoring tiles). `tests/ui-tools.spec.ts` asserts in a comment that this view
  is "retired and no longer reachable from any route". It was reachable, by that
  one click.
- `/documents/search?q=…&run=1`, clear -> `document-search-empty-state`, the
  "Start here" home that `documents/page.tsx` records as deliberately retired.

This is the same defect a clinician reported for Sources, in two more modes: the
URL and the composer disagreed, and the mode branch resolved the disagreement by
rendering a page that no longer exists as a destination.

The fix is one handler. `onClearQuery` now also drops the submitted search from
the URL, so the shared home renders. `appModeSelectionHref` rather than
`appModeHomeHref`: every mode reaching this handler is dashboard-owned, so its
home is the shared home at `/`, and `appModeHomeHref("prescribing")` still answers
`/medications`, which only 307s back. `/documents/search` has to leave its own
route entirely, which the selection href also does.

The pre-existing `answer` guard is kept and is load-bearing. A generated answer is
durable state the URL does not own, so the clear button must not discard one; a
browser check confirms answer mode is untouched.

Both paths are pinned by new browser cases, which is where the defect was proved
rather than inferred.

Also fixes the third governance suite that fails on a shallow clone. `#1M0J6D`
added guards to the clinical-hazard and rag-plan suites; `privacy-readiness` was
left without one and still reported `reviewedCommit does not exist: <sha>`, which
reads as a governance breach rather than a missing object. It now mirrors the two
siblings exactly. The gate is not weakened: on a full clone the commit resolves,
`checkGit` stays true, and the real ancestry check runs.

Queues a correction to ledger row `#ZKR5YK`, whose Sources, Documents and
Medication examples are gone now that all three bare paths redirect. Tools is the
only live case left, and it stays an owner ruling rather than a fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
…queued for it

Another session had a `done` pending on the same row (4036f064), backed by an
implementation of the mode-pill change the row asks for. Two pending mutations on
one row block reconciliation for everyone, which is the race `#686WHW` records,
and a re-scope is pointless against a close that supersedes the whole row.

The findings that prompted the re-scope are verified and worth keeping, so they
are recorded in the cancel reason and in this branch's PR rather than lost: the
row's Sources, Documents and Medication examples are all gone now that those three
bare paths redirect, Favourites is settled by owner decision `#V0EDR4`, and Tools
was the only live case left at the time of the check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
…udget

The explanatory comment pushed ClinicalDashboard.tsx to 4141 lines, one over its
4140-line no-growth budget. The gate's advice is to extract a module rather than
grow the monolith; extracting this file is a refactor of its own, so the
proportionate answer to a one-line overrun is to say the same thing in fewer
lines. The full reasoning, the measured reproduction and the mutation check all
live in the commit that carried the fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 80ff22d3-789a-45c2-bc77-250ace47c080


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@supabase

supabase Bot commented Sep 7, 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 ↗︎.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-07T11:12:46.510183Z d8367c6 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor

cursor Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_009cdec3-35ec-4549-a5c5-8d07f370c72f)

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: d8367c6a71

ℹ️ 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 thread tests/privacy-readiness-contract.test.ts Outdated
Comment thread src/components/ClinicalDashboard.tsx
@BigSimmo

BigSimmo commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. The only repository destination is BigSimmo/Database, and the only branch destination is the pull request head branch claude/mode-home-clear-fix at starting commit d8367c6; never publish fixes to a detached or synthetic work branch and never create a stacked pull request. Use the authenticated GitHub connector to commit each approved fix to BigSimmo/Database:claude/mode-home-clear-fix, then verify that the pull request head contains the published commit before reporting success. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. For a fixed thread, reply with as the first line and as the second line. For a no-code disposition, use followed by . These result markers authorize the workflow to close that exact thread only after it verifies a fixed commit is the pull request head; a local-only commit is not a fix. If publication or verification fails, do not use either result marker, do not claim success, and leave the thread open with the blocker. If human input or new authorization is required, do the same. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

  • Updated the composer clear path to abort and invalidate an active Answer request before navigating home, preventing a delayed response from restoring the submitted URL and answer. src/components/ClinicalDashboard.tsxL3318-L3337
  • Removed automatic git fetch --deepen behavior from the privacy readiness test. Shallow clones now skip only the unavailable ancestry assertion without network access, while full clones retain validation. tests/privacy-readiness-contract.test.tsL25-L58
  • Added a focused delayed-response browser regression proving that clearing an in-flight Answer request remains on the shared home after the response would have settled. tests/ui-universal-search.spec.tsL581-L607
  • Committed the changes as 4d85c42ab3e0f213452886a4437aa67fc581786c, pushed them directly to claude/mode-home-clear-fix, and verified that this commit is the head of PR #2730.
  • Replied to and resolved both existing Codex review threads using the required fixed-head markers. No stacked or replacement pull request was created.

Testing

  • npm run test -- --run tests/privacy-readiness-contract.test.ts — 8 tests passed.
  • npm run ensure
  • npm run test:e2e -- tests/ui-universal-search.spec.ts --project=chromium --grep "clearing a pending Answer request" — focused Playwright regression passed.
  • npm run format
  • npm run lint:changed:internal -- src/components/ClinicalDashboard.tsx tests/privacy-readiness-contract.test.ts tests/ui-universal-search.spec.ts
  • npm run typecheck:source
  • git diff --check
  • git ls-remote origin refs/heads/claude/mode-home-clear-fix — confirmed remote head 4d85c42ab3e0f213452886a4437aa67fc581786c.
  • gh api graphql -f query='query { repository(owner:"BigSimmo", name:"Database") { pullRequest(number:2730) { url headRefName headRefOid reviewThreads(first:50) { nodes { id isResolved } } } } }' — confirmed the expected PR head and both review threads resolved.

View task →

…ct its alias

Tools had two homes. `/tools` is the canonical directory (filter ribbon, full
catalogue, detail panel). `/?mode=tools` rendered a separate hub-shaped launcher,
because `shouldShowSharedHome` excludes `tools` and so the dashboard had nothing
else to fall back to. Which surface a clinician saw depended only on how they
arrived: the mode pill gave them the hub, every other link gave them the
directory.

The hub's one unique feature was its verb shortcut row (Ask, Compare, Prescribe,
Safety, Docs, Refer, Forms, Saved), which answers "what am I trying to do" rather
than naming a mode. That row moves to `src/components/tools/tool-quick-actions.tsx`
and renders above the directory results, sourced from `accessibleTools` so a
category filter cannot empty it, and hidden once a query is running. With nothing
of its own left to show, `/?mode=tools` now redirects to `/tools` unconditionally,
carrying its query string through.

`localSmartExcludedToolIds` was declared three times; the shared copy in
`src/lib/tools-catalog.ts` is now the one definition.

The legacy launcher component is left in place, unreachable, rather than deleted
here: `tests/favourites-auth-gate.dom.test.tsx` still proves real guest-access
behaviour against it, and re-homing that coverage onto `/tools` is a separate
change.

Browser cases that loaded `/?mode=tools` now assert the same contracts on
`/tools`: the shortcut row, the phone filter sheet, the detail panel launch
links, and no shared search chrome on a route that owns its own filtering.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
The redirect landed clinicians on a directory with no way to type a tools
search. `/tools` deliberately renders no shared composer because it owns its
own filtering, but its filtering was reachable only through the URL: the box
lived on the `/?mode=tools` hub, and redirecting that alias took the box with
it. `tests/ui-clinical-ask.spec.ts` caught it — local-only Smart search in
tools mode has nowhere to be typed.

So the box comes across with the shortcut row, in flow rather than fixed, as a
page-owned filter and not a second shared composer. A draft typed here wins
over the URL and the shared command draft, the same precedence the hub used.
Submitting navigates to `/tools?q=…&run=1` so a result set stays shareable and
survives reload, which is what the hub's submit control already did.

Verified in a browser on the failing journey: typing a natural-language query
hides the shortcut row, submitting reaches the ranked results, Answer,
Documents and Favourites stay excluded, and neither the clinical-ask nor the
universal-search endpoint is called.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
Sign up for free to 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.

3 participants