Skip to content

WI-014: Move reviews onto the session, add session tags

WI-014: Move reviews onto the session, add session tags

Problem

Two things, one a defect and one a gap.

The defect. review lives on shelf_entries while rating lives on sessions. ADR-0006 indicts this in its own words:

Goodreads is shelf-first and bolted rereads on badly — still one rating and one review per book.

WI-010 fixed the rating and left the review Goodreads-shaped. Writing a new review today overwrites what you thought last time, which is the exact loss the ADR exists to prevent. It is cheap to fix now and painful once there are reviews to migrate.

The gap. Notable moments — a comeback, a rules argument, a game decided on the last turn — can only go in free-text notes. Nothing is queryable, so there is no way to surface them in a feed or filter by them.

In scope

  • sessions.review (text, nullable) — the prose opinion for this play.
  • shelf_entries.review becomes a projection of the most recent session review, maintained by the existing trigger (ADR-0021), and raises on direct write exactly as rating does.
  • sessions.tags (text[], default {}) with a GIN index.
  • Regenerated packages/shared schemas; newShelfEntrySchema must now omit review as well as rating.

Out of scope

  • A moment/tag taxonomy with its own table. Free tags first; formalise only if they are actually used.
  • Backfill. No production data exists.
  • Teaching cost — WI-015 (#32). Mechanic reactions were deferred there; see that spec for why.

Definition of done

  • sessions.review exists and is nullable — one-tap logging is unaffected (ADR-0007).
  • A newer session review projects to the shelf and the older session’s review is unchanged — the rating invariant, applied to prose.
  • Deleting the most recent session re-projects the review, and clears it when no reviewed session remains.
  • Writing shelf_entries.review directly raises, with the same wording style as the rating guard.
  • sessions.tags accepts an array, defaults to empty, and is GIN-indexed; a query filtering by tag uses the index.
  • newShelfEntrySchema omits both rating and review; a test asserts it.
  • Gate green.

Verification

Terminal window
pnpm --filter db test -t "invariant"
pnpm --filter @tabletop/shared test
pnpm gate

Notes

The trigger already recomputes on insert, update and delete for rating. Extending it to review is the same query with a second column, so the delete and clear paths come along for free — but they still need their own tests, because “it should work the same way” is not evidence.

Tags are deliberately uncontrolled. A vocabulary imposed before anyone has logged a play is a guess; ADR-0017’s controlled vocabulary exists for catalogue mechanics, which is a different problem.