consensus

package
v0.577.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 6, 2026 License: AGPL-3.0 Imports: 1 Imported by: 0

Documentation

Overview

Package consensus implements ensemble voting over multi-model risk verdicts (roadmap P3-33). The motivating use-case: before a critical-risk tool fires (BadUSB execute, SubGHz TX > +10 dBm, BLE spam) run a Haiku-class classifier *and* a Sonnet-class classifier; require agreement; escalate to the operator on disagreement.

The package is deliberately tiny: it reasons over the *outputs* of per-model prospective critiques, not over the model calls themselves. Orchestration lives in the agent (which already knows how to call its providers and wire the per-tier model map). The agent invokes this package once per critical-tool dispatch.

Design notes:

  • Pure logic. No I/O, no Anthropic SDK, no goroutines. Trivially unit-testable.
  • Risk classifications are normalised to lowercase ASCII. Partial matches are rejected — `risk=ok` and `risk=okay` count as different verdicts.
  • An empty-Risk verdict is treated as "abstain" (the model failed to produce a parseable critique). If at least one non-abstain verdict exists, abstentions are excluded from the vote — the dissent payload still records them so the operator sees who abstained.
  • Unanimity is the only "passing" outcome. Two-of-three is not consensus by design; the operator should know about a split.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DisagreementMessage

func DisagreementMessage(r Result) string

DisagreementMessage produces a structured `<consensus-disagreement>` block for an agent's tool result when Vote returned Unanimous=false AND there are at least two non-abstain dissenting verdicts. Returns "" otherwise — the agent is responsible for treating "no message" as "no escalation needed".

The block includes one line per non-abstaining verdict so the operator can see exactly which model said what. Abstentions are reported as a tally rather than per-model lines because they don't carry a verdict to compare.

Types

type Result

type Result struct {
	Unanimous   bool
	AgreedRisk  string
	Verdicts    []Verdict
	Abstentions int
}

Result is the aggregated decision. Unanimous reports whether every non-abstain verdict agreed; AgreedRisk is set only when Unanimous is true. Verdicts is always the input list (preserved for reporting). Abstentions count is exposed so the operator can see when a "Unanimous" outcome was actually a one-vote consensus because the rest of the panel abstained.

func Vote

func Vote(verdicts []Verdict) Result

Vote tallies a slice of Verdicts.

Outcomes:

  • 0 verdicts: Unanimous=false, AgreedRisk="" (no input).
  • all abstain: Unanimous=false, AgreedRisk="" (no signal).
  • one non-abstain: Unanimous=true, AgreedRisk=that risk (abstentions don't block; a single voter still passes).
  • multiple agree: Unanimous=true, AgreedRisk=shared risk.
  • multiple disagree: Unanimous=false, AgreedRisk="".

type Verdict

type Verdict struct {
	Model    string
	Risk     string
	Critique string
}

Verdict is one model's risk classification. Model is the provider/ model identifier ("claude-haiku-4-5"); Risk is the classifier's graded judgement (one of `ok`, `unclear`, `risky` as defined by internal/agent's ProspectiveCritique). Critique is the raw JSON or prose the model returned, preserved so the operator-facing disagreement message can show source-of-truth excerpts on each path.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL