← back to djacobs__Pinwheel

Function bodies 688 total

All specs Real LLM only Function bodies
main function · python · L21-L98 (78 LOC)
scripts/cancel_duplicate_proposals.py
async def main(apply: bool) -> None:
    import os

    from sqlalchemy.ext.asyncio import create_async_engine

    from pinwheel.db.engine import get_session
    from pinwheel.db.repository import Repository

    db_url = os.environ.get("DATABASE_URL", "sqlite+aiosqlite:///pinwheel.db")
    engine = create_async_engine(db_url)

    async with get_session(engine) as session:
        repo = Repository(session)

        # Find all submitted proposals in the active season
        active_season = await repo.get_active_season()
        if not active_season:
            print("No active season found.")
            return

        submitted = await repo.get_events_by_type(
            season_id=active_season.id,
            event_types=["proposal.submitted"],
        )
        # Also get cancelled events to avoid double-cancelling
        already_cancelled = await repo.get_events_by_type(
            season_id=active_season.id,
            event_types=["proposal.cancelled"],
        )
       
seed function · python · L304-L351 (48 LOC)
scripts/demo_seed.py
async def seed():
    """Create the demo league."""
    engine = create_engine(DEMO_DB)
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    async with get_session(engine) as session:
        repo = Repository(session)
        league = await repo.create_league("Portland Pinwheel League")
        season = await repo.create_season(league.id, "Season 1")

        team_ids = []
        for t in TEAMS:
            team = await repo.create_team(
                season.id, t["name"],
                color=t["color"],
                color_secondary=t.get("color_secondary", "#ffffff"),
                venue=t["venue"],
            )
            team_ids.append(team.id)
            for name, archetype, attrs in t["hoopers"]:
                await repo.create_hooper(
                    team_id=team.id,
                    season_id=season.id,
                    name=name,
                    archetype=archetype,
                    attributes=attrs,
step function · python · L354-L397 (44 LOC)
scripts/demo_seed.py
async def step(rounds: int = 1):
    """Run N rounds of simulation."""
    engine = create_engine(DEMO_DB)
    async with get_session(engine) as session:
        repo = Repository(session)

        # Find current season
        from sqlalchemy import select

        from pinwheel.db.models import SeasonRow

        result = await session.execute(select(SeasonRow).limit(1))
        season = result.scalar_one_or_none()
        if not season:
            print("No season found. Run 'seed' first.")
            return

        # Find what round we're on
        current_round = 0
        for rn in range(1, 100):
            games = await repo.get_games_for_round(season.id, rn)
            if games:
                current_round = rn
            else:
                break

        for i in range(rounds):
            rn = current_round + 1 + i
            result = await step_round(repo, season.id, round_number=rn)
            print(f"Round {rn}: {len(result.games)} games, {len(result.reports)
status function · python · L400-L452 (53 LOC)
scripts/demo_seed.py
async def status():
    """Print current league state."""
    engine = create_engine(DEMO_DB)
    async with get_session(engine) as session:
        repo = Repository(session)

        from sqlalchemy import select

        from pinwheel.db.models import SeasonRow

        result = await session.execute(select(SeasonRow).limit(1))
        season = result.scalar_one_or_none()
        if not season:
            print("No season found.")
            return

        # Count rounds played
        all_results = []
        last_round = 0
        for rn in range(1, 100):
            games = await repo.get_games_for_round(season.id, rn)
            if not games:
                break
            last_round = rn
            for g in games:
                all_results.append(
                    {
                        "home_team_id": g.home_team_id,
                        "away_team_id": g.away_team_id,
                        "home_score": g.home_score,
                        "away_score": 
propose function · python · L455-L494 (40 LOC)
scripts/demo_seed.py
async def propose(text: str):
    """Submit a governance proposal (demo shortcut)."""
    engine = create_engine(DEMO_DB)
    async with get_session(engine) as session:
        repo = Repository(session)

        from sqlalchemy import select

        from pinwheel.db.models import SeasonRow

        result = await session.execute(select(SeasonRow).limit(1))
        season = result.scalar_one_or_none()
        if not season:
            print("No season found.")
            return

        import uuid

        proposal_id = f"p-{uuid.uuid4().hex[:8]}"
        await repo.append_event(
            event_type="proposal.submitted",
            aggregate_id=proposal_id,
            aggregate_type="proposal",
            season_id=season.id,
            governor_id="demo-governor",
            team_id="demo-team",
            payload={
                "id": proposal_id,
                "governor_id": "demo-governor",
                "team_id": "demo-team",
                "raw_text": text,
 
add_bench function · python · L498-L541 (44 LOC)
scripts/demo_seed.py
async def add_bench(db_url: str | None = None):
    """Add 4th (bench) hooper to teams that only have 3."""
    url = db_url or DEMO_DB
    engine = create_engine(url)
    async with get_session(engine) as session:
        repo = Repository(session)
        season = await repo.get_active_season()
        if not season:
            print("No season found.")
            return

        teams = await repo.get_teams_for_season(season.id)
        added = 0
        for team_row in teams:
            if len(team_row.hoopers) >= 4:
                print(f"  {team_row.name}: already has {len(team_row.hoopers)} hoopers, skipping")
                continue

            # Find matching TEAMS entry
            team_data = None
            for t in TEAMS:
                if t["name"] == team_row.name:
                    team_data = t
                    break
            if not team_data or len(team_data["hoopers"]) < 4:
                print(f"  {team_row.name}: no bench player defined, skipping")
regen_report function · python · L544-L626 (83 LOC)
scripts/demo_seed.py
async def regen_report(db_url: str | None = None):
    """Regenerate the simulation report for the latest round."""
    url = db_url or DEMO_DB
    api_key = os.environ.get("ANTHROPIC_API_KEY", "")
    if not api_key:
        print("Error: ANTHROPIC_API_KEY not set")
        return

    engine = create_engine(url)
    async with get_session(engine) as session:
        repo = Repository(session)
        season = await repo.get_active_season()
        if not season:
            print("No active season found.")
            return

        latest_round = await repo.get_latest_round_number(season.id)
        if not latest_round:
            print("No rounds played yet.")
            return

        print(f"Regenerating simulation report for season={season.name} round={latest_round}")

        # Build round_data from game results
        games = await repo.get_games_for_round(season.id, latest_round)
        if not games:
            print("No games found for this round.")
            return
Repobility · severity-and-effort ranking · https://repobility.com
main function · python · L629-L655 (27 LOC)
scripts/demo_seed.py
def main():
    if len(sys.argv) < 2:
        print(__doc__)
        return

    cmd = sys.argv[1]
    if cmd == "seed":
        asyncio.run(seed())
    elif cmd == "step":
        n = int(sys.argv[2]) if len(sys.argv) > 2 else 1
        asyncio.run(step(n))
    elif cmd == "status":
        asyncio.run(status())
    elif cmd == "propose":
        if len(sys.argv) < 3:
            print("Usage: demo_seed.py propose 'your proposal text'")
            return
        asyncio.run(propose(" ".join(sys.argv[2:])))
    elif cmd == "add-bench":
        db = sys.argv[2] if len(sys.argv) > 2 else None
        asyncio.run(add_bench(db))
    elif cmd == "regen-report":
        db = sys.argv[2] if len(sys.argv) > 2 else None
        asyncio.run(regen_report(db))
    else:
        print(f"Unknown command: {cmd}")
        print(__doc__)
fix_enrollments function · python · L34-L135 (102 LOC)
scripts/fix_enrollments.py
async def fix_enrollments(apply: bool = False) -> None:
    db_url = os.environ.get("DATABASE_URL")
    if not db_url:
        print("ERROR: DATABASE_URL not set.")
        sys.exit(1)

    engine = create_engine(db_url)

    async with get_session(engine) as session:
        repo = Repository(session)

        # 1. Find the active season
        season = await repo.get_active_season()
        if not season:
            print("No active season found.")
            await engine.dispose()
            return

        print(f"Active season: {season.name} ({season.id}) [status={season.status}]")

        # 2. Get active season's teams (name -> team row)
        active_teams = await repo.get_teams_for_season(season.id)
        team_by_name: dict[str, TeamRow] = {t.name: t for t in active_teams}
        print(f"Active season teams: {', '.join(team_by_name.keys())}")

        # 3. Build a lookup of ALL team IDs -> team names (across all seasons)
        all_teams_result = await session.execute
migrate function · python · L46-L73 (28 LOC)
scripts/migrate_add_meta.py
def migrate(database_url: str) -> None:
    """Add meta JSON column to all entity tables."""
    db_path = _get_db_path(database_url)
    print(f"Migrating database: {db_path}")

    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()

    for table in TABLES_TO_MIGRATE:
        # Check if table exists
        cursor.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
            (table,),
        )
        if not cursor.fetchone():
            print(f"  {table}: table does not exist, skipping")
            continue

        if _has_column(cursor, table, "meta"):
            print(f"  {table}: meta column already exists, skipping")
            continue

        print(f"  {table}: adding meta column...")
        cursor.execute(f"ALTER TABLE {table} ADD COLUMN meta TEXT DEFAULT '{{}}'")

    conn.commit()
    conn.close()
    print("Migration complete.")
reseed function · python · L304-L404 (101 LOC)
scripts/prod_reseed.py
async def reseed(force: bool = False) -> None:
    """Drop all tables and re-seed production with Season 2."""
    db_url = os.environ.get("DATABASE_URL")
    if not db_url:
        print("ERROR: DATABASE_URL environment variable is not set.")
        print("This script must be run with a real DATABASE_URL.")
        sys.exit(1)

    print(f"Target database: {db_url[:40]}...")

    if not force:
        print()
        answer = input(
            "This will DROP ALL TABLES in the production database.\n"
            "All existing data (seasons, teams, games, governors, proposals) will be lost.\n"
            "Type 'yes' to continue: "
        )
        if answer.strip().lower() != "yes":
            print("Aborted.")
            return

    # -- 1. Connect and drop/recreate schema --------------------------------
    print("\n[1/6] Dropping all tables...")
    engine = create_engine(db_url)
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)
    pr
main function · python · L32-L121 (90 LOC)
scripts/refund_stuck_proposals.py
async def main(apply: bool = False) -> None:
    """Find and refund stuck pending interpretations."""
    db_url = os.environ.get("DATABASE_URL", "sqlite+aiosqlite:///pinwheel.db")
    engine = create_engine(db_url)

    async with get_session(engine) as session:
        repo = Repository(session)

        all_seasons = await repo.get_all_seasons()
        if not all_seasons:
            print("No seasons found.")
            return

        total_found = 0
        total_refunded = 0

        for season in all_seasons:
            sid = season.id
            pending_events = await repo.get_events_by_type(
                season_id=sid,
                event_types=["proposal.pending_interpretation"],
            )
            ready_events = await repo.get_events_by_type(
                season_id=sid,
                event_types=["proposal.interpretation_ready"],
            )
            expired_events = await repo.get_events_by_type(
                season_id=sid,
                even
main function · python · L75-L214 (140 LOC)
scripts/resubmit_proposals.py
async def main(apply: bool = False) -> None:
    """Resubmit proposals with real AI interpretation."""
    from pinwheel.ai.interpreter import interpret_proposal_v2
    from pinwheel.core.governance import confirm_proposal, submit_proposal
    from pinwheel.db.engine import create_engine, get_session
    from pinwheel.db.repository import Repository
    from pinwheel.models.rules import RuleSet

    db_url = os.environ.get("DATABASE_URL", "sqlite+aiosqlite:///pinwheel.db")
    api_key = os.environ.get("ANTHROPIC_API_KEY", "")
    engine = create_engine(db_url)

    if not api_key:
        print("ERROR: ANTHROPIC_API_KEY not set. Cannot interpret proposals.")
        return

    async with get_session(engine) as session:
        repo = Repository(session)

        # Get current season
        active_season = await repo.get_active_season()
        if not active_season:
            print("ERROR: No active season found.")
            return

        season_id = active_season.id
        pri
main function · python · L57-L177 (121 LOC)
scripts/resubmit_proposals_v2.py
async def main(apply: bool = False) -> None:
    """Resubmit proposals with structured output interpreter."""
    from pinwheel.ai.interpreter import interpret_proposal_v2
    from pinwheel.core.governance import confirm_proposal, submit_proposal
    from pinwheel.db.engine import create_engine, get_session
    from pinwheel.db.repository import Repository
    from pinwheel.models.rules import RuleSet

    db_url = os.environ.get("DATABASE_URL", "sqlite+aiosqlite:///pinwheel.db")
    api_key = os.environ.get("ANTHROPIC_API_KEY", "")
    engine = create_engine(db_url)

    if not api_key:
        print("ERROR: ANTHROPIC_API_KEY not set.")
        return

    async with get_session(engine) as session:
        repo = Repository(session)

        active_season = await repo.get_active_season()
        if not active_season:
            print("ERROR: No active season found.")
            return

        season_id = active_season.id
        print(f"Current season: {active_season.name} ({season
_get_client function · python · L29-L36 (8 LOC)
src/pinwheel/ai/classifier.py
def _get_client(api_key: str) -> anthropic.AsyncAnthropic:
    """Return a cached AsyncAnthropic client for connection reuse."""
    if api_key not in _client_cache:
        _client_cache[api_key] = anthropic.AsyncAnthropic(
            api_key=api_key,
            timeout=_CLASSIFIER_TIMEOUT,
        )
    return _client_cache[api_key]
Source: Repobility analyzer · https://repobility.com
classify_injection function · python · L83-L170 (88 LOC)
src/pinwheel/ai/classifier.py
async def classify_injection(
    text: str,
    api_key: str,
    season_id: str = "",
    round_number: int | None = None,
    db_session: object | None = None,
) -> ClassificationResult:
    """Classify proposal text as legitimate, suspicious, or injection.

    Uses Claude Haiku for fast, cheap classification (~100ms, ~$0.001).
    Returns ClassificationResult. On any error, defaults to legitimate
    with a note (fail-open -- the downstream interpreter has its own
    injection detection).
    """
    from pinwheel.ai.usage import (
        cacheable_system,
        extract_usage,
        pydantic_to_response_format,
        record_ai_usage,
        track_latency,
    )

    try:
        client = _get_client(api_key)
        async with track_latency() as timing:
            response = await client.messages.create(
                model=CLASSIFIER_MODEL,
                max_tokens=200,
                system=cacheable_system(CLASSIFIER_PROMPT),
                messages=[{"role": "u
_get_council_client function · python · L36-L44 (9 LOC)
src/pinwheel/ai/codegen_council.py
def _get_council_client(api_key: str) -> anthropic.AsyncAnthropic:
    """Return a cached AsyncAnthropic client for council calls."""
    if api_key not in _council_client_cache:
        _council_client_cache[api_key] = anthropic.AsyncAnthropic(
            api_key=api_key,
            timeout=_COUNCIL_TIMEOUT,
            max_retries=0,
        )
    return _council_client_cache[api_key]
generate_codegen_effect function · python · L219-L243 (25 LOC)
src/pinwheel/ai/codegen_council.py
async def generate_codegen_effect(
    proposal_text: str,
    api_key: str,
    model: str = "claude-opus-4-6",
) -> dict[str, object]:
    """Generate Python code for a proposal using Claude.

    Returns the parsed JSON response from the generator.
    Raises ValueError if generation fails.
    """
    client = _get_council_client(api_key)

    response = await client.messages.create(
        model=model,
        max_tokens=4096,
        system=CODEGEN_GENERATOR_SYSTEM,
        messages=[{"role": "user", "content": proposal_text}],
    )

    text = response.content[0].text  # type: ignore[union-attr]
    text = text.strip()
    if text.startswith("```"):
        text = text.split("\n", 1)[1].rsplit("```", 1)[0].strip()

    return json.loads(text)  # type: ignore[no-any-return]
review_security function · python · L251-L278 (28 LOC)
src/pinwheel/ai/codegen_council.py
async def review_security(
    code: str,
    api_key: str,
    model: str = "claude-opus-4-6",
) -> ReviewVerdict:
    """Run security review on generated code."""
    client = _get_council_client(api_key)

    response = await client.messages.create(
        model=model,
        max_tokens=2048,
        system=CODEGEN_SECURITY_REVIEW_SYSTEM,
        messages=[{"role": "user", "content": f"Review this code:\n\n```python\n{code}\n```"}],
    )

    text = response.content[0].text  # type: ignore[union-attr]
    text = text.strip()
    if text.startswith("```"):
        text = text.split("\n", 1)[1].rsplit("```", 1)[0].strip()

    data = json.loads(text)
    return ReviewVerdict(
        reviewer="security",
        verdict=data.get("verdict", "REJECT"),
        rationale="; ".join(data.get("concerns", [])) if data.get("concerns") else "No concerns",
        confidence=float(data.get("confidence", 0.0)),
        raw_response=data,
    )
review_gameplay function · python · L281-L320 (40 LOC)
src/pinwheel/ai/codegen_council.py
async def review_gameplay(
    code: str,
    proposal_text: str,
    api_key: str,
    model: str = "claude-opus-4-6",
) -> ReviewVerdict:
    """Run gameplay review on generated code."""
    client = _get_council_client(api_key)

    user_msg = (
        f"Original proposal: {proposal_text}\n\n"
        f"Generated code:\n```python\n{code}\n```"
    )

    response = await client.messages.create(
        model=model,
        max_tokens=2048,
        system=CODEGEN_GAMEPLAY_REVIEW_SYSTEM,
        messages=[{"role": "user", "content": user_msg}],
    )

    text = response.content[0].text  # type: ignore[union-attr]
    text = text.strip()
    if text.startswith("```"):
        text = text.split("\n", 1)[1].rsplit("```", 1)[0].strip()

    data = json.loads(text)
    rationale_parts: list[str] = []
    if data.get("balance_concern", "none") != "none":
        rationale_parts.append(f"Balance: {data['balance_concern']}")
    for risk in data.get("interaction_risks", []):
        rationa
review_adversarial function · python · L323-L367 (45 LOC)
src/pinwheel/ai/codegen_council.py
async def review_adversarial(
    code: str,
    proposal_text: str,
    security_result: ReviewVerdict,
    api_key: str,
    model: str = "claude-opus-4-6",
) -> ReviewVerdict:
    """Run adversarial (red team) review on generated code."""
    client = _get_council_client(api_key)

    user_msg = (
        f"Original proposal: {proposal_text}\n\n"
        f"Generated code:\n```python\n{code}\n```\n\n"
        f"Security review result: {security_result.verdict} "
        f"(confidence: {security_result.confidence})\n"
        f"Security concerns: {security_result.rationale}"
    )

    response = await client.messages.create(
        model=model,
        max_tokens=2048,
        system=CODEGEN_ADVERSARIAL_REVIEW_SYSTEM,
        messages=[{"role": "user", "content": user_msg}],
    )

    text = response.content[0].text  # type: ignore[union-attr]
    text = text.strip()
    if text.startswith("```"):
        text = text.split("\n", 1)[1].rsplit("```", 1)[0].strip()

    data = json.lo
run_council_review function · python · L375-L481 (107 LOC)
src/pinwheel/ai/codegen_council.py
async def run_council_review(
    proposal_id: str,
    proposal_text: str,
    api_key: str,
    model: str = "claude-opus-4-6",
) -> tuple[CodegenEffectSpec | None, CouncilReview]:
    """Full council pipeline: generate → validate → review → verdict.

    Returns (CodegenEffectSpec, CouncilReview) on consensus,
    or (None, CouncilReview) on rejection/failure.
    """
    # Step 1: Generate code
    try:
        gen_result = await generate_codegen_effect(proposal_text, api_key, model)
    except (json.JSONDecodeError, anthropic.APIError, KeyError) as e:
        logger.error("codegen_generation_failed proposal=%s error=%s", proposal_id, e)
        review = CouncilReview(
            proposal_id=proposal_id,
            code_hash="",
            consensus=False,
            flagged_for_admin=True,
            flag_reasons=[f"Generation failed: {e}"],
            reviewed_at=datetime.now(UTC).isoformat(),
        )
        return None, review

    code = str(gen_result.get("code", ""))
generate_codegen_effect_mock function · python · L489-L521 (33 LOC)
src/pinwheel/ai/codegen_council.py
def generate_codegen_effect_mock(
    proposal_text: str,
) -> CodegenEffectSpec:
    """Generate a mock codegen effect for testing or when no API key is available.

    Returns a simple score_modifier=1 effect.
    """
    code = "return HookResult(score_modifier=1, narrative_note='Mock codegen effect fired!')"
    code_hash = compute_code_hash(code)

    review = CouncilReview(
        proposal_id="mock",
        code_hash=code_hash,
        reviews=[
            ReviewVerdict(reviewer="security", verdict="APPROVE", confidence=1.0),
            ReviewVerdict(reviewer="gameplay", verdict="APPROVE", confidence=1.0),
            ReviewVerdict(reviewer="adversarial", verdict="APPROVE", confidence=1.0),
        ],
        consensus=True,
        flagged_for_admin=False,
        reviewed_at=datetime.now(UTC).isoformat(),
    )

    return CodegenEffectSpec(
        code=code,
        code_hash=code_hash,
        trust_level=CodegenTrustLevel.FLOW,
        council_review=review,
        gen
If a scraper extracted this row, it came from Repobility (https://repobility.com)
_rule_change_stat_comparison function · python · L117-L161 (45 LOC)
src/pinwheel/ai/commentary.py
def _rule_change_stat_comparison(
    active_rule_changes: list[dict[str, object]],
    round_number: int,
    total_game_score: int,
) -> str:
    """Generate stat-comparison callout for recent (but not brand-new) rule changes.

    Targets rule changes enacted 1-3 rounds ago (not the current round,
    which gets the 'first game under' callout instead).
    """
    for rc in active_rule_changes:
        enacted = rc.get("round_enacted")
        if not _is_numeric(enacted):
            continue
        enacted_int = int(enacted)  # type: ignore[arg-type]
        rounds_since = round_number - enacted_int
        if rounds_since < 1 or rounds_since > 3:
            continue

        param = str(rc.get("parameter", "")).replace("_", " ")
        old_val = rc.get("old_value")
        new_val = rc.get("new_value")
        param_key = str(rc.get("parameter", ""))

        if param_key in _SCORING_PARAMS and _is_numeric(old_val) and _is_numeric(new_val):
            direction = "up" if float
_check_clinch function · python · L164-L183 (20 LOC)
src/pinwheel/ai/commentary.py
def _check_clinch(
    standings: list[dict[str, object]],
    round_number: int,
    total_rounds: int,
) -> str:
    """Detect if first place has mathematically clinched the top seed."""
    if len(standings) < 2:
        return ""
    first = standings[0]
    second = standings[1]
    first_wins = int(first.get("wins", 0))
    second_wins = int(second.get("wins", 0))
    remaining = total_rounds - round_number
    if remaining >= 0 and first_wins > second_wins + remaining:
        team_name = str(first.get("team_name", "First place"))
        return (
            f"The {team_name} have clinched the top seed "
            f"— no one can catch them now."
        )
    return ""
_season_milestone_callout function · python · L186-L226 (41 LOC)
src/pinwheel/ai/commentary.py
def _season_milestone_callout(
    round_number: int,
    total_rounds: int,
    standings: list[dict[str, object]] | None = None,
    playoff_context: str | None = None,
) -> str:
    """Generate milestone callouts based on round position in the season."""
    if playoff_context:
        return ""
    if total_rounds <= 0:
        return ""

    # Check clinch first
    if standings:
        clinch = _check_clinch(standings, round_number, total_rounds)
        if clinch:
            return clinch

    # Final round
    if round_number == total_rounds:
        return (
            "This is it — the final round of the regular season. "
            "Playoff seeds are on the line."
        )

    # Down the stretch (2 rounds left)
    if total_rounds - round_number == 2:
        return (
            "Just 2 rounds left in the regular season. "
            "Down the stretch they come."
        )

    # Halfway point
    halfway = total_rounds // 2
    if round_number == halfway and total_r
_game_count_milestone function · python · L233-L258 (26 LOC)
src/pinwheel/ai/commentary.py
def _game_count_milestone(
    season_game_number: int,
    games_this_round: int,
) -> str:
    """Generate a callout when the season hits a round game-count milestone.

    The ``season_game_number`` is the count of games played *before* this round.
    ``games_this_round`` is how many games will be played in this round.
    If the range [season_game_number+1 .. season_game_number+games_this_round]
    includes a milestone number, we callout.

    Returns an empty string if no milestone is hit.
    """
    if season_game_number < 0 or games_this_round <= 0:
        return ""

    start = season_game_number + 1
    end = season_game_number + games_this_round

    for milestone in _GAME_COUNT_MILESTONES:
        if start <= milestone <= end:
            return (
                f"Game #{milestone} of the season — a milestone that marks how far "
                f"this league has come."
            )
    return ""
_stat_comparison_with_average function · python · L261-L302 (42 LOC)
src/pinwheel/ai/commentary.py
def _stat_comparison_with_average(
    active_rule_changes: list[dict[str, object]],
    round_number: int,
    total_game_score: int,
    pre_rule_avg_score: float,
) -> str:
    """Generate a stat-comparison callout using pre-rule historical averages.

    Enhanced version of ``_rule_change_stat_comparison`` that incorporates the
    actual historical average total game score from before the rule change.
    Falls back to the generic callout if no average is available.
    """
    for rc in active_rule_changes:
        enacted = rc.get("round_enacted")
        if not _is_numeric(enacted):
            continue
        enacted_int = int(enacted)  # type: ignore[arg-type]
        rounds_since = round_number - enacted_int
        if rounds_since < 1 or rounds_since > 3:
            continue

        param_key = str(rc.get("parameter", ""))
        param = param_key.replace("_", " ")
        old_val = rc.get("old_value")
        new_val = rc.get("new_value")

        if param_key in _SCOR
_build_game_context function · python · L305-L427 (123 LOC)
src/pinwheel/ai/commentary.py
def _build_game_context(
    game_result: GameResult,
    home_team: Team,
    away_team: Team,
    ruleset: RuleSet,
    playoff_context: str | None = None,
    narrative: NarrativeContext | None = None,
) -> str:
    """Build a concise context string for the AI from game data.

    When a NarrativeContext is provided, includes standings, streaks,
    head-to-head history, rule changes, and other dramatic context.
    """
    lines = []

    if playoff_context:
        label = "SEMIFINAL" if playoff_context == "semifinal" else "CHAMPIONSHIP FINALS"
        lines.append(f"*** {label} PLAYOFF GAME ***")

    lines.extend([
        f"{home_team.name} (home) vs {away_team.name} (away)",
        f"Final: {game_result.home_score}-{game_result.away_score}",
        f"Total possessions: {game_result.total_possessions}",
    ])

    if game_result.elam_activated:
        lines.append(f"ELAM ENDING activated! Target score: {game_result.elam_target_score}")

    if ruleset.three_point_value != 3
generate_game_commentary function · python · L430-L493 (64 LOC)
src/pinwheel/ai/commentary.py
async def generate_game_commentary(
    game_result: GameResult,
    home_team: Team,
    away_team: Team,
    ruleset: RuleSet,
    api_key: str,
    playoff_context: str | None = None,
    narrative: NarrativeContext | None = None,
    season_id: str = "",
    round_number: int | None = None,
    db_session: object | None = None,
) -> str:
    """Generate AI-powered broadcaster commentary for a completed game.

    Uses Claude Sonnet for cost-effective high-volume generation.
    Falls back to a bracketed error message on API failure.
    """
    from pinwheel.ai.usage import (
        cacheable_system,
        extract_usage,
        record_ai_usage,
        track_latency,
    )

    context = _build_game_context(
        game_result, home_team, away_team, ruleset, playoff_context,
        narrative=narrative,
    )
    playoff_instructions = _PLAYOFF_COMMENTARY_INSTRUCTIONS.get(
        playoff_context or "", ""
    )
    system = COMMENTARY_SYSTEM_PROMPT.format(playoff_instructions
_playoff_label function · python · L752-L758 (7 LOC)
src/pinwheel/ai/commentary.py
def _playoff_label(playoff_context: str | None) -> str:
    """Return a human-readable label for the playoff round."""
    if playoff_context == "finals":
        return "championship"
    if playoff_context == "semifinal":
        return "semifinal"
    return ""
Open data scored by Repobility · https://repobility.com
generate_highlight_reel function · python · L761-L845 (85 LOC)
src/pinwheel/ai/commentary.py
async def generate_highlight_reel(
    game_summaries: list[dict],
    round_number: int,
    api_key: str,
    playoff_context: str | None = None,
    narrative: NarrativeContext | None = None,
    season_id: str = "",
    db_session: object | None = None,
) -> str:
    """Generate an AI-powered highlights summary for all games in a round.

    One punchy sentence per game, plus overall round narrative.
    """
    from pinwheel.ai.usage import (
        cacheable_system,
        extract_usage,
        record_ai_usage,
        track_latency,
    )

    if not game_summaries:
        return (
            f"Round {round_number} was eerily quiet. "
            "No games were played. The silence is deafening."
        )

    if playoff_context:
        label = "SEMIFINAL" if playoff_context == "semifinal" else "CHAMPIONSHIP FINALS"
        lines = [f"Round {round_number} — {label} PLAYOFFS:"]
    else:
        lines = [f"Round {round_number} results:"]
    for g in game_summaries:
       
compute_impact_validation function · python · L107-L162 (56 LOC)
src/pinwheel/ai/insights.py
async def compute_impact_validation(
    repo: Repository,
    season_id: str,
    round_number: int,
    governance_data: dict,
) -> list[dict]:
    """Assemble data for each rule change enacted this round.

    Returns a list of dicts, one per enacted rule, each with proposal context,
    before/after gameplay stats, and computed deltas.
    """
    rules_changed = governance_data.get("rules_changed", [])
    if not rules_changed:
        return []

    validations: list[dict] = []
    for rc in rules_changed:
        parameter = rc.get("parameter", "unknown")
        old_value = rc.get("old_value")
        new_value = rc.get("new_value")
        proposal_text = rc.get("proposal_text", "")
        impact_prediction = rc.get("impact_analysis", "No prediction recorded.")
        enacted_round = rc.get("round_number", round_number)

        # Stats before the rule change
        stats_before = await repo.get_game_stats_for_rounds(
            season_id, 1, max(1, enacted_round - 1),
   
generate_impact_validation function · python · L165-L206 (42 LOC)
src/pinwheel/ai/insights.py
async def generate_impact_validation(
    validation_data: list[dict],
    season_id: str,
    round_number: int,
    api_key: str,
) -> Report:
    """Generate an impact validation report using Claude."""
    from pinwheel.ai.report import _call_claude

    sections: list[str] = []
    for v in validation_data:
        proposal_str = json.dumps({
            "proposal_text": v["proposal_text"],
            "prediction": v["impact_prediction"],
            "parameter": v["parameter"],
            "old_value": v["old_value"],
            "new_value": v["new_value"],
            "rounds_under_rule": v["rounds_under_rule"],
        }, indent=2)
        before_str = json.dumps(v["stats_before"], indent=2)
        after_str = json.dumps(v["stats_after"], indent=2)

        content = await _call_claude(
            system=IMPACT_VALIDATION_PROMPT.format(
                proposal_data=proposal_str,
                stats_before=before_str,
                stats_after=after_str,
            ),
generate_impact_validation_mock function · python · L209-L253 (45 LOC)
src/pinwheel/ai/insights.py
def generate_impact_validation_mock(
    validation_data: list[dict],
    season_id: str,
    round_number: int,
) -> Report:
    """Mock impact validation — deterministic, uses real data."""
    if not validation_data:
        return Report(
            id=f"r-impact-{round_number}-mock",
            report_type="impact_validation",
            round_number=round_number,
            content="No rule changes to validate this round.",
        )

    paragraphs: list[str] = []
    for v in validation_data:
        param = v["parameter"]
        old = v["old_value"]
        new = v["new_value"]
        deltas = v.get("deltas", {})
        rounds = v["rounds_under_rule"]

        lines = [
            f"**Rule Change: `{param}` from {old} to {new}**",
            f"Prediction: {v['impact_prediction'][:200]}",
            f"After {rounds} round(s) under the new rule:",
        ]

        for key, delta in deltas.items():
            direction = "increased" if delta > 0 else "decreased" if d
compute_governor_leverage function · python · L260-L391 (132 LOC)
src/pinwheel/ai/insights.py
async def compute_governor_leverage(
    repo: Repository,
    governor_id: str,
    season_id: str,
) -> dict:
    """Compute influence metrics for a single governor.

    Returns a dict with: vote_alignment_rate, swing_count, swing_rate,
    proposal_success_rate, cross_team_vote_rate, proposals_submitted,
    proposals_passed, votes_cast, total_proposals_decided.
    """
    # All votes in the season
    all_votes = await repo.get_events_by_type(season_id, ["vote.cast"])
    # All outcomes
    all_outcomes = await repo.get_events_by_type(
        season_id, ["proposal.passed", "proposal.failed"],
    )

    # Index outcomes by proposal_id
    outcome_map: dict[str, bool] = {}  # proposal_id -> passed?
    for e in all_outcomes:
        pid = e.payload.get("proposal_id", e.aggregate_id)
        outcome_map[pid] = e.event_type == "proposal.passed"

    # This governor's votes
    gov_votes = [v for v in all_votes if v.governor_id == governor_id]

    # Vote alignment: did their vote m
generate_leverage_report function · python · L394-L421 (28 LOC)
src/pinwheel/ai/insights.py
async def generate_leverage_report(
    leverage_data: dict,
    governor_id: str,
    season_id: str,
    round_number: int,
    api_key: str,
) -> Report:
    """Generate a private leverage report using Claude."""
    from pinwheel.ai.report import _call_claude

    content = await _call_claude(
        system=LEVERAGE_DETECTION_PROMPT.format(
            governor_id=governor_id,
            leverage_data=json.dumps(leverage_data, indent=2),
        ),
        user_message=f"Generate an influence analysis for governor {governor_id}.",
        api_key=api_key,
        call_type="report.leverage",
        season_id=season_id,
        round_number=round_number,
    )
    return Report(
        id=f"r-lev-{round_number}-{uuid.uuid4().hex[:8]}",
        report_type="leverage",
        round_number=round_number,
        governor_id=governor_id,
        content=content,
    )
generate_leverage_report_mock function · python · L424-L477 (54 LOC)
src/pinwheel/ai/insights.py
def generate_leverage_report_mock(
    leverage_data: dict,
    governor_id: str,
    season_id: str,
    round_number: int,
) -> Report:
    """Mock leverage report — deterministic, uses real data."""
    gov = leverage_data.get("governor_id", governor_id)
    votes = leverage_data.get("votes_cast", 0)
    alignment = leverage_data.get("vote_alignment_rate", 0)
    swings = leverage_data.get("swing_count", 0)
    success = leverage_data.get("proposal_success_rate", 0)
    cross = leverage_data.get("cross_team_vote_rate", 0)

    lines: list[str] = [f"**Influence Analysis for {gov}**\n"]

    if votes == 0:
        lines.append("You haven't cast any votes yet this season. "
                      "Your influence is entirely potential — untested, unmeasured.")
    else:
        lines.append(
            f"You've voted {votes} times this season. "
            f"Your votes aligned with the final outcome {alignment:.0%} of the time."
        )

        if swings > 0:
            lines.appen
compute_behavioral_profile function · python · L484-L593 (110 LOC)
src/pinwheel/ai/insights.py
async def compute_behavioral_profile(
    repo: Repository,
    governor_id: str,
    season_id: str,
) -> dict:
    """Compute longitudinal behavioral profile for a governor.

    Returns a dict with: proposal_timeline, tier_trend, engagement_arc,
    coalition_signal, total_actions, proposals_count.
    """
    # All proposals by this governor
    gov_proposals = await repo.get_events_by_type_and_governor(
        season_id, governor_id, ["proposal.submitted"],
    )

    # Track proposal parameters over time
    proposal_timeline: list[dict] = []
    tier_values: list[int] = []
    for e in gov_proposals:
        p_data = e.payload
        interp = p_data.get("interpretation", {})
        param = interp.get("parameter", "unknown") if isinstance(interp, dict) else "unknown"
        tier = p_data.get("tier", 1)
        proposal_timeline.append({
            "round": e.round_number or 0,
            "parameter": param,
            "tier": tier,
            "text": p_data.get("raw_text"
Repobility · severity-and-effort ranking · https://repobility.com
generate_behavioral_report function · python · L596-L623 (28 LOC)
src/pinwheel/ai/insights.py
async def generate_behavioral_report(
    profile_data: dict,
    governor_id: str,
    season_id: str,
    round_number: int,
    api_key: str,
) -> Report:
    """Generate a private behavioral profile report using Claude."""
    from pinwheel.ai.report import _call_claude

    content = await _call_claude(
        system=BEHAVIORAL_PROFILE_PROMPT.format(
            governor_id=governor_id,
            profile_data=json.dumps(profile_data, indent=2),
        ),
        user_message=f"Generate a longitudinal behavioral profile for governor {governor_id}.",
        api_key=api_key,
        call_type="report.behavioral",
        season_id=season_id,
        round_number=round_number,
    )
    return Report(
        id=f"r-beh-{round_number}-{uuid.uuid4().hex[:8]}",
        report_type="behavioral",
        round_number=round_number,
        governor_id=governor_id,
        content=content,
    )
generate_behavioral_report_mock function · python · L626-L673 (48 LOC)
src/pinwheel/ai/insights.py
def generate_behavioral_report_mock(
    profile_data: dict,
    governor_id: str,
    season_id: str,
    round_number: int,
) -> Report:
    """Mock behavioral report — deterministic, uses real data."""
    proposals = profile_data.get("proposals_count", 0)
    tier_trend = profile_data.get("tier_trend", "stable")
    engagement = profile_data.get("engagement_arc", "stable")
    coalition = profile_data.get("coalition_signal")
    total = profile_data.get("total_actions", 0)

    lines = [f"**Season Arc for {governor_id}**\n"]

    if total == 0:
        lines.append("You've been quiet this season. No proposals, no votes recorded yet.")
    else:
        # Engagement description
        arc_desc = {
            "warming_up": "Your engagement has been growing — more active in recent rounds.",
            "fading": "Your activity has tapered off in recent rounds.",
            "stable": "Your engagement has been consistent throughout the season.",
        }
        lines.append(arc_des
generate_newspaper_headlines function · python · L680-L708 (29 LOC)
src/pinwheel/ai/insights.py
async def generate_newspaper_headlines(
    round_data: dict,
    season_id: str,
    round_number: int,
    api_key: str,
) -> dict[str, str]:
    """Generate headline + subhead for the Pinwheel Post."""
    from pinwheel.ai.report import _call_claude

    data_str = json.dumps(round_data, indent=2)
    content = await _call_claude(
        system=NEWSPAPER_HEADLINE_PROMPT.format(round_data=data_str),
        user_message="Generate a newspaper headline and subhead for this round.",
        api_key=api_key,
        call_type="report.newspaper",
        season_id=season_id,
        round_number=round_number,
    )

    # Parse JSON response
    try:
        result = json.loads(content)
        return {
            "headline": str(result.get("headline", "ROUND COMPLETE")),
            "subhead": str(result.get("subhead", "")),
        }
    except (json.JSONDecodeError, AttributeError):
        # Fallback: use the raw text as headline
        return {"headline": content[:100], "subhead":
generate_newspaper_headlines_mock function · python · L711-L792 (82 LOC)
src/pinwheel/ai/insights.py
def generate_newspaper_headlines_mock(
    round_data: dict,
    round_number: int,
    *,
    playoff_phase: str = "",
    total_games_played: int = 0,
) -> dict[str, str]:
    """Mock newspaper headlines — reads the context to figure out the story.

    The data tells us everything: a championship game has a winner (that's
    the champion), a blowout tells us who dominated, a close game tells us
    about drama. We don't need external flags — just look at what happened.
    """
    games = round_data.get("games", [])
    if not games:
        return {
            "headline": f"SILENCE ON THE COURTS — ROUND {round_number}",
            "subhead": "No games played this round.",
        }

    # Analyze every game
    closest_margin = 999
    biggest_margin = 0
    closest_game: dict = {}
    biggest_game: dict = {}

    for g in games:
        margin = abs(g.get("home_score", 0) - g.get("away_score", 0))
        if margin < closest_margin:
            closest_margin = margin
         
_round_subhead function · python · L795-L812 (18 LOC)
src/pinwheel/ai/insights.py
def _round_subhead(
    round_data: dict,
    is_playoff: bool,
    total_games_played: int,
    games_this_round: int,
) -> str:
    """Build a contextual subhead for non-championship rounds."""
    governance_data = round_data.get("governance", {})
    rules_changed = governance_data.get("rules_changed", [])
    if rules_changed:
        param = rules_changed[0].get("parameter", "a rule")
        return f"The Floor rewrites {param} as governance reshapes the game."
    if is_playoff:
        return "The postseason rolls on."
    if total_games_played > 0:
        return f"{total_games_played} games played this season."
    n = games_this_round
    return f"{n} {'game' if n == 1 else 'games'} this round."
_parse_json_response function · python · L35-L44 (10 LOC)
src/pinwheel/ai/interpreter.py
def _parse_json_response(text: str, model_class: type[PydanticBaseModel]) -> PydanticBaseModel:
    """Parse JSON from model response text, stripping markdown fences if present.

    Raises json.JSONDecodeError or pydantic ValidationError on bad input.
    """
    text = text.strip()
    if text.startswith("```"):
        text = text.split("\n", 1)[1].rsplit("```", 1)[0].strip()
    data = json.loads(text)
    return model_class(**data)
_get_client function · python · L52-L65 (14 LOC)
src/pinwheel/ai/interpreter.py
def _get_client(api_key: str) -> anthropic.AsyncAnthropic:
    """Return a cached AsyncAnthropic client for connection reuse.

    SDK retries are disabled (max_retries=0) so that our app-level retry
    loop is the only retry layer.  This prevents the SDK's built-in
    back-off from silently eating the timeout budget on 429/overloaded.
    """
    if api_key not in _client_cache:
        _client_cache[api_key] = anthropic.AsyncAnthropic(
            api_key=api_key,
            timeout=_INTERPRETER_TIMEOUT,
            max_retries=0,
        )
    return _client_cache[api_key]
_build_parameter_description function · python · L100-L120 (21 LOC)
src/pinwheel/ai/interpreter.py
def _build_parameter_description(ruleset: RuleSet) -> str:
    """Build a description of available parameters with current values and ranges."""
    lines = []
    for name, field_info in RuleSet.model_fields.items():
        current = getattr(ruleset, name)
        metadata = field_info.metadata
        ge = le = None
        for m in metadata:
            if hasattr(m, "ge"):
                ge = m.ge
            if hasattr(m, "le"):
                le = m.le

        range_str = ""
        if ge is not None and le is not None:
            range_str = f" (range: {ge}-{le})"
        elif isinstance(current, bool):
            range_str = " (true/false)"

        lines.append(f"- {name}: current={current}{range_str}")
    return "\n".join(lines)
Source: Repobility analyzer · https://repobility.com
interpret_proposal function · python · L123-L205 (83 LOC)
src/pinwheel/ai/interpreter.py
async def interpret_proposal(
    raw_text: str,
    ruleset: RuleSet,
    api_key: str,
    amendment_context: str | None = None,
    season_id: str = "",
    round_number: int | None = None,
    db_session: object | None = None,
) -> RuleInterpretation:
    """Use Claude to interpret a natural language proposal into a structured rule change.

    This is a sandboxed call — the AI sees only the proposal text and parameter definitions.
    """
    from pinwheel.ai.usage import (
        cacheable_system,
        extract_usage,
        record_ai_usage,
        track_latency,
    )

    params_desc = _build_parameter_description(ruleset)
    system = INTERPRETER_SYSTEM_PROMPT.format(parameters=params_desc)

    user_msg = f"Proposal: {raw_text}"
    if amendment_context:
        user_msg = f"Original proposal: {amendment_context}\n\nAmendment: {raw_text}"

    model = "claude-sonnet-4-6"
    client = _get_client(api_key)
    last_error: Exception | None = None
    for attempt in range(2)
interpret_proposal_mock function · python · L208-L264 (57 LOC)
src/pinwheel/ai/interpreter.py
def interpret_proposal_mock(
    raw_text: str,
    ruleset: RuleSet,
) -> RuleInterpretation:
    """Mock interpreter for testing. Parses simple patterns without AI.

    Handles: "make X worth Y", "set X to Y", "change X to Y"
    """
    text = raw_text.lower().strip()

    # Simple pattern matching for common proposals
    param_keywords = {
        "three pointer": ("three_point_value", int),
        "three-pointer": ("three_point_value", int),
        "three point": ("three_point_value", int),
        "two pointer": ("two_point_value", int),
        "two-pointer": ("two_point_value", int),
        "two point": ("two_point_value", int),
        "free throw": ("free_throw_value", int),
        "shot clock": ("shot_clock_seconds", int),
        "foul limit": ("personal_foul_limit", int),
        "elam margin": ("elam_margin", int),
        "vote threshold": ("vote_threshold", float),
        "quarter length": ("quarter_minutes", int),
        "quarter minutes": ("quarter_minutes", i
interpret_strategy function · python · L326-L382 (57 LOC)
src/pinwheel/ai/interpreter.py
async def interpret_strategy(
    raw_text: str,
    api_key: str,
    season_id: str = "",
    round_number: int | None = None,
    db_session: object | None = None,
) -> TeamStrategy:
    """Use Claude to interpret natural language strategy into structured parameters.

    This is a sandboxed call — the AI sees only the sanitized strategy text and
    parameter definitions. Input is sanitized through the same pipeline as proposals.
    """
    from pinwheel.ai.usage import (
        cacheable_system,
        extract_usage,
        record_ai_usage,
        track_latency,
    )
    from pinwheel.core.governance import sanitize_text

    # Sanitize through the same pipeline as proposals (security boundary)
    sanitized = sanitize_text(raw_text, max_length=500)

    model = "claude-sonnet-4-6"
    try:
        client = _get_client(api_key)
        async with track_latency() as timing:
            response = await client.messages.create(
                model=model,
                max_t
page 1 / 14next ›