Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
106fa8f
feat(home): consolidate DSM, Dictionary and Factsheets onto the share…
claude Aug 18, 2026
bd6e09d
feat(home): consolidate the remaining seven mode homes onto the share…
claude Aug 18, 2026
57ecb20
feat(home): consolidate Documents onto the shared home
claude Aug 18, 2026
da2fb7f
fix(home): forward a submitted deep link to the mode's own results su…
claude Aug 18, 2026
ba0a375
test(ui): drop the redirected /dictionary from the Dictionary route s…
claude Aug 18, 2026
aa08d0c
fix(home): keep Therapy and Documents out of the consolidation, and a…
claude Aug 18, 2026
17e4044
Merge origin/main and resolve conflicts
Copilot Aug 18, 2026
3c116d1
fix(differentials): register /differentials/search for the phone comp…
claude Aug 18, 2026
2a24dd3
fix(docs): align the adoption route count with the manifest (75)
claude Aug 18, 2026
150394e
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
ebe7d66
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
de160b6
feat(home): consolidate Documents and Therapy onto the shared home
claude Aug 18, 2026
175ba5d
Merge remote-tracking branch 'origin/claude/lightweight-mode-homes' i…
claude Aug 18, 2026
24f3999
test(ui): retarget the Documents and Therapy browser journeys
claude Aug 18, 2026
dc620e7
docs(index): index /calculators/search in the route table
claude Aug 18, 2026
3928df0
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
1cffafb
fix(test): update stale search-route assertions for lightweight mode …
claude Aug 18, 2026
354339a
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
9a827f3
docs(ledger): record the Run PR sweep review for #2157
claude Aug 18, 2026
e04a19f
Merge remote-tracking branch 'origin/claude/lightweight-mode-homes' i…
claude Aug 18, 2026
f53668c
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
4b600c9
fix(routing): treat `search` as a reserved route suffix, not a record…
claude Aug 18, 2026
4ff5285
Merge remote-tracking branch 'origin/claude/lightweight-mode-homes' i…
claude Aug 18, 2026
4daf042
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
34bdfd9
fix(perf): drop the redirecting mode routes from the Lighthouse and b…
claude Aug 18, 2026
a4de684
Merge remote-tracking branch 'origin/claude/lightweight-mode-homes' i…
claude Aug 18, 2026
ef5ca49
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
084fef0
Merge remote-tracking branch 'origin/claude/lightweight-mode-homes' i…
claude Aug 18, 2026
caa8479
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
ba6bb9e
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
60f4495
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 18, 2026
3c4a786
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 19, 2026
64f4c4f
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 19, 2026
923fec7
test(mockups): derive tool counts from the fixture instead of pinning…
claude Aug 19, 2026
f7736c7
Merge remote-tracking branch 'origin/claude/lightweight-mode-homes' i…
claude Aug 19, 2026
7d96781
fix(home): close the two real gaps the review found, and forward para…
claude Aug 19, 2026
a91451b
fix(routing,docs): resolve unsubmitted mode searches in the proxy, an…
claude Aug 19, 2026
e87aa4f
fix(home): restore the Documents workspace the shared-home fold-in de…
BigSimmo Aug 19, 2026
9b2145e
test(ui): exercise the Documents actions from where they now live
claude Aug 19, 2026
65b5ae3
fix(home): keep the empty-query browse view for differentials/formula…
BigSimmo Aug 19, 2026
f747ef9
test(ui): restore the pre-consolidation Documents Playwright specs
BigSimmo Aug 19, 2026
d2d7f3d
Merge origin/claude/lightweight-mode-homes, keep the restored Documen…
BigSimmo Aug 19, 2026
fe372d6
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 19, 2026
45ad8f6
docs(ledger): record the PR #2157 review at d2d7f3d
BigSimmo Aug 19, 2026
b940ac7
Merge remote-tracking branch 'origin/claude/lightweight-mode-homes' i…
BigSimmo Aug 19, 2026
914a487
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 19, 2026
54265d8
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 19, 2026
a01bcbd
fix(differentials): edit-selection href always targets /differentials…
Copilot Aug 19, 2026
b676b07
Merge branch 'main' into claude/lightweight-mode-homes
BigSimmo Aug 19, 2026
f395dce
Initial plan (#2177)
Copilot Aug 19, 2026
aad3a20
ci: bound and retry the Chromium apt-deps install in setup-ui-e2e
claude Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions .github/actions/setup-ui-e2e/action.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,13 +23,27 @@ runs:
playwright-chromium-${{ runner.os }}-

# The browser archive is cached, but apt libraries are runner-local and must be
# installed even on a browser-cache hit.
# installed even on a browser-cache hit. `install-deps` shells out to `apt-get
# update`, which occasionally hangs indefinitely against the runner's default
# azure.archive.ubuntu.com mirror (no timeout of its own) and silently burns the
# whole job until something external cancels it. Bound each attempt and retry so a
# stuck mirror fails fast instead of stalling the job for the better part of an hour.
- name: Install Chromium
shell: bash
run: |
if [ "${{ steps.pw-cache.outputs.cache-hit }}" = "true" ]; then
npx playwright install-deps chromium
npx playwright install chromium
else
npx playwright install --with-deps chromium
fi
install_chromium() {
if [ "${{ steps.pw-cache.outputs.cache-hit }}" = "true" ]; then
npx playwright install-deps chromium
npx playwright install chromium
else
npx playwright install --with-deps chromium
fi
}
for attempt in 1 2 3; do
if timeout 180 bash -c "$(declare -f install_chromium); install_chromium"; then
exit 0
fi
echo "Chromium install attempt $attempt timed out or failed, retrying..." >&2
sleep 5
done
exit 1
12 changes: 0 additions & 12 deletions bundle-budget.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,21 +14,9 @@
"gzipBytes": 203889,
"tolerancePct": 10
},
"/therapy-compass": {
"gzipBytes": 210999,
"tolerancePct": 10
},
"/documents/search": {
"gzipBytes": 206572,
"tolerancePct": 10
},
"/dsm": {
"gzipBytes": 206586,
"tolerancePct": 10
},
"/forms": {
"gzipBytes": 230048,
"tolerancePct": 10
}
},
"totalGzipBytes": 1708296,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
| 2026-08-19 | claude/lightweight-mode-homes | d2d7f3d361f89fe45791b5b79074804d718e4b9f | PR #2157 lightweight mode homes consolidation | Reviewed and fixed: PR policy body was empty, blocking merge. Restored Documents mode's dedicated workspace after another automated pass folded it into the generic shared home and deleted real functionality (recent documents, browse library, open-a-source-PDF) with no equivalent replacement -- confirmed live via browser and Playwright, and confirmed the account owner never actually approved that loss. Reverted a redirect-on-empty-query regression on differentials/formulation/specifiers search routes that two separate automated passes introduced, both breaking a pinned Playwright phone-scroll spec that neither pass's Vitest-only verification could catch. Fixed stale docs/comments (Therapy devOnly rationale, route tables, site-map descriptions). Full unit suite green (683 files), targeted Playwright verification on Chromium green for the restored Documents spec. | npm run test (683 files, full suite green); 187 targeted vitest re-runs across every touched file; npx tsc typecheck clean; npx eslint clean; npx prettier --check clean; Playwright tests/ui-smoke.spec.ts Documents @critical spec run live against dev server -- chromium passes end to end, webkit fails one narrow cross-browser focus-restoration assertion (non-functional) |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
| 2026-08-18 | claude/lightweight-mode-homes (PR #2157) | 1cffafbc172fc22625aa3cba62d5d685b7720f25 | Run PR sweep: CI fix + threads + drift | Before: PR mergeability/PR policy transiently failed (self-healed on retry, no real conflict); Production UI (3) failed 3 Chromium specs (ui-specifiers.spec.ts mobile composer, 2x ui-tools.spec.ts stale /differentials and /services route assertions) caused by this PR's own consolidated-mode-home routing change, not main drift. Fixed: updated the 3 stale test assertions to match the new /differentials/search, /?mode=services, and /specifiers/search routes already established elsewhere in this PR's own diff. After: fixes pushed, fresh CI re-running. Still blocked on a blank Clinical Governance Preflight in the PR body (human-owned, not auto-fillable) and a real Lighthouse TBT regression pattern matching known main-wide drift from a prior merge. | npx tsc --noEmit (clean), npx eslint tests/ui-tools.spec.ts tests/ui-specifiers.spec.ts (clean), npx prettier --write (no changes needed); Playwright not run locally (browser revision mismatch, chromium-1194 vs CI's 1234) — delegated to CI Production UI. No provider-backed checks run. |
6 changes: 3 additions & 3 deletions docs/codebase-index.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,7 +85,7 @@ Smaller top-level directories that are easy to miss:
| `/privacy` | `src/app/privacy/page.tsx` → `privacy-quiet-signal-page.tsx` + `privacy-page-content.tsx` |
| `/reference/colour-coding` | `src/app/reference/` |
| `/safety-plan` | `src/app/safety-plan/page.tsx` |
| `/calculators`| `src/app/(search-app)/calculators/page.tsx` |
| `/calculators`, `/calculators/search` | `src/app/(search-app)/calculators/` |
| `/services`, `/services/[slug]` | `src/app/(search-app)/services/` |
| `/therapy-compass` | `src/app/(search-app)/therapy-compass/` |
| `/tools` | `src/app/(search-app)/tools/` |
Expand DownExpand Up@@ -348,10 +348,10 @@ visible reasons and a human confirms or overrides.

One shared composer (`master-search-header.tsx`) serves every mode. Placement:

- **Mode homes** (`/services`, `/forms`, `/favourites`, `/differentials`, `/dsm`, `/specifiers`, `/formulation`, `/factsheets`, `/dictionary`, `/therapy-compass`, `/tools`, and dashboard homes): inline in the hero via the `mode-home-composer-slot` portal, on phone and tablet+ alike. (`/applications` is a redirect to `/tools`, not a composer surface.)
- **Mode homes**: the one shared home at `/?mode=<id>`, plus the three routes that still own a home of their own — `/medications` (the prescribing workspace, with dose/safety/monitoring checks), `/favourites` (a hub) and `/tools` (a launcher). None of those three is a duplicate of the shared home; each is its mode's only functional surface. Composer inline in the hero via the `mode-home-composer-slot` portal, on phone and tablet+ alike. The other eleven modes were consolidated onto the shared home: `/services`, `/forms`, `/differentials`, `/dsm`, `/specifiers`, `/formulation`, `/calculators`, `/factsheets`, `/dictionary`, `/therapy-compass` and `/documents` are now `redirect()` stubs (`src/lib/consolidated-mode-home-redirect.ts`, resolved in `src/proxy.ts` so they emit a real 307 rather than a streamed meta-refresh). Their per-mode copy is `sharedHomePresentation` in `src/lib/ui-copy.ts`; the retired detailed pages are preserved off the live routes at `/mockups/<mode>-home-detailed`. (`/applications` is a redirect to `/tools`, not a composer surface.)
- **Information (detail) pages**: catalogue/record routes under each mode (`/services/[slug]`, `/forms/[slug]`, `/medications/[slug]`, `/specifiers/[slug]`, `/formulation/[slug]`, `/factsheets/[slug]`, `/dictionary/[slug]`, `/dictionary/topics/[slug]`, `/therapy-compass/[slug]`, `/dsm/diagnoses/[slug]`, …). Route detection: `src/lib/information-pages.ts` (`isInformationPage`). Shared outer chrome: `src/components/information-page-shell.tsx` (`InformationPageShell`, breadcrumbs, optional footer). Specifier/formulation mode shells re-export that primitive. Intentional opt-outs: document viewer and the differentials presentation workflow.
- **Result and detail views**: fixed bottom dock on phone (compact variant on submitted searches), sticky top from `sm` up.
- **Results routing**: standalone routes own their submitted searches via `?q=…&run=1` (`/services` → `ServicesNavigatorPage`, `/forms` → `FormsSearchResultsPage`, `/differentials` → `DifferentialsHome` results view, `/formulation` → local mechanism results, `/favourites` filters the command library in place). Answer, Documents, and Prescribing submitted searches render inside `ClinicalDashboard` — intentional, since they need retrieval/answer state. Bare `/?mode=<id>` always renders the shared home with that mode preselected; only a submitted deep link (`q` plus `run=1`) resolves to the mode's own search surface (proxy early-redirect still covers favourites/differentials/specifiers for those submitted aliases).
- **Results routing**: each consolidated mode owns its submitted searches at `<mode>/search` (`/services/search` → `ServicesNavigatorPage`, `/forms/search` → `FormsSearchResultsPage`, `/differentials/search` → `DifferentialsHome` results view, `/formulation/search` → local mechanism results, and the same shape for dsm, dictionary, factsheets, specifiers, calculators, therapy-compass and documents). That split is not cosmetic: the bare path redirects to the shared home, so routing a submitted query back at it would loop — `consolidatedModeHomeModeIds` drives both halves from one list, and `tests/consolidated-mode-home-redirect.test.ts` pins the no-loop property. `/favourites` and `/tools` keep filtering in place on their own routes. Answer, Documents, and Prescribing submitted searches render inside `ClinicalDashboard` — intentional, since they need retrieval/answer state. Bare `/?mode=<id>` always renders the shared home with that mode preselected; only a submitted deep link (`q` plus `run=1`) resolves onward to the mode's own search surface.
- **Intentionally composer-free routes**: `/differentials/presentations/*` and `/differentials/compare` (comparison workflow owns its chrome), `/documents/[id]` viewer (has its own in-document ask composer), `/documents/source/*` (document flow owns mobile chrome). Do not re-flag these in search-consistency audits.
- **Shared in-page navigation**: `src/components/in-page-nav/` is the default template for section navigation on any mode page (`docs/search-chrome-behaviour.md`). `in-page-nav-header.tsx` (`InPageNavHeader`) owns the header row, both sheets and the `PhoneHeaderCollapsePortal` wrapper; `page-section-index.ts` (`PageSection`, `toDocumentSections`, `sectionTargetIds`) is the declaration shape; `use-resolved-page-sections.ts` narrows a declaration to the anchors actually rendered at this breakpoint; `use-in-page-section-nav.ts` composes that with `useDocumentSectionSpy` and `jumpToDocumentSection`; `use-page-section-weights.ts` measures segment weights; `use-in-page-chrome-metrics.ts` publishes `--inpage-anchor-offset`; `in-page-nav-classes.ts` holds the shared anchor (`inPageAnchor`) and actions-sheet row classes; `in-page-section-rail.tsx` (`InPageSectionRail`) is the optional visible second rail, opted into with `rail={{ label }}` by panel-swap routes with few sections (medications only) in place of the weighted track. Anchor measurement itself is `src/components/sticky-chrome-metrics.ts` (`useStickyChromeMetrics`), shared with the document viewer's `use-document-chrome-metrics.ts`. Mounted by `dictionary/dictionary-term-page.tsx`, Dictionary topic detail in `dictionary/dictionary-catalogue-pages.tsx`, `differentials/differential-detail-page.tsx`, `services/service-detail-page.tsx`, `forms/form-detail-page.tsx`, `dsm/dsm-differential-considerations-page.tsx`, and — through a colocated `"use client"` nav-header sibling that owns and exports the route's section table — `specifiers/specifier-nav-header.tsx`, `formulation/formulation-nav-header.tsx`, `dsm/dsm-diagnosis-nav-header.tsx`, `factsheets/factsheet-nav-header.tsx` and `clinical-dashboard/medication-nav-header.tsx`. The sibling is mandatory for the four Server Component pages (neither `onSelectSection` nor a `LucideIcon` crosses the RSC boundary) and the convention for the rest. Two adopters swap panels instead of scrolling — `differential-detail-page.tsx` and the medication record page — so they pass explicit weights, carry no `inPageAnchor`, and use neither `useResolvedPageSections` nor the scroll spy. Every declared section is pinned against rendered DOM by `tests/in-page-nav-route-sections.dom.test.tsx` (anchors for the scrolling routes, swapped-in panels for the tab routes) and focused Dictionary DOM contracts.
- **Shared secondary navigation**: `src/components/page-secondary-navigation.tsx` (`PageSecondaryNavigation`, mode destinations only). Mode destinations come from `src/lib/mode-secondary-navigation.ts` (`modeSecondaryNavigationRegistry`, no "Home" item). `GlobalSearchShell` renders it in normal flow at the top of `#main-content` for its owned namespaced modes; it self-suppresses on clean mode homes, on Therapy Compass, and on every information page — `hasLocalInformationPageNavigation` is now just `isInformationPage`, because each of those routes owns its own in-page navigation. The older shared `SecondaryNavigation` component was deleted here (`/issues #271`): its `section` kind and "On this page" pill rail went when the last six information routes moved onto `InPageNavHeader`, and the surviving `route`/`action` kinds had no production constructor left — `RegistryModeNav` renders `ModeNav`, not `SecondaryNavigation`, so the only remaining caller was its own test file, which went with it.
Expand Down
16 changes: 8 additions & 8 deletions docs/design-system/ADOPTION.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -359,12 +359,12 @@ product exclusions; the only route-only disposition is the documented legacy doc
redirect. Shared shell/component roots carry their own explicit `shared-shell` disposition.

Registered public components: 54
Declared product roots: 82
Declared product roots: 88
Roots with a literal `.ckb-v2` opt-in: 1
Roots inheriting `.ckb-v2` from the global `<html>`: 81
Roots inheriting `.ckb-v2` from the global `<html>`: 87
Production surfaces observed under v2: 15/15
Dynamic `ckb-v2` constructions: 0
Declared production page routes: 69/69
Declared production page routes: 75/75

Source observation and contract declaration are independent. A literal `ckb-v2` on the global `<html>` makes every production surface inherit v2, but it does not approve that adoption.
The Proof column summarizes each surface's dark, forced-colours, 320px, print and browser declarations; exact statuses and evidence paths live in the manifest.
Expand All@@ -373,15 +373,15 @@ Observed v2 under a compatibility declaration fails closed. A declared v2 shell
| Surface | Disposition | Routes | Roots | Declared shell | Observed shell (mount) | Proof | Baseline |
| ---------------------------------- | --------------- | -----: | ----: | -------------- | -------------------------- | -------------- | -------------- |
| `root-shell-and-settings` | shared-shell | 3 | 6 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `catalogues-forms-and-info` | owned | 21 | 21 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `differentials` | owned | 6 | 6 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `formulation` | owned | 5 | 5 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `specifiers` | owned | 5 | 5 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `catalogues-forms-and-info` | owned | 23 | 23 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `differentials` | owned | 7 | 7 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `formulation` | owned | 6 | 6 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `specifiers` | owned | 6 | 6 | v2 | v2 (inherited-global-root) | passed | not-committed |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| `therapy-compass` | owned | 9 | 10 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `documents-and-source-evidence` | owned | 3 | 4 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `documents-source-legacy-redirect` | legacy-redirect | 1 | 0 | v2 | v2 (inherited-global-root) | not-applicable | not-applicable |
| `favourites` | owned | 1 | 1 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `tools-and-calculators` | owned | 2 | 2 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `tools-and-calculators` | owned | 3 | 3 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `ward-management` | owned | 10 | 15 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `privacy-safety-and-reference` | owned | 3 | 3 | v2 | v2 (inherited-global-root) | passed | not-committed |
| `search-results-shared` | shared-shell | 0 | 1 | v2 | v2 (inherited-global-root) | passed | not-committed |
Expand Down
Loading
Loading