Nine questions to ask anyone running AI on your business.
Here are our answers. Every one points to something built into the system — a spending limit, an approval step, an undo — not a promise. Where we’re not there yet, we say so.
§1
What does it cost to run?
Control: a per-invocation cost ledger.
Every agent call writes one row before it’s forgotten. The row carries the agent slug, the human-readable agent name, the model that actually ran, input and output tokens, cost in USD to four decimal places, start and finish timestamps to the millisecond, and any error message.
The model is recorded from the response rather than from the agent’s configuration. That distinction matters more than it sounds: when you change which model an agent uses, every historical row still attributes to the model that actually did the work. Cost history doesn’t silently rewrite itself.
Cost is attributed through one of 9 mutually-exclusive parent references — a chat session, a service order, a prospect session, a user, a quality report, a legacy archive record, a shipper prospect, a qualification batch, a cold prospect. Exactly one is set per row, enforced at the write layer. So “what did AI cost us on this account, on this workflow, last month” is a query. Not a reconstruction, not an estimate, not a spreadsheet someone maintains.
§2
What stops it running away?
Control: hard cost ceilings, in code, per agent.
Every agent definition carries three numbers: a hard per-invocation dollar ceiling, a soft warning threshold below it, and a timeout. They live in the agent’s configuration file, in version control, next to the prompt.
The ceiling is checked the moment the call returns. Exceed it and the result is a hard stop — an error, a logged row, and nothing downstream runs. It does not fall back to a cheaper model and continue. It does not truncate and pretend. The most expensive failure mode in agentic systems is the one that degrades quietly, so we made that path absent rather than discouraged.
To be precise about what this does and doesn’t do: the check is post-call, so an overage is incurred once. What it prevents is the cascade — the retry, the follow-up call, the send. A ceiling that genuinely pre-empted the spend would require the provider to enforce it, and no provider does.
Example, from the agent that produces our weekly prospect insight: ceiling $0.0500, soft warn $0.0400, timeout 60 seconds. Those are the real numbers, in the real file. Every agent we run has a complete set — there is no agent operating without one.
Ceilings exist at two levels.
Individual calls have ceilings. So do whole runs: our brand-voice validation workflow, which fans out across 7 sub-agents, carries its own whole-run ceiling of $1.85 — independent of what each call inside it is allowed to spend.
That matters because per-call limits alone don’t see the fleet: ten cheap calls can cost more than one expensive one. To be precise here too: the whole-run check is post-hoc — the total is compared to the ceiling after the fan-out completes, and an overage is logged loudly rather than hard-stopping a run that has already finished. The per-call caps are the hard-stop layer; the run-level ceiling is what makes an aggregate overage impossible to miss.
Related control: output contracts enforced outside the prompt.
Where a response shape matters, the constraint lives in code, not in the instructions. Word-count bounds, exact list cardinality, and an allow-list of permitted recommendation values are all validated after the fact. A response that violates them returns a parse failure rather than being accepted because it looked plausible.
The strongest version of this is in our campaign reviewer. The prompt tells the model it must never invent a finding, a number, a severity or a lever. The code then independently drops any finding reference the model invented and re-appends any real finding it omitted. The prompt states the rule; something other than the prompt enforces it. A guardrail a model can talk its way past is not a guardrail.
§3
What can it touch?
Control: blast-radius classification on every scheduled workflow.
All 32 recurring workflows are registered in a single file with metadata, and every one carries a blast-radius class: none, low, or high.
None means it only reads. Low means it writes inside our own system — a draft that sits in a held state, for instance, where the classification is low precisely because nothing is sent. High means it can reach the outside world, spend money, or touch real calendars and mailboxes. A high-blast-radius workflow cannot be triggered manually without typing its name to confirm.
This is a governance taxonomy enforced in the interface, not described in a wiki that nobody reads after onboarding.
Related control: nothing double-fires.
Every scheduled workflow acquires a cross-process lock before it runs. The lock is shared by the scheduler and by the manual “run now” control, so a human retry during a scheduled run cannot double-send, double-charge or double-post. The loser skips and writes an audit row saying the lock was held.
When the lock service itself is unreachable, the workflow runs unlocked and records that it did. That is a deliberate choice — for these workflows, not running at all is the worse failure — and it’s recorded rather than hidden.
Related control: dormant by default.
Every agent and every specialist fleet is dormant until its identifier is provisioned. Code ships continuously; the capability activates as a separate, reversible decision with a name attached to it.
This is why a fleet with 14 specialists in it can sit in production costing exactly nothing until someone decides otherwise. With the identifiers unset, the expensive path is byte-identical to the cheap one.
The same applies to scheduled work. Every one of our 32 recurring workflows carries an explicit activation policy, and every workflow that can reach a customer is off until someone turns it on. That’s enforced in one place — not per-handler, where it would drift — and checked by tests that fail the build if a customer-reachable workflow is ever given a weaker policy. When a disabled workflow skips its tick, it writes a record saying so. Silent non-execution is the failure mode we were trying to eliminate, so a dormant workflow is visible rather than invisible.
One workflow cannot be switched off by configuration: the job that redacts personal data after 30 days. A config mistake that silently stopped that one would be a data-protection incident, so it isn’t given the option.
§4
Who approved it?
Control: nothing that reaches outside, writes.
14 tools are exposed to our agents. Every tool that would change something on a client’s own property — their tag manager, their site, their accounting system — is named propose_ something and returns a link for a person to confirm. Not a soft convention: the direct write path isn’t restricted, it isn’t implemented. An agent can draft an invoice. It cannot post one.
One tool writes to our own database rather than proposing: it records a content idea as an advisory draft for review. It publishes nothing and it reaches nothing of yours.
Control: drafts that stop.
Outreach drafts persist in a held state and wait for a person. Content proposals, testimonials and case studies are drafted by an agent, reviewed by a person, published by a person. Our internal campaign and SEO reviews are team-facing only and are never automatically sent to the client they’re about — the workflow that drafts them is classified low-risk precisely because nothing is sent.
Control: an approval lifecycle the database enforces.
Consequential actions are modelled as a queue with an explicit lifecycle — pending, then approved, rejected or cancelled, then executed — carrying the proposing actor, the deciding actor and a decision reason.
Those aren’t conventions. The database rejects self-approval outright, rejects a decided action that carries no reason, and rejects an approval that records no approver. Three constraints, each with a test proving the invalid case fails. Not a code path that could be bypassed by a future caller who didn’t read the docs — a rule the storage layer refuses to break.
Not yet operational: no production traffic flows through this queue today. The schema shipped ahead of its traffic, deliberately — the controls above, propose-only tools and drafts that stop, are what carry approvals in production right now.
§5
What if it’s wrong?
Control: preview, confirm, roll back. Never delete.
7 external providers are wired for writes: Google Tag Manager, WordPress, Cloudways, Shopify, Netlify, SFTP, and GA4 Admin. Each one goes through the same sequence — the change is previewed, a person confirms it, the prior version is retained, and one click restores it.
Nothing is deleted. Each provider authenticates with its own dedicated credential — a least-privilege OAuth application where the platform supports one, a client-supplied scoped credential where it doesn’t — and each has its own independent kill switch, so revoking one capability doesn’t require revoking the rest.
Control: every long-running process has a dead letter queue.
21 background orchestrators each have a paired dead-letter handler and an administrative surface. Delivery events are written to an append-only ledger. When something fails, there is a queryable record of what failed and a place it went — not a silent gap in a log.
§6
Can it see another customer’s data?
Control: build-time enforcement, plus a test suite that must return nothing.
113 tables are annotated with a tenant scope — one of 7 kinds in our schema registry, declared directly above the table definition. Those annotations generate a registry file that is committed to the repository, and CI fails if the file and the schema disagree.
Two custom lint rules then read that registry. The first fails the build on any select, update or delete against a tenant-scoped table that lacks the correct filter for its scope kind. The second blocks the escape hatch: raw SQL against a scoped table, in any of its forms. Where a table’s scope is inherited, the rule walks the join to the declared parent. Where a table is polymorphic, it requires both the target type and the target id — because ids collide across target spaces and filtering on one of the two is a leak that looks like a filter.
The rules are deliberately biased: when uncertain, they fail. There are no lint suppressions for tenant scope anywhere in production code.
On top of that, a cross-tenant integration suite runs against a synthetic pair of tenants — one a pure prospect, one a converted client — with 31 test cases across 11 suites, ending in an aggregate probe that makes 20 distinct cross-tenant attempts and requires every one to return zero rows.
One detail for anyone who has written a multi-tenant test suite themselves: the probes use identifiers guaranteed not to exist rather than the other tenant’s identifiers, because every table auto-increments from one — tenant A’s row 1 and tenant B’s row 1 are numerically identical, and a naive probe returns a row by accident and looks like a leak.
What this is not.
We do not have runtime row-level enforcement. Our tenant scoping is enforced at build time by the linter and verified by the test suite; the query itself carries its filter because a human wrote it and CI proved it. The runtime helper that carries tenant context does not inject a filter — it says so in its own source comments — and automatic injection is a later phase. We’d rather tell you that here than have your engineer find it.
Control: a single sanctioned bypass, with a mandatory reason, recorded before it runs.
There is exactly one sanctioned way to query across tenants. It requires a written reason — not optional, enforced at the call site, the function refuses to run without one.
The audit record is written before the query executes. Not after it succeeds: before it starts. Which means a query that throws still leaves a trace, and a bypass can’t complete without having announced itself first. 5 tables, including the audit log itself and the security-event log, cannot be read at all without going through this path.
The reason field is treated as personal data and redacted on the same 30-day clock as everything else — because in practice people write ticket numbers and customer names into it, and a governance control shouldn’t become a place where personal data accumulates.
§7
What happens to our data?
Control: minimisation on a clock.
A nightly job redacts personally identifiable information from audit records after 30 days. It is driven by a rule registry keyed to action type, it is idempotent, and when it encounters an action type it has no rule for it warns rather than guessing. A redaction job that improvises is worse than one that stops.
Control: erasure is a job, not a SQL session.
Right-to-erasure runs as an orchestrator with a defined cascade and a dead-letter queue. Agent invocation rows attributed to the subject are soft-deleted as part of it.
Control: named subprocessors, and no training.
5 subprocessors, each with its stated purpose, the data it may process, and its region. Connected-platform tokens are encrypted at rest and are never shared with any subprocessor, logged, or echoed. Data from connected platforms is never used to train models. Deletion propagates within 30 days of a disconnection or a deletion request.
§8
How do you know it hasn’t drifted?
Control: a regression harness for live agents.
Models change under you. Prompts get edited. Tool lists grow. So there’s a check that replays 5 behavioural routes against the live agents, with a single de-flake retry, alongside a deterministic detector that compares each governed agent’s live model, tool list and prompt markers against an expected registry held in the repository.
Drift, in an agentic system, is not a metaphor. It is a diff.
What this is not.
An evaluation suite. 5 behavioural routes replayed weekly against the live founder-facing agent is a regression tripwire, not a benchmark — the deterministic config detector is what covers every governed agent, and behavioural coverage beyond these routes is a later phase. We’d rather run a small check that is on than describe a large one that isn’t.
§9
Who’s on the other end?
Control: four distinct identities, none of which can escalate.
A human signs in through our auth stack, with TOTP, WebAuthn, recovery codes and trusted devices available. A client’s AI agent authenticates with a per-client key scoped to that client and cannot reach beyond it. Our internal agents authenticate per-agent, with an allow-list enforced on the server rather than merely stated in the prompt. Service accounts are a later phase.
The distinction that matters: an agent’s permissions are not a paragraph in its instructions. They’re checked somewhere the agent cannot reach.
Take this list to whoever you’d hire instead.
We wrote this page because most buyers don’t have a framework for evaluating an AI services firm — only about one in eight uses a formal one — and the absence favours whoever talks best. So here are the nine questions. Ask them of us and ask them of everyone else. We’d rather compete on this than on adjectives.
