Teaching and CME: durable supervisor feedback link, learning directory flags, iPhone QR wording

Merged
BI
21d ago
+1874-148

Summary

This PR covers four Teaching and CME ledger items. The owner approved the screens from his own PC on 21 Oct 2026.

  • #MAAXAX supervisor feedback link. Production returned 503 because the old store lived only in process memory. This PR adds a durable table, public.teaching_epa_magic_links, in migration 20261021190000. It was renumbered from 20261021170000 after main took that number in #3732.
    • The server stores only the SHA-256 hash of each link token.
    • A link works once, expires after 14 days (the database caps it at 30), and only the doctor who made it can see or cancel it. Each doctor can have at most 20 waiting links.
    • The table is service-role only, with RLS enabled and forced.
    • New routes: GET/POST /api/teaching/assessments/magic-links and DELETE /api/teaching/assessments/magic-links/[id]. The respond route now reads and writes the durable store.
    • The doctor uses the new "Or ask for feedback by link" panel in Request EPA. The supervisor gets a page with no sign-in.
    • Both screens say that what comes back is feedback for the doctor to copy into CLA, not an official RANZCP sign-off, and that no patient details should be included. Text that looks like patient details is refused.
    • Demo mode serves a fixed example link, /teaching/assessments/respond/demo-supervisor-feedback-link-example, so the page can be shown without a database. Nothing is saved.
  • #R2FNTJ WA learning directory.
    • Entries can be marked as cancelled. A cancelled entry shows a red tag, cannot be logged or added to a calendar, and never appears in Past.
    • Entries can carry an access note: "Included in your workshop" or "Available through your employer".
    • A weekly report-only check, .github/workflows/learning-directory-weekly.yml, follows the owner's 21 Oct decision. It flags stale entries, broken links and finished items in a single GitHub issue. It never edits the data.
  • #ZTDZ4Z QR check-in. The Today code sheet hides "Scan in app" in browsers that cannot read QR codes, such as iPhone Safari. Those users see the phone Camera app and 6-digit code wording instead.
  • #WVS32F attendance gaps. This was already built on main. The ledger is closed through an inbox request.

Areas touched: Personal practice, Knowledge and records, Data platform and access, Delivery and assurance, App experience (plus 3 files not yet placed in any area)

RAG impact: no retrieval behaviour change. No ranking-protected files are touched.

Verification

  • Checks run:
    • npm run verify:pr-local (at 2dd1d66b5): every static, docs, migration, lint and typecheck step passed. Unit tests: "Tests 2 failed | 33357 passed | 15 skipped". The 2 failures are in tests/test-runner-safety.test.ts (heavyweight lock) and fail the same way in this cloud sandbox without these changes. The build was not reached locally, so CI covers it.
    • After the renumber (0798117ce): check:migration-immutability, check:migration-role, check:ledger-write-discipline, check:outstanding-issues and docs:check-index pass. tests/supabase-schema.test.ts and tests/migration-history-guards.test.ts report "Tests 177 passed (177)". The drift manifest was regenerated by local replay.
    • Focused tests: tests/epa-magic-link-repository.test.ts, tests/epa-magic-link.test.ts, tests/cme-learning-directory.test.ts and tests/learning-directory-weekly.test.ts pass.
    • The migration seal is append-only. The new entry is added and no existing hash changes.
    • The owner proofed and approved the screens from his PC, including dark mode and the iPhone WebKit view, at commit ede56d78a.

Risk and rollout

  • Risk: this PR adds one new table, used only by the supervisor link feature. No existing table, policy or grant changes.
  • Database: this PR includes supabase/migrations/20261021190000_teaching_epa_magic_links.sql. Merge only after the owner's own yes. The owner presses Merge himself, with auto-merge off. Merging applies the table to the live database. After merge, the live-drift workflow must pass both check:drift and check:migration-history.
  • Rollback: revert the app code, then drop the table with a new migration if needed. The table holds only supervisor feedback links, and no existing data is touched.
  • Provider or production effects: none from this PR before merge. After merge, the weekly directory check runs on GitHub Actions and makes plain link requests to the listed course pages.

Notes

  • /teaching/assessments/respond/* and its API must stay open to signed-out visitors when the 1 Nov sign-in gate (#3737) lands. This was confirmed with the security thread.
  • Follow-ups, not done here:
    • Retire the old in-memory PUT registration on the respond route.
    • Decide a retention period for completed links.
  • One cosmetic note for the owner: in dark mode, Send feedback stays grey until a supervision level is picked. That is the design system's standard disabled state.

🤖 Generated with Claude Code

https://claude.ai/code/session_0132J6dZHRBPxR1Ay1iyjywE

Summary by CodeRabbit

  • New Features

    • Doctors can create, share, monitor, and cancel single-use supervisor EPA feedback links.
    • Supervisors can submit trainee feedback through authenticated, expiring links.
    • Learning entries now show access details and cancellation status.
    • QR check-in guidance adapts to browser support, offering camera and manual-code alternatives when needed.
  • Improvements

    • Cancelled learning cannot be logged as CPD or added to calendars.
    • EPA feedback forms clarify privacy requirements and that feedback is not an official sign-off.
  • Chores

    • Added automated weekly learning-directory reporting.