fix(mana/web): bump Dexie schema to v3 for the who tables

The who module landed with whoGames + whoMessages declared inside
db.version(1). That's wrong: existing browsers (every tester
including the developer's own) already had Dexie persisted at v1
with the OLD schema (no who tables). When the new bundle declared
v1 with a different schema, Dexie refused the schema diff and the
optimistic insert in whoGamesStore.sendMessage silently failed —
neither the user's message nor the server reply appeared in the
PlayView, even though the deck picker and game start worked
(those write whoGames which has the same schema-mismatch issue
but the failure is only visible once a chat starts).

The pre-launch cleanup doc says "edit version(1) directly until
launch", but in practice that bricks every developer's local
state on every additive change. The right rule is: bump the
version for additive table additions even pre-launch — Dexie
handles the additive case cleanly with no upgrade function.

This commit:
  - Removes whoGames + whoMessages from db.version(1)
  - Adds them to a new db.version(3) block (v2 was already taken
    by the bodyExercises / bodyRoutines / etc. body module)
  - Existing IndexedDB databases at v1 or v2 will run the
    additive upgrade automatically on next page load. No data
    loss, no upgrade function needed (no rows to migrate yet).

Also: add a console.error to PlayView's send() catch so future
sendMessage failures actually show up in DevTools instead of
only being visible as a tiny error banner near the input.

Fixes the "ich tippe eine frage und nichts passiert" symptom
the developer hit on the first end-to-end live test of the who
module on production.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-09 16:14:39 +02:00
parent 5480a8dfdf
commit 6124ae9cd6

View file

@ -99,14 +99,6 @@ db.version(1).stores({
cards: 'id, deckId, difficulty, nextReview, order, [deckId+order]',
deckTags: 'id, deckId, tagId, [deckId+tagId]',
// ─── Who (appId: 'who') ───
// LLM character-guessing game. whoGames holds one row per session
// (status, deck, character id, message count); whoMessages holds the
// chat scrollback. Standard plaintext index pattern: id, FK, status,
// timestamps for sort/filter; content + revealedName encrypted.
whoGames: 'id, status, deckId, startedAt, finishedAt, [status+startedAt]',
whoMessages: 'id, gameId, sender, createdAt, [gameId+createdAt]',
// ─── Zitare (appId: 'zitare') ───
zitareFavorites: 'id, quoteId',
zitareLists: 'id',
@ -303,6 +295,40 @@ db.version(1).stores({
manaLinks: 'id, sourceAppId, sourceRecordId, targetAppId, targetRecordId',
});
// Schema version 2 — adds the unified Body module (combined fitness training
// + body composition tracking). Additive only; no v1 tables touched.
//
// Index strategy:
// - bodySets indexes [workoutId+order] so the per-workout view can do a
// range scan instead of loading every set and filtering in JS.
// - bodyMeasurements indexes [type+date] for the per-metric trend chart
// (e.g. "weight over time").
// - bodyChecks indexes `date` so the daily upsert can `.where('date')`.
// - bodyPhases indexes `endDate` to find the active (open) phase quickly.
db.version(2).stores({
bodyExercises: 'id, muscleGroup, equipment, isArchived, isPreset',
bodyRoutines: 'id, order, isArchived',
bodyWorkouts: 'id, startedAt, endedAt, routineId, [endedAt+startedAt]',
bodySets: 'id, workoutId, exerciseId, order, [workoutId+order], [exerciseId+createdAt]',
bodyMeasurements: 'id, date, type, [type+date]',
bodyChecks: 'id, date',
bodyPhases: 'id, kind, startDate, endDate',
});
// Schema version 3 — adds the Who module (LLM character-guessing game).
// Additive only; no v1/v2 tables touched.
//
// Index strategy:
// - whoGames indexes status + startedAt + the [status+startedAt] composite
// so the past-games ListView can sort active vs finished cleanly without
// loading the full set every render.
// - whoMessages indexes [gameId+createdAt] for the chat scrollback query
// (range scan inside one game's messages, ordered by time).
db.version(3).stores({
whoGames: 'id, status, deckId, startedAt, finishedAt, [status+startedAt]',
whoMessages: 'id, gameId, sender, createdAt, [gameId+createdAt]',
});
// ─── Sync Routing ──────────────────────────────────────────
// SYNC_APP_MAP, TABLE_TO_SYNC_NAME, TABLE_TO_APP, SYNC_NAME_TO_TABLE,
// toSyncName() and fromSyncName() are now derived from per-module