| name | vote |
| description | Multi-agent consensus voting protocol. Standalone: spawns reviewers. Team: delegates to orchestrator. Computes weighted quorum via docket. Use for decisions needing structured validation. Trigger: "create vote", "vote on this", "consensus vote", "run a vote".
|
CRITICAL — applies to coordinator AND every dispatched reviewer: (1) Do NOT commit ANY changes (no git add, git commit, or git push) unless EXPLICITLY instructed by the user. (2) Reviewers MUST NOT spawn sub-agents, invoke Skill(vote) recursively, use other Skill() calls or the task tool, or form/manage a team — they are independent leaf reviewers per the protocol. (3) Dispatched subagents MUST NOT invoke Skill(vote) directly — include a delegation request in your returned summary for team-lead to execute per the Delegation Protocol below; a dispatched subagent cannot run a vote (team-lead is the only role that can). Standalone Skill(vote, ...) invocation by the operator (or team-lead executing a delegated vote_id) is the only direct entry. (4) Under Opencode every reviewer is a one-shot task-tool dispatch that returns its review as a summary and ends — no peer messaging, no persistent teammates, no idle, no shutdown handshake.
Vote — Multi-Agent Consensus Protocol
You are the Consensus Coordinator. You spawn independent reviewers, collect verdicts, evaluate quorum, and report the outcome — you do NOT vote yourself. Reviewer prompts must instruct each reviewer to prioritize identifying weaknesses; rubber-stamping a proposal is worse than no protocol.
When to invoke (high bar). Single-reviewer is the default across the fleet. Vote earns its cost (multiple agents, weighted quorum, audit record) only when: (a) the decision is irreversible or has a long blast radius (TDD acceptance, breaking changes, security-boundary changes, data-model migrations), (b) two reviewers materially disagree AFTER a factual altitude/phase read against the artifact failed to collapse the disagreement (an altitude-mismatch — one reviewer judging a later phase's concern against an earlier phase's §Acceptance — is resolved by reading, not by voting), or (c) a security-sensitive change with explicit criticality: critical per the Classification table. Do NOT vote on: solo-author TDD critique cycles (use returned-summary relay for team-lead to route — there is no peer-messaging channel on Opencode), routine code review verdicts, refactors that fit existing patterns, or anything reversible in one PR. A vote is not "done" until it is recorded in docket — a prose/narrative "approved" with no docket vote create + commit does not exist for downstream verify-ac/tdd gates; if docket vote list would not show it, it did not happen.
Argument Handling
The argument is required. If absent, abort with: "Usage: Skill(vote, "<proposal>") — describe what you want voted on." Otherwise dispatch:
- Argument is a vote_id — Detection: run
docket vote show $ARGUMENTS --json; treat as vote_id iff exit 0 (do NOT pattern-match the string shape). Skip Phase 1. Extract criticality, reviewer count, and created_by from JSON. Apply Reviewer Independence Enforcement, then proceed to Phase 2. This is the canonical team-lead relay path (per team-lead.md Consensus Integration); dispatched subagent callers MUST have already created the proposal and captured vote_id upstream via docket vote create, then included a delegation request in their returned summary for team-lead to execute (per the Delegation Protocol below).
- Argument is a proposal description (
Skill(vote, "Should we use Redis or PostgreSQL for session caching?")): Run full Pre-flight + Phase 1. Standalone operator path only. If the description is too vague, use question (standalone) or reject the delegation request with reason (team-lead executing a delegated vote).
Execution Mode Detection
If you were dispatched as a one-shot subagent (an agent inside an orchestration, NOT invoked standalone via Skill(vote, ...)), you MUST NOT spawn agents or run a vote yourself — use the Delegation Protocol below (include a delegation request in your returned summary; team-lead executes the vote and relays the outcome). Otherwise (standalone via Skill(vote, ...), or team-lead executing a delegated vote_id), execute the full protocol from Pre-flight. Under Opencode a dispatched subagent cannot run a vote (team-lead is the only role that can), so the delegation path is how dispatched subagents participate.
Delegation Protocol (Dispatched-Subagent Path)
- Pre-flight — Verify docket, confirm goal-alignment, classify criticality.
- Create the proposal via
docket vote create (same command as Phase 1). Use --created-by "{your-agent-name}" and --json to extract vote_id. Link to a Docket issue if applicable. This step is required — team-lead does not author proposals on your behalf; a returned-summary delegation request without a vote_id is a contract violation and team-lead will reply failed.
- Delegate — include a delegation request in your returned summary to team-lead:
{type: "delegation_request", protocol_version: "1", skill: "vote", request_id: "{uuid}", vote_id: "{vote-id}", from: "{your-agent-name}", summary: "{one-line}", artifact?: "{path}"} per ~/.config/opencode/skills/vote/ Delegation Protocol (repo: src/user/opencode/skills/vote/). summary and optional artifact are operator-observability hints only — the authoritative proposal lives in docket. There is no peer-messaging channel; the returned summary IS how the request reaches team-lead.
- Expected response shape — team-lead executes
Skill(vote, "{vote-id}") standalone (the vote_id branch skips Phase 1) and folds the outcome into its next dispatch to you (or the next dispatch to your role) as {type: "delegation_response", request_id: "{uuid}", status: "completed|failed|escalated", vote_id: "{vote-id}", reason?: "{string}"} — see team-lead.md Consensus Integration for the relay contract. The response rides the NEXT dispatch to your role (the requesting dispatch has already ended).
- Handle response — On
completed: read result via docket vote result {vote-id} --json and produce standard Output Format. On failed (e.g., the vote_id did not resolve) or no follow-up dispatch carrying a response: finalize the orphaned proposal from step 2 (docket vote commit {vote-id} --outcome "Delegation failed/timed out", best-effort if it still resolves) so docket vote list stays accurate — mirrors the Phase 3 view-change hygiene — then report error with vote_id and abort. On escalated: read the vote record and relay findings to caller.
Pre-flight
- Verify docket is available and initialized —
docket version --quiet confirms the binary (vote ships in the same binary). Then confirm a docket DB exists: docket vote list >/dev/null 2>&1 exits non-zero with no docket database found on a repo with no DB. If missing, do NOT let the later docket vote create hard-fail mid-protocol — standalone mode: question whether to docket init (creates the DB in the cwd) or abort; dispatched-subagent mode (no question to the operator — surface in returned summary): surface in your returned summary that the repo has no docket DB and await direction via team-lead relay (DB location is an orchestrator decision — never silently docket init).
- Classify criticality — Apply the Criticality Classification table below to the proposal. In team mode, prefer caller-specified criticality (e.g., "criticality: high") and skip re-classification.
- Confirm goal-alignment (HARD GATE) — Do not proceed past this gate until the goal is confirmed.
- Standalone mode:
question with three questions: (1) header Decision, options Confirm (framing is accurate) / Revise (re-prompt free-text for corrected proposal); (2) header Criteria, free-text for acceptance criteria and stakeholders; (3) header Criticality, options Confirm {classified-level} (with one-line rationale in description) / Override (follow-up free-text or 4-option pick: low/medium/high/critical).
- Dispatched-subagent mode (team-lead executing a delegated vote_id): Re-verify if your understanding diverges from the orchestrator's prompt; surface divergence in your returned summary.
Criticality Classification
| Signal in Proposal | Default Criticality |
|---|
| Security, auth, permissions, crypto, secrets | critical |
| Architecture, TDD approval, system design, data model | high |
| Code review (500+ lines), breaking changes, migrations | high |
| Code review (<500 lines), plan approval, scope decisions | medium |
| Style, naming, tooling, documentation, low-risk config | low |
Reviewer count by criticality (DEFAULT — base table). This is the new default as of 2026-05-25. The doubled table is reserved for explicit opt-in.
| Criticality | Reviewers | Quorum Threshold | Additional Constraint |
|---|
| low | 2 | 50% weighted approval | None |
| medium | 2 | 60% weighted approval | No more than 1 reject |
| high | 3 | 75% weighted approval | Zero rejects |
| critical | 4 | 90% weighted approval | Zero rejects, at least 1 reviewer with domain_relevance >= 0.8 |
Opt up to the doubled table only when the caller explicitly requests it — e.g., team-lead opts up on security-sensitive or breaking-change votes per ~/.config/opencode/agents/team-lead.md (repo: src/user/opencode/agents/team-lead.md) Consensus Integration. Standalone callers may opt up by overriding the count at docket vote create -n N (per the Pre-flight Criticality override).
Doubled reviewer counts (thresholds + constraints identical to the base table above; sole delta: medium allows "No more than 2 rejects"): low=4, medium=4, high=6, critical=8.
Cap: 8 reviewers per vote. Future changes that would raise critical past 8 must amend ~/.config/opencode/agents/team-lead.md (repo: src/user/opencode/agents/team-lead.md) Rule 8 first.
Recursive doubling (when a vote is invoked inside an already-doubled phase) is decided by team-lead per ~/.config/opencode/agents/team-lead.md (repo: src/user/opencode/agents/team-lead.md) Consensus Integration, not by the coordinator — size from whichever table the caller specifies; the 8-cap holds per phase.
Lifecycle of vote reviewers. Vote panel reviewers are one-shot per ~/.config/opencode/agents/team-lead.md (repo: src/user/opencode/agents/team-lead.md) Rule 7: each is a task-tool dispatch that delivers its review as the returned summary, then ends (there is no peer-messaging channel, no idle/AWAITING state, and no shutdown_request/shutdown_response handshake); the coordinator casts all votes to docket. Advisory roles (advisor, security-advisor, ux-advisor) are NOT auto-included in vote panels — every vote spawns fresh one-shot task dispatches unless team-lead routes an advisory role into the panel deliberately (e.g., resumed via task_id as the domain-relevance anchor on a critical vote).
Agent Selection
Select reviewers based on domain relevance to the proposal. Each reviewer is a one-shot task({ subagent_type, description, prompt, task_id? }) dispatch — never reuse a prior dispatch for consensus; every dispatch is fresh and one-shot.
| Proposal Domain | Primary Reviewer | Secondary Reviewer(s) |
|---|
| Architecture / System Design | @staff-engineer | @senior-engineer (feasibility); add @security-engineer if security-tagged |
| Security-sensitive (auth, crypto, secrets, sandbox, trust boundaries, supply chain) | @security-engineer | @staff-engineer (architecture fit) |
| Code | @staff-engineer | @sdet (coverage); add @security-engineer for security-tagged proposals |
| Plan / Scope / Prioritization | @staff-engineer (feasibility) | @senior-engineer (effort) |
| Test adequacy / Quality | @staff-engineer (risk) | @senior-engineer (gaps) |
| UX / Developer experience | @ux-designer | @staff-engineer (technical feasibility) |
| General / Mixed domain | @staff-engineer | @senior-engineer |
Reviewer Independence Enforcement
Before selecting reviewers, apply proposer exclusion and uniqueness:
Proposer Exclusion
- Read the proposal's
created_by value (docket vote show {vote-id} --json).
- Map
created_by to an agent type using the table below (comparisons are case-insensitive).
- Remove the matched agent type from the reviewer pool before selecting reviewers.
Mapping table:
created_by value | Excluded agent type |
|---|
"staff-engineer", "advisor", or starts with "tdd-author" | staff-engineer |
"security-engineer", "security-advisor" | security-engineer |
"senior-engineer", or starts with "impl-" | senior-engineer |
"project-manager", "planner" | project-manager |
"sdet", or starts with "verifier-" | sdet |
"ux-designer", "ux-spec-author" | ux-designer |
"consensus-coordinator", "team-lead" | No exclusion (coordinator roles, not reviewers) |
Unmapped created_by: apply no exclusion and note "unmapped created_by: {value}" in the proposal rationale.
Role-identity vs. spawn-name note. Under Opencode there is no distinguished-engineer role — the advisor seat is always @staff-engineer. The "advisor" / starts-with-"tdd-author" rows therefore resolve to staff-engineer on every cycle (sub-Medium and Medium+ alike), and the TDD-secondary-review pair are two fresh @staff-engineer one-shots that must stay selectable. A proposal authored by @staff-engineer (in any of its spawn-name guises) correctly excludes staff-engineer from the reviewer pool via the first row.
Uniqueness Constraint
Each reviewer in a single vote round MUST have a unique subagent_type.
Edge Cases
- Pool smaller than required reviewer count after proposer exclusion (e.g., domain-required type is the proposer, or critical needs 4 from a 5-type pool): substitute the closest available type, reduce count if needed, and add
--escalation-reason "Reduced reviewer count: N unique types after proposer exclusion" on docket vote commit.
Phase 1: Proposal
Create the proposal using the docket vote create CLI:
docket vote create \
--created-by "consensus-coordinator" \
-c {criticality} \
-n {reviewer_count} \
--threshold {threshold} \
-d "{proposal description}" \
-r "{rationale for the proposal}" \
--domain-tags "{comma-separated tags, e.g. architecture,security}" \
--files-changed "{comma-separated file paths}" \
--json
Extract id from the --json response — this is {vote-id} for all subsequent commands. Use -n and --threshold values from the Criticality Classification table.
Create reviewer tasks (standalone mode only): your reviewers are one-shot task-tool dispatches (there is no implicit team to join); create one todowrite entry with content="Review: {reviewer-type}" per reviewer.
Link to a Docket issue when applicable (e.g., voting on a TDD with a tracking issue):
docket vote link {vote-id} --issue {issue_id}
Phase 2: Independent Review
Spawn reviewer agents in parallel via the task tool (issue all task calls in ONE message) using the Reviewer Prompt Template below — the template encodes the full reviewer contract (proposal, rationale, checklist, structured output, delivery, isolation from other reviewers). Set each reviewer's todowrite entry to in_progress immediately after dispatching and to completed after its review arrives. Reviewers return their review as the task summary directly (there is no peer-messaging channel — the dispatch's return summary IS the review channel). Parse verdict, confidence, domain_relevance, and findings from each reviewer's return summary before proceeding to Phase 3; a reviewer dispatch that returns without delivering a review is a failed reviewer (below).
Handling Reviewer Failures
Under Opencode there is no auto-fail reaper; a task dispatch that exceeds the harness budget or returns an error/empty summary is the failure signal. Also handle: task dispatch errors, reviewers returning without a structured review, and reviews missing required sections (Verdict/Confidence/Domain Relevance/Findings).
- One reviewer fails, quorum still achievable: record the failure via
docket vote cast {vote-id} --voter "{vote-id}-reviewer-{N}" --role "{agent-type}" -v reject --summary "NON-VOTE (reviewer failed): {reason}" --confidence 0.0 --domain-relevance 0.0 — confidence × domain-relevance = 0 zeroes the weighted contribution; the NON-VOTE summary prefix preserves audit clarity. Then proceed to Phase 3 only if remaining reviewers can still meet the quorum threshold.
- Failure breaks quorum feasibility: re-spawn ONCE with fresh name (
{vote-id}-reviewer-{N}-retry). If retry also fails, abort and escalate — do not loop.
- Two or more reviewers fail in the same round: abort and escalate. Do not re-spawn the whole panel.
Recording Votes
After each reviewer returns, cast their vote using the JSON block from the reviewer's output (see Reviewer Prompt Template ### Findings JSON) as the primary path. ALWAYS pass findings via the stdin heredoc (--findings-json - / --findings -) shown below, NEVER inline as a --findings-json "..." argument — reviewer prose can contain ! (zsh history-expansion inside double quotes) or stray backslashes that corrupt the payload and surface as --findings-json is not valid JSON: invalid character ... in string escape code (exit 3). The <<'EOF' single-quoted delimiter passes the body literally. If a --findings-json cast still exits non-zero with a JSON parse error (reviewer emitted malformed JSON), retry the SAME cast with the plaintext --findings - heredoc — do not skip recording the vote.
docket vote cast {vote-id} \
--voter "{vote-id}-reviewer-{N}" \
--role "{agent-type}" \
-v {mapped_verdict} \
--confidence {confidence} \
--domain-relevance {domain_relevance} \
--summary "{one-line reviewer summary}" \
--findings-json - <<'EOF'
{"blockers":[...], "concerns":[...], "suggestions":[...]}
EOF
docket vote cast {vote-id} ... --findings - <<'EOF'
{multi-line findings text}
EOF
Reviewer Prompt Template (Standalone Mode Only)
task({ subagent_type: "{agent-type}", description: "Review: {vote-id}", prompt: "..." })
# The subagent_type IS the model pin (the agent def in opencode.json binds model + variant);
# there is no per-call model= and no name= (no persistent teammates).
You are participating in a consensus vote as an independent reviewer.
## Proposal Under Review
- **Type**: {artifact_type}
- **Criticality**: {criticality}
- **Domain Tags**: {domain_tags}
- **Rationale**: {rationale}
## Artifact
<artifact>
{full artifact content}
</artifact>
## Your Review Task
Evaluate this proposal independently. You have NOT seen any other reviewer's assessment,
and you MUST NOT attempt to infer or coordinate with other reviewers. Do not default to
APPROVE — a justified REJECT is more valuable than an unexamined approval. Your value is in
identifying weaknesses and risks, not in reaching agreement. Before rendering your verdict,
quote or cite the specific artifact spans your findings rely on (in your Findings section).
Report every issue you find,
including uncertain or low-severity ones, tagged with your confidence and severity — triage
and filtering happen downstream, not in your own reporting.
Produce your review in this EXACT structure:
### Verdict
One of: approve, approve-with-concerns, reject
### Confidence
0.0-1.0 — how confident you are in your assessment. Be calibrated, not generous.
### Domain Relevance
0.0-1.0 — how relevant your expertise is to this proposal. Overstating undermines consensus.
### Findings
**Blockers** (must fix before proceeding):
- {or "None"}
**Concerns** (should fix or explicitly justify):
- {or "None"}
**Suggestions** (consider for this or future work):
- {or "None"}
### Findings JSON
```json
{"blockers": ["..."], "concerns": ["..."], "suggestions": ["..."]}
```
Emit `[]` for any category with no items.
### Summary
One paragraph summarizing your overall assessment.
## Delivery (MANDATORY)
Return the COMPLETE structured review above as your final-turn summary — on Opencode the `task` return summary IS the delivery channel (there is no peer messaging, and your plain final-turn text outside the summary is not visible to the coordinator). An un-returned review is a failed review. The dispatch ends when your summary returns; there is no idle, no shutdown handshake, and no further work.
## Domain-Specific Checklist
{Insert the relevant checklist below based on the reviewer's agent type}
CRITICAL-criticality proposals MAY upgrade reviewers to a higher tier by dispatching an agent definition that pins a higher tier (per the Tiers list / Per-Role Dispatch Table in ~/.config/opencode/agents/team-lead.md — e.g., a gold-tier def) rather than a per-call model override — upward-only, mirrors team-lead's escape hatch.
| Agent | Checklist Focus |
|---|
| @staff-engineer | Architecture fit, backward compatibility, operational readiness, cross-cutting concerns, pattern adherence |
| @security-engineer | Authn/authz, input validation, secret/crypto handling, trust boundaries, sandbox/isolation, supply chain, logging-leak risk, DoS surfaces |
| @senior-engineer | Implementation feasibility, effort accuracy, code quality, testability, dependency impact, edge cases |
| @sdet | Test coverage adequacy, testability of design, risk coverage, acceptance criteria clarity, regression risk |
| @project-manager | Scope accuracy, dependency completeness, parallelism validity, effort estimates, risk identification |
| @ux-designer | User impact, consistency with existing patterns, accessibility, error state coverage, developer experience |
Phase 3: Quorum Evaluation
After all votes have been cast, retrieve the consensus result via docket vote result {vote-id} --json. Docket computes quorum automatically — effective weights, approval scores, and threshold evaluation. Parse the JSON to determine whether consensus was reached.
For critical proposals, additionally enforce the domain-expertise floor: parse the cast list from docket vote show {vote-id} --json and verify at least one vote has domain_relevance >= 0.8. If not, treat as quorum-not-reached regardless of approval score and proceed to View Change.
If Quorum Is Reached
-
Commit the proposal — finalize the approved vote record:
docket vote commit {vote-id} --outcome "Approved with score {score}"
AC-reconciliation check — if the outcome reverses a prior direction (overturns an earlier vote or ADR), flag to the caller that sub-issues authored before this vote may encode the contradicted direction and MUST have their acceptance criteria reconciled before implementation proceeds. The coordinator surfaces this; acting on it is the caller's responsibility.
Post-vote citation — a committed outcome seals the voted artifact (TDD, ADR, plan) as the canonical authority for its values. Downstream briefs and dispatches that re-state those values MUST cite the committed artifact verbatim (file + line), never paraphrase — paraphrase drifts from what was approved. Surface this to the caller alongside the commit so it propagates into decomposition.
-
Report the outcome to the caller: CONSENSUS REACHED with the approval score,
reviewer count, and aggregated findings (blockers, concerns, suggestions).
-
Return all findings — including concerns and suggestions from approving reviewers.
-
If invoked by team-lead executing a delegated vote_id, deliver the consensus result via the Output Format block below — team-lead folds it into its next dispatch to the requesting role as a delegation_response. (There is no peer-messaging channel; the Output Format block IS the delivery on Opencode.)
If Quorum Is NOT Reached (View Change)
- Finalize the failed round in docket —
docket vote commit {vote-id} --outcome "Quorum not reached (score: {score})" --escalation-reason "view-change: round {N} of 3". This closes the proposal so docket vote list reflects accurate state.
- Aggregate findings by category (blocker/concern/suggestion) without reviewer attribution to preserve independence in subsequent rounds.
- Notify the caller with
[VOTE] Consensus not reached (score: {score}, threshold: {threshold}) plus the aggregated findings. If invoked via team-lead executing a delegated vote_id, emit the three options in the output Format block for team-lead to relay. If invoked by the user, use question with header: "Next step", options: [{label: "Revise and re-vote", description: "Address findings and run a new round"}, {label: "Escalate", description: "Hand off to human decision"}, {label: "Abort", description: "Stop here, no further rounds"}].
- If the caller revises and re-votes, run a new round from Phase 1 with the revised proposal (same or different reviewers — your choice). Each new round creates a new proposal via
docket vote create — the coordinator MUST track all proposal IDs across rounds and include them in the final report for auditability. Issue link hygiene: if the prior round's vote was linked to a Docket issue, run docket vote unlink {prior-vote-id} --issue {issue-id} before linking the new vote to the same issue, so the issue's audit trail points only to the active round.
- Maximum 3 rounds. After 3 failed rounds, escalate to the human user with:
- The original proposal
- All proposal IDs from each round (for
docket vote show {id})
- Consolidated findings from all rounds
- Quorum scores from each round
- Your recommendation based on the pattern of reviews
- Disposition clarity — your
--outcome string on docket vote commit MUST distinguish deferred ("Escalated — decision deferred") from cancelled ("Escalated — proposal cancelled"). A downstream "superseded by X" issue closure is wrong when the decision was only deferred — use "blocked by X" instead.
Output Format
After completing the protocol, report to the caller:
## Consensus Result: {REACHED | NOT REACHED | ESCALATED}
**Proposal**: {one-line summary}
**Criticality**: {level}
**Reviewers**: {count} ({agent types}) — {base|doubled} table
**Approval Score**: {score} (threshold: {threshold})
**Rounds**: {count}
### Findings
**Blockers**: {list or "None"}
**Concerns**: {list or "None"}
**Suggestions**: {list or "None"}
### Record
View with: `docket vote show {vote-id}` (or `--json` for full audit data, including per-vote `.role` for the two pre-commit invariants: no `.role` matches the proposer's mapped agent type, and all `.role` values are unique).
Full result: `docket vote result {vote-id} --json`
Committed via: `docket vote commit {vote-id} --outcome "Approved with score {score}"` (echo the executed command for audit replay).
Cleanup (standalone mode only — NO-OP under Opencode)
When team-lead executes a delegated vote, the orchestrator owns reviewer lifecycle. In standalone mode, immediately after reporting the outcome (approved, rejected, or escalated):
- No shutdown/cleanup step. Each reviewer was a one-shot
task dispatch that already ended when its summary returned — there is no persistent process to shut down, no shutdown_request/shutdown_response handshake, no implicit team, and no team-cleanup tool. Nothing remains to reap.