Skip to content

Clinical tidy-up: Services call button, A to Z lists, Forms Clock and Act tabs, Safety plan header, Therapy - #3463

Merged
BigSimmo merged 36 commits into
mainfrom
claude/clinical-reorganisation-j1e2kv
Oct 11, 2026
Merged

BigSimmo merged 36 commits into
mainfrom
claude/clinical-reorganisation-j1e2kv

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

User description

Requested by Josh · project thread

Summary

Before: Services result cards showed no phone number; browsing DSM, Specifiers, Formulation, Therapy and Forms with nothing typed showed an "All" list with "Top match" labels; Forms had no tabs and the MHA clock lived only under Psychiatry; Safety plan had no app header; every Therapy pathway showed "Incomplete" with no explanation.

After: each Services card leads with "Call " (only when the record holds a dialable number; Review referral and Add to shortlist move behind More); browsing lists are titled by section and run A to Z, and DSM groups by chapter; Forms has Forms, Clock and Act tabs, the clock has "Copy for handover" and a Clinical blue Start button; Safety plan sits in the normal shell under Psychiatry with one home on the Psychiatry page; Pathways explains Incomplete in one line and desktop therapy cards use small buttons.

How: usefulness and layout audit (11 Oct 2026) items 6 (browse part), 7, 9 and the Therapy and Safety plan items of 8. Display and navigation only. The side menu regroup, one Compare and desktop layouts wait for the owner's yes on mockups and are not in this PR.

Areas touched: App experience, Knowledge and records, Design system, Clinical reference content.

Verification

  • npx tsc --noEmit: exit 0 on the combined branch.
  • npx eslint on changed source and tests: exit 0. Prettier: "All matched files use Prettier code style!"
  • npx vitest run on all changed and related unit/DOM tests: "Test Files 15 passed (15), Tests 245 passed (245)".
  • check:diff-integrity: "PASS — 18 changed test file(s), 350 -> 376 test case(s)".
  • Browser specs edited (ui-smoke, ui-tools, ui-overlap, ui-accessibility, ui-mode-nav-density) but not run locally; left to CI.

Clinical Governance Preflight

  • No clinical wording, criteria, comparison content, sign-off or review status changed. "Incomplete" and "Needs source review" statuses unchanged.
  • New explanatory line on Pathways is taken from the data's own definition ("Marked incomplete until clinician review confirms sequencing").
  • MHA clock handover copy reuses only time limits the page already computes and shows; it adds no new legal calculation. Copied text omits the Act quotes shown beside each limit on screen and ends with the existing "Reference only" line: needs the owner's clinical sign-off before going live.
  • Services call numbers come from each record's existing phone field; nothing is invented, and records with no dialable number get no button.
  • No patient data, no storage changes: Safety plan still stores nothing; clocks stay on device.

Risk and rollout

  • Risk: navigation and display only; no database, provider, ranking or service-worker change.
  • Rollback: revert this PR.
  • Provider or production effects: None.

Notes

  • Stays DRAFT: clinical content area; the owner sends it live.
  • RAG impact: no retrieval behaviour change — no ranking-protected file touched; empty-query sorting happens in the UI only.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N5U9yjzHumprWGGZ4R3hBj

Generated by Claude Code


Summary by cubic

A display-and-navigation tidy-up of the clinical areas: Services cards gain a dial button, empty browse lists run A to Z under the section name instead of "All", Clinical Forms gets Forms/Clock/Act tabs, the safety plan moves under the normal app header in Psychiatry, and Therapy explains its "Incomplete" tags.

Navigation and lists

  • Each Services result card leads with "Call <number>" taken from the record's own phone field (no button when the record has none; unbracketed extensions are not folded into the dialled number); Review referral and Add to shortlist move behind a "More" toggle.
  • Browsing with nothing typed shows the section name and a plain A to Z list with no "Top match"/"Why matched"; DSM groups by chapter, and Therapy treats punctuation-only input as browsing.
  • Clinical Forms gains Forms, Clock and Act tabs; the Clock is the same MHA clock the Psychiatry hub links to, now also at /forms/clock, with "Copy for handover" — each time limit carrying its Act citation, condition and repeat interval — and the primary blue Start button; switching tabs keeps the list's filters and sort selection. Neither the Clock nor the Act tab is recorded as a visit or counted among the hub's most-opened forms.
  • The safety plan now renders inside the PsychSift shell under Psychiatry with the normal header and no search composer; the URL and store-nothing behaviour are unchanged.
  • Header mode-menu, option links and shell navigations ask for confirmation before leaving an unfinished safety plan, since client-side navigation skips the browser's unload warning; declining keeps the mode menu open and focused, reloading keeps you on the plan, and a sentinel left by a reload is stepped back off to reach the previous page while a confirmed leave still skips both plan entries.

Therapy and safety-plan display

  • Pathways state once what "Incomplete" means (clinician review has not yet confirmed the step order) and count only steps linked to a therapy record.
  • Desktop therapy result cards shrink their actions to the row end since the whole card already opens the record; phone layout is unchanged.

Display and navigation only — no search or ranking change, clocks stay on device, and no numbers or legal text are invented. DRAFT: the new clock handover copy awaits the owner's clinical sign-off.

Written for commit f6b3052. Summary will update on new commits.

View guided diff Turn on auto-fix


CodeAnt-AI Description

Make clinical browsing clearer and surface key actions across services, forms, and safety planning

What Changed

  • Service results show a call button with the record’s dialable number when available; referral review and shortlisting are under More.
  • Browsing DSM, Specifiers, Formulation, Therapy, and Forms with no query now shows the section name and an A-to-Z list. DSM diagnoses are grouped by chapter; typed searches keep their ranked results.
  • Clinical Forms now has Forms, Clock, and Act tabs. Handover copies include form names and the time limits shown on the clock, with their conditions and Act references.
  • The safety plan uses the Psychiatry app header and prompts before navigation or Back can discard entries. Its Psychiatry home link is its single entry point.
  • Therapy pathways explain what “Incomplete” means, and desktop therapy cards use more compact action buttons.

Impact

✅ One-tap calls from service results
✅ A-to-Z browsing without misleading match labels
✅ Forms, clock and Act one tap apart
✅ Fewer lost safety-plan entries

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

…hapter

Audit item 6 (display-only). With no query, DSM, Specifiers, Formulation,
Therapy and Forms titled their list "All" and badged cards "Top match" /
"Why matched", which read as if the app had picked something for a patient.

- Results band: new emptyQuerySubject prop shows the section name
  ("DSM diagnoses", "Specifiers", "Formulation mechanisms", "Therapies",
  "Forms") as the heading instead of "All" when nothing is typed.
- Specifiers, Formulation, Therapy: empty-query lists sorted A to Z in the
  UI component; no Top match / Best match; Therapy cards label the first
  column "Best used for" instead of "Why matched" while browsing.
- Forms: register headings read "Forms" / "A to Z by form code" (register
  keeps its existing form-code order).
- DSM: browse groups by chapter in catalogue order as collapsible
  <details> headings with counts; rows A to Z with "[...]" generic
  templates last; compact one-line rows on a phone (title + ICD code);
  reference-only note kept, and shown on phones too.
- Typed queries keep today's ranked behaviour exactly. No search or
  ranking library changed.

Tests: new tests/browse-a-to-z.dom.test.tsx and
tests/therapy-browse-a-to-z.dom.test.tsx; forms register listing and the
therapy accessibility journey updated for the new headings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Move /safety-plan into the (search-app) route group so it renders inside
the shared search shell with the normal app header, under Psychiatry, with
no search composer (it is a working form, not a search). The URL is
unchanged and the page still stores nothing.

- The tool no longer owns the OS top inset or #main-content (the shell
  does); the back button falls back to Psychiatry, its one home.
- The patient copy is marked as the print output, so the shell chrome
  never prints with it.
- Psychiatry page: remove the duplicate Safety plan and MHA forms quick
  actions; they live once under "For a shift" and "Mental Health Act".
- Regenerate the site map, design-system adoption manifest and repo
  awareness snapshot for the moved route.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t cards

- Pathways: one line at the top says what the amber Incomplete tag means,
  in the terms the pathway data already uses ("Marked incomplete until
  clinician review confirms sequencing"). Shown only when a pathway is
  Incomplete; no status or step text changes.
- Pathways: the "linked steps" chip now counts only steps linked to a
  therapy record, matching each pathway row; a reviewed pathway's badge
  shows a check rather than a warning icon.
- Search/Recommend result cards: on desktop the whole card opens the
  record (the Open link already stretches across it) and the actions shrink
  to their own width at the row's end instead of three full-width buttons.
  Phone layout is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Searching 'crisis' returned 85 tall cards (~23,000px on a phone) and no card
showed a phone number. Each result card now leads with a tel: call button
showing the record's own number (57 of the 85 crisis results have one). Cards
with no dialable phone show no button; no number is ever guessed, and two
numbers run together in one string are refused rather than dialled.

The always-open Review referral / Add to shortlist row moves behind a
'More' disclosure on each card; the title is now the stretched link that
opens the record, so the whole card still opens it. Best-use text clamps
to 2 lines on a phone (3 from sm).

extractFirstDialablePhone moves from the service record page into
src/lib/service-call.ts so the card and the record dial the same number.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e Start

Clinical Forms now has three tabs, one tap apart: Forms (/forms/search),
Clock (/forms/clock, new) and Act (/forms/act). The shell draws the bar on
the list; Clock and Act are information pages, so each mounts the bar itself
through FormsModeNav. The clock is the same page as /psychiatry/mha-clock,
which stays, so the Psychiatry hub shortcut keeps working; on the Forms tab
its breadcrumb names Forms.

Copy for handover now copies each running clock's form code and name, when
it was made, and the time limits the page already shows: a signed-off
countdown's end as a Perth time (or "time limit passed"), otherwise the
existing not-calculated lines. Nothing new is calculated, no running or
time-left figures are copied, nothing is saved anywhere. Copied / failed
status is shown.

The clock's Start button now uses the shared primary face (Clinical blue)
instead of the near-black ink fill.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@BigSimmo BigSimmo self-assigned this Oct 11, 2026
@supabase

supabase Bot commented Oct 11, 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

coderabbitai Bot commented Oct 11, 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: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 0a1eb285-45bd-4294-a79f-c7c6762278d7

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@codspeed

codspeed Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ 2 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

ℹ️ 1 benchmark has no flame graph

The flamegraph could not be generated because too much time was spent in runtime internals, making the visualization not useful for performance analysis.

✅ 8 untouched benchmarks


Comparing claude/clinical-reorganisation-j1e2kv (f6b3052) with main (773df68)

Open in CodSpeed

…fety plan routes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: f6b30522
Scan Time: 2026-10-11 17:29:41 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

Comment thread src/components/patient-safety-plan.tsx Resolved
Inside the app shell, header and menu links are client-side navigations that
never fire beforeunload, so a half-built plan could be lost in one tap.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 11, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@BigSimmo
BigSimmo marked this pull request as ready for review October 11, 2026 10:13
@codeant-ai

codeant-ai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed 40a36df Oct 11, 2026 · 16:56 16:59
✅ Incremental review completed 9f629d0 Oct 11, 2026 · 15:54 15:58
✅ Incremental review completed ad72937 Oct 11, 2026 · 13:27 13:32
✅ Reviewed your PR 177ba14 Oct 11, 2026 · 10:13 10:20

@codeant-ai

codeant-ai Bot commented Oct 11, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@greptile-apps greptile-apps 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.

BigSimmo has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 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-10-11T16:05:39.421888Z 9f629d0 Draft marked ready
ℹ️ 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.

@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Oct 11, 2026
@qodo-code-review

qodo-code-review Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Improve clinical browsing, Forms navigation and safety-plan flow

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Put record-backed call links first on Services cards; move secondary actions behind More.
• Show section-named A-to-Z browse lists and connect Forms, Clock and Act tabs.
• Move Safety plan into Psychiatry; clarify therapy pathways and expand clock handover copy.
Diagram

graph TD
  Shell["Search shell"] --> Browse["Browse lists"] --> Therapy["Therapy cards"]
  Shell --> Forms["Forms tabs"] --> Clock["MHA clock"]
  Shell --> Safety["Safety plan"] --> Guard["Leave guard"]
  Shell --> Services["Services cards"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Redirect Forms Clock to Psychiatry Clock
  • ➕ One canonical clock URL.
  • ➖ Loses Forms tab context and breadcrumb.
2. Use only browser unload confirmation
  • ➕ Avoids custom history coordination.
  • ➖ Cannot protect client-side links, shell controls or in-app Back navigation.

Recommendation: Keep the shared clock component behind two context-specific routes and explicitly guard dirty safety plans. Review history behaviour in a browser and obtain clinical sign-off for the expanded handover copy before release.

Files changed (56) +1935 / -215

Enhancement (20) +684 / -101
page.tsxShow Forms tabs on Act +10/-6

Show Forms tabs on Act

• Mounts Forms secondary navigation beside existing Act and Standards content.

src/app/(search-app)/forms/act/page.tsx

page.tsxAdd Forms Clock route +26/-0

Add Forms Clock route

• Exposes the existing MHA clock as a Forms tab with a Forms breadcrumb and shared server-side form list.

src/app/(search-app)/forms/clock/page.tsx

error.tsxAdd safety-plan error boundary +7/-0

Add safety-plan error boundary

• Renders the shared error boundary for the relocated route.

src/app/(search-app)/safety-plan/error.tsx

page.tsxMove safety plan into app shell +0/-0

Move safety plan into app shell

• Relocates the route into the search-app group without changing its public URL.

src/app/(search-app)/safety-plan/page.tsx

search-results-header-band.tsxName empty-query sections +15/-3

Name empty-query sections

• Adds a section-name heading and accessible browse-region label when no query is present.

src/components/clinical-dashboard/search-results-header-band.tsx

dsm-search-page.tsxBrowse DSM by chapter +152/-27

Browse DSM by chapter

• Groups empty-query diagnoses into collapsible chapters with alphabetical, compact rows. Typed results stay flat and ranked.

src/components/dsm/dsm-search-page.tsx

forms-mode-nav.tsxProvide information-page Forms tabs +18/-0

Provide information-page Forms tabs

• Wraps registry navigation for Clock and Act, where the shell does not draw the bar.

src/components/forms/forms-mode-nav.tsx

forms-search-results-page.tsxClarify Forms register listing +4/-3

Clarify Forms register listing

• Replaces All forms headings with Forms and describes the existing form-code order as A to Z.

src/components/forms/forms-search-results-page.tsx

formulation-home-page.tsxAlphabetise formulation browsing +19/-4

Alphabetise formulation browsing

• Sorts empty-query mechanisms and concepts by name, removes Top match and names the browse section.

src/components/formulation/formulation-home-page.tsx

mode-nav-icons.tsAssign Forms tab icons +9/-0

Assign Forms tab icons

• Adds distinct icons for Forms, Clock and Act.

src/components/mode-nav/mode-nav-icons.ts

mha-clock-page.tsxExpand MHA clock handover copy +132/-25

Expand MHA clock handover copy

• Includes form names and displayed limits with qualifications in handover text. Adds a Forms breadcrumb option and a Clinical primary Start button.

src/components/psychiatry/mha-clock-page.tsx

psychiatry-home.tsxRemove duplicate quick actions +2/-2

Remove duplicate quick actions

• Removes Safety plan and MHA forms quick actions because Psychiatry already lists those destinations.

src/components/psychiatry/psychiatry-home.tsx

services-navigator-page.tsxLead service cards with Call +52/-13

Lead service cards with Call

• Shows a call link only for a dialable record phone and moves referral review and shortlisting behind More. The title still opens the record.

src/components/services/services-navigator-page.tsx

specifiers-home-page.tsxAlphabetise specifier browsing +21/-4

Alphabetise specifier browsing

• Sorts empty-query guide and catalogue lists, names the section and suppresses Top match.

src/components/specifiers/specifiers-home-page.tsx

pathways-screen.tsxExplain pathway status +28/-4

Explain pathway status

• Adds an explanation of Incomplete and counts linked therapy steps consistently with pathway rows.

src/components/therapy-compass/screens/pathways-screen.tsx

search-screen.tsxDistinguish therapy browsing +22/-4

Distinguish therapy browsing

• Alphabetises empty or non-searchable queries, names the Therapies section and preserves ranked presentation for searchable queries.

src/components/therapy-compass/screens/search-screen.tsx

therapy-card.tsxClarify and compact therapy cards +29/-5

Clarify and compact therapy cards

• Labels browse evidence Best used for rather than Why matched and shrinks desktop actions.

src/components/therapy-compass/therapy-card.tsx

mode-secondary-navigation.tsRegister Forms tabs +38/-1

Register Forms tabs

• Adds routes and active-tab rules for Forms, Clock and Act. Preserves list query and filters when returning to Forms.

src/lib/mode-secondary-navigation.ts

navigation-guards.tsAdd shell leave-guard registry +27/-0

Add shell leave-guard registry

• Provides registration and confirmation functions so pages with unsaved work can stop shell navigation.

src/lib/navigation-guards.ts

service-call.tsDerive safe service call targets +73/-0

Derive safe service call targets

• Shares phone extraction between cards and records, removes extensions and declines ambiguous or non-dialable values.

src/lib/service-call.ts

Bug fix (3) +188 / -19
global-search-shell.tsxHonor shell leave guards +6/-2

Honor shell leave guards

• Checks registered guards before mode navigation and starting a new answer chat.

src/components/clinical-dashboard/global-search-shell.tsx

patient-safety-plan.tsxAdapt safety plan to shell navigation +180/-15

Adapt safety plan to shell navigation

• Removes standalone-shell responsibilities, marks printable content and uses Psychiatry as the back fallback. Guards dirty plans against links, shell controls and browser history traversal.

src/components/patient-safety-plan.tsx

visits.tsExclude Clock from form rankings +2/-2

Exclude Clock from form rankings

• Treats the Forms clock route as a tool, not a form record, in Psychiatry visit summaries.

src/lib/psychiatry-hub/visits.ts

Refactor (3) +29 / -20
page.tsxShare clock-form selection +7/-10

Share clock-form selection

• Replaces route-local form selection with the common server-side helper.

src/app/(search-app)/psychiatry/mha-clock/page.tsx

service-detail-page.tsxReuse phone extraction +1/-10

Reuse phone extraction

• Imports the shared dialable-phone parser instead of maintaining a separate implementation.

src/components/services/service-detail-page.tsx

mha-clock-forms.tsCentralise eligible clock forms +21/-0

Centralise eligible clock forms

• Selects forms with governed timeline data on the server for both clock routes.

src/lib/psychiatry-hub/mha-clock-forms.ts

Tests (20) +961 / -42
browse-a-to-z.dom.test.tsxCover clinical browse ordering +182/-0

Cover clinical browse ordering

• Tests browse headings, DSM chapters and templates, plus formulation and specifier alphabetical lists.

tests/browse-a-to-z.dom.test.tsx

design-system-adoption.test.tsExpect new production route +2/-1

Expect new production route

• Adjusts adoption coverage expectations for Forms Clock.

tests/design-system-adoption.test.ts

forms-search-register-listing.dom.test.tsxVerify Forms browse headings +6/-3

Verify Forms browse headings

• Expects Forms and A to Z by form code on desktop and mobile.

tests/forms-search-register-listing.dom.test.tsx

mode-nav-addon-slot.dom.test.tsxCover Forms information-page tabs +6/-0

Cover Forms information-page tabs

• Adds Clock and Act to addon-slot ownership checks and includes the Forms navigation component.

tests/mode-nav-addon-slot.dom.test.tsx

mode-secondary-navigation.test.tsTest Forms tab routing +62/-3

Test Forms tab routing

• Checks destinations, active tabs, information-page treatment and preservation of list query and filters.

tests/mode-secondary-navigation.test.ts

navigation-guards.test.tsTest shell guard registration +31/-0

Test shell guard registration

• Verifies unguarded navigation, short-circuiting on refusal and guard removal.

tests/navigation-guards.test.ts

patient-safety-plan-privacy.dom.test.tsxExpect Psychiatry back fallback +2/-2

Expect Psychiatry back fallback

• Updates safety-plan back-navigation assertions for its new Psychiatry home.

tests/patient-safety-plan-privacy.dom.test.tsx

patient-safety-plan.dom.test.tsxExercise safety-plan leave guards +179/-13

Exercise safety-plan leave guards

• Tests print isolation, shell links, dirty-plan confirmation, Back sentinels and fallback navigation.

tests/patient-safety-plan.dom.test.tsx

psychiatry-hub-visits.test.tsKeep Clock out of form rankings +8/-0

Keep Clock out of form rankings

• Checks that Forms Clock does not count as opening a form record.

tests/psychiatry-hub-visits.test.ts

psychiatry-mha-clock.dom.test.tsxVerify clock handover and Forms context +145/-14

Verify clock handover and Forms context

• Tests copied limits and caveats, clipboard failure, breadcrumbs, Start styling and Psychiatry quick actions.

tests/psychiatry-mha-clock.dom.test.tsx

search-shell-props.test.tsTest safety-plan shell settings +10/-0

Test safety-plan shell settings

• Verifies Psychiatry mode and hidden composer on the exact safety-plan route.

tests/search-shell-props.test.ts

services-call-button.dom.test.tsxTest service calls and More actions +167/-0

Test service calls and More actions

• Covers phone formatting, extensions, missing or ambiguous numbers and secondary card actions.

tests/services-call-button.dom.test.tsx

therapy-browse-a-to-z.dom.test.tsxTest therapy browse and search states +121/-0

Test therapy browse and search states

• Checks alphabetical browse cards and neutral labels while retaining ranking language for searches.

tests/therapy-browse-a-to-z.dom.test.tsx

therapy-compass-responsive-contract.test.tsAssert compact desktop actions +5/-0

Assert compact desktop actions

• Checks the desktop therapy action layout and retained stretched card link.

tests/therapy-compass-responsive-contract.test.ts

therapy-pathways-mobile.dom.test.tsxTest pathway status explanation +15/-0

Test pathway status explanation

• Checks the Incomplete note's placement and linked-step count.

tests/therapy-pathways-mobile.dom.test.tsx

ui-accessibility.spec.tsExpect Therapies browse heading +4/-1

Expect Therapies browse heading

• Updates the accessibility journey to expect the section name instead of All.

tests/ui-accessibility.spec.ts

ui-mode-nav-density.spec.tsCover Forms tab density +1/-0

Cover Forms tab density

• Includes three-tab Forms navigation in browser density checks.

tests/ui-mode-nav-density.spec.ts

ui-overlap.spec.tsCheck expanded Services controls +7/-1

Check expanded Services controls

• Opens More before checking referral and shortlist controls, and includes the call link.

tests/ui-overlap.spec.ts

ui-smoke.spec.tsOpen More before shortlisting +5/-0

Open More before shortlisting

• Adapts the Services smoke journey to collapsed secondary actions.

tests/ui-smoke.spec.ts

ui-tools.spec.tsFollow referral through More +3/-4

Follow referral through More

• Opens the 13YARN card's More actions before its referral link.

tests/ui-tools.spec.ts

Documentation (5) +26 / -13
repo-awareness-snapshot.jsonRefresh discovered clinical routes +6/-1

Refresh discovered clinical routes

• Adds Forms Clock and records the safety plan's new route-group location.

data/repo-awareness-snapshot.json

codebase-index.mdDocument clinical route and clock changes +10/-4

Document clinical route and clock changes

• Updates the safety-plan path and explains the Forms tabs, shared clock-form source and handover content.

docs/codebase-index.md

ADOPTION.mdRefresh adoption counts +4/-4

Refresh adoption counts

• Updates product-root and production-route totals for the new Forms clock page.

docs/design-system/ADOPTION.md

site-map.mdList new and relocated routes +3/-2

List new and relocated routes

• Adds Forms Clock and updates the safety-plan description and source path.

docs/site-map.md

generate-site-map.tsAlign site-map generation +3/-2

Align site-map generation

• Changes the generated safety-plan description and ownership path to reflect its Psychiatry placement.

scripts/generate-site-map.ts

Other (5) +47 / -20
adoption-contract.jsonDeclare changed route ownership +2/-1

Declare changed route ownership

• Adds the Forms clock root and replaces the old safety-plan path.

docs/design-system/adoption-contract.json

adoption-manifest.jsonRegenerate adoption manifest +31/-18

Regenerate adoption manifest

• Records the clock route, relocated safety-plan root and resulting coverage totals.

docs/design-system/adoption-manifest.json

app-experience.jsonUpdate app-experience ownership +4/-1

Update app-experience ownership

• Removes the former safety-plan route pattern and registers new navigation, service-call and test files.

docs/organisation/systems/app-experience.json

registry-mode-nav.tsxSet compact Forms tab density +3/-0

Set compact Forms tab density

• Registers the Forms tabs with the compact navigation profile.

src/components/mode-nav/registry-mode-nav.tsx

search-shell-props.tsConfigure safety-plan shell +7/-0

Configure safety-plan shell

• Associates the safety plan with Psychiatry and hides its search composer.

src/lib/search-shell-props.ts

Resolve design-system adoption conflicts by regenerating; route count 224.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5U9yjzHumprWGGZ4R3hBj
@qodo-code-review

qodo-code-review Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

No findings for this group


Remediation recommended

No findings for this group


Informational

No findings for this group

Grey Divider


Resolved findings

1. Clock visits appear as recent forms ✗ Dismissed
Description
NOT_A_FORM excludes /forms/clock only in mostOpenedForms, while psychiatryVisitKindForPath
still classifies that route as a form record. With recent-search recording enabled, opening the
Clock tab adds a visit that can appear in Psychiatry’s resume or Continue rows as a form and count
toward its activity figures.
Code

src/lib/psychiatry-hub/visits.ts[307]

+const NOT_A_FORM = new Set(["/forms/act", "/forms/clock"]);
Relevance

●●● Strong

Recent visits.ts precedents accepted excluding non-record routes and correcting history/activity
contamination.

PR-#3248

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The review skill directs inspection of changed behavior and realistic failure paths. The changed
exclusion applies only to the most-opened selector; the route classifier still accepts
/forms/clock, and the shared-shell recorder records accepted routes for the Psychiatry history UI.

src/lib/psychiatry-hub/visits.ts[74-83]
src/lib/psychiatry-hub/visits.ts[306-322]
src/components/psychiatry/psychiatry-visit-recorder.tsx[24-58]
src/components/psychiatry/psychiatry-home.tsx[455-485]
Skill: review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Clock tab is excluded from the most-opened forms list but is still classified and recorded as a form record elsewhere in Psychiatry history.
## Fix Focus Areas
- src/lib/psychiatry-hub/visits.ts[74-83]
- src/lib/psychiatry-hub/visits.ts[306-307]
- tests/psychiatry-hub-visits.test.ts[117-124]
## Recommended Fix
Make the route classifier reject `/forms/clock` as a non-record page, while preserving actual form-detail routes. Add a test asserting that the classifier returns null for the clock route.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Duplicate entries fail the snapshot check ✓ Resolved
Description
documentation.documents in data/repo-awareness-snapshot.json lists
docs/design-system/ADOPTION.md three times, while the generator emits each tracked document path
once. In a Git checkout, checkRepoAwarenessSnapshot compares the committed and regenerated
documentation arrays, so the duplicate entries make the check fail and inflate the committed
document counts.
Code

data/repo-awareness-snapshot.json[R4604-4607]

+      {
+        "path": "docs/design-system/ADOPTION.md",
+        "section": "design-system",
+        "catalogued": true
Relevance

●●● Strong

Recent exact precedent accepted removing duplicate snapshot rows; PR #3424 addressed the same
ADOPTION.md duplication.

PR-#3424
PR-#3302

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Two added entries repeat an existing snapshot row for docs/design-system/ADOPTION.md. The
documentation builder maps each unique tracked path once, while the checker compares the complete
committed and regenerated documentation arrays, so those duplicate rows cause the comparison to
fail.

data/repo-awareness-snapshot.json[4594-4608]
scripts/generate-repo-awareness-snapshot.ts[250-277]
scripts/check-repo-awareness-snapshot.ts[114-119]
scripts/generate-repo-awareness-snapshot.ts[250-276]
scripts/check-repo-awareness-snapshot.ts[44-44]
Skill: code-quality-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The committed repository-awareness snapshot contains two extra entries for `docs/design-system/ADOPTION.md`, making its documentation inventory and counts differ from the generator's output.

## Fix Focus Areas
- data/repo-awareness-snapshot.json[4599-4608]

## Recommended Fix
Regenerate the snapshot with `npm run snapshot:repo-awareness`, retain the single generated entry for `docs/design-system/ADOPTION.md`, commit the resulting file, and run `npm run check:repo-awareness-snapshot` to verify it matches the repository.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Browser Back discards safety plans ✓ Resolved
Description
The new dirty-plan guard intercepts anchor clicks, but not client-side browser history navigation.
After a clinician enters draft content, browser Back or a back swipe can unmount the safety plan
without either that guard or beforeunload confirming the loss.
Code

src/components/patient-safety-plan.tsx[R586-589]

+  useEffect(() => {
+    if (!isDirty) return;
+    const guardLinks = (event: globalThis.MouseEvent) => {
+      if (event.defaultPrevented || event.button !== 0) return;
Relevance

●●● Strong

Draft-loss guards are treated as reliability issues, including navigation paths that bypass existing
lifecycle protection.

PR-#2789
PR-#2184

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The privacy skill calls for tracing sensitive-data retention and failure paths. The new guard covers
clicks, while the moved route uses client-side shell navigation; browser history traversal bypasses
the click handler and does not unload the document.

src/components/patient-safety-plan.tsx[573-604]
src/app/(search-app)/layout.tsx[24-30]
Skill: privacy

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Moving the safety plan into the client-navigated shell leaves dirty drafts unprotected during browser history traversal.

## Fix Focus Areas
- src/components/patient-safety-plan.tsx[573-604]
- tests/patient-safety-plan.dom.test.tsx[275-310]

## Recommended Fix
Protect dirty drafts on client-side Back and forward navigation as well as link clicks, without silently discarding entries. Add a browser-history regression test that enters draft content and verifies both cancellation and confirmed departure.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View resolved (10)
4. Handover limits lose Act citations ✓ Resolved
Description
handoverLimitLine copies each limit’s trigger and status or deadline but omits its applicable
condition, caveat, Act wording, section and source link. When a clinician pastes a Form 3A handover
outside the clock page, the recipient gets only a generic reference note, not the on-screen
qualification that detention can end when the referral expires or its primary source.
Code

src/components/psychiatry/mha-clock-page.tsx[R137-143]

+function handoverLimitLine(item: MhaTimerItem): string {
+  const trigger = item.entry.trigger;
+  if (item.kind !== "countdown") {
+    return `  ${trigger}: ${item.reason === "not-calculable" ? MHA_CLOCK_HANDOVER_NOT_CALCULABLE : MHA_TIMELINE_AWAITING_REVIEW}`;
+  }
+  const label = item.expired ? "time limit passed" : "time limit";
+  return `  ${trigger}: ${label} ${formatPerthDateTime(item.deadline)}`;
Relevance

●●● Strong

The team accepts findings requiring clinically significant caveats, provenance, and source
qualifications to remain visible.

PR-#3367
PR-#2838
PR-#2745

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The formatter uses the trigger and calculated status, while the page separately renders conditions,
Act quotations, caveats and a section-labelled link. The Form 3A entry includes an earlier-expiry
caveat, so its absence from clipboard output shows that the copied limit lacks both a clinically
significant qualification and the primary citation displayed on screen.

src/components/psychiatry/mha-clock-page.tsx[137-143]
src/components/psychiatry/mha-clock-page.tsx[393-447]
src/components/psychiatry/mha-clock-page.tsx[385-394]
src/components/psychiatry/mha-clock-page.tsx[427-452]
data/mha-timeframes.json[28-44]
docs/clinical-hazard-analysis.md[199-201]
Skill: clinical

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Copied MHA time limits separate statutory deadlines from the applicable conditions, caveats and section-specific Act evidence displayed beside them on screen.

## Fix Focus Areas
- src/components/psychiatry/mha-clock-page.tsx[137-143]
- src/components/psychiatry/mha-clock-page.tsx[166-173]
- tests/psychiatry-mha-clock.dom.test.tsx[270-305]

## Recommended Fix
Include each copied limit’s applicable condition and caveat, Act wording and section, and a usable primary-source link alongside its status or deadline. Retain the existing disclaimer and reference note, and add clipboard-output assertions for countdown and quote-only limits, including Form 3A’s earlier-expiry caveat.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Cancelling a mode switch loses focus ✓ Resolved
Description
selectAppMode closes the mode picker before calling confirmShellNavigation() and returns on
refusal without setting the pending focus-restoration ref or focusing the mode button. When a
keyboard user with an unsaved safety plan declines a standalone mode switch on desktop, the focused
menu option is removed while the page remains open.
Code

src/components/clinical-dashboard/master-search-header.tsx[963]

+      if (!confirmShellNavigation()) return;
Relevance

●●● Strong

Accepted focus-restoration precedents support fixing stranded keyboard focus when dismissing or
changing clinical UI state.

PR-#2589
PR-#2370

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
selectAppMode closes the picker at line 960, but the confirmation guard returns at line 963 before
setting pendingModeSelectionFocusRef. The focus-restoration effect depends on that ref, and the
desktop picker containing the selected option is rendered only while modeMenuOpen is true, so
declining the prompt leaves the page open after removing the focused option.

src/components/clinical-dashboard/master-search-header.tsx[960-965]
src/components/clinical-dashboard/master-search-header.tsx[648-667]
src/components/clinical-dashboard/master-search-header.tsx[959-969]
src/components/clinical-dashboard/master-search-header.tsx[647-659]
src/components/clinical-dashboard/master-search-header.tsx[2785-2816]
src/components/patient-safety-plan.tsx[683-696]
Skill: accessibility-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Declining the unsaved-work prompt closes the desktop mode picker and removes its focused option even though navigation is cancelled.

## Fix Focus Areas
- src/components/clinical-dashboard/master-search-header.tsx[959-969]
- src/components/clinical-dashboard/master-search-header.tsx[647-659]
- tests/master-search-header.dom.test.tsx[210-227]

## Recommended Fix
Check the navigation guard before closing the picker for standalone selections. If the user declines, keep the picker open and focus on the selected option; if they approve, retain the existing close and focus-restoration behavior. Add a keyboard-focused test that declines the guard and asserts focus remains on a usable control.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Back stalls after reloading an empty plan ✓ Resolved
Description
The stale-sentinel branch keeps the extra same-URL history entry even though the reloaded plan
starts empty and never installs the dirty-plan history guard. After a reload on that entry, the
first browser Back lands on the original safety-plan entry rather than the page the clinician came
from.
Code

src/components/patient-safety-plan.tsx[R661-666]

+    if (!onPlanSentinel()) {
+      const state = window.history.state as Record<string, unknown>;
+      const marker = state[PLAN_SENTINEL_KEY];
+      const fields = typeof marker === "object" && marker !== null ? marker : {};
+      window.history.replaceState({ ...state, [PLAN_SENTINEL_KEY]: { ...fields, documentId: PLAN_DOCUMENT_ID } }, "");
+      return;
Relevance

●●● Strong

Concrete reload/history correctness bug; recent precedents accept fixes preventing stale navigation
state and preserving intended history behavior.

PR-#2184
PR-#2393

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
pushPlanSentinel creates an additional entry without changing the URL. Adoption retains that
entry, while the freshly initialized plan is not dirty, so the guard effect returns before
installing its popstate handler; Back consequently traverses to the original entry at the same URL.

src/components/patient-safety-plan.tsx[94-98]
src/components/patient-safety-plan.tsx[560-567]
src/components/patient-safety-plan.tsx[618-628]
src/components/patient-safety-plan.tsx[657-666]
src/components/patient-safety-plan.tsx[683-684]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Reload adoption retains a duplicate safety-plan history entry while the fresh, empty plan has no guard to handle Back.
## Fix Focus Areas
- src/components/patient-safety-plan.tsx[657-667]
## Recommended Fix
When adopting a stale sentinel for an empty plan, return to the original plan history entry without leaving the page. Preserve the recorded prior-history value for any later dirty guard, and test browser Back after reload.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Reloading the safety plan sends you elsewhere ✓ Resolved
Description
pushPlanSentinel writes the marker, now including hadHistoryBefore, into history.state, and
browsers keep that state across a reload. When the page mounts, the code reads it with
sentinelHadHistoryBefore() and the effect that checks onPlanSentinel(). If a clinician reloads
while the plan has entries (they confirm the beforeunload warning), the fresh page sees the
marker, removes it, and calls history.go(-2) or router.replace('/psychiatry'). They land on the
previous page or the Psychiatry home instead of an empty safety plan, and the same happens when a
crashed or restored tab reopens on that entry.
Code

src/components/patient-safety-plan.tsx[R83-86]

+function pushPlanSentinel(hadHistoryBefore: boolean) {
+  const state: unknown = window.history.state;
+  const base = typeof state === "object" && state !== null ? state : {};
+  window.history.pushState({ ...base, [PLAN_SENTINEL_KEY]: { hadHistoryBefore } }, "");
Relevance

●●● Strong

Recent history-navigation findings were accepted, including reload-preserved state and URL-driven
navigation regressions.

PR-#3462
PR-#2393

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The marker lives in history.state (line 86), which persists across a reload of the same history
entry. As far as I recall, Next's App Router keeps the custom history state on its first load,
merging it in rather than replacing it; this was not checked against the installed Next version. The
mount effect only checks whether the marker is present, so it cannot tell a reload apart from
returning to the plan with Back, and it always navigates away.

src/components/patient-safety-plan.tsx[74-100]
src/components/patient-safety-plan.tsx[633-655]
src/components/patient-safety-plan.tsx[620-629]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The safety plan's extra Back entry keeps its marker in `history.state`, and the browser keeps that state on reload. After a reload, or when a restored tab reopens on that entry, the mount effect thinks the clinician came back to it with Back and navigates away (`history.go(-2)` or `router.replace` to Psychiatry) instead of showing the plan.

## Fix Focus Areas
- src/components/patient-safety-plan.tsx[83-100]
- src/components/patient-safety-plan.tsx[633-655]

## Recommended Fix
Only skip the extra entry when the clinician really returned to it with Back:
- Check `performance.getEntriesByType('navigation')[0]?.type`. When it is `'reload'`, or when this is the document's first render rather than a client-side mount, just remove the marker with `replaceState` and stay on the page.
- Alternatively, store a per-document id in the marker (for example a module-level random token). If the id does not match the current document, treat the marker as stale: remove it and do not navigate.

Add a DOM test that mounts the plan with the marker already in `history.state` and the navigation type set to reload, and assert that neither `history.go` nor `router.replace` is called.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Copied handover hides that a review repeats ✓ Resolved
Description
handoverLimitLines builds a countdown's line from only the trigger, the label and
formatPerthDateTime(item.deadline). It drops item.repeatsEveryHours, which LimitRow shows on
screen as "Repeats every N hours while the order is in force." Because firstOfEach keeps only the
next deadline of a recurring review, the pasted handover shows a single end time. Someone reading it
after the paste has no sign that another review falls due once that time has passed.
Code

src/components/psychiatry/mha-clock-page.tsx[R161-170]

+  } else {
+    const label = item.expired ? "time limit passed" : "time limit";
+    head = `  ${trigger}: ${label} ${formatPerthDateTime(item.deadline)}`;
+  }
+  return [
+    head,
+    ...(entry.condition ? [`    ${entry.condition}`] : []),
+    ...(entry.caveat ? [`    ${mhaClockCaveatLine(entry.caveat)}`] : []),
+    `    ${mhaClockSectionLabel(entry)}`,
+  ];
Relevance

●●● Strong

Recurring deadline’s repeat interval is a user-visible qualification missing from handover;
analogous recurrence omissions were accepted.

PR-#3147
PR-#3139

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
On screen, LimitRow shows the repeat interval for every countdown that has one (lines 447-449). The
handover helper copies the condition, caveat and section, but never repeatsEveryHours.
firstOfEach (lines 683-691) cuts each recurring entry down to its next deadline, so nothing else
in the copied text says the limit repeats. The helper's docstring claims the copy carries "the same
qualifications the page shows beside the limit", but the repeat interval is missing.

src/components/psychiatry/mha-clock-page.tsx[155-171]
src/components/psychiatry/mha-clock-page.tsx[424-450]
src/components/psychiatry/mha-clock-page.tsx[682-691]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The MHA clock handover copy leaves out the "Repeats every N hours while the order is in force." line that the page shows for recurring countdowns. A pasted handover therefore shows a recurring review as a single deadline.

## Fix Focus Areas
- src/components/psychiatry/mha-clock-page.tsx[155-171]
- src/components/psychiatry/mha-clock-page.tsx[447-449]

## Recommended Fix
Move the repeat sentence into a shared helper (for example `mhaClockRepeatLine(hours)`) and use it in both places: in `LimitRow`, and in `handoverLimitLines`. In `handoverLimitLines`, when `item.kind === "countdown" && item.repeatsEveryHours !== null`, add it as an indented sub-line (`    ${mhaClockRepeatLine(item.repeatsEveryHours)}`). Add a test that covers a recurring form.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Back returns to a cleared safety plan ✓ Resolved
Description
dropPlanSentinel() moves back only one entry when a returning safety-plan page mounts on the
sentinel. After a clinician approves navigation away from a dirty plan, Back from the destination
lands on that sentinel and then on the original safety-plan entry, remounting the plan without its
entries instead of reaching the page before it.
Code

src/components/patient-safety-plan.tsx[R707-710]

+  // Arriving on a sentinel left behind when an in-app link or shell control left a dirty
+  // plan: step off it, so Back from here goes to the page before, not this page again.
+  useEffect(() => {
+    dropPlanSentinel();
Relevance

●●● Strong

Concrete sentinel traversal bug causes Back to remount cleared safety-plan state instead of reaching
the prior page.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
A dirty plan pushes a same-URL sentinel; an approved shell action pushes a new destination without
removing it. On return, the mount effect calls a helper that traverses just one entry, leaving the
original plan entry current.

src/components/patient-safety-plan.tsx[83-100]
src/components/patient-safety-plan.tsx[661-663]
src/components/patient-safety-plan.tsx[696-711]
src/components/clinical-dashboard/global-search-shell.tsx[801-825]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After an approved in-app navigation from a dirty safety plan, browser Back reaches the leftover sentinel. The mount cleanup steps back only to the original safety-plan entry, so the clinician sees a newly mounted, empty plan.

## Fix Focus Areas
- src/components/patient-safety-plan.tsx[89-100]
- src/components/patient-safety-plan.tsx[696-711]

## Recommended Fix
Distinguish a sentinel reached by returning from another route from one being removed when a plan is cleared. On return, traverse past both the sentinel and original plan entry, with an appropriate fallback when there is no earlier entry. Keep the single-step cleanup for clearing a plan in place, and test the full destination-to-Back history sequence.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Safety plan Back can confirm and then stay put ✓ Resolved
Description
leaveByBackRef sends the user back with history.go(-2) whenever hadHistoryBefore is true. That
value comes from canNavigateBack(), which falls back to window.history.length > 1 when the
browser has no Navigation API. In a tab where the safety plan is the first entry, filling in the
plan, clearing it (dropPlanSentinel steps back but leaves the sentinel as a forward entry) and
filling it in again makes history.length 2. hadHistoryBefore is then wrongly true, so after the
clinician confirms, go(-2) from index 1 does nothing: the guard is already detached and the router
fallback is never taken.
Code

src/components/patient-safety-plan.tsx[R686-693]

+      detach();
+      if (!onPlanSentinel()) return "fallback";
+      if (hadHistoryBefore) {
+        // Skip the sentinel and this page's own entry together.
+        window.history.go(-2);
+        return "handled";
+      }
+      return "fallback";
Relevance

●●● Strong

Concrete reproducible history bug; the fallback counts forward entries, making go(-2) ineffective.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
canNavigateBack uses history.length > 1 when navigation.canGoBack is missing, and that count
includes forward entries. dropPlanSentinel uses history.back(), so the sentinel stays in the
forward stack and is still counted. The next dirty effect captures hadHistoryBefore as true, and
leaveByBack then returns "handled" after go(-2), so onBeforeNavigate returns false and nothing
navigates.

src/components/contextual-back-link.tsx[15-19]
src/components/patient-safety-plan.tsx[94-100]
src/components/patient-safety-plan.tsx[661-663]
src/components/patient-safety-plan.tsx[820-827]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The safety plan's header Back calls `history.go(-2)` based on `canNavigateBack()`. Without the Navigation API, that check falls back to `history.length`, which also counts forward entries (such as an old sentinel left by `dropPlanSentinel`). The jump can then target an entry that does not exist, and nothing happens after the clinician confirms leaving.

## Fix Focus Areas
- src/components/patient-safety-plan.tsx[661-663]
- src/components/patient-safety-plan.tsx[684-694]

## Recommended Fix
Work out `hadHistoryBefore` once on first mount, before any sentinel is pushed, and keep it in a ref (or record a 'has previous entry' flag in the plan's own history state). Do not recompute it from `history.length` each time the dirty effect runs. Alternatively, after `go(-2)`, check on the next tick whether the location still equals `planHref`; if it does, fall back to `router.replace(appModeHomeHref("psychiatry"))`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Punctuation browse claims a search ✓ Resolved
Description
SearchScreen treats punctuation-only input as browsing, but passes the unnormalized query to
SearchResultsHeaderBand, which labels any nonempty query as search results. For input such as
?!-, the cards appear as an A-to-Z browse list while the heading and region label claim they are
search results.
Code

src/components/therapy-compass/screens/search-screen.tsx[R37-39]

+  // "Nothing typed" uses the scorer's own normalisation, so punctuation alone
+  // (which the scorer cannot rank on) is browsing too.
+  const browsing = !hasSearchableTherapyQuery(q);
Relevance

●●● Strong

Accepted precedent flags punctuation-only queries as non-ranked; the heading must use the same
normalized browse state.

PR-#2342

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new browse condition uses the scorer's normalization, while the results band decides whether to
say “Search results for” using only query.trim(). Punctuation survives trimming but not scorer
normalization.

src/components/therapy-compass/screens/search-screen.tsx[37-45]
src/components/clinical-dashboard/search-results-header-band.tsx[253-261]
src/lib/therapy-ranking.ts[12-25]
Skill: ux-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Punctuation-only therapy queries produce browse cards but a search-results heading and region label.

## Fix Focus Areas
- src/components/therapy-compass/screens/search-screen.tsx[37-45]
- src/components/therapy-compass/screens/search-screen.tsx[178-183]
- tests/therapy-browse-a-to-z.dom.test.tsx[97-110]

## Recommended Fix
Pass an empty query to the results band when the scorer finds no searchable tokens, without changing the composer input. Extend the punctuation-only test to assert the browse heading and region label.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Shell buttons discard a safety plan without asking ✓ Resolved
Description
PatientSafetyPlan's guardLinks effect checks only clicks on <a href>, while the shell's mode
menu and sidebar New chat button navigate through router.push without consulting it. When a
clinician has entered plan items and uses either button, navigation bypasses the confirmation and
unmounts the component holding the entries and dirty state in local state.
Code

src/components/patient-safety-plan.tsx[R588-592]

+    const guardLinks = (event: globalThis.MouseEvent) => {
+      if (event.defaultPrevented || event.button !== 0) return;
+      if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
+      const anchor = (event.target as Element | null)?.closest?.("a[href]");
+      if (!(anchor instanceof HTMLAnchorElement)) return;
Relevance

●●● Strong

Client-side navigation state-loss findings are accepted when controls bypass draft protection and
unmount local clinical data.

PR-#2789
PR-#2184

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The click listener returns when the target is not inside an anchor, whereas the shell's changeMode
and startNewAnswerChat handlers navigate with router.push from button controls; selectAppMode
reaches changeMode through onSearchModeChange. These client-side transitions neither pass
through the anchor guard nor fire beforeunload, and navigation unmounts the safety-plan component
that holds the entries and dirty state locally.

src/components/patient-safety-plan.tsx[586-604]
src/components/clinical-dashboard/global-search-shell.tsx[777-838]
src/components/clinical-dashboard/ClinicalSidebar.tsx[1129-1138]
src/components/clinical-dashboard/master-search-header.tsx[977-1006]
src/lib/search-shell-props.ts[118-123]
src/components/clinical-dashboard/global-search-shell.tsx[804-827]
src/components/patient-safety-plan.tsx[502-508]
src/app/(search-app)/layout.tsx[24-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The safety plan's unsaved-entries guard intercepts anchor clicks but not shell controls that navigate with `router.push`. Using the mode menu or New chat button with a dirty plan leaves the page without the existing confirmation and discards the locally held entries.

## Fix Focus Areas
- src/components/patient-safety-plan.tsx[583-604]
- src/components/clinical-dashboard/global-search-shell.tsx[777-838]

## Recommended Fix
Add a shared route-transition guard for shell-initiated navigation, such as a context or module-level registry of `beforeNavigate` callbacks. Have `changeMode`, `navigateToMode`, and `startNewAnswerChat` consult it before calling `router.push`; register the safety plan's `isDirty` confirmation using `LEAVE_PLAN_PROMPT`, alongside the existing anchor guard. Add a DOM test that enters plan items and then clicks a shell mode button to verify the confirmation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. Forms tab drops the chosen A–Z sort ✓ Resolved
Description
In modeSecondaryNavigationHref, the forms-list branch carries only q, run, category, risk
and availability. It leaves out sort, which useResultSort reads to drive the forms list's
"Sorted A–Z" order. A clinician who sorted the list A–Z and taps the Forms tab (or comes back from
Clock or Act) gets the default order again, despite the comment saying the tab never clears the
results.
Code

src/lib/mode-secondary-navigation.ts[R787-790]

+    const filters = (["category", "risk", "availability"] as const).flatMap((key) => {
+      const value = currentSearchParams.get(key);
+      return value ? [[key, value] as const] : [];
+    });
Relevance

●●● Strong

Repository precedent accepts preserving URL state across navigation; omitting sort visibly resets
the chosen ordering.

PR-#2567
PR-#3152

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The forms results page orders its list with sortResultItems(filteredMatches, sortValue, ...), and
useResultSort reads sortValue from the URL's sort param. The new tab href rebuilds the query
from a fixed key list that leaves sort out.

src/components/use-result-sort.ts[8-12]
src/components/forms/forms-search-results-page.tsx[701-723]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Forms tab href keeps the query and filters but drops `sort`, so the chosen A–Z order is lost when the tab is tapped.

## Fix Focus Areas
- src/lib/mode-secondary-navigation.ts[781-796]

## Recommended Fix
Add "sort" to the list of carried keys (`["category", "risk", "availability", "sort"]`), or change the comment so it no longer says the tab keeps every result setting.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 9 rules
✅ Cross-repo context — repo relationships
  Explored: repo: BigSimmo/Medications (sha: f0fe3d04) — View relationship
✅ REVIEW.md
Review mode: Auto: 🚀 Fast: Localized test expectation update for a single navigation helper change

Grey Divider

Tip of the day
💡 Did you know, you can tweak Display settings with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/components/patient-safety-plan.tsx Resolved
Comment thread src/components/patient-safety-plan.tsx Resolved
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 11, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

…e first card is shortlisted

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5U9yjzHumprWGGZ4R3hBj
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 11, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit e148713

…ved work

The header's direct router.push calls (standalone modes, option shortcuts) now go through
confirmShellNavigation(), so a dirty safety plan is no longer dropped from the mode picker.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5U9yjzHumprWGGZ4R3hBj
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 234f503

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5U9yjzHumprWGGZ4R3hBj
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 11, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 11, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit d970bdd

…nd focus

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5U9yjzHumprWGGZ4R3hBj
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 11, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 40a36df

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5U9yjzHumprWGGZ4R3hBj

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit f6b3052

@gitar-bot

gitar-bot Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor
Code Review ✅ Approved 5 closed / 5 findings

🔴 High risk · Adding copied MHA deadlines and statutory caveats to handovers could lead to clinical decisions based on stale or misinterpreted timing.

Adds a Call button to Services results, A to Z browse lists, Forms Clock and Act tabs, and a safety plan header under Psychiatry. Unsaved safety plan leave guards now cover shell navigation and Back, and reload sentinel handling is fixed. No open issues remain.

Auto-approved: No blocking issues found.
Please see Auto-approve Docs for details on setting custom approval criteria.

✅ 5 closed
✅ Bug: Shell links can now drop an unsaved safety plan without warning

📄 src/components/patient-safety-plan.tsx:559-573 📄 src/components/patient-safety-plan.tsx:684-692 📄 src/lib/search-shell-props.ts:121-123
The safety plan guards unsaved work in two ways: a beforeunload listener (lines 572-578) and a window.confirm in NavigationBackButton.onBeforeNavigate (line 688). Before this PR, /safety-plan sat outside the shell, so the back button was its only in-app way out. Now it renders inside the (search-app) shell, which has a header, a side menu and mode links. Those are client-side Next <Link> navigations, so beforeunload never fires and the back-button confirm is skipped. One tap on the header or menu unmounts the page, and since the plan stores nothing, the whole half-built plan is gone with no prompt. Fix: block in-app navigation while isDirty is true. One way is to capture clicks on internal anchors (document-level, in capture phase) and ask with the same confirm. Another is to have the shell check a shared 'unsaved work' flag that the safety plan sets. Add a DOM test that clicks a shell link with a dirty plan.

✅ Bug: Back-gesture guard runs after the App Router has already navigated

📄 src/components/patient-safety-plan.tsx:607-621
This repo uses the App Router (src/app, next 16.3.8). The router registers its own popstate listener when it mounts, long before this effect adds guardHistory, so on Back/Forward Next has already queued the traversal to the destination tree before confirm runs. If the clinician presses Cancel, pushState(currentState, …) only puts back the URL. Next's patched pushState skips its restore step for state that already carries __NA, so the previous route still renders, the plan unmounts and the entries are lost. The URL bar still shows the safety-plan path and a stray duplicate history entry is left behind. App Router history state also has no idx, so currentIndex is always 0 and direction is always -1, which means the forward/back detection never works. Fix: stop relying on cancelling popstate. Push a sentinel history entry when the plan becomes dirty so the first Back only pops that sentinel and no route change happens. Then confirm, and call history.back() again only when the clinician confirms. Alternatively, guard through a listener registered before the router's (capture on window at module load) and check the outcome with a browser test.

✅ Edge Case: Back history check on a returned sentinel is always true

📄 src/components/patient-safety-plan.tsx:620-623 📄 src/components/patient-safety-plan.tsx:632-642 📄 src/components/patient-safety-plan.tsx:685 📄 src/components/patient-safety-plan.tsx:711-714
When the page remounts on a leftover sentinel, hadHistoryBeforeRef is set by canNavigateBack() while the sentinel is the current entry. The original plan entry sits right behind it, so the check always returns true. That means the router.replace(appModeHomeHref("psychiatry")) fallback can never run from this path. Trigger: a tab opened directly on the plan, entries added, an in-app link confirmed, then Back. History is [plan, sentinel, next], and on the sentinel go(-2) asks for index -1, which the browser ignores. The reader stays on the plan with a duplicate entry behind it, so the next Back appears to do nothing. The same wrong true then feeds leaveByBackRef, so the header Back control's go(-2) can also land back on the plan instead of falling back to Psychiatry. Suggested fix: record whether there was history before the original plan entry when the sentinel is first pushed, store it in the sentinel's state (for example { [PLAN_SENTINEL_KEY]: { hadHistoryBefore } }), and read that value back on return instead of calling canNavigateBack() again.

✅ Edge Case: After reload, confirming leave lands on another plan entry, guard off

📄 src/components/patient-safety-plan.tsx:662-671 📄 src/components/patient-safety-plan.tsx:722-736
While a plan has entries, the current history entry is always the sentinel, so reloading a dirty plan leaves history as [prev, plan, plan(stale sentinel)]. The new branch removes the marker and stays on the page, but that entry is still a second copy of the plan URL. Once the user types into the fresh plan, the guard pushes a new sentinel: [prev, plan, plan, S].

If they then press Back and confirm, history.back() lands on the original plan entry instead of the page before. The header back control's go(-2) lands there too. Either way the user is still on the safety plan with the guard detached. Their next Back leaves without asking, and the confirmed leave did not leave. A simple fix is to re-stamp a stale marker with this document's PLAN_DOCUMENT_ID instead of deleting it. The guard then reuses it as its own sentinel, and the Back step and go(-2) again skip both plan entries.

Closed: Edge Case: Reload on a sentinel makes history.back() load the page a second time

📄 src/components/patient-safety-plan.tsx:656-666 📄 tests/patient-safety-plan.dom.test.tsx:416-430
After a reload, the sentinel entry gets a new document. The plan's own entry still belongs to the old, discarded document. Under the HTML session-history model, the new window.history.back() is therefore a cross-document traversal, not a same-document popstate. The plan page fully loads again right after the reload: the user sees a second load. If they start typing before it lands, the dirty beforeunload guard fires a browser leave prompt on a page they never chose to leave. The DOM test mocks back, so it can't show this. Instead, keep the leftover entry, as the previous commit did, and record on it that it should be skipped. Or call back() only when the arrival was a real Back traversal (for example performance.getEntriesByType('navigation')[0].type === 'back_forward'), not a reload.

Review coverage

🤖 Auto-approval Approved

📋 Rules No rules evaluated

🧪 Functional validation Not enabled · Set up

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Copy link
Copy Markdown
Owner Author

Unit coverage partition (1) failed on one timing assertion in tests/challenger-m3-2-sql-and-lighthouse-stress.test.ts (the SQL comment-stripping speed check came in just over its 100 ms limit under coverage instrumentation). This PR does not touch SQL comment stripping or that test; the other 12,907 tests in the partition passed. No fix exists to port. Re-running the failed jobs once when the run finishes.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔴 Large blast radius size:XXL This PR changes 1000+ lines, ignoring generated files 🕐 40+ Minutes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants