Prove an AI Agent's Decision Was Never Altered (Clinical Decision Support)

Run a deterioration-watch agent that can read the chart but never write to it, then keep a hash-chained, append-only record of every recommendation it made — the traceability a clinical safety review actually asks for.

All recipes· agents· 15 minutesintermediatesql
Instance: localhost:8080

Opens your running SynapCores (Prove an AI Agent's Decision Was Never Altered (Clinical Decision Support) will be staged for a preview — nothing runs until you click Run). No instance yet? Install free in ~30s.

Share

Objective

A clinical safety review asks three questions about any algorithm near a patient:

  1. Could it have changed the chart?
  2. What exactly did it recommend, when, and off which observation?
  3. 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 = FALSE is 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 ESCALATE for the deteriorating patient and ROUTINE for the stable one, each tied to its triggering observation.
  • Step 5 shows allow_writes | false with timestamps.
  • Step 6 shows a linked chain from 64 zeros, every row verified = true.
  • Step 7 returns 0.
  • Step 8 rejects UPDATE and DELETE at 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>; the query tool 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 = FALSE is a control. Ship the control.
  • memory = 'stateless' prevents reasoning carryover between patients — statelessness as a clinical safety property.
  • _system_agent_runs instruments every run automatically; there is no code path where an agent decides something unlogged.
  • verified is recomputed on read, so tamper-evidence costs you one COUNT(*) — 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

Tags

ai-agentcreate-agentaudit-trailimmutablegovernanceguardrailshealthcareclinical-decision-supporthipaatraceability

Run this on your own machine

Install SynapCores Community Edition free, paste the SQL or Cypher above into the bundled web UI, and watch it run.

Download Free CE