Your Spec Is the First Thing an Incident Investigator Should Read
Most post-incident retrospectives are missing the most important document in the room: the spec.
When the code under investigation was written by an AI agent, logs and traces tell you what happened. They do not tell you whether the implementation was ever correct in the first place. That gap is not a tooling problem. It is an epistemological one, and most teams are not equipped to close it.
The Forensic Gap Nobody Is Talking About
Post-mortem culture evolved around a core assumption: that a human wrote the code, understood the intent, and made a traceable decision that went wrong. Logs, traces, and test results make sense as evidentiary anchors in that world because the intent existed somewhere in the author's head, and the artifacts are proxies for it.
AI-assisted development breaks that assumption silently.
When an AI agent generates a payment routing function, a retry policy, or a rate-limiting middleware, the intent lived in the prompt and the spec, not in the code. The agent has no mental model to reconstruct. The code is the literal output of a probabilistic process conditioned on whatever context it was given. If the spec was vague, or absent, or was checked once at planning time and then discarded, you now have a system running in production with no authoritative record of what it was supposed to do.
When that system fails, you have a pile of artifacts that describe behavior. You have nothing that describes declared intent. That distinction matters more than most incident commanders appreciate.
Intent Drift Is Not a Bug. It Is a Category Error.
The phrase intent drift describes the condition where shipped behavior diverges from declared intent, not because the code regressed, but because the intent was never verifiably encoded in the first place. A logic bug is recoverable: you find the fault, patch it, and write a regression test. Intent drift is harder because there is no ground truth to regress against.
Consider a concrete case. An AI agent is asked to implement a retry policy for calls to an external payments API. The spec says: "retry up to three times with exponential backoff, do not retry on 4xx responses." The agent writes code that retries on 422 responses, which the payments API uses to signal duplicate transaction attempts. The system double-charges customers intermittently under load.
Now the incident investigation starts. The logs show the 422 responses and the retries. The traces show elevated latency on the retry path. The tests, if any exist for this path, probably passed because they mocked the external API and did not model the 422-means-duplicate semantic. What the logs and traces cannot show you is whether the original implementation was ever reviewed against the spec at merge time, or whether the spec said anything specific about 422 handling at all.
If the spec was a Confluence page that nobody versioned, or a Jira ticket that was closed when the PR merged, you are now doing archaeology. You cannot determine whether this was a failure of implementation, a failure of specification, or a failure of verification. Those are three completely different root causes with three completely different remediation paths.
The Spec as a First-Class Artifact
The operational definition of a useful spec for incident investigation is: a versioned, machine-verifiable record of declared intent that was actively checked against the code at the time of merge.
"Versioned" means you can reconstruct what the spec said at the moment a given commit shipped. This is not aspirational; it is a hard requirement. If you cannot correlate spec state to code state, you cannot do root cause analysis on intent drift. Git is already doing this for your code. Your spec needs the same treatment.
"Machine-verifiable" means the spec contains claims that a tool can check against the implementation, not just prose that a human can read and nod at. This is where most teams stop short. They write good specs, just in a format that only humans can process. A spec that says "the retry logic must not retry on client errors" is useful documentation. A spec that encodes that constraint as a machine-verifiable contract that a CI gate evaluates against the generated code is a forensic artifact.
"Actively checked at merge time" means the verification happened as a gating condition, not a suggestion. If the check was optional, or happened post-merge, or was skipped because the sprint was tight, it did not happen in any sense that matters to an incident investigator.
ThunderLang was built around this premise. The workflow is: declare what a change must satisfy, run a verify-diff at merge time, and store the proof artifact alongside the commit. When an incident happens six months later, the investigator can open the artifact for that commit and answer the question "was this implementation verified against its spec at the time it shipped?" That is not a question you can answer from Datadog.
What a Spec-First Incident Investigation Looks Like
If you have maintained specs as durable, versioned, machine-verifiable records, the investigation sequence changes substantially.
The first question is no longer "what did the logs show?" It is: "what did the spec say this component was supposed to do, and was the implementation verified against that spec at merge time?"
If the verification passed and the behavior still diverged, you have a specification gap: the spec did not capture the constraint that mattered. Update the spec, add the missing constraint, re-verify. Your incident postmortem produces a stronger spec, not just a patch.
If the verification was never run, or ran but was not blocking, you have a process failure. The remediation is structural: make the gate blocking, or decide explicitly not to, with full awareness of the forensic cost.
If the verification ran, passed, and the constraint is present in the spec but still violated in production, you have a verification gap: the tool is not checking what it claims to check. That is a serious finding. It is only discoverable because you have the proof artifact to compare against.
Three distinct root causes. Without a live, versioned spec that was verified at merge time, you collapse all three into a single undifferentiated "bug" category. That is why post-mortems that rely only on logs and traces tend to produce action items like "add more logging" or "improve test coverage," which address symptoms without touching the underlying epistemological failure.
The ThunderLang getting started guide walks through encoding constraints as verifiable claims rather than prose comments, which is where most teams need to start. The tooling gap is real, but the conceptual shift matters more.
Making the Spec Durable Without Making It Expensive
The objection I hear most often is that maintaining machine-verifiable specs is too much overhead for the velocity AI-assisted development is supposed to unlock. I disagree, but the disagreement is specific.
Writing a spec in a verifiable format does not take more time than writing a Confluence page. It takes different time. You are front-loading the precision that most teams back-load into the incident investigation. The question is not whether you pay the cost of precision. It is when.
Paying it at spec time, before the agent generates code, costs roughly the same as writing a thorough PR description. Paying it at incident time, six months later, at 2am, with customers affected and an SLA breach clock running, costs an order of magnitude more. And at incident time, you are paying it under the worst possible conditions without the context of the original implementation decision.
The teams I have seen get this right share one practice: they treat the spec review as part of the AI code review, not a predecessor to it. When a PR containing AI-generated code comes in, the first thing reviewers check is whether the spec is present, verifiable, and current. The code review is secondary. This sounds backwards until the first incident where the spec saves you two hours of ambiguous root cause analysis.
One constraint worth naming: machine-verifiable specs impose a formalism that not all teams can absorb immediately. If your team writes specs in dense natural language prose with a lot of implicit context, you cannot flip to formal constraint notation overnight. The practical path is incremental. Pick the highest-risk components, the ones where intent drift would cause real customer harm, and start there. Payment logic, authorization rules, rate limiting, retry behavior. These are the places where the forensic cost of a missing spec is highest.
Gate your first AI change
If the argument here landed, the next move is to pick one AI-generated component in your current codebase and write a verifiable spec for it retroactively. ThunderLang lets you declare what a change must satisfy, then gates AI-written code against that intent with a verify-diff and durable proof artifacts. Try it here.
The spec is not what you write before the code; it is the evidence you will need when the code fails.