precomputed_venue_pools) that the curation pipeline reads before doing anything else. A pool hit returns pre-ranked, pre-classified venues in under 100ms with no AI inference. This is the primary delivery path for any established market — and it gets richer with every weekly pipeline run.market_venue_cache for a previously generated full curation result for this exact market + intent + environment combination. On a hit: the saved result streams instantly. Lifetime: 7 days. This layer catches cold-path results on repeat lookups before the pool is built for a new market.market_venue_cache at the end of Step 7 and immediately available as a Layer 1 hit for the next request.blocked: true in seed.js. Permanent exclusions — chains, off-standard hotels, venues that have declined in quality — never make it through regardless of what Claude picks.environment_blocked in seed.js.book.curatevip.ai) — a dark, gold, CurateVIP-skinned booking experience where Nuitee is merchant of record. The other 10 venues are not in LiteAPI and fall back to an Expedia affiliate deep link. Both earn commission.book.curatevip.ai/hotels/{liteApiId}?checkin=…&checkout=…&occupancies=… — the member lands on the exact hotel, dates pre-filled, never touching a search box."Return the best 3 matching venues for this speed tier in this market. Never hold back a great match because it's well-known."
CURATE's job is to return the best 3 matches for the member's speed tier. Full stop. For Vanguard members, the Four Seasons and Ritz-Carlton ARE the answer. For Sanctuary, Atelier, and Estate members, a lesser-known discovery is usually the better match — but never exclude a great match simply because it's well-known.
"1 exceptional venue + honest contextMessage is always better than 3 mediocre venues with no message."
The platform's entire value is curation credibility. A member who sees a mediocre or questionable venue loses trust in every other recommendation. The AI is explicitly instructed: never stretch a venue's fit to clear a count threshold. If in doubt, the answer is no.
NEVER: stretch a venue's fit to clear a count threshold. If you are uncertain whether a venue belongs, the answer is no. Return 1 or 2 venues with a contextMessage rather than 3 venues where one is a compromise.
"You've spent hours on this before. Scrolling photos, reading reviews, trying to feel whether a room matches your mood. We built CurateVIP so you never have to do that again."
CurateVIP was built around a specific, real frustration. When a high-net-worth traveler arrives in an unfamiliar city and wants to find the right restaurant or hotel — not just any good one, but the one that matches how they experience spaces — they face a research process that is broken in several distinct ways. The time cost is real, the error rate is high, and no existing product solves it.
Conservative total: 2.5–3 hours per destination. For a traveler visiting a new city on a 3-day trip, researching where to eat and stay properly would consume most of a day. Most people don't do it properly — they default to what they've heard of, or ask a concierge, or rely on a recommendation from someone whose taste they only partially share. The research problem is real. The cost is high. And until CurateVIP, no product solved it.
~360× faster than a thorough manual research session. And the manual version still doesn't match the member's taste profile, verify credential attribution, apply chain disqualifiers, or classify venues by primary environment.
After Claude returns its venue list, four sequential server-side filters run in order before anything is sent to the client. The Signal Guard runs first — it is the only filter that requires a live Haiku call. The remaining three are mechanical checks against seed.js flags.
confirmed: true bypass this guard entirely. Runs only on cache misses (when fresh Brave Search data is in memory). On cache hit: skipped — the curation result was already validated on first run. Logged as [signal-guard] in Railway. Cost: ~$0.001–0.002 per run. Added June 30, 2026.blocked: true in seed.js. These are venues confirmed too casual, wrong tier, or otherwise not appropriate for the platform regardless of environment. Logged as [seed-block] in Railway.environment_blocked: ['Verandah'] on a seed entry. Venue stays available for other environments; only stripped when the requested environment matches. Logged as [env-block].includeSurrounding is false (default), removes all venues marked surrounding: true in seed.js. These are venues in the surrounding metro area that should only appear when the member explicitly checks the toggle. Logged as [surrounding-block].Signal Guard bypass: Set confirmed: true on a seed entry to exempt it from Signal Guard validation. Use for venues with thin web presence that you have manually verified.
Adding a blocked venue: Set blocked: true on the seed entry. The venue is excluded from Claude's calibration prompt AND stripped from final results.
Adding an environment block: Set environment_blocked: ['Verandah'] on the seed entry. The venue remains available in all other environments.
Named examples: Canlis and Goldfinch Tavern both have environment_blocked: ['Verandah'] — they're valid indoor Seattle dining venues but should never appear when a member requests Verandah (physically-outdoors) results.
Live beta uses a pared-down environment set and market list. All hidden items are commented out in code — not deleted — and can be re-enabled with a single line change.
DashboardPage.jsx — MARKETS array, lines 56–67. Re-enable by uncommenting the relevant object.DashboardPage.jsx — EXPLORE_SPEEDS object. Move environment string from comment back into the array to re-enable.While the backend is fetching live credential signals and running Claude, the member sees a sequential narration of every source being checked — not a spinning wheel. This communicates that real work is happening on their behalf and builds trust that results are genuinely curated, not cached or random.
Each source message is displayed for Math.floor(25000 / msgCount) milliseconds, spreading all sources evenly across ~25 seconds regardless of how many sources the category has. The full curation averages ~36 seconds — this ensures the member sees meaningful activity for the majority of that wait, not a 7-second flash followed by 29 seconds of a static spinner.
TABLE: 8 × 3,125ms = 25s | STAY: 9 × 2,778ms = 25s | SOCIAL: 5 × 5,000ms = 25s
event: status SSE payload with credentialedVenueCount. If the count is 0, shows "Curating just for you…" with no count.event: venues). Immediately followed by each venue name with a ✓ checkmark, one per 650ms.Decision (June 30, 2026): The SOCIAL tab is disabled for Phase 1 beta. The button remains visible in the UI at 40% opacity with a "COMING SOON" badge — it is not removed from the interface. This approach signals the product roadmap to members and investors without implying the feature doesn't exist. Re-enable by removing the comingSoon flag from the CATEGORIES map in DashboardPage.jsx.
const comingSoon = key === 'social' — sets opacity: 0.4, cursor: default, disabled={true}, and renders a "COMING SOON" sub-label. onClick is undefined (not a no-op) so click does nothing. Single-line change to re-enable.The full quality floor — qualifying credentials, named disqualifiers, and the "floor without credentials" rule — is documented in the IP Methodology tab, Section D. That is the authoritative source. The short operational summary: ultra-luxury caliber only. Forbes Five Star, Michelin Key, AAA Five Diamond, Relais & Châteaux, and flagship brands (Aman, Four Seasons, Ritz-Carlton, Rosewood) all qualify. TripAdvisor, Five Star Alliance, and AAA Four Diamond do not. Formal credentials are reference points — not a mandatory gate — but the spirit of the floor must be met.
One-line operational summaries for daily use. Full canonical definitions, primary tests, and disqualifiers are in IP Methodology tab, Section C — that is the authoritative source. Never update the AI prompt or seed classifications without checking Section C first.
environment_blocked field: A venue valid on the platform for some environments can be blocked from others. Set environment_blocked: ['Verandah'] to strip that venue when the requested environment matches — without removing it from other environments.
Current blocks: Canlis — environment_blocked: ['Verandah'] (Provision/Couture, never Verandah). Goldfinch Tavern — environment_blocked: ['Verandah'] (indoor glass-walled room at Four Seasons). See IP Methodology §F (Canlis worked example) for the full classification rationale.
A checkbox beneath the location dropdown expands the venue pool beyond the core market. The label is market-specific (not a generic "surrounding areas"). Only appears for markets that have surrounding areas defined.
includeSurrounding state change triggers a useEffect that calls handleCurate() if hasCurated is true. Unchecking does the same.hasSurrounding and surroundingLabel per market. Checkbox only renders when hasSurrounding: true.(includeSurrounding || !v.surrounding) — surrounding venues are invisible to Claude's calibration prompt when toggle is off. They appear in the seed reference block when toggle is on.!includeSurrounding. This is the safeguard that the seed filter alone doesn't cover.surrounding: true on any seed entry marks it as a surrounding venue. No other changes needed.DELETE FROM market_venue_cache WHERE market = 'seattle';"The Speed Engine runs while members sleep. It finds, evaluates, and pre-loads every qualifying venue into precomputed pools — so when a member taps CURATE, the answer is already there."
The Speed Engine is CurateVIP’s autonomous discovery and pre-computation layer — the infrastructure that makes the member experience feel instant. It runs every Sunday on Railway, completely independently of any member action, executing a four-scraper pipeline that pulls structured data from Michelin’s rendered DOM, Lartisien’s paginated city listings, Forbes’s award-winners JSON API, and 30 targeted Brave Search queries per weekly run. Every discovered venue is scored 0–100 across 13 weighted signals, evaluated by Claude Haiku for CCTF environment classification, passed through a quality gate, and pre-ranked into precomputed_venue_pools — one row per market × intent × environment combination. The result: the first time a member taps CURATE in any market, the answer is likely already there. The system gets faster and more complete with every weekly run, compounding its advantage over any product that discovers venues in real time.
.card__menu-content for hotel cards, .michelin-award for Key recognition. Extracts name, Michelin Key status, guest score, member privileges. Writes to michelin_hotels. Technology: Puppeteer DOM — not Brave Search. Ground truth from Michelin's own rendered pages./hotels/united-states/{slug}. Walks pages via ?page=N until no new hotel slugs appear. City-keyword filtering strips promoted non-market cards. Names extracted from h2/h3 inside hotel links. Markets: Scottsdale, Los Angeles, Santa Barbara. Seattle and Maui skipped (no Lartisien coverage). Writes to lartisien_hotels.forbestravelguide.com/award-winners.json — 2,400+ properties as structured JSON. No browser required. Filters to propertyType: 'HOTEL' then by city name. Ratings stored: FIVE_STAR, FOUR_STAR, RECOMMENDED. One API fetch covers all 5 markets. Writes to forbes_hotels.broad_search_hotels.| Market | Michelin | Lartisien | Forbes | Broad Search |
|---|---|---|---|---|
| Scottsdale | ✓ | ✓ | ✓ | ✓ |
| Los Angeles | ✓ | ✓ | ✓ | ✓ |
| Santa Barbara | ✓ | ✓ | ✓ | ✓ |
| Seattle | ✓ | ✗ (no coverage) | ✓ | ✓ |
| Maui | ✓ | ✗ (no coverage) | ✓ | ✓ |
broadSearchScraper.js extractHotelName() with two enhancements. (1) Strip patterns: em-dash subtitle strip (/\s*[—–]\s*.+$/) removes everything after an em/en dash; geographic suffix strip removes “ in City, ST 2026” patterns. (2) Generic title guards: new patterns reject any extracted name that starts with “the best”, “review of”, or article-opener phrases (this is / these are / here are / here’s / why / how / what / inside / meet / proud / discover / explore / introducing / welcome / find / ranked / rated / voted), ends in punctuation (.!?), ends with a year, or is longer than 8 words. If all these checks fail to produce a clean name, extractHotelName() returns null and the scraper skips that result entirely. Bad DB rows from pre-fix runs were cleaned via Supabase SQL (DELETE FROM broad_search_hotels WHERE name ~ pattern).scrape_log entry on success AND error, including source, market, new_venues count, status, and error_message. The AdminDiscoveryPage polls scrape_log for status: 'complete' or status: 'error' to track pipeline progress in real time. If a scraper cannot write to scrape_log, it logs at CRITICAL severity — this breaks the frontend polling contract.engine/qualityScore.js reads all 4 scraper tables to compute a composite score per venue. Stored in venue_quality_scores.
services/discoveryBridge.js evaluates venues from each scraper table not already in seed or pending queue. Three sequential passes with an identical sub-pipeline:
michelin_hotels. Per venue: dedup check against seed.js + pending_venues → Brave Search enrichment (description + location context) → cross-reference Forbes + Lartisien tables → Claude Haiku classifies (CCTF environment + quality_pass) → computeQuickScore() → upsert to pending_venues.forbes_hotels. Same sub-pipeline: dedup → enrich → cross-reference Michelin + Lartisien → Haiku classify → upsert.broad_search_hotels. Same sub-pipeline: dedup → enrich → cross-reference all 3 tables → Haiku classify → upsert.environment (CCTF tier — Atelier, Sanctuary, Estate, Vanguard) and quality_pass: true/false. This is the quality gate decision for each discovered venue.status = 'approved'. Goes live immediately — included in the next precompute run without any manual review.status = 'pending'. Enters the admin review queue. Requires manual approval before appearing in member curations.pending_venues holds all discovered venues in both states. Approved venues enter precompute. Pending venues wait in the queue.services/precompute.js reads all non-blocked seed venues plus approved discovered venues per market. Ranks by quality score × intent affinity × environment match. Writes to precomputed_venue_pools — one row per market + intent + environment combination.precomputed_venue_pools — queried by the curation pipeline for instant results when a member taps CURATE. A pool hit = sub-100ms delivery. The entire Speed Engine exists to make this table complete and accurate.POST /internal/run-scrapers with x-cron-secret header — all 4 scrapers → discovery bridge (3 passes) → precompute (all markets). Monitored via AdminDiscoveryPage polling scrape_log.POST /internal/run-discovery — bridge only, no re-scraping. Use when scraper data is fresh but re-classification or re-scoring is needed.POST /internal/precompute-all-markets — reruns precompute only. Use after manually approving pending venues to push them into live pools immediately without waiting for the next full pipeline run.0 2 * * 0) and runs all 4 scrapers + quality score recompute. Cron Service 2 fires at 4:00 AM UTC Sunday (0 4 * * 0) and runs precompute for all markets. The 2-hour gap gives scrapers time to complete before precompute starts building pools from fresh data.0 2 * * 0 Start Command: curl -fsS -X POST $API_BASE_URL/internal/run-scrapers -H "Authorization: Bearer $INTERNAL_CRON_SECRET" -H "Content-Type: application/json"0 4 * * 0 Start Command: curl -fsS -X POST $API_BASE_URL/internal/precompute-all-markets -H "Authorization: Bearer $INTERNAL_CRON_SECRET" -H "Content-Type: application/json"INTERNAL_CRON_SECRET (same value as main API service) and API_BASE_URL=https://api.curatevip.ai.POST /internal/run-scrapers).
/internal/* endpoints require the x-cron-secret header with value equal to INTERNAL_CRON_SECRET env var. This is a different secret from ADMIN_SECRET (which protects /api/admin/*). Never expose either value in logs or screenshots."Before you pick a venue, sweep the full geography and the full reference stack."
Hotel chain disqualifiers apply to STAY recommendations only. A great restaurant at a disqualified hotel still qualifies for TABLE on food quality, environment, and experience alone.
"Live credential intelligence is the signal. Seed data is the calibration. Claude's judgment is the engine." Updated — June 30
"The Scout goes ahead of the member, finds the reservation door, and comes back with the right link."
USE_EMBEDDED_BOOKING = true in VenueDetailPage.jsx line 11 to re-enable instantly. Off because Tock blocks iframe embedding (X-Frame-Options: DENY).The single biggest source of classification errors in this project was definitions evolving in conversation while old code ran with stale definitions. Four Seasons was tagged Estate in the AI prompt while Vanguard was simultaneously defined as brand flagships. These errors required human intervention to catch, multiple times.
server.js entry point + extracted engine modules (engine/curate.js, engine/taxonomy.js, engine/scoring.js) + services layer (websearch.js, broadSearchScraper.js, michelinScraper.js, lartisienScraper.js, precompute.js, discoveryBridge.js, qualityScore.js, reservationScout.js).venues, market_venue_cache, venue_content_cache, venue_photo_cache, vision_classifications, precomputed_venue_pools, michelin_hotels, forbes_hotels, lartisien_hotels, broad_search_hotels, pending_venues, venue_quality_scores, scrape_log, admin_credentials, members, classification_corrections.ENV_COLORS, INTENT_MAP, speed tier names, environment definitions, wild card adjacency map. Single source of truth imported by curate.js and server.js.computeSpeedProfile() — maps member CCTF quiz answers to intent affinity scores. Used to rank venues against member preferences when serving from precomputed pools..card__menu-content + .michelin-award. Extracts: name, Michelin Key, guest score, member privileges. Writes to michelin_hotels.?page=N pagination. Renders Lartisien city listing pages, walks pagination until exhausted, city-keyword filters to remove cross-market contamination. Writes to lartisien_hotels.award-winners.json. Returns 2,400+ Forbes-rated properties as structured JSON. No Puppeteer. Filters to propertyType: ‘HOTEL’ then city. Writes to forbes_hotels.extractHotelName() with 20+ pattern guards strips article titles to clean hotel names. Writes to broad_search_hotels.pending_venues. Auto-approve / pending-review routing.precomputed_venue_pools. Reads seed + approved discovered venues, scores by quality × intent × environment, writes ranked pools. Delete-before-rewrite integrity (throw on delete failure).venue_quality_scores.formatSignalsBlock().venue_reservation_links. Hardcoded overrides for bot-blocking sites.CurateVIP does not process bookings or take payment directly. STAY venues that exist in LiteAPI inventory deep-link to a branded White Label booking site on our own domain; the rest fall back to Expedia affiliate links. Curation is ours; fulfilment — and its regulatory burden — belongs to the partner.
book.curatevip.ai/hotels/{liteApiId} with checkin / checkout / occupancies. Mapped in WHITELABEL_HOTEL_IDS (venueId → lp-id). Nuitee is merchant of record.EXPEDIA_AFFILIATE_URLS / getExpediaUrl()). This is their only booking path. (1 Hotel Seattle, Lotte, Hermosa Inn, Global Ambassador, San Ysidro Ranch, Chateau Marmont, Sunset Tower, Faena, The Setai, Soho Beach House.)USE_WHITELABEL_STAY_BOOKING. True = White Label + Expedia fallback (current). False = restores the legacy in-app LiteAPI booking panel + Expedia everywhere. The in-app panel (StayBookingPanel.jsx) is preserved behind this flag as a failsafe.book.curatevip.ai — custom domain (CNAME to CloudFront), SSL auto-issued via AWS ACM. Set in the Nuitee dashboard (Settings → Domains) plus 3 DNS records at Porkbun: one CNAME for the domain, two CNAMEs for SSL validation.whitelabel-assets/curatevip-whitelabel-FINAL.css + curatevip-whitelabel.js.The restaurant counterpart to the Speed Engine (§11). Two signal families — durable prestige and decaying scene heat — feed the same discovery bridge, quality scoring, admin review queue, and precomputed pools. Rides the existing Sunday cron; no new infrastructure.
michelin_restaurants.table_search_signals with family + found_at.computeQualityScore() routes category === 'TABLE' venues to computeTableQualityScore(). STAY logic untouched.classification_corrections).pending_venues (category TABLE) → auto-approve on quality_pass or admin review → weekly precompute ranks approved venues into the TABLE pools members draw from. Migration: 008_table_discovery.sql. Cron: TABLE scrapers + Pass 4 added to POST /internal/run-scrapers.Every classification call in the Speed Engine (STAY and TABLE alike) now runs on Claude Fable 5 — the frontier model, reserved for this one judgment because it is the single decision in the pipeline that reaches a member directly. Fable does not decide cold, and it is not a "set once" choice either. Two feedback signals accumulate over time and are injected into every future classification prompt, so the system carries memory of what it has gotten right and wrong — and a third, ongoing discipline keeps the model itself current as Anthropic's lineup evolves.
quality_corrections, distinct from the pre-existing classification_corrections table (which teaches environment/category placement, e.g. Provision vs. Pulse). This is a parallel loop for the higher-stakes decision: existence in the pool, not bucket placement.venue_engagement. This is treated as a positive signal in its own right: a member choosing to act on a venue is real behavioral evidence that the curation was right, distinct from editorial theory. A completed LiteAPI/Nuitee booking (via webhook, once registered) logs separately as booking_completed — the true demand signal, one step stronger than click intent. Tracking is wrapped so a failure can never break or delay the member's actual booking flow.quality_score from a handful of early clicks risks letting one curious tap distort a venue's standing before the data means anything. The data accumulates starting July 2026; once weeks of real signal exist, a bounded demand nudge will be added deliberately — the same discipline applied to the Fable model upgrade itself — rather than guessing at weights from zero history.“CCTF solves the problem no luxury travel product has solved: mapping a specific person’s psychological state to a specific room at a specific moment. Every other luxury product matches budgets to star ratings. CCTF matches psychological appetite to spatial character — and does it autonomously, at scale, backed by a self-improving discovery pipeline that gets more complete every week.”
Most luxury travel products match venues to budgets, categories, or star ratings. None of them model the member's psychological appetite at the moment of travel. The CCTF Engine is the methodology that makes this possible.
During onboarding, members complete a psychographic intake for each of the three intent categories. The questions are designed to reveal underlying preference patterns through indirect questions — they do not ask "what kind of hotel do you like?" They ask questions whose answer patterns map to a Speed Tier.
{stay:"atelier", table:"couture", social:"shadow"}. This governs every curation the member receives. It can be updated by the member at any time.The indirect-question design: Members do not select "Atelier" from a list. They answer questions about how they experienced their last hotel stay, what they noticed first when they walked into the lobby, whether they wanted to hide away or be seen. The speed profile emerges from the pattern of answers. This prevents gaming and reveals genuine preference, not aspirational self-image.
Each tier has a canonical definition, a primary test question, and explicit disqualifiers. The definition is the authoritative source. Anything not in code or prompt that conflicts with these definitions is wrong.
"The quality floor is what makes CURATE trustworthy. Without it, it's just an opinionated list. With it, it's a methodology."
Every venue that appears in CURATE must clear the quality floor before Environment classification even begins. A venue that is a perfect Verandah but does not clear the quality floor is not in CURATE. The Environment taxonomy describes the type of experience. The quality floor describes the standard of execution.
The CCTF Engine does not rely solely on training data for credential validation. At the moment of every curation, the system queries live web sources via Brave Search API to fetch current credential signals for the market and category being curated. Results are cached for 7 days per market and refreshed automatically. This means Claude receives up-to-date Michelin, James Beard, Forbes, Robb Report, and other signals before making recommendations — not frozen training memory. If a venue has lost credentials, changed ownership, or closed since Claude's training cutoff, the live signal layer surfaces that information and Claude adjusts its recommendations accordingly.
Chef awards earned at a prior employer do NOT transfer to the chef's current restaurant. This is the single most common credential misattribution error in AI curation. A James Beard Award belongs to the venue where it was earned, not to wherever that chef works today.
formatSignalsBlock() guidance in websearch.js: credentials are DISCOVERY SIGNALS not admission tickets — chef pedigree at prior employers is context, not a credential for the current venue. (3) server.js curation prompt: CREDENTIAL INTERPRETATION section — "Is this credential FOR THIS VENUE, or did the chef earn it at a previous restaurant?" is the first test applied before any recommendation is made.{ id: 'tomo-seattle', blocked: true, description: 'Neighborhood restaurant in White Center, located in a former adult video store. Chef Brady Williams earned his James Beard Award at Canlis — Tomo itself has no equivalent venue-level credentials. The physical setting, neighborhood, and price tier do not meet CurateVIP caliber. Do not recommend regardless of chef pedigree.' }Formal credentials are reference points, not a mandatory gate. A venue can clear the quality floor without any credential if: it operates at a price point and service standard consistent with Five-Star hospitality, it appears in the curated editorial of major luxury travel publications (not rankings, editorial coverage), and a knowledgeable evaluator applying the CCTF protocol would immediately recognize it as ultra-luxury. Conversely, having a credential does not automatically clear the floor — the credential must reflect the actual current state of the property.
The protocol is the sequence of questions applied in order to any candidate venue. The protocol is what makes classification deterministic and reproducible by any trained evaluator, independent of personal taste.
blocked: false but not actively recommended (no Environment). Can be revisited if a new tier is added.surrounding: true and only surface when the member actively opts in via the surrounding toggle. Core market venues are always included.surrounding: true.environment_blocked: ['Verandah'] keeps it in the platform but prevents it from surfacing in Verandah curations.environment_blocked array.Each example shows the full protocol applied to a real venue. These walkthroughs prove the system is deterministic and consistently applicable to hard cases. A library of worked examples is what makes a methodology credible to a diligence team or IP attorney.
"The wild card is the recommendation the member didn't think to ask for but immediately recognizes as right."
The three main results match the member's declared Speed Tier. The Wild Card is selected from an adjacent tier — a venue that is sufficiently close to the member's profile that it's within reach, but distinctly different enough to expand their vocabulary. The wild card is the engine's most sophisticated output.
The question a buyer asks: "What would it cost a competitor to replicate this?" Here is the honest answer for each layer of the moat.
.card__menu-content + .michelin-award), Lartisien v3 (Puppeteer + ?page=N pagination across city listing pages with keyword filtering), Forbes (direct award-winners.json API — 2,400+ structured properties, no browser required), and Broad Search (6 targeted Brave Search queries × 5 markets = 30 award-source queries per run). Every discovered venue is scored 0–100 across 13 weighted signals, evaluated by Claude Haiku for CCTF environment classification, and passed through a quality gate. Auto-approved venues enter precomputed_venue_pools immediately; borderline cases route to admin review. The precompute engine rebuilds ranked pools for every market × intent × environment combination each Sunday. A competitor who calls the same APIs gets real-time latency and per-query AI cost on every curation. CurateVIP's curation is served from a pool that was built before the member arrived — and that pool grows deeper with every weekly pipeline run.This section is the investor-facing technical brief. It answers in one read: what exactly happens between a member tapping "TABLE" and receiving three curated venue cards — and why that pipeline cannot be replicated by a competitor who simply calls the same AI APIs.
CurateVIP does not ask Claude "what are the best restaurants in Seattle." It feeds Claude a structured block of live credential intelligence — fetched seconds before curation — alongside a proprietary seed library of manually confirmed venues and a member-specific speed profile. The AI's job is not to recall; it is to judge, filter, and match. That distinction is the entire moat.
What takes a traveler 2.5–3 hours of manual research across 14 browser tabs, CurateVIP returns in 30 seconds.
Fires on every cache miss. Results cached 7 days per market + category — a single API cost amortized across hundreds of curations.
Promise.all() — the full sweep completes in parallel, not sequentially.[{ name, credentials[] }] — venue names mapped to credential signals. Haiku does not rank or curate; it only extracts. Cost: ~$0.0003 per run. Haiku receives a CRITICAL rule at extraction time: chef awards follow venues, not chefs. A James Beard Award earned at a prior employer is context only — it does not become a credential for the chef's current restaurant.formatSignalsBlock() in websearch.js assembles the structured extraction output into a text block formatted for Claude Sonnet. The block lists each credentialed venue and its signals. It also states explicitly: credentials are DISCOVERY SIGNALS, not admission tickets. They tell Sonnet which venues have passed external quality checks — Sonnet still applies the full CCTF protocol to decide whether they belong in this member's curation.credentialedVenueCount), is returned to server.js and injected into the Sonnet prompt. The member-facing spinner shows the sources being checked in real time — 14 sources for TABLE, 9 for STAY, spread across 25 seconds (~1.8s each) — so members see the work happening, not a loading spinner.Cache-first. On a hit, Phase 2 costs $0.00 and returns in milliseconds. On a miss, Sonnet runs once and the result is cached asynchronously.
* Canlis: James Beard Award winner | Robb Report Best. This is live data, not training memory.seed.js) contains every venue manually researched, evaluated against the quality floor, classified by CCTF environment, and confirmed. Seed data tells Claude what ultra-luxury looks like in each specific market — a calibration layer, not an allowlist. Blocked venues are excluded from the calibration prompt entirely so Claude never considers them as candidates.confirmed: true in seed.js bypass this guard. Runs only when raw search results are in memory (cache miss on quality_signals). On cache hit: skipped. This is the guard that catches the Hotel 1000 class of error: a venue surfaced from training knowledge alone, with no live web signal, no primary credential, and no seed entry. Logged as [signal-guard] in Railway. Cost: ~$0.001–0.002/run. Latency: +500ms–1s on cache-miss path only.blocked: true in seed.js. These are venues confirmed too casual, wrong tier, or otherwise inappropriate regardless of what Sonnet returns. Logged as [seed-block] in Railway. Each blocked entry includes documentation of why — encoding institutional knowledge that prevents the system from repeating the same error. Example 1: Tomo Seattle — blocked permanently regardless of chef pedigree (James Beard attribution error case study). Example 2: Hotel 1000 Seattle — blocked after being surfaced by training knowledge and passed through the old quality gate via LHW membership alone. Documented: 120 rooms, $300–$450/night, no Forbes Five Star / Michelin Key / AAA Five Diamond. Its own character description (“zero scene, better than the address suggests”) is disqualifying language for CurateVIP.environment_blocked: ['Verandah'] on a seed entry. Example: Canlis is valid for Provision/Couture but blocked for Verandah — it is an indoor glass-wall building, not an open-air environment. Logged as [env-block].surrounding: true in seed.js when the member has not toggled "Include Surrounding Areas." These venues exist in the seed and Sonnet knows about them, but they do not appear until the member opts in. Logged as [surrounding-block].Brave Search API access: commodity. Any developer can get this in 10 minutes.
Claude API access: commodity. Any developer can get this in 10 minutes.
The CCTF vocabulary and Speed Tier taxonomy: not replicable without the same iteration history. Each of the 11 Speed Tier names encodes precise definitions, disqualifiers, and worked examples developed over multiple cycles. A competitor can copy the names; they cannot copy what the names mean in the context of a curation decision.
The seed database: not replicable without years of manual research. Every entry was evaluated, classified, and confirmed. Blocked entries encode hard-won decisions — Tomo Seattle (chef attribution error), Hotel 1000 Seattle (LHW-alone gate failure) — that prevent the specific class of errors that make AI curation untrustworthy. Each blocked entry documents why it was blocked, creating an institutional knowledge base that compounds with time. A competitor starting today gets generic LLM responses. CurateVIP's AI gets market-calibrated responses anchored to confirmed classifications.
The brand tier framework: The distinction between hard disqualifiers (Marriott, Hilton — no exceptions), scrutinize-harder brands (Thompson, Andaz — property-level evaluation required), and presumptively qualified operators (Aman, Four Seasons — brand is the credential) was developed through live curation testing, not desk research. The specific assignment of brands to tiers and the two-signal requirement for Tier 2 brands represent accumulated curation judgment that cannot be reconstructed without running the system against real markets.
The discovery source hierarchy: The architectural decision to make live Brave Search signals the primary discovery source — with training knowledge as supplement of last resort — prevents training-memory-only recommendations from passing quality gates. A competitor who copies the API calls without the prompt architecture gets the same class of errors CurateVIP had before this layer was built. The hierarchy requires both the signal pipeline and the curation prompt working in concert.
The attribution rules and worked-example library: the James Beard / Canlis / Tomo pattern is documented, seeded into the extraction prompt, the signal block formatter, and the Sonnet curation prompt. A competitor would need to independently discover and document every category of misattribution error. CurateVIP already has them — and adds new ones as edge cases emerge.
The Signal Guard: a second Haiku validation pass that runs after Sonnet, checking each proposed venue against raw Brave Search results before the result reaches the user. The three-state response design (found / not_found / uncertain), the seed-confirmed bypass, the cache-hit skip, and the non-fatal failure mode are all non-obvious design decisions. A competitor who copies the Brave Search queries and Sonnet prompt but omits the Signal Guard gets the same training-knowledge-surfacing errors CurateVIP eliminated. The guard adds ~$0.002/run and 500ms on the cache-miss path — an invisible cost for a significant trust guarantee.
The three-layer post-filter chain: each filter encodes classification decisions that took months to surface. Environment blocking (Canlis ≠ Verandah) requires knowing the venue, knowing the environment definition, and deciding to encode the block at the seed level. A competitor cannot guess these entries; they can only discover them by watching the system make errors, which requires having members who notice and report them.
The defensible summary for an investor: The APIs are commodity. The intelligence layered on top of them — seed data, classification vocabulary, attribution rules, post-filter chain, member profile matching — is not. It compounds with every new market, every new blocked venue, every new edge case discovered and encoded. A competitor who launches tomorrow starts with the same APIs and zero of the institutional knowledge. That gap widens, not narrows, with time.
The Speed Engine is the autonomous background pipeline that discovers and pre-ranks qualifying venues across all active markets. It is distinct from the member-facing pipeline in H1 — it runs asynchronously on a cron schedule, builds the pool of classified venues that H1 draws from, and is what allows curation results to be served from pre-ranked pools at sub-100ms latency rather than from real-time AI inference on every member request.
CurateVIP does not surface venues at curation time by asking Claude what's good in this city. Before any member taps CURATE, four autonomous scrapers have already collected every qualifying hotel from Michelin, Lartisien, Forbes, and Brave Search. A discovery bridge has classified each one by CCTF environment and quality-gated them. A precompute engine has ranked them into pools. The member receives a pre-ranked result. The AI at curation time matches, narrates, and personalizes — it does not discover. Discovery already happened.
.card__menu-content, .michelin-award. Extracts: name, Michelin Key, guest score, member privileges. Not a Brave Search query — actual DOM parsing of Michelin's rendered listing pages. Writes to michelin_hotels.?page=N pagination until exhausted. City-keyword filter prevents cross-market card contamination. Coverage: Scottsdale, Los Angeles, Santa Barbara. Writes to lartisien_hotels.award-winners.json returns 2,400+ properties. No browser. Structured data: property name, city, rating tier. All 5 markets covered in one request. Writes to forbes_hotels.broad_search_hotels.extractHotelName() function in broadSearchScraper.js applies two layers of defence: (1) strip patterns that remove em-dash subtitles and geographic suffixes (“in City, ST 2026”) from otherwise clean titles; (2) generic title guards that reject extracted strings matching article-opener patterns (the best, review of, here are, here’s, why/how/what/inside/discover/explore, etc.), ending in punctuation, ending with a year, or exceeding 8 words. If no clean hotel name can be extracted, the result is skipped entirely. This makes the broad_search_hotels table reliable as a discovery signal even though the underlying Brave API data is unstructured.0 2 * * 0). Sunday 4:00 AM UTC — precompute for all markets (0 4 * * 0). Two Railway Cron services, same backend repo, configured with INTERNAL_CRON_SECRET env var. The pipeline is self-sustaining: each Sunday it discovers new venues, scores them, classifies them, and rebuilds pools — with no manual intervention required.pending_venues.pending_venues carries its classification decision, quality score, signal breakdown, and discovery source. Every venue manually rejected carries the reason. This creates an institutional memory of what the quality floor looks like in each market — a judgment library that a competitor cannot reconstruct by running the same APIs, because the CCTF taxonomy governing the classifications is not available from any API.The precomputed pool is the infrastructure that makes CurateVIP feel instant. A member who curates Sanctuary hotels in Seattle is not waiting for the system to discover, evaluate, and rank hotels in real time. The pool was built before they arrived. Their curation is a retrieval + personalization, not a discovery. This is what separates a product from a demo.
A methodology that evolved deliberately has more legal and commercial weight than one that appeared fully formed. This version history demonstrates that the CCTF Engine is a living system, iterated with intention. For IP counsel: the dates below establish a timeline of development and first use.
.card__menu-content + .michelin-award, writes to michelin_hotels), Lartisien v3 (Puppeteer + ?page=N pagination, city-keyword filtering, writes to lartisien_hotels), Forbes (direct award-winners.json API, no browser, writes to forbes_hotels), Broad Search (6 Brave Search queries × 5 markets, writes to broad_search_hotels). Quality scoring engine: 0–100 point scale, 13 weighted signals plus source-bucket convergence bonus, stored in venue_quality_scores. Discovery Bridge: 3 sequential passes, each: dedup → Brave Search enrichment → cross-reference all scraper tables → Claude Haiku classifies (CCTF environment + quality_pass) → upsert to pending_venues. Quality gate: quality_pass true → status 'approved' (live immediately); quality_pass false → status 'pending' (admin review queue). Precompute engine: ranks seed + approved venues by quality score × intent × environment → precomputed_venue_pools. Full error handling audit across 9 backend files (55d3b93): 25+ gaps closed; throw on data-corrupting failures (delete+upsert race), warn+continue on signal-absent failures, CRITICAL log when scrape_log unreachable. Pipeline endpoints: /internal/run-scrapers (full), /internal/run-discovery (bridge only), /internal/precompute-all-markets (precompute only). New sections in IP methodology: §H moat item and §H2 Speed Engine Architecture.TRADE SECRET NOTICE. The methodology, taxonomy, decision protocol, classification rules, worked examples, seed data architecture, wild card logic, and version history contained in this document constitute trade secrets of RMK Group LLC, DBA CURATEvip, protected under the Defend Trade Secrets Act (18 U.S.C. § 1836 et seq.) and applicable state law.
formatSignalsBlock() signal assembly architecture. (12) The credential attribution rule system: the documented categories of misattribution error, the three-layer enforcement architecture (Haiku prompt → signal formatter → Sonnet curation prompt), and the canonical worked examples (Canlis / Tomo Seattle). (13) The discovery source hierarchy: Brave Search live signals as primary, seed calibration as approved reference, training knowledge as supplement of last resort. The requirement that a venue not in seed.js must appear in live signals (absent a primary credential) is a non-obvious architectural decision that prevents training-memory-only recommendations. (14) The brand tier framework: the three-tier classification of hospitality brands (hard disqualifiers / scrutinize-harder / presumptively qualified) and the two-signal requirement for Tier 2 brands, developed through live curation testing and edge-case discovery. (15) The Signal Guard: a post-Sonnet Haiku validation pass that cross-references each proposed venue against raw Brave Search results. The three-state response design (found / not_found / uncertain), seed-confirmed bypass logic, cache-hit skip behavior, and non-fatal failure mode constitute a distinct and non-obvious system. The guard eliminates the class of errors where AI training knowledge surfaces a venue with no live web verification — a problem that is invisible until a specific misclassification reaches a member.extractHotelName() function in broadSearchScraper.js: the specific pattern library for stripping article titles from Brave Search page titles (em-dash subtitle strip, geographic suffix strip, 8 generic article-opener guards, 8-word count heuristic). Derived through iterative live-data testing; not derivable from API documentation.Document prepared: July 9, 2026. Last updated: July 19, 2026 (v2.4 — Fable 5 learning loop expanded to a three-part discipline: quality-bar corrections + engagement tracking + model-currency verification; v2.3 — Fable 5 learning loop: quality-bar corrections + engagement tracking; v2.2 — TABLE dual-credential discovery engine; v2.1 — booking architecture / White Label fulfilment; v2.0 — Speed Engine architecture). Owner: Rick Bouffard, RMK Group LLC. Status: Active trade secret. Review annually or upon any material change to the methodology. Consult IP counsel before any M&A or licensing discussion that would require disclosure of this material.