PRODUCT UPDATES
Changelog
Every update, fix, and feature shipped — in order.
Day 75July 8, 2026
Hardening headers ship, 26 dead Content Library URLs fixed, verify-before-fix discipline holds twice in one day
- ▸Shipped four hardening headers site-wide via next.config.ts async headers() — X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, Permissions-Policy locking camera, microphone, and geolocation. Closes the missing-headers LOW finding from Day 74's blackbox security audit.
- ▸Verified the Day 74 audit's MEDIUM CORS wildcard finding against production before writing a fix. Turned out to be Vercel's CDN default on cached prerendered content — no /api/* endpoint has CORS headers set at all. Not a real vulnerability. No fix needed, no code written.
- ▸Discovered during E2E dogfooding: every /resources/[slug] cross-link across the Content Library had been shipping with the wrong URL shape since Day 70. 26 broken links total — 11 inside the 5 CL articles, 10 in Days 69-74 blog posts, 5 in changelog entry copy. All fixed in one mechanical URL-only rewrite commit.
- ▸E2E-verified all 5 CL article URLs return 200 with the new hardening headers present, blog HTML renders with corrected flat hrefs, and changelog live HTML has zero remaining broken URL shapes.
- ▸Site-wide guard grep confirmed zero remaining /resources/<category>/<slug> shapes across content/ and src/ before committing the link-fix batch.
Day 74July 7, 2026
Content Library complete — reputation math ships, filter chips added, BrightLocal 4.5-star cliff nearly doubles
- ▸Shipped fifth and final Content Library article at /resources/reputation-math — 2,020 words on how a one-star rating change translates into revenue for a $500k/year local business. Anchored on Michael Luca's HBS Working Paper 12-016 (5-9% revenue per one-star) with primary-source verification of every stat before writing.
- ▸Content Library sprint complete after 5 consecutive days. Five real articles now live across three categories: The Basics, Damage Control, The Numbers.
- ▸Added client-side category filter chips to /resources hub — All + 3 category buttons above the More Articles grid. Gold-pill visual language matching the existing category badges.
- ▸BrightLocal 2026 finding surfaced in today's blog: the share of US consumers who only use businesses rated 4.5+ stars jumped from 17% in 2025 to 31% in 2026. Nearly doubled in a single year.
- ▸Verify-or-drop upstream discipline held for the fourth consecutive day. Every stat in today's article and blog fetched from primary source in-chat before any code was written.
Day 73July 6, 2026
Content Library Day 5 — policies that work, plus /how-it-works TOC unification
- ▸Fourth real Content Library article shipped at /resources/policies-that-work — 1,962 words covering Google's six prohibited content categories, the Gemini enforcement stack (Google removed 292 million policy-violating reviews in 2025), the appeal path mechanics, and the FTC layer above Google.
- ▸Verify-or-drop upstream discipline held for the third consecutive day. Every stat, quote, and dollar figure cross-checked against primary sources — Google's Prohibited & Restricted Content policy page, Google's 2025 Trust & Safety Report at blog.google, Google Business Profile appeals documentation, and 16 CFR Part 465 at federalregister.gov — before the CC prompt was written.
- ▸FTC civil penalty confirmed at $53,088 per violation through January 14, 2027. The 2026 annual inflation adjustment was cancelled because the October 2025 government shutdown prevented BLS from publishing the CPI-U data required for the adjustment calculation (OMB Memo M-26-11, April 17, 2026).
- ▸Site-wide TOC unification: /how-it-works moved from left-static TOC to right-sticky active-state TOC matching the Content Library pattern shipped Day 72. Clears the specific inconsistency Day 72 logged for Day 75 polish — paid same-week because I was already in the surface. New src/components/how-it-works/HowItWorksTOC.tsx client component.
- ▸Blog covers the finding from research that reshaped how I think about the enforcement layer: Google says review appeals take up to 5 business days, but practitioners report real-world backlogs of 4 to 6+ weeks. Plan around the reality, not the SLA.
Day 72July 5, 2026
Third Content Library article + sticky Article TOC sidebar
- ▸Third real article shipped at /resources/asking-every-customer — 1,985 words on how to ask every customer for a Google review without training your team to memorize a script.
- ▸Three primary-source-verified anchors: BrightLocal Local Consumer Review Survey 2026 (78% asked, 83% of those wrote a review), Google Business Profile Prohibited and Restricted Content policy, and the FTC Consumer Review Rule (16 CFR Part 465).
- ▸Google's April 2026 policy update explicitly banned in-store review tablets and staff-name mentions — the article's Section 2 carries a hard call-out on the kiosk ban with the "unplug it today" line.
- ▸Sticky Article TOC sidebar shipped on every /resources/[slug] route — H2 anchors with active-state highlight, hidden below lg, retroactively lifting Day 70 and Day 71 articles alongside today's ship.
- ▸Content Library sprint is now 3 of 5 real articles live; Days 73-74 remaining (policies-that-work, reputation-math).
Day 71July 4, 2026
Second Content Library article + reading-time signal on cards
- ▸Second real article shipped at /resources/how-many-reviews-needed — 1,936 words on why the honest answer to "how many reviews do I need" is not a number. Data-backed framework: match your local competitors, hold recency, defend the star floor.
- ▸Three verified stats from primary sources: BrightLocal Local Consumer Review Survey 2026 (n=1,002 US adults) — 47% of consumers won't use a business with fewer than 20 reviews, 74% only care about reviews from the last three months, 68% require four or more stars (up from 55% in 2025). Whitespark 2026 Local Search Ranking Factors — review signals ≈ 20% of Local Pack weight, velocity + recency outweighing raw count.
- ▸Frontmatter aligned mid-session — original title and excerpt promised per-industry breakdowns (salons/restaurants/dental) that the article delivers as a competitor-relative framework instead. Title rewritten to "The framework that beats a flat number," excerpt aligned to the actual thesis.
- ▸Reading-time pill added to all three card variants — RelatedReading, FeaturedResources, and the "More articles" block on ResourcesIndex. Small dot-separator pattern next to the category pill, matching the existing pattern in the article header meta row. article.readingTimeText was already on ResourceArticleMeta so no data plumbing changed — visual-only.
- ▸Card element density principle extended from Day 70 — density inside a card matches the density of the card itself. Beefy homepage cards get the reading-time pill just like the lighter trailer cards do, because it's a discreet secondary signal not a competing element.
Day 70July 3, 2026
First real Content Library article + RelatedReading trailer
- ▸First real article shipped at /resources/fake-review-cost — 1,964 words on what a fake review actually costs a small business and three ways to fight it: flag through Google, reply publicly, escalate legally
- ▸Three external stats sourced from primary documents: Luca 2011 HBS working paper (5-9% revenue-per-star lift), Womply 28% revenue premium for the 4.0-4.5 star bracket (200,000-business study), FTC $51,744-per-violation penalty from the 2024 Rule on the Use of Consumer Reviews and Testimonials
- ▸Way 3 (legal escalation) framed correctly on review — the FTC penalty is leverage against fake-review farms and paid-fake operations, not a personal cause of action an individual sues under; Section 230 handled correctly too
- ▸RelatedReading trailer added to every /resources/[slug] page — up to 3 same-category articles, tops up from featured if the category is sparse, silent if there is nothing to surface
- ▸RelatedReading card weight kept intentionally lighter than the homepage FeaturedResources block — density matches the article context; beefy competes with a 1,964-word read, lighter invites
Day 69July 2, 2026
/resources hub + Content Library architecture
- ▸New /resources page mirrors /help structure — hero, category grid, MDX article routes, sitemap coverage
- ▸3 categories: the-basics, damage-control, the-numbers (mirroring HELP_CATEGORIES Record pattern)
- ▸5 MDX article stubs live at /resources/<category>/<slug> — Days 70-74 replace stubs with real content
- ▸FeaturedResources component ships on both /resources (position 3) and homepage (between FAQ and Pricing) — one source of truth
- ▸Homepage tagline: 'Four things about Google reviews we learned the expensive way. If you have a Google profile, one of them pays for itself.'
- ▸Resources link added to SiteHeader primary nav (desktop + mobile) after ROI, and to SiteFooter Column 6 Company below Help center
- ▸Category route at /resources/category/[slug] to avoid Next.js sibling dynamic-segment collision with the flat /resources/[slug] article route
- ▸Post-ship polish: heading split into reverse-pyramid heading+subheading pattern mirroring Pricing typography, and FeaturedResources card className mirrored to Pricing tier card weight so the two sections feel unified
Day 68July 1, 2026
Cookie consent banner + preferences modal + Privacy Policy Section 6 rewrite
- ▸Cookie consent banner — bottom-fixed dark card with brand palette. Three actions (Accept all / Reject all / Manage preferences) shown on first visit; disappears once a choice is stored in localStorage. Fails open on quota or private-mode errors.
- ▸Cookie preferences modal — Essential category always-on (Supabase auth session, Cloudflare Turnstile abuse-check, Stripe checkout). Analytics category toggleable. Body scroll lock, Escape close, backdrop click close.
- ▸Consent state stored in localStorage under cookie_consent_v1 with versioned schema. Adding new categories or processors bumps the schema version and re-prompts — a prior "accept all" does not carry over to categories that did not exist when it was given.
- ▸useConsent() hook + hasConsented(category) check exposed for future gating consumers. No analytics scripts are mounted today; the Analytics toggle stores your choice so it is honoured the moment we add them (Vercel Analytics is the likely first consumer).
- ▸Privacy Policy Section 6 rewritten. Previous copy said "we do not use analytics" (accurate but not future-proof). New copy describes both categories, the localStorage key, and commits to the schema-bump-and-re-prompt behaviour for future changes. Last-updated date bumped to June 30, 2026.
- ▸SiteFooter Policies column gains "Cookie Preferences" entry that reopens the modal from any page. Footer stays server-rendered — a client-only CookiePreferencesButton component was extracted so the parent footer does not need to hydrate.
- ▸ConsentProvider hydrates from localStorage after mount to avoid SSR mismatch; wraps the app in root layout above ServiceWorkerRegister and PWAInstallPrompt.
- ▸Cleanup: deleted the mail-tester test row from signup_attempts (Day 67 DKIM deliverability test residue). Handoff doc referenced a created_at column; actual column is attempted_at — Hard No #71 in action.
Day 67June 30, 2026
Errors tab rendering fixes + footer dedup + DKIM 9.7/10
- ▸Errors tab fix — count display added singular/plural handling (was always showing "events" plural), and lastSeen relative time gained null-fallback to current ISO (was showing "20634d ago" when Sentry returned null lastSeen for freshly-ingested events).
- ▸Test trigger route removed — /api/admin/trigger-test-error was shipped Day 66 to verify the Errors tab data-rendering path against a real Sentry event. Verified clean, test event resolved in Sentry via MCP, route removed.
- ▸SiteFooter cleanup — Privacy Policy and Terms of Service were rendering twice (Legal column + bottom strip). Removed the bottom strip duplicates. Legal column intact.
- ▸Deliverability check — Resend dashboard SPF/DKIM/DMARC all verified green. mail-tester.com score on signup verification email: 9.7/10. SpamAssassin -0.3 on minor content flag, deferred.
Day 66June 29, 2026
Admin Errors tab (Sentry) + /lifetime price fix
- ▸Admin Errors tab (12th tab) — Sentry issues with time/level filters, manual refresh, lazy-load on first visit.
- ▸New API route /api/admin/sentry-issues — admin-gated, maps Sentry response to minimal envelope, empty-data-fails-loud banners (green on 0 issues, red on Sentry unreachable).
- ▸Hotfix: Sentry time filter switched from statsPeriod (whitelist-limited to 24h/14d/empty) to age:-{period} query syntax — all 5 dropdown values (1h/24h/7d/14d/30d) work now.
- ▸/lifetime page — $79 price now visible in pre-launch mode via standalone PRICE_BLOCK constant (was hidden inside buy-button label, which is hidden when PRE_LAUNCH=true). Waitlist CTAs gain price anchor: "Join the waitlist · Lock in $79".
- ▸Two informal patterns logged: verify third-party API constraints from docs before spec, and Sentry Internal Integrations have separate Client Secret + Token fields (Token is what API calls need).
Day 65June 29, 2026
Health dashboard fix — /api/health restored + stale-data banner
- ▸/api/health rewritten to check all 4 services with real network calls: Supabase (live query on businesses), Stripe (balance.retrieve), Resend (domains.list), Anthropic (models.list). Previous version dropped Resend entirely and checked Anthropic via env-var presence only.
- ▸Response time measurement restored per service (wall-clock ms from before the call to after).
- ▸Error message capture restored — if a check throws, the error message is stored in the log row alongside status and response_time_ms.
- ▸service_health_log write restored — all 4 result rows inserted after every /api/health call via service-role client. The Day 60 rewrite silently dropped this insert; the table had not received a new row since June 24 (4 days). Root cause: side effect was not carried through during the rewrite.
- ▸Log write is fire-and-forget (try/catch) — if the insert fails, the route still returns real service status to UptimeRobot. Health check must not fail because its own logging is broken.
- ▸Admin Health tab gets a stale-data warning banner. Red banner if service_health_log has no rows at all. Amber banner if the most recent row is older than 15 minutes (3 missed pings). No banner if data is fresh. Banner mounts at the top of the Health tab above the service cards.
Day 64June 28, 2026
Per-IP rate limiting + IP Abuse admin tab
- ▸IP rate limit on /api/draft-reply — 150 requests per hour and 800 per day from any single IP, across all accounts behind it. Defense layer above per-business tier caps to catch cross-account abuse from one human running multiple accounts.
- ▸New ip_usage table tracks per-IP counters (RLS-locked to service role, mirrors api_usage shape). Increment runs for all tiers, not just paid.
- ▸New ip_rate_limit_events table logs a row every time the limit fires, with business_id attribution. Indexed on ip_address and attempted_at for fast admin queries.
- ▸IP Abuse admin tab (11th tab) grouped by IP, last 7 days. Per-business rows show traffic-light risk dot (red/yellow/green), tier badge, account age, AI replies in last 30 days, and a "from this IP" tag when the business signed up from the same IP.
- ▸Risk scoring locked server-side as single source of truth — red = Noob + (signed up from this IP OR account younger than 7 days); green = paid tier + account 30+ days + NOT signed up from this IP; yellow = everything else.
- ▸IP group level shows total signups from that IP in last 24h, cross-referencing the Day 63 signup abuse detection. Same IP appearing in both tabs = strongest correlated signal.
- ▸Block button calls existing /api/admin/block-business with new 8th canned reason template "Rate limit abuse" — reuses Day 54 suspension, appeals, and email flow. No parallel block infrastructure.
- ▸Hotfix mid-session — Rate limit abuse template was 535 chars; block-business route validates 5-500. Caught during dogfood, zero DB writes occurred during the failed attempt. Trimmed to 385 chars. Full block flow then verified end-to-end on a test business.
Day 63June 27, 2026
Signup abuse: soft-warning modal + admin review tab
- ▸/api/signup-precheck now counts completed signups from the same IP in the last 24h and returns flagged:true when the count hits 5 or more. Threshold is a soft warning, not a block.
- ▸/api/signup-precheck/dismiss accepts flag_dismissed (user clicked Continue anyway) or flag_shown_abandoned (user clicked Cancel) and updates the signup_attempts row.
- ▸SignupWarningModal — shown before supabase.auth.signUp fires when the precheck returns flagged. Same dark UI as the signup form. Two buttons: Continue anyway, Cancel.
- ▸Signup logic refactored into runSignup() helper called from both the normal path and the modal Continue path. Behaviour for non-flagged signups is byte-for-byte identical to Day 62.
- ▸Admin gets a 10th tab — Suspicious Signups. Groups signup_attempts by IP, last 24h, shows email list, abandoned vs continued counts, first/last seen, Mark Reviewed button.
- ▸signup_attempts table gained reviewed (bool, default false), reviewed_at (timestamptz), reviewed_by (uuid → auth.users). Mark Reviewed updates all flagged rows from that IP at once.
- ▸Carryover from Day 62: /api/signup-precheck silent backend logging. Every signup attempt writes IP, email, user_agent to signup_attempts. ADMIN_SIGNUP_BYPASS_IPS env var skips logging for admin IPs. Both calls wrapped in try/catch and fire-and-forget — never block real signup.
- ▸Carryover from Day 62: duplicate-email signup now shows "This email is already registered" with "Sign in instead" link. Detects Supabase anti-enumeration empty-identities signal. signup_attempts row stays as pending (accurate data) instead of misleading completed.
Day 62June 26, 2026
Graceful degradation UI
- ▸ServiceOutageBanner component — amber, inline, dismissible. Shared across Reviews and Pricing.
- ▸/api/draft-reply now returns structured JSON on failure. RateLimitError → 429 ai_rate_limited. All other errors → 503 ai_unavailable.
- ▸/api/create-checkout-session and /api/create-portal-session catch Stripe failures and return 503 payment_unavailable with consistent shape.
- ▸ReviewsClient and PricingClient surface outage messages inline below the relevant button instead of failing silently.
- ▸Manage Billing button now resets cleanly on API failure instead of locking on 'Redirecting...'.
- ▸Manage Billing distinguishes 'no billing account' (400, admin-upgraded accounts) from 'payment unavailable' (503, Stripe outage). Honest message in each case.
Day 61June 25, 2026
Auto-rollback with shallow + deep health checks
- ▸GitHub Actions cron polls /api/ping every 5 minutes
- ▸3-layer confirmation: T+0, T+5min, T+6min — only rolls back if all 3 fail
- ▸Calls Vercel API to roll back to previous READY deployment
- ▸Opens a GitHub issue on every rollback with per-check status codes
- ▸/api/ping — shallow health check, drives rollback (Next.js alive only)
- ▸/api/health — deep health check, drives alerts only (Supabase + Stripe + Anthropic)
- ▸Two-tier separation eliminates rollback misfires from 3rd-party outages
Day 60June 24, 2026
Error monitoring + health check
- ▸Sentry installed across client, server, and edge runtimes
- ▸10% trace sampling, PII disabled, DSN in env vars
- ▸Branded global-error.tsx — navy background, gold CTA, "Something went wrong" message
- ▸/api/health endpoint checks Supabase, Stripe, and Anthropic
- ▸Returns 200 ok / 503 degraded with per-service breakdown
- ▸UptimeRobot now monitors /api/health every 5 minutes
Day 59June 23, 2026
Loading skeletons on dashboard, reviews, and settings
- ▸Dashboard, reviews, and settings now show a skeleton while data loads — matches each page's layout to prevent content shift on hydration.
- ▸One shared pattern across all three: bg-white/[0.06] placeholder blocks with animate-pulse, no JavaScript, no external library.
- ▸Settings skeleton is tier-agnostic (generic card placeholders) to avoid flashing the wrong tier's UI before the real page loads.
Day 58June 22, 2026
Globe toggle + lifetime E2E verified
- ▸Billing toggle on /pricing and Settings replaced with animated 3D globe — rolls between Monthly and Annual with gold dust particles and Save 20% badge on annual
- ▸GlobeToggle is a shared React component used in all 3 billing toggle locations (pricing page, Settings upgrade section, lifetime → Chad upgrade modal)
- ▸Full E2E test of lifetime deal flow completed — Stripe $79 checkout, webhook tier flip to lifetime, Settings plan card, and lifetime → Chad upgrade all verified clean in production
- ▸GBP profile activated ahead of July 9 API eligibility window — photos, opening date, and business post added
Day 57June 22, 2026
Lifetime Deal launched
- ▸New lifetime tier added — $79 one-time payment, 50 AI replies/month, 1 location, Google only
- ▸Stripe one-time checkout + webhook handler — tier set to lifetime on payment confirmation
- ▸/lifetime landing page live — hero, what's included, why capped, what's not included, FAQ
- ▸Lifetime Deal added to site nav (desktop + mobile)
- ▸Settings page: lifetime buyers see dedicated plan card with upgrade-to-Chad modal
- ▸50 AI replies/month cap enforced server-side in draft-reply route
Day 56June 21, 2026
SMS features marked Coming Soon
- ▸Review generation via SMS is now clearly marked Coming soon across pricing and feature pages.
- ▸SMS replies launch with our Twilio rollout after beta — QR-based review generation remains available on GigaChad.
Day 55June 20, 2026
Customer-facing block flow, appeal system, and Refund Policy page
- ▸Customer-facing block flow: /blocked landing page shows the suspension reason and appeal CTA; /appeal/[token] is a public form for submitting an appeal within a 30-day window
- ▸Four transactional emails sent from notices@ominvo.com: suspension notice (includes appeal link), appeal received confirmation, appeal approved, appeal denied
- ▸Admin Appeals tab: pending appeals queue with message view and a decide modal — approve restores access as a goodwill gesture, deny is final
- ▸Block records are permanent for the account file — when an appeal is approved, the suspension is marked as lifted rather than deleted, preserving the full history
- ▸Approved appeals reinstate the account's prior subscription tier as goodwill (original payment is not refunded; resubscribe via Settings to keep billing aligned)
- ▸/legal/refund-policy: new standalone plain-English page covering monthly plans, annual plans, free tier, terminations for cause, and chargebacks — linked from the /legal hub and from Terms Section 5
Day 54June 19, 2026
Customer block system + TOS Section 14 amendment
- ▸New traffic-light risk_level on every business (green/yellow/red) — manual override via admin Customers tab
- ▸New Blocked tab in admin — pick a business + 5-500 char reason + 7 canned reason templates (each ~70-90 words referencing relevant TOS section)
- ▸Block flow cancels Stripe subscription immediately, no proration, no refund — sets risk_level red, subscription_status canceled, logs tos_violation signal
- ▸Unblock flow available directly from Blocked tab — risk back to green, status to inactive, full audit trail preserved in risk_signals
- ▸Stripe charge.dispute.created webhook auto-flags chargebacks to red and logs the dispute row + high-severity risk signal
- ▸Flagged testimonials write to risk_signals as spam_testimonial signals — auto-accruing audit trail for repeat offenders
- ▸TOS Section 14 rewritten — termination-for-cause covering chargebacks, spam, abuse, billing evasion, and Acceptable Use violations; no refunds; 30-day appeal window
- ▸New Supabase tables: risk_signals, disputes, blocked_businesses, appeals — all RLS-locked, service-role-only writes, security-definer policy for self-read
- ▸Block notice email, /blocked page, /appeal/[token] flow ship Day 55 as one connected unit
Day 53June 20, 2026
Competitor Tracking feature page + FAQ doubled
- ▸New feature page at /features/competitor-tracking — explains rating and review-velocity benchmarking against up to 5 named local competitors, GigaChad only, live once the Google Business Profile API connection goes live (~July 23)
- ▸Header dropdown, mobile menu, and footer Product column now link to the real /features/competitor-tracking page (previously placeholder #)
- ▸Removed stale "SOON" badges from Review Generation and Competitor Tracking in the header and footer — Review Generation has been live since Day 51
- ▸FAQ page expanded from 27 to 62 questions — 5 new Q&As added to each of the 7 categories (pricing, Google Business Profile, AI replies, tiers, launch, security, support)
Day 52June 19, 2026
Support funnel + Review Reply Playbook
- ▸Footer Support link now leads to a real funnel: FAQ → Help Center → a support form, instead of duplicating the Contact mailto link
- ▸New support form at /support — name, email, business name, description, optional screenshot/video link — sends directly to support@ominvo.com via Resend
- ▸New page: /review-reply-playbook — a working guide to writing review replies that protect or grow revenue, with bad/better reply examples for all seven industries Ominvo serves
- ▸Footer "Review Reply Playbook" link fixed — previously a placeholder #, now points to the real page
Day 51June 18, 2026
PWA install prompt + Review Generation feature page
- ▸Install Ominvo to your home screen — a custom install card on Android and desktop Chrome/Edge, with platform-aware fallback instructions for iPhone Safari
- ▸Service worker now checks for updates on tab focus, so deployed fixes reach existing users faster
- ▸New feature page at /features/review-generation — explains where each tier enters the review loop and why review generation is the GigaChad differentiator
- ▸Header dropdown and footer Product column now link to the real /features/review-generation page (previously placeholder #)
- ▸Mobile menu under Product gains a Review Generation entry to match desktop
Day 50June 17, 2026
PWA service worker + customer testimonials + auth redirect fixes
- ▸Service worker live — pages load offline with a dedicated fallback page, network-first when online
- ▸Launched /testimonials — a public page for customer feedback, with a moderated submission flow gated to logged-in users
- ▸Admin: new Testimonials moderation tab for handling flagged testimonial submissions
- ▸Improved auth redirect handling for new signups arriving from feature pages
- ▸obscenity package added for content moderation (replaces hardcoded profanity list)
Day 49June 16, 2026
PWA manifest upgrade + ReviewTrackers comparison page
- ▸PWA manifest upgraded — full icon set (16/32/48/180/192/512), start_url, scope, theme_color metadata
- ▸/vs/reviewtrackers competitor comparison page live — pricing table, feature sections, sitemap updated; ReviewTrackers tab unlocked on homepage CompetitorTabs (final tab)
Day 48June 16, 2026
Notifications gate + digest infrastructure + Early Signals carousel
- ▸Notifications gate: Noob tier sees locked blur overlay on Notifications card — email alerts are Chad & above
- ▸Digest email infrastructure: cron route + email template + preference API built, UI is Coming Soon pending Vercel Pro
- ▸Early Signals carousel: homepage fake testimonials replaced with auto-scrolling carousel of 10 honest beta feedback cards
Day 47June 15, 2026
Monthly report card emails + Podium comparison
- ▸GigaChad: monthly report card email, delivered on the 1st of each month at 9am ET
- ▸Settings: Monthly Report Card toggle card — opt out anytime (GigaChad only)
- ▸Locked upsell card shown to Noob and Chad tiers
- ▸businesses.email_monthly_report column (boolean, default true)
- ▸Cron route /api/cron/monthly-report — Bearer auth, tier filter, toggle filter, Resend send
- ▸Email template: em-dashes for GBP-blocked fields, real AI replies count from ai_responses
- ▸/vs/podium competitor comparison page — pricing table, feature sections, sitemap
- ▸Homepage CompetitorTabs: Podium tab unlocked
- ▸Competitor page: /vs/grade.us — Grade.us comparison page + Grade.us tab unlocked on homepage
Day 46June 15, 2026
Public review landing page (/r/[businessId])
- ▸Shipped /r/[businessId] — mobile-first public page, no site chrome, single CTA
- ▸Three states: 404 (invalid ID), fallback (no Place ID), full CTA (live businesses)
- ▸Added slug column to businesses table with format constraint + unique index
- ▸Added get_public_business_info() SECURITY DEFINER RPC — safe anon reads
- ▸Added src/lib/google-review-url.ts utility
- ▸robots noindex/nofollow on all /r/ pages — not for search engines
Day 45June 14, 2026
QR codes + competitor tabs + /vs/birdeye + pricing audit
- ▸GigaChad: QR code generator in Settings — download PNG, links to /r/[businessId]
- ▸/api/qr route — tier-gated to GigaChad, returns PNG, auth-guarded
- ▸src/lib/qr.ts — server-side QR generation utility (qrcode npm package)
- ▸Homepage: CompetitorTabs section — 4 tabs, Birdeye live, others coming-soon locked
- ▸/vs/birdeye competitor comparison page — pricing table, feature sections, SEO-targeted
- ▸sitemap.ts updated with /vs/birdeye
- ▸Homepage ROI card linked to /roi-calculator
- ▸Replan: all unshipped /pricing features will be built as mock-ready UI pre-GBP, live data swap on July 23
Day 44June 13, 2026
/faq page + full team invite flow + member permission lockdown
- ▸/faq page live with 27 questions across 7 categories — pricing, Google connection, AI replies, tiers, launch, security, support
- ▸JSON-LD FAQPage schema on /faq for Google rich snippets
- ▸/how-it-works and /faq added to sitemap.ts
- ▸Three broken #faq nav links fixed across header (desktop + mobile) and footer
- ▸team_invites Postgres table created with RLS — owner-only read/write, service role for accept-by-token
- ▸Four team API routes shipped — invite, accept, revoke, remove-member — all gated by requireBusinessOwner helper (owner + gigachad)
- ▸Resend invite email template with 1-day token expiry
- ▸/accept-invite page with separate unauthenticated and authenticated states
- ▸SettingsClient Team card rebuilt from stub into real member list with pending invites, revoke buttons, and invite form
- ▸Members RLS policy added on public.users so teammates can read each other's emails (scoped to same business)
- ▸redirectTo threaded through signup flow — signup page, SignupForm, emailRedirectTo, check-email — for invitee email verification redirect
- ▸Dashboard guard now bypasses /onboarding for users with a pending team invite
- ▸handle_new_user Postgres trigger updated to skip business creation when the new user has a pending team invite
- ▸Member permission lockdown — /api/create-checkout-session and /api/create-portal-session now require owner role
- ▸Settings UI hides owner-only controls from members — business info inputs become read-only, billing buttons hidden, team invite/revoke/remove hidden
- ▸Gold banner at top of /settings for members explaining the permission level
Day 43June 12, 2026
/how-it-works full build + framework security bump
- ▸Bumped framework version — closes batch of high/moderate security advisories, full E2E verified
- ▸Built /how-it-works — sticky sidebar, tier mockups (Noob/Chad/GigaChad), SVG trend chart, comparison table
- ▸Header + footer How it works links wired from #how-it-works to /how-it-works
- ▸Footer duplicate Help Center link (Resources column) removed
- ▸nav audit: catalogued all broken # links — FAQ deferred to /faq page, GBP-dependent links stay SOON
Day 42June 12, 2026
Alert Preferences + pre-launch SEO infrastructure
- ▸Shipped /features/alert-preferences — last # placeholder in Product dropdown closed
- ▸Added 6 missing routes to sitemap.xml (5 feature pages + /why-us)
- ▸robots.ts now includes explicit Allow rules for 9 AI crawlers, mirroring existing restrictions
- ▸Verified ominvo.com in Google Search Console — sitemap submitted, 60 URLs discovered
- ▸npm audit: closed 2 of 4 advisories (transitive deps)
Day 41June 11, 2026
Check-email page, server-validated verification, three polish fixes
- ▸/check-email page between signup and verification — email shown, Turnstile-gated resend, back link
- ▸Server-validated verification detection: getUser + email-match + email_confirmed_at guards
- ▸Cross-tab "Email verified" card with auto-redirect to /dashboard
- ▸Homepage Chad annual pricing fix: $288/year save $72, 20% off (was $240/year save $120, 33%)
- ▸Admin Plan Breakdown row math now uses per-tier MRR sums — annual subs no longer mislabeled
- ▸Settings Card 5 gated to noob-only — chad users no longer see duplicate upgrade CTA
Day 40June 10, 2026
Annual billing E2E passes; DKIM + settings rebuild + smart filters
- ▸Fixed Resend DKIM record in Hostinger DNS — email signup unblocked after 29 days of silent failure
- ▸Rebuilt /settings upgrade flow — Monthly/Annual toggle, tier-aware cards (Noob sees Chad+GigaChad, Chad sees GigaChad), real Stripe Checkout
- ▸Annual billing end-to-end test passed: $288 Chad annual → webhook → tier flip noob→chad → MRR $84 → cancel-at-period-end
- ▸Added /features/smart-filters page with static HTML/CSS star-rating filter mock (GigaChad-only positioning)
- ▸Wired Smart Filters in Product dropdown (desktop + mobile) and footer
Day 39June 13, 2026
MRR math fix, analytics dashboard page, /why-us comparison page
- ▸Admin revenue API: all 4 Stripe price IDs moved from hardcoded strings to env vars — no code changes needed at live-mode cutover
- ▸Annual MRR fix: annual subscription amounts now divided by 12 (was counting full $288/$948 as MRR contribution)
- ▸Revenue amounts read directly from Stripe price object (unit_amount / 100) — self-corrects if pricing changes
- ▸Annual subs correctly increment chadCount/gigachadCount instead of falling through to unknownCount
- ▸/features/analytics-dashboard launched — 10 sections: rating trend, response rate, velocity, star distribution, competitor tracking (GigaChad), tier comparison
- ▸/why-us launched — Ominvo GigaChad vs Birdeye Growth positioning, full HTML/CSS dashboard mock, SVG trend chart, donut chart, feature comparison table, bar chart
- ▸SiteHeader + SiteFooter: Analytics Dashboard + Why Ominvo nav links wired desktop, mobile, footer
- ▸Engineering dead-end logged: MDX chart system (BlogCharts.tsx + 5000-word Birdeye blog post) built and reverted — next-mdx-remote/rsc does not reliably evaluate multiline JSX array props during prerender
Day 39June 12, 2026
MRR calculation fixed + analytics dashboard feature page
- ▸Admin revenue API: all 4 Stripe price IDs moved from hardcoded strings to env vars — no code changes needed at live-mode cutover
- ▸Annual MRR fix: annual subscription amounts now divided by 12 for correct MRR contribution (was counting full $288/$948 annual amount)
- ▸Revenue amounts now read from Stripe price object directly — self-corrects if pricing changes
- ▸Annual subs now correctly increment chadCount/gigachadCount instead of falling through to unknownCount
- ▸/features/analytics-dashboard launched — rating trend, response rate, velocity, star distribution, competitor tracking (GigaChad), tier comparison
- ▸SiteHeader + SiteFooter: Analytics Dashboard nav links wired desktop, mobile, and footer
Day 38June 11, 2026
Annual billing wired end to end + AI Replies page
- ▸Annual billing checkout wired — billingInterval param, 4-way price ID map (chad/gigachad × monthly/annual)
- ▸Webhook bug fixed — annual price IDs added to TIER_MAP, prevents incorrect tier assignment on annual subscriptions
- ▸Pricing page CTA buttons converted to onClick handlers — loading state, Stripe redirect, inline error handling
- ▸/features/ai-replies live — hero, 3-step how it works, 6 feature tiles, tier callout, final CTA
- ▸SiteHeader + SiteFooter: AI Replies and Instant Alerts links wired to real destinations
- ▸Copy audit: "AI" replaces brand name references in homepage, FeatureRows, ProductShowcase
Day 37June 10, 2026
Annual billing toggle live + first feature landing page
- ▸Pricing page: Monthly/Annual toggle added, defaults to Annual
- ▸Chad annual: $24/mo (billed $288/yr, save $72) — GigaChad annual: $79/mo (billed $948/yr, save $240)
- ▸Stripe annual price IDs created in test mode: STRIPE_CHAD_ANNUAL_PRICE_ID + STRIPE_GIGACHAD_ANNUAL_PRICE_ID
- ▸Hero badge updated: "Save 20% with annual billing — now available"
- ▸Pricing page FAQ updated with annual billing Q&A and concrete prices
- ▸/features/instant-alerts launched — first Product dropdown item with a real destination
- ▸SiteHeader: Instant Alerts href wired desktop + mobile
Day 36June 10, 2026
Help Center complete — 6 articles, thumbs feedback wired
- ▸6 Help Center articles added: integrations (connecting GBP, Meta roadmap, webhooks & API) + account-and-security (changing password, data deletion, who can see your data)
- ▸HelpIndexClient.tsx renamed to HelpIndex.tsx (naming fix)
- ▸help_feedback table added to Supabase
- ▸New /api/help/feedback endpoint — unauthenticated, rate-limited, persists thumbs y/n votes
- ▸ArticleFooter thumbs buttons now fire-and-forget POST on click
Day 35June 9, 2026
Help Center content expansion and auth redirect sweep
- ▸6 Help Center articles: billing (upgrading, cancelling, invoices) + reviews-and-replies (how AI works, editing replies, negative reviews)
- ▸Auth redirect sweep: /dashboard, /settings, /reviews now redirect to /login?redirectTo=<path>
- ▸All 4 protected routes fully wired with safe redirect
Day 34June 8, 2026
Help Center foundation, open-redirect fix, and health log table
- ▸/api/admin/health-logs endpoint with filters (time range, service, status, limit) + raw log table in admin Health tab
- ▸safe-redirect.ts validator — closes open-redirect attack vector on login redirectTo param
- ▸/admin redirectTo wired: signed-out users return to /admin after login
- ▸Help Center foundation: /help, /help/[category], /help/[category]/[slug] routes live
- ▸3 seed articles in getting-started category
- ▸Help Center footer link added
Day 33June 7, 2026
Monitoring, social links, and bundled fixes
- ▸UptimeRobot external monitoring live on /api/health
- ▸Footer social icons updated: X, Facebook, Instagram, Pinterest with correct profile URLs
- ▸/api/health auth gate removed (public endpoint, no sensitive data)
Day 32June 6, 2026
Status page, admin Health tab, and social batch
- ▸/status public page live with 4 service cards, uptime bars, auto-refresh
- ▸/admin Health tab (6th tab) with lazy fetch and per-service status cards
- ▸28 Canva social designs scheduled June 8–14
Day 31June 5, 2026
Blog system, health monitoring, and revenue tab
- ▸Blog live at /blog with MDX, 5 categories, RSS feed, syntax highlighting
- ▸/api/health worker pings 4 services, logs to service_health_log
- ▸Revenue tab in /admin shows live MRR from Stripe
Day 30June 4, 2026
Heading gradients and last_active_date fix
- ▸Sitewide white-to-gold heading gradient applied
- ▸Hero H1 sizes reduced across all pages
- ▸last_active_date now stamped on every dashboard load (At-Risk tab fix)
Day 29May 31, 2026
ROI Calculator
- ▸/roi-calculator live with industry-specific lift percentages
- ▸Transparent math, no black box
- ▸Wired into header and footer
Day 28May 30, 2026
All 7 industry pages + email logo
- ▸All 7 industry pages live: Salons, Restaurants, Gyms, Dental, Med Spas, Auto Services, Home Services
- ▸Logo PNG added for transactional email templates