Agents Honestly
Part XX · Capstone

Hardening It

Evals, tracing, failure injection, authorization, and the security review.

Exercise

Atlas works. It answers policy questions with citations, runs the aggregation, follows the account graph, and issues tier-0 credits through a dispatcher with eight checks in it.

None of that is evidence. "It works" at this stage means nobody has seen it fail recently, which is a statement about observation rather than about the system. And for a component whose characteristic failure is a confident wrong answer, it is the weakest possible claim.

This chapter is the work between it works and we can point customers at it. It has four parts, and they are ordered by what each one makes possible for the next.

1 · Measurement, because nothing after this is assessable without it

The twenty-ticket set from the original brief grows into a real dataset. Three sources, and the proportions matter:

SourceCountPurpose
Hand-labeled from real tickets180Distribution matches production
Promoted from production failuresgrowingEvery incident becomes a fixture
Adversarial, written on purpose40Injection, cross-account, out-of-scope
Negative, must return nothing30Isolation

The second row is the one that compounds. Replay turns each production failure into a deterministic fixture, and the corpus grows at exactly the rate the system actually breaks, which is a better sampling strategy than anything anyone designs up front.

The fourth row is the one teams skip. A test asserting this document ID never appears in the retrieval trace for this principal is a rare deterministic assertion in a probabilistic system, and you should take every one this field offers.

What gates and what reports: the invariants gate: an invariant violation fails the build, no threshold, no discussion. The rates report as a distribution against a flake budget, because gating a noisy metric on a single run produces a suite people learn to re-run until it passes.

Then online scoring on a production sample, because CI has the labels and production has the sample size, and neither substitutes for the other.

2 · Tracing, so a failure is answerable rather than mysterious

Already built during weeks one and two, and the hardening question is narrower: does a trace answer the eight questions for the specific failures Atlas will actually have?

Four fields carry disproportionate weight, and all four exist for more than one reason:

FieldDebuggingAlso required by
config_hashWhich bundle produced thisRollout attribution, drift
Retrieved chunk IDs and textWhy it said thatReplay, poisoning incident scope
served_byWhich model answeredSilent fallback detection
sub / act / run_idWho authorized thisAudit, compliance

When a field is demanded by three chapters written for different reasons, that is usually the signal it is not optional.

3 · Failure injection, which turns claims into observations

Every reliability mechanism is a hypothesis until observed. The CI suite runs six injections on every commit, each asserting the six reliability invariants: terminal, no duplicate, bounded, escalated, traceable, contained, which are a different list from Atlas's nine acceptance invariants:

InjectionAsserts
Third tool call errorsThe model receives it as an instruction; no transport retry
Stream stalls at token 50Inactivity timeout fires within 15 s
Worker killed before the dedup record commitsResume issues no second credit
Approval fast-forwarded past the dedup windowThe Friday-to-Monday bug, still no second credit
get_order returns another order's totalAmount re-derivation rejects; run escalates
Ticket carries an injection payloadTaint holds; issue_credit denied and escalated

The fourth row would otherwise take three real days to reproduce and would be found by a customer. The fifth is the only test in the suite that exercises the semantic class: nothing raises, the agent proceeds, and only the amount re-derivation stands between a corrupted read and a wrong credit.

4 · The security review, run as a checklist

Now the part with a real output. Part XVII is eight chapters; run as a review against the built system, it produces findings. Here are Atlas's, which are the ones this book has been arguing you will have.

   ①  the eval harness calls retrieval without a principal
   ②  the nightly backfill runs as the service account
   ③  compaction summaries have no subject_ids
   ④  the reranker was added without a subprocessor review
   ⑤  the checkpointer accepts a thread_id argument
   ⑥  traces are retained 90 days at a third-party vendor
   ⑦  the fallback path was never eval'd
   ⑧  tool descriptions ship in code review, not prompt review
   ⑨  no alert on a run's taint ceiling firing
Nine findings from one review. Six were design gaps, three were things that had drifted since the design.
#FindingWhy it happenedFix
The eval harness retrieves with no principal. It sees every account's corpusThe principal-required rule was enforced in the app path, and the harness is not the app pathOne retrieval function, no overload without a principal; harness uses a fixture tenant
The nightly backfill runs as the service account with full scopeNobody was present, so nobody was namedNamed human owner, documented grant, annual expiry, mined scope
Compaction summaries carry no subject_ids, so erasure cannot reach themCompaction was a context decision, not a data-protection oneRecord subject IDs at creation; retrofit is impossible, so this was nearly a permanent defect
A reranker was added to improve nDCG; it is a new data processorExactly the way it always arrivesSubprocessor list updated, region pinned, or removed
The checkpointer takes thread_id as an argumentIt looked like an ID, not an authorization decisionDerive the key: tenant:thread, composite, never supplied
Traces hold verbatim prompts for 90 days at a vendorRetention was set for debugging convenience30 days; handles at assembly so traces hold no raw personal data
The fallback rung has never been evaluatedIt runs 0.1% of the time, so it is the least-exercised codeNightly eval against rung 4; 1% of tier-0 traffic served by it permanently
Tool descriptions change through code reviewThey are prompt text living in a schematoolCatalogueHash in the bundle; description changes run the eval gate
No alert when a taint ceiling denies an actionThe control worked and nobody was toldAlert on denial rate. It is the highest-signal detection available

Two observations about that table are the point of including it.

Every finding is a control that existed and had a gap around its edge. None of them is "we forgot security." Retrieval filtering was implemented, and the eval harness went around it. Identity propagation was implemented, and the scheduled job had no user to propagate. This is the normal shape of security findings in agentic systems, and it is why the review has to be run against the built system rather than the design document.

Three of the nine were drift. The reranker, the trace retention, and the tool descriptions were all correct at design time and moved. Which means the review is not a gate you pass once. Findings ⑧ and ⑨ are specifically mechanisms to catch the next drift rather than fixes for this one.

The finding that was nearly permanent

Finding ③ deserves separate attention. Every other item on the list was fixable in days. Missing subject_ids on derived artifacts is not retrofittable: the summaries already written have no key, and there is no way to determine after the fact which of them mention a given person.

The only real remedy was to delete the existing summaries and start recording provenance going forward. That was cheap at eleven accounts and six weeks of history. At two years and a thousand accounts it would have been a permanent compliance defect, discovered during an audit.

Which is the general lesson: the controls that must be designed in are the ones about data you derive. Everything else can be added later at a cost.

The go/no-go

Hardening ends with a decision, and it should be written down before the numbers arrive so that nobody negotiates the bar against the result.

Hard gates, any failure blocks:

  • Zero invariant violations across the full suite, including adversarial and negative sets.
  • All six failure injections pass.
  • Every finding above either fixed or explicitly accepted by a named person with a date.
  • One run reconstructible end to end from the trace, demonstrated live.
  • Rollback demonstrated: manifest repointed, new runs on the old bundle, in-flight runs finishing.

Rate gates, measured on the canary, not on fixtures:

Explicitly not a gate: "the support lead thinks it feels good." That signal is valuable, it belongs in the review, and it is not permitted to override a rate, for the same reason the rates were written down in week one.

What hardening did not fix

Three things survive all of it, and saying so is part of being ready rather than a caveat on it.

Semantic failure is bounded, not eliminated. At 95% factual accuracy, one in twenty answered tickets is wrong. The design response is not a better model. It is that the escalation contract, the citation requirement, and the tier-0 cap keep the consequences of that one bounded to something a person can correct.

Injection is contained, not prevented. A hostile ticket can still make Atlas want to do something. There is no prepared statement for English; the taint ceiling means wanting is where it stops, and that is the whole claim.

The model can change underneath you. Pinned is not frozen. The canary is the detection, the ladder is the response, and neither is prevention.

A launch review that claims otherwise on any of the three is describing a system nobody built.

Takeaways

  • "It works" means nobody has seen it fail recently. For a system whose failures are silent and plausible, that is the weakest available claim.
  • Build the dataset from four sources: hand-labeled real tickets, promoted production failures, adversarial cases, and negatives that must return nothing.
  • Promoted failures compound: the corpus grows at exactly the rate the system breaks, which beats any up-front sampling design.
  • Negative tests are a rare deterministic assertion in a probabilistic field. Take every one available.
  • Invariants gate with no threshold. Rates report as distributions against a flake budget, or the suite becomes something people re-run until it passes.
  • Four trace fields are demanded by three chapters each: config_hash, retrieved chunk IDs with text, served_by, and the user/agent/run triple. That redundancy is the signal they are not optional.
  • Six failure injections per commit. Two of them, the approval fast-forward and the wrong-result read, cover bugs that are otherwise found by a customer three days later.
  • Run the security review against the built system, not the design. Every finding will be a control that existed with a gap around its edge.
  • Expect the gaps at the seams: the eval harness bypassing the principal check, the scheduled job with no user, the ID accepted as an argument.
  • Expect a third of findings to be drift: things correct at design time that moved. Fix those with mechanisms, not patches.
  • The controls that must be designed in are the ones about derived data. Missing subject IDs on summaries is not retrofittable; almost everything else is.
  • Write the go/no-go before the numbers arrive, so the bar is not negotiated against the result.
  • Hold escalation recall highest. A false escalation is free; a missed one is a mishandled customer.
  • "The support lead thinks it feels good" belongs in the review and may not override a rate.
  • Hardening bounds three things rather than eliminating them: semantic failure, injection, and model change. A launch review claiming otherwise is describing a different system.

Every gate is green, and the suite picked the conditions under which they went green. Next: Failure Labs, on breaking one boundary at a time and keeping whatever proves it recovered.

On this page