{"id":50915705,"url":"https://github.com/bluedynamics/bdapoker","last_synced_at":"2026-06-16T14:32:35.385Z","repository":{"id":338212137,"uuid":"1157017142","full_name":"bluedynamics/bdapoker","owner":"bluedynamics","description":"A planning poker web application for agile estimation sessions. Share a link, join a room, vote simultaneously, discuss.","archived":false,"fork":false,"pushed_at":"2026-02-13T15:38:04.000Z","size":86,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-13T19:36:05.016Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bluedynamics.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-13T10:26:25.000Z","updated_at":"2026-02-13T15:38:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bluedynamics/bdapoker","commit_stats":null,"previous_names":["bluedynamics/bdapoker"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/bluedynamics/bdapoker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fbdapoker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fbdapoker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fbdapoker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fbdapoker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bluedynamics","download_url":"https://codeload.github.com/bluedynamics/bdapoker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fbdapoker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34410780,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-16T14:32:33.867Z","updated_at":"2026-06-16T14:32:35.381Z","avatar_url":"https://github.com/bluedynamics.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BDA Poker\n\nA planning poker web application for agile estimation sessions. Share a link, join a room, vote simultaneously, discuss.\n\n## Features\n\n- **Link-only joining** — no accounts, no signup. Create a room, share the URL.\n- **Simultaneous reveal** — votes are hidden until the moderator reveals them, preventing anchoring bias.\n- **Multiple deck types** — Fibonacci (0–100), T-shirt (XS–XXL), Powers of 2 (1–64).\n- **Card description flavors** — each card value comes with a description to help calibrate estimates:\n  - *Technical* — straightforward complexity language\n  - *Idioms* — \"Falling off a log\", \"Here be monsters\", etc.\n  - *Animals* — complexity by creature size (Ant → Whale)\n  - *Software* — developer analogies (\"Config change\" → \"Full rewrite\")\n- **Special cards** — `?` (need more info), `☕` (break), `∞` (too large, must split)\n- **Moderator controls** — reveal, re-vote, new story, kick, timer, deck change\n- **Statistics** — average, median, range, and consensus detection after reveal\n- **Timer** — optional countdown for timeboxed discussions\n- **Responsive** — works on desktop and mobile\n\n## Quick Start\n\n### Development\n\nStart the backend and frontend in separate terminals:\n\n```bash\n# Backend\ncd backend\npython -m venv .venv \u0026\u0026 source .venv/bin/activate\npip install -e .\nuvicorn app.main:app --reload --port 8000\n\n# Frontend\ncd frontend\nnpm install\nnpm run dev -- --port 5173\n```\n\nThe frontend dev server proxies `/api` requests to the backend automatically.\n\nOpen http://localhost:5173 to use the app.\n\n### Docker\n\n```bash\ndocker build -t bdapoker .\ndocker run -p 8000:8000 bdapoker\n```\n\nOpen http://localhost:8000. The single container serves both the API and the SvelteKit static build.\n\n## Architecture\n\n```\nbackend/           Python 3.11+, FastAPI, uvicorn\n  app/\n    main.py        FastAPI app, REST endpoints, static file serving\n    models.py      Pydantic data models (Room, Participant, Round, Vote)\n    decks.py       Deck definitions with descriptions per flavor\n    rooms.py       In-memory room store, creation, expiry cleanup\n    connection_manager.py   WebSocket connection tracking per room\n    ws.py          WebSocket endpoint, message handler, state broadcast\n\nfrontend/          SvelteKit 2, Svelte 5, TypeScript\n  src/\n    routes/\n      +page.svelte              Landing page (create room)\n      room/[id]/+page.svelte    Room page (join + game view)\n    lib/\n      stores/\n        websocket.ts    WS connection, send, message handler\n        room.ts         Room state, stats, timer stores\n      components/\n        JoinForm.svelte          Name + role selection\n        CardDeck.svelte          Votable card grid\n        Card.svelte              Single card with tooltip\n        ParticipantList.svelte   Participants + vote status\n        VoteResults.svelte       Post-reveal statistics\n        StoryField.svelte        Current story display\n        ModeratorControls.svelte Reveal, reset, new story, timer\n        Timer.svelte             Countdown display\n      types.ts       TypeScript interfaces\n```\n\n### How It Works\n\n1. **Create a room** — POST `/api/rooms` with deck type and description flavor. Returns a room ID and moderator token.\n2. **Join** — open `/room/{id}`, enter a name, pick voter or spectator.\n3. **Vote** — voters pick a card. Others see a checkmark but not the value.\n4. **Reveal** — moderator reveals all votes. Statistics are computed and outliers highlighted.\n5. **Discuss \u0026 re-vote** — moderator can reset for another round or start a new story.\n\nAll state is synchronized via WebSocket. The server broadcasts full room state on every change, with vote values conditionally hidden until reveal.\n\n### WebSocket Protocol\n\nAll messages are JSON `{\"type\": \"...\", \"payload\": {...}}`.\n\n**Client → Server:** `join`, `vote`, `reveal`, `new_round`, `reset_round`, `kick`, `change_deck`, `start_timer`, `stop_timer`\n\n**Server → Client:** `welcome`, `room_state`, `timer_start`, `timer_stop`, `error`\n\n### REST API\n\n| Method | Path | Description |\n|--------|------|-------------|\n| POST | `/api/rooms` | Create room |\n| GET | `/api/rooms/{id}` | Get room info |\n| GET | `/api/decks` | List all decks, flavors, descriptions |\n| WS | `/api/rooms/{id}/ws` | WebSocket connection |\n\n## Deployment\n\n### Kubernetes (Helm)\n\n```bash\nhelm install poker oci://ghcr.io/bluedynamics/charts/bdapoker \\\n  --set ingress.enabled=true \\\n  --set ingress.host=poker.example.com\n```\n\nOr from the repo:\n\n```bash\nhelm install poker ./helm/bdapoker \\\n  --set ingress.enabled=true \\\n  --set ingress.host=poker.example.com\n```\n\n**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.\n\nThe ingress template includes nginx annotations for WebSocket support (1h proxy timeouts, connection upgrade headers).\n\n### Docker image\n\nThe CI pipeline builds and pushes to `ghcr.io/bluedynamics/bdapoker` on every push to main.\n\n```bash\ndocker pull ghcr.io/bluedynamics/bdapoker:latest\ndocker run -p 8000:8000 ghcr.io/bluedynamics/bdapoker:latest\n```\n\n## Testing\n\n```bash\n# Backend (pytest, 90% coverage)\ncd backend\npip install pytest pytest-cov pytest-asyncio httpx\npython -m pytest tests/ -v --cov=app --cov-report=term-missing\n\n# Frontend (type checking)\ncd frontend\nnpx svelte-check --threshold error\n```\n\n## Design Decisions\n\n- **No database** — rooms are ephemeral (auto-expire after 4 hours of inactivity). In-memory state keeps the stack simple.\n- **Full state broadcast** — the server sends the complete room state after every mutation. This eliminates sync bugs and keeps the frontend simple.\n- **Single container** — the SvelteKit frontend is built as a static SPA and served by FastAPI alongside the API. One process, one port.\n- **Plain UI** — no CSS framework, no animations, no decorative elements. System fonts, black/white/grey palette with minimal accent color.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluedynamics%2Fbdapoker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbluedynamics%2Fbdapoker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluedynamics%2Fbdapoker/lists"}