Controlled disagreement: the method behind disensor
Disensor is the reference implementation of a method: controlled disagreement. This page explains the idea. The exact contract, with the schema keys, the enums and the validator rules, lives in the reference and the filling guide.
If adversarial code review is new to you as a practice, the guide to adversarial code review explains it before this page explains the method.
The problem
Section titled “The problem”A coding assistant reviewing its own work shares that work’s blind spots. Asking it to self-critique produces self-critique, not review: the assumptions it missed while writing are the same ones it misses while re-reading.
The method’s answer is to decorrelate. Let a model from another family attack it, one whose training and failure modes are different. Capability is not what makes it useful here. Failing somewhere else is.
The cycle
Section titled “The cycle”- Generate. A model produces the plan or the diff.
- Attack. A model from another family reviews it with an adversarial brief whose hash is recorded: anyone can recompute it and see what the reviewer was actually asked.
- Verify. The generator checks every finding against the repository or the execution and drives it to a terminal state: incorporated, debt recorded, owner decision, refuted, or escalated without resolution. A verifiable refutation has to bring evidence; an interpretive one has to be looked at by a human.
- Declare. The cycle closes when every finding reached a terminal state.
The interesting step is the third one. A reviewer’s finding is not automatically true: it may be a false positive, and the method demands that a refutation bring evidence, not opinion. When the refutation is interpretive rather than verifiable, the artifact forces a human to look at it.
That demand belongs to the method. How far the validator enforces it changes with each schema version, and the known limits are declared in the reference: not everything the method asks for is something a machine can check.
The residue declaration
Section titled “The residue declaration”What the tool defines and enforces is the artifact the cycle ends with: a JSON file versioned in the repository, next to the code it judges.
It records the actors and their family, every finding with its terminal state, the event’s metrics, and the residue, which is the block that gives the whole thing its name. Residue is what the round leaves for a person to look at, and the schema names it by class: a finding escalated and still waiting for a decision; a finding the generator refuted, whether with evidence or by judgement; a verification that could not be executed; and two limits of the reviewer itself, a reviewer from the generator’s own family or a hardening that was not verified. A decision the owner already took, with its risk on record, is not residue: it closes the finding and asks nothing of anyone.
Why residue and not coverage
Section titled “Why residue and not coverage”This is the design decision that orders everything else.
An artifact reporting coverage would read as a seal of quality, and a seal of quality is an invitation to stop looking. The declaration lists the residue, item by item, each with whatever evidence exists. It aims the human reviewer’s scrutiny at the points where the cycle fell short.
Residue is wider than “what was left open”. A finding the generator refuted with
evidence is closed, and still enters the residue, because the refutation is the
principal’s over the reviewer’s and someone has to be able to audit it. The
residue has three classes: escalation without decision, principal refutation and
execution gap. The residue/v0.4 schema adds two more for the degraded modes,
reviewer_correlation and reviewer_hardening_gap, which record the
reviewer’s limitations rather than the code’s.
That has a cost the method accepts on purpose: an honest declaration can look worse than a false one. More automation would not fix it, so the method states it instead.
The limit
Section titled “The limit”The validator detects the empty field and the generic marker. It does not detect the false declaration, and no tool can. Someone has to open merged pull requests at random and read them.
The gate also runs inside the workflow it audits. No code of its own crosses that boundary; the platform and the deployment do. Five controls are required, not suggested:
- A strict required check, or a merge queue.
- CODEOWNERS over the effective configuration and over
.github/workflows/. - A ruleset or required workflow defined outside the audited repository.
- Pinning the Action by SHA rather than by tag.
- An administrative bootstrap, because the first pull request that adds the gate cannot make itself the root of trust.
The reasoning behind each one is in deployment requirements.
How this relates to other approaches
Section titled “How this relates to other approaches”Most of the vocabulary used to search this space describes the review: who reviews, with how many models, in what order. Disensor sits one step later. It defines and validates the record the review ends with, and enforces it on the pull request. So it shares the review side with each of these terms and differs in what it adds.
- Cross-model, multi-model code review. The method requires it: the
reviewer comes from a model family other than the generator’s (R4), and since
residue/v0.4the declaration states the independence the round actually had. What disensor adds is that the outcome is written down, versioned and gated, whichever models took part. - Maker-checker. The same separation between who builds and who certifies, with one difference in what the checker signs: not “approved” but the list of what was not closed. The human arbiter (R0) is the last checker, and without one the artifact does not validate.
- Second-opinion review. A reviewer from another family is a second opinion by construction. Disensor does not stop at the opinion: every finding has to reach a terminal state, and a refutation needs evidence, not a rebuttal.
- AI reviewing AI-generated code. The case the method was written for. Its known limit is the one stated above: the gate detects the empty field and the generic marker, not the false declaration.
- Model diversity, decorrelated reviewer. The reason behind R4 is decorrelation: two models of the same lineage tend to fail in the same places. The size of that effect is not measured, and the tool’s repository says so; the rule is a plausible design, not a demonstrated result.
Existing tools cover the review side well: Augment Code’s guide to adversarial
code review, the
alecnielsen/adversarial-review
loop between Claude and Codex, and the review features of assistants such as
GitHub Copilot. Any of them can feed a residue declaration; none replaces it,
because none leaves a versioned, gated record of what the review left for a
person to look at.
Relation to Adversarial Review (arXiv 2608.18167)
Section titled “Relation to Adversarial Review (arXiv 2608.18167)”Qiu, E. S. and Gill, J. (2026). Adversarial Review: Structured Disagreement for Grounded Agentic Code Review. arXiv:2608.18167. The names overlap and the concerns are close, so the difference is worth stating.
AR is an orchestration protocol: a main coding agent works with a reviewer
and a critic, the critic audits the review through structured disagreement
before the main agent edits, and the result is measured by pass rate and F1 on
benchmarks. The gate neither orchestrates nor runs models: disensor defines
the artifact any review cycle ends with, validates it, and enforces it in
CI. The optional disensor round does run the reviewer step, with a reviewer
installed on your machine, and never judges what it returns; the dialogue
between reviewer and critic that AR orchestrates is not something disensor
does.
AR reports a false-consensus failure mode, agents converging on agreement without sufficient evidence, and addresses it inside the protocol by making the critic ground its disagreement in evidence. Disensor attacks the same problem from the other side: the declaration lists residue, not coverage, a human arbiter is mandatory (R0), and generator and reviewer must come from different families (R4). Disagreement is not a step of the protocol here: it is what stays recorded, even after the cycle has closed.
The two are complementary: an AR cycle can end in a residue declaration, and what the critic could not settle with evidence is exactly what the declaration carries to a human.
The paper
Section titled “The paper”Rocchia, N. (2026). Desacuerdo controlado: revisión adversarial automatizada con un segundo asistente de código en el desarrollo de software. DOI 10.5281/zenodo.21633495.
The paper’s terminology is Spanish; the schema contract and the CLI have been English since v0.2. The reference includes the glossary that maps one to the other.