Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 33 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,39 @@ When a branch or PR review completes, record the reviewed branch/ref, HEAD SHA,

<!-- END:process-hardening -->

<!-- BEGIN:page-and-button-wiring -->

# Page and button wiring

Interactive controls and routes follow conventions the codebase already holds to. Before adding
or moving a button, link, or route, read `docs/wiring-conventions.md`. A control that advertises an
action must perform one; a page that ships must be reachable.

- **Buttons.** Every interactive `<button>` must do something: an `onClick`, a `type="submit"`
inside a `<form onSubmit>`, or navigation (wrap it in a `<Link>` / call `router.push`). A control
whose feature is not yet built uses the explicit disabled-placeholder pattern — `disabled` or
`aria-disabled="true"` + `title="… — coming soon"` + an `sr-only` note wired via
`aria-describedby` (see `favourites-hub.tsx`). **Never** ship a styled, `aria-label`led button
with no handler and no disabled state — that was the "Language and region" defect fixed
2026-07-21.
- **Navigation.** Internal navigation uses `<Link>`, `router.push`, or server `redirect()` — never
a raw `<a href="/…">` to an internal route. Build hrefs from the existing sources
(`src/lib/app-modes.ts`, `src/lib/tools-catalog.ts`, `src/lib/universal-search.ts`), not
hardcoded strings scattered across components.
- **New-route checklist.** Add the page → link it from real nav (sidebar / launcher / mode home /
search) → `npm run sitemap:update` → document it in `docs/codebase-index.md` → add a
reachability/coverage assertion. A production page route with no inbound link is an orphan.
- **Gates.** `eslint-rules/require-button-wiring.mjs` (in `npm run lint`) fails on an un-wired
`<button>`; `tests/route-reachability.test.ts` (in `npm run test`) fails when a production page
route has no inbound nav link unless it is consciously added to that test's documented
allowlist (redirect targets / legacy-compat routes). Both run in `verify:cheap` and CI. Mockups
(`src/app/mockups/**`, `*-mockups.tsx`) are design-scratch and exempt from both.
- **Never** add a production page route without either an inbound link or a documented
reachability allowlist entry plus an `/issues` note, and never silence the button-wiring rule
with a blanket disable — wire the control or make it an explicit placeholder.

<!-- END:page-and-button-wiring -->

<!-- BEGIN:supabase-project-safety -->

# Supabase project safety
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ npm run docs:check-links
## Architecture

- [frontend-architecture.md](frontend-architecture.md) — shell, routing, dashboard module structure
- [wiring-conventions.md](wiring-conventions.md) — page/button wiring conventions and the dead-button / orphan-route gates
- [deployment-architecture.md](deployment-architecture.md) — app/worker/Supabase deployment topology
- [ingestion-state-machine.md](ingestion-state-machine.md) — ingestion job lifecycle and states
- [design-system.md](design-system.md) — tokens, primitives, styling conventions
Expand Down
5 changes: 3 additions & 2 deletions docs/codebase-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,12 @@ sequenceDiagram

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

- **Mode homes** (`/services`, `/forms`, `/favourites`, `/differentials`, `/formulation`, `/applications`, and dashboard homes): inline in the hero via the `mode-home-composer-slot` portal, on phone and tablet+ alike.
- **Mode homes** (`/services`, `/forms`, `/favourites`, `/differentials`, `/formulation`, `/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.)
- **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. `/?mode=favourites` redirects to `/favourites`; `/?mode=differentials` redirects to `/differentials`; `/?mode=formulation` redirects to `/formulation`.
- **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. `/?mode=favourites` redirects to `/favourites`; `/?mode=differentials` redirects to `/differentials`; `/?mode=dsm` redirects to `/dsm`; `/?mode=specifiers` redirects to `/specifiers`; `/?mode=formulation` redirects to `/formulation`.
- **Intentionally composer-free routes**: `/differentials/presentations/*` (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.
- **Local filter fields** (sidebar "Search chats", document drawer "Find a document"/"Find a source PDF") are scoped filters, not global search; they share the `fieldControlWithIcon`/`fieldIcon` primitives.
- **Wiring conventions** for buttons and route navigation (and the gates that enforce them — the dead-button ESLint rule and the orphan-route reachability test) live in `docs/wiring-conventions.md`.

---

Expand Down
21 changes: 13 additions & 8 deletions docs/outstanding-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,22 @@ Durable, cross-session memory of everything still outstanding for this repo: ope
- Resolving an item moves its row to **Resolved / archive** with the date and a one-line outcome —
rows are archived, not deleted, so the history stays auditable.

<!-- issues:next-id=006 -->
<!-- issues:next-id=011 -->

## Open items

| ID | Pri | Type | Summary | Detail / next action | Source | Added |
| ---- | --- | ---- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------- |
| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 |
| #002 | P2 | task | Process-ownership fix not yet isolated on `main` | `scripts/run-eval-safe.mjs` still terminates residual repo processes broadly. Isolate the child-owned `terminateOwnedProcessTree(child.pid)` approach + the regression test proving unrelated Vitest/Playwright/Next processes survive; verify statically — do **not** start a provider-backed eval. | `docs/process-hardening.md` (High-priority local process ownership) | 2026-07-21 |
| #003 | P2 | task | Staging tenancy release evidence outstanding | The provider-backed A/B tenancy regression is intentionally outside local + PR gates. Run the standalone manual/nightly workflow and attach a recent green evidence artifact **before release**. | `docs/process-hardening.md`; `docs/staging-tenancy-release-evidence.md` | 2026-07-21 |
| #004 | P3 | rec | Rescope provider-gated RAG safety ideas | If explicitly approved, rescope only the still-relevant preflight utilities/tests (answer-quality thresholds, cost-cap preflight, deep-health) against current `main`; keep live OpenAI/Supabase validation separate. Do **not** replay the stale 754-line retrieval migration/worktree wholesale. | `docs/process-hardening.md` (Provider-gated RAG safety ideas) | 2026-07-21 |
| #005 | P3 | rec | `finalScore` saturates at clamp ceiling | Base + ~40 stacked boosts routinely exceed 1.0, so strong matches tie at 1.0 and order by an arbitrary `document_id` tiebreak. If ranking is ever revisited, break ties by the **pre-clamp** score rather than raising the `[0,1]` ceiling (downstream gates assume `[0,1]`). Not a defect on the current golden set. | `docs/rag-hybrid-findings-and-todo.md` P1 item 4; `src/lib/rag/clinical-search.ts:1362` | 2026-07-21 |
| ID | Pri | Type | Summary | Detail / next action | Source | Added |
| ---- | --- | ----- | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ---------- |
| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 |
| #002 | P2 | task | Process-ownership fix not yet isolated on `main` | `scripts/run-eval-safe.mjs` still terminates residual repo processes broadly. Isolate the child-owned `terminateOwnedProcessTree(child.pid)` approach + the regression test proving unrelated Vitest/Playwright/Next processes survive; verify statically — do **not** start a provider-backed eval. | `docs/process-hardening.md` (High-priority local process ownership) | 2026-07-21 |
| #003 | P2 | task | Staging tenancy release evidence outstanding | The provider-backed A/B tenancy regression is intentionally outside local + PR gates. Run the standalone manual/nightly workflow and attach a recent green evidence artifact **before release**. | `docs/process-hardening.md`; `docs/staging-tenancy-release-evidence.md` | 2026-07-21 |
| #004 | P3 | rec | Rescope provider-gated RAG safety ideas | If explicitly approved, rescope only the still-relevant preflight utilities/tests (answer-quality thresholds, cost-cap preflight, deep-health) against current `main`; keep live OpenAI/Supabase validation separate. Do **not** replay the stale 754-line retrieval migration/worktree wholesale. | `docs/process-hardening.md` (Provider-gated RAG safety ideas) | 2026-07-21 |
| #005 | P3 | rec | `finalScore` saturates at clamp ceiling | Base + ~40 stacked boosts routinely exceed 1.0, so strong matches tie at 1.0 and order by an arbitrary `document_id` tiebreak. If ranking is ever revisited, break ties by the **pre-clamp** score rather than raising the `[0,1]` ceiling (downstream gates assume `[0,1]`). Not a defect on the current golden set. | `docs/rag-hybrid-findings-and-todo.md` P1 item 4; `src/lib/rag/clinical-search.ts:1362` | 2026-07-21 |
| #006 | P2 | issue | Globe "Language & region" button had no handler | Fixed in this PR to the disabled "Coming soon" placeholder convention (`aria-disabled` + sr-only note). Wire to a real language/region settings screen when one exists, then drop the placeholder state. | `src/components/clinical-dashboard/master-search-header.tsx:1829`; session 2026-07-21 | 2026-07-21 |
| #007 | P3 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | `/tools` (standalone `ApplicationsLauncherPage`) has no inbound in-app link; the sidebar Tools item uses `/?mode=tools`. Decide the canonical entry point and wire nav consistently, or drop the standalone `/tools` page + `/applications` redirect. Currently allowlisted in `tests/route-reachability.test.ts`. | `src/app/tools/page.tsx`; `src/app/applications/route.ts` | 2026-07-21 |
| #008 | P3 | rec | Dead href builders in `document-flow-routes.ts` | `documentReaderHref` / `documentEvidenceHref` (+ `*MockHref` variants) are exported but never called in `src`. Remove them, or start using them for the `/documents/source*` compatibility links. | `src/lib/document-flow-routes.ts` | 2026-07-21 |
| #009 | P3 | rec | Confirm `/api/jobs` is intentionally server/ops-only | No client `fetch()` reaches `/api/jobs` (only tests import it). Confirm it is a deliberate ops/manual surface; if abandoned, remove it. | `src/app/api/jobs/route.ts` | 2026-07-21 |
| #010 | P3 | task | Un-built "Coming soon" controls across forms/favourites | ~10 disabled placeholders (forms refine/reset, favourites sort/add/new-set, move-to-set, remove-favourite). Correctly flagged (`aria-disabled` + "Coming soon"), not defects — wire when the underlying features land. | `forms-search-results-page.tsx`; `favourites-hub.tsx`; `favourites-command-library-page.tsx` | 2026-07-21 |

## Resolved / archive

Expand Down
2 changes: 1 addition & 1 deletion docs/site-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ This file is generated by `npm run sitemap:update`. Run `npm run sitemap:check`

- `/applications` - Redirects to `/tools`. Source: `src/app/applications/route.ts`.
- `/differentials/presentations` - Redirects to `/differentials/presentations/[workflow-slug]`. Source: `src/app/differentials/presentations/route.ts`.
- `/documents/source` - Redirects to `/documents/search`. Source: `src/app/documents/source/page.tsx`.
- `/documents/source` - Redirects to `/documents/[id]`. Source: `src/app/documents/source/page.tsx`.
- `/medications` - Redirects to `/?mode=prescribing`. Source: `src/app/medications/route.ts`.
- `/mockups/favourites-hub` - Redirects to `/favourites`. Source: `src/app/mockups/favourites-hub/page.tsx`.
- `/mockups/medication-prescribing` - Redirects to `/medications/acamprosate`. Source: `src/app/mockups/medication-prescribing/page.tsx`.
Expand Down
Loading