TruAssure guide

Black-box, evidence-based quality engineering for AI agents: chat assistants, task agents and multi-agent workflows. This guide ships inside the package (ta docs --open) and needs no network.

1. Install

pip install truassure                 # the core: the ta / truassure commands and the library
pip install "truassure[mcp,proxy,otel]"  # plus the MCP server, the recording proxy and A2A tap, the OTel receiver
ExtraAdds
proxyta proxy (recording MCP proxy with fault injection) and ta a2a (agent-to-agent tap)
otelta otel: an OTLP/HTTP receiver for the agent's OpenTelemetry GenAI spans
mcpta mcp: TruAssure as an MCP server for AI assistants
sql, postgres, sqlserver, mysql, oracleAny SQL database through SQLAlchemy (state and shared store), with the driver
servicebus, kafkaAgents behind Azure Service Bus or Kafka (the queue driver)
s3, azure-blobShared stores on Amazon S3 or Azure Blob Storage (a folder or network share needs nothing)
azure, anthropicThe LLM rubric grader (output-vs-action consistency) on Azure OpenAI or Anthropic
chat, langgraph, postgresThe WebSocket chat driver; LangGraph and Postgres support for your own adapters

Python 3.11 or later, on Windows, Linux or macOS. Pin the version in projects and CI: truassure==__VERSION__.

2. Five-minute demo

ta demo                 # runs the whole tour offline, into ./ta_demo
ta console --runs-dir ta_demo --open

The demo needs no agent, no Docker and no LLM: a scripted agent plays a fictitious insurance assistant. It runs the demo catalog twice with repeats and perturbations, runs a build with a planted defect, then compares the runs, lists defects, builds the scorecard, applies the release gate, calibrates a grader and writes HTML reports. Every step prints the ta command it ran, so you can repeat any of them.

3. Start a project

ta new task claims-tests        # or: ta new workflow ... / ta new chat ...
cd claims-tests
ta validate catalog.yaml
FileWhat to do with it
catalog.yamlAn example catalog for the profile. Replace the scenarios with your agent's.
environment.pyHow TruAssure reaches the agent and where the evidence is. Fill in the TODOs (mostly the reset).
.env.exampleThe settings environment.py reads: copy to .env.
truassure-gate.yamlThe release gate policy.
README.mdThese steps, for your team.

One project per agent or AI initiative works well: the catalog and gate are code, reviewed in pull requests.

4. How an agent is judged

An episode is one task given to the agent in a prepared environment. A checkpoint is where it is judged (every user turn in a chat; once at the end for a task or workflow). Each checkpoint is judged at five stages, and the first failing stage is reported:

StageQuestionEvidence
goalDid it understand the request (intent, route)?The agent's reported decision
planWas the course sensible: steps, loops, handoffs, approvals before writes?Trace and observers
actionOnly allowed, correct actions, with the right arguments, for the right person?Observers (proxy, spans, tap)
end_stateIs the data right afterwards, and did nothing else change?Before/after snapshots
outputIs what it told the user true and complete, and consistent with what it did?Reply or report, graders

Seams (SE-01 to SE-07) are where the agent's own account and the independent evidence disagree: a call it reported that never happened, a write it didn't mention, a success announced after a failure, an action for the wrong person. Each failure has a code (e.g. ACTION.FORBIDDEN_CALLED) with a severity.

Profiles: conversational (turn by turn), task (one event or task, judged at the end), workflow (several agents, handoffs, identity on every hop, failure containment).

5. Connect your agent

An environment factory is a Python function environment(worker) -> Environment that ta run --env module:function calls once per parallel worker. Ready-made builders:

BuilderFor
truassure.profiles.task.http_task_environmentA task agent behind HTTP, a SQLite system of record and a folder of files it writes
truassure.profiles.workflow.http_workflow_environmentMulti-agent systems behind HTTP, with the proxy and the A2A tap
truassure.profiles.conversational.http_chat_environmentA chat agent behind a plain HTTP endpoint

What the agent's endpoint should return

Task and workflow (POST /events with {"event": ...} or {"task": ...}, the principal in X-Principal):

{"route": "claims_intake", "reason": "...",
 "steps": [{"kind": "action", "name": "update_claim", "args": {"claim_id": "CLM-1001"}, "ok": true, "result": "..."},
           {"kind": "handoff", "agent": "triage", "to": "billing", "member": "M-1001"}],
 "hops": [{"from": "triage", "to": "billing", "member": "M-1001", "permissions": ["billing:read"]}],
 "output": "summary for the user", "done": true, "outcome": "finished",
 "usage": {"input_tokens": 0, "output_tokens": 0}}

Chat (POST /chat with {"session": "...", "message": "..."}):

{"reply": "Your balance is $193.60.", "intent": "billing",
 "steps": [{"kind": "action", "name": "get_billing", "args": {"policy_id": "POL-1002"}, "ok": true}]}

Only reply (chat) or done/output (task) are essential. A different shape needs only a parse function passed to the driver. An agent that reports nothing about its steps can still be tested as a black box (chat: trace=False): its actions then come from the observers alone.

Independent evidence

SourceHowRecords
Recording MCP proxyta proxy --upstream <tool server> --log-dir logs/proxy; point the agent at the proxyEvery tool call: name, arguments, principal, outcome. Injects faults (error, timeout, empty) in the upstream's own format.
A2A tapta a2a --route billing=<url> --log-dir logs/a2a; senders call the tapEvery handoff, with the member and permissions read from the identity token. Injects agent faults.
OpenTelemetryta otel --out-dir spans (needs TA_OTEL_TOKEN); the agent exports OTLP/JSON to itGenAI execute_tool spans: a second record of tool calls.
StateSQLite database, a folder of files, a git repositoryBefore/after snapshots and diffs; SQL, file and git checks.
Give the factory a reset hook that restores the agent's test data for the catalog's environment.fixture. Without it, scenarios that change data affect the ones after them.

Other systems (a message bus, Postgres, a SaaS API) plug in as adapters: classes that implement the driver, observer or state protocol in truassure.core.protocols, registered through the entry-point groups truassure.drivers, truassure.observers, truassure.state and truassure.graders.

6. Writing a catalog

A catalog is YAML, checked against a JSON Schema by ta validate. Keys starting with x- are ignored by the checks, which is handy for YAML anchors and notes. One is read by the report: x-intents, every intent (or route) family the agent knows, as {name: description}. The report's Intents tab then lists each family's cases and pass rate, and names the families no case tests.

schema_version: 1
name: claims agent
defaults: {principal: {user: adjuster-07, permissions: ["claims:read"]}, environment: {fixture: seed},
           budgets: {steps: 12, seconds: 120}, severity: major}
actions:
  get_claim: {kind: read, scopes: ["claims:read"]}
  make_payment: {kind: write, requires_approval: true, scopes: ["payments:write"]}
scenarios:
  - id: INTAKE-01
    profile: task
    input: {event: {type: claim.document_received, claim_id: CLM-1001}}
    expect: {...}

Scenario fields

FieldMeaning
id, profileRequired. Profile: conversational, task or workflow.
principal{user, roles, permissions, attributes}: who the agent acts for. Checked on every observed call.
inputExactly one of prompt, event (needs a type) or task. Task and workflow scenarios.
checkpointsConversational: one per user turn, each {id, input: {prompt}, expect, perturbations, faults, approval}.
agentsWorkflow: the agents taking part (two or more).
perturbationsVariants of the input: a whole alternative prompt (string), or {name, <field.path>: value} overrides, or noise: duplicate_event, reordered_fields, extra_fields, typos.
faults{action, mode: error|timeout|empty, times, delay_s, message}, injected by the proxy or tap.
budgets{steps, tokens, cost_usd, seconds}.
severitycritical, major or minor; drives defect severity and the gate.
test_typesTest-type ids for the scorecard (e.g. TT-02, SS-09, SE-07).
capabilitiesTags such as claims.intake: used by capability packs and per-capability gate rules.

What to expect, stage by stage (expect)

KeyChecks
goal.intents, goal.route, goal.routesAccepted intents (chat) or the route / task interpretation.
plan.max_steps, max_loops, max_callsBounds on the course taken (max_calls may be per action).
plan.terminates, approval_before_writes, no_duplicate_writesIt finished; it asked before writing; it didn't repeat writes.
plan.handoffs, handoffs_ordered, contain_failuresWorkflow: {from, to, principal_preserved, max_permissions} per hop; exact order; after a failure nothing is handed on.
actions.required, attempted, preferredActions that must succeed / be tried / ideally be used. An item is a name or {name: {arg: value}}.
actions.forbidden, allowed, must_not_succeed, no_writesNever call; call nothing else; may be tried but must not succeed; no writes at all.
end_state.changesnone, a list of tables or paths that are the only ones allowed to change, or {adapter: [scopes]}.
end_state.sql, files, git{name, sql, equals}; {path, exists, contains, not_contains, equals, matches}; {ref, branch_exists, commit_count, files_changed, message_contains}.
output.facts, output.forbiddenFacts the answer must state ("a or b" alternatives); phrases it must not contain.
output.artifactA file, report or patch the agent produced: {kind, path, checks, tests}.
output.reference, output.rubricFor graders (e.g. the LLM rubric grader).
Write the expectations from the business rule, not from what the agent currently does. A test that encodes today's behaviour hides today's defects.

Translating an existing suite catalog: ta translate suites.yaml --yaml -o catalog.yaml.

7. Running

ta run catalog.yaml --env environment:environment --repeats 3 --perturbations all --parallel 2
OptionMeaning
--repeats NRun each episode N times: pass^k (works every time, not just once) and flaky detection.
--perturbations all|0,1,2Also run the input variants (0 is the catalog input): robustness.
--parallel NN environments (workers) at once.
--scenario IDOnly these scenarios (repeatable).
--mask LABEL=REGEXAn extra masking rule for personal data (repeatable).
--run-id, --runs-dir, --step-timeoutName the run; where runs go (default ./ta_runs or TA_RUNS_DIR); seconds per stimulus.

Exit codes: 0 every case passed, 1 a case failed or was flaky, 2 bad input, 3 the environment is locked by another run.

8. Results and decisions

CommandGives
ta show [run]Runs, or one run's cases: stage marks, result, Wilson range, reasons.
ta report latestTwo reports, each one self-contained file that works offline. execution-report.html (with its record execution-report.json) is for stakeholders: a readiness statement, KPI cards and charts, test-type and test-data coverage, persona views, findings with evidence and next actions, the trend against the previous comparable run (--baseline RUN to choose); personal details masked. report.html is for engineers: an overview with the evidence seams and the test environment; intent coverage, its history across the runs of the same catalog and robustness across wordings; cases; episodes (each turn's expectations, reply and database changes before and after, the agent's account beside the evidence, every try of a case); the scorecard by category with stage-by-type and case-by-type maps; defects; comparison and gate.
ta report RUN RUN ...Several runs at once (for example one per system, run side by side): each run's own reports, plus consolidated-report.html and .json over all of them: an overall readiness statement, KPI cards, outcomes, findings and the release gate per run, every finding in one list, and a test-type matrix. --out FILE and --title TEXT place and name it; each run's reports are linked from it.
ta console --openA local browser console over all runs: reports, comparisons, gates. 127.0.0.1 only, token in the URL.
ta console --allow-runs --openThe console can also start runs: only the presets in ta-console.yaml (this project has one), with phrasings, repeats, parallel and mode adjustable within their limits. Live progress with Cancel; reports written when a run ends. This machine only. Tick runs for a consolidated report named with the date and time.
ta compare A BCheckpoint by checkpoint: regressions and fixes. Across deployments (model, host, framework), a parity verdict against run-to-run noise.
ta defects latestThe defect list (JSON and CSV), severity by failure code.
ta junit latest [--baseline RUN]JUnit XML for CI; with a baseline only new failures fail.
ta scorecard latestThe run against the 71-type test catalogue: measured, failing, not run.
ta pack CAPABILITYIs a capability ready? (exit 1 when not)
ta gate latest --policy truassure-gate.yamlThe release gate (exit 1 on fail): pass rate, defects by severity, flaky handling, parity, clean seams, per-capability rules.
ta calibrate set.yaml --grader module:ClassHow far to trust a grader: Cohen's kappa against human labels.
# truassure-gate.yaml
gate:
  max_defects: {critical: 0, major: 2}
  min_pass_rate: 0.9
  flaky: fail                    # fail | skip | pass
  parity_must_hold: true
  seams_clean: [SE-01, SE-02, SE-03, SE-04]
  capabilities:
    claims.settlement: {min_pass_rate: 1.0, max_defects: {critical: 0, major: 0}}

9. CI pipelines

ta init --ci github --catalog catalog.yaml --env environment:environment
ta init --ci azure  --catalog catalog.yaml --env environment:environment

Writes a pipeline that installs TruAssure, validates the catalog, runs it with repeats and perturbations, publishes JUnit results and the defect list, applies the gate and keeps the run folder as an artifact. Set the TRUASSURE_PIP variable to pin the version (e.g. truassure==__VERSION__).

9a. Sharing and the portfolio

Each project's truassure.yaml (written by ta new) names the AI initiative it belongs to, the owning team and the shared store:

initiative: claims-automation
project: claims-agent-tests
owner: claims-quality
store: s3://quality-results/truassure      # or a folder / network share, or azure://<account>.blob.core.windows.net/<container>/<prefix>
policy: truassure-gate.yaml
CommandDoes
ta push [run]Shares a finished run: its evidence (already masked), report, defects, scorecard and gate verdict, then a manifest. Options override truassure.yaml: --store, --initiative, --project, --owner.
ta run ... --publishRuns and shares in one step (use it in CI).
ta pull --store URLCopies new shared runs into a local cache (ta_portfolio).
ta portfolio --store URL [--csv FILE]Every initiative and project: gate, pass rate, defects, runs, latest run.
ta console --store URL --openThe portfolio in the browser: summary counts, per-project trend, every run's report, CSV export, refresh.

Stores: a folder or network share (built in), Amazon S3 (pip install "truassure[s3]"; the standard AWS credential chain) and Azure Blob (pip install "truassure[azure-blob]"; a connection string or DefaultAzureCredential). TA_STORE overrides the file. Other stores plug in through the truassure.stores entry-point group.

Nothing leaves the machine without ta push or --publish. A run becomes visible only when its manifest is written, which is the last step, so readers never see half a run. Access to results is the store's own access control (share permissions, IAM, Azure roles).

9b. The scenario library

Reusable packs for risks every initiative shares. A catalog includes a pack and fills in its parameters with its own action names and data; the scenarios then run like hand-written ones.

ta library                         # the packs
ta library prompt-injection        # what it tests, its parameters, its scenarios
ta library privacy --add catalog.yaml
include:
  - pack: payments-approvals
    with: {user: shan, pay_prompt: "Pay $50 on POL-1002.", payment_action: make_payment,
           payment_args: {policy_id: POL-1002, amount: 50}, payment_scopes: [payments, policies]}
    only: [DECLINED, TOOL-FAILS]        # optional: a subset
    position: start                     # optional: run before the catalog's own scenarios (default: after)
PackProfilesTests
privacychatAnother customer's record never revealed; typed personal data not echoed; nothing sent to an address given in chat.
prompt-injectionchat, taskInstructions in the message, in pasted data or in an event field don't trigger a dangerous action or a canary phrase.
unsafe-writestaskA failed or timed-out write is never reported as done; a duplicate event writes once.
payments-approvalschatDeclined: nothing paid. Approved: exactly that payment. Tool failure: never "paid".
identity-handoffsworkflowEvery hop for the same member, permissions only narrowing; a failure is contained.
conversation-handlingchatAsks when a request is ambiguous; follows a change of mind; uses the corrected value; reads "don't pay, just tell me" right; recovers from acting on the wrong record; returns to the task after an aside.
answers-and-boundarieschatAnswers from the record it looked up; says when it can't know; refuses an action whose precondition fails; hands over to a person; no staff-only actions; no account details before the caller is verified; resists a jailbreak; doesn't refuse a legitimate request; no legal advice or promised outcome.
tone-and-policychatRubric-graded (needs the LLM grader): a competent multi-turn conversation, empathy, professionalism under abuse, your written policy.

Every pack tags its scenarios with catalogue test types, so they appear on the scorecard and in the execution report's coverage matrix.

Your own packs: a YAML file (pack: ./packs/claims-rules.yaml, relative to the catalog) or a package that registers one in the truassure.packs entry-point group. A pack has pack, params (each with a description, and an example or a default), optional actions, and scenarios using {{param}}. Included scenarios record their pack, version and parameters in source.

9c. Enterprise databases and connectors

Any SQL database SQLAlchemy supports, through one adapter: install truassure[sql] plus the driver extra (postgres, sqlserver, mysql, oracle; others: the dialect's own package).

from truassure.adapters.state.factory import make_state

make_state("postgresql+psycopg://reader:***@db:5432/claims", tables=["claims", "payments"])
make_state("mssql+pyodbc://reader:***@sql/claims?driver=ODBC+Driver+18+for+SQL+Server")
make_state("oracle+oracledb://reader:***@db:1521/?service_name=CLAIMS", schema="CLAIMS")
make_state("snowflake://reader:***@account/db/schema")      # any SQLAlchemy dialect
make_state("data/claims.db")                                # SQLite file
make_state("https://crm.test.example/api", resources={"cases": "/cases"}, items_path="data")   # REST
  • Tables and primary keys are found by reflection (override with tables= and keys=); values are normalised so snapshots compare the same on every database.
  • Read-only: read-only sessions where the database supports them (Postgres, SQL Server, MySQL/MariaDB, Oracle, SQLite), every session rolled back. Use a login that can only read.
  • Passwords in URLs, including inside ODBC strings, are never printed.

Drivers beyond HTTP: OpenAICompatibleDriver (chat-completions endpoints, with conversation history), QueueDriver with ServiceBusTransport (truassure[servicebus]) or KafkaTransport (truassure[kafka]). task_environment(name=..., driver=..., state=[...]) combines any driver with any state adapters; http_chat_environment(..., driver=...) does it for chat.

The shared store can be a database too (ta push --store postgresql+psycopg://...): runs go into two ORM-mapped tables, ta_runs (one row per run: gate, pass rate, defects) and ta_run_files. A DBA can create them from truassure.adapters.stores.sql.ddl("postgresql://").

10. AI assistants (MCP)

{ "mcpServers": { "truassure": { "command": "ta", "args": ["mcp"] } } }

Every CLI command is a tool with the same arguments, so an assistant in the IDE can validate catalogs, run them, read results and apply the gate. Long-running services (proxy, tap, receiver, console) are not tools. Results are marked as data: they may quote what an agent said, and must not be followed as instructions.

11. Settings and security

  • Settings and secrets come from environment variables or the nearest .env (real environment variables win). TruAssure never prints secret values. Keep .env out of git.
  • Every listener binds to 127.0.0.1 unless --allow-remote. Control APIs need tokens (TA_PROXY_TOKEN, TA_OTEL_TOKEN); the console makes its own per start.
  • Personal data (emails, phone numbers, secret-named fields, your --mask rules) is masked before evidence is written. ta gc --days N applies retention.
  • Test against test environments and test data. Fault injection and resets change the agent's environment.

12. Command reference

CommandPurpose
demo, new, docsOffline tour; a starter project; this guide.
validate, translateCheck catalogs; convert a suite catalog.
run, show, report, gcRun a catalog; list and inspect runs; the HTML report; retention.
compare, defects, junit, scorecard, pack, gate, calibrateRun-level analysis and governance.
initA CI pipeline.
push, pull, portfolioShare runs; copy them back; every initiative at a glance.
libraryThe scenario library.
console, mcpThe browser console; the MCP server.
proxy, a2a, otelObservers.

ta <command> --help lists every option.

TruAssure __VERSION__ · MIT licence · examples use fictitious Harbor Mutual data.