Problem Statement
The rooms API layer applies the repo's thin-handler rule unevenly, so the seam between API and model doesn't sit where the convention says it does. Most room handlers are the clean shape (guard, then one delegate call — the same shape the issues and votes handlers use throughout), but three are not: the room read re-implements the auth-identity-to-app-user lookup inline even though the auth model already exports exactly that interface (used by the users handlers); the auto-complete toggle performs two-step orchestration and a raw database write in the handler; and rename writes the database directly with no model call at all. Separately, the user-rooms listing is a live query wired to a permanent empty-list stub — zero callers anywhere in the app or tests, a pass-through to nothing that fails the deletion test in the good way: deleting it loses no behaviour. Note: investigation confirmed the auto-complete toggle already routes countdown cancellation through the voting round module's own interface, so there is no ADR-0002 sole-writer breach here — the defect is purely that orchestration lives at the wrong altitude.
Solution
Level the rooms API layer to the uniform thin shape: every handler validates, guards, and makes one delegate call. The room read uses the auth model's existing optional-user resolution instead of its inline copy. The auto-complete toggle and rename move behind the rooms model seam as two new deep operations — the toggle internally cancels the countdown via the voting round module's existing interface (mirroring the established precedent where the users model flips the spectator flag and hands off to the round's dropVoter), and rename patches name and activity in one place. The dead user-rooms listing is deleted end to end. The newly model-homed operations gain the rooms model's first unit tests, including the regression case that actually protects the round module's countdown ownership.
User Stories
- As a room owner, I want renaming and toggling auto-complete to behave exactly as today, so that this cleanup is invisible in use.
- As a room owner, I want toggling auto-complete off while an auto-reveal countdown is armed to cancel that countdown and leave the scheduled reveal inert, so that no reveal fires under a setting I just disabled.
- As a maintainer, I want every rooms handler to be guard-plus-one-delegate, so that reading the API layer tells me nothing is hiding there.
- As a maintainer, I want the identity-to-user resolution to have one implementation behind the auth model's interface, so that a change to user lookup lands in one place.
- As a maintainer, I want room writes homed in the rooms model, so that room behaviour is testable behind the model seam without simulating HTTP or auth.
- As a maintainer, I want the dead user-rooms stub deleted rather than kept "just in case", so that no reader wastes time tracing a query that returns nothing by design.
- As a contributor, I want the rooms model to have unit tests, so that the toggle/countdown interaction is pinned by an assertion rather than by care.
- As a future implementer of a real "my rooms" feature, I want to start from a clean absence rather than a misleading stub, so that the feature is designed on purpose.
Implementation Decisions
- The room read handler delegates identity resolution to the auth model's existing optional-auth-user interface (the same one the users handlers already call) and then makes its single delegate call. No new interface is invented.
- The rooms model gains two operations: a rename operation (patches name and bumps activity in one write) and an auto-complete toggle (loads the room, cancels the countdown through the voting round module's existing cancel interface, flips the flag). The API handlers become guard-plus-one-call.
- The whole toggle moves into the model rather than leaving a two-call sequence in the handler — chosen to mirror the existing precedent where a model operation with round consequences calls the round module itself (the users model's spectator flip handing off to dropVoter), and to match the one-call handler shape everywhere else.
- This adds a rooms-model-to-voting-round-model import edge. A function-level cycle of the same shape already ships in production between the rooms and users models, so this is an accepted pattern, but the type-check must be run as the first post-edit verification, not assumed.
- The voting round module is unchanged: its countdown cancel interface is reused as-is, and the countdown fields' write path stays exclusively inside it — ADR-0002's sole-writer rule is preserved and reinforced.
- The user-rooms query and its model stub are deleted end to end. Implementing a real rooms-for-user listing was considered and rejected: it is a feature (new indexing and design work), not an architecture cleanup, and no caller or planned consumer exists.
- Already-thin handlers (show cards, reset game, cancel countdown, update activity) are untouched.
Testing Decisions
- Good tests exercise the rooms model operations through their interface and assert observable state — the flag value, the countdown fields, the scheduled function's status — never the handler layer or auth guards (the repo's convex-test convention tests exclusively at the model layer; keep that).
- New rooms model test file (first one for this model; prior art: the voting round tests — convex-test in the edge-runtime vitest project, local seed helpers per that file's convention).
- Cases: rename sets the name and bumps activity; toggle flips the flag with no countdown armed (no-op cancel path); and the key regression — auto-complete on, countdown armed by all votes being in, toggle called: flag flips off, both countdown fields clear, and the scheduled reveal shows cancelled in the scheduled-functions system table (same assertion shape as the round module's existing cancel-countdown tests).
- No handler-level auth-simulated tests: that would introduce a new testing pattern, out of scope for this cleanup.
Out of Scope
- Implementing a real rooms-for-user listing feature.
- Any change to the voting round module or ADR-0002 (no violation found; no ADR update warranted).
- The permission guard's internals and other model modules.
- Shared convex-test seeding utilities (the new test file duplicates small local helpers, matching the existing convention; consolidating test scaffolding is a separate decision).
Further Notes
- Independent of the other architecture-review PRDs; can land in any order.
- Verification order: type-check first (confirms the new model-to-model import edge compiles for this specific pair), then the new model tests, then lint.
Problem Statement
The rooms API layer applies the repo's thin-handler rule unevenly, so the seam between API and model doesn't sit where the convention says it does. Most room handlers are the clean shape (guard, then one delegate call — the same shape the issues and votes handlers use throughout), but three are not: the room read re-implements the auth-identity-to-app-user lookup inline even though the auth model already exports exactly that interface (used by the users handlers); the auto-complete toggle performs two-step orchestration and a raw database write in the handler; and rename writes the database directly with no model call at all. Separately, the user-rooms listing is a live query wired to a permanent empty-list stub — zero callers anywhere in the app or tests, a pass-through to nothing that fails the deletion test in the good way: deleting it loses no behaviour. Note: investigation confirmed the auto-complete toggle already routes countdown cancellation through the voting round module's own interface, so there is no ADR-0002 sole-writer breach here — the defect is purely that orchestration lives at the wrong altitude.
Solution
Level the rooms API layer to the uniform thin shape: every handler validates, guards, and makes one delegate call. The room read uses the auth model's existing optional-user resolution instead of its inline copy. The auto-complete toggle and rename move behind the rooms model seam as two new deep operations — the toggle internally cancels the countdown via the voting round module's existing interface (mirroring the established precedent where the users model flips the spectator flag and hands off to the round's dropVoter), and rename patches name and activity in one place. The dead user-rooms listing is deleted end to end. The newly model-homed operations gain the rooms model's first unit tests, including the regression case that actually protects the round module's countdown ownership.
User Stories
Implementation Decisions
Testing Decisions
Out of Scope
Further Notes