AI Coding Risk Register Before Merge

A pre-merge risk register specifically for AI-generated code: the seven categories of risk the LLM won't flag, the one-line risk note per PR, what blocks merge vs raises a warning, and the rollout and rollback plan a high-risk row needs before it can ship.

statusreviewedpublished2026-03-10last-review2026-11-06read10 minauthorEditorial Teampolicyeditorial

Why AI Code Needs Its Own Risk Register

The point is not another template. The point is to stop getting surprised in production. The usual pattern: an AI-generated PR ships something nobody on the team would have shipped by hand, then another one does, and eventually the team has to accept that the assistant and the reviewer are not weighing risk the same way. The assistant optimizes for plausible code that compiles. The person merging it is supposed to optimize for whether they can still sleep at night afterwards.

The register closes that gap with a one-line risk note on every AI-assisted PR. Not a document. A single sentence that says what the change could break and what the author did about it. That sentence is the entire register. It lives in the PR description, it is required by CI, and it is the first thing the reviewer reads.

The Seven Categories the Model Will Not Flag

Seven buckets. Every PR gets scanned against all of them before a risk note is written:

What the One-Line Risk Note Actually Looks Like

Here is the exact format. It is boring on purpose. Boring is readable in a hurry.

Risk: adds GET endpoint with user-supplied ORDER BY — SQL injection class. Mitigation: whitelist columns.

That is the whole thing. A category cue, the specific thing that could go wrong, and what was done about it. If the author cannot write that sentence, the PR is not ready. If the mitigation is "none," the PR is not ready either — that is a signal, not a shortcut.

Green, Yellow, Red: The Gate Matrix

Every PR gets a color from the seven categories. The color decides what happens next. This is the register’s half of a broader gating story — the quality gates for AI-assisted work cover the automated side that runs before a human ever sees the colour:

The Author Writes the Note. The Reviewer Verifies It.

Non-negotiable: the person who prompted the AI writes the risk note. Not the reviewer, not the model, not a bot. The author has the context — what they asked for, what came back, what they accepted on faith. The reviewer's job shifts from "find the bugs" to "challenge the risk note." Did the author miss a category? Is the mitigation real or aspirational? Is yellow actually red?

This flip matters. Reviewers who start from the risk note catch more than reviewers who start from the diff, because the note exposes the author's reasoning. If the note is lazy, the code usually is too.

Prompting the AI to Surface Its Own Risks

Ask for the risks before asking for the implementation. The prompt: before writing any code, list the three highest risks in this change and how you would mitigate each. The answers are uneven, but the exercise forces the model to inspect the change instead of just emitting it. Keep those three lines in a scratch comment while writing the real risk note.

There is a trap here, and it is the whole reason the register exists. AI-generated code tends to look low-risk. Calm variable names, polite comments, short functions. A human reader pattern-matches on tone. The register is a forcing function against that bias: the note gets written regardless of how friendly the code looks.

Worked Example: The Unscoped User-Search Endpoint

Take a PR that adds a GET /api/users/search?q= endpoint. The code is clean, tests pass, and the assistant has written a plausible parameterized LIKE query. The author adds a light wrapper and opens the PR.

Writing the risk note is what catches it. Security trips, then performance, then regulatory — and the author sits with that last one long enough to realize the query is not scoped by tenant. Anyone signed into any account can search every user in the system. The code works exactly as written. It is also a cross-tenant data leak waiting to happen. The risk note makes it red. The fix is two lines. The incident that never happens is the one the register exists to prevent.

When the Mitigation Is the Rollout: Rollback Design for High-Risk Rows

Not every row closes with a two-line fix. A backfill, a pricing change, a new notification sender: for these the code can be correct and the release can still hurt users. The honest mitigation is how the change ships and how it gets stopped, and "we'll monitor closely" is not a mitigation.

Treat a row as high-risk when the change touches one of four things: money, identity, stored data, or user communications (emails, notifications). Anything else can usually ship behind a feature flag with light monitoring. For the four, the risk note's mitigation should point at a rollout section in the spec, and a red row in these areas stays red until that section exists. The plan has to match the blast radius: a broken UI component affects a button; a broken billing migration can affect weeks of revenue recognition.

Pre-flight checks

These run before the deploy. If any fails, the release does not go out.

Staged rollout with numbered gates

Not "gradual rollout." Gates with numbers:

Stage 1: internal users only (≤50 accounts)   soak 24h
Stage 2: 1% of customer traffic                soak 6h, check SLOs
Stage 3: 10% of customer traffic               soak 12h, check SLOs
Stage 4: 50% of customer traffic               soak 2h
Stage 5: 100%                                  monitor 72h before declaring stable

Each gate is a decision owned by a named person, not "the team," who decides to proceed, hold, or roll back. A gate entry names the cohort, the dashboard to watch, who can pause expansion, where the pause gets announced, and what has to be true before expansion resumes. Put the table and the rollback owner in the spec, not in a launch chat: on-call needs one place to look.

Stop-loss thresholds in numbers

A vague stop-loss ("if something looks bad") gets overridden at 3am by whoever wants to go back to bed. Write numbers:

SignalStop thresholdAction
5xx rate on the changed endpoint3× baseline for 10 consecutive minutesRoll back.
p95 latency2× baseline for 15 minutesRoll back.
Conversion on the affected funnelDown more than 15% vs. the same day last weekRoll back.
Data invariant (e.g. order total ≠ sum of line items)Any violationRoll back immediately.
Duplicate side effect (double charge, double send)Any confirmed duplicateRoll back the write path; open an incident review.
Related support ticketsFive in one hourReturn the rollout to internal users only.

Baseline against the same day last week, not last week's average, or normal daily variance will trigger false rollbacks and hide real ones. Have product review the thresholds too: a stop-loss written only by engineering tends to be either too strict or too loose about what error rate is acceptable.

Name the rollback type

"Rollback" means very different things physically, and the response times differ by orders of magnitude:

If the rollback falls in the last two, there is no rollback, only a forward-fix, and the risk note should say so. For schema changes, use expand-contract: add new columns as nullable, backfill, switch the read and write paths, then drop the old columns in a separate release. Each phase can be reversed on its own. If an AI-generated diff ships the migration and the code that depends on it together, splitting it into those phases is the mitigation. Flag the data migration as well as the code path, or there is no way to roll back partially. If the release cannot be described as a sequence of forward-compatible phases, it needs a maintenance window.

A runbook on-call can run at 3am

Link the runbook from the spec. It should answer, without interpretation:

The test: a new on-call engineer can execute it without paging anyone. And rehearse it. A rollback that has never been run tends to break the first time it is needed. Some rollbacks are not technical at all. For a new pricing engine, reverting code does not undo invoices already generated; those may need credit notes, audit records, and customer messaging. Write that down before launch, because nobody wants to invent accounting policy during the incident.

A risk note for a high-risk row then reads like this:

Risk: AI-generated backfill rewrites role on existing rows (data loss class). Mitigation: expand-contract over two releases, dry-run row-count diff attached, flag role_backfill_v2 pauses the job, stop on any invariant violation; rows already written need a forward-fix.

Given/When/Then Acceptance Criteria for the Register Itself

The register only works if it is a CI check, not a polite suggestion. Here is how to specify it:

Given an AI-assisted pull request is opened
When the PR description does not contain a line matching "Risk:"
Then CI fails with a message pointing to the risk register policy

Given a PR is marked red by the risk matrix
When the author attempts to merge without a linked redesign issue
Then the merge is blocked until a reviewer with the risk-gate role approves

Given a PR passes all seven category checks with no trips
When the author writes "Risk: none"
Then the PR may proceed through normal review at yellow-equivalent speed

The check itself is dumb on purpose. It looks for the literal string Risk: in the PR body. Judgment is still human. The gate just refuses to let you skip the judgment.

The Weekly Sweep: Reading the Register as a Dataset

Once a week, say every Friday, scan the week's risk notes — not the PRs, the notes. Patterns show up fast. If half the yellow notes say "N+1" the team has a query-layer problem, not a review problem. If "new dependency" keeps appearing, someone needs a package-vetting policy. If regulatory trips never show up from a team, they are either lucky or not looking. The register stops being paperwork and starts being a weak-signal sensor. That is when it earns its keep — not on any single PR, but in the aggregate. The register follows the same rule as the code it guards: if it is not helping anyone decide something, it should not exist.

AI Review Packet to Copy

Use this when the assistant produced a plausible diff and the team needs to decide what could still go wrong. The risk note belongs in the PR before approval, not after a reviewer gets nervous.

AI coding review packet: AI Coding Risk Register Before Merge

Decision to make:
- A pre-merge risk register specifically for AI-generated code: the seven categories of risk the LLM won't flag, the one-line risk note per PR, and what blocks merge vs raises a warning.

Owner check:
- Product owner:
- Engineering owner:
- QA or operations reviewer:

Scope boundary:
- In scope:
- Out of scope:
- Assumption that still needs approval:

Acceptance evidence:
- Test or fixture:
- Log, metric, or screenshot:
- Manual review step:

AI boundary: generated changes must stay inside the written scope and attach evidence for each acceptance criterion.

Reviewer prompt:
- What would still be ambiguous to someone who missed the planning meeting?
- What evidence would make this safe enough to ship?

Risk register example from an AI-generated change

For AI code, the risk note should name the exact behavior the model might have invented. “Looks okay” is not a review artifact.

RiskWhy AI may miss itMerge gate
Search endpoint returns users across tenants.The prompt said “admin search” but did not name tenant boundary.Permission test using two tenant fixtures.
Migration backfills null role as admin.The model inferred a default from sample data.Migration dry-run plus row-count diff.
Retry loop duplicates notification sends.Generated code retries the side effect, not the idempotent operation.Replay test proves one notification per event_id.

Case study: the AI-generated tenant leak

The generated code passed every happy-path test. The risk register caught the missing tenant boundary because reviewers had to name what data could cross accounts.

Risk note before merge:
- Change: add user search endpoint for support console
- AI-added query: SELECT * FROM users WHERE email ILIKE $1
- Missing boundary: tenant_id and support role scope
- Blast radius: any support user can search across all tenants
- Required evidence:
  - cross-tenant denial test
  - audit log includes actor_tenant_id
  - query plan shows tenant_id predicate
  - reviewer signs off on allowed support roles