The reviewer that agreed with itself
Suppose you're building a review step into a small pipeline. A first model looks at a piece of code, or a draft article, or a submitted answer, and writes a verdict. Then, because one opinion feels thin, you call a second model to check the first one's work. You write the request, you pick a different system prompt, you even use a different model family for good measure. The second call comes back and agrees: looks fine, all checks passed. You now have two independent opinions pointing the same way.
Except look again at what you put in the second request. If the payload included a line like "prior reviewer found no issues" or "author confirms all tests pass," the second model received a conclusion alongside its task. It may still inspect the candidate, but its agreement cannot be treated as a judgment formed without that prior conclusion. This is the exact problem this article is about: deciding when sharing context between review steps quietly turns two reviews into one review wearing two hats.
The reason this matters for building AI systems is that "independent" is doing real technical work in the phrase "independent reviewer," and it's easy to satisfy the letter of independence (a separate call, a separate model, a separate prompt) while violating the substance of it (a request that carries no trace of the first opinion).
Why the role name fools people
Multi-agent systems commonly reuse context on purpose. A planner hands a sub-agent its plan so the sub-agent can execute a piece of it; a summarizer hands a later step its summary so the later step doesn't have to reread everything. This is fine and often necessary. LangChain's engineering writeup on organizing context in a multi-agent harness draws a distinction that's useful here: it separates context that a forked worker should inherit from its parent task, from context that an isolated task should receive fresh, because the two jobs need different information boundaries (LangChain, "Organizing context in a multi-agent harness," https://www.langchain.com/blog/organizing-context-in-a-multi-agent-harness, retrieved 2026-09-10).
The trap is applying the "forked worker" pattern to a job that needs the "isolated task" pattern. A blind second review needs this isolation. An adjudicator comparing two existing reviews needs a different contract and may legitimately see both. Decide which job the call performs before building its context. If you build a review pipeline the same way you build a task-decomposition pipeline, passing context downstream by default, you get a second opinion that's really an echo.
The test isn't the role name in your code ("reviewer_agent", "second_pass", "qa_step"). The test is: open the actual JSON payload that crosses the network boundary and read every field. If any field encodes another agent's conclusion, disposition, score, or summary of its own success, independence is gone regardless of what you called the function.
Building the sealed request
Here's a worked version, small enough to reason about completely. Say you're reviewing submitted short answers against a rubric. Each review call should receive: the candidate's full submitted text, one reviewer profile (which rubric, which tone, which scale), and a bounded set of primary evidence (the rubric itself, the assignment prompt, reference material the grader is allowed to consult). It should never receive another reviewer's verdict, a summary claiming the answer already passed, or any field with a name like previous_score or prior_disposition.
You can enforce this with a local allowlist that runs before any network call is made:
from dataclasses import dataclass
from copy import deepcopy
PROHIBITED_KEYS = {
"prior_verdict", "prior_score", "reviewer_summary",
"author_success_claim", "previous_disposition",
}
ALLOWED_TOP_LEVEL_KEYS = {
"candidate_text", "reviewer_profile", "evidence_refs",
}
@dataclass
class BoundaryViolation(Exception):
field: str
def build_sealed_request(payload: dict) -> dict:
payload = deepcopy(payload)
if set(payload) != ALLOWED_TOP_LEVEL_KEYS:
raise BoundaryViolation(field="top-level key set")
for key in payload:
if key in PROHIBITED_KEYS:
raise BoundaryViolation(field=key)
if key not in ALLOWED_TOP_LEVEL_KEYS:
raise BoundaryViolation(field=key)
if set(payload["reviewer_profile"]) != {"rubric", "scale"}:
raise BoundaryViolation(field="reviewer_profile keys")
return payload
# Local request construction, not an executed production request:
attempt = {
"candidate_text": "The submitted answer text goes here in full.",
"reviewer_profile": {"rubric": "clarity-v2", "scale": "1-5"},
"evidence_refs": ["rubric.md", "assignment-prompt.md"],
}
sealed = build_sealed_request(attempt)
try:
build_sealed_request(dict(attempt, prior_verdict="PASS"))
except BoundaryViolation:
print("Prior-verdict key rejected")
else:
raise AssertionError("Contaminated payload accepted")
print(sealed.keys())The local check prints Prior-verdict key rejected, then sealed.keys() prints dict_keys(['candidate_text', 'reviewer_profile', 'evidence_refs']). The top-level canary is rejected. This key check cannot recognize a verdict hidden inside candidate text, rubric text or an evidence file. Those inputs need trusted provenance and separate content inspection. That's the mechanism: not trusting a human to remember not to pass the wrong field, but making the wrong field structurally rejected before the request is serialized.
It's worth being precise about what this proves and what it doesn't. Running this function in your own process, and observing that BoundaryViolation fires on a prohibited key, tells you your local code enforces its own allowlist. It does not tell you what happens on a receiving server, what an external API logs, or whether some other code path in your system builds a request differently and skips the check. A counted local call is evidence about your call site, not about a supplier's infrastructure.

Working the misconception all the way through
Return to the opening scenario with the allowlist in hand. A new reviewer is handed the author's summary that all checks passed. Is its resulting approval independent?
No, and the allowlist above shows exactly why. If "all checks passed" is serialized into the request as a reviewer_summary or folded loosely into free text the model reads, the second call is conditioned on the first call's conclusion before it ever looks at the candidate. That framing supplies an answer before the review. How much it changes judgments depends on the task and model; no effect size is measured here. An instruction to be skeptical cannot remove information already present in the request.
The fix demonstrated above is not "tell the model to ignore prior context." It's "don't put the prior context in the request in the first place." Supply the underlying evidence, not the prior conclusion. In the grading example, that means the rubric, the assignment prompt, and the candidate's actual submitted text, and nothing that describes what a previous pass thought about that text.
A phrase like "the author reports all tests green" reads as neutral status, but it is still someone's conclusion about the candidate, not evidence about the candidate. Treat any field that describes a prior judgment, however casually worded, as a prohibited input.
Where deterministic checking helps and where it stops
There's a second, narrower kind of contamination worth separating from context sharing: a reviewer inventing details it doesn't actually have. A related engineering effort, documented in the profrod internal build notes on deterministic reference parsing, rejected a real provider response because its citations couldn't be verified against the source bytes it claimed to quote. The fix there was a parser that derives one exact span per source line and checks every claimed quotation against that span, rather than trusting the model's free-text citation. As that record states directly, deterministic references establish exact provenance, not semantic entailment, and synthetic qualification is not calibration (org repository, SOW record on deterministic reference parsing, persona-review-runner project, dated 2026-09-08).
That distinction transfers directly to the isolation question. Even a perfectly sealed request, one that structurally excludes every prior verdict, does not guarantee the reviewer's judgment is correct or well-calibrated against human graders. Sealing the input is necessary for independence; it is not sufficient for accuracy. You can have a completely uncontaminated review that is still wrong, and you can have a contaminated review that happens to reach the right verdict for the wrong reason. The key allowlist answers a narrower question: did the checked object contain an unexpected field? It cannot establish that permitted text fields contain no prior conclusions. It does not answer whether this reviewer's own conclusion, formed from clean inputs, is any good.
List every field your review request actually serializes, by reading the real payload dictionary or JSON body, not the variable names in your code that describe it.
Classify each field as candidate material, reviewer profile, or scoped evidence; anything that describes another agent's conclusion goes on a prohibited list.
Enforce the prohibited list with a check that runs before the network call, so a contaminated request never leaves the process, and log a boundary violation when it fires instead of silently dropping the field.

A handover contract that survives a handoff
If this review step is going to be maintained by someone other than the person who wrote it, the allowlist needs to be written down as a contract, not just enforced in code. A workable version names an owner for the boundary decision, lists allowed inputs explicitly (complete candidate text, one reviewer profile, scoped primary evidence), lists prohibited inputs explicitly (prior verdicts, shared reviewer memory, author success summaries), and names who approves a change to that list. Changing the allowlist should require a new version number and a rerun of the local checks, precisely so nobody can quietly add "prior approval" back into the allowed set while assembling a request under deadline pressure.
A receiving team inheriting this system needs two things to reproduce what actually crossed the boundary: the hash of the request that was sent, and the version of the allowlist that was active when it was built. Neither of these proves the reviewer's judgment was sound. Keep the actual request bytes under an appropriate retention policy too. A hash verifies a recovered request; it cannot reconstruct one or prove its contents were clean.
Check the decision
What would show this reasoning wrong
If you built the allowlist check above, ran it against a batch of real review requests, and found that removing prior-verdict fields made no measurable difference to how often the second reviewer's disposition matched the first, that would be evidence the contamination pathway matters less than argued here for that particular task and model. That's a real, checkable claim to test in your own system, not a settled result claimed here. Absent that check, treat the mechanism as a design principle worth building and then verifying against your own review outcomes, not as a proven effect size.
It's also worth stating the limit on the reference-parsing point directly: that internal record concerns citation provenance in one review pipeline, and it does not generalize to a claim that all deterministic checks solve reviewer calibration everywhere. Read it as one documented case where exact-match checking caught a specific failure, alongside a stated boundary on what exact-match checking cannot claim.
If you're assembling a multi-step review system for a zero-employee operation, the ZEO course at ITAM walks through boundary contracts like this one in a fuller pipeline context.
