The method · 40 checks · 8 principles
What I actually check
The Trust Audit says what a system should do. This is the method underneath it: the questions I put to a real codebase, what clears each one, and the shape that fails.
A rubric is easy to agree with and impossible to act on. So here is the whole method, in the open. Eight principles, each one broken into the concrete questions I ask of your repository, what a passing answer looks like, and the anti-pattern I am looking for. Nothing here is generated. It is the list I work from.
Each check names where to look. Most are answerable by reading a handful of files, which is the point: a check you cannot verify is an opinion. Run them yourself, or point your own agent at them over MCP and have it report back against your codebase.
01 · 4 checks
Own your context
Own your context, rent the model.
Can a client take everything they own in one call?
High- Clears it
- One export path that enumerates every tenant-scoped table, with a test that derives the table list from the schema rather than restating it.
- Fails it
- An export covering the tables someone remembered, which silently stops covering the ones added later.
- Where to look
- the export routeany export testcolumns named tenant_id / workspace_id / account_id
Do the vectors live in a database you control?
High- Clears it
- Embeddings stored in your own store, re-derivable from source documents you also hold.
- Fails it
- The only copy of the index lives inside a vendor, so leaving means re-ingesting everything from scratch, if you still have it.
- Where to look
- the vector store clientthe ingestion pathwhether the source text is retained after embedding
Is the model behind one seam, or wired into call sites?
Medium- Clears it
- A single provider module and model ids as typed constants, so swapping a model is one edit.
- Fails it
- A vendor SDK imported directly wherever generation happens, with model ids as loose strings.
- Where to look
- imports of provider SDKs across the treewhere model id strings appear
Does delete reach the things that are not rows?
Critical- Clears it
- One teardown primitive that destroys external resources before the database cascade, reused by every delete path.
- Fails it
- A foreign-key cascade treated as the whole deletion contract, while provisioned projects, repositories, subscriptions and VMs keep running.
- Where to look
- delete servicesany column holding an external id or refwhat happens to those on delete
02 · 5 checks
Grounded or it doesn't answer
If it can't cite it, it shouldn't say it.
Does the answering call see only what was retrieved?
Critical- Clears it
- Retrieval runs first and its output is the only source material in the prompt.
- Fails it
- A system prompt full of background the model can blend with, so you cannot tell a retrieved fact from a remembered one.
- Where to look
- the prompt assembly for the answer pathwhat else is concatenated alongside the retrieved chunks
Are citations built from retrieved ids, or written by the model?
Critical- Clears it
- The renderer resolves citations against the chunks actually retrieved, and a citation that resolves to nothing is caught.
- Fails it
- The model emits citation markers as free text and the UI renders them, so a fabricated source looks identical to a real one.
- Where to look
- how citations are parsed and renderedany check that a cited id was in the retrieved set
Is refusal enforced in code, or only requested in the prompt?
Critical- Clears it
- A predicate decides refusal from the retrieval result, independent of what the model chose to say.
- Fails it
- The instruction says to refuse when unsure, and nothing verifies that it did.
- Where to look
- a grounding or refusal predicatewhether the answer path can return an ungrounded answer at all
Does the interface claim a refusal only when one happened?
High- Clears it
- What the user is told is derived from the same signal that is recorded, and the showable claim is never weaker than the recorded one.
- Fails it
- A reassuring card saying the system stopped rather than guess, rendered over an answer it actually generated.
- Where to look
- the refusal or trust componentthe field it readswhether that field is the recorded one
Does the eval set contain questions that must NOT be answered?
High- Clears it
- A golden set with explicit refusal cases, run when retrieval, chunking, the model or the grounding prompt changes.
- Fails it
- An eval that only measures recall, which a model that answers everything confidently passes.
- Where to look
- the eval setthe ratio of answerable to unanswerable cases
03 · 5 checks
Untrusted content is data, not instructions
Retrieved and user content is data the model reads, never orders it follows.
Is retrieved and user content marked as untrusted where it enters the prompt?
High- Clears it
- Every untrusted block is delimited and labelled as reference material that is never an instruction.
- Fails it
- Document text pasted into the prompt indistinguishable from your own instructions.
- Where to look
- how retrieved chunks are formatted into the promptany untrusted marker
Can retrieved content reach a tool call?
Critical- Clears it
- The call that answers from untrusted context has no tools bound to it, so there is nothing for injected text to trigger.
- Fails it
- One agent loop with both retrieval output and write tools available, where a crafted document can call them.
- Where to look
- the tool list on the answer callwhether retrieval and tool use share a loop
Does the component that reads hostile input have tools at all?
Critical- Clears it
- Anything parsing inbound email, uploads or third-party content returns structured data and can call nothing.
- Fails it
- An extractor with tool access, so the content it parses decides what it does.
- Where to look
- inbound email, upload and webhook processingthe tool set on each
Are tool arguments validated server-side before they execute?
High- Clears it
- Every tool argument is schema-checked at the boundary, with bounds shared with the API schema rather than restated.
- Fails it
- Arguments trusted because the model produced them, so a prompted id reaches a query.
- Where to look
- tool definitionswhere their inputs are parsedwhether bounds are duplicated
If the agent runs commands, is the set allowed or merely filtered?
Critical- Clears it
- An explicit allowlist of permitted commands, denying everything else by default.
- Fails it
- A denylist of dangerous patterns, which is a guess about every command you did not think of.
- Where to look
- any command execution pathhow the permitted set is expressed
04 · 5 checks
The agent proposes, a human decides
Autonomy stops at anything you can't undo.
Does agent-authored code reach the default branch directly?
Critical- Clears it
- Changes land on a branch as a pull request a human reviews. The default branch is never written by the agent.
- Fails it
- Commits pushed straight to main, where the review step is whatever the reviewer notices afterwards.
- Where to look
- the push pathwhich ref it targetswhether a PR is opened
Is approval required before anything you cannot undo?
Critical- Clears it
- Irreversible actions block on explicit human approval. Reversible ones do not, so the gate keeps its meaning.
- Fails it
- Either no gate at all, or a gate on everything, which trains people to click through it.
- Where to look
- the approval flowwhich actions require itwhich do not
Is the diff that was approved provably the diff that lands?
High- Clears it
- The base is compared between approval and push, and an ambiguous or truncated comparison blocks rather than proceeds.
- Fails it
- Approval on a diff computed against a base that has since moved, so the merge contains work nobody reviewed.
- Where to look
- what happens between approval and pushany base comparisonwhat it does when it cannot tell
Is the record of who did what append-only and attributed?
High- Clears it
- An audit trail nothing updates or deletes, recording actor, action and time, including the operator's own access.
- Fails it
- A log with an update path, or one that records the client's actions but not the vendor's.
- Where to look
- the audit tableits grantswhether operator access is recorded too
Is agent-written code scanned for secrets before it is pushed?
High- Clears it
- A scan runs before the push, and a hit blocks it rather than annotating it.
- Fails it
- Scanning after the fact, or not at all, so a generated example key becomes a real commit.
- Where to look
- any secret scanwhether it gates the push or reports on it
05 · 5 checks
Two layers or it's one bug from a breach
Auth in the app is not isolation. Enforce it at the database too.
Does every content row carry a tenant id, and does every query filter on it?
Critical- Clears it
- One tenant column on every content table, and services that filter by the RESOLVED tenant on every read and write.
- Fails it
- A table that joins its way to a tenant, so one missing join is a cross-tenant read.
- Where to look
- the schema for tenant columnsservice queries for the filterany table lacking one
Is there a second layer under the application checks?
Critical- Clears it
- Row-level security on tenant tables with policies, and closed grants on anything holding tokens, money or infrastructure state.
- Fails it
- Application checks only, so one missing filter is a breach rather than a bug.
- Where to look
- which tables have RLS enabledwhich have policieswhich grants exist for the anonymous and authenticated roles
Where does the tenant id come from?
Critical- Clears it
- Resolved from the authenticated user on every request. A tenant id in a request body is never trusted.
- Fails it
- An id read from the body or a query parameter, which makes every endpoint a tenant switcher.
- Where to look
- route handlerswhat the tenant id is derived fromany body field naming a tenant
Does a foreign id return 404 or 403?
Medium- Clears it
- 404, so the response does not confirm that the resource exists.
- Fails it
- 403 on a resource belonging to someone else, which is an existence oracle you can enumerate.
- Where to look
- the not-found path in the tenant gatewhat status a foreign id produces
Can a service-role or admin key reach the browser?
Critical- Clears it
- Privileged clients are constructed only in server modules, and the build would fail if one were imported into client code.
- Fails it
- A single shared client, or a key in a variable the bundler exposes.
- Where to look
- where the privileged client is builtwhether those modules are server-onlypublic environment variable names
06 · 5 checks
One boundary: API-first, validated, typed
One server boundary that validates everything. No business logic in the client.
Is every input schema-validated at one server boundary?
High- Clears it
- One validation helper and schemas that live with the domain, imported by every surface that accepts that input.
- Fails it
- Hand-rolled checks per handler, and bounds restated in each tool and form, which drift apart.
- Where to look
- the validation helperwhere schemas are definedany handler parsing input itself
Does authorization happen before any data work?
Critical- Clears it
- The tenant gate is the first statement in the handler, returning early on failure.
- Fails it
- Validation, lookups or writes before the gate, so an unauthorized request still moves the database.
- Where to look
- the first lines of each handleranything before the gate
Can the browser write to the database directly?
Critical- Clears it
- All mutations go through the server boundary. The client holds no write credential.
- Fails it
- A public key with insert rights, where correctness depends on rules written somewhere else.
- Where to look
- client-side database callsthe grants on the public role
Do handlers state their return type?
Medium- Clears it
- An explicit return annotation on every handler, so a drift in what it returns is a compile error.
- Fails it
- Inferred return types, which the compiler accepts happily while the response shape changes underneath.
- Where to look
- handler signaturesany without an annotation
Do route bodies do the work?
Medium- Clears it
- Handlers gate, validate and shape the response. Services own the work and the tenant scoping.
- Fails it
- Database work inside handlers, or services returning HTTP responses, which puts the security boundary in two places.
- Where to look
- handler lengthdatabase calls inside handlersHTTP types imported into services
07 · 6 checks
Bounded by design
An agent with no limits is a bill and an outage waiting to happen.
What stops an agent loop that does not converge?
High- Clears it
- Explicit step, retry and cost ceilings, enforced by the runtime rather than by the prompt.
- Fails it
- A loop that runs until the model decides to stop, which is a bill with no upper bound.
- Where to look
- the agent loopmax step and retry settingsany per-run cost ceiling
Does every outbound call carry a timeout?
Medium- Clears it
- A timeout on every external call, chosen deliberately rather than inherited from a default.
- Fails it
- A hung provider holding a request open until the platform kills it, taking the work with it.
- Where to look
- fetch and SDK calls to third partiestimeout or abort signals
Is a retried unit of work charged and applied once?
Critical- Clears it
- A deterministic key derived from the CONTENT of the work, so a resumed or re-delivered job collapses onto the same record.
- Fails it
- A key derived from position, a cursor or a parent id, which produces a fresh key on resume and bills the same work twice.
- Where to look
- idempotency keyswhat each is derived fromwhether a resumed batch would produce the same one
What happens at zero balance, and what happens when the balance cannot be read?
Critical- Clears it
- A hard, explicit block at zero, and a read failure that fails closed rather than assuming credit.
- Fails it
- Silent degradation, or a database error read as no balance found and treated as fine.
- Where to look
- the spend gateevery balance readwhat each does with an error result
What recovers work that dies between two states?
High- Clears it
- Every non-terminal status has a sweeper that does not depend on the in-request error handler, because a hard kill never runs it.
- Fails it
- A status moved to terminal only inside a catch block, so an out-of-memory kill leaves it stuck forever.
- Where to look
- every non-terminal status valuewhat sets it to terminalwhether anything sweeps it
Are unbounded reads capped, and does the surface admit when it truncated?
Medium- Clears it
- A cap on any query that grows with the data, and a surface that states it is showing a subset.
- Fails it
- A page that loads everything, or worse, one that caps silently and renders the capped number as the total.
- Where to look
- queries with no limitoperator pageswhether a truncated count is labelled
08 · 5 checks
You can tell when it's wrong
If you can't trace a bad answer back to its cause, you can't trust the good ones.
Is there a catch block that discards its error?
High- Clears it
- Every failure path logs with enough context to identify the tenant and the operation.
- Fails it
- An empty catch, or one that swallows and returns a default, which is a failure nobody will ever see.
- Where to look
- catch blockswhich ones logwhich return a fallback quietly
Does the data layer throw on failure, or resolve with an error you have to read?
Critical- Clears it
- If it resolves, every write reads the error and handles it, and webhook paths rethrow so the provider re-delivers.
- Fails it
- An awaited write whose result is discarded, which is a fully silent data loss on a constraint or a timeout.
- Where to look
- awaited writes with no destructured errorwebhook handlerswhether a failure there returns success
What does a failed read on a billing path return?
Critical- Clears it
- It throws. A transient database error is never allowed to read as zero, empty or not found on a money path.
- Fails it
- A wallet or subscription read that swallows its error, so a blip becomes a wrong charge or a free tier granted forever.
- Where to look
- reads on wallet, subscription, ledger and customer recordswhat each does with an error
Where do the loud signals actually go?
High- Clears it
- Logs reach a drain someone can query after the fact, so a mitigation whose enforcement is that it logs loudly is real.
- Fails it
- Structured error events written to standard output with no drain, which is detection nobody receives.
- Where to look
- the logger implementationwhether a drain or sink is configured
Can you get from a wrong answer back to what produced it?
High- Clears it
- A record per generation carrying what was retrieved, which model ran, what it cost, and whether it was grounded.
- Fails it
- A response with no trace, so the only debugging tool is asking again and hoping it happens twice.
- Where to look
- the per-generation recordwhether retrieval context is recoverable from it