why it existed
Different models had different blind spots. Dvandva explored how to expose them.
Decorrelated review
The reviewer is never the same role that produced the work — the engine locks two role identities, vadi and prativadi. Running those roles on different model vendors is a recommended decorrelation policy that reduces correlated blind spots, not a structural guarantee the engine enforces.
Proof, not prose
A run cannot end because an agent says it's done. Termination demands both role-bound approvals, artifacts that exist on disk, and a verification matrix fresher than the last code change.
Bounded arguments
Every review loop is capped — a cycle can't spin forever. When the pair keeps disagreeing past the cap, that repeated edge drops; what's left is either escalation to you or the run's legal next step forward.
the loop
One baton, one writer — thesis and antithesis, emergent
Thesis and antithesis are not documents anywhere in the schema. They're emergent: positions that accrue on the baton itself — questions, findings, counters, artifacts — checkpoint by checkpoint, as vadi proposes and prativadi presses back. Whoever the baton assigns acts; the other blocks on dvandva wait. A turn ends by proposing the entire next baton, and the engine validates it against the declared workflow before installing it. Wrong owner, stale checkpoint, missing evidence, dropped peer work, illegal edge: rejected with a machine-readable reason.
the proof
Receipts first, then the mechanism that earns them
Every row below is something the engine actually enforces at dvandva write — not a design intention. Citations are file:line in rust/dvandva/src/ on the current tree.
The headline receipt
@36f134c, 2026-07-11
git log 36f134c --format=%B | grep -c '^Dvandva-Checkpoint:' # → 206, as of 36f134c (2026-07-11)
git rev-list --count 36f134c # → 400, as of 36f134c (2026-07-11)
The ratio grows with every merge, so the claim is stated as-of a fixed commit plus the exact command that reproduces it — a reader re-running this today gets a higher count, and that's expected, not a contradiction.
What the engine enforces
| Guarantee | Enforcement | Citation |
|---|---|---|
| Atomic install, monotonic checkpoints | Every candidate is validated, then installed with a single atomic rename — a corrupt or partial install can't happen mid-write. Checkpoints only ever advance by exactly +1; a candidate that repeats or skips one is rejected (exit 27, stale_checkpoint) before the transition table is even consulted. A post-install snapshot failure surfaces its own distinct exit code, never silently swallowed. | write.rs:2681-2685, 2702-2707, 1992-2005 |
| Role-bound final approvals | Final approvals check DVANDVA_ROLE — a role can raise only its own final-approval bit, and only while entering termination_review. | write.rs:1799-1829 |
| Role-bound commits | The pre-commit hook delegates to the commit gate, which blocks a commit unless the committing role is the baton's assignee or listed in active_roles. | commit_gate.rs:125-137, hooks.rs:100-121 |
| A capped loop bounds one edge — it doesn't force every disagreement to you | Each loop edge is counted; at the cap, the engine removes that repeated edge from the legal-transitions set. Human escalation remains legal, but the engine only bounds the named edge — it doesn't mandate escalation as the sole exit. | write.rs:4303-4327 |
| Custom workflow graphs are checked at approval; presets are pre-cleared | A hand-authored graph runs through five invariant checks before it's approved — one that lets done be reached without crossing a review gate FAILS validation outright (ReviewGateBypass). Built-in presets are pre-validated once, so that same runtime check is a no-op for them — both families satisfy the same contract, by different means. | write.rs:4622-4629, workflow/invariants.rs (ReviewGateBypass) |
Dual-approval done, every mode | done is legal only from termination_review, and only once both vadi_final_approval and prativadi_final_approval are true — checked independently of mode or profile. | write.rs:2079-2083 |
| No coordination daemon | No long-lived controller owns the loop. Every subcommand is a synchronous, one-shot call; the optional watchdog scan never blocks — "a monitor, not a gate." The baton plus a fencing lock is the sole coordination primitive. | main.rs:74-118, watchdog.rs:1-12, lock.rs:1-9 |
| Two role identities are engine-locked; vendor pairing is policy, not a lock | done requires two ROLE identities' independent approvals — engine-enforced. Running those two roles on two different model vendors is the recommended decorrelation policy, not something the engine checks: which engine wrote a checkpoint is recorded as trace data only, never compared for gating. | composes write.rs:1799-1829 + 2079-2083 + 2854 |
Advisory-only — read the edges precisely
- Single-writer is not a blanket "the peer can't write on your turn" rule. Only outcome-deciding fields — approvals, review ownership, workflow sign-off — plus commits are role-locked. Most fields on a candidate baton are writable by whoever holds the lock.
disagreement_capis a self-declared ceiling, read straight off the candidate's own field — not an immutability-guarded constant liketotal_phases.
State this precision as a feature, not a hedge — a technical reader trusts a pitch more, not less, for naming its own edges.
mechanics, for the curious
How the default routes actually run — regenerated from source
Most runs ride one of two declarable presets and never think about the rest. The graph a run lives under is itself a peer-reviewed, invariant-checked artifact — a custom, unapproved workflow must pass through a workflow_declaring → workflow_review loop before it can reach spec drafting; a built-in preset is pre-approved and enters spec drafting directly. Every path, regardless of mode or profile, first clears a mandatory clarifying-questions floor (at least five questions combined, both roles represented) — there is no bypass edge.
FAST skips spec planning entirely and enters compact implementation directly from an accepted research package; STANDARD keeps the locked-plan stage. Both converge on the identical phase_review ⟲ phase_fixing → termination_review → done tail. Three more presets exist beyond these two — FULL (both roles implement concurrently and cross-review each other) and dedicated RESEARCH/REVIEW-mode graphs — five total, plus custom, peer-declared graphs; all five are validated against the same five invariants sketched above.
Full transition tables, every edge, every trigger: plugins/dvandva/references/state-transition-table.md ↗
delta re-verification
Re-laps reuse narrow test evidence, never final confidence
The first full-profile pass executes every gate. After phase_fixing or cross_fixing, a mechanical test_creation track may carry only when its declared covers_chunks and carry_reason resolve to the same engine-derived tracked-file closure, its same-id origin lies on the current phase-cycle ancestry, and git-covers-diff-v1 finds no covered change. Any ambiguity, drift, untracked file, symlink, global scope, or unbounded input fails closed and creates a new test track id for rerun.
Carry is a best-effort optimization, not a cryptographic or terminal guarantee: every verification_matrix row still reruns. Legacy batons without carry fields follow the original full-rerun behavior.
archive status
Preserved for learning, closed to new work
The repository, final crate release 3.4.2, and this explainer preserve the protocol's implementation and lessons as historical research. No further protocol or product development is planned.
Installation and onboarding commands have been removed from this page because Dvandva is no longer maintained, supported, or recommended for new work. The technical sections above remain in their original explanatory voice as a record of how the final system worked.