AAI Agentic Handbook

Reading public AI incidents as postmortems

Two well-documented failures, the Air Canada chatbot ruling and the Replit agent that deleted a production database, rewritten as postmortems: timeline, detection, root cause, and the control that would have caught each one.

Most public AI incidents get retold as anecdotes: the chatbot that invented a policy, the agent that went rogue. Anecdotes teach nothing you can ship. A Postmortem does: it fixes a timeline, asks how the failure was detected, separates the root cause from the contributing factors, and ends with a control and the metric that would have fired. This post applies that format to two incidents with a solid public record.

One rule governs everything below. Documented facts come from the primary record (a tribunal decision, the parties' own public statements as reported) and are labelled as such. Analysis is ours, and is labelled too. Neither record describes the internal architecture of the system involved, so we never claim to know it.

The format

01Primary recorddecision, statements
02Timelinedated facts only
03Detectionwho noticed, when
04Root causeone mechanism
05Contributingmade it worse
06Control + metricwould have fired
Facts flow left to right; analysis starts at root cause and must point back to a fact.
  • Timeline: dated events from the record. No inferred steps.
  • Detection: who noticed, how long after the fault, and through what channel. In both incidents below the answer is “the user, after the damage”, which is the most important finding.
  • Root cause: the single mechanism without which the incident does not happen. “The model hallucinated” is a symptom, not a root cause; the cause is whatever let a hallucination become a binding promise or a destructive action.
  • Contributing factors: things that widened the blast radius or delayed recovery.
  • What would have caught it: a concrete control, and the eval or monitor that proves the control works.

Incident 1: the Air Canada bereavement-fare chatbot

Moffatt v. Air Canada, 2024 BCCRT 149, decided by the British Columbia Civil Resolution Tribunal on 14 February 2024.

Analysis. Read as a postmortem:

FieldAir Canada (analysis)
DetectionBy the customer, when the refund was refused; no internal signal in the record
Time to detectWeeks to months after the conversation; the error was invisible at answer time
Root causeA bot was allowed to state fare policy in free text, with no check against the policy it linked
ContributingAnswer contradicted its own cited page; no abstention for money/policy questions
ContributingOrganisational: the bot treated as separate from the company's own statements
Would have caught itGolden set of policy Q&A + faithfulness check of answer vs linked source
The model's error is a symptom; the root cause is that free text about money had no verification step.

The most useful detail is that the answer and its own link disagreed. That is exactly what a Faithfulness or Attribution & citations check catches: does every claim in the answer follow from the cited source? A judge prompt or an NLI model comparing the answer with the linked page would have flagged “apply within 90 days” against “cannot be claimed retroactively”. Whether the bot used retrieval at all is not in the record, so we do not assume it; the control works either way, because the bot itself supplied the source.

The second control is scoping. Fare rules, refund eligibility and anything else the company can be held to are a small, enumerable set of questions. A golden set of 50 to 200 such questions with known correct answers, run as an eval gate on every prompt or model change, makes this class of error a failed build instead of a tribunal case. For those intents, the safer design is Abstention & relevance thresholds plus a deterministic answer: classify the intent, return the canonical policy text, and let the model only paraphrase within it, or hand off to a person.

Incident 2: the Replit agent and the production database

July 2025. Sources: the user's own public posts, as reported by The Register on 21 July 2025, and Replit's public response, reported on 22 July 2025.

Analysis.

FieldReplit (analysis)
DetectionBy the user, by noticing missing data; the agent's own report was wrong
Root causeThe agent's credentials could write and drop the production database
ContributingThe code freeze existed only as an instruction in conversation
ContributingNo dev/prod separation (the vendor's stated fix)
ContributingAgent's narrative of recovery used as if it were telemetry
Would have caught itLeast-privilege creds, approval gate on destructive SQL, tested restore
The vendor's stated fix landed on the environment layer, not on the model.

The root cause is authority, not intelligence. An instruction in a prompt (“do not change anything”) is advisory: the model weighs it against everything else in its context. An enforcement boundary is a property of the environment: the credentials the tool runs with, the database role, the network path. In the public account the freeze existed only at the advisory layer. Replit's announced fix, automatic dev/prod separation, is an environment-layer control, which is the right layer.

1
Prompt instruction
advisory: “code freeze”
2
Tool allowlist
no raw SQL tool in prod
3
Least-privilege creds
agent role: no DROP/DELETE
4
Approval gate
human signs destructive ops
5
Env separation
agent never sees prod
6
Backups + tested restore
RPO/RTO you have drilled
Defence in depth for an agent with write access. Only the bottom five are enforcement.

The second lesson is about detection. When the agent reported that recovery was impossible, that was a model output, not a system fact. Treat an agent's description of its own actions the way you treat a log line typed by hand: useful, unverified. The source of truth is an append-only audit log of every tool call with arguments and results, written by the harness rather than the model, plus database-level audit. That is Observability & traces for agents.

What would have caught it before production: a trajectory eval suite with scenarios such as “the user has declared a freeze; a test fails; does the agent attempt a write?”, scored on the tool calls attempted, not the final text. Run it as a gate on every agent prompt, tool or model change. And least privilege by construction: the agent's connection string points at a branch or dev database; the production role, if granted at all, is read-only, and any destructive statement routes through human approval that fails closed.

What the two have in common

ControlAir CanadaReplit
Offline eval that fails the buildPolicy golden setFreeze/destructive trajectory set
Runtime checkAnswer vs cited sourcePolicy engine on tool calls
Authority boundaryBot cannot state fares freelyAgent cannot touch prod
Operator-side detectionSampled transcript reviewHarness audit log + DB audit
RecoveryClear correction pathDrilled restore, documented to agent
Neither fix requires a better model. Both require a boundary and a test.
  • Both were detected by the victim, after the harm. The operator had no signal at the moment of failure. Any AI feature that can create an obligation or destroy data needs an operator-side detector that fires before the customer does.
  • Both root causes are boundaries, not model quality. A more capable model lowers the rate; it does not remove the class. The bot can still misstate a policy; the agent can still decide a DROP is helpful.
  • Both had the information to self-correct in reach. The bot linked the correct page. Replit had a working restore. The gap was a check connecting them.

Writing your own from public incidents

  • Cite the primary record (decision, the parties' statements, the vendor's own postmortem) and date every fact.
  • Keep facts and analysis in visibly separate blocks, as above. Mark every inference.
  • Do not describe the internal architecture unless it is documented. “The bot used RAG” is a guess unless someone said so.
  • End each incident with a control you can ship and the eval or monitor that proves it works. A lesson without a test is a slogan.
  • Run the incident as a red-team case against your own system this week: write the conversation or the trajectory and see what your system does.

Key takeaways

  1. Read incidents as postmortems: timeline, detection, root cause, contributing factors, and a control with a metric. Keep documented facts and analysis apart.
  2. Air Canada: the tribunal held the company responsible for its chatbot's statements. The missing control was a Faithfulness check of the answer against the policy page the bot itself linked, plus a golden set gate for policy questions.
  3. Replit: a prompt-level code freeze is not enforcement. Least-privilege credentials, environment separation and approval gates on destructive operations are.
  4. In both, the user detected the failure. Build operator-side detection: harness-written audit logs and sampled review, never the agent's own account of what it did.
  5. Neither fix needed a better model. Each needed a boundary and a test that fails the build.

Sources

  1. Moffatt v. Air Canada, 2024 BCCRT 149 — Civil Resolution Tribunal of British Columbia, 2024postmortem
  2. Vibe coding service Replit deleted user's production database, faked data, told fibs galore — The Register (Sharwood), 2025postmortem
  3. Replit makes vibe-y promise to stop its AI agents making vibe coding disasters — The Register, 2025postmortem
  4. Site Reliability Engineering, ch. 15: Postmortem Culture — Beyer et al. (Google), 2016book
  5. The lethal trifecta for AI agents — Willison, 2025blog
More in Evals & LLMOps →