Skip to main content

Living Word Classroom Reporting — Teacher View (Phase D-3)

STATUS: SHIPPED 2026-06-04 (PR #740). Founder resolved R1–R4: R1=performance, R2=instrument, R3=AGGREGATE-only (no per-child scores), R4=AI-Bridge framing. Implemented: lw_classroom_student_activity table (completion events), POST /classrooms/activity (device-cookie, no-op if not joined), in-game fire-and-forget useEffect on enemy defeat, owner-scoped GET /classrooms/[id]/report (aggregate), and the ClassroomReportSection on the manage page. Instrumentation chain preview-verified; report UI is on the auth-gated page (founder manual confirm).

§0 — The load-bearing finding (verified 2026-06-04)

The deep-dive roadmap calls the teacher reporting view "cheap / high-value — the quiz-attempt data is already written and never surfaced, mostly a read on existing data." That is not true for classroom students. Verified against the live DB:

  • game_quiz_attempts.user_id is NOT NULL → attempts are recorded only for authenticated users (cloud-save accounts). There is no device_id and no classroom_id column.
  • lw_classroom_students rows are anonymous + device-based (founder-locked D2: first name only, no email, no password, per-device cookie). A classroom student has no auth.users row, so they generate zero game_quiz_attempts rows.

Therefore: for the typical classroom (kids who joined with a code + nickname, never signed in), there is no per-student quiz-performance data in existence to "read." A meaningful performance report requires new instrumentation, not a read.

§1 — What we CAN report today with zero new instrumentation

From lw_classroom_students + lw_classroom_events (already built, Phase D):

  • Roster: nickname, joined date.
  • last_played_at (column exists on lw_classroom_students — but NOTE: verify whether anything currently WRITES it; if nothing updates it on play, it is always null and must be wired).
  • Join funnel: counts of student_joined, failed-join attempts, code rotations (lw_classroom_events).

That is an engagement/roster report, not a learning/performance report. It answers "who joined and when," not "how is each student doing on the quizzes."

§2 — The decisions (founder)

#DecisionOptions
R1Scope of v1 reporting.(a) Engagement-only (roster + joined/last-played + join funnel) — buildable now, no new writes, but no quiz performance. (b) Performance (per-student/score/topic mastery) — requires R2.
R2Instrument anonymous-student activity? Performance reporting needs a new device-keyed write path: a lw_classroom_student_activity table (FK → lw_classroom_students) written when a joined device completes a quiz, capturing score/percentage/scene/difficulty/timestamp. This is genuinely new code in the game's quiz-completion flow, gated on "this device is in a classroom."(a) Yes, instrument. (b) No — keep engagement-only until demand proven.
R3Minors' performance data — privacy posture. Even anonymous (nickname-only), per-student score data about children is sensitive.(a) Aggregate-only to the teacher (class averages, topic gaps — no per-child scores). (b) Per-student scores visible to the owning teacher only (RBAC: owner_user_id = auth.uid()), never exported, never to other students. (c) Per-student + printable/exportable.
R4AI-Bridge framing. Reporting must not become a surveillance or judgment surface.Confirm copy frames it as "where your class needs a hand," points teachers to teach the gap (bridge to the human teacher), never ranks/shames a child.

§3 — Proposed v1 (pending R1–R4), illustrative only

If R1=engagement-only: a "Class activity" section on /living-word/classrooms/[id] — roster with joined/last-played, a small join-funnel summary. Read-only over existing tables. ~half a day. (Requires wiring last_played_at updates if not already written.)

If R1=performance + R2=instrument + R3=aggregate: add lw_classroom_student_activity (migration, founder-gated apply), write to it from the quiz-completion path when the device is classroom-joined, then a reporting section showing class-level mastery by scene/topic + per-student last-played (no per-child score list under R3a). Multi-PR: instrument → migration → reporting UI. ~2–4 days.

§4 — PASS/FAIL (to be completed once R1–R4 are set)

TBD after decisions. At minimum: owner-scoped (a teacher sees only their own classes — 404 otherwise); no minor PII beyond nickname; AI-Bridge copy reviewed; deployed-preview verification of the report rendering with seeded data.

§5 — Recommendation

Start with R1=(a) engagement-only for v1 (real value: "did my students actually join and play?", buildable now over existing data, zero new minor-data collection), and treat performance reporting as its own later sub-phase once a teacher actually asks for it — at which point R2/R3 get decided with that real demand in view. This avoids instrumenting children's score data speculatively.