Features
Canonical system design intent for the Metrognome codebase. Each feature is a folder containing the spec plus implementation plans, ralph prompts, research notes, and post-mortems. See ADR-016 for the source-of-truth chain (business > spec > code) and the spec/code coexistence rule. Spec/plan/prompt conventions: aaron-hogan/ralph.
When a spec disagrees with code, fix one of them — they never coexist contradicted. Authority chain: business > spec > code.
Layout per feature
features/<name>/
spec.md canonical design intent
plan.md implementation plan with **Status:** + checkboxes (ralph)
prompt.md ralph protocol for this divergence
progress.txt cross-iteration learnings (auto-grown)
research.md background, when needed
runbook.md ops procedure when applicable (e.g., burst-runbook for incidents)
retro.md post-mortem when applicable
deploy-checklist.md one-time deploy artifacts
Strict folder-always — every feature is a folder, even single-file ones.
Index
| Feature | Code | Purpose | Status |
|---|---|---|---|
| promo-credits-location-borne | apps/api/src/services/scheduling/ReservationCreationService.ts, apps/api/src/services/payment/ReserveService.ts |
Promo-origin credit redemptions pay locations $0; purchased keeps ~90c; reserve + audits purchased-only (ADR-026, reaffirmed + extended by ADR-028 after a same-day reversal cycle) | spec — impl PR #917 reopened |
| consolidate-lockout-side-effects | apps/api/src/services/payment/{FulfillmentService,StripeWebhookService,SubscriptionCreateHandler}.ts, apps/api/src/services/scheduling/ReservationSideEffectService.ts |
One idempotent finalizeLockout(reservationId) reconciler for all lockout signup side effects — per-effect preconditions + claim ledger, sweep cron repairs dropped work |
spec · plan |
| stripe-webhook-ingress | apps/api/src/app/api/stripe/webhooks/route.ts, apps/api/src/services/shared/WebhookEventService.ts |
Ingress/transport: signature verification, durable persistence, idempotency, claim/lease/retry, reconciliation | initial (done) · hardening (done) |
| refactor-scheduler | apps/web/src/components/organisms/scheduling/, apps/web/src/components/organisms/booking/, apps/web/src/hooks/scheduling/ |
cal.com-style slot picker + on-page /book surface replacing the accordion scheduler and booking modals |
spec · plan (in-progress) |
| posthog | apps/web/src/lib/tracking.ts, apps/api/src/lib/analytics/, apps/web/src/app/providers.tsx |
Replace Umami with PostHog Cloud — funnels, session replay, identification | in-progress |
| privacy-compliance | apps/web/src/app/(marketing)/privacy/page.tsx, apps/web/src/lib/tracking.ts, apps/api/src/lib/meta/, apps/web/src/app/layout.tsx |
US-only privacy floor: accurate policy, opt-out propagated to Pixel + CAPI + Google Ads, GPC honoring, DSAR intake | in-progress |
| bookkeeping | analytics/scripts/*bookkeeping*, apps/api/prisma/migrations/*bookkeeping* |
Per-location LLC bookkeeping with penny-perfect Stripe reconciliation. Multi-layer pipeline feeding a 4-tab Metabase dashboard (Revenue, Money Movements, Transactions, Receivables), used directly by staff and CB Solutions (main accounting firm, all locations). A location-scoped parity variant (dashboard 34, MG4/5/6/8/9) serves COR Accounting, the smaller firm on that subset. | shipped (dashboard.md) |
| service-period-recognition | apps/api/prisma/migrations/*bookkeeping* (planned), analytics/scripts/*bookkeeping* |
ADR-027 accrual lens for the bookkeeping pipeline: three dates per ledger row, deposits/credits as deferred revenue recognizing at redemption or 12mo/18mo expiry breakage, execution-dated settlements tab, no retroactive mutation. Recognition mapping awaits accountant sign-off. | spec · plan — ready to build; CB Solutions reviewing |
| payment-record-architecture | apps/api/src/services/**/*Payment*.ts |
ADR-008 unified Payment model. Eliminates Stripe metadata dependencies, creates records at checkout not in webhooks. | shipped (see deploy-checklist.md) |
| permission-redesign | apps/api/src/utils/auth/role-check.ts |
ADR-006 permission redesign — role checks replace the permission grammar | shipped (Mar 2026, #494/#497/#498) |
| token-exchange | apps/api/src/app/api/auth/token-exchange/ (planned) |
Service-principal on-behalf-of token exchange — a confidential service acts as a staff user via a short-lived opaque delegated token. Foundation for the staff MCP. | spec |
| sms | (not started) | SMS via Twilio for member notifications | research |
| account-dedup-merge | (not started) | Verified-phone person-key: dedup at signup (Twilio Verify OTP) + staff account-merge tool. Fixes Apple-relay duplicate accounts. | spec · research |
| stripe-data-pipeline | (foundational for bookkeeping) | ADR-007 Stripe data sync pipeline | planning |
| studio-reference | apps/web/src/app/(staff)/studios/ |
Staff studio reference page | planning |
| staff-ui-redesign | apps/web/src/app/(app)/staff/, apps/web/src/components/molecules/detail/, apps/web/src/components/organisms/{modals,sheets}/ |
Stripe-style read-only detail pages for the three highest-traffic staff entities (User, Resource, Reservation); edits via CrudModal, multi-pane/preview flows via TakeoverSheet; canonical Activity (audit-log) section, entity-bound row actions. Permanent coexistence with unmigrated pages. Engineering pattern in architecture/frontend/detail-pages.md. |
spec · design · plan — v1 shipped (#783/#788/#789); Resource + tour create flows outstanding |
| unified-cherry-city-lp | apps/web/src/app/(landing)/lp/cherry-city/, apps/web/src/lib/tracking.ts, apps/api/src/lib/meta/ |
Replace dual /cherry-city/{tour,hourly} LPs with a unified /lp/cherry-city; pain-led hero, monthly/hourly fork, new LandingFunnelChoice Pixel + CAPI event for unified Meta optimization |
spec · done |
| cherry-city-ppc-prospecting | (Meta Ads Manager — no code) | Phase 1 Meta paid prospecting campaign driving the unified LP — Traffic objective, LPV optimization, awareness + retargeting-pool building. Companion to unified-cherry-city-lp. |
spec |
| market-landing-pages | apps/web/src/app/(landing)/lp/[marketSlug]/, apps/web/src/components/organisms/marketing/, apps/api/prisma/schema.prisma (LocationGroup) |
Tour-driven /lp/[marketSlug] template (Salem first, Portland for free). Replaces /lp/cherry-city. Hero tour-form → direct TourSchedulerModal hand-off (no auth), locations-near-you (IP geo), 3-fork to /monthly /hourly /groups. |
spec · done |
| marketing-scorecard-attribution | analytics.* (view + ad-spend/leads sync), apps/api/src/services/scheduling/ReservationCreationService.ts, apps/api/src/services/payment/{FulfillmentService,PaymentIntentHandler}.ts |
Make the cold-start scorecard one queryable analytics object and complete channel attribution lead→tour→move-in. Ad-data ETL + scorecard view + coarse CM tour lead-source/auto-match (the lever) + move-in coverage audit. Builds on #727. |
spec |
| lead-market-attribution | apps/api/src/app/api/contact/route.ts, apps/api/src/services/contact/InquiryService.ts |
Resolve an inquiry's market to location_id from the already-collected city when it maps to exactly one active location (Salem and single-building metros), so front-of-funnel leads grid on the per-location scorecard. Page/explicit context only, no geo-IP. |
spec |
| align-waitlist-checkout | apps/web/src/app/waitlist/invite/[id]/, apps/web/src/features/checkout/, apps/api/src/app/api/checkout/waitlist-invitation/[id]/route.ts |
Route the no-auth waitlist deposit (/waitlist/invite/[id]) through the shared CheckoutExperience; auth-aware saved payment methods (member case) via server-minted customer session; retire the dead /checkout/waitlist authed flow. |
plan |
| ai-workspace | (Claude Cowork plugin library — separate mg-plugins repo, no code in this monorepo) |
Private library of Claude Cowork plugins shaped by the Metrognome org model — Platform services + Pillars + Universal layer. Plugins encode procedure (SOPs); Drive remains the human artifact surface. | spec |
| make-music-salem-comp | apps/api/src/services/scheduling/mms-comp.ts, apps/api/src/app/api/mms-comp/ |
Original MMS 2026 free rehearsal comp — hardcoded MMS_COMP.resourceIds allowlist + /api/mms-comp/* endpoint pair. One-off pattern; subsumed by comp-reservations primitive. Resources provisioned in prod via clone-from-source script. |
spec · shipped |
| comp-reservations | apps/api/src/app/api/comp/reservations/, apps/api/src/app/api/comp/resources/, apps/web/src/app/(landing)/lp/[marketSlug]/ (comp hero), /lp/mms2026 (embedded modal) |
Offer-row-driven free-booking primitive. POST /api/comp/reservations + GET /api/comp/resources. Active offers: MMS_SALEM_2026 (Free Practice Week) + CHERRY_CITY_FIRST_PRACTICE (first 4 hours free, Salem). |
spec |
| free-first-practice | apps/web/src/components/molecules/navigation/PromoButton.tsx, apps/api/src/app/api/promo/eligibility/[offerId]/, apps/api/src/app/api/offers/dashboard/, apps/web/src/app/(landing)/lp/[marketSlug]/ |
Replace the 50-free-credits trial promo with the CHERRY_CITY_FIRST_PRACTICE comp offer on all promo surfaces (header button, dashboard callout, /promo/free-credits 301), pointing at /lp/salem/free-first-practice. Market-LP hero integration shelved pending scheduler refactor. MG10 only. |
spec |
| guest-phone-verification | apps/api/src/services/verification/PhoneVerificationService.ts, apps/api/src/app/api/phone-verification/, apps/web/src/components/molecules/booking/CompDetailsForm.tsx |
Prove an unauthenticated visitor controls a phone number (Twilio Verify OTP, transaction-scoped proof — not identity, see ADR-024) for requiresVerifiedPhone offers; drives the cross-identity PromoRedemption.phone redemption cap and post-possession account-exists routing to phone-OTP sign-in. First consumer: comp bookings. |
spec · plan — shipped, gate live (#882, #883; CHERRY_CITY_FIRST_PRACTICE) |
| refactor-mms-pages | apps/web/src/app/(landing)/lp/mms2026/, apps/api/src/app/api/leads/green-room-registration/, apps/api/src/app/api/leads/giveaway-entry/, apps/api/src/templates/emails/GreenRoomConfirmationEmail.tsx |
MMS 2026 Artist HQ — LP restructured around Green Room + FPW pillars, Green Room registration endpoint + immediate email, Vedder giveaway kiosk endpoint (R-02 gated), 10 MMS source codes in ConversionAttribution. |
spec · kickoff |
| staff-mcp | apps/staff-mcp/ (planned) |
Staff-ops MCP server — thin proxy over @mg/api; LLM-driven member operations on behalf of the signed-in staffer via token-exchange OBO. FastMCP-TS, homelab Docker + Cloudflare tunnel. | spec |
| reservation-health | apps/api/src/services/health/ (planned), apps/api/src/app/api/cron/reservation-health-*/ (planned) |
Continuous reservation-invariant engine — data complete, sub linked + healthy, billing/transfers correct, payments linked. Consolidates ~15 one-off audit scripts (reservation-validate-lockout is the prototype) into one engine run nightly across the fleet, alerting on violations. |
spec · plan |
| api-include-parent-mgids-in-response | apps/api/src/app/api/reservations/, apps/api/src/app/api/payments/, apps/api/src/services/health/ |
API convention: any response exposing a flat entity's mgId (reservation, payment, etc.) also exposes its parent chain's mgIds, so consumers can render a locatable ref like MG11-SMO33 RSV592. Display layer joins; data layer supplies the parts. |
spec · plan |
| staff-invitations | apps/web/src/components/organisms/{InvitationsTable.tsx,sheets/CreateLockoutSheet.tsx,sheets/CreateWaitlistSheet.tsx}, apps/api/src/app/api/checkout/session/ |
Unified staff invitations surface (lockout + waitlist over CheckoutInvitation) + the daily-use create flows as TakeoverSheet sheets; honest pending-payment status; waitlist data hygiene. Companion arc to staff-ui-redesign. |
plan — Phases 1–5 shipped (#807/#808/#814); Resource + tour create outstanding |
| waitlist-status-model | apps/api/prisma/schema.prisma (WaitlistStatus), apps/web/src/app/(app)/staff/waitlist/ |
Replace conflated waitlist PENDING with honest INVITED/ACTIVE statuses + single-table staff UI; PROCESSING payment status. Builds on staff-invitations. |
plan — shipped (#810/#811/#814) |
| reservation-invited-status | apps/api/prisma/schema.prisma (ReservationStatus), apps/web/src/app/(app)/staff/reservations/ |
Reservations gain INVITED — a lockout invitation creates a real reservation that holds the studio and expires in 7 days (staff notified). Reservation analogue of the waitlist status model. |
plan — Phases 1–3 shipped (#815, expiry notice #834); backlog open |
| sent-email-log | apps/api/src/services/email/EmailService.ts, apps/api/src/app/api/emails/, apps/web/src/app/(app)/staff/ (detail-page email tables) |
EmailService.dispatch logs every sent/failed email; read-only staff email log surfaced on detail pages (GET /api/emails). Shipped as a staff-ui-redesign adjunct. |
spec — v1 shipped (#789/#798); webhooks/retention (phase 4) deferred |
| account-activity-timeline | (planned) apps/api/src/app/api/users/[id]/activity/ |
Unify the User detail page's Payments + Transactions tables into one server-paginated "Account activity" feed. Carried-forward deferral from the staff-ui-redesign User page. | spec — deferred / not built |
| public-lockout-checkout | apps/web/src/app/checkout/lockout-invitation/[id]/ |
Public no-login lockout checkout (on-demand sub) + routing-by-account-state (stub → public page, set-up user → authed /checkout/reservation). |
spec — shipped (#822) |
| unified-checkout | apps/web/src/features/checkout/ |
ADR-020 unified auth-optional checkout surface — one CheckoutExperience for authed and public flows. |
plan — shipped (#829) |