Framework specification · v0.1 draft

Review Intelligence Framework

A tenant-configurable Voice-of-Customer platform: one dashboard shell, hundreds of review sources normalised into a single model, and a page set that adapts to whether the company runs stores or not.

Reviewed build · bluestone_cxm (internal deployment) Reference tenant · Titan / BlueStone Companion wireframes · 9 artboards
Review

Where the current build stands

The deployed app is a React SPA — five views, one connected source, no authentication. It is a credible single-brand demo and a poor starting point for a multi-tenant product, in ways worth naming precisely before redesigning anything.

What it does today

ViewWhat it showsBacking dataVerdict
OverviewFour KPI tiles, star distribution, volume donut, 12-month rating trend, sentiment split, top three critical issues3,179 real Play Store reviewsKeep the shape
Reviews feedPaginated review cards, time-window tabs, star filterSame corpusToo thin to be evidence
InsightsSeven findings across six categories, each with three verbatim citationsHand-authored from a manual read-throughRight idea, not generated
Competitor intelSentiment vs four named brands, mention counts, matching reviewsKeyword matches — 14, 10 and 2 mentionsSample far too small
Store deep diveCity/store pickers, per-store KPIs, trend, review listSix hardcoded mock storesEntirely fictional

What it gets right

Three decisions are worth carrying forward unchanged. Every insight already cites real verbatim reviews rather than summarising them away — that instinct is the product's core. The source code is honest about its own fiction: mockStores.js opens with a comment stating that Play Store reviews carry no store information at all and that the view is placeholder. And the ingestion run log already records complete_history_verified: false with a coverage note — provenance discipline most review tools never bother with.

What blocks it from becoming a platform

Tenancy
No login, no company concept
BlueStone is compiled in — logo imported as a module, brand name hardcoded in copy. There is no company_id anywhere in the data or the query path.
Sources
One source, and the filter admits it
The source dropdown is disabled with a single option. Every cross-source claim the UI implies — "review volume" as a donut of one — is unearned.
Entities
Store analytics with no store data
The review schema is {id, name, p, r, d, s, t, reply, c}. There is no store, city, region, product, channel or segment field. Store deep dive cannot be made real without a new source and an entity resolver.
Sentiment
Sentiment is derived from the star rating
Labelled "sentiment score" in the KPI tile but computed by bucketing r. A 5★ review complaining about delivery counts as positive. Fine as a fallback, wrong as a headline metric.
Insights
Insights are static content, not output
Seven findings in a JS file, with a comment saying to re-derive them by hand if the corpus refreshes. Nothing recomputes when data arrives.
Filters
Filter state does not survive navigation
Each view holds its own local state. A date range set on Overview is lost on the way to Insights — which breaks the one workflow that matters: notice a number, go find the reviews behind it.
No action layer
Findings terminate in a card. Nothing can be assigned, tracked, escalated or verified, so nothing closes the loop from insight to operational change.

Architecture

Tenant model

Company selection happens once, at the login page. Sign-in resolves exactly one tenant configuration bundle, and the dashboard renders from it. There is no switcher inside the product; changing company means signing out.

The rule that makes this safe is not a UI rule. Every query carries company_id at the data layer — row-level security or schema-per-tenant, enforced below the API. A hidden switcher is a UI convenience; a scoped query is the actual boundary.

The configuration bundle

KeyContentsConsumed by
brandingLogo, wordmark, palette, favicon, report cover, display nameApp shell, PDF/report renderer
company_typeSTORE_BASED or NON_STORERoute table, nav builder, filter registry
entity_modelDeclared hierarchy and its labels — e.g. Brand → Region → City → Store → CounterAll drilldowns, group-by, benchmarks
dimensionsSecondary axes available: product, app, channel, plan, segment, journey stage, cohortFilter bar, theme breakdowns
sourcesConnector list with credentials, granularity and field capability per sourceData Hub, every "which sources" disclosure
taxonomyUniversal theme set plus tenant-specific extensions and synonymsTheming pipeline, theme filters
dashboard_configEnabled page set, default filters, KPI targets, minimum-n thresholdsNav, page guards, KPI tiles
permissionsRole, entity scope (region/store or product/segment), PII visibility, export rightsEvery page and every export

Two company types, one shell

LayerStore-basedNon-store
Primary entityStore / branch / outletProduct / app / plan
HierarchyBrand → Region → City → Store → CounterBrand → Product line → Product → Release / Plan
Secondary axesProduct, app, channel, segment, plan, journey stage, cohort — store-based tenants get these tooChannel, segment, journey stage, cohort, device/OS
Exclusive pagesStore Performance, City & Region, Staff & Service QualityProduct & App, Channel Performance, Segments & Cohorts, Journey Stages
Exclusive filtersRegion, City, Store, Store format, Store ageProduct, App version, Plan, Segment, Journey stage
Shared everywhereOverview · Reviews Feed · Insights · Competitor & Trust · Support & Resolution · Data Hub · Actions · Reports · date, source, rating, sentiment, theme, issue-type filters

A page that does not apply to a tenant is absent from the nav and returns 404 on its route. Showing it empty invites the question "why is our store data missing" for a company that has no stores.


Navigation

Sitemap & navigation

Five nav groups, ordered by how a working day actually runs: notice, investigate, locate, act, report. Group two is the only one that swaps between company types.

GroupPageRouteAvailability
MonitorOverview/overviewAll
Reviews Feed & Evidence Explorer/reviewsAll
Insights/insightsAll
NetworkStore Performance/storesStore-based
City & Region Performance/geographyStore-based
PortfolioProduct & App/productsAll (primary for non-store)
Channel Performance/channelsAll
Segments & Cohorts/segmentsAll
Journey Stages/journeyAll
ExperienceStaff & Service Quality/staffStore-based
Delivery & Fulfilment/fulfilmentAll
Support & Resolution/supportAll
Competitor & Brand Trust/competitionAll
OperateActions & Workflows/actionsAll
Reports/reportsAll
Data Hub & Source Coverage/dataAll

Three navigation rules

  • Filter state is global and lives in the URL. Moving from Overview to Insights carries date range, sources, entity scope, theme and sentiment. A pasted link reproduces exactly what the sender saw.
  • Every drilldown terminates in evidence. A KPI opens a breakdown, a breakdown opens a filtered Reviews Feed. No path ends at a number with nothing behind it.
  • Company context is display-only. Logo, name and type sit in the sidebar header as fixed text. Nothing in that area is clickable.

Specification

Page specifications

Each page states its job, its owner, the metrics it is accountable for, and the fields it cannot run without. Layouts for the first eight are drawn in the companion wireframe canvas.

OverviewAll tenants

Answer "what changed, where, and does it need me this week" in under a minute.

User
CX lead, brand head, regional leadership
KPIs
Total reviews · Avg rating · Positive share · Response rate · Entity coverage · Open critical actions
Modules
KPI row with deltas · volume-and-rating combo trend · source mix with coverage health · theme performance table · priority insight list · entity movers (improving/declining)
Filters
Date, comparison period, source, entity scope, rating, sentiment, theme, category
Drill
KPI → breakdown by entity · theme row → filtered feed · mover row → entity profile
Fields
rating, sentiment, theme_mention, entity_id, source_id, captured_at
Actions
Export summary, save view, subscribe to weekly digest
Reviews Feed & Evidence ExplorerAll tenants

Let anyone verify any claim in the product by reading the underlying reviews.

User
Analyst, store manager, support lead
KPIs
Matching records · unreplied count · median reply time · unmapped-to-entity count
Modules
Faceted filter rail · record list with theme chips and confidence · record-detail pane showing normalised fields and provenance · reply composer · bulk action bar
Filters
Full-text and phrase search, source, rating, sentiment, theme, entity, language, record state
Drill
Record → source record at origin · record → linked insight · record → linked action
Fields
raw_text, translated_text, reviewer_pseudonym, source_url, ingest_run_id, theme_mention.confidence, entity_match_score
Actions
Reply, tag theme, correct a label, create action, add to report, export selection
InsightsAll tenants

Surface generated findings ranked by impact, each stating its evidence, denominator and limits.

User
CX lead, analytics team, category owners
KPIs
Open insights by severity · insights actioned % · median age · precision on the labelled sample
Modules
Type tabs (emerging / recurring / anomaly / comparative / competitive) · ranked list with sparkline · detail pane with what-the-data-shows, breakdown chart, affected entity table, sampled quotes, limitations block, routing suggestion
Filters
Type, severity, confidence, status, theme, entity scope, date
Drill
Insight → all cited reviews · affected entity → entity profile
Fields
insight.pattern_id, evidence_count, denominator, confidence, first_seen_at, affected_entity_ids
Actions
Create action, assign owner, dismiss with reason, subscribe, add to report
Store PerformanceStore-based

Rank every location against its city and the network, and separate genuine problems from small samples.

User
Regional manager, retail operations, store manager
KPIs
Stores reporting vs in scope · region avg rating · p10–p90 spread · stores below threshold · median reviews per store · response rate
Modules
Volume-vs-rating quadrant scatter · store rating histogram against network median · sortable league table · coverage-gap strip · store profile drawer
Filters
Region, city, store format, cluster, store age, minimum n, source, theme, date
Drill
Store → profile → theme breakdown → filtered feed
Fields
store_id, city_id, region_id, format, opened_on, geo, store_master_match
Actions
Assign store action, claim a listing, add store to watchlist, export league table
City & Region PerformanceStore-based

Show where geography explains the difference — and where it only looks like it does.

User
Regional leadership, expansion and ops planning
KPIs
Cities with coverage · best and weakest region · inter-city variance · coverage debt
Modules
Treemap or geo map shaded by negative share · region comparison with store spread · theme mix heatmap by region · city watchlist
Filters
Region, city tier, date, source, theme, minimum n
Drill
Region → city → store → feed
Fields
region_id, city_id, city_tier, store_count, geo_centroid
Actions
Assign regional action, trigger listing-claim task, schedule city review
Product / Service ExperienceAll tenants

Separate what people think of the product from what they think of buying it.

User
Category manager, product owner, merchandising
KPIs
Rating by category · quality-complaint rate · return-linked mentions · design-praise share · app crash mentions per release
Modules
Category league table · theme mix by category · release/version overlay on trend · quality-issue cluster list · competitor product comparison
Filters
Category, SKU or product line, app version, price band, date, source
Drill
Category → product → issue cluster → feed
Fields
product_id, category_id, sku, app_version, price_band, order_ref
Actions
Raise quality investigation, flag SKU, brief merchandising, notify engineering
Staff & Service QualityStore-based

Turn named praise and complaints about people into coaching and rota decisions — without building a surveillance tool.

User
Store manager, regional manager, L&D
KPIs
Staff-mention rate · positive staff-mention share · behaviour complaints per 1,000 reviews · weekend vs weekday gap
Modules
Store × day × time-band heatmap · behaviour sub-theme breakdown (knowledge, pressure, courtesy, attentiveness) · named-mention list with consent policy note · coaching recommendation list
Filters
Store, region, day of week, time band, shift, tenure band, theme
Drill
Store → time band → sub-theme → feed
Fields
store_id, visit_timestamp, shift_id, staff_mention (text-derived), tenure_band
Actions
Schedule coaching, adjust rota, escalate conduct issue, recognise a named employee
Delivery & FulfilmentAll tenants

Track the gap between promised and experienced fulfilment, by route and by partner.

User
Supply chain, logistics, e-commerce ops
KPIs
Delivery-complaint rate · promise-vs-reality gap mentions · damaged-on-arrival mentions · exchange and pickup delay mentions
Modules
Complaint rate by city and partner · promised-date-gap distribution · fulfilment stage breakdown · repeat-offender route list
Filters
City, partner, order type, delivery window, date, source
Drill
Partner → route → order type → feed
Fields
order_ref, promised_date, delivered_date, carrier_id, fulfilment_type, destination_city
Actions
Raise partner review, adjust promise windows, trigger proactive outreach
Customer Support & ResolutionAll tenants

Measure whether contact with support made the experience better or worse.

User
Support lead, CX operations
KPIs
Response rate and median reply time by source · reopen rate · post-contact sentiment shift · scripted-reply rate · refund and return dispute volume
Modules
Response funnel by source · resolution-outcome breakdown · sentiment before and after contact · template performance table · escalation queue
Filters
Channel, issue type, agent group, SLA state, date, source
Drill
Channel → issue type → ticket thread → linked public review
Fields
ticket_id, channel, first_response_at, resolved_at, reopen_count, linked_review_id, reply_template_id
Actions
Retire a template, reassign queue, escalate, trigger recovery outreach
Competitor & Brand TrustAll tenants

Understand what customers say when they compare you to someone else, and what they doubt about you.

User
Brand, marketing, strategy
KPIs
Competitor mention rate · share of voice within own corpus · switch-intent rate · trust and authenticity complaint rate
Modules
Mention volume by competitor with sample-size warning · comparison-context breakdown (price, design, service, trust) · switch-intent evidence list · trust sub-theme trend
Filters
Competitor, comparison dimension, sentiment, date, source, entity scope
Drill
Competitor → comparison dimension → feed
Fields
competitor_mention.entity, comparison_dimension, switch_intent_flag, trust_subtheme
Actions
Brief marketing, raise policy-clarity action, commission research
Data Hub & Source CoverageAll tenants

Make the limits of the data visible, because every number elsewhere inherits them.

User
Data engineering, analytics, CX operations
KPIs
Connectors live vs configured · records ingested · duplicate rate · entity match rate · sources breaching freshness SLA · text coverage
Modules
Connector table with granularity and capability per source · ingestion run log · data-quality queue · language mix · read-only tenant configuration panel
Filters
Source type, health state, date
Drill
Source → run → failed records → mapping tool
Fields
source.granularity, auth_state, last_run_at, dedupe_rate, match_rate, sentiment_origin, history_start
Actions
Re-authenticate, run now, resolve entity mapping, add a source, raise a data ticket
Actions & WorkflowsAll tenants

Turn an insight into someone's job, with an SLA and a verification step.

User
Every owner group; CX lead owns the queue
KPIs
Open actions · breaching SLA · median time to close · insights actioned % · verified improvements
Modules
Status board (triage → assigned → in progress → verifying → closed) · routing rule table · post-close impact watch with explicit non-causal caveat
Filters
Owner, owner group, theme, severity, SLA state, entity scope, date
Drill
Action → source insight → cited reviews
Fields
action.insight_id, owner_id, owner_group, sla_days, status, closed_at, verification_window, baseline_metric
Actions
Assign, reassign, escalate, close with outcome, reopen
ReportsAll tenants

Produce the recurring artefacts people already circulate, with provenance attached.

User
CX lead, regional managers, executive audience
KPIs
Scheduled reports · delivery success · open rate · reports with stale data at send time
Modules
Template library (monthly brand, regional pack, store scorecard, product review, board summary) · schedule manager · branded PDF and deck export · snapshot archive
Filters
Template, recipient group, entity scope, period
Drill
Report → section → underlying view at the time of generation
Fields
report.template_id, period, scope, generated_at, data_as_of, recipient_group
Actions
Schedule, send now, export, share a frozen snapshot link

Reference

KPI dictionary

Every metric gets one definition across all tenants. Where a metric is only meaningful under conditions, the condition is part of the definition, not a footnote someone can drop.

MetricDefinitionGrainDenominator shownCaveat that must ship with it
Review volumeCount of normalised feedback records after dedupeAny entity × periodTotal records before filtersVolume reflects source coverage, not customer count
Average ratingMean of rescaled ratings, 1–5Any entity × periodRated records only, as % of totalExcludes unrated records; sources with 0–10 or thumbs scales are rescaled
Positive shareModel-classified positive records ÷ classified recordsAny entity × periodClassified records; unscored % statedName the model version; state when a source is rating-derived
Net sentimentPositive share minus negative share, in pointsAny entity × periodSame as positive shareNot comparable across tenants with different taxonomies
Theme mention rateRecords with theme above confidence threshold ÷ records with textTheme × entity × periodRecords with usable textRating-only records cannot carry themes and are excluded
Theme negative rateNegative records mentioning theme ÷ records mentioning themeTheme × entity × periodTheme mentionsThreshold value must be visible
Response rateRecords with a published reply ÷ records on reply-capable sourcesSource × entity × periodReply-capable records onlyNever computed over sources that do not support replies
Median first-response timeMedian hours from capture to first published replySource × entity × periodReplied recordsCapture time is not post time on scraped sources
Review velocityRecords per entity per 30 days, trailingEntity × periodActive entitiesSensitive to source outages — check Data Hub before reading a drop
Entity coverageEntities with ≥1 record ÷ entities in the masterEntity type × periodEntities in masterDistinguish "no reviews" from "not connected"
Entity match rateRecords resolved to an entity ÷ records from location-bearing sourcesSource × periodLocation-bearing recordsUnmatched records are absent from all entity analytics
Rating spread (p10–p90)90th minus 10th percentile of entity mean ratingsEntity type × periodEntities meeting minimum nEach entity weighted equally, not by volume
Competitor mention rateRecords naming a tracked competitor ÷ records with textCompetitor × periodRecords with textOwn-corpus only — not market share, not share of voice
Switch-intent rateCompetitor-mentioning records also expressing intent to leave ÷ competitor mentionsCompetitor × periodCompetitor mentionsStated intent, not observed churn
Insights actionedOpen insights with ≥1 linked action ÷ open insightsPeriodOpen insightsCounts assignment, not resolution
Verified improvementClosed actions whose theme rate fell beyond the control band in the watch windowAction × windowClosed actions past their windowAssociation, never causation; seasonality shown alongside
Data freshnessHours since the last successful run per sourceSourceShown on every page header, not buried in settings

Minimum-n rule. Any entity below the tenant's threshold (default 30 records in the period) is listed but never ranked, benchmarked, shaded on a map, or used in an insight. It shows n/a with the reason, not a blank.


Reference

Insight taxonomy

Fifteen universal themes that hold across jewellery retail, QSR, banking, clinics, gyms and SaaS. The theme is constant; only its sub-themes and the entity it attaches to change per tenant.

ThemeSub-themesStore-based expressionNon-store expressionDefault owner
Staff behaviourCourtesy, product knowledge, sales pressure, attentiveness, named praiseConsultant on the floorSupport agent, relationship managerStore / Regional manager
Ambiance & environmentCleanliness, crowding, lighting, seating, parking, safetyStore interior and surroundsApp UI clarity, web experienceRetail ops / Product
Wait time & queueQueue length, token system, counter staffing, appointment delayBilling and consultation queuesLoad time, processing delay, callback waitStore manager / Engineering
Inventory & availabilityOut of stock, size or variant gap, display vs actual, range depthSKU availability in storeFeature availability, plan limitsMerchandising / Product
Pricing & valuePrice level, hidden charges, discount clarity, comparison to rivalsMaking charges, offer termsFees, plan pricing, renewal costPricing / Product
Product or service qualityDurability, finish, defect, accuracy, performance, taste or fitCraftsmanship, certificationStability, correctness, uptimeQuality / Engineering
Billing & checkoutPayment failure, invoice error, GST or tax handling, receiptCounter billingPayment flow, invoicingFinance ops / Engineering
Location & accessibilityFindability, hours, wheelchair access, transport, listing accuracyStore listing and premisesChannel availability, device or OS supportRetail ops / Product
Delivery & fulfilmentDelay, damage, wrong item, tracking silence, pickup failureHome delivery, try-at-homeProvisioning, activation, dispatchSupply chain
Customer supportResponsiveness, scripted replies, escalation, resolution qualityStore and central supportChat, email, phone, in-appSupport lead
Returns, refunds & exchangePolicy clarity, refund delay, buyback terms, exchange frictionBuyback, exchange countersCancellation, downgrade, refundSupport / Finance ops
Trust, authenticity & complianceCertification doubt, hallmark, fraud accusation, data privacy, mis-sellingPurity and certification claimsData handling, disclosure, securityBrand / Compliance
Competitor mentionsNamed rival, comparison dimension, switch intent, switch completedNearby brand, other mall storeRival app or providerBrand / Strategy
Occasion & seasonalityFestival, wedding, gifting, sale period, month-end, paydayFestive and wedding peaksRenewal cycles, tax seasonMarketing / Ops planning
Loyalty & repeat signalsRepeat purchase, advocacy, referral, long-tenure mention, churn signalReturning customer languageTenure, plan upgrades, advocacyCRM / CX lead

Tenants extend this set rather than replace it — a clinic adds clinical outcome and appointment scheduling; a QSR adds food temperature and order accuracy. Extensions live under a universal parent so cross-tenant benchmarking stays possible.


Analytics

Advanced insight patterns

The insight engine is a library of patterns, each binding to whichever entity dimension the tenant declares. The same pattern that finds a struggling store finds a struggling product.

PatternWhat it detectsInputs requiredWhat it must not claim
Entity sentiment rankingBest and worst entities against the network, spread-awareRating, sentiment, entity, minimum nThat low rating means low performance when n is small
Geographic varianceCities or regions whose internal spread exceeds their gap to the meanEntity geography, entity meansThat a weak region is uniformly weak
Time-band behaviourThemes concentrating on a weekday, weekend or shift windowVisit or capture timestamp, theme, entityStaff accountability without rota data joined
Silent churnReview velocity falling sharply with no rating declineVelocity trend, source health, entityLost customers — needs footfall or transaction data to confirm
Demand signalVelocity rising ahead of a known season or launchVelocity trend, calendar, categorySales forecast without revenue data joined
Availability perception gapStock complaints high where inventory data says stock is healthyInventory theme rate, stock feed, entityAnything, unless the stock feed is joined at the same grain
Wait-time spikeQueue mentions breaching a control band for an entity and periodTheme rate, entity, period, control bandActual wait duration — this is reported, not measured
Switch intentCompetitor mention plus stated intent to leave, by comparison dimensionCompetitor mention, intent classifierChurn rate or market share
Occasion-linked dipSentiment falling in a repeatable window around a seasonal peak≥2 prior cycles, calendar, sentimentA pattern from a single observed season
Policy vs reality gapComplaints citing a published promise — offer terms, SLA, warrantyTheme, policy reference list, textLegal exposure — this is a CX signal, not a compliance finding
Review-to-revenue correlationAssociation between theme rates and sales at matched grain and lagSales data joined by entity and periodCausation, or any figure at all when sales data is absent

Every pattern emits the same envelope: headline · severity · confidence · evidence_count · denominator · affected_entities · sampled_quotes · limitations · suggested_owner. A pattern that cannot fill the denominator or limitations fields does not publish an insight.


Engineering

Common data model

Raw source records are kept immutable and separate from the normalised layer, so a re-theming or a model upgrade replays from source without re-scraping.

EntityKey fieldsRelationshipsNotes
companyid, name, type, residency, branding_ref, config_ref, statusRoot of everythingtype drives the page set. Every other table carries company_id.
brandid, company_id, name, logo_ref→ companyMulti-brand groups (Titan → Tanishq, Taneira) sit here, not as separate tenants
entityid, company_id, brand_id, type, parent_id, name, external_ref, attributesSelf-referencing treeOne polymorphic table for store, city, region, product, app, channel, plan, segment. type plus tenant hierarchy gives meaning.
store_profileentity_id, geo, format, opened_on, cluster, floor_area, manager_id→ entityStore-specific attributes kept out of the generic entity table
sourceid, company_id, kind, granularity, auth_state, capabilities, history_start, sla_hours→ companycapabilities declares rating scale, text, reply support, language, location. This is what gates a source from a page.
source_recordid, source_id, external_id, payload_raw, captured_at, ingest_run_id, checksum→ source, ingest_runImmutable. Never edited, never deleted on re-ingest.
feedbackid, company_id, source_record_id, entity_id, entity_match_score, occurred_at, rating_raw, rating_scale, rating_norm, language, text_raw, text_translated, reply_text, replied_at, dedupe_group→ source_record, entity, reviewerThe normalised record everything queries. One row per unique piece of feedback after dedupe.
reviewerid, company_id, pseudonym, source_handle_hash, is_repeat, first_seen_at, consent_state→ feedbackPseudonymised by default. Raw handles hashed. Retention policy per tenant residency.
sentimentfeedback_id, label, score, model_version, origin→ feedbackorigin is model or rating_derived — the UI must be able to tell them apart
theme_mentionfeedback_id, theme_id, subtheme_id, confidence, polarity, span_start, span_end, model_version→ feedback, taxonomySpans let the UI highlight the phrase that triggered the label
taxonomyid, company_id, parent_id, name, scope, synonyms, threshold, is_universalSelf-referencingUniversal nodes shared; tenant extensions hang off a universal parent
competitor_mentionfeedback_id, competitor_entity, comparison_dimension, switch_intent, confidence→ feedbackCompetitors are a reference list per tenant, not free text
insightid, company_id, pattern_id, headline, severity, confidence, evidence_count, denominator, scope_entity_ids, period, status, first_seen_at, limitations→ evidence_link, actionRecomputed each run; identity is stable so status and history survive
evidence_linkinsight_id, feedback_id, rank, is_sampled→ insight, feedbackMakes "show me the reviews behind this" a join, not a re-query
actionid, company_id, insight_id, title, owner_id, owner_group, entity_scope, severity, sla_days, status, opened_at, closed_at, outcome, baseline_metric, verification_window→ insight, userBaseline captured at open so verification has something to compare against
reportid, company_id, template_id, scope, period, generated_at, data_as_of, snapshot_ref, recipients→ companydata_as_of is distinct from generated_at — a report built on stale data says so
ingest_runid, source_id, started_at, ended_at, status, records_new, records_dup, records_failed, coverage_note, scraper_version→ sourceThe existing scraper's run-info shape is already close to this — keep it
data_quality_issueid, company_id, kind, severity, affected_count, detected_at, resolved_at, assignee→ source, entityUnmapped entities, expired auth, missing fields, schema drift
user / roleid, company_id, email, role, entity_scope, pii_visible, export_allowed→ companyA person provisioned for two companies has two user rows. Sessions never span them.

Normalisation rules that must be explicit

  • Rating rescaling. Store rating_raw with rating_scale, and derive rating_norm on 1–5. NPS 0–10, thumbs, and 1–10 scales each need a documented mapping, not an implicit one.
  • Dedupe. Same text posted to two sources, and the same review re-scraped, join one dedupe_group. One row counts; the others remain queryable as alternates.
  • Entity resolution. Listing name and address → store master, with a match score. Below threshold, the record lands in the data-quality queue rather than being silently attached or silently dropped.
  • Time. occurred_at (when the experience happened, where known), posted_at, and captured_at are three different fields. Scraped sources often only give the third.
  • Language. Detect, translate, theme on the translation, display the original with the translation available. The existing scraper already pulls 13 languages — the pipeline must not quietly drop twelve of them.

Requirements

Analytics requirements

Pipeline

  • Incremental ingestion per source with checkpointing; a failed run never loses the prior watermark.
  • Theming and sentiment run as replayable jobs over source_record, versioned by model, so an upgrade re-derives history instead of re-scraping.
  • Insight patterns run on a schedule per tenant, writing stable insight identities so status and age persist across runs.
  • Pre-aggregated rollups by entity × theme × period for dashboard latency; the feed always queries the base table.

Statistical discipline

  • Minimum-n threshold per tenant, applied to every ranking, benchmark, map shade and insight.
  • Control bands for anomaly detection derived from the entity's own history, not a global constant.
  • Comparison periods matched for length and seasonality; a 90-day window compares to the prior 90 days, with the same-period-last-year available.
  • Every model reports precision on a maintained labelled sample, visible in the product.

Honesty constraints

  • No revenue or footfall claim without that data joined at the same entity and period grain.
  • Sentiment origin (model vs rating_derived) is carried through to every chart that shows sentiment.
  • Percentages below a stated sample size render as a count, not a percentage — 2 of 3 is not 67%.
  • A source outage is shown on any chart whose period it overlaps, so a volume drop is not read as a demand drop.

Performance targets

  • Dashboard pages under 2s at p95 for a tenant with 5M feedback rows.
  • Feed search under 1s at p95 with facets applied.
  • Insight recompute within the nightly window for the largest tenant.
  • Ingestion backlog alarm when any source exceeds its sla_hours.

Requirements

UI requirements

Shell & configuration

  • One codebase, one component library; tenants differ only by configuration and theme tokens.
  • Branding applied from the config bundle at boot — logo, wordmark, accent, report cover.
  • Nav built from the enabled page set; disabled routes 404.
  • Filter registry is config-driven, so adding a tenant dimension needs no UI code.

Filters & state

  • Global filter bar, state in the URL, preserved across navigation.
  • Standard set: date, comparison, source, entity scope, rating, sentiment, theme, category, issue type.
  • Saved views per user; shared views per team.
  • Match count visible on the filter bar at all times.

Evidence & disclosure

  • Every insight, KPI and chart element links to the reviews behind it.
  • Every figure displays sample size and denominator; every chart states its excluded sources.
  • Data freshness shown in the page header, not in settings.
  • Confidence and limitations render as first-class page content, never tooltips.

Craft

  • Semantic colour (critical / warning / healthy) is distinct from the tenant accent, so branding never changes what a status means.
  • Tables sortable on every column, with sticky headers and a column picker.
  • Keyboard navigation and visible focus throughout; charts have accessible table equivalents.
  • Exports carry the filter state, the data-as-of timestamp and the limitations text.

Engineering

Tenancy, configurability & scale

Tenant safety

  • company_id enforced at the data layer — row-level security or schema-per-tenant — never by application filtering alone.
  • Token scoped to one company; a user provisioned for several signs in separately per company.
  • Sign-out clears all cached tenant state, including saved views and local storage.
  • No cross-tenant model training. Shared universal taxonomy, isolated tenant data.
  • Per-tenant residency and retention honoured in storage, backups and exports.
  • Audit log for every export, reply posted, permission change and config edit.

Scaling to hundreds of sources

  • Connector SDK with a fixed contract: authenticate, fetch incrementally, emit a raw record, declare capabilities. A new source is a plugin, not a release.
  • Capability declaration is load-bearing — granularity decides which pages a source may feed, automatically.
  • Rate limiting, retry and backoff handled by the framework, not per connector.
  • Onboarding a tenant is a config bundle plus an entity master import, targeted at days rather than weeks.
  • Taxonomy versioned per tenant, with re-theming replayed from raw records.
  • Config validated on write; an invalid bundle fails at provisioning, never at a user's login.

Delta

Gaps to close

The distance between the deployed build and the framework above, ordered by what blocks the next thing.

GapTodayRequiredBlocks
TenancyBrand hardcoded, no authLogin, company selection, config bundle, scoped queriesEverything multi-tenant
Entity masterSix mock stores in a JS fileStore or product master with external refs and hierarchyAll store, city, region and product pages
Entity resolutionNoneListing-to-master matcher with score and a review queueAny location analytics
Source breadthPlay Store onlyGoogle Maps first, then directories, marketplace, support, surveyCross-source claims, coverage metrics
Normalised schemaNine-field flat review objectFull feedback model with provenance, scales, language, entity linkEvery downstream feature
SentimentBucketed from star ratingText model with version, score and origin flagCredible sentiment KPIs
ThemingNone — themes exist only inside hand-written insightsTaxonomy plus classifier emitting confidence and spansTheme filters, all theme analytics
Insight generationSeven static findingsPattern library, scheduled runs, stable identities, evidence linksInsights page, actions, reports
Filter architectureLocal state per viewGlobal URL-backed filter store with a config-driven registryDrilldowns, shared links, saved views
Action layerNoneActions, routing rules, SLAs, verification watchAny operational outcome
Data hubRun info exists but is not surfacedConnector health, quality queue, freshness, coverage — in the productTrust in every other number
ReportsExport buttons with no behaviourTemplates, schedules, branded output, frozen snapshotsExecutive adoption
PermissionsNoneRoles, entity scoping, PII and export controls, audit logEnterprise deployment
Competitor analysisKeyword counts as low as 2 presented as comparisonReference list, comparison dimensions, minimum-n suppressionCredible brand reporting

Discovery

What to collect from owners

Questions to put to each group before build. Anything unanswered here becomes an assumption baked into the schema.

Data owners

  • Store or product master: system of record, refresh cadence, unique key, and whether closed entities are retained.
  • Which sources have owned accounts and API access, and who holds the credentials.
  • Historical depth available per source, and whether backfill is permitted.
  • Whether sales, footfall, inventory, ticket and CRM data can be joined — at what grain, with what lag, under what agreement.
  • PII policy: what may be stored, for how long, in which region, and who may see reviewer identity.
  • Existing warehouse and whether this platform reads from it or ingests independently.

Analytics owners

  • Metric definitions already in use, so the KPI dictionary reconciles rather than competes.
  • Existing taxonomy, tagging or coding frames worth preserving.
  • Acceptable minimum-n threshold and confidence floor for publishing an insight.
  • Whether a labelled sample exists for model evaluation, and who maintains it.
  • Seasonality calendar: festivals, sale periods, launch windows, fiscal boundaries.
  • Which claims the organisation will not accept without statistical backing.

Business owners

  • The decisions this product must support, named concretely — and who makes each one.
  • Current reporting cadence, formats and audiences to be replaced or fed.
  • Escalation paths and SLAs already in force, so routing rules mirror reality.
  • Entity hierarchy as the business actually uses it, including clusters and formats that are not on any org chart.
  • Competitor set worth tracking, by market.
  • What a good quarter looks like in this product's terms — the definition of success.

UI / product owners

  • Primary users per page and their working context — desk, shop floor, mobile.
  • Brand assets and any existing design system or token set.
  • Localisation scope: interface languages, review display languages, date and number formats.
  • Accessibility standard to meet, and whether it is contractual.
  • Role matrix: who sees which entities, which fields, and who may export.
  • Whether replying to public reviews happens in this product or stays in an existing tool.

Engineering owners

  • Tenancy isolation model: RLS, schema-per-tenant or separate databases — and why.
  • Identity provider and SSO requirements per tenant.
  • Hosting, residency and compliance obligations by market.
  • Expected tenant count, entity count and record volume at 12 and 36 months.
  • Build-versus-buy for sentiment and theming, and the cost envelope for inference.
  • Operational ownership: who is paged when a connector breaks at 3am.

Sequence

MVP and beyond

Ordered so each phase is usable on its own. Phase one earns trust in the numbers; phase two makes them act on them.

Phase 1Trustworthy foundationOne store-based tenant, end to end
  • Login with company selection; config bundle; company_id enforced at the data layer.
  • Normalised feedback schema; Google Maps and app-store connectors; entity resolution against a store master.
  • Text sentiment and the 15-theme universal taxonomy with confidence scores.
  • Overview, Reviews Feed, Insights, Store Performance, City & Region, Data Hub.
  • Global URL-backed filters; evidence links everywhere; sample size and denominator on every figure.
  • Insight patterns: entity ranking, geographic variance, wait-time spike, policy-vs-reality gap.
Phase 2Operational loopSecond tenant, non-store
  • Actions, routing rules, SLAs, escalation, post-close verification watch.
  • Non-store page set: Product & App, Channel, Segments & Cohorts, Journey Stages.
  • Reports: templates, schedules, branded PDF, frozen snapshots.
  • Roles and entity scoping; PII controls; audit log.
  • Support, survey and marketplace connectors; reply posting where the source allows.
  • Patterns added: silent churn, time-band behaviour, switch intent, occasion-linked dip.
Phase 3Scale and self-serviceMany tenants
  • Connector SDK and a source catalogue; onboarding a source without a platform release.
  • Tenant provisioning console: taxonomy editor, entity import, branding, page-set configuration.
  • Cross-brand rollups for groups; per-brand and group-level views.
  • Multi-language interface; regional residency options.
  • Alerting and digests: threshold breaches, new critical insights, source outages.
Phase 4Business-outcome linkageWhere the data exists
  • Sales, footfall and inventory joins at matched grain — unlocking availability-gap and revenue-correlation patterns.
  • Predictive patterns: entities trending toward threshold breach, seasonal demand signals.
  • Natural-language query over the corpus, answering only with cited evidence.
  • Benchmarking against anonymised category norms, subject to tenant consent.

Companion artefact — nine mid-fidelity wireframes covering login, the store-based page set, and the non-store variant.
Figures shown in the wireframes are illustrative sample values, not measurements from the current corpus. The only real figures cited in this document are from the deployed build: 3,179 Play Store reviews, 3.84 average rating, six mock stores, and competitor mention counts of 14, 10 and 2.