https://github.com/bluedynamics/bdapoker
A planning poker web application for agile estimation sessions. Share a link, join a room, vote simultaneously, discuss.
https://github.com/bluedynamics/bdapoker
Last synced: about 2 months ago
JSON representation
A planning poker web application for agile estimation sessions. Share a link, join a room, vote simultaneously, discuss.
- Host: GitHub
- URL: https://github.com/bluedynamics/bdapoker
- Owner: bluedynamics
- License: mit
- Created: 2026-02-13T10:26:25.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-02-13T15:38:04.000Z (6 months ago)
- Last Synced: 2026-02-13T19:36:05.016Z (6 months ago)
- Language: Python
- Homepage:
- Size: 84 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# BDA Poker
A planning poker web application for agile estimation sessions. Share a link, join a room, vote simultaneously, discuss.
## Features
- **Link-only joining** — no accounts, no signup. Create a room, share the URL.
- **Simultaneous reveal** — votes are hidden until the moderator reveals them, preventing anchoring bias.
- **Multiple deck types** — Fibonacci (0–100), T-shirt (XS–XXL), Powers of 2 (1–64).
- **Card description flavors** — each card value comes with a description to help calibrate estimates:
- *Technical* — straightforward complexity language
- *Idioms* — "Falling off a log", "Here be monsters", etc.
- *Animals* — complexity by creature size (Ant → Whale)
- *Software* — developer analogies ("Config change" → "Full rewrite")
- **Special cards** — `?` (need more info), `☕` (break), `∞` (too large, must split)
- **Moderator controls** — reveal, re-vote, new story, kick, timer, deck change
- **Statistics** — average, median, range, and consensus detection after reveal
- **Timer** — optional countdown for timeboxed discussions
- **Responsive** — works on desktop and mobile
## Quick Start
### Development
Start the backend and frontend in separate terminals:
```bash
# Backend
cd backend
python -m venv .venv && source .venv/bin/activate
pip install -e .
uvicorn app.main:app --reload --port 8000
# Frontend
cd frontend
npm install
npm run dev -- --port 5173
```
The frontend dev server proxies `/api` requests to the backend automatically.
Open http://localhost:5173 to use the app.
### Docker
```bash
docker build -t bdapoker .
docker run -p 8000:8000 bdapoker
```
Open http://localhost:8000. The single container serves both the API and the SvelteKit static build.
## Architecture
```
backend/ Python 3.11+, FastAPI, uvicorn
app/
main.py FastAPI app, REST endpoints, static file serving
models.py Pydantic data models (Room, Participant, Round, Vote)
decks.py Deck definitions with descriptions per flavor
rooms.py In-memory room store, creation, expiry cleanup
connection_manager.py WebSocket connection tracking per room
ws.py WebSocket endpoint, message handler, state broadcast
frontend/ SvelteKit 2, Svelte 5, TypeScript
src/
routes/
+page.svelte Landing page (create room)
room/[id]/+page.svelte Room page (join + game view)
lib/
stores/
websocket.ts WS connection, send, message handler
room.ts Room state, stats, timer stores
components/
JoinForm.svelte Name + role selection
CardDeck.svelte Votable card grid
Card.svelte Single card with tooltip
ParticipantList.svelte Participants + vote status
VoteResults.svelte Post-reveal statistics
StoryField.svelte Current story display
ModeratorControls.svelte Reveal, reset, new story, timer
Timer.svelte Countdown display
types.ts TypeScript interfaces
```
### How It Works
1. **Create a room** — POST `/api/rooms` with deck type and description flavor. Returns a room ID and moderator token.
2. **Join** — open `/room/{id}`, enter a name, pick voter or spectator.
3. **Vote** — voters pick a card. Others see a checkmark but not the value.
4. **Reveal** — moderator reveals all votes. Statistics are computed and outliers highlighted.
5. **Discuss & re-vote** — moderator can reset for another round or start a new story.
All state is synchronized via WebSocket. The server broadcasts full room state on every change, with vote values conditionally hidden until reveal.
### WebSocket Protocol
All messages are JSON `{"type": "...", "payload": {...}}`.
**Client → Server:** `join`, `vote`, `reveal`, `new_round`, `reset_round`, `kick`, `change_deck`, `start_timer`, `stop_timer`
**Server → Client:** `welcome`, `room_state`, `timer_start`, `timer_stop`, `error`
### REST API
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/rooms` | Create room |
| GET | `/api/rooms/{id}` | Get room info |
| GET | `/api/decks` | List all decks, flavors, descriptions |
| WS | `/api/rooms/{id}/ws` | WebSocket connection |
## Deployment
### Kubernetes (Helm)
```bash
helm install poker oci://ghcr.io/bluedynamics/charts/bdapoker \
--set ingress.enabled=true \
--set ingress.host=poker.example.com
```
Or from the repo:
```bash
helm install poker ./helm/bdapoker \
--set ingress.enabled=true \
--set ingress.host=poker.example.com
```
**Important:** The app uses in-memory state. All rooms and WebSocket connections live in a single process. Do not scale beyond 1 replica without adding a shared state backend (e.g. Redis). The Helm chart defaults to `replicas: 1` and `strategy: Recreate` to prevent split-brain during rollouts.
The ingress template includes nginx annotations for WebSocket support (1h proxy timeouts, connection upgrade headers).
### Docker image
The CI pipeline builds and pushes to `ghcr.io/bluedynamics/bdapoker` on every push to main.
```bash
docker pull ghcr.io/bluedynamics/bdapoker:latest
docker run -p 8000:8000 ghcr.io/bluedynamics/bdapoker:latest
```
## Testing
```bash
# Backend (pytest, 90% coverage)
cd backend
pip install pytest pytest-cov pytest-asyncio httpx
python -m pytest tests/ -v --cov=app --cov-report=term-missing
# Frontend (type checking)
cd frontend
npx svelte-check --threshold error
```
## Design Decisions
- **No database** — rooms are ephemeral (auto-expire after 4 hours of inactivity). In-memory state keeps the stack simple.
- **Full state broadcast** — the server sends the complete room state after every mutation. This eliminates sync bugs and keeps the frontend simple.
- **Single container** — the SvelteKit frontend is built as a static SPA and served by FastAPI alongside the API. One process, one port.
- **Plain UI** — no CSS framework, no animations, no decorative elements. System fonts, black/white/grey palette with minimal accent color.
## License
[MIT](LICENSE)