Land main: settlement blocker sweep + venue hierarchy + seed-places fix #3
Loading…
Reference in a new issue
No description provided.
Delete branch "tmp-push-probe"
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?
Transport-only PR: direct pushes to refs/heads/main are being rejected with "reference already exists" (Forgejo branch-table desync). Fast-forward-only merge, no merge commit.
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>Pull request closed