File size: 30,032 Bytes
8683d2c 5a79992 612e777 499ee49 8683d2c 612e777 8683d2c a23cf6c 8683d2c 612e777 8683d2c 612e777 8683d2c 612e777 8683d2c 612e777 5f864ad 5a79992 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <article class="doc" id="doc-engineering-dataflow" aria-hidden="true"><div class="strip"><span class="tag">Engineering</span><span class="sep">·</span><span>FHIR R4 Data Flow</span></div>
<h1>FHIR R4 Data Flow</h1>
<p>How clinical data moves from the hospital EHR through Travi's validation pipeline to patients and clinicians. The architecture is <strong>read-mostly by design</strong>: Travi never writes discrete orders, medications, or clinical impressions into the EHR, the only planned export is a CCD summary to the media tab, deferred past the MVP (INT-008, ADR-010).</p>
<div class="diagram-wrap"><svg class="diagram-svg" viewBox="0 0 1080 848" xmlns="http://www.w3.org/2000/svg" role="img" style="width:100%;height:auto;display:block;background:#fff;border:1px solid #d7dee8;border-radius:12px;"><defs><marker id="dfah5b6b7e" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 1 L 9 5 L 0 9 z" fill="#5b6b7e"/></marker><marker id="dfah0891b2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 1 L 9 5 L 0 9 z" fill="#0891b2"/></marker><marker id="dfah166534" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 1 L 9 5 L 0 9 z" fill="#166534"/></marker><marker id="dfahb45309" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 1 L 9 5 L 0 9 z" fill="#b45309"/></marker><marker id="dfahb91c1c" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 1 L 9 5 L 0 9 z" fill="#b91c1c"/></marker><marker id="dfah8a97a6" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 1 L 9 5 L 0 9 z" fill="#8a97a6"/></marker></defs><text x="540" y="30" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="14" font-weight="600" fill="#0a1628" letter-spacing=".14em">TRAVI AI AGENT: FHIR R4 DATA FLOW</text><text x="540" y="48" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9.5" font-weight="600" fill="#5b6b7e" letter-spacing=".12em">READ-MOSTLY BY DESIGN: NO DISCRETE EHR WRITE-BACK IN THE MVP</text><rect x="20" y="66" width="240" height="612" rx="12" fill="#f4f7fa" stroke="#d7dee8" stroke-width="1"/><text x="34" y="88" text-anchor="start" font-family="JetBrains Mono, ui-monospace, monospace" font-size="10" font-weight="600" fill="#0891b2" letter-spacing=".12em">HOSPITAL EHR · EPIC / ANY FHIR R4</text><rect x="36" y="100" width="208" height="58" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="140.0" y="119" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">ADT A03 discharge event</text><text x="140.0" y="134" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">HL7v2 over site VPN</text><text x="140.0" y="147" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">disposition = home filter</text><rect x="36" y="174" width="208" height="84" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="140.0" y="193" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">FHIR R4 reads</text><text x="140.0" y="208" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">Patient · Encounter</text><text x="140.0" y="221" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">MedicationRequest / MedicationStatement</text><text x="140.0" y="234" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">DocumentReference (AVS, DC summary)</text><rect x="36" y="274" width="208" height="58" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="140.0" y="293" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">SMART backend-services</text><text x="140.0" y="308" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">OAuth2 client-credentials</text><text x="140.0" y="321" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">registered scopes only</text><rect x="36" y="356" width="208" height="58" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2" stroke-dasharray="5 4"/><text x="140.0" y="375" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Poll fallback (ADR-007)</text><text x="140.0" y="390" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">Encounter poll q5 min</text><text x="140.0" y="403" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">if ADT is declined</text><rect x="36" y="430" width="208" height="58" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2" stroke-dasharray="5 4"/><text x="140.0" y="449" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Document drop / SFTP</text><text x="140.0" y="464" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">first-line integration path</text><text x="140.0" y="477" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">works without an IT project</text><rect x="36" y="544" width="208" height="58" rx="8" fill="#ffffff" stroke="#8a97a6" stroke-width="1.2" stroke-dasharray="5 4"/><text x="140.0" y="563" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">CCD summary export</text><text x="140.0" y="578" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">post-MVP (INT-008 · ADR-010)</text><text x="140.0" y="591" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">media tab only: never orders</text><rect x="280" y="66" width="460" height="612" rx="12" fill="#f4f7fa" stroke="#d7dee8" stroke-width="1"/><text x="294" y="88" text-anchor="start" font-family="JetBrains Mono, ui-monospace, monospace" font-size="10" font-weight="600" fill="#166534" letter-spacing=".12em">TRAVI PIPELINE · TOP TO BOTTOM, EVERY STEP AUDITED</text><rect x="300" y="100" width="280" height="58" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="440.0" y="119" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Ingest & parse</text><text x="440.0" y="134" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">ehr_adapter boundary · fingerprint + supersession</text><text x="440.0" y="147" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">PDF / CDA / HL7 parsers · OCR only for scans</text><rect x="300" y="186" width="280" height="58" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="440.0" y="205" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">LLM extraction</text><text x="440.0" y="220" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">AWS Bedrock · Claude · version-pinned</text><text x="440.0" y="233" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">temp 0 · zero-retention BAA</text><rect x="300" y="272" width="280" height="58" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="440.0" y="291" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Seven validation gates</text><text x="440.0" y="306" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">schema · RxNorm/LOINC · clinical checks</text><text x="440.0" y="319" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">verbatim span proof · semantic pass</text><rect x="594" y="268" width="130" height="66" rx="8" fill="#fff8ec" stroke="#b45309" stroke-width="1.2"/><text x="659.0" y="287" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="11.5" font-weight="600" fill="#b45309">Review queue</text><text x="659.0" y="302" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9" fill="#5b6b7e">uncertain goes to</text><text x="659.0" y="315" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9" fill="#5b6b7e">clinical review</text><text x="659.0" y="328" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9" fill="#5b6b7e">never auto-published</text><rect x="300" y="358" width="280" height="52" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="440.0" y="377" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">compiler.publish()</text><text x="440.0" y="392" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">gate-passing facts only · one transaction</text><text x="440.0" y="405" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">idempotent · rerun-safe</text><rect x="300" y="438" width="424" height="58" rx="8" fill="#eef7f1" stroke="#166534" stroke-width="1.2"/><text x="512.0" y="457" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#166534">Episode engine: single writer of state</text><text x="512.0" y="472" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">five-element transition contract · transactional outbox ≤5 s</text><text x="512.0" y="485" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.8" fill="#5b6b7e">the only path that changes any task or episode</text><rect x="300" y="524" width="136" height="62" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="368.0" y="543" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="11.5" font-weight="600" fill="#0a1628">Scheduling</text><text x="368.0" y="558" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.2" fill="#5b6b7e">requirement vs appt</text><text x="368.0" y="571" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.2" fill="#5b6b7e">match · assisted requests</text><rect x="444" y="524" width="136" height="62" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="512.0" y="543" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="11.5" font-weight="600" fill="#0a1628">Notifications</text><text x="512.0" y="558" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.2" fill="#5b6b7e">SMS-first ladders</text><text x="512.0" y="571" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.2" fill="#5b6b7e">quiet hours · ≤60 s stop</text><rect x="588" y="524" width="136" height="62" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="656.0" y="543" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="11.5" font-weight="600" fill="#0a1628">Clinician brief</text><text x="656.0" y="558" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.2" fill="#5b6b7e">T−24 h snapshot</text><text x="656.0" y="571" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.2" fill="#5b6b7e">staleness · post-visit</text><rect x="300" y="614" width="424" height="46" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="512.0" y="633" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12" font-weight="600" fill="#0a1628">Analytics: honest numbers</text><text x="512.0" y="648" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="9.5" fill="#5b6b7e">frozen denominators · coverage flags · n<11 suppression</text><circle cx="299" cy="129" r="12" fill="#166534" stroke="#fff" stroke-width="2"/><text x="299" y="132.8" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="11" font-weight="700" fill="#fff">1</text><circle cx="299" cy="215" r="12" fill="#166534" stroke="#fff" stroke-width="2"/><text x="299" y="218.8" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="11" font-weight="700" fill="#fff">2</text><circle cx="299" cy="301" r="12" fill="#166534" stroke="#fff" stroke-width="2"/><text x="299" y="304.8" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="11" font-weight="700" fill="#fff">3</text><circle cx="299" cy="384" r="12" fill="#166534" stroke="#fff" stroke-width="2"/><text x="299" y="387.8" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="11" font-weight="700" fill="#fff">4</text><circle cx="299" cy="467" r="12" fill="#166534" stroke="#fff" stroke-width="2"/><text x="299" y="470.8" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="11" font-weight="700" fill="#fff">5</text><circle cx="299" cy="555" r="12" fill="#166534" stroke="#fff" stroke-width="2"/><text x="299" y="558.8" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="11" font-weight="700" fill="#fff">6</text><circle cx="299" cy="637" r="12" fill="#166534" stroke="#fff" stroke-width="2"/><text x="299" y="640.8" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="11" font-weight="700" fill="#fff">7</text><line x1="440" y1="158" x2="440" y2="183" stroke="#5b6b7e" stroke-width="1.4" marker-end="url(#dfah5b6b7e)"/><line x1="440" y1="244" x2="440" y2="269" stroke="#5b6b7e" stroke-width="1.4" marker-end="url(#dfah5b6b7e)"/><line x1="440" y1="330" x2="440" y2="355" stroke="#166534" stroke-width="1.4" marker-end="url(#dfah166534)"/><line x1="440" y1="410" x2="440" y2="435" stroke="#166534" stroke-width="1.4" marker-end="url(#dfah166534)"/><line x1="368" y1="496" x2="368" y2="521" stroke="#5b6b7e" stroke-width="1.4" marker-end="url(#dfah5b6b7e)"/><line x1="512" y1="496" x2="512" y2="521" stroke="#5b6b7e" stroke-width="1.4" marker-end="url(#dfah5b6b7e)"/><line x1="656" y1="496" x2="656" y2="521" stroke="#5b6b7e" stroke-width="1.4" marker-end="url(#dfah5b6b7e)"/><line x1="368" y1="586" x2="368" y2="611" stroke="#5b6b7e" stroke-width="1.4" marker-end="url(#dfah5b6b7e)"/><line x1="512" y1="660" x2="512" y2="691" stroke="#8a97a6" stroke-width="1.4" marker-end="url(#dfah8a97a6)"/><line x1="580" y1="301" x2="591" y2="301" stroke="#b45309" stroke-width="1.4" marker-end="url(#dfahb45309)"/><path d="M 659 334 L 659 346 L 500 346 L 500 356" fill="none" stroke="#b45309" stroke-width="1.5" stroke-dasharray="5 4" marker-end="url(#dfahb45309)"/><text x="580" y="342" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="8" font-weight="600" fill="#b45309" letter-spacing=".04em">after clinical decision</text><path d="M 244 129 L 272 129" fill="none" stroke="#5b6b7e" stroke-width="1.5"/><path d="M 272 129 L 272 459" fill="none" stroke="#5b6b7e" stroke-width="1.5"/><path d="M 272 150 L 296 150" fill="none" stroke="#5b6b7e" stroke-width="1.5" marker-end="url(#dfah5b6b7e)"/><path d="M 244 216 L 272 216" fill="none" stroke="#5b6b7e" stroke-width="1.5"/><path d="M 244 385 L 272 385" fill="none" stroke="#5b6b7e" stroke-width="1.5" stroke-dasharray="5 4"/><path d="M 244 459 L 272 459" fill="none" stroke="#5b6b7e" stroke-width="1.5" stroke-dasharray="5 4"/><circle cx="272" cy="150" r="2.7" fill="#5b6b7e"/><circle cx="272" cy="216" r="2.7" fill="#5b6b7e"/><circle cx="272" cy="385" r="2.7" fill="#5b6b7e"/><circle cx="272" cy="459" r="2.7" fill="#5b6b7e"/><path d="M 280 573 L 248 573" fill="none" stroke="#8a97a6" stroke-width="1.5" stroke-dasharray="5 4" marker-end="url(#dfah8a97a6)"/><rect x="830" y="66" width="230" height="612" rx="12" fill="#f4f7fa" stroke="#d7dee8" stroke-width="1"/><text x="844" y="88" text-anchor="start" font-family="JetBrains Mono, ui-monospace, monospace" font-size="10" font-weight="600" fill="#b45309" letter-spacing=".12em">PEOPLE & CHANNELS</text><rect x="846" y="100" width="198" height="80" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="945.0" y="119" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Patient & caregiver</text><text x="945.0" y="134" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">Today via SMS link</text><text x="945.0" y="147" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">no app · no password</text><text x="945.0" y="160" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">≤3 actions · 4 honest answers</text><rect x="846" y="202" width="198" height="72" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="945.0" y="221" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">PCP</text><text x="945.0" y="236" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">60-second brief · secure link</text><text x="945.0" y="249" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">no account needed</text><text x="945.0" y="262" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">step-up before confirmations</text><rect x="846" y="296" width="198" height="64" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="945.0" y="315" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Clinical review</text><text x="945.0" y="330" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">works the review queue</text><text x="945.0" y="343" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">approves patient-facing text</text><rect x="846" y="382" width="198" height="62" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="945.0" y="401" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Ops (contracted)</text><text x="945.0" y="416" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">scripted med-access flows</text><text x="945.0" y="429" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">evidence in · engine decides</text><rect x="846" y="466" width="198" height="64" rx="8" fill="#ffffff" stroke="#d7dee8" stroke-width="1.2"/><text x="945.0" y="485" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Hospital admin</text><text x="945.0" y="500" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">reports · kill switches</text><text x="945.0" y="513" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">zero routine work queue</text><path d="M 766 600 L 766 140 L 844 140" fill="none" stroke="#b45309" stroke-width="1.5" marker-end="url(#dfahb45309)"/><path d="M 766 238 L 844 238" fill="none" stroke="#b45309" stroke-width="1.5" marker-end="url(#dfahb45309)"/><circle cx="766" cy="238" r="2.7" fill="#b45309"/><path d="M 766 328 L 844 328" fill="none" stroke="#b45309" stroke-width="1.5" marker-end="url(#dfahb45309)"/><circle cx="766" cy="328" r="2.7" fill="#b45309"/><path d="M 766 413 L 844 413" fill="none" stroke="#b45309" stroke-width="1.5" marker-end="url(#dfahb45309)"/><circle cx="766" cy="413" r="2.7" fill="#b45309"/><path d="M 724 301 L 766 301" fill="none" stroke="#b45309" stroke-width="1.5"/><circle cx="766" cy="301" r="2.7" fill="#b45309"/><path d="M 724 555 L 766 555" fill="none" stroke="#b45309" stroke-width="1.5"/><circle cx="766" cy="555" r="2.7" fill="#b45309"/><path d="M 512 586 L 512 600 L 766 600" fill="none" stroke="#b45309" stroke-width="1.5"/><path d="M 724 637 L 812 637 L 812 498 L 844 498" fill="none" stroke="#5b6b7e" stroke-width="1.5" marker-end="url(#dfah5b6b7e)"/><rect x="20" y="694" width="1040" height="52" rx="8" fill="#f4f7fa" stroke="#d7dee8" stroke-width="1.2"/><text x="540.0" y="713" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="12.5" font-weight="600" fill="#0a1628">Immutable audit ledger: every PHI read, state transition, agent action, and config change</text><text x="540.0" y="728" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="10.5" fill="#5b6b7e">INSERT-only role · monthly partitions · 6-year WORM archive · any instruction reproducible to its source span in ≤5 minutes</text><rect x="158.59999999999997" y="762" width="178.4" height="26" rx="13" fill="#fff" stroke="#d7dee8"/><text x="247.79999999999995" y="779" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9.5" font-weight="600" fill="#5b6b7e" letter-spacing=".02em">TLS 1.2+ / AES-256 at rest</text><rect x="347.0" y="762" width="223.20000000000002" height="26" rx="13" fill="#fff" stroke="#d7dee8"/><text x="458.6" y="779" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9.5" font-weight="600" fill="#5b6b7e" letter-spacing=".02em">BAA: AWS · Bedrock · Twilio · SES</text><rect x="580.2" y="762" width="159.20000000000002" height="26" rx="13" fill="#fff" stroke="#d7dee8"/><text x="659.8000000000001" y="779" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9.5" font-weight="600" fill="#5b6b7e" letter-spacing=".02em">No PHI in notifications</text><rect x="749.4000000000001" y="762" width="172.0" height="26" rx="13" fill="#fff" stroke="#d7dee8"/><text x="835.4000000000001" y="779" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9.5" font-weight="600" fill="#5b6b7e" letter-spacing=".02em">RxNorm / LOINC normalized</text><line x1="267.8" y1="816" x2="293.8" y2="816" stroke="#5b6b7e" stroke-width="2" marker-end="url(#dfah5b6b7e)"/><text x="301.8" y="820" text-anchor="start" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9" font-weight="600" fill="#5b6b7e" letter-spacing=".02em">data flow</text><line x1="378.20000000000005" y1="816" x2="404.20000000000005" y2="816" stroke="#166534" stroke-width="2" marker-end="url(#dfah166534)"/><text x="412.20000000000005" y="820" text-anchor="start" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9" font-weight="600" fill="#5b6b7e" letter-spacing=".02em">validated path</text><line x1="516.6" y1="816" x2="542.6" y2="816" stroke="#b45309" stroke-width="2" marker-end="url(#dfahb45309)"/><text x="550.6" y="820" text-anchor="start" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9" font-weight="600" fill="#5b6b7e" letter-spacing=".02em">review & delivery</text><line x1="671.8" y1="816" x2="697.8" y2="816" stroke="#8a97a6" stroke-width="2" stroke-dasharray="5 4" marker-end="url(#dfah8a97a6)"/><text x="705.8" y="820" text-anchor="start" font-family="JetBrains Mono, ui-monospace, monospace" font-size="9" font-weight="600" fill="#5b6b7e" letter-spacing=".02em">fallback / post-MVP</text></svg></div>
<h2 id="doc-engineering-dataflow--h1">Inbound: triggers and reads</h2>
<p>The episode trigger is an <strong>HL7v2 ADT A03</strong> (discharge, disposition=home) delivered over the site interface engine/VPN, verified against the FHIR <code>Encounter</code> before a candidate episode is created (≤15 min, ENR-001). If a hospital declines the ADT feed, an Encounter poll every 5 minutes is the contracted fallback (ADR-007), and the first-line integration for any partner is a plain <strong>document drop</strong>, which needs no IT project at all. FHIR R4 reads are limited to the registered SMART backend-services scopes: <code>Patient</code>, <code>Encounter</code>, <code>MedicationRequest</code>/<code>MedicationStatement</code>, and <code>DocumentReference</code> for the discharge summary and AVS. Every read crosses one boundary, <code>core/ehr_adapter</code>, so swapping the FakeEHR fixture for a live Epic connection is configuration, not a rewrite.</p>
<h2 id="doc-engineering-dataflow--h2">Through the pipeline</h2>
<p>Documents are fingerprinted on ingest (duplicates are no-ops; amended versions chain by supersession), parsed and segmented deterministically, then extracted by <strong>AWS Bedrock (Claude, version-pinned, temperature 0, zero-retention BAA)</strong>, one section per call, JSON-schema tool outputs. Nothing an LLM produces is patient-visible until it passes all <strong>seven validation gates</strong>, including RxNorm/LOINC normalization, deterministic clinical checks, the verbatim source-span proof, and a pinned-embedding semantic pass (DQS-011). Uncertain or conflicting material routes to the clinical review queue, never to a patient. Published facts become tasks through the episode engine, the single writer of state, whose transactional outbox drives every 60-second SLA downstream (notification suppression, brief staleness, caregiver revocation). Two clarifications the diagram compresses: the numbered spine is seven pipeline <em>stages</em>, a different seven from the validation gates that live inside stage 3; and failure paths are explicit, malformed or unparseable documents land in a dead-letter queue with ops alerting (DPC-001) rather than silently dropping, while gate failures route to the review queue with per-gate metrics so a drifting extractor is visible within a day.</p>
<h2 id="doc-engineering-dataflow--h3">PHI handling</h2>
<p>TLS 1.2+ in transit, AES-256 at rest under KMS across RDS, S3, Redis, and backups; BAAs with every PHI-touching vendor (AWS/Bedrock, Twilio, SES; error telemetry is PHI-scrubbed by configuration). Notifications carry no PHI, "you have a new task" plus a link is the entire message. Every PHI read, state transition, automation action, and config change lands in the append-only audit ledger (INSERT-only role, 6-year WORM archive); any instruction a patient sees is reproducible to its source span and gate trail in under five minutes.</p>
<h2 id="doc-engineering-dataflow--h4">Integration timeline</h2>
<p>Sequenced so development and the pilot never wait on hospital IT, each step is independently useful, and the ADT/FHIR steps can proceed in parallel with paperwork.</p>
<div class="tbl-wrap"><table class="col-table">
<thead><tr><th>Step</th><th>Typical window</th><th>What happens</th><th>Unblocks</th></tr></thead>
<tbody>
<tr><td data-label="Step"><strong>0 · FakeEHR (no hospital)</strong></td><td data-label="Window">Day one</td><td data-label="What happens">All development and demos run against the synthetic hospital-in-a-box with the same adapter interface.</td><td data-label="Unblocks">Every development phase</td></tr>
<tr><td data-label="Step"><strong>1 · Document drop</strong></td><td data-label="Window">~1 week</td><td data-label="What happens">Discharge packets arrive via secure SFTP/upload; full pipeline runs on real documents. No EHR project required.</td><td data-label="Unblocks">Pilot ingestion on day one of the partnership</td></tr>
<tr><td data-label="Step"><strong>2 · App registration</strong></td><td data-label="Window">1–2 weeks</td><td data-label="What happens">SMART backend-services client registered in the partner's Epic environment; scopes approved; sandbox keys issued.</td><td data-label="Unblocks">Steps 3–4</td></tr>
<tr><td data-label="Step"><strong>3 · ADT trigger</strong></td><td data-label="Window">1–2 weeks</td><td data-label="What happens">A03 feed over the site VPN (or the ADR-007 poll fallback if declined); trigger verified against Encounter reads.</td><td data-label="Unblocks">Automatic episode creation ≤15 min</td></tr>
<tr><td data-label="Step"><strong>4 · FHIR reads + UAT</strong></td><td data-label="Window">2–3 weeks</td><td data-label="What happens">Demographics, med orders, and documents read live; sandbox scenarios replayed with hospital IT; stage soak.</td><td data-label="Unblocks">Full go-live</td></tr>
<tr><td data-label="Step"><strong>5 · Production cutover</strong></td><td data-label="Window">~1 week</td><td data-label="What happens">Production keys, monitoring dashboards, runbook rehearsal, kill-switch drill with the partner.</td><td data-label="Unblocks">Live episodes</td></tr>
</tbody>
</table></div>
<p style="font-size:12.5px;color:var(--slate);">End-to-end: roughly 6–9 weeks of hospital-side elapsed time, most of it paperwork, engineering is never blocked because steps 0–1 carry the build and the pilot's first ingestion path.</p>
<h2 id="doc-engineering-dataflow--h5">See also</h2>
<p><a href="#doc-product-cycle">The 30-Day Episode</a>, the product-level timeline view of the same system · <a href="#doc-reference-api">API specification</a>, the full endpoint catalog · <a href="#doc-modules-ingestion">ingestion module</a>, the contract behind this page.</p>
<div class="pn"><a class="pn-prev" href="#"></a><a class="pn-next" href="#"></a></div>
</article>
|