Objective
A clinical safety review asks three questions about any algorithm near a patient:
- Could it have changed the chart?
- What exactly did it recommend, when, and off which observation?
- Can you prove that record hasn't been edited since?
Most AI deployments answer the first with a code review, the second with application logs, and the third not at all. This recipe answers all three with database mechanics — a deterioration-watch agent whose inability to write is enforced by the engine, whose every recommendation is recorded automatically, and whose record is sha256 hash-chained so an edit is detectable.
Nothing here is a policy document. It is all SELECT-able.
Synthetic data only. The observations below are invented. This recipe demonstrates governance and traceability mechanics — it is not a validated clinical tool.
Requires: SynapCores v1.9.1+ (the decision-lineage columns on
_system_agent_runs). Every statement below was validated end-to-end on a stock v1.11.0-ce container — with no AI configuration at all, since CE ships a tool-capable default model (qwen2.5-coder:7b) and auto-pulls it on first boot.
Step 1: The observations table
CREATE TABLE IF NOT EXISTS recipe_patient_observations (
obs_id INTEGER PRIMARY KEY,
patient_ref TEXT,
heart_rate INTEGER,
resp_rate INTEGER,
temp_c DOUBLE,
systolic_bp INTEGER,
lactate_mmol DOUBLE,
recorded_at TIMESTAMP
);
Step 2: Create the agent — decision support that structurally cannot act
CREATE AGENT recipe_deterioration_watch
PERSONA 'aidb-assistant'
TASK 'A new vitals observation was recorded. Using execute_query on recipe_patient_observations, assess deterioration risk. Reply with exactly one of ESCALATE, MONITOR or ROUTINE, then the two vital signs that drove it. Under 50 words. You are decision support only; you never alter the chart.'
ON INSERT INTO recipe_patient_observations
WITH (
max_iterations = 3,
allow_writes = FALSE, -- enforced by the engine, not the prompt
timeout_seconds = 120,
budget_tokens_per_day = 200000,
on_budget_exhausted = 'pause',
memory = 'stateless' -- no cross-patient carryover
);
Two choices matter more here than anywhere else:
allow_writes = FALSEis the whole safety argument. The task text says "you never alter the chart" — but text is a request. This flag is the only route by which an agent's database tools may mutate a row, and it is off. The prompt is advisory; the envelope is enforced.memory = 'stateless'means no reasoning context carries between runs. Patient B's assessment cannot be contaminated by patient A's. For cohort-level work you would choose'persistent'; for per-patient triage, statelessness is the control.
Step 3: Record observations
INSERT INTO recipe_patient_observations VALUES
(7001,'PT-4417', 128, 26, 38.9, 88, 3.4, '2026-07-22 03:14:00'),
(7002,'PT-2290', 74, 16, 36.8, 122, 0.9, '2026-07-22 03:20:00');
Returns in milliseconds. Charting never waits on inference — the agent runs asynchronously off the write path, and it observes committed rows only (AFTER-semantics), so it can never abort or alter the write that woke it.
Allow 30–60 seconds per run on a local 7B model before Step 4.
Step 4: Read the recommendations
SELECT agent_name, activation, started_at, status, output
FROM _system_agent_runs
ORDER BY started_at;
| activation | status | output |
|---|---|---|
| INSERT ON recipe_patient_observations | success | ESCALATE — Heart Rate: 128 (above threshold); Lactate: 3.4 (above threshold) |
| INSERT ON recipe_patient_observations | success | ROUTINE — Systolic BP: 122 (within normal range); Heart Rate: 74 (within normal range) |
The tachycardic, febrile, hypotensive, hyperlactatemic patient gets ESCALATE; the stable one gets ROUTINE. More importantly for a safety review: both recommendations were recorded by the engine, with the observation that triggered them, without the application being asked to log anything.
Step 5: Show the safety envelope as data
DESCRIBE AGENT recipe_deterioration_watch;
Persona | aidb-assistant
Bindings | INSERT ON recipe_patient_observations
Enabled | true
max_iterations | 3
allow_writes | false
timeout_seconds | 120
budget_tokens_per_day | 200000
on_budget_exhausted | pause
memory | stateless
max_retries | 1
Created At | 2026-07-24T00:39:12Z
Updated At | 2026-07-24T00:39:12Z
allow_writes | false, with a creation and modification timestamp. That is the evidence that answers "could it have changed the chart?" — and Updated At tells a reviewer whether the envelope was ever loosened after go-live.
Step 6: The hash chain
SELECT agent_name, prev_hash, entry_hash, verified
FROM _system_agent_runs
ORDER BY started_at;
recipe_deterioration_watch prev 000000000000… -> entry e0bca42809f0… verified: true
recipe_deterioration_watch prev e0bca42809f0… -> entry 332923460100… verified: true
Every run's entry_hash is sha256(prev_hash ‖ the run's immutable content), linking back to the run before it and ultimately to a genesis value of 64 zeros.
verified is computed, not stored: the engine recomputes the chain on every read. Alter a recommendation in storage and that row stops hashing to its recorded value, and the next run's prev_hash no longer links. Retroactively softening an ESCALATE to a MONITOR after a bad outcome is exactly the scenario this defeats.
The chain is per tenant — all agents share one ledger, so one agent's history cannot be excised without breaking every record written after it.
Step 7: The standing control
SELECT COUNT(*) AS unverified_runs
FROM _system_agent_runs
WHERE verified = false;
unverified_runs
---------------
0
Zero is the only acceptable answer. Run it nightly. (verified is NULL for pre-v1.9.1 runs, which were never chained — reported honestly as "not attestable" rather than as a false pass.)
Step 8: An append-only clinical decision ledger
Detection is good; prevention plus a retention-independent copy is better. Mirror the trail into a table the storage engine will not let anyone modify:
CREATE IMMUTABLE TABLE recipe_clinical_decision_ledger (
run_id TEXT PRIMARY KEY,
agent_name TEXT,
activation TEXT,
decided_at TEXT,
status TEXT,
entry_hash TEXT,
recommendation TEXT
);
INSERT INTO recipe_clinical_decision_ledger
(run_id, agent_name, activation, decided_at, status, entry_hash, recommendation)
SELECT run_id, agent_name, activation, started_at, status, entry_hash, output
FROM _system_agent_runs;
Attempt to revise history:
UPDATE recipe_clinical_decision_ledger SET recommendation = 'ROUTINE' WHERE status = 'success';
DELETE FROM recipe_clinical_decision_ledger WHERE status = 'success';
Error: Cannot UPDATE immutable table 'recipe_clinical_decision_ledger'. Immutable tables are append-only.
Error: Cannot DELETE from immutable table 'recipe_clinical_decision_ledger'. Immutable tables are append-only.
There is no elevated role that grants this. Append-only is a storage-engine property, so a DBA has no more power over the ledger than an application does.
Step 9: The kill switch
If a model regresses, you must be able to stop it in one statement and still keep everything it did:
ALTER AGENT recipe_deterioration_watch DISABLE;
Then record another observation:
INSERT INTO recipe_patient_observations VALUES
(7003,'PT-9001', 140, 30, 39.4, 80, 4.1, '2026-07-22 04:00:00');
The observation is charted normally. No recommendation is produced — the binding is torn down. Clinical workflow is unaffected by the agent being switched off, because the agent was never in the write path to begin with.
SHOW AGENTS will show state = disabled alongside the tokens already spent today. And a full DROP AGENT still retains the run history: you cannot delete the audit by deleting the agent.
Cleanup
DROP AGENT IF EXISTS recipe_deterioration_watch;
DROP TABLE IF EXISTS recipe_patient_observations;
DROP TABLE IF EXISTS recipe_clinical_decision_ledger;
Expected Outcomes
- Step 3 returns in milliseconds; charting is never blocked on inference.
- Step 4 shows
ESCALATEfor the deteriorating patient andROUTINEfor the stable one, each tied to its triggering observation. - Step 5 shows
allow_writes | falsewith timestamps. - Step 6 shows a linked chain from 64 zeros, every row
verified = true. - Step 7 returns
0. - Step 8 rejects
UPDATEandDELETEat the storage engine. - Step 9 stops recommendations immediately while charting continues and history is retained.
Use it from your agent
- REST / SDK — every statement runs through
POST /v1/query/execute. Step 7 becomes a nightly integrity job; Step 4 becomes the "what did the algorithm say about this patient" panel in your review tooling. - MCP — point an MCP client at
ws://<your-instance>/mcp?token=<jwt>; thequerytool reaches the same trail, so a safety reviewer's assistant can read the audit directly. - Why in-database matters here — PHI does not need to leave the building for any of this. The observation, the recommendation, the governance envelope, the hash chain, and the immutable ledger are all rows in one self-hosted database. There is no vendor round-trip to include in your risk assessment, and no second system whose logs you must reconcile.
Key Concepts Learned
- A prompt that says "never alter the chart" is a request;
allow_writes = FALSEis a control. Ship the control. memory = 'stateless'prevents reasoning carryover between patients — statelessness as a clinical safety property._system_agent_runsinstruments every run automatically; there is no code path where an agent decides something unlogged.verifiedis recomputed on read, so tamper-evidence costs you oneCOUNT(*)— not a separate audit pipeline.- Hash chain (detect) + immutable table (prevent) + kill switch (stop) + retained history after
DROP AGENT(preserve) is the full governance loop, in plain SQL.
Related recipes
- Prove an AI Agent's Decision Was Never Altered (Loan Underwriting) — the same controls under ECOA/FCRA adverse-action scrutiny.
- Agentic Healthcare Clinical QA — the reasoning side: grounding clinical answers in your own documents.
- Self-Checking Grounded Generation — reduce the chance the agent says something unsupported in the first place.