12 KiB
Issue #312 — FupOgFakta extraction map for logic currently living in lobby/
Parent: #311
Issue: #312
Purpose
This artifact documents the concrete FupOgFakta-specific logic that still lives in lobby/, separates it from true platform/session concerns, and names the intended destination ownership before any larger code move happens.
It is intentionally an inventory + extraction plan only. It does not perform the full move.
Architectural boundary this map is enforcing
The target boundary is already described in:
docs/plans/2026-03-09-fupogfakta-game-engine-design.mddocs/plans/2026-03-09-fupogfakta-implementation-plan.mddocs/ARCHITECTURE.md
Those docs consistently describe:
lobby/as the platform layer for session lifecycle, player presence, host ownership, generic game-run orchestration, and transport-facing platform concerns.fupogfakta/as the game cartridge that owns question selection rules, round config semantics, lie/guess/reveal/scoreboard flow, answer mixing, scoring, and game-specific response/event payloads.
In other words:
- Platform (
lobby/) should know that a session exists and that a game can be started/observed. - Cartridge (
fupogfakta/) should know what a lie is, what a guess is, how answers are mixed, when phases advance, and what payload shape those game phases expose.
Summary split
Generic platform/session concerns that belong in lobby/
These are not FupOgFakta-specific and should remain platform-owned:
- Session code parsing/generation:
lobby/views.py::_generate_session_codelobby/views.py::_normalize_session_codelobby/views.py::_create_unique_session_code
- Generic request parsing:
lobby/views.py::_json_body
- Session lifecycle and player presence endpoints:
lobby/views.py::create_sessionlobby/views.py::join_sessionlobby/views.py::session_detailonly for the generic session/player shell part
- Generic ownership / host authorization checks
- Generic session detail payload fields:
session.codesession.statussession.host_idsession.current_roundsession.players_countplayers[].id|nickname|score|is_connected
- Generic i18n/error transport helper usage:
lobby/i18n.pyapi_error(...)
- Route mounting / namespace ownership in
lobby/urls.pyfor platform routes only
FupOgFakta-specific logic currently misplaced in lobby/
These items are game-cartridge logic and should move behind fupogfakta/ ownership:
- Round question selection by category and previously-used questions
- Lie-phase payload construction and lie timer semantics
- Mixed-answer preparation for bluff gameplay
- Guess correctness / fooled-player detection
- Bluff/correct-answer score resolution
- Reveal payload construction
- Reveal → scoreboard promotion rules
- Start round / mix answers / submit lie / submit guess / calculate scores / reveal scoreboard / next round / finish game gameplay endpoints
- Phase view-model booleans that encode FupOgFakta rules rather than generic platform readiness
Extraction map
| Source file | Current function / concern | Why it is FupOgFakta-specific | Intended destination / owner |
|---|---|---|---|
lobby/views.py |
_build_player_ref(player) |
Helper is only used to shape FupOgFakta reveal payloads; not a generic platform concern today. | fupogfakta/serializers.py or fupogfakta/payloads.py owned by cartridge. |
lobby/views.py |
_build_reveal_payload(round_question) |
Encodes FupOgFakta reveal contract: lies, guesses, fooled-player refs, correct answer, prompt. | fupogfakta/payloads.py::build_reveal_payload or equivalent cartridge response builder. |
lobby/views.py |
_build_leaderboard(session) |
Current implementation is generic-ish, but used exclusively by FupOgFakta scoreboard/finish flow and coupled to that response shape. | Short term: keep shared helper if multiple games will consume same contract; otherwise move to fupogfakta/payloads.py until a true shared scoreboard contract exists. |
lobby/views.py |
_get_current_round_question(session) |
Depends on FupOgFakta RoundQuestion model and current-round semantics. |
fupogfakta/services/rounds.py or fupogfakta/queries.py. |
lobby/views.py |
_select_round_question(session, round_config) |
Implements FupOgFakta question selection rules by category, active questions, and not-yet-used question set. | fupogfakta/services/rounds.py::select_round_question. |
lobby/views.py |
_build_lie_started_payload(session, round_config, round_question) |
Builds a FupOgFakta event/response contract for lie phase, including category, prompt, lie deadline, round question id. | fupogfakta/payloads.py::build_lie_started_payload. |
lobby/views.py |
_prepare_mixed_answers(round_question) |
Bluff-answer dedupe and shuffle is core FupOgFakta gameplay logic. | fupogfakta/services/answers.py::prepare_mixed_answers. |
lobby/views.py |
_resolve_scores(session, round_question, round_config) |
Applies FupOgFakta scoring rules for correct guesses and successful bluffs; depends on Guess, LieAnswer, ScoreEvent, points_correct, points_bluff. |
fupogfakta/services/scoring.py::resolve_scores. |
lobby/views.py |
_maybe_promote_reveal_to_scoreboard(session) |
Encodes FupOgFakta reveal completion semantics and scoreboard transition trigger. | fupogfakta/services/phases.py::maybe_promote_reveal_to_scoreboard. |
lobby/views.py |
_build_phase_view_model(session, players_count, has_round_question) |
Most booleans are not platform-generic; they encode FupOgFakta phase names (lie, guess, scoreboard) and MVP constraints (3-5 players, round-question readiness, next-round/finish gating). |
Split: keep platform-shell fields in lobby/; move game-specific readiness/action flags to fupogfakta/payloads.py::build_phase_view_model or cartridge driver payload builder. |
lobby/views.py |
start_round(request, code) |
Starts FupOgFakta round, binds category, creates RoundConfig, selects RoundQuestion, transitions to LIE, broadcasts phase.lie_started. |
fupogfakta/views.py or cartridge command handler behind a future GameDriver.on_game_start / round bootstrap service. |
lobby/views.py |
show_question(request, code) |
Emits lie-phase question payload using FupOgFakta RoundQuestion and RoundConfig. |
fupogfakta/views.py or remove entirely once canonical driver flow owns the transition. |
lobby/views.py |
submit_lie(request, code, round_question_id) |
Pure FupOgFakta gameplay endpoint: lie validation, deadline semantics, auto-advance to guess phase, phase.guess_started payload. |
fupogfakta/views.py::submit_lie (or cartridge intent handler). |
lobby/views.py |
mix_answers(request, code, round_question_id) |
Manual FupOgFakta host action for lie→guess transition and answer mixing. | fupogfakta/views.py short term; long term likely deleted in favor of cartridge-driven automatic transition. |
lobby/views.py |
submit_guess(request, code, round_question_id) |
Pure FupOgFakta gameplay endpoint: validates answer choice, resolves correctness/bluff source, auto-calculates scores, transitions to reveal. | fupogfakta/views.py::submit_guess plus fupogfakta/services/scoring.py and fupogfakta/services/phases.py. |
lobby/views.py |
reveal_scoreboard(request, code) |
FupOgFakta reveal/scoreboard progression, not a generic platform capability. | fupogfakta/views.py::reveal_scoreboard or cartridge phase service. |
lobby/views.py |
start_next_round(request, code) |
FupOgFakta next-round bootstrap: copies prior RoundConfig, increments round, picks next question, re-enters lie phase. |
fupogfakta/services/rounds.py::start_next_round plus cartridge-owned endpoint/driver integration. |
lobby/views.py |
finish_game(request, code) |
Current finish path is tied to FupOgFakta scoreboard semantics and winner payload. | fupogfakta/views.py::finish_game until a truly generic platform finish contract exists. |
lobby/views.py |
calculate_scores(request, code, round_question_id) |
Explicit FupOgFakta score resolution endpoint. | fupogfakta/services/scoring.py and/or remove when fully absorbed by cartridge phase driver. |
lobby/urls.py |
Gameplay routes for rounds, lies, guesses, scoreboard, finish | These route names expose FupOgFakta-specific phase/actions from the platform namespace. | Re-home under fupogfakta/urls.py or leave mounted under /lobby/sessions/... only as a temporary façade delegating to cartridge-owned code. |
lobby/tests.py |
StartRoundTests, LieSubmissionTests, MixAnswersTests, GuessSubmissionTests, CanonicalRoundFlowTests, ScoreCalculationTests, RevealRoundFlowTests, SessionDetailRoundQuestionTests, SessionDetailPhaseViewModelTests, SmokeStagingCommandTests |
These test classes verify FupOgFakta game flow rather than platform mechanics. | Move/split into fupogfakta/tests/ with only session creation/join/platform transport tests left in lobby/tests.py. |
lobby/management/commands/smoke_staging.py |
End-to-end gameplay smoke through lies/guesses/finish | Script executes one concrete game flow and should be cartridge-aware, not platform-owned. | fupogfakta/management/commands/ or a shared smoke harness that delegates into cartridge-specific scenario runners. |
Recommended ownership split by module
Keep in lobby/
- Session creation/join and session-code lifecycle
- Generic player membership/presence reads
- Generic auth/host checks helpers (if extracted from views)
- Generic API error/i18n plumbing
- Future
GameRun/ driver orchestration, timers, and cartridge dispatch - A slim generic
session_detailenvelope that can embed cartridge payloads under a dedicated game key
Move to fupogfakta/
- Round state queries
- Question selection
- Lie/guess/reveal/scoreboard/finish transition rules
- Score calculation
- Answer mixing
- Gameplay payload/response builders
- Gameplay endpoints and tests
- Gameplay smoke command
Explicit boundary for session_detail
session_detail is currently mixed.
Generic part that should remain platform-owned
- Session identity/status metadata
- Player list / presence list
- Generic host/player capability envelope if it is game-agnostic
FupOgFakta part that should move or be delegated
round_questionpayloadrevealpayloadscoreboardpayloadphase_view_modelfields keyed tolie,guess,scoreboard,finished,question_ready, and 3–5-player MVP rules
A clean future shape would be:
{
"session": {"code": "ABC123", "status": "active", "game_type": "fupogfakta"},
"players": [...],
"game": {
"phase": "lie",
"payload": {"round_question": {...}, "reveal": null, "scoreboard": null}
}
}
That makes lobby/ the shell and fupogfakta/ the authority for game-state payloads.
Concrete extraction sequence
- Move pure helpers first
_get_current_round_question_select_round_question_prepare_mixed_answers_resolve_scores_build_lie_started_payload_build_reveal_payload
- Move gameplay endpoints behind cartridge-owned service functions
submit_liesubmit_guessstart_roundstart_next_roundfinish_gamereveal_scoreboardcalculate_scores
- Slim
session_detailinto platform envelope + delegated cartridge payload - Move gameplay tests out of
lobby/tests.py - Optionally leave compatibility routes in
lobby/urls.pyas a façade until clients are rewired
Risks this map is explicitly preventing
- Moving only models but leaving hidden phase-transition rules in
lobby/views.py - Treating
session_detailas platform-generic while it still leaks cartridge payload semantics - Leaving scoreboard/reveal transition logic behind as an undocumented coupling
- Splitting tests incorrectly so regressions stay "green" in
lobby/while FupOgFakta behavior silently drifts
Decision
For #311 / #312, the repository should treat the following as game-specific and extraction candidates:
- round-question selection
- lie/guess/reveal/scoreboard/finish transitions
- answer mixing
- score resolution
- reveal/scoreboard payload builders
- FupOgFakta-specific session-detail subpayloads
- gameplay flow tests and smoke command
And it should treat the following as platform-generic:
- session identity/lifecycle
- player presence/membership
- host authorization shell
- generic error transport
- future game-driver dispatch/orchestration
That is the explicit lobby vs fupogfakta boundary this issue needs before code extraction proceeds.