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.
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:
- Security. Injected secret leak, unsafe
eval, untrusted input reaching a shell, auth bypass behind a helper. The model reaches forexecwhen a parser would do. - Data loss. DB writes, destructive migrations,
DELETEwithoutWHERE, file overwrites, schema drops. Data is a one-way door. - Performance. N+1 queries, unbounded loops, sync calls in a hot path, missing pagination. Fast on a demo dataset, catastrophic on the real one.
- Correctness. Silent wrong answers. Off-by-one, wrong sign, wrong rounding, swallowed exceptions. The code runs; the result is subtly wrong.
- Dependency. New packages, unfamiliar packages, packages with ten weekly downloads. The assistant imports almost anything.
- Regulatory. PII, GDPR, HIPAA, PCI, anything touching payments. The model does not know your jurisdiction.
- Operational. New queue, new cron, new webhook — anything that now needs monitoring and nothing is watching it yet.
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:
- Green. No category trips. Pure refactor in a tested module, log tweak, doc change. Merge normally. A Risk: none line is still welcome.
- Yellow. One or two categories trip with a clear mitigation. Write the note, proceed through normal review. Most real PRs.
- Red. Any destructive data op, any new auth surface, any regulatory touch, or three-plus categories at once. Block merge. Redesign, split, or escalate. Red never gets waived inline.
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.
- Integration tests pass against production-like volume: at least 10% of production row counts, not a 100-row fixture. The performance category's "fast on a demo dataset" failure only shows up at real volume.
- A dry-run migration on a production snapshot finishes inside the maintenance-window budget.
- The rollback has been executed end to end in staging by someone other than the author.
- On-call knows the release window and has the runbook open.
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:
| Signal | Stop threshold | Action |
|---|---|---|
| 5xx rate on the changed endpoint | 3× baseline for 10 consecutive minutes | Roll back. |
| p95 latency | 2× baseline for 15 minutes | Roll back. |
| Conversion on the affected funnel | Down more than 15% vs. the same day last week | Roll back. |
| Data invariant (e.g. order total ≠ sum of line items) | Any violation | Roll back immediately. |
| Duplicate side effect (double charge, double send) | Any confirmed duplicate | Roll back the write path; open an incident review. |
| Related support tickets | Five in one hour | Return 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:
- Feature flag flip: seconds. Users are back on the old behavior within about a minute.
- Config change: 1 to 5 minutes, and only if the config hot-reloads without a deploy.
- Code revert and redeploy: 5 to 20 minutes depending on CI. The old code must still work with the data the new code wrote.
- Job pause: variable. New work stops, but in-flight jobs may still need cleanup.
- Database rollback or repair: hours to days. Usually a forward-fix in practice.
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:
- How do I check right now whether the release is working? (A specific dashboard and metric.)
- What does healthy look like there? (A specific value range.)
- What is the first step if it is not healthy? (A specific command or button.)
- Who do I page if I am unsure? (A specific person or rotation, with a response time.)
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.
| Risk | Why AI may miss it | Merge 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