Skip to main content
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


3. Public API surface (@/platform/transcription)

See PF-100 spec §8.1 for full hook list. Core consumer pattern:
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).

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 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:
  1. cl_ambient_sessions continues writing through CL-36 hooks; PF-100 hooks are added in parallel and wrap the same vendor calls.
  2. 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.
  3. CL-36 attribution overlay (cl_ai_note_attributions) consumes pf.transcription.draft.attested event with module_attest_payload.cl_progress_note_id.
  4. 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):
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


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).
  1. Sign the BAA — paper artifact between the org and the vendor (Deepgram / AssemblyAI / AWS). Cannot be code-completed.
  2. Register the BAA row in pf_transcription_vendor_baas:
    Use the Settings → Transcription → Vendors UI (VendorBaaTable) instead of raw SQL when possible.
  3. 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'.
  4. (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.

PF-100-EN-01 — Ambient Vendor-BAA Enablement Runbook

PF-100-EN-01 is an operational enhancement to PF-100 (Platform Ambient Transcription & AI Note Generation) that adds the vendor-BAA procurement runbook: operators record executed BAA agreements into pf_transcription_vendor_baas, provision per-tenant AWS KMS aliases for Part-2 audio segregation, and complete the PF-96 recording-law jurisdiction profile so that the fail-closed vendor gate opens only for fully BAA-covered vendors. This enhancement operates entirely within the existing PF-100 integration surface documented above — it introduces no new cross-core contracts, no new events, and no new consumer-side APIs; all behavioural changes are internal to the PF transcription substrate.