Documentation
¶
Overview ¶
Package consilium owns the deterministic consilium engine for the `specscore` CLI: the vote-schema types and validator, the roster resolver and validator, the gate-knob and roster config loaders for the `consilium:` block in specscore.yaml, and the gate-rule arbiter that turns a panel's votes into a deterministic verdict.
See `spec/features/cli/consilium/README.md` for the full Feature contract.
This file currently scopes the package to the Vote type and its validator (REQ:vote-schema-types-and-validation). The gate engine, roster resolution, and config loaders land in follow-on tasks.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ValidateRoster ¶
func ValidateRoster(roster []RosterEntry) error
ValidateRoster enforces REQ:roster-validation over an already-resolved roster (defaults − exclude ∪ custom): each of the three groups (builders/customers/adversaries) MUST have ≥1 member; the total roster MUST be ≤12; no custom name may collide with a default slug (case-insensitive); and every custom entry (one carrying a Path) MUST resolve to a markdown file conforming to the custom-role contract. The first violation is returned as a single clear error naming the specific violation; a valid roster returns nil.
Types ¶
type Denominators ¶
Denominators holds the per-group counts used as the consensus denominators, after high-confidence abstain exclusion (step 2).
type GateKnobs ¶
type GateKnobs struct {
AdversaryVetoConfidence string // high | medium | low
CostCeiling string // low | medium
ComplexityCeiling string // low | medium
MinMedianConfidence string // low | medium | high
RequireAllBuilders bool
RequireAllCustomers bool
}
GateKnobs is the resolved set of the six consilium gate knobs the arbiter applies (REQ:gate-knob-config-schema). Every field is populated: a value supplied by config, otherwise the strict baseline.
func LoadGate ¶
LoadGate reads <projectRoot>/specscore.yaml and resolves the gate knobs: the strict baseline merged knob-by-knob with any `consilium.gate` mapping. When the file does not exist, or has no `consilium.gate` mapping, the strict baseline is returned. An unknown knob key or an out-of-enum value fails with an error naming the key path (e.g. `consilium.gate.cost_ceiling`), the offending value, and the accepted enum.
func ResolveGate ¶
ResolveGate resolves the effective gate knobs the arbiter applies: the specscore.yaml-resolved baseline (LoadGate) merged knob-by-knob with an optional `--gate` override file. The override file is a YAML document with a top-level `gate:` mapping of knobs (the shape `consilium config --print-gate` emits and accepts verbatim). An empty overridePath skips the override step. Override knobs are validated with the same enum rules; violations name the override key path (e.g. `gate.cost_ceiling`).
func StrictBaseline ¶
func StrictBaseline() GateKnobs
StrictBaseline returns the strict-baseline gate knobs applied knob-by-knob when a knob is absent from config.
type Group ¶
type Group string
Group is one of the three fixed roster groups. The values double as the accepted enum for a custom role's `group:` key.
type Result ¶
type Result struct {
Verdict Verdict
RuleTrace []string
ExcludedVotes []string
Denominators Denominators
}
Result is the deterministic output of the gate engine. RuleTrace lists the rule names of the steps that fired, in evaluation order, ending in the terminal-verdict rule. ExcludedVotes lists the role slugs excluded in step 2. Denominators are the per-group counts after exclusion.
func Evaluate ¶
func Evaluate(votes []Vote, roster []RosterEntry, knobs GateKnobs) Result
Evaluate runs the gate algorithm (REQ:gate-engine-rule-order) over the votes against the roster and knobs, producing a deterministic Result. It is a pure function: no clock, no randomness, no I/O.
Steps run in order: step 1 computes denominators; step 2 excludes high-confidence abstain votes (decrementing denominators and recording ExcludedVotes); step 3 caps the verdict at needs-human-review on any low/medium-confidence abstain (low-abstain-veto); step 4 fires adversary-veto; steps 5–13 run the approval-count and median gates.
func (Result) Marshal ¶
Marshal renders the Result as a deterministic YAML document. The encoding is a pure function of the Result's values: fixed key order (verdict, rule_trace, excluded_votes, denominators), nil slices normalized to empty lists, and no clock/random/map-iteration input. The same Result therefore yields byte-identical output across repeated calls, runs, and processes — the property the snapshot suite gates on (AC:gate-engine-deterministic).
type RosterEntry ¶
RosterEntry is one resolved roster role: its slug name, its group, and (for custom roles only) the repo-root-relative path to its markdown file. Default roles carry an empty Path.
func ParseCustomRole ¶
func ParseCustomRole(path string) (RosterEntry, error)
ParseCustomRole reads the markdown file at path and validates it against REQ:custom-role-markdown-contract: body-metadata `**Name:**` (matching the filename without `.md`), `**Group:**` in the three-value enum, `**Output Schema Version:** 1`; a `## Role Prompt` H2; and a `## Example Vote` H2 carrying one valid vote (parsed via ParseVotes). On success it returns the role as a RosterEntry whose Path is the input path. Any violation yields a single error naming the missing/invalid field AND the file path.
func ResolveRoster ¶
func ResolveRoster(projectRoot string) ([]RosterEntry, error)
ResolveRoster loads <projectRoot>/specscore.yaml, parses the `consilium.roster` block, and resolves the active roster as (defaults − exclude) ∪ custom, preserving group membership. The result is an ordered list of entries: the surviving defaults in declared order, followed by custom roles in declared order. When the config file or the roster block is absent, the full 9-role default set is returned.
Resolution intentionally does NOT perform roster validation (group floors, ≤12 cap, name collisions, custom-file parsing); that is a separate task that consumes this surface.
type ValidationError ¶
type ValidationError struct {
// Index is the zero-based position of the offending vote in the bundle.
Index int
// Role is the offending vote's role slug when the entry carried one; "".
Role string
// Field is the offending field name (e.g. "verdict", "argument").
Field string
// Rule is a human-readable description of the rule that was violated.
Rule string
}
ValidationError is the deterministic error returned by vote validation. Its message names the offending vote (by index, and role when present) and the rule violated, so the malformed-bundle ACs can string-match on both.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
Error formats the validation error as `vote[<i>]<(role=<r>)>: <field>: <rule>`. The shape is stable so tests can string-match on it.
type Verdict ¶
type Verdict string
Verdict is one of the three terminal consilium outcomes (REQ:gate-engine-rule-order).
type Vote ¶
type Vote struct {
Verdict string `yaml:"verdict"`
Confidence string `yaml:"confidence"`
Cost string `yaml:"cost"`
Complexity string `yaml:"complexity"`
Argument string `yaml:"argument"`
// Role is optional metadata used only to make validation errors more
// legible; it is not one of the five schema fields.
Role string `yaml:"role,omitempty"`
}
Vote is one expert-panel vote. It carries exactly the five fields defined by REQ:vote-schema-types-and-validation. Role is not part of the vote schema itself; when a votes-file entry carries a `role:` key it is retained so validation errors can name the offending vote by role as well as by index.
func ParseVotes ¶
ParseVotes parses a YAML votes bundle (a list of votes) and validates every entry. On success it returns one Vote per entry with fields populated verbatim. Tolerance for malformed votes is zero: a YAML-parse failure, a missing required field, an out-of-enum verdict/confidence/cost/complexity, or an argument longer than 280 characters fails the entire bundle with a *ValidationError (or a parse error) naming the offending vote and rule. A malformed bundle never yields a partial result.