Skip to main content
Evaluator brief

Product, QA and assurance

Written for
auditor
The decision it ends in
Decide what can be tested, what can be signed off, and what has to be written down as a known limit.
The pain

Where this starts

You are asked to give release confidence on a system whose behaviour is non-deterministic, whose failure mode is a side effect rather than a wrong answer, and whose test oracle — did the bad thing not happen — is an absence.

Conventional assertions confirm that an error was raised, which is not the same fact.

The trigger

What makes it this quarter’s problem

An agent-backed feature enters your release, and the acceptance criteria say “must not be able to” for the first time.

What is supported

What Agent Assembly does about it — and how far each answer reaches

The product turns “must not be able to” into a decision, on the paths you route. Four scenarios carry approved wording for reuse — the flagship egress refusal, secret exfiltration, a destructive production action and runaway cost — each with its decider, its default state and its boundary named. The entries behind them are below.

Each card below is one entry of a sixteen-entry claim register shared by all four of these pages, quoted rather than paraphrased. The term is the register’s own, copied off the evidence rather than chosen to suit the sentence; the bound beside it is part of the claim, not context for it. Every entry — including the ones this page does not cite — is published with the manifest rows behind it: the shared claim register.

Denied before executionRC1

A connection made on a path you routed through Agent Assembly is checked against the destination list you configured and refused before the proxy dials it.

Where this stops. The refusal is the proxy’s own local egress configuration, not a control-plane decision. The destination lists are empty by default — this refusal exists because an operator configured it. Linux release artifact; on macOS cargo install aa-proxy is the only route; on Windows there is no local mediation. If the proxy is not in front of the connection, the connection is simply made.

RedactedRC3

On the model-provider hosts Agent Assembly inspects, a recognised credential is removed from the request before it is forwarded.

Where this stops. Three built-in hosts, because llm_only defaults on. The default action is redact and forward, not refuse. Recall is bounded by the pattern set — there is no Stripe detector. Model responses on that path are not scanned.

Denied before executionRC4

An MCP tool call can be checked against your policy by the control plane and refused before the proxy forwards it.

Where this stops. The only gateway-bound pre-dial refusal in the product, and it is off by default. It reaches MCP sent as an ordinary HTTP/1.1 POST on an intercepted non-LLM host with a gateway endpoint configured. Tool servers over stdio — the most common setup — SSE and WebSocket have no interception mechanism; Streamable HTTP is recorded as functionally broken rather than merely uncovered.

UnmeasuredRC13

Whether a declared spend cap is checked in the decision path is not established by any manifest row.

Where this stops. Same shape as RC6, and the same remedy. The manifest’s only budget row is the one for a store that is unreadable or corrupt, carrying coverage: unmeasured, failure_posture: fail_open_silent and evidence: gap — its gap reason records a positive control showing the budget path never queries the control-plane store. Risk scenarios’s T3 reaches Evaluated and states in the same table that it has no positive row; this register does not restate T3’s term over a row that does not carry it. What is bounded regardless: a cap exists only where a policy declares one, an undeclared budget is uncapped, reaching Denied before execution needs a caller that waits for the answer, and a corrupt store resets the cap to zero spend silently. See the two gaps.

UnmeasuredRC6

Whether a given decision’s record durably reaches the audit chain is not established. The verification tool is real — aasm audit verify-chain ships in the open-source build — but what it proves is the integrity of the entries that are present, not that any particular decision produced one.

Where this stops. The manifest’s only row for this subject is the row for what happens when the write fails, and it carries coverage: unmeasured, failure_posture: fail_open and evidence: gap. So the honest term is the row’s own. Everything else about the chain is a bound, not a capability: it is tamper-evident, not immutable and not signed — an unkeyed digest, so anyone able to rewrite the sink can recompute it. The chain head advances before the send and a full channel drops the entry while the call still returns, which makes a dropped entry indistinguishable from a deleted one. An emptied log verifies clean. The proxy writes no local record at all unless its audit path is configured. See the two gaps.

The outcome

What is different after

A refusal becomes an observable decision, so a test can assert on the decision.

Assert against the decision and against an independent observer, not against the audit log. RC6 is Unmeasured, so a missing entry does not distinguish “the decision was not made” from “the record was dropped”, and a test that reads the log inherits that ambiguity as a flaky pass. The negative control published with the risk scenarios is built on an independent listener for exactly this reason.

Assertions about the averted consequence are a separate and stricter thing again — see the Limitations below before you write one.

Proof

What you can check, and where

  • The four scenarios, each with a determination and its manifest rows — and specifically the negative-control section, which specifies the absence check, the paired positive control that proves the check can see the effect, and the assertion ordering.

    Risk scenarios and the negative control →
  • Per-row evidence: the evidence and runs-on-main fields, present on all 80 rows. Read the row before quoting it — some are pinned by standing integration tests, some by unit tests only.

    The capability manifest →
  • What is deliberately not asserted yet — the Provisional table on the product promise is the list of claims held back, and it is the fastest way to see what an acceptance criterion cannot rest on.

    The Provisional table →
  • Release and version position — a demo recorded on one platform is not evidence about a released artifact on another.

    The compatibility matrix →
Limitations

What you would otherwise find out later

  • Approval is the gap, not a feature.An acceptance criterion written against a human approval step cannot pass today.
    Approval requiredRC12

    No claim.

    Where this stops. No manifest row reaches this term. The hold itself is real in the gateway path and fails closed on timeout, but no shipped operator surface can answer it, and inside the MCP tunnel a pending decision is downgraded to a refusal, so a human cannot be reached there either. AAASM-5657.

  • A prevented-outcome claim is gated.Designing a negative control is not the same as having run one. Until AAASM-5532 and AAASM-5529 close, describe the decision, not the averted consequence.
  • An error is not an absence.An agent can receive a refusal and still have reached the endpoint by another route. A test that asserts on the error and not on the independent observer is measuring the wrong thing.
  • Evidence quality is not uniform.Some rows are pinned by standing integration tests; some by unit tests only; some carry no evidence at all and are recorded as gaps. Two rows are explicitly marked unit-only. Read the row before quoting it.
  • An empty audit log is evidence about the observer, not about the agent.And a passing chain verification does not mean the log is whole (RC6).
  • Two of the sixteen register entries rest on no capability row at all.RC6 and RC13 are Unmeasured because the manifest’s only rows for the evidence pipeline and for budget are the rows for those subsystems failing. An acceptance criterion written against “the decision is in the audit log” or “the cap was applied” is currently asserting something the evidence base does not carry. Tracked as AAASM-5531.
  • A budget cap exists only where a policy declares one.An undeclared budget is uncapped, and a corrupt budget store resets the cap silently (RC13).
  • Coverage figures are not available.No percentage, count of governed actions or fleet-level number may be derived — including from the four scenarios. Self-reported layer availability is not evidence of coverage.
  • Three signals look like coverage and are not.An environment variable that replaces the probe result outright, a proxy probe satisfied by a binary existing on the path, and an SDK layer flag asserted unconditionally. Do not build a check on any of them.
  • Platform and channel change the answer.A demo recorded on macOS is not evidence about a released Linux artifact (RC8, RC9, RC14).
Next

One page to read, one thing to do

  • Read the risk scenarios and their negative-control section — it specifies the absence check, the paired positive control, and the order the assertions have to run in.

    Risk scenarios →
  • Then check the Provisional table before writing an acceptance criterion — it is the list of claims that are not yet assertable, and writing against one is the cheapest mistake to avoid here.

    The Provisional table →