merge(main): resolve PR #320 gameplay conflicts
This commit is contained in:
52
docs/ISSUE-301-CLIENT-ACTION-GATING-ARTIFACT.md
Normal file
52
docs/ISSUE-301-CLIENT-ACTION-GATING-ARTIFACT.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Issue #301 Artifact — Client action gating from canonical phase state
|
||||
|
||||
Refs: #287, #301
|
||||
|
||||
## What changed
|
||||
|
||||
Frontend host/player shells now prefer the canonical phase exposed by `phase_view_model.current_phase` when deciding:
|
||||
|
||||
- which gameplay actions are enabled
|
||||
- whether reveal data should still be shown
|
||||
- which SPA hash-route should represent the active game state
|
||||
|
||||
This tightens the #301 slice so the client stays aligned with backend canonicalisation even when `session.status` lags during reveal/scoreboard promotion.
|
||||
|
||||
## Gated UI actions by phase
|
||||
|
||||
### Lobby
|
||||
- **Host:** `startRound`
|
||||
- **Player:** `join`
|
||||
|
||||
### Bluff / lie
|
||||
- **Host:** `showQuestion`
|
||||
- **Player:** `submitLie`
|
||||
- **Blocked:** guess submission, scoreboard load, next round, finish game
|
||||
|
||||
### Guess
|
||||
- **Host:** `mixAnswers`, `calculateScores`
|
||||
- **Player:** `submitGuess`
|
||||
- **Blocked:** lie submission, scoreboard load, next round, finish game
|
||||
|
||||
### Reveal
|
||||
- **Host:** `loadScoreboard`
|
||||
- **Player:** display-only reveal state
|
||||
- **Blocked:** start next round, finish game, guess/lie submission
|
||||
|
||||
### Scoreboard
|
||||
- **Host:** `startNextRound`, `finishGame`
|
||||
- **Player:** display-only reveal/scoreboard state
|
||||
- **Blocked:** scoreboard reload, guess/lie submission
|
||||
|
||||
## Test evidence
|
||||
|
||||
Targeted tests added/updated for:
|
||||
|
||||
- host shell canonical gating and route sync when `current_phase` differs from `session.status`
|
||||
- player shell canonical gating and route sync when `current_phase` differs from `session.status`
|
||||
- shared gameplay phase machine gating from canonical permissions
|
||||
- shared API mapper contract coverage, including reveal/scoreboard payload stability
|
||||
|
||||
## Contract note
|
||||
|
||||
No backend protocol redesign was introduced. This follow-up only preserves and consumes the existing canonical phase/action contract more strictly on the client side.
|
||||
55
docs/ISSUE-302-CANONICAL-LOOP-EVIDENCE.md
Normal file
55
docs/ISSUE-302-CANONICAL-LOOP-EVIDENCE.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Issue #302 Evidence — canonical bluff → guess → reveal → scoreboard regression
|
||||
|
||||
## Runnable command
|
||||
|
||||
```bash
|
||||
python manage.py migrate --noinput
|
||||
python manage.py smoke_staging --artifact docs/artifacts/issue-302-canonical-loop-smoke.json
|
||||
```
|
||||
|
||||
`migrate` is the normal local bootstrap precondition when the database has not been initialized yet; the regression evidence itself is produced by `smoke_staging`.
|
||||
|
||||
## What the regression proves
|
||||
|
||||
`smoke_staging` now exercises one full canonical round and fails fast with step-specific diagnostics if any of these break:
|
||||
|
||||
1. `start_round` lands the session in `lie` and returns a concrete `round_question_id`.
|
||||
2. Final `submit_lie` auto-advances the session to `guess` and exposes mixed answers containing both the correct answer and player bluffs.
|
||||
3. Final `submit_guess` auto-advances the session to `reveal` and returns the canonical reveal payload.
|
||||
4. The reveal payload includes:
|
||||
- correct answer
|
||||
- all lies
|
||||
- all guesses
|
||||
- fooled-player references for bluff hits
|
||||
5. The first canonical state read after reveal promotes the session to `scoreboard`.
|
||||
6. Scoreboard promotion preserves the same reveal payload and exposes a leaderboard with `scoreboard_ready=true`.
|
||||
|
||||
## Artifact shape
|
||||
|
||||
When `--artifact` is provided, the JSON file records:
|
||||
|
||||
- the exact smoke command
|
||||
- session code and round question id
|
||||
- deterministic guess plan used to produce both bluff hits and one correct guess
|
||||
- per-step evidence for:
|
||||
- `create_session`
|
||||
- `join_players`
|
||||
- `start_round`
|
||||
- `auto_guess_transition`
|
||||
- `submit_guesses`
|
||||
- `auto_reveal_transition`
|
||||
- `auto_scoreboard_transition`
|
||||
- `finish_game`
|
||||
- reveal summary (`correct_answer`, lie/guess counts, fooled-player ids, correct guess player ids)
|
||||
- promoted scoreboard leaderboard payload
|
||||
|
||||
## Targeted test coverage
|
||||
|
||||
Backend regression coverage lives in `lobby/tests.py`:
|
||||
|
||||
- `test_smoke_staging_command_runs_full_flow`
|
||||
- `test_smoke_staging_writes_phase_evidence_artifact_when_requested`
|
||||
|
||||
Together they ensure the command stays runnable in normal workflow and that the evidence artifact contains phase-by-phase proof instead of only a generic pass/fail.
|
||||
|
||||
Refs #287 #302
|
||||
110
docs/artifacts/issue-302-canonical-loop-smoke.json
Normal file
110
docs/artifacts/issue-302-canonical-loop-smoke.json
Normal file
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"ok": true,
|
||||
"command": "python manage.py smoke_staging --artifact <path>",
|
||||
"generated_at": "2026-03-16T15:19:30.105231+00:00",
|
||||
"question": {
|
||||
"prompt": "Smoke prompt?",
|
||||
"correct_answer": "Correct"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"step": "create_session",
|
||||
"session_status": "lobby"
|
||||
},
|
||||
{
|
||||
"step": "join_players",
|
||||
"players_count": 3
|
||||
},
|
||||
{
|
||||
"step": "start_round",
|
||||
"session_status": "lie",
|
||||
"round_question_id": 1
|
||||
},
|
||||
{
|
||||
"step": "auto_guess_transition",
|
||||
"session_status": "guess",
|
||||
"answers": [
|
||||
"Lie from P3",
|
||||
"Lie from P1",
|
||||
"Lie from P2",
|
||||
"Correct"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "submit_guesses",
|
||||
"guess_results": [
|
||||
{
|
||||
"player_id": 1,
|
||||
"selected_text": "Lie from P2",
|
||||
"is_correct": false,
|
||||
"fooled_player_id": 2
|
||||
},
|
||||
{
|
||||
"player_id": 2,
|
||||
"selected_text": "Correct",
|
||||
"is_correct": true,
|
||||
"fooled_player_id": null
|
||||
},
|
||||
{
|
||||
"player_id": 3,
|
||||
"selected_text": "Lie from P1",
|
||||
"is_correct": false,
|
||||
"fooled_player_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "auto_reveal_transition",
|
||||
"session_status": "reveal",
|
||||
"reveal": {
|
||||
"correct_answer": "Correct",
|
||||
"lies_count": 3,
|
||||
"guesses_count": 3,
|
||||
"fooled_player_ids": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"correct_guess_player_ids": [
|
||||
2
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"step": "auto_scoreboard_transition",
|
||||
"session_status": "scoreboard",
|
||||
"leaderboard": [
|
||||
{
|
||||
"id": 2,
|
||||
"nickname": "P2",
|
||||
"score": 7
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"nickname": "P1",
|
||||
"score": 2
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"nickname": "P3",
|
||||
"score": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "finish_game",
|
||||
"session_status": "finished"
|
||||
}
|
||||
],
|
||||
"session_code": "7YV59E",
|
||||
"players": [
|
||||
"P1",
|
||||
"P2",
|
||||
"P3"
|
||||
],
|
||||
"round_question_id": 1,
|
||||
"guess_plan": {
|
||||
"P1": "Lie from P2",
|
||||
"P2": "Correct",
|
||||
"P3": "Lie from P1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user