RevParPro: Internal Tool to SaaS

Generated 2026-08-18 by Woz. Sources: 12-agent read of revparpro origin/main and the AceHQ vault, live production queries (PostgREST, 2026-08-18), the CoStar STR census you sent Marriott (Feb 2026), and Ace's decisions of 2026-08-18. Every number carries its source inline.

The short answer. RPP is a deep operating system for one company: every one of the 17 active people sits in a single tenant called default, and "my portfolio" is a flag on THM's own 15 hotels (RPP Authorization Architecture.md, 2026-07-23 production verification; profiles all tenant_id='default' per the 2026-07-16 audit). What already works for any Texas hotel is the market layer: 27,686 hotel records, statewide room-tax receipts, the census, and compset discovery (hotels row count, PostgREST 2026-08-18). To sell it, four layers have to exist that do not exist today, and they should be built in this order: (1) a mailer control plane, (2) a customer registry and admin desk, (3) plans and billing, (4) real tenancy. The first three can ship without giving any outsider a login, which is exactly what your July decisions asked for (lightweight report surface, no /app login for trials, card-less trial, email first then trial). Tenancy comes when the first management company converts.

1. Where RPP stands as a SaaS

LayerExists todayGapOrder
Mailer control plane
blocker
The v06 letter (HTML/PDF sample for Best Western Plus Austin Central); teaser_tokens and teaser_leads with 6 dev rows; a token minting script; the /t/:code teaser page (artifacts/direct-mail; migrations 252/253/270; teaser_tokens query 2026-08-18) No record anywhere of a piece being sent; the printed QR points at revparpro.com/?mail= which nothing handles; every version was hand-built with hand-queried numbers; no address roster, no suppression list, no campaign or wave entity, no response report (direct-mail reader; grep of src/landing/workers for 'mail='; live curl of revparpro.com/?mail=) 1
Customer registry
blocker
Nothing. Prospects, Sales Pipeline and Frontline Leads are THM's own hotel-sales tools (companies buying room nights), not RPP customers (migration 073; ProspectPipeline.tsx; living doc 2026-07-29) No table says who a customer is, which hotels they claimed, what plan, what status, or which letter they came from. "Start my free trial" only flips wants_trial; nothing consumes it (Teaser.tsx 1010-1022; teaser-page/index.ts 379-388) 2
Plans and billing
blocker
A dormant subscriptions table from migration 001 with 0 rows and Stripe columns nobody reads (001_initial_schema.sql 1288-1300; repo-wide grep for stripe) No processor, no plan catalog, no entitlement check anywhere in RLS, edge functions or nav. Two entry prices were set the same day: $9/month (Instagram note) and "Plans from $12.50/month" (all direct-mail artifacts) (RPP Instagram Soft Launch.md:30; rpp-june-snapshot-decision-packet.md:20) 3
Tenancy and isolation
major, gated
The primitives: tenant_id on about 322 tables, about 207 tenant-keyed RLS policies, a fail-closed tenant_id() helper, the authz capability schema, and a synthetic second tenant for the RLS harness (tenancy reader parse of origin/main migrations 2026-08-18; migrations 002/007/342/005) No tenants/organizations table; roughly 30 finance and deal tables are role-only and about 28 operating tables are open to any authenticated user, so an outside login today would read THM P&L and night audits; invite-user hardcodes default; 34 edge-function files and 63 scripts write to default (migrations 259, 258, 320, 308, 306; invite-user/index.ts:95; grep counts on origin/main) 4 (after first company converts)
Public funnel
major
Marketing site, /t/:code teaser, star-intake worker, Resend one-pager email (landing/index.html; workers/star-intake) Site has no pricing, no signup, no /pricing, footer /terms and /privacy resolve to the homepage; star@revparpro.com has no Cloudflare routing rule so forwarded STAR files go nowhere (live curl 2026-08-18; Cloudflare API email routing rules 2026-08-18) with 1 and 3
Data feed ownership
major
SIFT sync, month-close eval, discovery precompute, all working (scripts/sift-sync.mjs; scripts/month-close-eval.mjs) The Comptroller feed runs under a THM entity login with the password committed in the script, on the Mac Mini crontab; the census is a one-time Feb 2026 extract with no refresh path (sift-sync.mjs:74-78; pipelines reader) program dependency

2. Gap 1: mailer issuance and tracking

The mailer needs a ledger and a loop, not a screen first. Every printed number gets frozen at render time so the letter and the QR page are the same bytes, and every step writes one event so "campaign to paid" is one query.

campaigntarget (hotel or company)recipientpiece + tokenrenderedproof approvedprintedmailedscanned (revparpro.com/r/<code>)email capturedtrialpaidmonthly brief
EntityPurposeAttaches to what exists
mail_campaignsOne row per wave: report period, market, format, offer copy as printed, fulfillment path, approval, status (draft, proofing, approved, printing, mailed, closed)parent that teaser_tokens.campaign never had; issuance refuses to mint while the plan price is null
mail_targetsWho the campaign is aimed at: a hotel (str_number) or a management company (company_id), with tier and scorestr_hotel_summary, new management_companies
mail_recipientsPerson and address block per target: "General Manager" at the property address (locked), or a named executive at company HQ; source and verified flaghotels.address, Comptroller location address; company HQ later via Apollo
mail_piecesThe issuance ledger: one row per envelope, its unique token, frozen report payload and set snapshot, render hash, printed_at, mailed_at, returned1:1 with a teaser_tokens row (new tier mail, plus campaign_id, expires_at)
funnel_events (append-only)printed, mailed, returned, scanned, page_view, email_captured, star_uploaded, trial_started, brief_sent, checkout_started, subscription_activated, canceledwritten by the /r redirect, teaser-page GET/POST, star-intake worker, provisioner, webhook, brief job
mail_suppressionsDo-not-mail and dedupe: THM properties, existing customers, returned addresses, dual-brand siblings sharing a taxpayer numberseeded from hotels.is_thm_property, taxpayer_chain, active claims
vw_campaign_funnelThe report: pieces, scanned, scan rate, leads, trials, paid, cost per lead per campaign, plus company-level rollupsthe four tables above

Issuance is a script with a proof gate

  • mail-batch select: applies the frozen target rule (metro, complete filings for the period and prior year, suggested-set coverage, one piece per taxpayer, not suppressed), inserts pieces and mints tokens.
  • mail-batch render: suggested set from the str_number-keyed discovery path (deal-compset), monthly named receipts from compset-engine.ts (allowlisted, so the "never read raw receipts" CI gate holds), then the v06 template with QR = revparpro.com/r/<code>. Proof index goes to Drive.
  • Ace clicks Approve on the Mailers screen; only then can pieces be marked printed and mailed. In-house or vendor, the mailed event is entered the same day.
  • Cadence gate: a period is "closed for mail" when month_close_evals passes and the row count is stable against the same month last year, not a calendar day (scripts/month-close-eval.mjs; GTM proposal, verified by the founder-lens judge).

What the scan lands on

  • revparpro.com/r/<code> records the scan and 302s to app.revparpro.com/t/<code>. Today the printed URL lands on the generic homepage and the token is discarded (funnel reader; live curl 2026-08-18).
  • Mailed tokens render the same named list the letter printed (nothing new leaks) with the full period report behind the existing email gate, then the card-less trial. This is a rule change from the teaser page's "no names pre-gate" and needs your yes (decision 6).
  • Trial provisioning ships before the first envelope is mailed. Today "Start my free trial" is a dead end.

3. Gap 2: managing the client base

Customers are accounts, not logins, for the first waves. A single-hotel buyer's account is their email plus a full-tier report link and a monthly brief. A management company is the same account kind with many claimed hotels. Nobody gets an /app login until isolation is proven, which keeps every tenancy leak latent.

PieceWhat it doesNotes
rpp_accountskind (single_hotel, management_company), display name, primary email, plan, status (trial, active, past_due, canceled, comped), source campaign and piece, trial datesManual create exists so the first five concierge-onboarded companies (locked, Instagram note) are registered the same way as self-serve buyers
rpp_account_hotelsThe claim model: which hotels this account owns access to, claim status (self-declared, verified, rejected), chosen set, report tokenReplaces is_thm_property as "my hotels" for outsiders; a STAR upload whose auto-detect matches the str_number auto-verifies the claim
plan_products, rpp_subscriptionsThe catalog (key, price, interval, Stripe price id, entitlements JSON) and the commercial state per accountCopies the shipped email_products / email_policies pattern from migration 364; the letter, the /t page and the trial email all read one price row
platform_admins + is_platform_admin()Who may see and run customers and mailersDeliberately separate from THM company access administrators (Ace, Rahil, Zabir, Alyssa) and from the 8 owner-role profiles that isAdmin lets in today (nav-config.ts; RPP Authorization Architecture.md 2026-07-23)
Screens/app/platform/customers (list, filters, MRR, next brief), /app/platform/customers/:id (claimed hotels, subscription, funnel timeline, actions: provision trial, extend, resend link, record payment, comp, cancel), /app/platform/mailers and /:id (campaign metrics, piece table, Approve, Mark printed, Mark mailed, Mark returned, Suppress)Existing PageShell, tokens, UserTable and dialog primitives; numbers and labels only, explanations behind hover icons (2026-08-17 rule)
Money, first versionA hosted Stripe Payment Link (or invoice) emailed at trial end, payment recorded by hand on the customer screen with paid_through; the webhook backfills laterEnough for wave-1 volume; any live billing write or webhook go-live needs your explicit authorization per the finance-writes rule

4. Management companies: the second target, and the natural customer

You are right that the letter is GM-shaped. The census in RPP already names the operator, and the decision packet already says the ultimate product is for management companies with GM outreach as the exposure channel (artifacts/direct-mail/rpp-june-snapshot-decision-packet.md:17; RPP Instagram Soft Launch.md:26-27). Two facts change the plan: the management-company names live in str_census, not hotels (the hotels.management_company column from migration 025 does not exist in prod, error 42703 on 2026-08-18), and RPP's census is Texas only, so Texas-share needed the national file. That file is the "Participating Hotels" sheet inside the CoStar Comp Set Check Tool you emailed star.program@marriott.com on 2026-03-23; it is now extracted at ~/agents-runtime/data/str-census-global-feb2026/.

96,413
hotels in the global STR census file; 39,150 United States; 4,225 Texas (participating_hotels.csv)
754
distinct management companies on Texas hotels; 2,478 TX hotels carry one, 1,756 carry none (str_census, PostgREST 2026-08-18)
127
companies manage 5+ Texas hotels (1,563 hotels); 59 manage 10+ (1,119); 19 manage 20+ (598) (str_census 2026-08-18)
70
of those 127 are Texas-centric (50%+ of their US portfolio in TX), 709 TX hotels between them (mgmt_company_tx_scores.csv)
Tier (companies with 5+ TX hotels)CompaniesTX hotelsExamples (TX / US total)Treatment
Tier 1: 50%+ of US portfolio in Texas70709Ocean Gate 25/25, BH Hospitality 25/25, Texas Western 25/30, Magnolia 24/30, Whitestone 20/20, RGB 20/20, Palette 20/31, Patel Capital 18/22, Insignia 18/25, American Liberty 16/18Company letter to the Texas ops executive plus GM letters to their hotels. Wave 1 subset with 2+ hotels in Austin or San Antonio: 21 companies, 89 hotels
Tier 2: 25 to 50% Texas14193MCR 41/136, J&P Asset Management 41/146, K&K 19/41, White Lodging 15/59 (all 15 in Austin/SA), Avion 13/38Second wave after tier 1 reads
10 to 25% Texas25Aimbridge 116/634, InTown Suites 42/195, Hotel Equities 26/161, HHM 26/211, Remington 22/101, Baywood 21/131, GF Hotels 20/125Later, or a different pitch; a Texas-heavy product is a small slice of their book
Under 10% Texas18Marriott International 39/480, Hyatt 25/178, Extended Stay America 20/533, Highgate 18/301, Drury 16/128Deprioritized; mostly brand-managed

TX counts from prod str_census (2026-08-18); US totals and shares from participating_hotels.csv (Feb 2026). Tier hotel counts for the two middle rows were not summed. Brand-managed proxy (management company equals parent company) covers 199 TX rows: InTown 42, Marriott 39, Hyatt 25, ESA 20, Drury 15, Hilton 10, Omni 10, Kasa 7. Owner-operator proxy (management company equals owner company) covers 897 TX rows; 420 of tier 1's 709 hotels are owner-operated. 166 TX census rows carry a hotel name in the management-company field and need an alias table.

What gets built

  • management_companies (canonical name, kind: brand_manager, third_party, owner_operator; HQ address, domain, Apollo org id, TX and US counts, TX share, THM relationship flag) seeded from the national file, plus management_company_aliases for the variants, plus a national census reference table (str_census_national) so the live TX table stays lean.
  • management_company_portfolio rollup: TX hotels, Austin/SA count, TTM receipts as a ratio of sums (the stored per-hotel YoY percent has absurd outliers, one RGB hotel at 550,944,373.9), hotels trailing their suggested set.
  • Campaign targets keyed by company as well as by hotel; a GM-letter scan at an Aimbridge hotel also credits the Aimbridge company account.
  • Company letter variant: one page to the regional or portfolio executive. Hero is their Texas portfolio (N hotels, June filed, YoY, how many trailed their set), one row per hotel, worst gap first; the named eight-hotel list stays a per-hotel artifact so the company letter never reads as an audit of their GMs (your "arm the GM, never audit them" rule). QR to a company token that opens a portfolio page. Ask: one account for the portfolio, a seat per GM, per-hotel pricing.
  • Executive names come from Apollo once a domain exists (the existing apollo-enrich function already resolves an org by domain and searches people by title); the domain list for the top 60 is a curation task.

What this changes in the customer model

  • Tenant = management company is already how the schema thinks: management_company is an authz scope kind (migration 342) and email_tenant_settings.management_company_name exists. When tenancy arrives, one org per company, hotels claimed underneath, GMs as property-scoped seats, one subscription with quantity = properties (per-property base plus premium tier, locked).
  • THM itself is a row in the file: Texas Hotel Management 13/13, 100% Texas. Two THM hotels (DALDL, HOUUS) carry a null manager and THM as owner, and two sold hotels (HOUZK, HOUZV) still say THM, so the census lags operator changes and needs a human pass on the top 60.

5. Sequenced roadmap

Estimates assume Woz plus agents while THM operations continue; each phase is cut so it can pause without leaving THM half-migrated. Two of the three roadmap designs were rated top by one judge each; this merges them and takes the architecture judge's grafts (platform admins from day one, no re-keying of tenant_id, price seeded before any print, trial provisioning before the first envelope).

PhaseWhenDeliverablesExitNeeds from Ace
0. Lock inputsDays 1-2Decisions 1 to 8 below recorded in the vault; one plan row seeded from the price; rebase the working branch onto origin/main before any migration (working tree holds 335 migration files, main holds 581); star@ routing rule created; real return addressWritten price, plan, admin set, landing rule, set algorithm, fulfillment path; star@ round-trip verifiedAll of section 6
1. Ledger, redirect, trial provisioningWeeks 1-2Migrations: mail_campaigns, mail_targets, mail_recipients, mail_pieces, funnel_events, mail_suppressions, plan_products, rpp_accounts, rpp_account_hotels, platform_admins; teaser_tokens tier mail + expiry; revparpro.com/r/* redirect writing the scan; trial-provision edge function that mints the full-tier link and sends the report email; landing site moved into CI so a redeploy cannot be skippedA test /r/<code> scanned from a phone writes scan and page_view; a lead clicking Start my free trial gets a working link within minutes; RLS harness passesNone
2. Render pipeline, Mailers desk, wave 1Weeks 2-4v06 moved out of artifacts into a template on its embedded JSON contract; mail-batch select/render/proof; Mailers screens with Approve, printed, mailed, returned; Austin/SA GM wave under the frozen rule (metro, complete filings both years, one piece per taxpayer, suggested-set coverage, no bad-month hunting)Ace opens the proof index and the numbers match /t/<code> byte for byte; wave 1 in status mailed with a piece-level ledger; metrics view liveProof approval; the physical mailing
3. Customers desk, first dollarWeeks 4-6Customers screens; monthly brief job (compose, preview index, explicit send, analyst-letter voice); trial-ending email with the Payment Link; Record payment; real /terms and /privacy; runbook for run a wave, provision a trial, record a paymentEnd to end on a test hotel: scan, gate, trial, link, expiry, trial-ending email; first payment recorded and attributed to a pieceTrial email copy; terms text; per-action billing authorization
4. Management-company trackWeeks 5-8Company tables and national census load; portfolio rollup; alias pass on the top 60; company letter template and company token page; Apollo executive lookup for tier 1; tier-1 Austin/SA company wave (21 companies) alongside GM wave 2Company letters mailed with company-level attribution visible per campaign; company accounts registered manually for concierge onboardingDecision 7; Apollo credit approval
5. Money automation and cadenceWeeks 6-9Stripe Checkout, webhook with idempotency table, billing portal; entitlement read in the report page and brief; /pricing on the landing site; cadence check tied to filing close; weekly loop report (pieces, scan rate, email rate, trials, paid, cost per lead)A test card converts a trial to active through the webhook; two consecutive monthly issuances run from the close trigger with Ace approving only the proofStripe as processor; card vs ACH split; go or no-go after touch 2
6. Real tenancyGated: first company convertsorganizations whose slug equals the existing tenant_id text (THM stays default, nothing re-keyed); per-tenant authz template seeding; invite-user takes tenant from the caller; tenant term added to the ~30 role-only and ~28 open tables; hardcoded default writes fixed; check-tenant-policies CI gate; harness proves a second tenant sees zero THM rows; then a customer login surface and MFA policyOne management company operates inside its own tenant with its own hotels and team; THM users see the identical portfolio they see todayDecision 4; feature slice for outsiders; MFA for customers
Program dependencies, not phases: move the Comptroller (SIFT) feed to a RevParPro-owned account with the secret in Vaultwarden instead of the script; a census refresh path (STAR-upload self-heal plus tax-only promotion is the only one today); a hosted runner for market-wide jobs instead of the Mac Mini crontab; per-customer DFS metering before any rate-scan feature is sold (one shared $150/month cap today, $49.14 spent in the last 31 days) (migration 010; dfs_spend_log 2026-08-18).

6. Decisions only you can make

  1. Price on the letter, and the plan behind it. $9/month (your Instagram-session call, 2026-08-18) or "Plans from $12.50/month" billed annually with $15 month to month (every direct-mail artifact the same day). Nothing prints and no plan row is seeded until one number wins; Warren owns the ten-minute economics check. Recommendation: pick one today; whichever it is, it becomes the single price row that the letter, the page and the email all read.
  2. Who runs customers and mailers. Ace only, Ace plus Alyssa (the 2026-07-19 global admins), or the four THM company access administrators (adds Rahil and Zabir). This decides whether THM partners see RPP customer lists and revenue. Recommendation: platform admins = Ace plus Alyssa, kept separate from THM company access administration.
  3. Legal footing before wave 1 prints. (a) Which entity sells RevParPro and holds Stripe, Resend, Cloudflare and Supabase: the existing terms and privacy pages on origin/main name Management HD LLC and describe RPP as an internal system (commit ed0abf8c, 2026-07-23). (b) Redistributing named per-hotel room-tax receipts: SB 1086 (85th Legislature) stops state agencies from posting individual hotels' taxable receipts on public websites and keeps the data public information via open records and the SIFT login; it does not itself bar a private party, but the SIFT account terms and the bill's stated purpose (cutting unsolicited vendor mail to hotels) make this a counsel question, and the vault's own SIFT spec says do not expose raw receipts publicly (sift-operations-spec.md "SB 1086 Compliance"; capitol.texas.gov SB01086I analysis; comptroller.texas.gov hotel-receipts page). (c) Texas sales tax on a SaaS subscription (CPA Niraj; a printed price is either tax-inclusive or tax is added at checkout). (d) Whether the CoStar census extract obtained through Marriott's tool may underpin a product sold to outsiders. Recommendation: engage counsel and Niraj this week; phases 1 and 2 build regardless; the print run waits on (b).
  4. Formally reopen the 2026-07-23 choice "THM plus outside owners, not multi-management-company SaaS yet" (RPP Authorization Migration Contract.md). Selling to outside GMs and management companies is that SaaS. Recommendation: reopen it in writing so phase 6 has a mandate; nothing before phase 6 needs it.
  5. Fulfillment, return address, recipient rule, wave size. In-house copy, fold, stamp (August packet) or PostGrid at $0.902 per piece on the 500 per month starter tier or Lob at $0.48 plus $550 per month (July research, verified 2026-07-15) (str-upload-public-tool-design.md section 7); the real street address to replace "123 Sample Street"; "General Manager" at the property address with no name (current samples) or sourced GM names; first wave 10 percent test or the full 500 to 1,000. Recommendation: in-house for wave 1 with the mailed event entered the same day, unnamed GM at the property address, 10 percent test then full.
  6. Mailed-token landing rule. May the page a mailed QR opens show the same named rows the letter printed, with the full report behind the email gate? Today's teaser rule keeps names behind the trial. Recommendation: yes; the letter already printed them.
  7. Management-company track. Do we mail operators THM competes with or displaced (Hotel Equities ran HOURP before THM; Aimbridge is the largest Texas operator)? Which executive gets the company letter (President or COO vs regional VP of operations)? May the company letter reference their Houston and DFW hotels, given the GM wave is Austin/SA only? Are brand-managed portfolios (Marriott, Hyatt, Hilton, ESA) off the list entirely? Recommendation: neutral software, everyone in tier 1 is a prospect except Hotel Equities held for your call; letter to the highest-ranking Texas operations executive Apollo can find; company letters may show the whole Texas portfolio; brand-managed off the list.
  8. Two infrastructure yeses. Create the star@revparpro.com Cloudflare routing rule now via the API (the token is in Vaultwarden), and open a RevParPro-owned Comptroller SIFT account so the data feed stops running under ace@howdyhotels.com. Recommendation: yes to both; the second needs you only for the account signup.

7. Verified vs assumed

Verified

  • All tenancy, funnel, billing, mailer and pipeline facts above were read from origin/main or the vault by six read-only agents and spot-checked by two judges; file and line references are inline. The working branch is 246 migration files behind main (335 vs 581, git ls-tree origin/main), which is why the roadmap starts with a rebase.
  • Census counts: prod str_census queried by PostgREST on 2026-08-18; national figures from participating_hotels.csv extracted from Spark attachment 21912 (message 58190, thread "HOURP STR ID 9971 Compset Change and OTR", 2026-03-23).
  • Locked decisions honored: value ladder email then trial; card-less trial; GM at the property address; Austin/SA first; trial surface is not the /app cockpit; core customer is a management company; per-property base plus premium tier; no explanatory prose on data screens; light mode only.

Assumed, not checked

  • Time estimates cite no velocity data; the critic flagged this on all three designs.
  • Whether the str_number-keyed deal-compset path reproduces the hand-assembled v06 suggested set exactly; parity must be proven on the sample hotel before printing.
  • Stripe as processor (Warren assumed it; nothing committed).
  • Census company names are STR's labels as of Feb 2026 and lag operator changes; the top 60 need a human pass.
  • Whether the "revpar-pro" Pages project supports Functions or _redirects for /r/*; if not, a Worker route.