# PRD — UtiVerse Team Quest

**Status:** Phase 0–7 selesai dan terverifikasi
**Versi:** 1.0
**Tanggal:** 28 Juli 2026
**Produk:** UtiVerse — Game Pembelajaran
**Nama fitur:** Team Quest / Arena Kelompok

---

## 0. Cara menggunakan PRD ini

Dokumen ini adalah kontrak implementasi Team Quest. IDE/agent yang mengerjakannya wajib:

1. Membaca bagian compatibility contract sebelum mengubah kode.
2. Memeriksa ulang route, controller, model, migration, dan komponen aktual karena repo dapat berkembang setelah tanggal PRD ini.
3. Mengerjakan fase secara berurutan; jangan langsung membuat seluruh game dalam satu perubahan besar.
4. Menjalankan verification commands dan memenuhi Definition of Done pada fase aktif.
5. Tidak mengedit migration lama yang sudah pernah dijalankan; selalu buat migration baru.
6. Tidak melakukan refactor kosmetik yang tidak diperlukan pada alur Live Arena individual.
7. Menyebutkan secara eksplisit pada handoff: fase yang selesai, test yang dijalankan, test yang belum dijalankan, dan risiko yang tersisa.

Jika ditemukan konflik antara ide reuse dan perlindungan Live Arena individual, perlindungan alur existing menjadi prioritas.

### Status implementasi

- [x] Phase 0 — Safety net Live Arena individual.
- [x] Phase 1 — Domain dan migration Team Quest.
- [x] Phase 2 — Setup kelompok, lobby, join, dan device binding.
- [x] Phase 3 — Mesin turn dan Sentence Relay.
- [x] Phase 4 — Category Relay dan Match Chain.
- [x] Phase 5 — Listening dan Error Hunter.
- [x] Phase 6 — Secret Taboo.
- [x] Phase 7 — Report, hardening, dan rollout.

**Checkpoint terakhir:** Seluruh Phase 0–7 selesai dan terverifikasi pada 28 Juli 2026. Pekerjaan berikutnya adalah perbaikan hasil pemakaian kelas atau fitur pascarilis; jangan mengulang implementasi Phase 0–7.

---

## 1. Ringkasan keputusan

Team Quest adalah mode permainan kelas real-time di mana **satu perangkat mewakili satu kelompok**, bukan satu siswa. Mode ini ditujukan untuk kondisi ketika hanya sebagian siswa membawa HP, misalnya:

- 20 siswa, 4 HP: 4 kelompok × 5 anggota.
- 20 siswa, 2 HP: 2 kelompok × 10 anggota, dengan mini-squad bergilir.

Team Quest bukan pengganti Live Arena. Implementasinya wajib menjadi mode paralel:

| Mode | Peserta pada satu perangkat | Runtime | Status setelah Team Quest dibuat |
|---|---:|---|---|
| `serious` | 1 siswa | Individual asynchronous | Tetap berfungsi |
| `live` | 1 siswa | Individual real-time | Tetap berfungsi tanpa perubahan perilaku |
| `faction` | 1 siswa, skor diakumulasi per kelas | Asynchronous antar kelas | Tetap berfungsi |
| `team_live` | 1 kelompok | Kelompok real-time di kelas | Mode baru |

### Compatibility contract — tidak boleh dilanggar

1. Jangan menghapus atau mengganti nilai `live`.
2. Jangan mengubah arti `LearningGameSession`; model ini tetap mewakili **satu siswa dalam Live Arena individual**.
3. Jangan mengubah skor Live Arena individual menjadi skor kelompok.
4. Jangan menghapus atau mengganti route, event, controller, atau halaman Live Arena lama.
5. Team Quest wajib memakai sesi, grup, jawaban, event, channel, dan laporan tersendiri.
6. `GameItem` dan komponen pertanyaan yang aman boleh dipakai bersama karena konten soalnya sama.
7. Skor Team Quest tidak otomatis menjadi nilai akademik individual.

Jika implementasi mengharuskan perubahan pada alur individual, buat regression test terlebih dahulu dan pertahankan hasil serta kontrak route yang sekarang.

---

## 2. Latar belakang masalah

Live Arena saat ini mengasumsikan satu siswa membawa dan menggunakan satu perangkat. Di kelas nyata, sering hanya 2–5 siswa yang membawa HP. Akibatnya:

- Sebagian besar siswa hanya menonton.
- Satu siswa yang memegang HP dapat mendominasi jawaban.
- Guru sulit memastikan semua anggota ikut berpikir.
- Live Arena individual tidak dapat berjalan jika perangkat tidak mencukupi.

Team Quest harus membuat keterbatasan perangkat menjadi mekanik kolaborasi: siswa berdiskusi bersama, tetapi aplikasi tetap mengatur siapa yang melakukan tindakan berikutnya.

---

## 3. Tujuan produk

### Tujuan utama

- Satu HP dapat digunakan secara efektif oleh 2–5 siswa.
- Dua kelompok besar tetap dapat digunakan untuk kelas yang hanya memiliki dua HP.
- Setiap siswa memperoleh giliran yang terlihat dan tercatat.
- Guru mengendalikan tempo melalui layar host/LCD.
- Jawaban dan leaderboard kelompok diperbarui secara real-time.
- Game tetap dapat dilanjutkan setelah koneksi WebSocket atau halaman HP sempat terputus.

### Indikator keberhasilan

- Guru dapat menyiapkan sesi 20 siswa menjadi 4×5 dalam maksimal 3 menit.
- Hanya satu perangkat yang dapat aktif untuk satu kelompok pada saat yang sama.
- Setiap anggota aktif mendapat minimal satu giliran dalam satu rotasi.
- Tidak ada kunci jawaban yang dikirim ke perangkat sebelum fase pembahasan.
- Sesi dapat dipulihkan setelah refresh tanpa kehilangan skor, giliran, atau susunan jawaban.
- Live Arena individual tetap lolos seluruh regression test.

### Non-goals versi pertama

- Team Quest bukan video conference atau permainan lintas sekolah.
- Tidak mendukung lebih dari satu perangkat aktif per kelompok.
- Tidak memberi nilai akademik individual otomatis dari skor kelompok.
- Tidak membuat AI menilai speaking bebas pada versi pertama.
- Tidak mengganti Faction Wars.
- Tidak menjalankan dua host aktif untuk game dan kelas yang sama pada versi pertama.

---

## 4. Terminologi

- **Game:** `LearningGame` yang dibuat guru dan berisi `GameItem`.
- **Team Quest Session:** satu pelaksanaan/hosted run Team Quest pada waktu tertentu.
- **Team:** kelompok peserta yang berbagi satu HP dan satu skor.
- **Member:** siswa yang terdaftar pada Team.
- **Active squad:** maksimal lima anggota yang mendapat giliran pada soal saat ini.
- **Device representative:** siswa yang login pada HP kelompok. Identitas ini hanya untuk otorisasi perangkat, bukan pemilik seluruh skor.
- **Turn:** satu kesempatan anggota melakukan satu aksi digital.
- **Contribution:** aksi yang sah dan tercatat, misalnya memasang kata, memasangkan item, memindahkan kategori, merevisi, atau mengonfirmasi.
- **Host:** layar guru/LCD yang mengendalikan sesi.

---

## 5. User stories

### Guru

1. Sebagai guru, saya dapat memilih Team Quest tanpa kehilangan pilihan Live Arena individual.
2. Saya dapat memilih satu kelas dan membuat 2–8 kelompok.
3. Saya dapat membagi siswa otomatis, lalu memindahkan anggota sebelum sesi dimulai.
4. Saya dapat memastikan setiap kelompok memiliki minimal satu siswa yang membawa HP.
5. Saya dapat melihat kelompok mana yang sudah terhubung.
6. Saya dapat memulai, menutup soal, membuka jawaban, melanjutkan soal, atau mengakhiri sesi.
7. Saya dapat menonaktifkan anggota yang tidak hadir agar rotasi tidak macet.
8. Saya dapat melihat skor kelompok dan catatan kontribusi setiap anggota.

### Siswa pemegang perangkat

1. Sebagai siswa, saya login seperti biasa lalu memasukkan PIN Team Quest.
2. Sistem otomatis memasukkan saya ke kelompok tempat saya terdaftar.
3. Saya melihat nama anggota yang sedang mendapat giliran.
4. Saya dapat refresh atau kembali masuk tanpa kehilangan state kelompok.

### Anggota kelompok

1. Saya mengetahui kapan giliran saya dimulai.
2. Saya hanya melakukan satu aksi pada giliran saya, kemudian HP diteruskan.
3. Saya tetap dapat berdiskusi ketika bukan giliran saya.
4. Kontribusi saya tercatat dalam laporan sesi.

---

## 6. Alur operasional di kelas

### 6.1 Membuat game

Guru menggunakan form Game Pembelajaran yang ada:

1. Mengisi judul, mata pelajaran, dan kelas.
2. Memilih mode `Team Quest` (`game_mode = team_live`).
3. Menambahkan soal dari tipe yang didukung.
4. Mengatur timer kelompok; default 75 detik, rentang 30–180 detik.
5. Publish game.

Pilihan mode harus tetap menampilkan:

- Mode Serius — mandiri.
- Live Arena — multiplayer individual.
- Faction — antar kelas.
- Team Quest — satu HP per kelompok.

### 6.2 Menyiapkan sesi

Ketika guru menekan **Host Team Quest**:

1. Pilih satu kelas dari kelas yang terpasang pada game.
2. Pilih jumlah kelompok, minimal 2 dan maksimal 8.
3. Sistem membagi siswa secara seimbang.
4. Guru dapat drag-and-drop siswa antar kelompok.
5. Guru menandai siswa yang tidak hadir.
6. Guru memastikan setiap kelompok memiliki seorang device representative.
7. Guru menyimpan pembagian dan masuk ke lobby.

Aturan validasi:

- Satu siswa aktif hanya boleh berada di satu kelompok dalam satu session.
- Satu kelompok minimal 2 anggota aktif.
- Satu kelompok maksimal 10 anggota aktif.
- Untuk kelompok 6–10 anggota, sistem membentuk dua mini-squad dan merotasinya per soal.
- Session tidak dapat dimulai sebelum semua kelompok mempunyai perangkat yang terhubung, kecuali guru memilih **Mulai tanpa kelompok yang belum siap**.

### 6.3 Bergabung dari HP kelompok

MVP menggunakan autentikasi siswa yang sudah ada:

1. Salah satu siswa dalam kelompok login di HP.
2. Siswa membuka halaman **Gabung Team Quest**.
3. Siswa memasukkan PIN session.
4. Server mencari membership siswa tersebut pada session.
5. Perangkat terikat ke kelompoknya dan lobby menampilkan status **Siap**.

Siswa tidak memilih kelompok secara bebas. Hal ini mencegah perangkat masuk ke kelompok yang salah. Guru dapat memindahkan siswa atau mengganti device representative dari lobby.

Login siswa pada HP hanya berfungsi sebagai otorisasi. Semua jawaban disimpan atas nama Team, sementara aksi dicatat atas nama anggota yang sedang mendapat giliran.

### 6.4 Menjalankan soal

1. Guru menekan **Mulai Game**.
2. Host menampilkan instruksi, soal, timer, dan progres setiap kelompok.
3. HP kelompok menampilkan workspace jawaban dan nama anggota aktif.
4. Setelah satu aksi valid, server mencatat kontribusi dan memindahkan giliran.
5. HP menampilkan pesan `Oper HP ke {nama anggota berikutnya}`.
6. Team dapat submit setelah seluruh anggota active squad mendapat kontribusi atau reviewer terakhir mengonfirmasi.
7. Guru menutup soal atau timer berakhir otomatis.
8. Host membuka jawaban benar, pembahasan, skor, dan leaderboard.
9. Guru melanjutkan ke soal berikutnya.

### 6.5 Kelompok besar: 2×10

Untuk kelompok beranggotakan 6–10 orang:

- Soal ganjil dimainkan mini-squad A, maksimal 5 siswa.
- Soal genap dimainkan mini-squad B, maksimal 5 siswa.
- Jika jumlah tidak seimbang, anggota dengan jumlah kontribusi paling sedikit diprioritaskan.
- Leaderboard tetap menggunakan nama dan skor kelompok besar.
- Laporan menunjukkan active squad dan kontribusi per soal.

Guru dapat memilih rotasi manual jika kondisi kelas membutuhkan penyesuaian.

---

## 7. Mekanik giliran dan anti-dominasi

Giliran harus divalidasi server, bukan hanya disembunyikan melalui UI.

### Aturan dasar

1. Server menyimpan `active_member_id` dan `turn_number`.
2. Request aksi wajib membawa `member_id`, `turn_number`, dan `client_action_id` UUID.
3. Server menolak aksi jika member bukan anggota aktif, turn sudah berubah, atau `client_action_id` sudah pernah dipakai.
4. Setelah aksi diterima, server menyimpan contribution dan memajukan turn dalam satu database transaction.
5. Satu anggota tidak mendapatkan giliran kedua sebelum semua anggota active squad mendapatkan giliran, kecuali anggota lain di-skip guru.
6. Tombol **Lewati Giliran** tersedia setelah 5 detik dan tetap dicatat sebagai skip, bukan contribution.
7. Guru dapat skip, menonaktifkan, atau mengganti anggota melalui host.

### Aksi yang dihitung sebagai contribution

- Menempatkan satu kata/item.
- Memindahkan atau menukar satu item.
- Membuat satu pasangan.
- Memperbaiki satu kesalahan.
- Mengonfirmasi/review jawaban setelah seluruh unit sudah terisi.
- Menjadi clue-giver atau recorder pada Secret Taboo, sesuai phase soal.

Hanya tap acak, membuka halaman, atau menekan lewati tidak dihitung sebagai contribution.

---

## 8. Tipe permainan

### 8.1 Sentence Relay — prioritas P0

Menggunakan fondasi `sentence_rearrange` yang sudah ada.

Contoh jawaban benar:

```text
I went to the market yesterday.
```

HP menerima token dalam urutan acak. Pada setiap turn, anggota boleh:

- Menempatkan satu token ke slot jawaban; atau
- Menukar posisi dua token yang sudah ditempatkan.

Setelah aksi berhasil, giliran berpindah. Host hanya menampilkan progres `4/6 kata`, bukan susunan jawaban kelompok, agar kelompok lain tidak menyalin.

Ketika soal ditutup, host menampilkan jawaban benar, jawaban tiap Team, persentase posisi benar, dan poin.

### 8.2 Category Relay — prioritas P0

Menggunakan fondasi `grouping`.

Setiap turn hanya boleh memindahkan satu item ke satu kategori. Scoring menggunakan jumlah item pada kategori yang benar dibagi total item.

### 8.3 Match Chain — prioritas P0

Menggunakan fondasi `matching`.

Setiap turn membuat atau memperbaiki satu pasangan. Setelah seluruh pasangan terisi, anggota berikutnya bertindak sebagai reviewer dan dapat menyetujui atau memperbaiki satu pasangan.

### 8.4 Listen and Build — prioritas P1

Tipe baru: `listen_and_build`.

- Audio diputar dari host/LCD agar semua Team mendengar sumber yang sama.
- Default maksimal dua kali putar.
- HP menampilkan token acak setelah pemutaran pertama selesai.
- Mekanik penyusunan sama dengan Sentence Relay.
- Guru dapat mengunggah MP3, WAV, atau M4A; batas default 10 MB.
- TTS browser boleh menjadi fallback, tetapi audio upload adalah sumber utama karena suara TTS dapat berbeda antar perangkat.

Metadata minimal:

```json
{
  "audio_path": "game-audio/example.mp3",
  "replay_limit": 2,
  "transcript": "I went to the market yesterday."
}
```

`transcript` adalah kunci jawaban dan tidak boleh dikirim sebelum fase reveal.

### 8.5 Error Hunter — prioritas P1

Tipe baru: `error_hunter`.

Contoh:

```text
She go to school yesterday.
```

Setiap anggota pada turn-nya memilih satu token yang salah dan memasukkan atau memilih koreksinya. Struktur jawaban:

```json
[
  {
    "token_index": 1,
    "original": "go",
    "correction": "went",
    "explanation": "Simple Past"
  }
]
```

Scoring dipisah menjadi deteksi token salah dan koreksi yang benar. Penjelasan ditampilkan saat reveal.

### 8.6 Secret Taboo — prioritas P2

Tipe baru: `secret_taboo`.

Alur satu soal:

1. HP meminta perangkat diberikan kepada clue-giver.
2. Secret word dan forbidden words tampil selama 5 detik.
3. Layar menyembunyikan kata dan HP diberikan kepada recorder.
4. Clue-giver menjelaskan secara lisan tanpa menyebut kata terlarang.
5. Anggota menebak dan recorder memasukkan jawaban.
6. Jawaban dinilai menggunakan answer utama dan aliases.

Struktur:

```json
{
  "answer": "elephant",
  "aliases": ["an elephant"],
  "forbidden_words": ["animal", "trunk", "big"],
  "clue_seconds": 45
}
```

Pemeriksaan forbidden words bersifat kejujuran kelas pada versi awal. Speech recognition bukan bagian MVP.

---

## 9. Scoring

Scoring harus mengutamakan kebenaran dan partisipasi, bukan kecepatan saja.

```text
accuracy_points      = round(1000 × correct_ratio)
participation_points = round(250 × contributors / active_squad_size)
speed_points         = jawaban benar penuh ? round(250 × remaining_time / total_time) : 0
question_score       = accuracy_points + participation_points + speed_points
```

Aturan:

- `correct_ratio` bernilai 0–1 dan mendukung partial score untuk matching, grouping, sequencing, Sentence Relay, dan Error Hunter.
- Speed bonus hanya diberikan jika jawaban sepenuhnya benar.
- Contribution dihitung maksimal satu kali per anggota untuk participation bonus pada satu soal.
- Skip tidak dihitung sebagai contribution.
- Timeout tetap menyimpan draft jawaban dan partial score.
- Skor maksimal dasar per soal adalah 1.500.
- Team streak boleh ditampilkan sebagai efek visual, tetapi bukan bagian formula MVP agar scoring mudah dijelaskan.

---

## 10. Tampilan LCD dan HP

### Host/LCD

Host menampilkan:

- Judul dan nomor soal.
- Instruksi singkat dan timer besar.
- Konten soal yang aman ditampilkan bersama.
- Progres per Team tanpa membocorkan draft jawaban.
- Status: berpikir, belum lengkap, siap, submitted, disconnected.
- Setelah reveal: jawaban benar, pembahasan, skor soal, dan leaderboard.

Contoh progres:

```text
Meteor   4/6 aksi   Giliran: Nabila   ███████░░
Galaxy   Siap                         █████████
Comet    3/6 aksi   Giliran: Rahmat   █████░░░░
Nebula   Submitted                    █████████
```

### HP Team

HP menampilkan:

- Nama dan warna Team.
- Nama anggota yang mendapat giliran.
- Workspace pertanyaan.
- Satu primary action yang jelas.
- Pesan oper HP setelah aksi.
- Status koneksi dan tombol retry.
- Tidak menampilkan leaderboard penuh saat soal aktif.

### Accessibility dan kondisi kelas

- Tombol dan token minimum tinggi 44 px.
- Tidak mengandalkan warna saja untuk membedakan status.
- Efek suara host dapat dimatikan.
- Timer mempunyai warning visual pada 15 dan 5 detik.
- Animasi tidak boleh memblokir input lebih dari 500 ms.

---

## 11. Arsitektur data

Jangan memakai `learning_game_sessions` untuk Team Quest. Tambahkan tabel baru melalui migration baru; jangan mengedit migration lama.

### 11.1 `team_quest_sessions`

| Kolom | Tipe | Catatan |
|---|---|---|
| `id` | bigint | PK |
| `uuid` | uuid/string unique | Dipakai pada channel dan URL, bukan sequential ID publik |
| `learning_game_id` | FK | Cascade delete |
| `synced_class_id` | FK | Kelas yang sedang bermain |
| `host_user_id` | FK users | Guru pemilik sesi |
| `pin` | string unique | PIN join, simpan uppercase |
| `status` | string | `waiting`, `question_active`, `revealing`, `leaderboard`, `completed`, `cancelled` |
| `current_item_index` | integer | Default -1 |
| `question_started_at` | timestamp nullable | Sumber timer server |
| `settings` | json nullable | Snapshot timer, team count, scoring, rotation |
| `started_at` | timestamp nullable |  |
| `completed_at` | timestamp nullable |  |
| timestamps |  |  |

### 11.2 `team_quest_teams`

| Kolom | Tipe | Catatan |
|---|---|---|
| `id` | bigint | PK |
| `team_quest_session_id` | FK | Cascade delete |
| `name` | string | Unik dalam session |
| `color` | string | Nilai allow-list, bukan CSS bebas |
| `score` | integer | Default 0 |
| `correct_answers` | integer | Default 0 |
| `current_turn_number` | integer | Default 0 |
| `active_member_id` | FK nullable | Member yang sedang mendapat turn |
| `device_user_id` | FK users nullable | Akun siswa pada HP aktif |
| `device_token_hash` | string nullable | Binding perangkat, jangan simpan token mentah |
| `last_seen_at` | timestamp nullable | Status koneksi |
| timestamps |  |  |

Unique index: `(team_quest_session_id, name)`.

### 11.3 `team_quest_members`

| Kolom | Tipe | Catatan |
|---|---|---|
| `id` | bigint | PK |
| `team_quest_team_id` | FK | Cascade delete |
| `synced_student_id` | FK | Restrict/cascade sesuai kebijakan siswa yang ada |
| `display_name` | string | Snapshot nama saat session |
| `turn_order` | unsigned integer | Mulai dari 1 |
| `squad_number` | unsigned tinyint | 1 atau 2 |
| `is_active` | boolean | False jika absen/di-skip guru |
| `contribution_count` | integer | Cache laporan, default 0 |
| timestamps |  |  |

Unique indexes:

- `(team_quest_team_id, synced_student_id)`.
- Satu `synced_student_id` hanya boleh muncul sekali dalam satu session; enforce melalui service/transaction dan feature test.

### 11.4 `team_quest_answers`

| Kolom | Tipe | Catatan |
|---|---|---|
| `id` | bigint | PK |
| `team_quest_team_id` | FK | Cascade delete |
| `game_item_id` | FK | Cascade delete |
| `answer_data` | json nullable | Draft/final answer Team |
| `correct_ratio` | decimal(5,4) | 0–1 |
| `is_correct` | boolean | True hanya jika ratio = 1 |
| `accuracy_points` | integer |  |
| `participation_points` | integer |  |
| `speed_points` | integer |  |
| `score_earned` | integer | Jumlah komponen |
| `status` | string | `draft`, `submitted`, `timed_out`, `revealed` |
| `time_taken_ms` | integer | Berdasarkan waktu server |
| `submitted_at` | timestamp nullable |  |
| timestamps |  |  |

Unique index: `(team_quest_team_id, game_item_id)` untuk mencegah jawaban ganda.

### 11.5 `team_quest_contributions`

| Kolom | Tipe | Catatan |
|---|---|---|
| `id` | bigint | PK |
| `team_quest_answer_id` | FK | Cascade delete |
| `team_quest_member_id` | FK | Cascade delete |
| `client_action_id` | uuid/string unique | Idempotency key |
| `turn_number` | integer | Turn saat aksi dilakukan |
| `action_type` | string | Allow-list per game type |
| `action_payload` | json nullable | Data minimum untuk audit/replay |
| `created_at` | timestamp |  |

Jangan menyimpan password, token mentah, atau data audio siswa dalam contribution.

### 11.6 Ekstensi konten soal

Tambahkan kolom nullable melalui migration baru:

- `game_items.metadata` JSON untuk audio, aliases, forbidden words, dan konfigurasi tipe baru.
- Jangan memindahkan struktur `options` tipe lama pada fase yang sama.

Tambahkan nilai `team_live` pada enum database `learning_games.game_mode` dan validation `GameController`. Migration harus mempertahankan `serious`, `live`, dan `faction`.

---

## 12. Backend boundaries

### Class baru

- `TeamQuestSession`
- `TeamQuestTeam`
- `TeamQuestMember`
- `TeamQuestAnswer`
- `TeamQuestContribution`
- `TeamQuestHostController`
- `TeamQuestPlayController`
- `TeamQuestScoringService`
- `TeamQuestTurnService`
- `TeamQuestStateService`

Controller harus tipis. Pembentukan kelompok, validasi turn, mutasi draft, scoring, dan snapshot state ditempatkan pada service dan dijalankan dalam transaction.

### Batas controller lama

File berikut tidak boleh dialihkan menjadi Team Quest:

- `ArenaPlayController`
- `LiveArenaHostController`
- `LearningGameSession`
- `GameAttempt`
- `GameAttemptAnswer`

Perubahan yang diizinkan pada `GameController` hanya:

- Mengizinkan mode `team_live`.
- Mengizinkan item type baru.
- Menyimpan metadata/media baru dengan validation yang aman.
- Menampilkan tombol Host Team Quest di index.

### Otorisasi

- Hanya host/pemilik game atau user dengan kemampuan `manage` yang dapat mengubah session.
- Siswa hanya dapat join jika terdaftar sebagai member aktif di session.
- Siswa hanya dapat membaca dan mengubah Team yang terikat pada perangkatnya.
- Semua endpoint mutation memakai CSRF, auth, policy/authorization, validation, throttle, dan transaction.
- Jangan percaya `team_id`, `member_id`, score, correctness, timer, atau active turn dari client.

---

## 13. Route contract baru

Nama dapat disesuaikan mengikuti konvensi repo, tetapi jangan memakai nama route Live Arena lama.

### Guru

```text
GET    /games/{game}/team-quest/setup
POST   /games/{game}/team-quest/sessions
GET    /team-quest/sessions/{session:uuid}/host/lobby
POST   /team-quest/sessions/{session:uuid}/host/groups/regenerate
PUT    /team-quest/sessions/{session:uuid}/host/groups
POST   /team-quest/sessions/{session:uuid}/host/start
GET    /team-quest/sessions/{session:uuid}/host/play
POST   /team-quest/sessions/{session:uuid}/host/question/show
POST   /team-quest/sessions/{session:uuid}/host/question/end
POST   /team-quest/sessions/{session:uuid}/host/question/next
POST   /team-quest/sessions/{session:uuid}/host/member/skip
POST   /team-quest/sessions/{session:uuid}/host/end
GET    /team-quest/sessions/{session:uuid}/report
```

### Siswa/perangkat Team

```text
GET    /team-quest/join
POST   /team-quest/join
GET    /team-quest/sessions/{session:uuid}/wait
GET    /team-quest/sessions/{session:uuid}/play
GET    /team-quest/sessions/{session:uuid}/state
POST   /team-quest/sessions/{session:uuid}/action
POST   /team-quest/sessions/{session:uuid}/submit
POST   /team-quest/sessions/{session:uuid}/heartbeat
```

Endpoint `state` menjadi snapshot canonical untuk initial load, reconnect, dan fallback polling.

---

## 14. Realtime dan recovery

Gunakan channel terpisah berbasis session UUID agar dua pelaksanaan game tidak saling menerima event:

```text
private-team-quest.session.{uuid}
private-team-quest.host.{uuid}
```

Tambahkan authorization channel pada `routes/channels.php`.

Event minimal:

- `TeamDeviceJoined`
- `TeamQuestStarted`
- `TeamQuestionDisplayed`
- `TeamTurnAdvanced`
- `TeamProgressUpdated`
- `TeamAnswerSubmitted`
- `TeamQuestionEnded`
- `TeamLeaderboardUpdated`
- `TeamQuestEnded`
- `TeamDeviceReset`

Aturan payload:

- Event tidak boleh menyiarkan model mentah.
- Payload pertanyaan harus melalui sanitizer per item type.
- Kunci jawaban, transcript, correction, aliases, dan `is_correct` tidak dikirim sebelum reveal.
- `TeamProgressUpdated` hanya berisi jumlah aksi/progres, bukan draft jawaban.

### Sanitizer contract per item type

Menyembunyikan `is_correct` saja tidak cukup karena beberapa struktur `options` sudah mengandung relasi jawaban. Gunakan DTO/presenter khusus dan uji payload JSON-nya.

- `sentence_rearrange` / `sequencing`: hapus field `order`; kirim token yang sudah diacak server-side.
- `matching`: jangan kirim pasangan `{left, right}` dalam satu object; kirim `left_items` dan `right_items` sebagai dua array terpisah dengan urutan acak.
- `grouping`: kirim daftar nama kategori dan satu `item_pool` acak; jangan kirim item di dalam kategori jawabannya.
- `listen_and_build`: jangan kirim `transcript`; kirim token acak hanya setelah audio pertama selesai.
- `error_hunter`: jangan kirim `correction` atau `explanation` sebelum reveal.
- `secret_taboo`: secret dan forbidden words hanya boleh dikirim melalui response terotorisasi untuk Team serta phase clue-giver, tidak melalui channel host/umum.
- `multiple_choice` / `true_false`: hapus `is_correct` dan acak opsi hanya jika mapping opsi tersimpan sehingga scoring tetap stabil.

Urutan acak harus dibuat di server dan disimpan/snapshot untuk setiap Team-answer. Refresh tidak boleh menghasilkan urutan token baru karena dapat merusak draft yang sudah dikerjakan.

### Fallback jaringan

WebSocket bukan satu-satunya sumber pemulihan state:

- Client mengambil snapshot `state` saat mount dan setelah reconnect.
- Jika tidak menerima event/heartbeat selama 3 detik ketika session aktif, client polling setiap 2 detik.
- Polling berhenti setelah WebSocket stabil.
- Timer dihitung dari `question_started_at` server, bukan dimulai ulang saat refresh.
- Setiap mutation idempotent melalui `client_action_id`.

---

## 15. Struktur frontend

Tambahkan halaman terpisah:

```text
resources/js/pages/TeamQuest/Host/Setup.tsx
resources/js/pages/TeamQuest/Host/Lobby.tsx
resources/js/pages/TeamQuest/Host/Play.tsx
resources/js/pages/TeamQuest/Join.tsx
resources/js/pages/TeamQuest/Wait.tsx
resources/js/pages/TeamQuest/Play.tsx
resources/js/pages/TeamQuest/Result.tsx
resources/js/pages/TeamQuest/Report.tsx
```

Komponen yang disarankan:

```text
resources/js/pages/TeamQuest/components/TurnBanner.tsx
resources/js/pages/TeamQuest/components/PassDeviceOverlay.tsx
resources/js/pages/TeamQuest/components/TeamProgressCard.tsx
resources/js/pages/TeamQuest/components/ConnectionStatus.tsx
resources/js/pages/TeamQuest/questions/SentenceRelayQuestion.tsx
resources/js/pages/TeamQuest/questions/CategoryRelayQuestion.tsx
resources/js/pages/TeamQuest/questions/MatchChainQuestion.tsx
resources/js/pages/TeamQuest/questions/ListenAndBuildQuestion.tsx
resources/js/pages/TeamQuest/questions/ErrorHunterQuestion.tsx
resources/js/pages/TeamQuest/questions/SecretTabooQuestion.tsx
```

Komponen pertanyaan Arena lama boleh diekstrak menjadi komponen presentational bersama hanya jika:

1. Perilaku individual sudah ditutup regression test.
2. Komponen tidak menyimpan aturan score/turn sendiri.
3. Callback Team Quest tetap divalidasi server.

Jika syarat belum terpenuhi, buat wrapper/komponen Team Quest baru agar perubahan tidak merusak Arena lama.

---

## 16. State machine

### Session

```text
waiting
  -> question_active
  -> revealing
  -> leaderboard
  -> question_active (soal berikutnya)
  -> completed

waiting/question_active/revealing/leaderboard
  -> cancelled (hanya oleh guru)
```

Transition yang tidak sesuai harus ditolak dengan HTTP 409.

### Jawaban Team per soal

```text
draft -> submitted -> revealed
draft -> timed_out -> revealed
```

Setelah `submitted` atau `timed_out`, mutation jawaban ditolak. Guru menutup soal harus mengubah semua draft yang tersisa menjadi `timed_out` dan menghitung partial score.

---

## 17. Tahap implementasi wajib

Setiap fase harus selesai dan lolos Definition of Done sebelum fase berikutnya.

### Phase 0 — Safety net Live Arena individual

- [x] Tambahkan feature test untuk route lobby/join/start/submit/end Live Arena individual.
- [x] Pastikan join Live Arena membuat `LearningGameSession` per siswa.
- [x] Pastikan finish Live Arena membuat `GameAttempt` per siswa.
- [x] Pastikan mode `serious` dan `faction` tidak berubah.
- [x] Catat payload event lama yang dipakai frontend.

**Status:** Selesai pada 28 Juli 2026. Verifikasi: 10 test, 63 assertion; seluruh suite hijau.

**Definition of Done:** seluruh test lama dan regression test baru hijau tanpa kode Team Quest. ✅

### Phase 1 — Domain dan migration Team Quest

- [x] Tambahkan nilai enum `team_live` tanpa menghapus nilai lama.
- [x] Tambahkan `game_items.metadata` nullable.
- [x] Buat lima tabel Team Quest.
- [x] Buat model, relation, factory, dan policy.
- [x] Buat state, turn, dan scoring service dengan automated test.
- [x] Tambahkan database constraint dan index yang disebutkan pada bagian arsitektur data.

**Status:** Selesai pada 28 Juli 2026. Verifikasi: migration rollback→migrate berhasil; 16 test dan 108 assertion hijau; typecheck dan production build hijau.

**Definition of Done:** migration up/down berhasil pada database uji, model relation teruji, dan Live Arena regression tetap hijau. ✅

### Phase 2 — Setup kelompok dan lobby

- [x] Tambahkan pilihan Team Quest pada Form dan label mode pada Index.
- [x] Tambahkan tombol **Host Team Quest** hanya untuk game published dengan mode `team_live`.
- [x] Implementasikan setup kelas, jumlah kelompok, auto-balance, pemindahan member, absent toggle, dan active squad preview.
- [x] Buat session UUID dan PIN unik.
- [x] Implementasikan join berdasarkan membership siswa.
- [x] Implementasikan satu device binding per Team dan reset oleh guru.
- [x] Buat lobby realtime dengan fallback snapshot/polling.

**Status:** Selesai pada 28 Juli 2026. Verifikasi: route setup/lobby/join/state/heartbeat terdaftar; skenario 4×5 dan 2×10 teruji; 19 test dan 138 assertion hijau; typecheck serta production build hijau.

**Definition of Done:** skenario 4×5 dan 2×10 dapat disiapkan, seluruh kelompok join, refresh aman, dan perangkat kedua ditolak atau meminta reset guru. ✅

### Phase 3 — Mesin turn dan Sentence Relay

- [x] Implementasikan state machine host.
- [x] Implementasikan sanitizer pertanyaan.
- [x] Implementasikan server-authoritative turn.
- [x] Implementasikan draft answer dan contribution idempotent.
- [x] Implementasikan Sentence Relay dengan placement/remove/swap/review.
- [x] Implementasikan timer server, timeout, reveal, scoring, dan leaderboard.
- [x] Implementasikan reconnect dan polling fallback.

**Status:** Selesai pada 28 Juli 2026. Verifikasi: vertical flow host/device, stable shuffled tokens, reviewer turn, stale-turn rejection, UUID idempotency, server timer, timeout satu kali, scoring, reveal, next question, completion, dan answer-leakage test; 25 test dan 189 assertion hijau; typecheck serta production build hijau.

**Definition of Done:** satu sesi lengkap Sentence Relay berjalan pada 4 HP, semua anggota mendapat turn, double click tidak menggandakan aksi, dan jawaban tidak bocor. ✅

### Phase 4 — Category Relay dan Match Chain

- [x] Tambahkan daftar action type yang valid untuk `grouping` dan `matching`.
- [x] Implementasikan snapshot/sanitizer tanpa pemetaan jawaban pada payload aktif.
- [x] Implementasikan komponen HP Team Quest untuk Category Relay dan Match Chain.
- [x] Implementasikan server-authoritative action, partial scoring, dan reviewer turn.
- [x] Tambahkan statistik akurasi, partisipasi, waktu, dan poin pada reveal per Team.
- [x] Verifikasi sesi campuran Sentence/Category/Match tanpa reset state manual.

**Status:** Selesai pada 28 Juli 2026. Verifikasi: snapshot opaque dan stabil, answer-leakage guard, action/turn atomic, reviewer turn, perfect dan partial scoring, reveal statistics, mixed-session transition; seluruh suite 29 test dan 242 assertion hijau; typecheck, production build, 20 route Team Quest, scoped Pint, dan diff check hijau.

**Definition of Done:** game campuran Sentence/Category/Match dapat dijalankan dalam satu session tanpa reset state manual.

### Phase 5 — Listening dan Error Hunter

- [x] Implementasikan upload MP3/WAV/M4A tervalidasi (MIME, extension, dan 10 MB) serta penghapusan file ketika diganti, item dihapus, tipe berubah, atau game dihapus.
- [x] Tambahkan metadata dan authoring UI Listen and Build.
- [x] Putar audio hanya dari host dengan replay counter dan limit server-side.
- [x] Kunci token HP sampai pemutaran pertama ditandai selesai oleh host.
- [x] Tambahkan authoring, sanitizer, action, play UI, partial scoring, dan reviewer turn Error Hunter.
- [x] Pastikan transcript, correction, dan explanation tidak bocor sebelum reveal.
- [x] Verifikasi automated test, typecheck, production build, route, scoped Pint, dan diff check.
- [x] Lakukan pemeriksaan manual host dengan sesi guru login: putar WAV nyata dari LCD, pastikan token terbuka setelah audio selesai, dan pastikan replay berhenti pada limit `2/2`.
- [x] Periksa UI host pada LCD 1366×768: empat kartu kelompok terbaca tanpa overflow dan kalimat rich text tidak menampilkan tag HTML mentah.
- [x] Periksa UI pemain/HP pada lebar 360 px menggunakan akun siswa dan perangkat yang benar-benar terikat ke salah satu Team.

**Status:** Selesai pada 28 Juli 2026. Simulasi IX A menghasilkan 4 Team × 5 siswa; WAV nyata berhasil diputar dua kali dari host, token terbuka setelah putaran pertama selesai, replay ketiga terkunci oleh UI/server, dan console bersih. QA LCD 1366×768 menemukan lalu memperbaiki tag HTML mentah serta kartu Team keempat yang terpotong; verifikasi ulang menunjukkan seluruh kartu berada di viewport tanpa overflow. UI host lolos smoke check 360 px dan tampilan pemain/HP 360 px diterima pengguna melalui pemeriksaan browser. Verifikasi akhir: seluruh suite 33 test dan 305 assertion hijau; typecheck, production build, dan scoped Pint hijau.

**Definition of Done:** audio bekerja dari LCD, replay limit dipatuhi, dan kedua tipe lolos feature test, typecheck, build, serta pemeriksaan manual.

### Phase 6 — Secret Taboo

- [x] Tambahkan authoring secret word, aliases, forbidden words, dan duration 15–120 detik dengan normalisasi serta validasi server.
- [x] Implementasikan role clue-giver dan recorder sebagai peran server-authoritative serta batasi kebutuhan kontribusi pada kedua peran tersebut.
- [x] Implementasikan handoff HP, reveal-to-clue-giver satu kali selama 5 detik, hide server-side, lalu handoff ke recorder.
- [x] Implementasikan exact/alias scoring yang case-insensitive, whitespace-normalized, dan rotasi role pada soal berikutnya.
- [x] Pastikan secret/forbidden words tidak masuk answer snapshot, host/LCD state, atau payload umum; hanya response Team terotorisasi pada phase reveal.
- [x] Jelaskan pada authoring, HP, dan LCD bahwa forbidden-word checking belum otomatis dan memakai kejujuran kelas.
- [x] Tambahkan UI pemain responsif untuk handoff, secret countdown, forbidden words, recorder input, dan tebakan terkunci.
- [x] Tambahkan feature test authoring, validation, leakage guard, timer 5 detik, role handoff, alias scoring, contribution, dan role rotation.
- [x] Perbaiki transisi form Create→Edit agar `_method` berubah ke PUT dan izinkan FormData menghilangkan array opsi kosong pada tipe tanpa opsi.
- [x] Verifikasi browser authoring dan host live 4 Team; label, durasi, aturan manual, progress, dan active role tampil tanpa secret word serta console error bersih.

**Status:** Selesai pada 28 Juli 2026. Verifikasi akhir: seluruh suite 35 test dan 344 assertion hijau; test fokus Secret Taboo + Listening 6 test dan 102 assertion hijau; typecheck, production build, 22 route Team Quest, scoped Pint, dan diff check hijau. Browser QA membuat game `QA Phase 6 — Secret Taboo` dan sesi IX A 4 Team; host/LCD tidak memuat secret word dan console bersih. QA browser juga menemukan lalu memperbaiki bug `_method=post` pada transisi Create→Edit serta validasi FormData untuk `options` kosong.

**Definition of Done:** secret word tidak muncul di host/LCD atau event umum dan role berpindah pada soal berikutnya.

### Phase 7 — Report, hardening, dan rollout

- [x] Buat halaman report per sesi berisi skor Team, correct ratio, durasi, kontribusi total, kontribusi setiap anggota, dan rincian jawaban per soal.
- [x] Tambahkan export CSV UTF-8 yang memuat ringkasan Team dan kontribusi anggota.
- [x] Arahkan report game `team_live` ke report sesi Team Quest terbaru tanpa mengubah report mode game lain.
- [x] Catat host mutation penting pada `SystemAuditLog`: create session, update group, reset device, start/audio/reveal/leaderboard/next/complete, dan cancel.
- [x] Perbarui heartbeat/polling agar `last_seen_at` mencerminkan perangkat aktif dan tampilkan indikator `Terhubung`/`Terputus` kepada guru.
- [x] Tambahkan navigasi akhir menuju report dan daftar game pada halaman host maupun pemain.
- [x] Uji rate limit, authorization siswa/guru lain, isolasi Team, duplicate submit, refresh/presence, reconnect fallback, timeout, dan end-session dari setiap state aktif.
- [x] Jalankan simulasi classroom gabungan: host browser login dengan 4 Team IX A, lifecycle leaderboard hingga completed, indikator disconnect, report, dan console; binding empat perangkat serta kondisi concurrency diverifikasi oleh feature test.
- [x] Verifikasi compatibility Live Arena individual, Serious, dan Faction melalui safety-net test yang tetap hijau.

**Status:** Selesai pada 28 Juli 2026. Verifikasi akhir: seluruh suite 41 test dan 483 assertion hijau; test fokus Phase 7 sebanyak 6 test dan 139 assertion hijau; typecheck, production build, 24 route Team Quest, scoped Pint, dan diff check hijau. Browser QA menyelesaikan sesi `QA Phase 6 — Secret Taboo`, memastikan empat Team berstatus `Terputus` ketika heartbeat kedaluwarsa, membuka report lengkap, dan menghasilkan console tanpa error. Ekspor CSV dan redirect report divalidasi oleh feature test. Pemeriksaan Pint seluruh repo masih menemukan formatting debt pada file lama di luar perubahan Team Quest; file tersebut sengaja tidak diubah.

**Definition of Done:** acceptance criteria produk dan compatibility matrix seluruhnya lulus.

---

## 18. Acceptance test minimum

### Compatibility

- [x] Guru masih dapat membuat dan menjalankan `live` individual.
- [x] Dua siswa pada dua HP menghasilkan dua `LearningGameSession` individual.
- [x] Live Arena individual tetap membuat `GameAttempt` per siswa saat selesai.
- [x] Team Quest tidak membuat `LearningGameSession` atau `GameAttempt` individual.
- [x] Faction leaderboard tidak berubah.

### Setup dan join

- [x] Guru dapat membuat 4 Team × 5 siswa dari kelas berisi 20 siswa.
- [x] Guru dapat membuat 2 Team × 10 siswa dengan dua mini-squad per Team.
- [x] Siswa tidak dapat tergabung pada dua Team dalam session yang sama.
- [x] Siswa di luar roster tidak dapat join.
- [x] Satu Team tidak dapat memiliki dua device aktif tanpa reset guru.

### Turn dan answer integrity

- [x] Member di luar giliran mendapat HTTP 409/422 dan draft tidak berubah.
- [x] `client_action_id` yang sama hanya menghasilkan satu contribution.
- [x] Pergantian turn dan penyimpanan contribution bersifat atomic.
- [x] Refresh memulihkan draft, active member, timer, dan score yang benar.
- [x] Submit kedua untuk Team/item yang sama tidak menggandakan score.
- [x] Timeout menyimpan draft dan partial score satu kali.

### Security

- [x] Payload aktif tidak berisi kunci jawaban.
- [x] Team A tidak dapat membaca atau mengubah draft Team B.
- [x] Siswa tidak dapat memanggil endpoint host.
- [x] Guru yang bukan pemilik/tidak berwenang tidak dapat mengubah session.
- [x] Upload audio menolak extension/MIME/size yang tidak valid.

### Classroom UX

- [x] LCD terbaca pada resolusi 1366×768.
- [x] HP nyaman digunakan pada lebar 360 px.
- [x] Status disconnect terlihat oleh guru.
- [x] Mode polling mengambil alih ketika WebSocket terputus.
- [x] Guru dapat mengakhiri session dari setiap state aktif.
- [x] Semua halaman akhir mempunyai tombol kembali ke dashboard/game report.

---

## 19. Verification commands

Jalankan dari root repo setelah setiap fase:

```powershell
php artisan route:list
php artisan test
vendor\bin\pint --test
npm run typecheck
npm run build
```

Untuk perubahan migration, verifikasi juga pada database testing bersih melalui test suite `RefreshDatabase`. Jangan menjalankan `migrate:fresh` pada database development yang berisi data tanpa persetujuan eksplisit.

Manual matrix sebelum release:

1. Live Arena individual: minimal 2 akun siswa dan 2 browser/device.
2. Team Quest 4×5: satu host dan 4 browser/device.
3. Team Quest 2×10: validasi rotasi squad A/B.
4. Refresh salah satu HP saat soal aktif.
5. Putuskan Reverb lalu pastikan polling fallback bekerja.
6. Double-click action dan submit.
7. Akhiri sesi saat waiting, question active, revealing, dan leaderboard.

---

## 20. Current repo map yang sudah diverifikasi

Fondasi yang sudah ada dan harus dihormati:

- `routes/web.php`: route guru Live Arena berada di area guru; route pemain berada di middleware `can:siswa`.
- `app/Http/Controllers/LiveArenaHostController.php`: runtime host Live Arena individual.
- `app/Http/Controllers/ArenaPlayController.php`: join, play, submit, dan scoring individual.
- `app/Models/LearningGameSession.php`: sesi aktif per siswa.
- `app/Models/GameAttempt.php` dan `GameAttemptAnswer.php`: hasil individual.
- `app/Models/GameItem.php`: `item_type`, `options`, dan konten soal bersama.
- `resources/js/pages/Game/Host/*`: host individual.
- `resources/js/pages/Arena/Live*.tsx`: pemain individual.
- `resources/js/pages/Arena/Questions/*`: fondasi UI tipe pertanyaan.
- `resources/js/pages/Game/Form.tsx`: saat ini mendukung `serious`, `live`, `faction` dan beberapa `item_type`.
- Event existing menggunakan channel `lobby.{gameId}`, `live-arena.{gameId}`, dan `host-arena.{gameId}`. Team Quest tidak boleh memakai channel ini.

Tipe soal yang sudah tersedia:

- `multiple_choice`
- `true_false`
- `matching`
- `grouping`
- `sequencing`
- `fill_in_the_blank`
- `jumbled_word`
- `spelling_bee`
- `sentence_rearrange`

Karena Sentence Relay, Category Relay, dan Match Chain sudah mempunyai fondasi data/scoring, tiga game tersebut wajib menjadi rilis pertama sebelum menambahkan tipe baru.

---

## 21. Definition of complete

Team Quest dinyatakan siap dipakai di kelas hanya jika:

1. Guru dapat menyelesaikan sesi end-to-end dengan 2–8 HP.
2. Rotasi anggota berjalan dan divalidasi server.
3. Sesi pulih setelah refresh atau gangguan realtime.
4. Tidak ada answer key leakage.
5. Laporan membedakan skor Team dan kontribusi individual.
6. Live Arena individual, Serious, dan Faction tetap berfungsi.
7. Automated test, typecheck, formatting check, build, dan manual matrix lulus.

Prioritas produk tetap: **mesin Team Quest yang stabil terlebih dahulu; animasi, power-up, dan polish visual dikerjakan setelah alur kelas terbukti aman.**
