fix(api): unblock the dev deploy — seed-places prisma stub tracks Room→Space #5
Loading…
Reference in a new issue
No description provided.
Delete branch "probe-push-test"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Lands the 20 commits already on
mainlocally plus the one-file fix that makes the quality gate green.deploy-dev#689 failed at the quality gate ond0bd5126and skipped all eleven downstream jobs, so dev has not rolled since 2026-08-19. Reproduced in a clean worktree: the ONLY failing check in the whole gate wasapps/api/test/seed-places.test.ts(2 tests) — the stub still declaredroom/calendarRoomafter0217c676renamed the model to Space.Verified at HEAD with only that file applied: apps/api 28 files / 302 tests pass.
Opened as a PR because
git pushtomainis rejected withreference already exists—git-receive-packis not advertisingrefs/heads/mainon this server, thoughls-remotereturns it.Step 6 (docs/specs/2026-08-18): places.{rooms,calendars,availability} procedures with output schemas + strict inputs (availability is full-PUT — isOpen required); getCalendarEvents split admin/public with the mode server-pinned; bookingRequests.messages.{post,list,markRead} (post rate-limited 30/h per actor, proven to write nothing on exhaustion) + attachments.{add,list,delete} with storageKey claim-once (interim guard until the Step-8 upload mint); notify composed at the router layer mirroring the received-notification exactly. Embed subjectType "calendar": authz via the calendar's place across upsert/list/revoke, widget payloads structurally leak-free (serialized-payload pins), checkoutEligible false fail-closed. Public read collapses place-gate codes to CALENDAR_NOT_FOUND (no existence oracle). ROOM_NOT_FOUND / CALENDAR_NOT_FOUND / CANNOT_RETIRE_LAST_ACTIVE_ROOM / CALENDAR_SLUG_TAKEN promoted to USER_FACING with en+es copy (parity gates green). Hold layer untouched (deferred). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>`PlatformSettingsRepo.getAll()` is a `findMany` with no `where`, and it sits on the hottest path in the API: `getFeatureGates` calls it, and that use case runs from `features.*`, `events.*` and `ticket-types.*` — so one batched tRPC request commonly triggered it more than once. Measured in production over 7d (2026-08-19, `docs/ops/resource-burn.md`): **37,700 full-table reads of PlatformSetting against 18,950 tRPC requests** — ~2 per request — for a table that changes on the order of days. That was ~2 of the ~17.8 queries/request behind Neon's +31% growth since 2026-07-30. Adds `withCachedPlatformSettings`, a read-through cache applied at the composition root beside `withGuardedRepos` rather than inside `createPrismaRepos`. A repo that silently caches is a nasty surprise when you go hunting for stale reads, so the caching is a visible wiring decision. Also collapses a cold-cache stampede: concurrent callers share one in-flight load. Without that, a batched request on a cold cache lets every caller miss and issue its own full-table read — the exact pileup being removed. TTL is 60s, not the 10 minutes `createFooterDefaultsCache` uses, because these rows drive FEATURE GATES. A toggle that takes ten minutes to appear reads as a broken toggle; a minute reads as a save. Invalidation, which is subtler than it looks: - The five writers that call `repos.platformSettings.upsert(...)` directly (theme presets, POS terminal location) go through the cached instance and invalidate themselves. - `updatePlatformSetting` does NOT. It writes inside `repos.tx(...)`, and transaction-scoped repos are built fresh from the tx client, so the cached wrapper never sees that write. Left alone, the main settings-editing path would have kept serving pre-write values for the full TTL. The platform router now invalidates explicitly for it — unconditionally, since this cache holds the whole table so any key makes the snapshot stale. - The TTL remains the backstop for OTHER instances, which get no invalidation signal. Any future write path that goes through a transaction needs the same explicit invalidation; noted in the decorator's docs. Verified: build 8/8 · @th/adapters 1,292 (incl. 8 new) · @th/trpc 627 · @th/core 7,907. `apps/api` has 2 failures in test/seed-places.test.ts that predate this change (its prisma stub lacks `room`, which `places.ts` began requiring inf42f16b4) — fixed separately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>Step 8 + placement persistence (docs/specs/2026-08-18). Clicking a day on an admin calendar now lands on quick-create with the date, time, venue, calendar and room already filled — and the created event KEEPS them: save-event-for-{human,org} accept optional calendarId/roomId, validated inside the write tx (cross-place calendar or room, a RETIRED room, or placement on a freeform event all reject with no mutation; a calendar without a room never guesses one). Verified against the dev DB: the row carries both columns, and a forged cross-place calendarId creates nothing. Month cells gain an always-rendered create affordance (not hover-only — hover doesn't exist on touch and month is the default view) that does not hijack the existing click-to-Day-view. The long-dead onSlotPress prop is finally wired for week/day. Prefill survives the moderator Ticketed vs Community dialog and is dropped if the venue is changed away from it. Embeds: a calendar-subject picker grouped by venue, and the widget's list view no longer renders blank for them (it was gated on "owner"; calendar subjects return "calendar"). Calendar subjects force link-out because assertEventAllowedForEmbed fails closed for them. Booking requests gain a message thread (attachments list-only — no upload mint yet). Also carries this session's earlier dashboard/calendar overflow fixes: the restored moderator dialog, the orders-table scroll, and two 320px wrap fixes. Hold layer untouched (deferred). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>Final vocabulary for the venue hierarchy: Place (the venue you own) → Space (the physical bookable thing conflicts resolve against) and Calendar (the named public programming stream). "Space" reads naturally for a stage, back bar or patio, and frees "Room" from competing with "Calendar" for the stream meaning. Pure rename, no behaviour change. The migration is ALTER ... RENAME throughout — table, columns, enum, indexes and constraints each renamed explicitly, since Postgres does not cascade a table rename to them. Nothing is dropped or recreated, so the seeded dev data survives; the migration also replays clean from scratch. Carries the user-facing surface with it: SPACE_NOT_FOUND and CANNOT_RETIRE_LAST_ACTIVE_SPACE with en/es copy (sala → espacio), the tRPC procedures, the ?spaceId= prefill parameter, and the help article (rooms-and-calendars → spaces-and-calendars) with its cross-links. Deliberately untouched: prose where "room" means a venue generally ("if you run a room"), layout words like headroom, fixture strings such as "The Velvet Room", the Space keyboard-key comments in packages/ui, and the dated spec records, which document what was specified at the time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>Pull request closed