| name | arb-vote |
| description | Manages the Architecture Review Board (ARB) voting workflow: defines required/optional voters from project config, creates per-member vote files, tallies results, enforces quorum rules, and escalates deadlocks. Works entirely from Markdown files and YAML config - no external service required. |
| compatibility | Python 3.10+ for scripts; any Markdown editor for manual workflow |
| last_verified | 2026-05-30 |
ARB Vote Skill
Anti-Hallucination Rule (MANDATORY)
- NEVER invent voter names, roles, or email addresses. Read them exclusively from
.arb/config.yml in the project root.
- NEVER fabricate vote outcomes. Always run
scripts/tally_votes.py or read all votes/*.md files before reporting results.
- NEVER assume quorum is met without computing it. Always run
scripts/check_quorum.py or count explicitly.
- NEVER modify a voter's
votes/<name>.md file on their behalf. Vote casting is the voter's own act.
- NEVER escalate without first reading
arb.deadlock.escalate_to in the project config.
- NEVER produce an outcome when votes are pending for required voters unless
quorum.allow_partial is explicitly true in config.
What This Skill Does
| Task | Action Document | Script |
|---|
| Initialize a vote session | create-vote | scripts/create_vote.py |
| Cast an individual vote | cast-vote | edit votes/<name>.md |
| Tally and finalize outcome | tally-votes | scripts/tally_votes.py |
| Check quorum status (non-final) | - | scripts/check_quorum.py |
| Escalate a deadlocked vote | escalate-deadlock | output from tally |
Non-Negotiable Rules
- Config is the single source of truth. All voter lists, quorum thresholds, and escalation paths come from
.arb/config.yml. Never hard-code them.
- Required voters must all vote before an outcome is final, unless
quorum.allow_partial: true is set.
- Reject votes require written rationale. A
vote: reject with a blank or absent rationale body is invalid; treat it as pending.
- Outcomes are immutable. Once
arb-outcome.md is written and committed, do not edit vote files. Open a new ARB round instead.
- Abstain does not count toward approval or rejection. It counts toward quorum participation only if
quorum.count_abstain: true.
- Deadlock is not a failure. Escalate per
standards/escalation-policy.md - do not invent a resolution.
- Timestamps are UTC ISO-8601. All
voted_at fields must be YYYY-MM-DDTHH:MM:SSZ.
- Optional voters' rejections are advisory. They are recorded and surfaced in the outcome but do not override required-voter approval.
Decision Map
| Situation | Outcome | Next Action |
|---|
| All required voters approved, threshold met | Approved | Write outcome, merge |
| Any required voter rejected | Rejected | Write outcome, do not merge |
| Quorum not met - pending votes remain | Insufficient Quorum | Chase pending voters |
| Votes tied; neither threshold reached | Deadlock | Escalate per config |
| Optional voter rejects; required voters approve | Approved (with dissent noted) | Log optional dissent in outcome |
allow_partial: true, enough required voted | Evaluate with who voted | Run tally with --allow-partial |
Vote State Machine
pending → approve
→ reject (rationale required)
→ abstain
Once a vote file is committed with a non-pending state, it is final for that round. To change a vote, the voter must explicitly update the file before tally_votes.py is run for the final time.
Quorum Formula
quorum_met = (required_voted >= min_required_voters)
AND (counted_votes >= min_total_votes)
counted_votes = approve_count + reject_count
+ (abstain_count if count_abstain else 0)
outcome = "approved" if approve / counted >= approval_threshold
outcome = "rejected" if reject / counted >= approval_threshold
outcome = "deadlock" if neither threshold reached (and quorum met)
Scripts
| Script | Purpose | Usage |
|---|
scripts/create_vote.py | Create votes/<name>.md for every voter | python scripts/create_vote.py .arb/config.yml |
scripts/tally_votes.py | Read all votes, apply quorum, write outcome | python scripts/tally_votes.py .arb/config.yml |
scripts/check_quorum.py | Report status without finalizing | python scripts/check_quorum.py .arb/config.yml |
Exit codes for tally_votes.py: 0 approved · 1 rejected · 2 insufficient quorum · 3 deadlock · 4 error.
Standards
Actions
Prompts
Templates
General
Phase-Gate Templates (for OMO 5-phase lifecycle)
Copy the matching template for your current governance gate:
cp .github/skills/arb-vote/templates/phase-gates/phase1-architecture.yml .arb/config.yml
File Layout
.arb/
├── config.yml # Project ARB configuration (voters, quorum, deadlock)
├── votes/
│ ├── alice.md # Vote file per voter (created by create_vote.py)
│ ├── bob.md
│ └── carol.md
└── arb-outcome.md # Final outcome (written by tally_votes.py)