Skip to content
disensor.devv0.9.4

CLI · GitHub Action · JSON artifact

Your AI wrote the code. Another AI reviewed it.

What happened in that review ends up as a JSON file in your repo, next to the code it judges. disensor is the CLI and the GitHub Action that check it: against the schema, against its rules, and against the change the pull request actually makes.

Install

pip install disensor

init writes four things into your repo. A config file, a CI workflow, and, for Claude Code, a skill and an instruction, so the record gets written as each round closes. Any other agent gets the same guide from disensor guide. The gate runs no model and holds no API key: it validates what is already in the repository. The round does run one, and you pick which: disensor round executes the reviewer installed on your machine and anchors what came out of it. Either way you need a human arbiter: without one the artifact does not validate.

§ 01

That file is called a residue declaration. It lists residue instead of coverage. The method page explains what that means in practice, and why it reads nothing like a green badge.

What controlled disagreement is

§ 02

Controlled disagreement

Within one event the cycle is linear: the reviewer attacks once. disensor stays out of that cycle. It defines the artifact the cycle ends with, and checks it.

01family A

Generate

the plan or the diff

A model produces the plan or the diff to be merged.

02another family

Attack

one or more reviewers (R4)

A model from another family reviews it with an adversarial brief. Two models of the same lineage fail in the same places, which is why the family has to differ.

03family A

Verify

against the repository or the execution

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.

04

Declare

closes the cycle, not the problem

The cycle closes when every finding reached a terminal state. Whatever did not close stays written, versioned with the commit. Your assistant writes that file, not you.

escalated_open, refuted_interpretive

Human arbiter

Required in every event. Without one the declaration does not comply with the protocol.

R0
fix_verification: pending_in_diff_gateIf what was reviewed was the plan, the fix does not exist yet and there is nowhere to verify it. It stays pending for the diff event, which has its own declaration.

§ 03

The artifact

A JSON file versioned in the repository, next to the code it judges. It records the actors, their family and how independent the reviewer actually was, every finding with its terminal state, and the event’s metrics. A round that could not reach another model family is recorded as the degraded mode it is, and carries its own residue item.

The block to read is residue. In this real event, the 3 findings all reached a terminal state, and it still left 3 residue items, one per class. Residue is whatever rests on someone’s judgement, including the findings that closed.

The gate runs no model and holds no model API key: it validates a file that is already in the repository. Running the review is a separate step, and that one does hand your diff to a model from another family; since 0.9 disensor round orchestrates it with the reviewer installed on your machine.

The counts add up. The residue still has 3 items.
total_findings3
incorporated1
debt_recorded0
owner_decision0
refuted_verifiable1
refuted_interpretive0
escalated_open1
residue.items3
spec/examples/example_2_diff_gate.jsonv0.9.4
"residue": {
  "items": [
    {
      "id": "r1",
      "class": "escalation_without_decision",
      "finding_ref": "h2",
      "requires_human_attention": true,
      "description": "El limite de filas de la exportacion quedo escalado a producto y sin resolver al cierre del ciclo."
    },
    {
      "id": "r2",
      "class": "principal_refutation",
      "finding_ref": "h3",
      "refutation_type": "verifiable",
      "requires_human_attention": false,
      "description": "Refutacion con enlace al objeto de consulta compartido, para que el revisor humano pueda no darla por buena.",
      "evidence": {
        "text": "La validacion vive en el objeto de consulta compartido reutilizado por la exportacion.",
        "link": "src/consultas/RangoFechas.cs#L41-L58"
      }
    },
    {
      "id": "r3",
      "class": "execution_gap",
      "requires_human_attention": true,
      "gap_reason": "environment_not_reproducible",
      "description": "El comportamiento bajo concurrencia real no se probo porque el entorno de desarrollo no la reproduce."
    }
  ]
}
Excerpt from a real event, anonymised. It is the example the tool ships with. See the whole artifact

§ 04

What the gate enforces

A GitHub Action that validates the declarations the pull request adds, applies the repository’s scope policy and posts the result as a comment.

It fails closed
If the gate cannot resolve the pull request range, it does not go green: it could not read the change, so it has nothing to approve.
Evidence is append-only
A pull request cannot modify, delete or rename declarations that were already there, nor reuse an existing event identifier.
A stale declaration covers nothing
If the path changed after the reviewed commit, the declaration stops covering it. Reviewing and then writing more does not count.
Everything comes from git
Scope is derived from the objects in the reviewed range, never from the working tree: reading from disk would classify one tree and validate another.

§ 05

What it does not catch

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.

It also does not protect against a workflow that was modified, skipped or replaced. That part is on the platform: strict required checks, CODEOWNERS, and pinning the Action by SHA.

What the artifact demands and what it cannot yet check changes with each schema version. The known limits are declared in the reference.

Try it without touching your CI

The gate runs the same on your machine and says exactly what it would say in CI. Only when you want it to enforce do you write the workflow.

pip install disensor && disensor init --no-workflow