modules/brief.mdEngineering, modules~1 min read

Module: brief

Part of the Travi AI Agent monolith (app/modules/brief/). Read 00-overview.md §3 (hard rules) before changing anything here.

Purpose The clinician's one-screen snapshot: precompute, staleness, post-visit update loop.
Owns (data) brief_snapshot
Public surface /clinician/brief/*, /clinician/confirmations, /clinician/post-visit
Depends on compiler (published facts only), identity (grants/step-up), ingestion (post-visit docs)
Requirements owned VIS-002/005/007, CLN-004/008
Constraining ADRs ADR-014, ADR-017 (step-up)
Key references reference/api.md (clinician table)

14. Clinician Brief Service

14.1 Content Contract (renders in this order)

  1. Reason for hospitalization + discharge date.
  2. Major hospital events only when they affect current care.
  3. New / changed / stopped medications with source + current evidence state.
  4. Patient-reported use, access barriers, material events since discharge.
  5. Completed and outstanding tests, referrals, appointments.
  6. Patient questions + discrepancies (pending_clinical_decision items).
  7. Date + provenance of most recent update.

14.2 Generation & Delivery

Trigger: appointment detected → precompute at T−24 h (config brief.lead_hours) by the Brief Agent from published facts; snapshot persisted (content JSON + rendered PDF to S3) with episode_version_at_build; secure link + grant issued per §9.3; open p95 ≤2 s (snapshot read, no recompute); brief understandable ≤60 s is a usability exit criterion (§17.4).

14.3 Post-Visit Loop (VIS-005/007)

Clinician uploads note/AVS or submits structured summary → new source_document (level 5) → full compiler reprocess → supersessions + new tasks → patient Today updates ≤60 s after publication. Ambiguous material changes park as pending confirmations (step-up) and cannot alter patient instructions until confirmed.

14.4 Staleness Algorithm (ADR-014, GAP-C1)

on brief open:
 cur = episode.version (SELECT)
 if cur == snapshot.episode_version_at_build: render(snapshot)
 else:
 delta = material_diff(snapshot, episode) # classes below
 if delta.empty: render(snapshot, banner="Updated <ts>; minor changes since")
 else: return 202 regenerating; enqueue rebuild (p95 ≤ 5 s); client polls
material classes: med plan item added/changed/stopped/confirmed;
 new pending_clinical_decision; episode paused/closed; new safety event