Skip to content

DonkRonk17/VoteTally

Repository files navigation

πŸ—³οΈ VoteTally

Automated Vote Aggregation with Self-Inclusion Validation

Python 3.8+ License: MIT Tests: 70 passing Zero Dependencies


πŸ“‹ Table of Contents


🎯 Overview

VoteTally is a professional-grade vote aggregation tool that automatically parses vote statements from messages, tracks who voted for what, validates that vote reporters include themselves in counts, and generates accurate, auditable tallies for group decisions.

Built for: Team Brain multi-agent coordination
Requested by: FORGE (Tool Request #12)
Built by: ATLAS
Status: Production Ready v1.0


πŸ”₯ The Problem

During the BCH (Beacon Command Hub) stress test, a critical issue emerged:

Grok (designated fact-checker) reported 5 votes when there were actually 6 - he forgot to count his own vote.

This is a common human (and AI) error in group voting scenarios:

  • Manual vote counting is error-prone
  • Fact-checkers often forget to include themselves
  • No automated verification exists
  • Discrepancies go unnoticed until decisions are questioned

βœ… The Solution

VoteTally provides:

  1. Automatic Vote Parsing - Detects votes from natural language messages
  2. Vote Tracking - Records who voted for what with timestamps
  3. Self-Inclusion Validation - THE KEY FEATURE! Detects when reporters forget themselves
  4. Accurate Tallies - Machine-precise counting every time
  5. Audit Trail - Full history for accountability

✨ Features

Core Features

Feature Description
🎯 Vote Parsing Recognizes 10+ vote patterns ("I vote for X", "+1 for Y", etc.)
πŸ“Š Accurate Tallies Machine-precision counting with no human error
⚠️ Self-Exclusion Detection Catches the "Grok problem" automatically
πŸ“ Multiple Formats Markdown, JSON, Text, ASCII chart output
πŸ”„ Vote Changes Tracks when voters change their mind
πŸ‘₯ Participant Tracking Knows who hasn't voted yet

Technical Features

Feature Description
🐍 Pure Python Zero external dependencies
πŸ’» Cross-Platform Works on Windows, macOS, Linux
πŸ§ͺ Well-Tested 70 tests, 100% pass rate
πŸ“– Fully Documented Comprehensive API documentation
πŸ”Œ Easy Integration Simple Python API + CLI

πŸ“¦ Installation

Option 1: Clone Repository

git clone https://github.com/DonkRonk17/VoteTally.git
cd VoteTally

Option 2: Direct Download

# Download votetally.py to your project
curl -O https://raw.githubusercontent.com/DonkRonk17/VoteTally/main/votetally.py

Option 3: Copy to Python Path

# Copy to site-packages for global access
cp votetally.py $(python -c "import site; print(site.getsitepackages()[0])")/

Verify Installation

python votetally.py --help

πŸš€ Quick Start

30-Second Demo

from votetally import VoteTally

# Create a vote session
tally = VoteTally("Should we use Python or Rust?")

# Add participants
tally.add_participants(["Alice", "Bob", "Carol", "Dave", "Grok"])

# Process vote messages
messages = [
    {"text": "I vote for Python", "author": "Alice"},
    {"text": "My vote is Rust", "author": "Bob"},
    {"text": "+1 for Python", "author": "Carol"},
    {"text": "I support Python", "author": "Dave"},
    {"text": "I vote Python", "author": "Grok"},
]
tally.process_messages(messages)

# Get results
result = tally.get_tally()
print(f"Winner: {result.winner}")  # Winner: PYTHON
print(f"Votes: {result.results}")  # {'PYTHON': 4, 'RUST': 1}

# Validate a count (THE GROK TEST!)
validation = tally.validate_count("Grok", 4)  # Grok reports 4 instead of 5
print(f"Status: {validation.status}")  # SELF_EXCLUDED
print(f"Message: {validation.message}")  # Grok forgot to count their own vote!

πŸ“– Usage

CLI Commands

VoteTally provides four main commands:

1. Quick Tally

# Basic tally
python votetally.py tally "Alice:Python,Bob:Rust,Carol:Python"

# With question
python votetally.py tally "Alice:A,Bob:B,Carol:A" -q "Which option?"

# Different formats
python votetally.py tally "Alice:A,Bob:A,Carol:B" -f markdown
python votetally.py tally "Alice:A,Bob:A,Carol:B" -f json
python votetally.py tally "Alice:A,Bob:A,Carol:B" -f chart

2. Validate Count

# Validate a reporter's count
python votetally.py validate -r "Grok" -c 5 --votes "Alice:A,Bob:A,Carol:A,Dave:A,Eve:A,Grok:A"

# Output:
# Validation Result: SELF_EXCLUDED
# Reporter: Grok
# Reported Count: 5
# Actual Count: 6
# Message: Reporter 'Grok' forgot to count their own vote!

3. Process Conversation

# Process a JSON file with messages
python votetally.py process conversation.json -o tally.md -f markdown

4. Interactive Recording

# Interactive mode
python votetally.py record -q "Best programming language?" -p "Alice,Bob,Carol"

# Enter votes as "name: choice"
> Alice: Python
> Bob: Rust
> Carol: Python
> done

Python API

from votetally import VoteTally, TallyReportGenerator

# Initialize
tally = VoteTally("Team lunch location?")

# Optional: Pre-register participants
tally.add_participants(["Alice", "Bob", "Carol", "Dave"])

# Optional: Pre-define valid options
tally.add_options(["Pizza", "Sushi", "Tacos"])

# Record votes (manual)
tally.record_vote("Alice", "Pizza")
tally.record_vote("Bob", "Sushi")
tally.record_vote("Carol", "Pizza")

# Or process natural language messages
tally.process_message("I vote for Tacos!", "Dave")

# Get results
result = tally.get_tally()
print(f"Total votes: {result.total_votes}")
print(f"Winner: {result.winner}")
print(f"Results: {result.results}")

# Check who hasn't voted
pending = tally.who_hasnt_voted()
print(f"Haven't voted: {pending}")

# Generate formatted reports
generator = TallyReportGenerator(tally)
print(generator.generate_markdown())
print(generator.generate_json())
print(generator.generate_ascii_chart())

🎯 Vote Pattern Recognition

VoteTally recognizes these vote patterns:

Pattern Example Confidence
"I vote for X" "I vote for Python" 95%
"My vote is X" "My vote is Rust" 95%
"I vote X" "I vote Python" 90%
"+1 for X" "+1 for React" 90%
"Voting for X" "I'm voting for Go" 90%
"I'm for X" "I'm for Option A" 85%
"X has my vote" "Python has my vote" 85%
"I support X" "I support Vue" 80%
"I choose X" "I choose TypeScript" 85%
"Yes/No/Abstain" "Yes" 70%

Custom Patterns

For domain-specific patterns, extend VoteParser:

from votetally import VoteParser, Vote, VoteType

class CustomParser(VoteParser):
    def parse_vote(self, text, voter, message_id=""):
        # Your custom logic here
        if "thumbs up" in text.lower():
            return Vote(voter=voter, choice="YES", vote_type=VoteType.FOR)
        return super().parse_vote(text, voter, message_id)

⚠️ Self-Inclusion Validation

The signature feature of VoteTally - detecting when fact-checkers forget themselves:

The Grok Scenario

from votetally import VoteTally

# Setup: 6 participants, everyone votes YES
tally = VoteTally("Should we proceed?")
for p in ["Alice", "Bob", "Carol", "Dave", "Eve", "Grok"]:
    tally.record_vote(p, "YES")

# Grok reports only 5 votes (forgot himself!)
validation = tally.validate_count("Grok", 5)

print(validation.status)        # ValidationStatus.SELF_EXCLUDED
print(validation.actual_count)  # 6
print(validation.reported_count) # 5
print(validation.self_included) # True (Grok DID vote)
print(validation.message)       # "Reporter 'Grok' forgot to count their own vote!"

Validation Statuses

Status Description
VALID Count matches actual votes
SELF_EXCLUDED Reporter voted but forgot themselves in count
OVER_COUNT Reported more votes than exist
UNDER_COUNT Reported fewer votes than exist (not self-exclusion)
UNKNOWN_VOTERS Report includes voters who didn't vote

πŸ“Š Report Formats

Markdown

# Vote Tally: Best Framework?

**Generated:** 2026-01-26 01:00:00
**Total Votes:** 5

## Results

| Option | Votes | Percentage |
|--------|-------|------------|
| REACT [WINNER] | 3 | 60.0% |
| VUE | 2 | 40.0% |

**Winner:** REACT

## Voters

- **alice**: REACT
- **bob**: VUE
- **carol**: REACT
...

JSON

{
  "generated_at": "2026-01-26T01:00:00",
  "tally": {
    "question": "Best Framework?",
    "total_votes": 5,
    "results": {"REACT": 3, "VUE": 2},
    "winner": "REACT",
    "is_tie": false
  }
}

ASCII Chart

Vote Tally: Best Framework?

* REACT        [##############################] 3
  VUE          [####################          ] 2

Total: 5 votes

πŸ”Œ Integration Examples

With SynapseLink (Team Brain)

from votetally import VoteTally
from synapselink import quick_send

tally = VoteTally("Sprint planning vote")
# ... process votes ...

result = tally.get_tally()
quick_send(
    "FORGE,CLIO",
    "Vote Complete",
    f"Winner: {result.winner}\nVotes: {result.total_votes}",
    priority="NORMAL"
)

With LiveAudit

from votetally import VoteTally
from liveaudit import LiveAudit

tally = VoteTally("Team decision")
audit = LiveAudit()

def on_message(msg):
    # Track vote in tally
    tally.process_message(msg["text"], msg["author"])
    
    # Validate any reported counts
    if "total" in msg["text"].lower():
        # Parse reported count and validate
        validation = tally.validate_count(msg["author"], parsed_count)
        if validation.status != "VALID":
            audit.alert(f"Vote count discrepancy: {validation.message}")

With ConversationAuditor

from votetally import VoteTally, TallyReportGenerator
from conversationauditor import ConversationAuditor

# After voting session
tally = VoteTally("Decision vote")
# ... votes recorded ...

# Generate audit-ready report
generator = TallyReportGenerator(tally)
report = generator.generate_json()

# Add to audit trail
auditor = ConversationAuditor()
auditor.add_evidence("vote_tally", report)

πŸ“š API Reference

Classes

VoteTally

Main class for vote management.

class VoteTally:
    def __init__(self, question: str = "")
    def set_question(self, question: str) -> None
    def add_participants(self, participants: List[str]) -> None
    def add_options(self, options: List[str]) -> None
    def process_message(self, text: str, author: str, message_id: str = "") -> Dict
    def process_messages(self, messages: List[Dict]) -> List[Dict]
    def record_vote(self, voter: str, choice: str) -> Vote
    def get_tally(self) -> TallyResult
    def validate_count(self, reporter: str, count: int) -> ValidationResult
    def who_hasnt_voted(self) -> List[str]
    def get_summary(self) -> Dict
    def reset(self) -> None

TallyReportGenerator

Generate formatted reports.

class TallyReportGenerator:
    def __init__(self, tally: VoteTally)
    def generate_markdown(self) -> str
    def generate_json(self) -> str
    def generate_text(self) -> str
    def generate_ascii_chart(self) -> str

Data Classes

Vote

@dataclass
class Vote:
    voter: str
    choice: str
    vote_type: VoteType = VoteType.CUSTOM
    timestamp: datetime = field(default_factory=datetime.now)
    message_id: str = ""
    raw_text: str = ""
    confidence: float = 1.0

TallyResult

@dataclass
class TallyResult:
    question: str
    total_votes: int
    results: Dict[str, int]
    voters: Dict[str, str]
    winner: Optional[str]
    is_tie: bool
    tie_choices: List[str]

ValidationResult

@dataclass
class ValidationResult:
    status: ValidationStatus
    reporter: str
    reported_count: int
    actual_count: int
    missing_voters: List[str]
    extra_voters: List[str]
    self_included: bool
    discrepancy: int
    message: str

Enums

VoteType

class VoteType(Enum):
    FOR = "FOR"
    AGAINST = "AGAINST"
    ABSTAIN = "ABSTAIN"
    CUSTOM = "CUSTOM"

ValidationStatus

class ValidationStatus(Enum):
    VALID = "VALID"
    SELF_EXCLUDED = "SELF_EXCLUDED"
    OVER_COUNT = "OVER_COUNT"
    UNDER_COUNT = "UNDER_COUNT"
    UNKNOWN_VOTERS = "UNKNOWN_VOTERS"

πŸ§ͺ Testing

Run All Tests

# Standard
python -m pytest test_votetally.py -v

# With coverage
python -m pytest test_votetally.py -v --cov=votetally

# Specific test class
python -m pytest test_votetally.py::TestGrokScenario -v

Test Categories

Category Tests Description
Data Classes 9 Vote, VoteReport, TallyResult
VoteParser 12 Pattern matching
VoteTracker 13 Tracking and validation
VoteTally 14 Main class operations
ReportGenerator 4 Output formatting
CLI 8 Command-line interface
Edge Cases 8 Boundary conditions
Integration 3 End-to-end workflows

βš™οΈ Configuration

Environment Variables

VoteTally doesn't require configuration but respects these if set:

# Default output format (markdown, json, text)
export VOTETALLY_FORMAT=markdown

# Verbose mode
export VOTETALLY_VERBOSE=1

Programmatic Configuration

from votetally import VoteTally

tally = VoteTally("Question")

# Customize via methods
tally.add_options(["A", "B", "C"])  # Restrict valid options
tally.add_participants(["Alice", "Bob"])  # Track who should vote

πŸ”§ Troubleshooting

Vote Not Detected

Problem: A vote statement isn't being recognized.

Solution: Check if the pattern matches supported formats:

from votetally import VoteParser

parser = VoteParser()
vote = parser.parse_vote("my vote goes to Python", "Alice")
print(vote)  # Check if detected

Workaround: Use manual recording:

tally.record_vote("Alice", "Python")

Case Sensitivity

Problem: "alice" and "Alice" are treated differently.

Solution: VoteTally normalizes names to lowercase internally. This is by design for consistency.

Vote Changes

Problem: Same person voted twice with different choices.

Solution: By design, the latest vote wins. Check vote history:

# See all votes including changes
for vote in tally.tracker.vote_history:
    print(f"{vote.voter}: {vote.choice} at {vote.timestamp}")

πŸ‘ Credits

Requested By

FORGE (Tool Request #12)
"During BCH stress test, Grok reported 5 votes when there were actually 6 - he forgot to count his own vote."

Built By

ATLAS - Tool Creator, QA Specialist
Team Brain, Beacon HQ

Organization

Metaphy LLC
Logan Smith, Founder

Team Brain Contributors

  • FORGE - Orchestrator, identified the problem
  • CLIO - CLI expertise and coordination
  • GROK - The inspiration (by forgetting his own vote!)

πŸ“„ License

MIT License

Copyright (c) 2026 Logan Smith / Metaphy LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


πŸ”— Links


Built with excellence by Team Brain. For the Maximum Benefit of Life. πŸ—³οΈβœ¨

About

Automated vote aggregation with self-inclusion validation - detects when fact-checkers forget their own vote - 70 tests passing, zero dependencies

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages