Changelog
O que há de novo no Invest-like
Cada release significativa enviada para produção. Assine via RSS para receber aviso quando publicarmos.
Reels rebuilt as a 4-scene commercial + logo render variants for socials
v69- FeatureBuffett Verdict reel (Remotion) restructured from a flat 12s slideshow into a proper 4-scene commercial. New arc: HOOK ('Would Buffett buy AAPL?' with ticker drop) → REVEAL (logo + score count-up + verdict pill burst) → BREAKDOWN (pillar bars cascade in with bar fills + count-up numbers) → PUNCH (headline word-by-word reveal in serif quote, CTA pill bounce, URL hold). Each scene takes over the full canvas instead of layering onto a static stage - actually feels like a commercial now.
- PolishReel background now subtly pulses with verdict colour (emerald/amber/red) and applies a slow ken-burns zoom across the full 12s for kinetic feel. Brand wordmark holds top-left throughout.
- FeatureNew 'logo' Remotion composition + render script (npx tsx scripts/logo/render.ts) outputs three 600×600 PNG variants of the invest-like icon: black (matches the live site), gold (premium), green (brand primary). Use these for LinkedIn / X / favicon uploads - files land in out/logo/.
- FixuseSceneOpacity helper was double-subtracting the scene start frame, which made scenes 2-4 fully transparent. Caught by rendering preview frames before declaring victory.
Compare hub - animated demo replaces Featured grid, page now centred
v68- PolishCompare hub container now uses 'mx-auto max-w-5xl' so it's centred like every other page in the app. Was full-width with everything justified to the left edge, leaving acres of empty space on the right.
- FeatureRemoved the 'Featured comparisons' card grid - it was twelve link tiles that gave no signal of what /compare actually does. Replaced with a CompareDemo component that animates a real AAPL vs MSFT comparison: dual-stock header, four strategy rows with bars growing from each side toward the centre + score numbers counting up, winning side highlighted in emerald, overall winner summary, and a 'See the full comparison →' CTA. Beginners now SEE the output before clicking through.
- PerformanceDropped two Supabase queries the deleted Featured section needed (slug lookup + 12 comparison_pages joins). Compare hub now does one query (the published-page count for the dek).
Buffett hub stripped back - curated picks + methodology, no more data widgets
v67- PolishRemoved all four data-driven widgets from /buffett (Top Buffett-fit picks, Buffett-fit by sector heatmap, Top-scoring right now, Recently analysed). They surfaced unfamiliar tickers, mismatched scores, and visually competed with each other instead of helping anyone learn what Buffett Brain does.
- FeatureNew 'Try one of these' showcase: six hand-picked stocks Buffett actually owns (KO, AAPL, AXP, BAC, MCO, OXY) with one-line captions explaining why each fits the framework. Curated > random data - every visitor sees recognisable names that lead to reliable verdicts.
- FeatureNew 'How the verdict is built' section explaining the five-pillar methodology: wide moat, honest management, consistent earning power, fair price, circle of competence. Educational by design - visitors understand the framework before they hit Analyze.
- PerformanceBuffett hub now renders with zero database calls (was three Supabase round-trips for the deleted widgets). Page is instant on every load.
Visual redesign - TOP BUFFETT-FIT, TOP-SCORING, Featured comparisons
v66- PolishTOP BUFFETT-FIT cards rebuilt as scorecard tiles. Was a cramped single-line row that mashed logo + ticker + sector + name + verdict + score + grade + chevron together. Now: logo + identity on the left, big colour-toned score on the right, a thin progress bar showing the 0–100 score visually, then verdict pill + chevron on a dedicated bottom row. (Note: this section was removed entirely in v67.)
- PolishTOP-SCORING RIGHT NOW cards rebuilt with hierarchy: logo + ticker + verdict pill on the left, big toned score with '/100' on the right, headline as body prose, 'Open analysis →' CTA pinned to a divider. (Note: removed in v67 along with the rest of the Buffett-hub data widgets.)
- PolishCompare hub Featured comparisons rebuilt as dual-logo head-to-head cards. Both stocks shown with logo + ticker + name on opposite sides of a centred 'vs' chip - the matchup reads visually before you read a word. Caption + 'Compare →' CTA on a divider underneath.
Curated company names + real logos in TOP-SCORING + contextual back link
v65- PolishprettyName now uses a curated NAME_OVERRIDES map for the top 60 tickers - 'BERKSHIRE HATHAWAY Class B' → 'Berkshire Hathaway', 'Mastercard Incorporated' → 'Mastercard', 'Eli Lilly & Co.' → 'Eli Lilly', 'Amazon.Com Inc.' → 'Amazon', 'Alphabet Inc. Class A' → 'Alphabet', 'Coca-Cola Company' → 'Coca-Cola', 'JPMorgan Chase & Co.' → 'JPMorgan Chase'. Plus tightened regex set: Class A/B/C, '& Co.', 'Incorporated', 'ETF Trust'.
- PolishTOP-SCORING RIGHT NOW panel was showing initials ('BR', 'MS', 'V', 'SP', 'AA') instead of company logos. Swapped from StockLogo to BrandLogo so it now uses Clearbit logos like the other panels.
- FeatureStock-detail back link is now context-aware. Filter result cards link with '?back=Buffett+Classic+filter', so the stock page shows '← Back to Buffett Classic filter' instead of generic 'Back to all stocks'. Each preset gets its own label - Graham filter, Greenblatt Magic Formula filter, etc.
Real names + recognisable picks + curated compare pairs
v64- PolishStripped catalogue boilerplate from every stock display: 'Tesla, Inc. Common Stock' → 'Tesla', 'VICI Properties Inc. Common Stock' → 'VICI Properties', 'KLA Corporation Common Stock' → 'KLA'. Applied across compare detail, compare hub, filter results, and the Buffett top-picks panel.
- FixBuffett hub's 'Top Buffett-fit stocks right now' panel was surfacing random small-caps (JCAP, TRINI, BBDC, ATLCL - bond instruments scoring well on raw quant). Now: pull a wider 60-row window, filter out non-equity instruments (notes, preferred shares, warrants), require ≥$3B market cap. Result: recognisable names like Mastercard, Visa, Brookfield instead of micro-cap noise.
- FixWrong sectors in DB: Mastercard was tagged 'Industrials', KLAC was 'Healthcare', VICI was 'Financials'. Manually corrected for top 10 mislabelled tickers.
- PolishCompare hub 'Featured comparisons' replaced the random 'recently built' list (NE vs RIG, GTLS vs SMR, IR vs ITW) with 12 curated head-to-heads: AAPL vs MSFT, NVDA vs AMD, BRK.B vs SPY, V vs MA, KO vs PEP, GOOGL vs META, COST vs WMT, JPM vs BAC, TSLA vs F, LLY vs PFE, AMZN vs WMT, XOM vs CVX. Each pair has a one-line caption.
- PolishCompare hub cards now lift on hover (matches the rest of the app's interaction language) and use BrandLogo (Clearbit) wherever feasible.
Remotion video pipeline + back-nav fix + compare button overflow
v63- FeatureRemotion video pipeline shipped. Mirrors the /reel/ HTML design as a true MP4 composition that renders to 1080×1920 at 30fps. Local CLI: `npm run reel:render -- --ticker AAPL` produces out/reels/aapl.mp4 in ~25s. Batch: `npm run reel:render:all` does the top 10 stocks. Studio: `npm run reel:studio` for live editing. Full docs in docs/REELS.md.
- FixBack link on stock profile (/stocks/[ticker]) was hardcoded to /stocks. If you got there via the screener, pressing Back lost your filter results. New SmartBackLink uses router.back() for normal click and falls back to /stocks for cmd-click/middle-click. Label adapts to context ('Back to filter results', 'Back to scenarios', etc.).
- FixCompare hub: 'Compare' submit button was getting clipped on wide viewports because flex-1 inputs over-expanded. Added max-w-3xl to the form section, min-w-0 on inputs (so they shrink instead of pushing the button), and shrink-0 on the button itself.
Brand consistency · percentages · strategy fit aligned with filters
v62- PolishReverted serif H1s back to plain bold Inter sans across every redesigned page (Buffett hub + verdict, Compare hub + pair, Filters hub). Matches the 'What If…?' headline style - one font system across the whole app.
- PolishCompare form: removed the duplicate 'Compare any two stocks' heading inside the input card. Page already says it once at the top - saying it twice read as unprofessional.
- PolishFilter result cards now show '93%' instead of '13/14'. The 'X of Y rules passed' detail is in the dot-row caption below + tooltip on the badge.
- FixStrategy Fit Scores on /stocks/[ticker] now use the SAME math as the screener: pass count against the canonical preset for each strategy. So a stock that passes 13/14 Buffett-Classic rules shows 93% on both surfaces, not 13/14 here and 56/100 there. Built a new computePresetFits server function that loads the system presets once per 5-min window and counts pass rate. Closes the 'why does the score not match?' bug for good.
/filters hub redesigned + result link goes to stock profile (not Buffett Brain) + score confusion fixed
v61- Polish/filters hub redesigned end-to-end. Serif H1 + kicker matching the rest of the app, icon-led preset cards (Brain for Buffett, Shield for Defensive, Coins for Dividends, Target for ROIC, etc.), strategy-colour accents (emerald/blue/amber/violet), and a 35ms staggered fade-in for each card. No longer reads like a SaaS dashboard.
- FixFilter result cards were linking to /buffett/[ticker] (the AI verdict page) - should be /stocks/[ticker] (the stock profile). Fixed: clicking a result now opens the stock profile, not a fresh Buffett analysis.
- FixRemoved the secondary 'fit score' (e.g. 56) from the filter result cards. It contradicted the pass count: '13/14 rules passed' (93%) next to 'Fit 56' looked like a bug. They measure different things - pass count is THIS preset, fit score is the broader Buffett framework. Kept just the pass count on filter results so what the user reads matches what's being measured.
Filters that actually return results - score-and-rank, card UI, collapsible criteria
v60- FixFilter engine rewritten from strict-AND to score-and-rank. Old behaviour: 14 strict criteria → 0 stocks pass → empty results. New behaviour: pull top-800 stocks by market cap, count how many criteria each passes, rank by pass count then fit score. Every preset now returns ranked results, with a clear 'X/N rules passed' badge per stock.
- FeatureNew card-style filter results - logo + ticker + sector + name + pass-count badge + 16-dot visual progress bar + market cap. Replaces the dense stocks table for the filter detail view (table is still on /stocks where it belongs).
- PolishCriteria list is now collapsed by default - header shows '14 rules across 6 categories' with View/Hide toggle. Expanding cascades each section + row in with a 60ms staggered fade. No wall of text on first load.
Filter presets get real depth - 11-16 criteria each, plain English
v59- FeatureAll 12 curated screener presets rebuilt with 11-16 criteria each (was 5-7), properly mirroring the documented frameworks: Buffett's full owner-mindset checklist, Graham's Defensive Investor 7 rules + modern adaptations, Greenblatt's Magic Formula with quality guards, Dividend Aristocrats with payout safety, High Compounders Terry-Smith style, etc. Each preset description now names the exact source.
- PolishEvery criterion has a plain-English label - 'Earns at least 15¢ per dollar invested' instead of 'ROIC > 15%'. Beginners see what each rule means without needing to know the metric.
- PolishReplaced the flat criteria-chip row on the filter detail page with a grouped list. Sections labelled in beginner English ('What you pay', 'How good the business is', 'How much debt it carries'). Scales to 16+ criteria without reading like a wall of pills.
Reel mobile fix + cron auth fix + CTA pill
v58- Fix/reel/ layout broke on phones - verdict pill was absolutely-positioned and bled into the pillar bars on shorter viewports. Rewrote with a flex-column layout (no absolute positioning) so spacing auto-distributes across any 9:16 height.
- FeatureAdded a real CTA pill at the bottom of every Reel: 'See the full reasoning →' with the canonical URL underneath. Animates in at the 10s mark - fits the social-media attention curve.
- FixAll Vercel cron jobs were silently 401'ing. Root cause: agent endpoint required Authorization header, but Vercel only injects one when CRON_SECRET env var is set - and we were reading from AGENT_CRON_SECRET. Set CRON_SECRET in Vercel + updated the auth check to accept either secret. Crons should fire on schedule again starting tonight.
Vertical /reel/ pages for TikTok+Reels, plus Clearbit logos
v57- FeatureNew /reel/buffett/[ticker]/ route - a 9:16 vertical animated verdict reveal designed to be screen-recorded on a phone (or in any browser) for direct posting to TikTok, Instagram Reels, YouTube Shorts. 12-second timeline (logo → ticker → name → score count-up → verdict pill → pillars → headline + CTA). No video editor required - open URL, screen-record, post; music added natively in TikTok/Reels at upload time.
- FeatureBrandLogo component with Clearbit > FMP > initials fallback chain. Curated 200-ticker domain map for sharp transparent PNGs at any size. Wired into /buffett/[ticker]/ masthead - verdicts now show the company logo prominently.
- PolishBoth /reel/ and /buffett/ surfaces use the same logo, so a viewer who lands on the verdict page after seeing the Reel sees the same brand mark.
UI cleanup - investing-tool restraint, not magazine cover
v56- Polishv55's wax seal, Buffett silhouette watermarks, crossed-swords compare crest and 'Two tickers enter, one leaves with the trophy' headline all got cut. Too gimmicky for a serious investing tool. Replaced with the standard Stamp pill + clean tabular score on /buffett/[ticker], a tight typography-only header on /buffett, and a plain 'Stock A vs Stock B' headline on compare pages.
- PolishConsistency pass: dropped the parchment gradient backgrounds (stone/amber/cream) from every editorial card. Every page now uses the standard bg-card + border treatment that matches the rest of the app. Serif (Fraunces) reserved for H1s only - section headers and body stay sans for visual coherence.
- Polish/buffett insight panel: heat bars on sector rows tuned from 10% to 5% opacity so the rows don't read as 'tinted cards'. Subtler, less busy.
- PolishCompare overall-winner callout: removed the giant trophy watermark + serif display name. Now a plain accent card with a small trophy icon, restrained styling.
Editorial UI - Buffett verdict + Compare pages get the annual-report treatment
v55- Polish/buffett/[ticker] redesigned end-to-end. Magazine masthead with a hand-built Buffett silhouette watermark, a large wax-seal verdict mark (Yes/No/Maybe stamped in emerald/red/amber), drop-cap on the opening business-model paragraph, pull-quote treatment of the headline, ledger-row profile (label … dotted rule … value), Roman-numeral essay sections for the four philosophy pillars, and grade pills (A–F) on every pillar bar.
- Polish/buffett hub redesigned with the same masthead language - silhouette watermark, serif headline, decorative top/bottom rules. Reads like the cover of a quarterly journal instead of a SaaS dashboard.
- Polish/compare and /compare/[pair] redesigned. Hub uses a numbered editorial list (01., 02., ...). Pair pages get a big VS treatment between the two ticker names, a trophy callout for the overall winner with serif typography, and editorial section heads throughout.
- FeatureAdded Fraunces variable serif font (loaded once, scoped to .font-serif). Used as the editorial display face on Buffett + Compare; rest of the app stays on Inter.
- FeatureNew shared editorial primitives library at src/components/editorial/ - BuffettSilhouette, WaxSeal, Kicker, SectionHead, EditorialRule, ParchmentCard, PullQuote, LedgerRow, DropCap. Reusable for future surface-area.
Compare in sidebar · /compare/[pair] fixed · Buffett Brain v4
v54- FeatureCompare added to the app sidebar (Research section). Goes to /compare which has the side-by-side stock picker.
- Fix/compare/[pair] was 500ing because ISR config conflicted with a downstream loader that reads cookies. Switched to force-dynamic - comparison_pages table still acts as the cache layer so first-hit is the only slow one.
- FeatureBuffett Brain v4 prompt - dramatically deeper reasoning. Each pillar now demands 5-8 sentences with specific numbers from the data table, an inversion (Munger's 'what would destroy this'), and a named weak link. Strengths/concerns expanded to 5-8 specific items each. Banned generic adjectives - every claim has to cite a number, a Berkshire parallel, or a specific business mechanic. Bumped prompt to v4 to invalidate cached verdicts.
- PolishNewsletter Resend API key rotated (new one with sending-only scope).
Newsletter v3 - premium content + banger titles + lowercase brand
v53- PolishBrand wordmark canonicalised to lowercase 'invest-like' everywhere it's customer-facing - emails, footer, sender name, SITE_NAME constant. The 'il' favicon mark stays as-is.
- FeatureNewsletter prompt rewritten for premium-tier output. New 'Premium Test' rule: every paragraph must hit either a specific number, a non-obvious pattern, a contrarian angle, a historical analogy, or name the weak link. Banned-word list (navigate, leverage, robust, etc) so the prose stops sounding like content marketing.
- FeatureBanger-title pattern rules baked into the prompt - model now brainstorms 5 candidates internally and picks the one most likely to make a smart reader open. No more 'Three Businesses Doing Their Job'.
- FeatureSelf-check rules: model re-reads its output before returning, rewrites if the title sounds like a blog post, the opening starts with 'This week', or a pick reasoning has fewer than 2 specific numbers.
Newsletter v2 - real news, OpenAI-generated, weekly auto-send
v52- FeatureNewsletter now opens with a 'This week in markets' section: 3 curated headlines from refresh-news, each with the editor's 2-3 sentence take connecting it to the picks. Score lists alone weren't a newsletter - this is.
- FixSwitched newsletter generation from Anthropic to OpenAI (key already configured + working for refresh-news). Empty Anthropic key was causing every issue to fall back to a generic template.
- PolishEditor's prompt rewritten - now asks GPT to pick a lens that ties news to picks, write a 220-380 word lead, and reason in front of the reader instead of just ranking stocks.
- FeaturePer-news-item impact pills (high/medium/low) + source attribution + 'Read the source' link. Email layout updated to render the news cards above the picks.
The Weekly Letter actually sends - and is worth reading
v50- FixThe newsletter agent only DRAFTED issues - never sent them. That's why running it from /admin produced no email. Now it generates AND sends to every opted-in profile, one personalised email per recipient.
- FeaturePremium content rewrite. New editor's-prompt asks Claude for a real essay around a self-picked theme, with 3 deeply-analysed picks (50-90 words of reasoning each), a 'what we're watching' list, and a closing in the editor's voice. No more generic 5-bullet templates.
- PolishEditorial email layout: cover with date + volume number, theme title in display weight, accent rule, pick cards with score badge + verdict pill + 'why now' callout, watchlist section, signed-off closing. Built to look like a Stripe Press / Howard Marks memo, not a transactional email.
Buffett page fix + better German + compare back
v49- Fix/buffett/[ticker] was throwing ZodError (digest 1688328501) when the AI returned compound enums like 'brand_and_ecosystem' or synonyms like 'high' for pricing power. Schema now normalises common variants (high → strong, brand_and_ecosystem → brand) instead of crashing.
- PolishGerman translations cleaned up: 'Burggraben' (literal castle moat) replaced with 'Wettbewerbsvorteil' across the verdict UI and prompt. Added per-locale terminology rules so future AI generations don't fall back into literal/awkward German.
- FeatureBrought the homepage Compare section back. Three pre-baked pairs (AAPL vs MSFT, NVDA vs AMD, BRK.B vs SPY) plus a 'compare any two stocks' deep link.
- FixBumped Buffett prompt to v3 - invalidates cached verdicts so users see fresh, German-fluent results.
Sentry error tracking
v48- FeatureSentry wired up across server, edge, and client. Production-only, 10% trace sampling, no PII captured. Session replays kick in only when an error happens - gives reproduction footage for free without recording happy paths.
- PolishBrowser SDK calls tunnel through /monitoring/ (same-origin) so adblockers can't drop them. CSP updated to allow Sentry's EU ingest. Hidden source maps so stack traces stay readable in Sentry without leaking source publicly.
Unblock /buffett/ for crawlers + 3 redirect fixes
v47- SEOrobots.txt was disallowing /buffett/ for every crawler - wiping the viral mechanic (per-ticker verdict pages with OG cards) from Google. Removed the disallow, kept the comment block explaining why it must stay open.
- SEOThree internal links pointed to /buffett/aapl (no slash) → 308 redirect → Google flagged 'Seite mit Weiterleitung'. Fixed in blog CTA, /open empty-state, and command palette.
- SEOAdded /welcome to robots disallow (also has noindex meta) so it doesn't appear in 'Crawled, not indexed' reports.
- FixPostHog dashboard reported `ingested_event: false`. Root cause #1: consent gate prevented init. Root cause #2: a zero-width BOM character in the NEXT_PUBLIC_POSTHOG_KEY env var made every event 503. Both fixed; events now flow.
PostHog actually receives events
v46- FixPostHog dashboard reported `ingested_event: false` and `team_sdk_count: 0` - nobody was firing events because init() was gated on cookie consent. Switched to a two-phase model: cookieless capture (persistence: 'memory') by default, upgrade to cookie persistence + session recording on Accept, hard opt-out on Decline. GDPR-safe and finally working.
- PolishMicrosoft Clarity stays gated on explicit consent (it sets first-party cookies from the start with no cookieless mode).
MCP connections live (PostHog, Vercel, Clarity, Supabase)
v45- PolishConnected PostHog, Vercel, Microsoft Clarity, and Supabase via MCP so future incident-response and verification rounds are one tool call instead of a screenshot exchange.
- SEOSubmitted sitemap.xml to Google Search Console + set Supabase Site URL to invest-like.com.
Onboarding + watchlist quick-add
v44- Feature/welcome 3-step post-signup tour with one-click 'try AAPL' pills. /auth/callback redirects new users here on first email confirmation.
- FeatureWatchlist quick-add: sticky bottom bar on /watchlist lets you add a ticker without leaving the page (was the #1 friction point per the UX audit).
- PolishWelcome email primary CTA now points to /welcome instead of /buffett - users see the onboarding sequence first.
- FeatureMore PostHog events instrumented: watchlist_added/removed, settings_changed, data_exported, account_deleted, feedback_sent. All 5 funnels now have end-to-end coverage.
All brand pages translated (5 locales)
v43- SEO/about, /changelog, /roadmap, /status, /open all fully translated to FR, ES, PT, DE. Single biggest remaining SEO gap closed.
- FeatureCommand palette (Cmd+K) commands, group labels, hints, placeholder, and ticker-specific 'View {ticker} verdict' actions all localised.
- PolishPattern: Server-Component shell + Client-Component body so each page can keep generateMetadata + JSON-LD while picking up the user's locale via cookie.
More verdicts + admin warm tool + remaining events
v42- FeatureCache up to 45 verdicts (15 → 45) seeded from a curated top-tickers list. Every popular ticker shared post-launch will land on a real verdict OG card.
- Feature/admin/warm-verdicts: one-click button to fire off background generation for the top 25 tickers. Skips already-cached entries.
- Featurewatchlist_added / watchlist_removed / settings_changed events instrumented. Funnels are now wired end-to-end.
- PolishDashboard empty-state heading was duplicated (label === h2). Fixed.
Translations everywhere + 5 major issues fixed
v41- SEOGDPR cookie banner translated to all 5 locales (was hardcoded English - legal risk for EU users).
- SEONewsletter form, footer (links + trust badges + cookie prefs), share button, /error, /not-found, privacy data card all i18n'd.
- FeatureHero ticker pill bar (AAPL/MSFT/NVDA/TSLA/GOOGL) - single highest-leverage HN-day conversion path.
- FeatureCompare page now has an actual input form + popular pairs (was 'type the URL directly' dead-end).
- FeatureDashboard empty state for new users: onboarding hero with ticker pills replaces the all-zero stats grid.
- Feature/buffett/[ticker] now shows 'Try another verdict' cross-link block with 5 popular tickers + Compare CTA. Kills the dead-end.
- FixQuant_fallback now persists to cache so even no-AI verdicts fill /open and the OG card.
- FeatureSeeded 15 high-quality Buffett verdicts (cache went from 0 → 15).
- FeatureSeeded 50 scenario picks across 10 scenarios - /scenarios page comes alive.
- PolishTouch targets bumped on Refresh, Share, Newsletter, Cookie banner (mobile WCAG).
- FixFR pricing typo: '5 800+ actions' → '12 000+ actions'. DE auth.sessionRetry uses informal 'du' to match the rest of the file.
Security audit + data quality dashboard
v40- SecurityRemoved spoofable x-vercel-cron header bypass on /api/agents/[name] - was a worldwide trigger-any-agent backdoor that could burn AI quota.
- SecurityPer-ticker OG route hardened: strict TICKER_RE validation, switched ilike → eq (no more wildcard fetches), dropped service-role for anon read.
- SecurityAccount deletion now removes the auth user FIRST, then wipes public-schema rows - closes the race window where a logged-in tab could re-create rows.
- SecurityNewsletter signup: in-memory IP rate-limit (5/hour), uniform success message removes the email-existence oracle, dropped user-agent capture, source charset-restricted.
- Security/api/revalidate now uses constant-time comparison for the internal secret.
- Security/open page no longer calls supabase.auth.admin.listUsers (which fetched all emails server-side); uses profiles count instead.
- SecurityMiddleware regex anchored: /auth/callback exclusion no longer matches /auth/callbackXYZ.
- SecurityBlog slug validated against strict regex before filesystem read - no path traversal even if dynamicParams flips.
- SecurityRedirect to_path validated before middleware uses it - DB compromise no longer becomes an open-redirect.
- PolishCookies marked Secure only in production so local-dev OAuth works without HTTPS.
- Feature/admin/data-quality dashboard: live audit of stocks completeness, verdict cache health, and user-data integrity. Severity-coded (FIX / WATCH / OK).
- Fix57 stocks with market_cap ≤ 0 (mostly ETFs) had their market_cap NULLed so they no longer corrupt sorts and aggregations.
- SecurityClosed Supabase advisor warnings: explicit no_direct_access RLS policies on ai_cache + financial_metrics.
More PostHog funnel coverage
v39- Featureblog_post_viewed event on every /blog/[slug] mount - powers the 'is the blog actually driving signups' funnel.
- Featurepricing_viewed event on /pricing - first step of the Free → Paid funnel (post-Stripe).
OG image trailing-slash fix
v38- Fixog:image meta now points to /api/og/buffett/{ticker}/ with trailing slash. Previously it 308-redirected and some social crawlers don't follow redirects.
Viral mechanics - share verdict + per-ticker OG cards
v37- FeatureEvery /buffett/{ticker} now generates a beautiful 1200×630 OG card on the fly - verdict pill (YES / NO / MAYBE), score, headline, brand. Served at /api/og/buffett/{ticker}.
- FeatureShare Verdict button in the buffett page header - copy link, X intent, LinkedIn intent, native Web Share on mobile. UTM tags so we can track which channel converts.
- FeatureVerdict_viewed and verdict_shared events fire to PostHog for funnel analysis.
- PolishBuffett page now ships proper Open Graph metadata (description, ogType:article, per-ticker image) - every shared verdict link gets a rich preview.
Analytics gets a backbone
v36- FeaturePostHog event taxonomy - 21 typed named events, plus 5 funnel definitions for product, growth, and churn analysis.
- FeatureCmd+K / Ctrl+K / '/' now fires command_palette_opened so we can see how power-user behaviour spreads.
- FeatureSign-in buttons + newsletter form now emit named events; the rest of the funnel wires up over the next two weeks.
Blog goes live
v35- Feature/blog and /blog/[slug] live, MDX-driven from src/content/blog/*.mdx. Article + BreadcrumbList JSON-LD on every post.
- FeatureFirst post: 'How I built Invest-like - the stack, the SEO playbook, and what I would do differently'.
- SEOPosts auto-listed in sitemap.xml and llms.txt; linked from footer, /open, and the Cmd+K palette.
- PolishTailwind typography plugin shipped - prose body styling consistent across long-form pages.
PKCE 'two-click sign-in' bug fixed
v34- Fix/auth/callback excluded from middleware matcher - middleware was racing the route handler over the PKCE verifier cookie.
- FixCallback route now builds NextResponse.redirect() first, then binds setAll to it before exchangeCodeForSession - session cookies actually reach the browser on the redirect.
- Fixcookies() primed with getAll() so Next 15's lazy getter has the verifier in scope before the SDK looks for it.
- SecurityflowType: 'pkce' set explicitly on both Supabase clients + cookieOptions { path: '/', sameSite: 'lax', secure: true }.
Security & polish
v29- SecurityLocked down Content Security Policy - only Vercel, PostHog, Clarity, and Supabase can be loaded.
- SecurityHSTS preload header (2-year), submitted-ready for hstspreload.org.
- SecurityPublished security.txt at /.well-known/security.txt for responsible disclosure.
- FeaturePWA manifest - Invest-like is now installable on iOS and Android home screens.
- FeatureRSS feed at /feed.xml - subscribe in Feedly / Inoreader to follow new verdicts.
- PolishCustom 404 page with three quick-jump tiles (Home, Stocks, About).
- PolishLoading skeletons on every route - no more blank flashes during navigation.
- PolishGDPR cookie banner with one-click accept / decline; revisit anytime via the footer.
Brand discoverability
v27–v28- SEONew /about page that directly answers 'What is Invest-like?' - first sentence carries the brand name.
- SEOFAQ section on the homepage with FAQPage schema - what LLMs surface for brand queries.
- SEOStrengthened Organization schema with founder, location, knowsAbout, and alternate names.
- SEOShortened homepage title to 53 chars and meta description to 147 chars (was being rejected by Bing).
- FixTrailing-slash bug - /about/ used to 308-redirect to /about, breaking sitemap canonicity.
LLM crawlers + admin truth
v26- SEOAdded llms.txt at /llms.txt - the new standard LLMs use to discover canonical site content.
- SEOrobots.txt now explicitly allows GPTBot, ClaudeBot, PerplexityBot, Google-Extended, and 18 other AI crawlers.
- SEOhreflang declared for all 5 languages (EN/FR/ES/PT/DE).
- FixAdmin dashboard now shows real totals - 5 pages were filtered by RLS to the admin's own row.
Analytics + perf
v22–v25- FeaturePostHog wired up - events, funnels, heatmaps, session replays. EU region, input masking on by default.
- FeatureMicrosoft Clarity wired up - session replays + heatmaps.
- PerformanceLazy-loaded the strategy playground (recharts ~100KB) - homepage LCP cut by ~1.5s.
- PolishFriendly 'try again' banner for the one-shot PKCE OAuth retry.
Mobile UX overhaul
v20- FixSidebar drawer was opening only 1cm - duration-250 isn't a real Tailwind class.
- Polish44px touch targets across hamburger, theme toggle, language selector, and user menu (WCAG 2.5.5).
- PolishCards now full-width on mobile and use 2-col grids instead of stacking.
- PolishReduced hero green-tint, removed scroll-down indicator.