# AGENTS.md: building AI that touches health or personal data

<!-- Arbiter safe practices for AI agents that handle health or sensitive personal data, version 1.1.0 (agent policy 1.1.0), updated 2026-11-02. Source: https://www.arbiterai.tech/practices.html. License: Apache-2.0. Not legal advice: these are technical safeguards; compliance also needs agreements, a risk analysis, policies and training. -->

When you write or change code that sends health records or other sensitive personal data to a language model, or that stores, retrieves or logs such data, apply every practice below. If a practice cannot be met, stop and say so in your summary instead of working around it.

Ground rules:
- Use synthetic data in tests and examples. Never put real patient data in code, fixtures, logs or prompts you write.
- Prefer enforcing a rule in code with a test over writing it into a prompt.
- The full reasoning and sources are at https://www.arbiterai.tech/practices.html; a runnable policy engine with these rules is at https://www.arbiterai.tech/kit/arbiter-policy-kit-1.1.0.zip.

## Practices

### De-identify every prompt and stored chunk, and measure it per component
Remove direct identifiers (the 18 HIPAA Safe Harbor categories) from every document and every question before any model call or embedding, and measure de-identification recall per component (street, city, ZIP), not per whole string.
- Run the de-identifier at ingest and on every question; store only de-identified text and embeddings.
- Add recognizers for the formats stock tools miss (record numbers with OCR'd labels, phone extensions, clinician names after a title, street addresses) and filter date hits to calendar dates.
- Keep the token-to-value map encrypted per tenant; re-identify only the tokens an answer uses, only for the authorized caller, after validation.
- Before re-identification, scan the model output for raw identifiers; one found there is a miss upstream.
- Test: Seed synthetic records with known identifiers, report recall per component, and block a release below 0.99.

### Send data only to local models or vendors under a signed BAA, and refuse to start otherwise
Every model, embedding and vision endpoint is on this machine or network, or belongs to a vendor with a signed business associate agreement; check it when the service starts, not per request.
- Keep an allowlist of local hosts and BAA vendors (with the date each BAA was signed) in configuration.
- At startup, parse every model URL and stop the service in production if a host is not on the list.
- Prefer on-device or on-premises open models for the sensitive path.
- Test: Point a model URL at an unlisted host in a test and assert the service refuses to start.

### Keep prompts out of third-party tracing and persistent agent state
Do not export prompts, retrieved text or answers to SaaS tracing or observability tools, and do not persist agent graph state that contains pre-de-identification text.
- Turn off LangSmith/LangChain tracing and any OpenTelemetry exporter that points off-network.
- Use an in-memory or no checkpointer for runs that hold raw text; log ids, counts and timings only.
- Test: A startup check fails when a tracing variable is on or a persistent checkpointer is configured.

### Enforce tenant and patient isolation in the database, never as a superuser
Scope every query by tenant with row-level security, add a hard patient filter to retrieval, and connect as a role that RLS applies to.
- Derive the tenant from the credential, never from the request body; set it transaction-locally.
- Connect as an application role without SUPERUSER or BYPASSRLS; check the role at startup.
- {'Probe it': "a second tenant's key gets 404, and a cross-tenant insert violates the policy."}
- Test: Cross-tenant probes in the test suite, and a startup check of rolsuper and rolbypassrls.

### Require a purpose of use on every request, and refuse what the law doesn't permit
Each request states its purpose (treatment, payment, operations, the patient's own access); any other purpose needs a recorded authorization, and a tenant without a BAA gets nothing.
- Make purpose a required, validated field; record it on the audit row.
- Deny before retrieval when the purpose is not permitted, so nothing is read.
- Test: A marketing-purpose request is refused with zero records read.

### Give the agent a closed tool list and the least data each purpose needs
The agent can call only a fixed set of tools, each allowed per purpose, and retrieval is capped per purpose (fewer excerpts for billing and operations than for treatment).
- Put the tool names in the planner's JSON schema as an enum, then check each call against the purpose.
- Cap k per purpose; no web, email or write tools on the sensitive path.
- Test: An unknown tool is refused and recorded; a payment request never retrieves more than its cap.

### Keep psychotherapy notes and substance-use treatment records out unless consent says otherwise
Label documents by type at ingest and drop psychotherapy notes without an authorization and substance-use treatment records without Part 2 consent, before the model sees them.
- Carry the document type with every retrieved excerpt and filter after retrieval, before drafting.
- Read consents from the organization's consent records, not from the caller.
- Test: A request whose search hits a therapy note drafts from the other excerpts only, and counts what was withheld.

### Answer only with citations to the excerpts provided, and reject anything else
Every answer cites at least one excerpt it was given; a draft with no citation, a raw identifier or a low grounding score is rejected, never delivered with a warning.
- Number excerpts in the prompt and require [id] citations.
- Validate before re-identification; return a failed status with the reasons, not an answer.
- Test: A draft without a citation comes back rejected with the reason.

### State a "no" only when the record states it; otherwise say it is not documented
Check negative claims separately from positive ones: a negation counts as supported only when the source negates the same thing, and an unsupported one is restated as "the records searched do not document this".
- Split answers into sentences; send positive claims to the judge and check negative ones against negation cues in the source.
- Strip citation markers before judging; a small judge scored identical facts 0.0 or 1.0 by citation position.
- Report accuracy per question type, including questions whose right answer is "no" or "not in the record".
- Test: Replay correct negations, family-history cases and absent facts; correct answers pass or are restated, wrong ones are rejected.

### Never make coverage, eligibility or medical-necessity decisions; a licensed clinician decides
In billing, utilization review and operations, the agent summarizes evidence and labels its output as decision support; it never approves, denies or deems care necessary.
- Block determination language in drafts for these purposes and append a decision-support notice.
- Route "approve or deny" requests to a person.
- Test: "Is this medically necessary? Approve or deny it." is escalated or rejected, never answered with a decision.

### Tell patients when AI wrote the message and how to reach a person, by state
Patient-facing messages name the patient's state; where the state requires it (California AB 3030), they open with an AI disclosure and how to reach a human, unless a licensed provider reviewed them first.
- Refuse patient-facing requests that do not carry a state.
- Add the disclosure after validation, before delivery; skip it only when a provider review is recorded.
- Test: A California patient message starts with the disclosure; the same message marked provider-reviewed does not.

### Audit every request with the policy version and the rules that fired, never the content
Write one append-only audit row per request with who, which patient, purpose, outcome, tools used, counts, the policy version and the rule ids checked and fired; no question, excerpt or answer text.
- Revoke UPDATE and DELETE on the audit table from the application role.
- Distinguish delivered, rejected, denied and escalated outcomes.
- Test: Grep the audit rows from a test run for any answer text; there should be none.

### Keep an inventory of every AI tool's inputs, training data, evaluation and known risks
For each model or decision-support tool, document its input variables, whether any measure race, color, national origin, sex, age or disability, its training data, how it was evaluated, and its known risks; refuse to start without it.
- Keep the inventory in the repository next to the policy and review it on every model change.
- Say plainly what is unknown, such as an undocumented training set or synthetic-only evaluation.
- Test: A startup check fails when any tool is missing a field.

### Gate releases on evals big enough to see the change you claim
A release counts as verified only if the eval can detect the size of difference being claimed (about 346 questions for 5 points), strict de-identification recall holds, and there are zero cross-patient leaks; report accuracy per question type.
- Run a power calculation before trusting a comparison; report error bars clustered by record.
- Fail the release when any gate fails, and publish the failure.
- Test: Feed the gate a 40-question report and confirm it fails.

### Keep the rules in a versioned file, re-review them on a schedule, and let a person approve every change
The agent's rules live in one versioned file with a source and a test for each; new laws and findings become proposals with evidence, and only a person promotes them.
- Refuse to load a rule that has no source or no test; stamp the version on every audit row.
- Warn, without stopping the service, when the scheduled review is overdue.
- Test: A rule without a source fails to load; the audit row of every request carries the policy version.

## When you finish

List which practices the change touches, how each is enforced (file and test), and any you could not meet.
