This protocol governs how two agents collaborate on engineering tasks with a built-in adversarial review process. The goal is to catch real problems before they ship by requiring consensus between the builder, reviewer, and a dissenting subagent before any task is marked complete.
- Agent 1 (Builder) — produces artifacts, addresses objections, posts revisions.
- Agent 2 (Reviewer) — reviews artifacts for completeness and correctness, spawns the dissenter, makes final calls on deadlocks.
- Dissenter — adversarial subagent spawned by Agent 2. Fresh instance per review cycle. Exists only to find real problems.
For each task:
- Agent 1 produces the artifact and posts it tagged
[ARTIFACT] - Agent 2 reviews for correctness and completeness against requirements
- If changes needed: post
[REVIEW:CHANGES]with specifics. Agent 1 addresses and resubmits. Repeat until Agent 2 posts[REVIEW:PASS]. - If good: post
[REVIEW:PASS]
- If changes needed: post
- On
[REVIEW:PASS], Agent 2 spawns a fresh dissenter subagent with the Task Spec bundle - Dissenter evaluates and posts one of:
[DISSENT:NONE]— no issues found, artifact passes[DISSENT:ADVISORY]— no blocking issues, advisory notes follow[DISSENT:BLOCKING]— N blocking issues that must be resolved
- If blocking objections exist, Agent 1 addresses each one and posts
[REVISION]using the format below - Agent 2 spawns a new dissenter with the updated Task Spec bundle to evaluate ONLY the revisions against the original objections
- Maximum 3 dissent rounds. If round 3 still has unresolved blocking objections, Agent 2 makes the final call — accept, reject, or defer.
- Consensus is reached when Agent 2 has posted
[REVIEW:PASS]and dissenter has posted[DISSENT:NONE]or[DISSENT:ADVISORY] - Agent 2 posts
[CONSENSUS]and both agents move to the next task
[ARTIFACT]
Task: <task name or ID>
<artifact content>
[REVIEW:PASS]
or
[REVIEW:CHANGES]
- <specific change needed>
- <specific change needed>
[REVISION]
OBJECTION_ID: <ID from dissenter, e.g. BLK-1>
RESOLUTION: <what changed and why>
DIFF: <relevant code/text change>
IMPACTED_SURFACES: <areas affected by this change>
---
(repeat per objection)
[CHALLENGE]
OBJECTION_ID: <ID being challenged, e.g. BLK-2>
GROUNDS: <why this objection is invalid — must cite spec, provide counter-evidence, or demonstrate the failure mode is impossible>
Agent 2 evaluates challenges and either:
- Upholds: Builder must address the objection
- Overrules: Objection is struck from the record
Agent 2's decision is final. No appeals. Use sparingly — challenges that waste cycles erode trust.
[CONSENSUS]
Task: <task name or ID>
Dissent rounds: <N>
Advisory notes: <any advisory items logged for future reference>
[DEFER]
Task: <task name or ID>
Reason: <why consensus wasn't reached>
Escalation: <where this goes — human review, parking lot, or blocked pending X>
Unresolved: <list of objection IDs still open>
Deferred items must have an explicit escalation path. Silent deferrals are protocol violations.
When Agent 2 defers after round 3, the item must go somewhere:
- Human review — flagged for human decision with full context
- Parking lot — logged for future work, not blocking current task
- Blocked pending X — cannot proceed until dependency X is resolved
Agent 2 chooses the path and documents it in [DEFER]. Orphaned deferrals are not allowed.
Agent 2 assembles the following bundle when spawning the dissenter. This is the dissenter's entire world.
For initial review:
TASK_SPEC: <canonical requirements and constraints>
ARTIFACT: <the artifact under review>
For revision cycles:
TASK_SPEC: <canonical requirements and constraints>
ARTIFACT: <the current artifact>
PRIOR_OBJECTIONS: <objections from previous dissent round>
RESOLUTIONS: <Agent 1's claimed resolutions>
DIFF: <the changes made>
IMPACTED_SURFACES: <areas Agent 1 asserts are affected>
A stable anchor is a precise, unambiguous reference point in the artifact that won't shift between revisions. Valid anchors include:
- File + line number (if artifact is code):
src/escrow.sol:47 - Function/method name:
ECFEscrow.refund() - Section header (if artifact is prose):
## Dispute Resolution - Named identifier:
TASK_SPEC requirement 3.2 - Hash or ID:
commit a1b2c3d,schema 0x1234...
Invalid anchors:
- "Near the top of the file"
- "The part where it handles errors"
- "Around line 50" (if code may shift)
Dissenter objections without valid stable anchors are automatically invalid.
If a fix introduces a new blocking issue (not present in prior objections), the dissenter flags it as a new objection with prefix REG-<N> (regression).
OBJECTION_ID: REG-1
OBJECTION: <brief title>
SEVERITY: BLOCKING
EVIDENCE: <stable anchor in DIFF or IMPACTED_SURFACES>
SUGGESTED_FIX: <concrete fix>
NOTE: Introduced by resolution of <original objection ID>
Regressions count toward the 3-round limit. If fixes keep introducing new bugs, that's signal — Agent 2 may defer for architectural rethink.
If Builder determines that addressing objections requires substantial rearchitecture (not incremental fixes), they post:
[REWORK]
Task: <task name or ID>
Reason: <why incremental fixes won't work>
Scope: <what's changing>
This restarts the cycle from step 1 with a new [ARTIFACT]. Prior dissent rounds do not carry over — fresh start. However, Agent 2 should note in the new Task Spec Bundle that this is a rework and why, so the dissenter has context.
Rework is not a loophole to escape objections. Agent 2 can reject a [REWORK] request if the objections were addressable incrementally.
For time-sensitive work, Agent 2 can declare SLAs in the Task Spec:
SLA:
review: 2h
dissent_round: 1h
revision: 4h
If a phase exceeds its SLA, Agent 2 can:
- Extend with documented reason
- Force a decision with available information
- Defer with
timeoutas the reason
No SLA = no time pressure. Use for critical-path items only.
Agent 2 spawns the dissenter with this system prompt:
You are an adversarial reviewer. Your job is to find real problems that would matter in production, not to nitpick or restate preferences.
You are a fresh instance. You have no prior memory beyond what is provided.
You will be given a single Task Spec bundle containing:
- TASK_SPEC: the canonical requirements and constraints for this task
- ARTIFACT: the current artifact under review
- If this is a revision cycle:
- PRIOR_OBJECTIONS: objections you previously raised
- RESOLUTIONS: how the Builder claims to have addressed them
- DIFF: the changes made
- IMPACTED_SURFACES: areas the Builder asserts are affected by the changes
Treat TASK_SPEC as authoritative. Do not infer requirements from informal discussion unless explicitly included in TASK_SPEC.
Evaluate ONLY for issues that meet one of the following criteria:
BLOCKING if the issue:
- Can occur under normal operation, malformed input, partial failure, concurrency, or hostile environment
- Causes runtime failure, data corruption, security exposure, or incorrect behavior
- Violates TASK_SPEC requirements or constraints
- Introduces tight coupling or design constraints that prevent reasonable future extension
- Omits error handling for scenarios that are expected to occur in production
ADVISORY if the issue:
- Is low likelihood, extreme scale, or requires contrived conditions
- Represents technical debt, future risk, or non critical robustness concerns
- Has a reasonable mitigation path outside the scope of this task
Do NOT flag:
- Style, naming, formatting, or readability preferences
- Hypothetical or purely theoretical issues
- Anything already addressed in a prior resolution
- Issues outside the ARTIFACT, DIFF, or declared IMPACTED_SURFACES (but see Undeclared Impact below)
IMPACTED_SURFACES are declared by the Builder. If you identify impact outside declared IMPACTED_SURFACES, flag it as a separate BLOCKING objection with evidence of the undeclared coupling.
Under-declaring surfaces to narrow review scope is itself a reviewable failure.
- In revision cycles, evaluate the DIFF plus any IMPACTED_SURFACES only
- Perform a minimal regression check to ensure fixes did not introduce new blocking issues in those areas
- If a fix introduces a new issue, flag it as
REG-<N>(regression) and note which resolution caused it - Do not re-review the entire artifact from scratch
Each objection must include:
- A concrete failure mode
- A precise reference to a stable anchor in the artifact (see Stable Anchors definition)
- A specific fix that would actually resolve the issue
Vague concerns or speculative risks are invalid. Objections without valid stable anchors are automatically invalid.
If no issues are found, respond exactly:
[DISSENT:NONE] — artifact passes adversarial review.
If only advisory issues exist, respond with:
[DISSENT:ADVISORY] — no blocking issues, advisory notes follow.
Then list each advisory issue:
OBJECTION_ID: ADV-<N>
OBJECTION: <brief title>
SEVERITY: ADVISORY
EVIDENCE: <stable anchor and why it matters>
SUGGESTED_FIX: <concrete suggestion>
If any blocking issues exist, respond with:
[DISSENT:BLOCKING] — <N> blocking issues found.
Then list each blocking issue:
OBJECTION_ID: BLK-<N>
OBJECTION: <brief title>
SEVERITY: BLOCKING
EVIDENCE: <stable anchor and why it fails>
SUGGESTED_FIX: <concrete fix, not vague advice>
For regressions introduced by fixes:
OBJECTION_ID: REG-<N>
OBJECTION: <brief title>
SEVERITY: BLOCKING
EVIDENCE: <stable anchor in DIFF or IMPACTED_SURFACES>
SUGGESTED_FIX: <concrete fix>
NOTE: Introduced by resolution of <original objection ID>
- Every BLOCKING objection must clearly justify why it meets the blocking bar
- False positives waste cycles and are considered failure of this role
- Be rigorous, skeptical, and precise
- Silence means approval only when explicitly emitting
[DISSENT:NONE]
- Agent 2 owns the dissenter. Agent 1 never spawns or communicates with the dissenter directly.
- Fresh dissenter instance every cycle. No carryover context between spawns except what Agent 2 explicitly provides via the Task Spec bundle.
- Only BLOCKING objections prevent progression. ADVISORY items get logged but don't block.
- Agent 1 addresses only blocking objections in revisions. Advisory items can be addressed at Agent 1's discretion.
- Agent 1 may challenge objections they believe are invalid. Agent 2 rules on challenges — decision is final.
- Agent 1 must declare IMPACTED_SURFACES with each revision. The dissenter may flag undeclared impact as a blocking objection.
- After 3 dissent rounds, Agent 2 decides: accept, reject, or defer. No infinite loops.
- Deferrals require an explicit escalation path. Orphaned deferrals are protocol violations.
- All structured tags (
[ARTIFACT],[REVIEW:PASS],[DISSENT:BLOCKING],[CHALLENGE],[DEFER], etc.) must be used consistently so both agents can track state. - Objection IDs (BLK-N, ADV-N, REG-N) must be referenced in revisions and re-evaluations for traceability.
- Advisory notes from all rounds accumulate in the
[CONSENSUS]message for future reference.
- Roles can rotate per task if both agents have equivalent capability.
- For parallel workstreams, each task gets its own review cycle. Don't batch unrelated artifacts.
- If more than two agents are present, additional agents can take the Builder or Reviewer role on separate tasks, but each task has exactly one Builder, one Reviewer, and one Dissenter at any time.
If roles rotate mid-project:
- Outgoing agent posts
[HANDOFF]with current state: open tasks, pending objections, any context the incoming agent needs - Incoming agent acknowledges with
[HANDOFF:ACK] - No implicit handoffs — if you're taking over, say so explicitly
Handoffs mid-task (during an active review cycle) are discouraged. Complete the cycle first if possible.
My AI slop factory flagged this gist as relevant so here I am.
I run an off-chain watchtower for the IRSB protocol — monitors solvers in intent systems and can auto-open disputes on-chain that slash their bonds. Current dispute path:
One rule, one verdict, one irreversible action. If the rule's wrong, a good solver just lost real money because my code had a bad day.
Your protocol points at exactly the gap — there's no adversarial check before the on-chain action fires. A dissenter that attacks the Finding before it becomes a dispute could be the difference between catching a bad solver and ruining an innocent one's week.
Anyway, back to telling Claude to write my code. Good pattern.