Course / Lesson 18  ·  PT-BR
Lesson 18 · Engine & method · 5 of 8

Council & Verifier: debate, score, prove

The L2 decision engine is a layered kernel: a DebateEngine that preserves dissent, a quantitative 0–10 scoring system, an independent maker-checker Verifier, and an N-lens panel that is the T3+ emission gate. The trick that makes it trustworthy: contrarian-last and read-only verification are enforced structurally, not by prompts. Source: packages/council/src/{debate,verifier,board}.ts, governed by ADR-0003.

The DebateEngine — serial phases, parallel members

Phases run in the board's declared order (serial), and members within a phase run in parallel. Because phases are serial, a later phase receives the accumulated contributions of earlier ones. This is what makes "the contrarian sees everything and speaks last" real sequencing, not a prompt claim — the engine's own header comment says so:

// packages/council/src/debate.ts:30-44 — the engine's contract, verbatim intent
// Phases are run in the board's declared order (SERIAL between phases) and the
// members within a phase are dispatched IN PARALLEL. … The contrarian phase is
// ordered last by the board loader, so "contrarian sees everything and speaks
// last" is REAL sequencing — earlier phases have already produced their
// statements before the contrarian phase's model inputs are even built.
serial → Phase 1 member A ‖ B (parallel) Phase 2 sees phase 1 Contrarian (last) sees everything contrarian_not_last = hard board-load error aggregate weighted votes → ConsensusResult <3 valid votes ⇒ NO_GO (fail-closed) adapter calls obey never-throws: the engine branches on ModelRunResult.ok, no try/catch around run()

Adapter calls obey the never-throws invariant (lesson 14): each run() resolves to a ModelRunResult discriminated on ok, so the engine branches on the result instead of wrapping calls in try/catch. A member that fails to bind, errors, or returns an unparseable vote is recorded as a MemberFailureReason — never a thrown exception.

Scoring — 0–10, and risk is not inverted

Each member emits axis scores over SCORING_AXES (feasibility / revenue / cx / ttm / risk) weighted .25/.25/.20/.15/.15. GO_THRESHOLD = 7, PIVOT_THRESHOLD = 5. The non-obvious correctness detail: the risk axis is scored so 10 = low risk, adding in the same direction as every other axis — there is no 1 - risk normalization to get backwards:

// packages/council/src/scoring.ts — direction is uniform; axis names are one source of truth
SCORING_AXES = ['feasibility', 'revenue', 'cx', 'ttm', 'risk'];  // typo can't silently zero an axis
// high score → GO; risk 10 = low risk; NO inversion — all axes add the same way

Fail-closed quorum. aggregateConsensus returns NO_GO before consulting any score when fewer than MIN_VALID_AGENTS = 3 valid votes are present (consensus.ts:101-125). "A sparse or degraded board must never green-light." A board that lost two members to adapter failures can't accidentally pass on one optimistic vote.

The Verifier — read-only by architecture

The maker-checker Verifier is the embodiment of invariant ④ (lesson 16). It accepts only readonly views, exposes no adapter or mutation surface, and proves atomic claims with deterministic oracles over the structured evidence — never over the maker's prose:

The N-lens panel — the T3+ emission gate

For T3 and above, emission is gated by verifyPanel / isPanelEmissionApproved — three perspective-diverse lenses aggregated by quorum with a preserve-dissent veto:

LensChecks
COHERENCE= verifyDecision reused — quorum, verdict↔score, self-consistency
FAITHFULNESSevidence is present and clears confidence/strength floors
DOMAINa GO needs a validation signal; evidence is not a monoculture
Quorum AND a veto — both, on purpose

The panel approves by quorum (default 2 of 3) but a hard rejection in any single lens vetoes the whole panel (preserve-dissent), and escalate-after-N propagates. So a confident two-lens majority can still be stopped by one lens that found a fatal flaw. This conjunction — majority to pass, any-veto to block — is what the funnel's verified-GO (lesson 15) depends on. It is deliberately harder to pass than a simple vote.

1. How is "the contrarian speaks last" guaranteed?
Correct: c. Members within a phase are parallel, but phases are serial and the contrarian phase is last, so earlier statements exist before its inputs are built. The board loader rejects a board where the contrarian isn't last. Real sequencing, enforced at load — not a prompt.
2. A board produces only 2 valid votes (one member failed to bind). What's the verdict?
Correct: b. Quorum is checked first: fewer than 3 valid votes is an automatic NO_GO. A degraded board must never green-light, and the bind failure is recorded as a MemberFailureReason, not a throw.
3. Two of three panel lenses approve, but the DOMAIN lens issues a hard rejection (no validation signal for a GO). Does the panel approve emission?
Correct: d. The panel needs quorum and no hard veto. The preserve-dissent rule means one lens finding a fatal flaw blocks emission even when the others pass. This is the conjunction the funnel's verified-GO relies on.

Common confusions

"Higher risk score must mean more risk." No — in this scoring, risk = 10 means low risk, so it adds in the same direction as feasibility/revenue. There is deliberately no 1 - risk step; getting that backwards would invert every decision, so the axes are kept uniform and the axis names are a single source of truth.
"The Verifier is just another council member." No — it has no adapter and no mutation surface. It is read-only by architecture and proves claims with deterministic oracles over structured evidence, not by asking a model. That's what makes it a check, not another opinion.