AI Governance Engine
Date: 2026-05-14
Scope: The system that enforces Rōvn's doctrine, "AI operates, humans decide."
Posture: LIVE for executor + advisor ledger schema, vendor BAA matrix, truth-tier CHECK constraints, and per-decision human-actor enforcement. PARTIAL on per-tenant policy controls UI and complete ai_runs call-site coverage.
1. The doctrine, in code
Rōvn's product doctrine has one non-negotiable: humans are the actor on every credentialing, privileging, hiring, and clinical decision. AI operates time and surfaces signal; AI never adjudicates.
The AI Governance Engine is the set of code-level, schema-level, and process-level controls that make that doctrine enforceable, not merely promised.
It has four layers:
| Layer | Mechanism | LIVE? |
|---|---|---|
| Schema | CHECK constraints + immutable columns | LIVE |
| Gateway | ai_gateway.py policy enforcement |
LIVE |
| Ledger | ai_runs + audit_log hash-chain |
LIVE schema; PARTIAL coverage |
| Process | Replay + policy controls + vendor BAA matrix | LIVE matrix; PARTIAL controls UI |
2. The governance loop
┌─────────────────────────────────────────────────────────┐
│ Human-facing surface │
│ (facility workflow layer committee, Passport reviewer, Admin) │
└────────────────────────┬────────────────────────────────┘
│
┌──────────────────▼──────────────────┐
│ AI proposes (executor + advisor) │
│ Written to: ai_runs │
│ Output: advisory recommendation + │
│ confidence + sources │
└──────────────────┬──────────────────┘
│
┌──────────────────▼──────────────────┐
│ Human reviews │
│ Surface: facility workflow layer packet UI or │
│ Passport reviewer queue │
└──────────────────┬──────────────────┘
│
┌───────────────────────┼─────────────────────────┐
│ │ │
┌─────▼──────┐ ┌──────▼──────┐ ┌───────▼──────┐
│ APPROVE │ │ REJECT │ │ ESCALATE │
│ + rationale│ │ + rationale │ │ to senior │
└─────┬──────┘ └──────┬──────┘ └───────┬──────┘
│ │ │
└───────────────────────┼─────────────────────────┘
│
┌──────────────────▼──────────────────┐
│ Write decisions row │
│ actor_user_id = THIS HUMAN │
│ ai_advisory_id = FK to ai_runs row │
│ decision_rationale = required text │
└──────────────────┬──────────────────┘
│
┌──────────────────▼──────────────────┐
│ Hash-chain append to audit_log │
│ actor_kind = 'human' │
│ payload includes both decision + │
│ reference to AI advisory │
└─────────────────────────────────────┘
Three writes per decision, every time:
1. ai_runs row (the AI's advisory)
2. decisions row (the human's act)
3. audit_log row (the hash-chained event)
The AI cannot skip step 2. The decision row has no path from an AI actor, the schema rejects it.
3. Layer 1: Schema enforcement
Decision-row actor constraint
decisions has an enum constrained actor_kind column. Only the value 'human' is allowed for any decision type in the privileging / credentialing / hiring / clinical class. 'ai_advisory' is allowed for the separate privileging_recommendation_log table (an advisory log, not a decision). 'system' is allowed only for system-triggered events like cadence expirations or source-adapter retries.
If the application code attempts to insert a decisions row with actor_kind != 'human', the database rejects it. This is not a service-side check that can be bypassed by a buggy service; it is a CHECK constraint at the data layer.
Truth-tier CHECK constraint
On worker_trust_records (tiers 1-5: imported → attested → processed → source-verified → approved):
CHECK (
(tier < 4)
OR
(tier >= 4 AND source_receipt_id IS NOT NULL
AND last_verified_at IS NOT NULL
AND last_verified_at > NOW() - INTERVAL '<source TTL>')
)
No code path, including AI extraction, can promote a fact to tier 4 (source-verified) without a valid source receipt within the source's TTL. The AI extractor literally cannot mark a license source-verified; the only way that bit flips is through the source-adapter rail.
Immutable audit columns
audit_log has triggers that block UPDATE and DELETE on every row. The deploy IAM role does not have DELETE or UPDATE privileges on this table. The audit bucket in S3 is in Object Lock COMPLIANCE mode, even AWS root cannot delete during the 7-year retention window.
4. Layer 2: Gateway enforcement (ai_gateway.py)
Every AI call goes through app/services/ai_gateway.py. The gateway enforces:
-
Vendor BAA matrix check. The model name (e.g.,
claude-3-5-sonnet) must be present inai_vendor_baa_matrixwithbaa_active = TRUEandzdr_active = TRUE. If a developer attempts to call a non-registered model, the gateway raises and writes apolicy_violationevent toaudit_log. -
Per-tenant policy controls. Tenants can declare: -
require_human_review_for_phi_extraction(default: ON for hospital tenants) -block_ai_committee_response_drafts(default: OFF; some buyers require ON) -require_advisor_for_low_confidence(default: ON for clinical credentials) -daily_token_budget_usdThese rows live inai_policy_controls. -
PHI scrubber. Identifiers that don't need to leave the perimeter are tokenized. The model sees
<patient_token_a3f9>; the app rehydrates on return. The scrubber is a default-deny allow-list, fields not on the allow-list don't reach the model. -
Idempotency key. Required on every call. (
hash(tenant_id, purpose, input_hash, model)). Replay is deterministic: same key returns the cachedai_runsrow, not a new model call. -
ai_runswrite. Every call produces exactly oneai_runsrow, with token cost, latency, model, prompt hash, output hash, advisor calls (if any), and the requesting user.
If any of these checks fail, the AI call is refused. The gateway is the single entry point, there are no side-channel AI calls in the codebase (enforced by lint rule: import anthropic is allowed only inside ai_gateway.py).
5. Layer 3: Ledger (ai_runs + audit_log)
The two ledgers serve different purposes:
| Ledger | Purpose | Hash-chained? | Retention |
|---|---|---|---|
ai_runs |
Every AI call: model, prompt, output, cost, latency | No (per-row hashes only) | 7 years |
audit_log |
Every decision + every consequential event | YES (chained SHA-256) | 7 years (S3 Object Lock) |
When a human approves a privileging packet, three things happen:
- An ai_runs row already exists (the advisory the human reviewed).
- A decisions row is written with the human as actor.
- An audit_log event is written, hash-chained, referencing both rows.
This separation lets an auditor ask two distinct questions:
- "What did the AI tell the human at the moment of decision?" →
ai_runs(immutable per-row hash; we can prove the advisory has not been edited). - "What did the human decide, and was the chain tampered with?" →
audit_log(replay the chain; verifyentry_hashagainstprev_hash || payload).
6. Layer 4: Process (replay, policy controls, vendor matrix)
Replay
Every AI run is replayable:
- idempotency_key lets us recall the exact ai_runs row.
- prompt_hash lets us prove the prompt has not changed.
- output_hash lets us prove the output has not changed.
- With the same model name, same prompt, same input, the model would (within model-provider determinism guarantees) reproduce the output. For Anthropic, we additionally store full prompt JSON and full output JSON in ai_runs so replay does not depend on third-party determinism.
This makes "show me what the AI said and what the human decided on 2026-03-14 at 14:32:09 UTC" a one-query operation.
Per-tenant policy controls
ai_policy_controls is the per-tenant config table. Hospital procurement teams often ask: "Can we disable the AI from drafting committee responses?", the answer is yes, flip the toggle, and the gateway will refuse to invoke executor on that route for that tenant. The toggle write is itself audit-logged. UI for self-serve toggle management is PARTIAL.
Vendor BAA matrix
ai_vendor_baa_matrix holds the canonical list of AI vendors authorized to receive PHI traffic. Each row has:
- vendor (anthropic, aws_bedrock)
- model (claude-3-5-haiku, claude-3-5-sonnet, claude-opus-4-7-advisor)
- baa_active (boolean)
- baa_expiration
- zdr_active (boolean)
- notes
A vendor not in this matrix cannot receive PHI traffic. Adding a row is an admin-role action that writes to audit_log. This is how we make "BAA discipline" enforceable rather than aspirational.
7. Failure modes and what happens
| Failure | Engine response |
|---|---|
| Model API down | Block the decision flow. Do NOT auto-approve. Surface "AI advisory unavailable, proceed manually" to the human. |
| Low confidence | Escalate to advisor; if still low, escalate to senior reviewer queue. |
| Cross-source disagreement | Hold worker_trust_records.tier = 3 (processed); block source-verified (tier 4) promotion until human resolves the conflict. |
| AI gives different answer on replay | output_hash mismatch → audit alarm fires; the canonical output is the stored ai_runs row, not the replay. |
| Vendor BAA matrix entry expires | Gateway refuses calls to that model; audit alarm fires. |
| Policy violation attempt | policy_violation audit event written; ops paged. |
No failure mode auto-approves a decision. No failure mode hides itself from the audit chain.
8. What an auditor can do today
Procurement and compliance auditors evaluating Rōvn can perform these checks against the LIVE system:
- Read chain head.
GET /audit/chain-headreturns the latest entry hash. - Replay recent events.
GET /audit/events/recent?since=...returns events; verify each entry hash againstprev_hash || payload_hash. - Inspect any decision. Every
decisionsrow carriesactor_user_id,decision_rationale, and (when applicable)ai_advisory_id. The auditor can verify that AI never appears as the actor. - Inspect any AI call.
ai_runsrows expose model, prompt, output, token cost, latency. Auditors can sample any decision and demand "what did the AI say here?" - Verify vendor BAA matrix. Every AI vendor is in the matrix with BAA + ZDR status; an auditor can confirm the matrix matches the BAAs on file in the compliance folder.
9. What this engine does not claim
- We do not claim "no AI hallucination is possible." We claim that hallucinations cannot move a tile to source-verified, and cannot become a decision row.
- We do not claim per-tenant policy UI is fully self-serve, PARTIAL (admin-applied today).
- We do not claim 100% of routers have
ai_runscoverage today, PARTIAL. - We do not claim the audit log is immune to source-data manipulation upstream of Rōvn (e.g., a state Board of Nursing publishing wrong data). We claim the receipt captures what the source said at that timestamp, and our chain is tamper-evident from that point forward.
10. Why this is a moat, not overhead
Buyer procurement teams have asked us, repeatedly: "How do we know your AI isn't quietly making decisions?" The honest answer is structural. The schema does not let it. The gateway does not let it. The audit log does not let it.
That structural answer is what underwrites the words "AI Trust Layer" on the marketing site. It's not a logo, it's a CHECK constraint.
End of memo.