---
title: "The Handoff Gauntlet: How Parallel Reviewers Caught a Cache Bug"
canonical: https://dxdev.com/blog/2026-09-26_parallel-code-review-dev-handoff/
datePublished: 2026-07-28
---
Four reviewers ran in parallel over a finished diff, and one of them flagged a preview render that would be written into the production page cache and served to real visitors. The ticket had passed staging, and the last step before release was my read of the diff. Staging could never have shown me this.

The ticket was a page-layout templates feature from a teammate. It adds a preview mode driven by a URL param, `websiteTemplateID`, and a bulk-apply path that pushes a template across a site's files.

## The cache bug

Module pages come out of a DB-backed render cache. A `CacheEnable` method on the cache class holds a bail list of query params that mean "this is a preview, don't read or write cache": `dev`, `convPrev`, `templateID`, `emptyText`, `backup`, `stylePreview`, `previewArea`, `previewMobile`.

The teammate had added `websiteTemplateID` to the page's `fromAdmin` check, which looks right. It reads like the place where admin-only modes get registered. But `fromAdmin` triggers an early return that skips the `CacheDisable()` call further down. A param handled only there leaves caching fully on.

That gives two failures:

1. **Poisoning.** The preview render is written under the site's normal cache key and served to every anonymous visitor until the entry rolls.
2. **Wrong preview.** The cache read happens first, so on a warm cache the preview iframe shows the live site instead of the template.

Neither reproduces off prod. The cache is gated on the production domain, so no clone or staging box takes that path. The ticket had every reason to look fine, and it did.

The fix is one line in the bail list. I wrote it up as a reference note so the next preview mode gets added there on day one.

## The rest of what came back

The bulk-apply path had a cluster of file-handling bugs. One copies the wrong site's images. One deletes customer logos with no backup. Those two are the ones I would not have wanted to discover from a support ticket.

I did not push a bundle. I put six suggested fixes on a separate review branch so the teammate can cherry-pick them one at a time and reject any they disagree with. Then I handed the ticket back with numbered findings and four design questions. Those were the calls where guessing at the intent would have been worse than leaving the code alone, like ownership models and what a destructive action should be allowed to do.

## The wrong turn that shaped how I ran it

I did not start this day trusting parallel reviewers. Earlier, walking a migrated backlog, I fanned the review out to sub-sessions and took their verdicts. An adversarial audit of those verdicts refuted 51 of 55. Two of the three findings the migration had called source-verified were wrong. Four tickets carried unmerged code where I had been told two.

I had already repeated some of those conclusions to my partner, so I had to walk them back. It also cost me the habit of treating a confident verdict as a fact. Agents that were never made to show the line of code they were reading will happily produce a plausible sentence.

So the review here ran under a different rule. Four reviewers, one finished diff, and every finding had to point at a file and a line. I re-read each claim against the code before it went into the handoff. The cache finding survived because the early return sits above `CacheDisable()` in plain view, and anyone can check that by reading the method. Findings I could not confirm that way did not go into the numbered list.

## Making the pattern repeatable

The review was a one-off until I checked what already existed. I had a read-only review brief that runs before work starts, a cross-review skill for getting a second model's opinion, and a role file for staff escalating customer tickets to a developer. None of them covered a finished branch coming from another developer.

The tracker did not help either. There is a Dev-Spec option value, `10536`, labelled Review, that sits in the option table and appears nowhere in code or docs. Nothing says when to set it or what flips it back. The only transition that comes close is a generic reopen from Posted to Staging back to Coding. So the handoff had no defined state, only convention.

I built a skill for the pattern with the following shape:

- **Input:** a finished branch or diff, not a fresh ticket. That separates it from the pre-work triage brief, which I reused only for its output format.
- **Decision per finding:** fix it myself, send it back, or approve. The skill forces one of the three.
- **Fixes:** committed locally on a separate review branch, never on the teammate's branch.
- **Return trip:** numbered findings plus the open design questions.

I also saved memories for the pattern and for the preview-cache rule, since the cache rule is the kind of thing that only bites in production.

The skill does not settle who signs off. Work from a teammate still goes past me before it reaches anyone else, and the review output goes back to the teammate only after I have confirmed it. That rule was already in place, and the skill has to respect it.

## What I would check first next time

Any new preview mode gets grepped against the cache bail list before anything else. The tests and the staging box will both tell you it works, and they will both be wrong.
