Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
fd1dece
feat: add master + separated NPC voice bots + human STT pipeline
skr19930617 Apr 26, 2026
e53bf43
feat: wire NPC bot production pipeline and Master voice-ingest integr…
skr19930617 Apr 27, 2026
eaef5de
refactor: split master/ + npc/ packages, rename LLM env vars by role
skr19930617 Apr 27, 2026
ecd1cfa
Add judgment profiles and public deduction prompts
skr19930617 Apr 27, 2026
b092915
feat: add tmux launcher scripts for Master + NPC bots
skr19930617 Apr 27, 2026
e7a6eba
feat: add tts_voice_id to Persona dataclass
skr19930617 Apr 27, 2026
3f7a2ee
feat: consolidate NPC env templates into a single generator
skr19930617 Apr 27, 2026
f5a7b56
fix(scripts/run-bots.sh): bypass uv shim, wait for Master WS before NPCs
skr19930617 Apr 27, 2026
e3d193f
Merge origin/main: shared multi-provider LLM switch for Gameplay + NPC
skr19930617 Apr 27, 2026
5429d6a
fix(speak_arbiter): prefer silent seats in dispatch picker
skr19930617 Apr 27, 2026
0534f2b
feat(llm): add mock provider for offline integration tests
skr19930617 Apr 27, 2026
2a5e8bf
feat(durations): runtime-mutable PhaseDurations singleton
skr19930617 Apr 27, 2026
eab5a6f
feat(scripts/run-bots.sh): --mock flag and VOICEVOX probe
skr19930617 Apr 27, 2026
0a4c342
feat(speech-event): plumb addressed_name/addressed_seat_no end-to-end
skr19930617 Apr 27, 2026
45daecc
feat(speak_arbiter): route addressed NPC ahead of silent rotation
skr19930617 Apr 27, 2026
da3a4dc
feat(text-analyzer): mirror voice's structured analysis on the text c…
skr19930617 Apr 27, 2026
998433a
fix(npc): join VC only when Master picks the bot via /wolf start
skr19930617 Apr 27, 2026
539b041
fix(master): join VC only when /wolf start fires reactive_voice
skr19930617 Apr 27, 2026
10d4a9f
feat(settings): /wolf settings View for phase durations (host-only)
skr19930617 Apr 27, 2026
eb622cf
feat(master): Levi-style TTS narration in VC + phase_baseline seed fix
skr19930617 Apr 27, 2026
4c47482
fix(master): serialize Master TTS with NPCs + mute dead players
skr19930617 Apr 27, 2026
c1847b0
fix(mock): unblock end-to-end run-bots --mock loop
skr19930617 Apr 28, 2026
7998378
feat(master): voice opening narration into populated VC
skr19930617 Apr 28, 2026
b1f1bf0
fix(mock): scan system prompt + user context for task keywords
skr19930617 Apr 28, 2026
d57bcdd
feat(vc): phase-aware mute for humans + self-mute NPC bots
skr19930617 Apr 28, 2026
2cd7e0f
fix(reactive_voice): contain Master output + clean abort teardown
skr19930617 Apr 28, 2026
0f26b6d
fix(narration): drop duplicate dawn line on day 2+ discussion start
skr19930617 Apr 28, 2026
3895df2
feat(observability): JSONL trace of every LLM/STT call per game
skr19930617 Apr 28, 2026
2943a9f
feat(observability): capture token usage in LLM trace
skr19930617 Apr 28, 2026
fcb3090
feat(viewer): Next.js + MUI viewer for per-game LLM/STT trace
skr19930617 Apr 28, 2026
95b14fe
feat(export): auto-export game JSON for viewer on victory + abort
skr19930617 Apr 28, 2026
a2d2d6a
feat(viewer): split into games-list + per-game detail routes
skr19930617 Apr 28, 2026
31fbfbc
feat(export): typed exporter contract + cross-stack integration tests…
skr19930617 Apr 28, 2026
715fb23
test(llm): per-provider trace integration tests
skr19930617 Apr 28, 2026
36ea3fc
fix(voice): keep voice_recv RX alive across single-packet OpusError
skr19930617 Apr 28, 2026
1bd909b
feat(stt): Groq Whisper STT pipeline + consolidate CO_CLAIM source
skr19930617 Apr 28, 2026
c18424f
fix(stt): wrap raw PCM as WAV before posting to Groq + record err body
skr19930617 Apr 28, 2026
22c5752
fix(stt): use Whisper's no_speech_prob for confidence, not analyzer's
skr19930617 Apr 28, 2026
01e8c9b
feat(voice): per-segment audio + transcript dump for debugging
skr19930617 Apr 28, 2026
dabc959
feat(voice): include full analyzer JSON in debug dump .txt sidecar
skr19930617 Apr 28, 2026
2f86b7d
fix(voice): pad opus gaps with silence + group dumps by speaker
skr19930617 Apr 28, 2026
de05a81
ci: install libopus + lazy-construct OpusError in resilience tests
skr19930617 Apr 28, 2026
f1d96a3
fix(viewer): dedupe speech rows + surface LLM traces per source
skr19930617 Apr 28, 2026
dacc53b
feat(reactive_voice): close NPC prompt parity gap with rounds mode
skr19930617 Apr 28, 2026
bc10d7d
feat(reactive_voice): Phase-D scaffold — NPC bot owns per-seat state
skr19930617 Apr 28, 2026
6b09a6c
feat(reactive_voice): Phase-D vote path — NPC bot decides its own vote
skr19930617 Apr 28, 2026
db32321
feat(reactive_voice): Phase-D 3+4+5 — night, wolf chat, digest, speec…
skr19930617 Apr 28, 2026
2ae90b2
feat(reactive_voice): Phase-D night-result push + wolf-chat coordination
skr19930617 Apr 28, 2026
70e6061
feat(voice): DAVE E2EE inner decrypt + voicetest harness + pre-STT si…
skr19930617 Apr 28, 2026
cf2f238
feat(voice): ground STT analyzer prompt on the live VC roster + emit …
skr19930617 Apr 28, 2026
1b783e6
feat(viewer): expose arbiter selection_reason on NPC speech rows
skr19930617 Apr 29, 2026
ac0c00a
chore(viewer): migrate to ESLint flat config and add @eslint/eslintrc
skr19930617 Apr 29, 2026
f9e35bc
feat(narration): announce night phase entry on execution-confirmed path
skr19930617 Apr 29, 2026
e79d959
fix(reactive_voice): self-heal PrivateStateSnapshot push after roles …
skr19930617 Apr 29, 2026
2d14e9c
chore(reactive_voice): wire per-game in-memory cleanup at game end
skr19930617 Apr 29, 2026
ad80f27
fix(reactive_voice): rotate fairly after first round (avoid last spea…
skr19930617 Apr 29, 2026
82fc6d4
feat(reactive_voice): NPC speeches emit addressed_seat_no like humans
skr19930617 Apr 29, 2026
b979d50
fix(reactive_voice): hydrate snapshot with persisted role-specific hi…
skr19930617 Apr 29, 2026
3a6c553
feat(reactive_voice): demote stuck pair volleys + same-seat consecuti…
skr19930617 Apr 29, 2026
f56419c
fix(reactive_voice): names-only speech + no-abstain vote + execution/…
skr19930617 Apr 29, 2026
d86acba
fix(reactive_voice): carry CO claims across phase boundaries
skr19930617 Apr 29, 2026
c13ed48
fix(reactive_voice): pair-volley gate now ignores re-declared COs
skr19930617 Apr 29, 2026
2e25bab
feat(reactive_voice): expose past-day vote history in NPC prompt
skr19930617 Apr 29, 2026
37e4c4b
fix(reactive_voice): dispatch next NPC after TTS timeout instead of s…
skr19930617 Apr 29, 2026
9ae68fa
fix(reactive_voice): forbid night-action skip the same way as votes
skr19930617 Apr 29, 2026
a155289
feat(reactive_voice): role-callout detection + propagation to NPC prompt
skr19930617 Apr 29, 2026
782f47f
feat(reactive_voice): per-seat speech_count axis to even out NPC rota…
skr19930617 Apr 29, 2026
0c4193f
fix(reactive_voice): NPC utterances no longer truncated mid-sentence
skr19930617 Apr 29, 2026
07c4b99
fix(reactive_voice): runoff candidate speeches now go through TTS pip…
skr19930617 Apr 29, 2026
eb03710
feat(reactive_voice): multi-address list + randomised tiebreak
skr19930617 Apr 29, 2026
e19797c
fix(reactive_voice): vote/night digest now carries discussion content
skr19930617 Apr 29, 2026
fe4523f
fix(reactive_voice): accept VC-chat text input as a speech channel
skr19930617 Apr 29, 2026
d4c71c7
fix(reactive_voice): match text-path analyzer to voice-path provider,…
skr19930617 Apr 29, 2026
f9ced2d
fix(reactive_voice): collapse 席N into a single roster header in LLM p…
skr19930617 Apr 29, 2026
3b4ba54
feat(reactive_voice): re-attack GJ'd target — knight can't guard same…
skr19930617 Apr 29, 2026
ee34b4f
feat(reactive_voice): role-callout pool + arbiter / prompt hardening
skr19930617 Apr 30, 2026
ef7d552
feat(reactive_voice): claim ledger + split-attack RNG + CO integrity …
skr19930617 Apr 30, 2026
bc2105b
fix(reactive_voice): document NIGHT_0 has no attack/guard so day 1 mo…
skr19930617 Apr 30, 2026
ae7618a
fix(reactive_voice): per-recipient nudge when CO'd seat is short on r…
skr19930617 Apr 30, 2026
8c73cb6
fix(reactive_voice): inject _build_game_rules_block into NPC system p…
skr19930617 May 1, 2026
fb59f82
fix(prompt): teach LLM that attacked seats are non-wolf (HARD fact)
skr19930617 May 1, 2026
316ab7d
fix(prompt): forbid claim revisions for both real and fake role-CO
skr19930617 May 1, 2026
51f6001
feat(reactive_voice): Master-side claim validator + same-NPC retry loop
skr19930617 May 1, 2026
6dda6ec
fix(reactive_voice): validator must read NIGHT_0 from logs_private
skr19930617 May 1, 2026
8ff23bf
feat(decision_dispatcher): stagger per-actor LLM calls to avoid quota…
skr19930617 May 1, 2026
1a19d4a
feat(llm): support Google AI Studio API-key auth + per-family thinkin…
skr19930617 May 1, 2026
d1cc59f
chore(decision_dispatcher): bump default inter_request_stagger_ms 800…
skr19930617 May 1, 2026
9ad6a73
fix(reactive_voice): block fabrication-capped seat from re-selection
skr19930617 May 1, 2026
68dc7fb
fix(reactive_voice): bump heartbeat_timeout_ms 5s→15s (3x interval)
skr19930617 May 1, 2026
9780729
feat(reactive_voice): hard-block 4th seer CO / 3rd medium CO via vali…
skr19930617 May 1, 2026
ccaa8dc
fix(npc): apply abstain fallback for LLM transport errors too
skr19930617 May 1, 2026
5934f77
feat(prompt): restructure CO ledger + dead-CO awareness rule
skr19930617 May 1, 2026
9d3d86b
fix(reactive_voice): block duplicate runoff candidate intro on double…
skr19930617 May 1, 2026
c3328ac
fix(prompt): correct seer claim count off-by-one + add runoff speech …
skr19930617 May 1, 2026
0788aec
fix(reactive_voice): random-legal fallback when NPC night-action disp…
skr19930617 May 1, 2026
75db64a
fix(npc): wolf-chat prompt now includes WEREWOLF role-strategy block
skr19930617 May 1, 2026
a9f2c7e
feat(llm/template): add minimal markdown template engine for prompts
skr19930617 May 2, 2026
e1b6f24
refactor(prompts): extract DeepSeek contracts + 9p game rules to .md …
skr19930617 May 2, 2026
273f67f
refactor(prompts): extract NPC speech system prompt to .md template
skr19930617 May 2, 2026
9499bd5
refactor(prompts): extract NPC decision (vote/wolf_chat/night) prompt…
skr19930617 May 2, 2026
0f9088a
refactor(prompts): extract Master task_* prompts (daytime/vote/night/…
skr19930617 May 2, 2026
1183fbe
refactor(prompts): extract Master STT + text-analyzer prompts to .md …
skr19930617 May 2, 2026
e19d606
refactor(prompts): extract Master narration + runoff intro to .md tem…
skr19930617 May 2, 2026
ef2327a
refactor(master): reorganize into 6 subdomains
skr19930617 May 2, 2026
15e83ad
refactor(npc): reorganize into 4 subdomains
skr19930617 May 2, 2026
c660ddc
refactor(persistence): split schema DDL into per-entity .sql files
skr19930617 May 2, 2026
af9eea9
feat(viewer): resolve game files by game.id with name-resolution fall…
skr19930617 May 2, 2026
89c625e
fix(claim_history): dedupe same-day CO re-assertions
skr19930617 May 2, 2026
cab2b92
fix(state_machine): tag MORNING + day-start logs with next day_number
skr19930617 May 2, 2026
41b002b
fix(tests): isolate WOLFBOT_LLM_TRACE_DIR session-wide
skr19930617 May 2, 2026
39621be
feat(viewer): surface vote/night/wolf-chat LLM trace + wolf chat in t…
skr19930617 May 2, 2026
2c59f5c
feat(chat): post kill announcement + survivor roster after each morning
skr19930617 May 2, 2026
541a899
feat(viewer): show speaker role chip on every speech row
skr19930617 May 2, 2026
dd09a01
feat(viewer): per-role chip and seat-card colour palette
skr19930617 May 2, 2026
baaaeaf
refactor(prompts): extract per-role strategy text to .md templates
skr19930617 May 2, 2026
df8c323
refactor(prompts): rewrite rules + strategy from scratch, ~60% smaller
skr19930617 May 2, 2026
ec28b67
fix(viewer): retag morning/day-start logs from text instead of bumpin…
skr19930617 May 2, 2026
cf2c673
feat(prompts): inject game rules into NPC decision prompts + grey-sea…
skr19930617 May 2, 2026
a37b384
feat(prompts): tighten grey-seat lynch rule, drop addressed-count dig…
skr19930617 May 2, 2026
71af83a
feat(prompts): mathematical roller-opposition rule + CO mutual-attest…
skr19930617 May 2, 2026
f1a460e
fix(arbiter): speech_count > addressed; pending CO results top priori…
skr19930617 May 2, 2026
79be500
refactor(config): restructure voice/text ingest into VOICE_PIPELINE /…
skr19930617 May 2, 2026
6d1aaae
feat(viewer): sortable/paginated/searchable games table + lazy LLM tr…
skr19930617 May 2, 2026
8983370
refactor(viewer): simplify sort to pure toggle
skr19930617 May 2, 2026
eaab7cf
feat(prompts): HARD guard for day-1 single CO + 2-1 灰吊り定石明示 + knight …
skr19930617 May 2, 2026
9283c50
fix(runoff): treat humans as first-class candidates + Levi intro de-d…
skr19930617 May 2, 2026
37121d0
feat(prompts): force vote-line evidence into day N+1 morning discussion
skr19930617 May 2, 2026
c1c31fb
feat(prompts): force wolves to 身内切り when partner is confirmed black
skr19930617 May 2, 2026
6bf3d33
feat(suspicions): structured suspicion timeline + anti-fabrication rules
skr19930617 May 2, 2026
58934e7
feat(suspicions): vote + seer divine paths emit suspicions; wolves av…
skr19930617 May 2, 2026
8879596
feat(viewer): suspicion timeline + matrix panel
skr19930617 May 2, 2026
bea72af
feat(prompts): auto medium-callout day≥2 + wolf decision block + susp…
skr19930617 May 2, 2026
f88180a
fix(prompts): bidirectional 3-1 ローラー rule + first-mover bias guard
skr19930617 May 2, 2026
5a0ee84
fix(prompts): independent-judgment rule against social-pressure cascade
skr19930617 May 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 0 additions & 29 deletions .env.example

This file was deleted.

141 changes: 141 additions & 0 deletions .env.master.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# ── Master bot (.env.master) ───────────────────────────
# Used by `uv run wolfbot`. Loaded from `.env.master` via
# wolfbot.config.MasterSettings.

# Discord Developer Portal で発行した bot トークン (Master 用)
DISCORD_TOKEN=
# bot を動かす Discord サーバー(guild)の数値 ID
DISCORD_GUILD_ID=
# 議論用メイン text チャンネルの数値 ID
MAIN_TEXT_CHANNEL_ID=
# プレイヤーが会話するメイン VC の数値 ID
MAIN_VOICE_CHANNEL_ID=
# SQLite データベースの保存先パス
WOLFBOT_DB_PATH=./wolfbot.db
# ログ出力レベル (DEBUG / INFO / WARNING / ERROR)
LOG_LEVEL=INFO

# ── 議論モード ─────────────────────────────────────────
# rounds : Master が全 LLM 席のターン制議論文も生成する
# reactive_voice : 議論文は NPC bot プロセスが VC で発話する
# (Master は投票・夜行動の判断のみ生成)
LLM_DISCUSSION_MODE=reactive_voice

# ── Master ↔ NPC bot / voice-ingest WebSocket ────────
# Master ↔ NPC bot 間 WebSocket の listen アドレス
MASTER_WS_LISTEN=127.0.0.1:8800
# NPC bot の WebSocket 認証用 Pre-Shared Key (NPC 側 .env.npc.* と同値)
MASTER_NPC_PSK=

# ── Gameplay LLM (Master が"判断する"側) ───────────────
# Master が LLM 席の挙動を決めるときに使う LLM。担当範囲:
# • 投票判断 (誰を処刑するか) ────────────── 全モード
# • 夜行動 (人狼の襲撃先 / 占い / 護衛) ──── 全モード
# • 議論ターンの発言文生成 ──────────────── rounds モードのみ
# (reactive_voice モードでは議論文は NPC bot 側に移譲され、Master は
# この LLM で投票と夜行動だけを生成する)
#
# Backend は 4 種類から選べる:
# xai ─ xAI Grok など OpenAI Chat Completions 互換 endpoint
# (OpenAI / Groq / Together / vLLM / Ollama も同じ枠で可)
# deepseek ─ DeepSeek (json_object モード + thinking + reasoning_effort)
# gemini ─ Vertex AI Gemini (ADC/IAM 認証、API key 認証は非対応)
# mock ─ オフライン統合テスト用。API コールせず決定論的な
# LLMAction を返す (投票/夜行動は target_name=None で
# LLMAdapter 側の random fallback に委譲、議論発言は
# 定型句のラウンドロビン)。API キー不要。
# NPC bot 側 NPC_LLM_PROVIDER と独立に選べる (片方を Vertex Gemini、
# 片方を xAI Grok にする、といった片寄せも可能)。
GAMEPLAY_LLM_PROVIDER=xai

# xAI / DeepSeek / OpenAI 互換 endpoint で使う API キー (gemini なら不要)
GAMEPLAY_LLM_API_KEY=
GAMEPLAY_LLM_MODEL=grok-4-1-fast
# 自前 endpoint や別プロバイダを叩く場合のみ上書き。空のままなら provider
# 既定値 (xai: https://api.x.ai/v1, deepseek: https://api.deepseek.com)
GAMEPLAY_LLM_BASE_URL=

# DeepSeek 専用 (provider=deepseek 以外では無視される)
GAMEPLAY_LLM_THINKING=enabled
GAMEPLAY_LLM_REASONING_EFFORT=max

# Vertex AI Gemini 専用 (provider=gemini 以外では無視される)
# 認証は ADC のみ: ローカルは `gcloud auth application-default login`、
# 本番は Vertex AI 権限を持つ service account を attach する。
GAMEPLAY_LLM_VERTEX_PROJECT=
GAMEPLAY_LLM_VERTEX_LOCATION=global
GAMEPLAY_LLM_THINKING_LEVEL=high

# ── フェーズ duration (検証・モック用の早送り) ────────
# 全フェーズの長さに掛ける倍率。0.1 = 10倍速、1.0 = デフォルト。
# 1秒未満になる場合は 1秒にクランプ。空のままならデフォルト維持。
# 個別フェーズのみ短くしたいときは下の WOLFBOT_*_DURATION で絶対秒指定
# (倍率と個別指定は両方適用、個別指定が後勝ち)。
# 将来的には Master bot の slash command で動的変更できるようになるため、
# このシングルトンが書き換え地点 (wolfbot.domain.durations.set_phase_durations)。
WOLFBOT_PHASE_DURATION_FACTOR=

# 個別フェーズ秒数の絶対指定 (空ならデフォルト or 倍率適用後の値を維持)
WOLFBOT_VOTE_DURATION=
WOLFBOT_RUNOFF_DURATION=
WOLFBOT_NIGHT_DURATION=
WOLFBOT_DAY_DISCUSSION_GRACE=
WOLFBOT_RUNOFF_SPEECH_GRACE=
WOLFBOT_DISCUSSION_DAY1=
WOLFBOT_DISCUSSION_DAY2=
WOLFBOT_DISCUSSION_DAY3PLUS=

# ── 音声 / テキスト ingest パイプラインの形状 ──────────────
# 人間の発言 (音声 / テキスト) を構造化 SpeechEvent
# (addressed_seat_no / co_declaration / role_callout) に変換する経路
# を 2 つの直交スイッチで明示する。各経路に必要なコンポーネントの
# クレデンシャルは下のブロックで個別に設定する。
#
# VOICE_PIPELINE: 人間の音声をどう取り込むか
# audio_analyzer : 1 コールの multimodal (audio → 構造化 JSON)
# 現状は AUDIO_ANALYZER_PROVIDER=gemini のみ対応
# stt_then_text_analyzer : 2 段 (Stt で文字起こし → TextAnalyzer で構造化)
# TextAnalyzer はテキスト経路と同じインスタンスを共有
# disabled : Master は VC に参加しない (rounds モードのデフォルト)
VOICE_PIPELINE=disabled
#
# TEXT_PIPELINE: テキスト発言をどう取り込むか
# text_analyzer : 各テキスト発言ごとに TextAnalyzer を 1 コール
# passthrough : 構造解析なし (SpeakArbiter は LRU 一本でルーティング)
TEXT_PIPELINE=passthrough

# ── AudioAnalyzer (audio → transcript + 構造化) ─────────────
# VOICE_PIPELINE=audio_analyzer のときのみ必須。
# 1 API コールで書き起こし + CO 検出 + 投票先 + addressed_name を返す
# multimodal LLM。現状 Gemini Flash 系のみ (xAI / Groq は audio-in 非対応)。
AUDIO_ANALYZER_PROVIDER=gemini
AUDIO_ANALYZER_API_KEY=
AUDIO_ANALYZER_MODEL=gemini-2.0-flash-lite

# ── Stt (audio → transcript) ────────────────────────────
# VOICE_PIPELINE=stt_then_text_analyzer のときのみ必須。
# Whisper-large-v3-turbo が Groq 上で最安かつ日本語実用品質。
# whisper-large-v3 に切り替えると精度↑コスト約3倍。
STT_PROVIDER=groq
STT_API_KEY=
STT_MODEL=whisper-large-v3-turbo
STT_BASE_URL=https://api.groq.com/openai/v1

# ── TextAnalyzer (text → 構造化) ────────────────────────
# TEXT_PIPELINE=text_analyzer のとき (発言ごと)、または
# VOICE_PIPELINE=stt_then_text_analyzer のとき (Stt 後段) に使用。
# 両経路で同じインスタンスを共有 → クレデンシャルプールも一本化。
# GAMEPLAY_LLM_* とは意図的に分離 (Gameplay は重い思考モデル、
# 構造解析は安い高 RPM モデル、の組み合わせを許す)。
#
# プロバイダ:
# xai / deepseek / openai : OpenAI Chat Completions 互換
# (API_KEY 必須、BASE_URL は任意)
# gemini : AI Studio REST (AIza... 形式の API_KEY 必須)
# ※ VOICE_PIPELINE=stt_then_text_analyzer の
# 後段としては未対応 (audio_analyzer に寄せる)
TEXT_ANALYZER_PROVIDER=xai
TEXT_ANALYZER_API_KEY=
TEXT_ANALYZER_MODEL=grok-4-1-fast
# 自前 endpoint (vLLM / Ollama 等) のときのみ上書き
TEXT_ANALYZER_BASE_URL=
86 changes: 86 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: CI

on:
push:
branches: [main]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
python:
name: Python (lint + type + tests)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Set up Python 3.11
run: uv python install 3.11

# discord.py loads libopus via ctypes for VC tests. The ubuntu
# runner image doesn't ship it by default, so OpusError(...)
# crashes inside `_lib.opus_strerror` and breaks test collection
# in tests/test_voice_recv_resilience.py.
- name: Install libopus (for voice tests)
run: sudo apt-get update && sudo apt-get install -y libopus0

- name: Sync deps
run: uv sync --all-extras --dev

- name: Lint
run: uv run ruff check src tests

- name: Type check
run: uv run mypy

- name: Test
run: uv run pytest tests

viewer:
name: Viewer (typecheck + contract tests)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# The viewer's contract test spawns the real Python exporter
# (`scripts/export-game.py`) against a seeded fixture DB, so this
# job needs both Node and uv.
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Set up Python 3.11
run: uv python install 3.11

- name: Sync Python deps (for live exporter spawn)
run: uv sync --all-extras --dev

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
cache-dependency-path: viewer/pnpm-lock.yaml

- name: Install viewer deps
working-directory: viewer
run: pnpm install --frozen-lockfile

- name: Type check
working-directory: viewer
run: pnpm typecheck

- name: Contract tests (vitest + live Python exporter)
working-directory: viewer
run: pnpm test
22 changes: 21 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,31 @@ __pycache__/
dist/
build/
.env
.env.master
# NPC bot env files live under envs/npc/. The committed `.env.npc.example`
# template is the only one tracked; the per-persona `.env.<persona>` files
# (generated by scripts/generate_npc_envs.py) stay out of git.
envs/npc/.env.*
!envs/npc/.env.npc.example
envs/voicetest/.env.*
!envs/voicetest/.env.voicetest.example
# Local secrets used by the generator
tokens.txt
*.db
*.db-journal
*.db-wal
*.db-shm
.DS_Store

# scripts/run-bots.sh writes per-bot logs here.
logs/

# for git push
.claude/
.claude/

# Specflow local env
.specflow/config.env
.specflow/runs/
.specflow/worktrees/
.codex
openspec
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Repository Guidelines

## Project Structure & Module Organization
Runtime code lives in `src/wolfbot`. Keep pure game logic in `domain/`, orchestration in `services/`, SQLite access in `persistence/`, LLM personas and prompts in `llm/`, and Discord UI views in `ui/`. Tests live in `tests/`, with shared fixtures in `tests/conftest.py` and fakes in `tests/fakes.py`. Use `.env.example` for local settings, and read `prompts/IMPLEMENTATION_PROMPT.md` before changing roles, phases, or event ordering.
Runtime code lives in `src/wolfbot`. Keep pure game logic in `domain/`, orchestration in `services/`, SQLite access in `persistence/`, shared persona types in `llm/persona_base.py` (player personas in `npc/personas.py`, GM personas in `master/personas.py`), and Discord UI views in `ui/`. The Master-only reactive-voice pipeline lives in `master/`; the per-process NPC bot worker lives in `npc/` with its own `NpcSettings`. Each NPC bot process is bound to one persona at startup (`NPC_PERSONA_KEY`); Master's `/wolf start` fills LLM seats from the online NPC registry in reactive_voice mode. Tests live in `tests/`, with shared fixtures in `tests/conftest.py` and fakes in `tests/fakes.py`. Use `.env.master.example` at the repo root for the Master env, and the per-persona templates in `envs/npc/.env.<persona>.example` (see [envs/npc/README.md](envs/npc/README.md)) for each NPC process. Read `prompts/IMPLEMENTATION_PROMPT.md` before changing roles, phases, or event ordering.

## Build, Test, and Development Commands
Use `uv` for all local work. `uv sync` installs runtime and dev dependencies. `uv run wolfbot` starts the bot with values from `.env`. `uv run pytest tests` runs the full suite; `uv run pytest tests/test_rules_votes.py` runs one module. `uv run ruff check src tests` runs lint and import checks, `uv run ruff format src tests` formats code, and `uv run mypy` runs strict type checking.
Use `uv` for all local work. `uv sync` installs runtime and dev dependencies. `uv run wolfbot` starts the Master bot with values from `.env.master`. NPC workers are launched per persona: `WOLFBOT_NPC_ENV=envs/npc/.env.<persona> uv run wolfbot-npc`. `uv run pytest tests` runs the full suite; `uv run pytest tests/test_rules_votes.py` runs one module. `uv run ruff check src tests` runs lint and import checks, `uv run ruff format src tests` formats code, and `uv run mypy` runs strict type checking.

## Coding Style & Naming Conventions
Target Python 3.11 and keep 4-space indentation. Follow Ruff defaults with a 100-character line length; let `ruff format` handle wrapping instead of manual alignment. New code should be fully type-annotated because `mypy` runs in strict mode. Use `snake_case` for modules, functions, and test files such as `test_recovery.py`; use `PascalCase` for classes and Pydantic models.
Expand Down
Loading