Skip to content

feat(therapy): expose Therapy in production ahead of clinical sign-off - #2145

Closed
BigSimmo wants to merge 7 commits into
mainfrom
claude/therapy-production-visibility
Closed

feat(therapy): expose Therapy in production ahead of clinical sign-off#2145
BigSimmo wants to merge 7 commits into
mainfrom
claude/therapy-production-visibility

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

Therapy was invisible in production, and the reason was two separate gates rather than one: devOnly: true on the mode hid the entrance and made the generic route guard notFound() the routes, while a record filter served only reviewStatus === "reviewed". With all 205 records needs_review, that combination meant a hidden mode over an empty library — removing either one alone would not have produced a working Therapy mode.

The owner lifted both on 2026-08-18, deliberately and with the numbers in hand: 205 records, 0 reviewed, 1435 unticked checklist items. This is a private clinical reference tool and that exposure call is theirs.

  • Mode exposure (0e51be0e). devOnly removed from the therapy-compass mode, with the decision and its date recorded at the site rather than in a commit message nobody re-reads.
  • Record filter (0e51be0e). Switched off behind a named HIDE_UNREVIEWED_IN_PRODUCTION = false constant instead of being deleted, so re-arming after sign-off is a one-word edit, the condition stays greppable when someone asks "why is unreviewed content live?", and therapyNeedsReview keeps its meaning for every caller that surfaces review state.
  • Deferred tooling (ee440f41). The sign-off tooling this PR deliberately does not build is queued as an immutable inbox request.

What was not done, and this is the important part. No record was relabelled. reviewStatus still reads needs_review on all 205. Marking them reviewed would have produced exactly the same screen far more cheaply — and it would have been a false clinical attestation rather than a product decision, destroying the only signal a reader now has.

layout.tsx is untouched. Its guard is generic over devOnly, so it self-disarmed when the flag came off and still protects the next mode that needs it.

What protects the clinician now

While the gate stood, hiding the content was the protection, and nothing needed to say "unreviewed" out loud. Now that it ships, the protection is that every surface still says so:

Channel Text
Record badge (StatusBadge) "Needs source review" + TriangleAlert glyph
Universal-search result badge "Needs source review"
Route metadata description "(Awaiting source review.)"

tests/therapy-ranking.test.ts now pins all four — including the glyph, so the warning can never degrade to colour-only — against the real 205-record catalogue rather than fixtures.

Tests rewritten, not deleted

Three pinned assertions guarded a property that moved rather than disappeared, so all three were replaced with the stronger condition for the new world:

  • therapy-ranking — "excludes unreviewed content from production" → serves the catalogue and never drops the unreviewed marking. Strictly more than the old assertion checked: it now verifies the honesty channels, which nothing previously did.
  • app-modes — visibility assertions inverted rather than dropped, so a silent re-hiding still fails.
  • therapy-review-regressions — was "keeps Therapy unavailable in production until clinical review is complete"; now asserts the gate machinery still exists and is re-armable. A future change that rips the mechanism out fails loudly.

Verification

  • npm run verify:cheap

Test Files 673 passed (673), Tests 7276 passed | 4 skipped (7280), Design-system contract passed (784 production files; raw colors 2; literal shadows 0; legacy tap classes 0; sub-floor interactive min-heights 37; edge conflicts 18; 1px shadow spreads 2).

  • npm run verify:ui when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed

447 passed (14.9m), Chromium. Run deliberately rather than by default: Playwright builds in production mode, so this is the first gate that renders Therapy as a production mode, and mode-list composition is exactly what these journeys exercise. npm run ensure was intentionally skipped — verify:ui builds and serves its own instance, and starting a dev server is precisely what triggers the BUILD_REFUSED_DEV_SERVER conflict queued as an issue earlier today.

  • npm run check:production-readiness when clinical workflow, privacy, environment, Supabase, source governance, or deployment behavior changed

Ran, and did not fully pass — reporting it rather than calling it green. 2 PASS, 5 WARN, 2 FAIL. Both FAILs are missing NEXT_PUBLIC_SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY / OPENAI_API_KEY in this offline container, which is the documented provider capability gap for the offline Cloud profile, not a regression. Nothing in the output relates to review status or mode exposure.

  • npm run verify:release — not run: provider-backed, not authorized, no release confidence claimed.
  • npm run eval:retrieval:quality — not applicable: ragRanking: false, no retrieval, ranking, selection, chunking or scoring surface touched.
  • npm run eval:rag / eval:quality — not applicable: answer generation untouched.
  • npm run check:deployment-readiness — not applicable: no deployment startup, hosting or rollout behaviour changed.

Risk and rollout

  • Risk. This is the substantive one: 205 therapy records that no qualified clinician has signed off become reachable clinical decision support on a production tool. That is the accepted, informed cost of the decision, not an oversight. It is bounded by the records remaining truthfully marked needs_review on every surface, by the catalogue being source-grounded rather than generated advice, and by this being a single-clinician private reference tool rather than a multi-tenant product.
  • Rollback. Two independent levers, either sufficient: set HIDE_UNREVIEWED_IN_PRODUCTION = true in src/lib/therapies.ts to restore the record filter, or restore devOnly: true in src/lib/app-modes.ts to hide the mode again. Both are one-word edits and both are covered by tests that will fail until the matching assertions are flipped back. git revert on either commit also works; they are independent.
  • Provider or production effects. No provider mutation was performed. main auto-deploys to the Railway production service, so merging this makes Therapy live on psychiatry.tools. That is the intent of the change and is stated here so it is not a surprise.

Clinical Governance Preflight

scripts/pr-policy.mjs returns clinicalRisk: false for this diff — src/lib/therapies.ts and src/lib/app-modes.ts match none of its filename patterns, so no governance section is enforced. That is itself a gap worth noting: the classifier does not flag the switch that controls whether unreviewed clinical content reaches production. Completed voluntarily, and honestly rather than compliantly.

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative — deliberately not ticked. Review metadata stays conservative and truthful: every record still reports needs_review and every surface still displays it. But the behaviour is by design less conservative than before, because unreviewed records are now served rather than withheld. Ticking this would misrepresent the change, and a false tick here is the same category of error as a false sign-off.
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

On the last item: the change alters who can reach existing decision-support content, not what it computes or claims. No new decision-support behaviour was introduced, so the classification is unchanged — but this is the item most worth a second opinion, since widening the audience for unreviewed clinical content is arguably a classification-relevant fact in its own right.

Notes

An assistant did not and must not tick the five clinical checks in reviewChecklist (clinicalAccuracyReviewed, sourceChecked, evidenceAppraised, safetyCautionsChecked, patientExplanationChecked) — those are qualified-clinician attestations. The deferred issue records that stop rule explicitly, and notes that proofread and australianEnglishChecked are non-clinical and may be done with attribution.

This branch is separate from PR #2131 because AGENTS.md forbids bundling a change needing a governance preflight with unrelated docs chores.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CXpFsse3vVMy7CTxNRi568


Generated by Claude Code

claude added 2 commits August 18, 2026 16:06
Queued at the owner's direction while Therapy is made production-visible
ahead of clinical review. Three gaps it records:

- `reviewStatus` is a bare string, so a record can be flipped to
  `reviewed` with all seven `reviewChecklist` booleans still false and
  nothing detects it.
- No attribution: none of the 44 record fields carries `reviewedBy` or
  `reviewedAt`, the same defect `#318` flags against the medication
  interaction lexicon.
- No review workflow: 205 records x 7 checks is 1435 clinical judgements
  by hand.

Carries an explicit stop rule that an assistant must never tick the five
clinical checks — those are qualified-clinician attestations — while
`proofread` and `australianEnglishChecked` are non-clinical and may be
done with attribution.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Therapy was gated out of production by two mechanisms: `devOnly: true` on
the mode, which hid the entrance and made the generic route guard 404 the
routes, and a record filter that served only `reviewStatus === "reviewed"`.
With all 205 records `needs_review`, that meant an invisible mode over an
empty library.

The owner lifted both on 2026-08-18, deliberately and with the numbers in
hand: 205 records, 0 reviewed, 1435 unticked checklist items. This is a
private clinical reference tool and that exposure call is theirs.

What was NOT done, because it would have been the easy way to the same
screen: no record was relabelled. `reviewStatus` still reads
`needs_review` on all 205. Marking them reviewed would have been a false
clinical attestation rather than a product decision, and it would have
destroyed the only signal a reader now has.

The record filter is switched off by a named `HIDE_UNREVIEWED_IN_PRODUCTION`
constant rather than deleted, so re-arming the gate after sign-off is a
one-word edit, the condition stays greppable, and `therapyNeedsReview`
keeps its meaning for every caller that surfaces review state. The route
guard in `layout.tsx` is generic over `devOnly` and self-disarmed, so it
is untouched and still protects the next mode that needs it.

Three pinned tests are rewritten, not deleted, because the property they
guarded moved rather than disappeared. While the gate stood, hiding the
content WAS the protection. Now that it ships, the protection is that
every surface still says "Needs source review" — so `therapy-ranking`
pins exactly that, against the real catalogue: the record badge, the
universal-search badge, the route metadata suffix, and the paired glyph
that keeps the warning from being colour-only. `app-modes` inverts its
visibility assertions so a silent re-hiding still fails, and
`therapy-review-regressions` now asserts the gate machinery still exists
and is re-armable rather than that it is engaged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@supabase

supabase Bot commented Aug 18, 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 Aug 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

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

Your current included review allowance is based on your included PR review attempts over the past 7 days.

Next review available in: 26 minutes

Limit details: You’ve used the included review currently available. Your 101 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2a1e11d6-8126-4909-8902-eb0472f16172

📥 Commits

Reviewing files that changed from the base of the PR and between baec777 and bfeacf2.

📒 Files selected for processing (7)
  • docs/branch-review-records/60f7559f21cf484e008441a428df84d3a6f127b81692ab314e8bf97a54197ffd.record.md
  • docs/outstanding-issues-inbox/1d0dbb76-a35d-4c8c-8816-3f82f3e1a5f6.json
  • src/lib/app-modes.ts
  • src/lib/therapies.ts
  • tests/app-modes.test.ts
  • tests/therapy-ranking.test.ts
  • tests/therapy-review-regressions.test.ts

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



Immutable record for the owner-directed lifting of the Therapy clinical
review gate, including the numbers the decision was taken on and the
explicit note that no record was relabelled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@BigSimmo
BigSimmo enabled auto-merge August 18, 2026 17:57
@BigSimmo
BigSimmo disabled auto-merge August 18, 2026 18:51
@BigSimmo
BigSimmo enabled auto-merge August 18, 2026 18:53
@BigSimmo
BigSimmo disabled auto-merge August 18, 2026 18:55

Copy link
Copy Markdown
Owner Author

Closing in favour of #2150, which supersedes this and does the job better.

Both remove the Therapy production gate, but #2150 is the more complete change:

  • It removes all three gates, including the route-layout notFound() this PR left in place on the argument that it self-disarms once devOnly is gone. feat(therapy): ship Therapy in production with its review state disclosed #2150's reasoning is better — a bypass left behind an absent gate is how a half-restored gate passes locally and 404s in production.
  • It adds TherapyReviewNotice, a non-dismissible catalogue-wide disclosure above the Therapy home hero. That closes the gap this PR left open: with the gate removed, the per-record badge was the only thing standing between an unreviewed record and a clinical decision.
  • The notice is driven by THERAPY_CATALOGUE_SUMMARY.needsReviewCount, emitted by the index generator and compared in its check mode, so the wording cannot drift from the data and the notice disappears on its own once sign-off completes.
  • It retires the now-orphaned PLAYWRIGHT_OFFLINE_MODE bypass, which this PR left behind.
  • Its therapies.ts is cleaner: the environment plumbing is removed outright rather than left as an unreachable branch behind this PR's HIDE_UNREVIEWED_IN_PRODUCTION = false constant. A single-commit revert is a better rollback story than dead code.

#2150 also completes the Source metadata, review status, and outdated/unknown-source behavior remain conservative governance item, which this PR deliberately left unticked. Having read its justification, that is correct and this PR was over-strict: disclosing an unreviewed record as unreviewed, with nothing upgraded or inferred to reviewed, is conservative handling.

The one thing here that #2150 does not carry — the queued Therapy sign-off tooling task — has been re-queued in #2152 so it is not lost with this branch.

No code from this PR is needed. Closing rather than merging avoids landing a second, weaker version of the same change.


Generated by Claude Code

@BigSimmo
BigSimmo enabled auto-merge August 18, 2026 19:22
@BigSimmo
BigSimmo disabled auto-merge August 18, 2026 19:22
@BigSimmo BigSimmo closed this Aug 18, 2026
BigSimmo added a commit that referenced this pull request Aug 18, 2026
docs(issues): re-queue the Therapy sign-off tooling task after #2145 closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants