Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Platform Ambient Transcription & AI Note Generation โ Integration Contract
Owner: PF (Platform Foundation)
Status: ๐ Specification
Spec: specs/pf/specs/PF-100-platform-ambient-transcription-and-note-generation.md
Last Updated: 2026-04-22
Cross-core integration contract for PF-100. Captures the boundary between PF (capture, STT, LLM, template engine, evidence map, lifecycle) and consuming cores (CL, PM, HR, GR, CE, RH). All cross-core consumption is via @/platform/transcription โ no direct module imports.
1. Pattern
This is a Pattern 1 (Platform Integration Layer) + Pattern 2 (Event Contracts) integration.
- Layer:
@/platform/transcription exposes hooks, components, types.
- Events:
pf.transcription.* event family published; cores subscribe via PF-66 realtime + edge-function subscriptions.
- Tables:
pf_transcription_* owned by PF; cores own their domain tables (e.g. cl_progress_notes, hr_meeting_summaries) and link via session_id UUID column (no cross-core FK except CL โ PM encounter per ADR-002).
2. Scope of contract
| Owned by PF (PF-100) | Owned by consumer core |
|---|
| Capture (browser/mobile/bot/external/upload/dictation) | Choosing the recording_purpose and template |
| Consent gate + revocation cascade | Subject-of-record CRUD |
| STT vendor routing + adapters | โ |
| LLM vendor routing + adapters | โ |
| Template engine + evidence mapping | Per-core templates (registered under platform builtins, or per-tenant custom) |
| Audio storage + retention + lifecycle | Domain-record retention (clinical record vs HR record vs GR record) |
pf_transcription_* tables | Domain tables (cl_progress_notes, hr_meeting_summaries, gr_meeting_minutes, ce_call_outcomes, rh_resident_interactions) |
| BAA matrix + vendor-call audit | โ |
| PF-91 evidence emission for Part 2 / consent / retention controls | Domain-specific compliance (e.g. CL-04-EN-66 Policy 940) |
See PF-100 spec ยง8.1 for full hook list. Core consumer pattern:
import {
useStartTranscriptionSession,
useReviewQueue,
useAttestDraft,
} from '@/platform/transcription';
// CL example (encounter-linked)
const start = useStartTranscriptionSession();
await start.mutateAsync({
module_key: 'cl',
recording_purpose: 'cl_individual',
encounter_id,
subject_of_record_type: 'patient',
subject_of_record_id: patient_id,
source: 'browser',
});
// HR example
await start.mutateAsync({
module_key: 'hr',
recording_purpose: 'hr_one_on_one',
subject_of_record_type: 'employee',
subject_of_record_id: employee_id,
source: 'browser',
});
Cores MUST NOT import from @/platform/ai directly for transcription concerns; the PF-100 layer wraps PF-27/PF-59 internally.
4. Event Contracts
All events published on the platform realtime + outbox pattern. Subscribers register via PF-66 (frontend) or via a database-trigger-driven edge subscription (backend).
| Event | Publisher | Subscribers | Payload |
|---|
pf.transcription.session.created | PF-100 | CL-36 (link to encounter), PM-08 (charge prep), PF-91 (evidence) | { session_id, organization_id, module_key, recording_purpose, source, subject_of_record_type, encounter_id? } |
pf.transcription.session.consent_verified | PF-100 | CL-11 (audit), PF-91 | { session_id, consent_record_id, jurisdiction_profile_id, is_part2_program } |
pf.transcription.session.consent_revoked | PF-100 | All consumers โ STOP downstream processing | { session_id, revoked_by, revoked_at } |
pf.transcription.session.cancelled | PF-100 | All consumers | { session_id, reason } |
pf.transcription.draft.ready | PF-100 | CL-36 (review queue surface), HR/GR/CE/RH UIs, PF-10 (notify reviewer) | { session_id, draft_id, template_id, generation_latency_ms } |
pf.transcription.draft.attested | PF-100 | Consumer core (write domain record), PF-91 (evidence), PF-04 (audit) | { session_id, draft_id, reviewer_id, attested_at, attested_content_sha256, module_attest_payload } |
pf.transcription.audio.deleted | PF-100 | PF-91, PF-04, subject-of-record portal (notify subject) | { session_id, deleted_at, retention_policy } |
pf.transcription.deletion_requested | PF-100 | Operator alert, PF-91 | { session_id, requested_by, requested_at, scope } |
pf.transcription.deletion_completed | PF-100 | PF-91, subject portal | { session_id, completed_at, scope } |
pf.transcription.policy.violation | PF-100 | PF-91 (evidence), PF-10 (notify admin) | { session_id, violation_type, blocked_at } |
Consumed events
pf.consent.cl_consent.revoked (CL-11) โ triggers session-revocation cascade for any active CL session referencing the consent
pf.encounter.created (PM) โ optional auto-link encounter for a clinical session in pre-record state
pf.encounter.cancelled (PM) โ cancels any in-progress capture for that encounter
5. Cross-core FK rules (ADR-002 compliance)
Per ADR-002 and constitution ยง5.2.7:
pf_transcription_sessions.encounter_id โ UUID column with no FK constraint to pm_encounters.id at the database level (cross-core FK exception is reserved for CLโPM, not PFโPM).
- Validation is enforced at the application layer (edge function
pf-transcription-start-session rejects sessions whose encounter_id does not resolve to an existing pm_encounters.id in the same organization_id at session start).
- No trigger-enforced FK is created; an earlier draft of spec ยง7.1 implied a trigger โ that has been corrected.
pf_transcription_sessions.subject_of_record_id โ polymorphic UUID; no FK.
- CL projection layer (Phase 3):
cl_ambient_sessions becomes a VIEW over pf_transcription_sessions WHERE module_key='cl'. The CLโPM FK from CL-36-EN-01โs existing schema is preserved independently on the CL side (via a separate cl_ambient_session_encounters materialization or a CL-owned trigger that enforces the FK only when module_key='cl' AND encounter_id IS NOT NULL). PF-100 itself does not own the CLโPM FK; the CL DX team retains that responsibility per ADR-002.
6. Vendor adapter contract
Each STT or LLM adapter implements a strict TypeScript interface in supabase/functions/_shared/transcription-vendors/. PF-100 spec ยงFR-2.3 defines the STT contract; the LLM adapter contract mirrors PF-27/PF-59.
| Vendor | Tier | BAA | Adapter file |
|---|
| Deepgram Nova-3 Medical | primary STT | required (enterprise) | _shared/transcription-vendors/deepgram-nova3-medical.ts |
| AssemblyAI Universal-3 Pro + Medical Mode | failover STT, EU residency | self-serve | _shared/transcription-vendors/assemblyai-u3.ts |
| AWS Transcribe Medical | alternate STT | AWS BAA | _shared/transcription-vendors/aws-transcribe-medical.ts |
| AWS HealthScribe (bundled STT+LLM) | alternate end-to-end | AWS BAA | _shared/transcription-vendors/aws-healthscribe.ts |
| Azure AI Speech | alternate STT | Microsoft BAA | _shared/transcription-vendors/azure-speech.ts |
| OpenAI Whisper / gpt-4o-transcribe | alternate STT (ZDR) | OpenAI BAA | _shared/transcription-vendors/openai-whisper.ts |
| Self-hosted WhisperX | high-sensitivity STT | n/a (on-prem) | _shared/transcription-vendors/whisperx-self-hosted.ts |
| Anthropic Claude (Bedrock) | primary LLM | AWS BAA | _shared/transcription-vendors/claude-bedrock.ts |
| Azure OpenAI GPT-4o | alternate LLM | Microsoft BAA | _shared/transcription-vendors/azure-openai.ts |
| Google Vertex Gemini 2.5 Pro | alternate LLM | Google BAA | _shared/transcription-vendors/vertex-gemini.ts |
| Self-hosted Llama 3.3 70B | high-sensitivity LLM | n/a | _shared/transcription-vendors/llama-self-hosted.ts |
| Recall.ai | meeting bot | Recall.ai BAA | _shared/transcription-vendors/recall-bot.ts |
| Plaud Developer Platform | external recorder | Plaud BAA | _shared/transcription-vendors/plaud-device.ts |
Vendor BAA expiry monitor (pf-transcription-vendor-baa-monitor cron) emits PF-91 evidence + alerts at 30 / 14 / 7 day thresholds.
7. CL-36 migration contract (Phase 3)
Non-breaking. Sequence:
cl_ambient_sessions continues writing through CL-36 hooks; PF-100 hooks are added in parallel and wrap the same vendor calls.
- CL backend swap:
cl_ambient_sessions becomes a VIEW over pf_transcription_sessions WHERE module_key='cl'; cl_ambient_transcripts similarly over pf_transcription_segments.
- CL-36 attribution overlay (
cl_ai_note_attributions) consumes pf.transcription.draft.attested event with module_attest_payload.cl_progress_note_id.
- CL-36 hooks become deprecated shims; deletion in next major release per constitution ยง8.4 deprecation policy.
The CL-36-EN-01 deferred Phase 2 (group sessions, multi-language) is delivered through PF-100 Phase 2 (group de-identification) and Phase 2/3 (multi-language templates).
8. PF-96 jurisdiction profile extension contract (PF-96-EN-RECORDING)
PF-100 requires PF-96 enhancement adding a new top-level recording_rules JSONB DEFAULT '{}' NOT NULL column to pf_jurisdiction_profiles (the policy_json column referenced by an earlier draft does not exist; the actual JSONB columns on the table are clinical_rules, billing_rules, compliance_rules, metadata). All recording-policy keys live under recording_rules.*.
Keys (see PF-100 spec ยง7.2 for the authoritative list):
recording_consent_model
wiretap_class_severity
recording_max_retention_days_part2
recording_max_retention_days_general_clinical
recording_max_retention_days_hr
recording_max_retention_days_gr
external_recorder_allowed
external_recorder_forbidden_for_part2
mental_health_record_retention_years
requires_visible_recording_indicator
requires_per_participant_verbal_affirmation
recording_pause_resume_consent_threshold_seconds
Tracked as a PF-96 enhancement in specs/cross-cutting/MULTI-STATE-COMPLIANCE-ENHANCEMENT-CATALOG.md and authored at specs/pf/enhancements/PF-96-EN-RECORDING-recording-consent-jurisdiction-fields.md. PF-100 Phase 0 includes the corresponding migration.
8A. PF-71 dependency contract (patient-name list lookup)
Spec ยงFR-3.4 (co-attendee de-identification) requires a server-side patient-name list lookup to verify that AI-generated draft text does not leak names of patients who are NOT the subject of record (e.g. group-session co-attendees).
Expected API (PF-71 to provide; consumed by pf-transcription-generate-note edge function):
// supabase/functions/_shared/pf-71/patient-name-list.ts (PF-71 owned)
export async function pf71PatientNameList(args: {
organization_id: string;
encounter_id?: string; // when present, returns names of all patients linked to the encounter
group_session_id?: string; // when present, returns names of all attendees of the group session
context: 'draft_redaction';
}): Promise<{ names: string[]; phonetic_variants: string[]; }>;
Behavior:
- Returns first/last/preferred names + phonetic/common-misspelling variants for every patient in scope.
- Excludes the subject-of-record (caller passes
subject_of_record_id separately).
- Server-side only โ never invoked from the browser.
- Names are used solely for redaction matching; PF-100 MUST NOT log returned names. Edge function logs only counts (
{ matched_count: N }).
Status: PF-71 currently exposes patient-context primitives but not this exact list-lookup. PF-100 Phase 0 includes a sub-task to either (a) confirm an existing PF-71 RPC is sufficient, or (b) scope a small PF-71 enhancement to add pf_71_patient_name_list(). Tracked in PF-100-TASKS.md Phase 0 prerequisites.
9. PF-91 evidence emission contract
For every attested session, PF-100 emits structured evidence:
- Part 2 control evidence (
is_part2_program=true only): consent record id + version, segregation hash (Part 2 KMS alias confirmation), retention proof, deletion proof.
- All-party consent control evidence: per-participant consent acknowledgment timestamps, jurisdiction profile id, recording-indicator-shown proof.
- Retention control evidence: applied retention policy, scheduled deletion date, actual deletion timestamp.
- BAA-coverage control evidence: vendor id, BAA evidence id, model version, call counts.
Evidence rows are written to pf_compliance_evidence (PF-91) with source='pf-100-transcription' and evidence_kind per row above.
10. Audit & enforcement
npm run audit:integration-contracts โ verifies this file is referenced from PF-100 spec
npm run audit:permissions โ verifies all keys in PF-100 ยง6 are seeded
npm run audit:routes-navigation โ verifies any new routes (e.g. /platform/transcription/sessions, /platform/transcription/review) are registered
11. Backlinks
Activating a Vendor (Runbook)
Until these four steps are completed for at least one STT vendor, every call to pf-transcription-stt-stream returns 409 vendor_baa_inactive. LLM note generation works today via Lovable AI Gateway (no BAA gate).
- Sign the BAA โ paper artifact between the org and the vendor (Deepgram / AssemblyAI / AWS). Cannot be code-completed.
- Register the BAA row in
pf_transcription_vendor_baas:
INSERT INTO pf_transcription_vendor_baas
(organization_id, vendor_id, status, baa_signed_at, baa_expires_at)
VALUES ('<org>', '<vendor_id>', 'active', now(), now() + interval '1 year');
Use the Settings โ Transcription โ Vendors UI (VendorBaaTable) instead of raw SQL when possible.
- Add the API key to PF-76 vault under the credential name expected by the adapter (e.g.
deepgram_api_key). The router calls pf_retrieve_credential(org_id, name); missing keys surface as vendor_baa_inactive with reason: 'missing_credential'.
- (Optional) Add a routing rule in
pf_transcription_jurisdiction_routing so a specific (jurisdiction_code, recording_channel) resolves to this vendor instead of the org default.
The JurisdictionRoutingDialog shows a live resolver preview so admins can confirm routing before saving. The VendorBaaTable displays a green/amber/red status dot per BAA so live status is visible at a glance.