● Changelog · what shipped

Engine, Data, UX, Security — in Plain Product Voice

Curated entries — not the git log. Each item explains what changed for the reader of the site, why it matters, and links to where you can verify it live.

2026

seoux

Hero architecture rewrite — conditional render, brand-anchored H1, Three.js lazy-load

The Easy and Quant heroes used to both sit in the DOM at the same time with CSS hiding the inactive one — paint hidden but JS, RAF and Three.js GPU work still ran for both. Switched to a React-state conditional render so exactly one hero mounts. Easy hero h2 promoted to h1 with the Framler brand token leading. Three.js bundle moved to a separate chunk that loads after first paint.

Three coupled changes following a deep performance plus SEO audit of the homepage. Performance: replacing the dual data-easy-only / data-quant-only wrapper pattern with a useReadingMode hook that subscribes to the existing reading-mode broadcast event and conditionally renders exactly one hero. Hidden DOM still runs setInterval, RequestAnimationFrame, IntersectionObserver and Three.js render passes; the previous structure had every animation running twice in parallel on every page load even though only one mode was visible. SEO: the only h1 containing the Framler brand token used to be inside the Quant subtree which is display:none for the default Easy visitor — Google de-weights display:none content under mobile-first indexing. Easy hero is now an actual h1 reading "Framler — from market noise to one clean read", so the visible brand anchor is present for every first-time visitor. Critical given the ongoing brand-query battle with Framer.com (the design tool at DR 92). Bundle: Three.js (around 90 KB gzip) is no longer in the initial JavaScript chunk — switched to a Next dynamic import with ssr disabled, so the search field, headline and chips render instantly and the 3D lens fades in once the chunk is downloaded. Mobile below 1100 px viewports get a pure text-first hero with no Three.js downloaded at all. Same hero design on desktop, much faster Largest Contentful Paint on every viewport.

Homepage
ux

Easy hero 3D scene — noise to signal narrative with live ticker carousel

Easy mode homepage now opens with a cinematic 3D scene: particle stream flowing left to right, converging into a glass-lens at the right, with a live ticker card emerging from the lens core showing one of ten real Framler reads — bullish, neutral and bearish.

The Easy mode hero is now a single visual narrative: thousands of input signals (price action, earnings beats, analyst revisions, insider buys, options flow, short interest, macro regime, yield curve, sector rotation, 10-K filings, SEC Edgar, patent grants, FDA calendar, clinical trials, copula tail, NLP tone, walk-forward, factor load, crisis stress, IV minus RV) converge into the Framler lens, and the lens emits one clean read per cycle. The card on the right cycles every eight and a half seconds through a real mixed-verdict sample from the live universe (four bullish picks at score above sixty-five, three neutral at score thirty-five to sixty-five, three bearish at score below thirty-five) — the actual top and bottom of the universe today, not placeholder data. Each ticker shows its company logo (Clearbit lookup with a graceful diamond glyph fallback for unmapped tickers), verdict colour-coded (green bullish, amber neutral, red bearish), and the score in the same bucketed five-point grid the rest of the public surface uses. The scene runs entirely client-side: Three.js for the lens geometry (eight torus rings with refractive transmission material plus arcs and inner glow), HTML5 canvas for the 1450 particles (420 on mobile when the scene rendered there — now mobile renders text-only since the next-day cleanup), and SVG for the seventy-eight bezier flow paths underneath. All animation is gated by Intersection Observer so it pauses when scrolled off-screen, and Three resources are explicitly disposed on unmount so the GPU buffer is returned cleanly.

Homepage
engineux

Math hygiene + per-regime FDR stratification + /traction launch

Two engine guardrails added — sigma validation on every conformal interval and a Mondrian-monotonicity invariant on the per-bin halfwidth floor. False-discovery-rate control now stratifies by date crossed with regime instead of pooling across the whole sample. New /traction page surfaces real Supabase counts for milestones and cohort progress.

Engine math: added a runtime guard that any sigma input to the conformal interval must be strictly positive and finite — degenerate inputs (zero variance from a flat ticker, NaN from a malformed factor) used to fall through to a default halfwidth, masking the bug. Now the engine throws and the failing input is logged with the upstream factor name. Added a Mondrian monotonicity invariant: the per-bin halfwidth floor (which lifts the calibrated halfwidth in low-sample bins) must monotonically decrease as bin sample count grows. Without this, a literal sort-order bug in the bin lookup table would silently produce inverted floors. The invariant is checked at calibrate-weights cron startup and on every /diag/engine view. False-discovery-rate control on the public pattern-fire announcements now stratifies by date crossed with regime (so a pattern firing in risk_off does not borrow significance from risk_on firings in the same window) instead of pooling all firings across all regimes — closer to the regime-conditional architecture the rest of the engine uses. Mondrian per-bin floor moved from a two-tier step function (bins above twenty samples versus below) to a smooth three-tier gradient (mondrian_bin floor 0.5, marginal_inflated 0.6, prior 0.7) selected via a conformalSource enum surfaced in the public preview. New /traction page: real Supabase row counts driving milestone progress (ticker universe size, signal_history depth, distinct dates with signal coverage, accumulated conformal observations, factor-weight calibration cells), honest cohort deferral copy until the first thirty-day cohort matures around 2026-06-26, and the roadmap quarters re-synced from a hardcoded list to a date-conditional view that flips milestones from "planned" to "shipped" as their actual dates pass.

/traction/diag/engine
enginedata

Three new factor scaffolds + pattern library expansion + GH Actions cost cuts

HAR-RV (Corsi 2009), IVRP (Bali-Cakici-Whitelaw 2011) and analyst_trend now write into stock_universe daily but stay outside the composite weights until their thirty-day measurement window closes. Bearish pattern library expanded 5 → 11. GitHub Actions cron schedule pruned to save 400-600 minutes per month.

Engine: HAR-RV implements the Corsi heterogeneous autoregressive model on realised volatility with cascade weights daily 0.36, weekly 0.28, monthly 0.28 and a small intercept. Runs in parallel with the existing Huber-sigma 60-day estimator at 23:30 UTC Monday through Friday. IVRP populates the implied-minus-realised volatility risk premium using a robust z-score (median plus MAD times 1.4826) over the cross-section every weekday at 07:00 UTC. Both write into their own stock_universe columns but the composite math ignores them until thirty days of paired forward returns have accumulated — measured weekly by the calibrate-weights cron. analyst_trend already shipped in Phase 1+2 form a week earlier; its column has been populated since 2026-05-17 and the calibration window opens around 2026-06-15. Pattern library: the bearish side of the confluence engine was thin (five patterns versus eight bullish) which biased the verdict distribution. Six new bearish patterns added — DISTRIBUTION_TOP, FAILED_BREAKOUT, EARNINGS_MISS_FOLLOWTHROUGH, INSIDER_DUMPING, SECTOR_BREAKDOWN, MOMENTUM_DIVERGENCE — bringing the total to eleven, closer to symmetry with bullish. Pattern interaction factor (multi-pattern fires score-amplification) was producing under-calibrated lifts; reworked the cross-pattern logic so the empirical sigma of fired-multi versus fired-single now matches the documented model. Infrastructure: GitHub Actions cost cuts — Security scan moved from on-push to nightly plus PR, news sentiment from four-times-per-week to two times Mon plus Thu, alerts cadence two hours to four hours, Dependabot weekly to monthly. Roughly 400 to 600 minutes per month saved on the free tier, no observable change in security or alert coverage since most runs were no-ops.

/patterns
securitydata

Observability — Sentry integrated across server, edge, client + email infra cleanup

Sentry now captures errors and traces across server components, edge middleware and client bundles. PII scrubber strips Supabase row identifiers and engine-internal scalars before any event leaves the box. Email sender migrated from a stale Brevo identity to the new framler.com domain with DKIM and DMARC.

Observability: production errors were previously only visible via Vercel build logs which lose stack traces after the next deploy. Sentry SDK integrated into the Next runtime across all three execution contexts (Node server components, edge middleware, browser client) with traces sampled at 5% server-side and 10% client-side, errors unsampled. Scrubber runs as a Sentry beforeSend hook stripping Supabase row IDs, the nine engine-moat scalars (overall_raw, coherence, three confluence amplifier fields, four forward-return audit fields), API tokens and authentication cookies before any event leaves the boundary. New /diag/sentry endpoint forces a controlled error to verify the pipeline end-to-end. Email: the Brevo sender identity was still set to a legacy noreply at deepvane.com address — every transactional email (welcome on signup, password reset, account deletion confirmation) was silently bouncing for about a week because the domain had been redirected. Created the new noreply at framler.com sender on Brevo, registered SPF, DKIM and DMARC records via OVH DNS, updated the BREVO_FROM env on Vercel, and reissued the welcome and account-deleted templates with the new brand. Both transactional paths verified end-to-end on a test account before flipping live.

/status
data

Sectors freshness threshold realigned to monthly cadence

/status was flagging the sector layer as WARN for most of every month because the freshness threshold expected a weekly refresh — the cron has been monthly since 2026-05-15 by design.

Dynamic sector benchmarks (P/E, P/B, gross margin, ROE, FCF yield, revenue growth medians) recompute monthly on the first Saturday of each month because the underlying fundamentals only update on quarterly earnings cycles. Weekly recomputation would be busy-work with no signal change. The /status freshness threshold had been left at the weekly setting (4d ok, 8d warn), so on-schedule months still read as WARN. Realigned to 30d ok and 45d warn — an on-schedule monthly refresh now passes green; only a genuinely missed refresh (more than six weeks late) flags.

/status
seosecurity

Brand disambiguation — entity graph + on-page reinforcement

Created Wikidata entity Q139861198, registered the X handle @FramlerQuant, anchored the brand in 20+ H1s, bucketed sitemap freshness, and enriched JSON-LD on 2,002 ticker routes.

A week into the new domain, the bare brand search "framler com" was being out-ranked by Framer.com — an established 10+ year design tool with much higher domain authority. Treated as a brand-disambiguation problem rather than an SEO penalty. Off-page: created the canonical entity on Wikidata as Q139861198 with six statements (instance of website plus application software, official website, inception 2026-05-13, country France, industry financial services), registered the X handle @FramlerQuant with a Wikipedia-style bio, and wired both as additional sameAs anchors on the Organization JSON-LD. On-page: the Framler name is now in the H1 of every important landing surface — homepage, every themed best-stocks page, every alternative-to comparison, every why-up-today page. The entity FAQ "What is Framler?" is the first question on the homepage FAQ. The sitemap lastModified field now buckets by refresh cadence (daily, weekly, monthly) instead of claiming every URL changed seconds ago. Two thousand and two why-today routes gained FinancialProduct and Article JSON-LD with author Person typing for E-E-A-T credit. /investors had a stale countdown component rendering "0 days / Day 41 of 30 / landing 9 May 2026" — replaced with an honest live-measurement state showing the per-horizon roll-in dates (1d and 7d live since 2026-05-17, 30d ETA around 2026-06-26, 90d ETA around 2026-09-15). The locked preview card "AI Analysis & Scenarios" was renamed to "Plain-English Explanation & Scenarios" — the counterpart hint already said "No AI in the scoring path", so the old title contradicted the positioning.

/investors
securityengineux

Engine moat sealed + IVRP and HAR-RV factors scaffolded + 3 ticker-page interactions

Nine engine-internal scalars are now stripped at the server-to-client boundary so View Source on any ticker page no longer leaks raw coefficients. Two new variance factors (IVRP, HAR-RV) running observe-only. Three new interactions on /stocks/[ticker].

Security: identified that nine engine-internal scalars (overall_raw, coherence, three confluence amplifier coefficients, four forward-return audit fields) were reaching the browser via the React Server Components hydration payload — present in the DOM source despite never being rendered. Added a server-side sanitiser that strips them at every server-to-client prop boundary; verified zero matches in View Source on /stocks/* after the fix. Engine factors: scaffolded two new variance signals running observe-only. IVRP (implied minus realised volatility, Bali-Cakici-Whitelaw 2011) populates daily across the full universe. HAR-RV (heterogeneous autoregressive realised volatility, Corsi 2009) runs in parallel with the existing Huber-sigma 60-day estimator. Composite math ignores both until the calibrated IC measurement window opens in roughly six weeks — the rolling measurement starts now so the activation decision will be evidence-based. Three new interactions on /stocks/[ticker]: hover any vertex of the factor radar to see the factor name in plain English plus its academic citation; click any point on the 30-day score sparkline to expand an inline day-detail card (date, score, regime, verdict, 90% interval, confluence pattern); click the verdict pill in Easy mode to open a conviction-breakdown drawer that visualises the positive-versus-negative signal balance behind the agreement caveat. Plus a weekly accuracy-check now auto-flags any confluence pattern whose hit-rate drops below 50% on n at least 50 firings, surfaced as a red banner on /admin (review-only — patterns never auto-disable).

/stocks/NVDA
uxseo

Ticker page reshape — verdict unified, legacy display removed, /admin engine verdict

Removed a legacy 5-bar factor display that was still showing old labels and weights from before the 13-factor expansion. Unified verdict labels across Easy and Quant. /admin gained an engine-verdict banner with a plain-English yes/no plus three supporting numbers.

Three credibility issues caught from a single live ticker walkthrough. First, the ticker page was still rendering a legacy "Factor Breakdown" block with five labels ("Fundamental / Supply Chain / Insider / Narrative / Regulatory") that were backward-compat aliases from before the 13-factor expansion — and its tooltip still claimed weights that have not been in the engine since v2. Removed entirely. Second, the same score could be tagged "Strong Signal" in Easy mode while Quant said BULLISH-with-low-conviction. Unified through a shared verdictBadgeLabel that mirrors Quant thresholds AND downgrades the tier to "low conviction" when agreement is weak. Third, the "AGREEMENT" bar and a "CONFIDENCE" field were rendering the same Bayesian posterior with contradicting tooltips — merged into one source of truth. /admin gained an ENGINE VERDICT banner at the top: tier-coloured headline (green "working", amber "marginal", violet "too early", red "broken"), three metric cards (Score-versus-future-return correlation, direction hit rate, top-20% minus bottom-20% spread), each with a plain-English descriptor and a small-sample warning when the underlying n is below the publish threshold.

/stocks/AAPL
engine

First measured calibration — engine measures itself

After 30 days of accumulating forward returns, the calibrate-weights cron passed its first real run: 1d and 7d horizons across three regimes, Information Coefficient 0.055-0.102 on a sample of 1,091 daily snapshots.

The first day the engine no longer relies on literature-prior weights for short horizons. The calibrate-weights cron ran against the accumulated signal_history (started 2026-04-16) and the forward-return window has matured for enough daily snapshots that walk-forward Markowitz optimisation produced statistically meaningful per-factor Information Coefficients for both the 1d and the 7d horizon across the three macro regimes (risk-on, transition, risk-off). The measured IC band lands at 0.055-0.102 on a sample of 1,091 daily ticker observations — within the professional quant range (Grinold-Kahn rule of thumb: IC above 0.05 is useful, above 0.10 is good). isPriorMode flipped to false at midnight UTC 2026-05-16; the first weights were persisted into apex_factor_weights with weight_source inv_cov on six calibrated regime-horizon cells and copula_blend on four cells with stronger tail-coupling. Longer horizons stay on the literature prior until their windows mature — 30d expected around 2026-06-26 and 90d expected around 2026-09-15. The /track-record page now shows the calibration progress strip per horizon. This is the milestone the project has been building toward since 2026-04-09 when signal_history started accumulating; the prediction surface is now self-correcting on measured forward-return evidence rather than on theoretical priors.

/track-record/backtest
engine

Calibration flip — prior mode off

Per the engine constant RECALIBRATION_DATE, isPriorMode flipped to false at 00:00 UTC 2026-05-16. Forward-return intervals now reference accumulated forward-return data on calibration windows that have matured, instead of literature priors.

The forward-return engine returned true from isCurrentlyPriorMode() since launch — every conformal interval, every regime amplifier, every variance dampener fell through to literature-prior parameters because not enough realised forward returns had accumulated to support calibrated values. The constant RECALIBRATION_DATE is set to 2026-05-16; the function flips to false the moment that midnight passes. The first calibrate-weights cron that runs against the matured signal_history happens the following Sunday at 12:00 UTC — that is the first real Markowitz optimisation against measured per-factor returns. From this day forward the engine outputs reflect what was actually measured on the universe, not what the academic literature predicted.

engineux

Engine v2.4 + brand v5 — strict conformal + Framler wordmark

Engine v2.4: strict Vovk-Shafer conformal intervals (the coverage guarantee now strictly holds — no per-ticker scaling of the calibrated halfwidth), copula heavy-tie clamp, IV rejection band raised to 10% to filter the Yahoo scraper artefact. Brand v5: double chevron, Framler wordmark with the FRAME letters white, ShareButton on every ticker page.

Strict conformal: applyConformalInterval previously scaled the calibrated halfwidth by a confidence-dependent factor as a "sensible defaults" UX layer. This broke the Vovk-Shafer 2005 coverage guarantee on the most-trusted calls — a nominal 90% interval delivered noticeably less than 90% empirical coverage at high confidence. The picked option was the strict math contract: that scaling factor is gone; the calibrated halfwidth flows through directly; confidence is surfaced separately via the Agreement bar so no information is lost from the UI. Copula heavy-tie clamp: with financial scores clustering at round numbers (50, 25, 75) the Schmidt-Stadtmuller tail-dependence estimator could yield lambda above 1 from average-rank ties — added Math.min(1, low / k) before shrinkage so the documented invariant lambda in [0, 1] holds even on degenerate cohorts. IV floor: the Yahoo options scraper was returning placeholder implied volatilities far below any realistic market value for every ticker tested; the producer-side and engine-side floors now both reject IV below 10% so degenerate values cannot reach stock_universe in the first place. The engine falls through to per-ticker realised volatility (Huber-sigma 60-day) in those cases. Brand v5: new double-chevron mark, Framler wordmark with the FRAME letters in white (foregrounding the methodological hook — conformal prediction frames the uncertainty), l and r in violet. ShareButton on every /stocks/[ticker] page (native Web Share API on mobile, X / LinkedIn / Reddit / Copy popover on desktop). Forty-plus commits across the day; tests held at 740 throughout.

Methodology
engine

Engine v2.2 — pattern-conditional variance dampener

When a confluence pattern fires AND its factors are tail-coupled, the engine narrows the prediction interval by up to 15%. Three independent witnesses → posterior strictly narrower than the prior.

Patterns previously moved only the MEAN of the forecast — a structurally supported pattern (Squeeze, Quality Compounder, etc.) pushed the score in its direction. Variance was unchanged regardless of how strongly the pattern was supported by the underlying factor co-movement (Schmidt-Stadtmüller tail-dependence λ). New patternIntervalDampener: when patternFires AND λ ≥ 0.20, total variance inflation shrinks linearly down to 0.85× at λ ≥ 0.40. Information-theoretic justification: three independent affirmations of the same direction — composite score, pattern label, empirical tail co-movement — give a posterior strictly narrower than the prior under non-redundant evidence. Capped at 0.85× so a runaway pattern can never collapse the interval.

engine

Engine v2.1 — math-foundation: piecewise α + VIX-conditional base variance

Short-horizon alpha at h=1 nearly doubles so 1d/7d forecasts actually separate across tickers. Market base variance now tracks daily VIX implied move instead of a fixed 16% literal.

A1 — piecewise alphaHorizonScale: prior pure √(h/90) gave h=1 alpha of only 0.105 of full alpha; nearly every bullish ticker clustered at P(positive 1d) = 62–65%. New scaler is 0.20·h^0.171 for h ≤ 7 (anchored so h=7 still matches √(7/90)) and √(h/90) for h ≥ 7 (h=30 and h=90 calibration anchors preserved). Top setups now land 1d P(up) 0.72–0.78, weak bulls 0.55–0.62 — readable spread instead of a flatline. Literature: Moskowitz-Ooi-Pedersen 2012 "Time series momentum" show the premium expresses faster than random-walk √t in the first 5–10 trading days. A4 — VIX-conditional base variance: refreshCalibrationCache() now also pulls vix_implied_move_30d from apex_calibration_constants (calibrate-vix cron writes it daily). Per-day variance becomes (vim²)/30 instead of the static (0.16²/252) literal. High-VIX regimes widen base variance for every ticker without per-ticker IV; per-ticker IV still overrides when available.

Methodology
seoux

Brand migration — deepvane.com to framler.com

274 source files rebranded in one commit. Middleware 301-redirects every deepvane.com path to its framler.com counterpart. OG, sitemap, JSON-LD and Schema.org Organization all re-pointed.

The old brand DeepVane was colliding with adult-content search results on the bare brand name. After investigating 35+ alternative names and ruling each out on either domain unavailability, namespace collision, or phonetic anti-positioning, the founder's own 10-year-old domain framler.com became the answer — clean trademark check, .com TLD with a decade of latent reputation, semantic tie to the methodological core (conformal prediction frames the prediction interval), no AI-positioning baggage. The rebuild was executed in one batch: 274 tracked source files updated, OVH DNS configured, Vercel domain alias added, Supabase auth allow-list extended, middleware now redirects every deepvane.com path to its framler.com counterpart (308 from apex to www, 301 from old domain to new). All public surfaces — homepage, /investors, /about, /press, /research, /methodology, JSON-LD Organization, sitemap, RSS, OG cards — now reference Framler exclusively. The framler.com domain has been registered since 2016 and previously hosted a Kickstarter-era prototype by the same founder, so the link history is internally consistent.

/about
ux

Easy / Quant reading modes across the dashboard

Pharma Signals, Smart Money Confluence, Emerging Signals and Insider Clustering all respect the Easy / Quant toggle in the top bar. Easy shows a one-line verdict per row; Quant keeps the full breakdown.

Until today the global Easy / Quant toggle had visible effect only on the homepage and on per-ticker pages. Four dashboard pages now mirror the same pattern. Easy on /dashboard/pharma renders one card per Phase 3 trial: ticker, condition, failure probability and a STRONG BEARISH / BEARISH / MIXED / LOW RISK verdict pill - no expand. Easy on /dashboard/confluence is a one-line "why" per ticker plus the DOUBLE or TRIPLE pill. Easy on /dashboard/emerging shows the type icon, headline and risk pill only. Easy on /dashboard/insiders is a single sentence per cluster ("5 insiders at Energy companies bought $14M..."). Quant mode keeps the previous detailed cards on every page. Reading mode persists across sessions in localStorage.

/dashboard/pharma/dashboard/confluence
uxengine

Smart Money Mirror Portfolio - adaptive thresholds

The dashboard portfolio builder, formerly "My Portfolio Builder" with all-Amazon allocations, is now Smart Money Mirror Portfolio with adaptive thresholds that scale to the number of funds the user has loaded.

Two regressions fixed. First: at $5K budget with five funds loaded, Stable strategy was collapsing into 100% AMZN because filters were hardcoded "fundCount >= 4". Thresholds now scale via active-fund-count factory: Giants requires 80% of loaded funds, Stable 60%, Mixed 40%. Null-safe score defaults so a ticker with no APEX row still passes a "score >= 45" gate. Mixed strategy was reweighted to prefer concentrated single-fund alpha bets (+8 score bonus when a manager holds >=8% of a name) so the feed actually mingles mega-caps with off-radar Druckenmiller / Ackman / Loeb picks. Second: the dashboard variant was rendering an AI Portfolio Analysis section that was calling a deleted API route - removed entirely (the deterministic rationale on every pick card was always there).

/dashboard/portfolio
mobileux

Mobile-responsive dashboard tables

Insider Clustering raw-feed table and the Emerging Signals 4-up legend reflow correctly below 480px instead of crushing columns into unreadable slivers.

Wrapped the Insider raw-feed 6-column grid in an overflow-x:auto container with a 620px minimum width so phones get horizontal scroll rather than crushed cells (touch-momentum enabled on iOS). The Emerging Signals signal-type legend (Lone Wolf / Conviction / Early Movers / Stealth Exit) was rendered with a rigid 4-column grid; now uses auto-fit minmax so it flows 4-up on desktop, 2-up on tablet, 1-up on phone. Headers (h1) across nine dashboard pages switched from a literal fontSize of 36 to clamp(24px, 6vw, 36px).

security

Next.js 15.5.18 - security advisory closed

Bumped Next.js to address GHSA-8h8q-6873-q5fj (Server Components denial-of-service).

npm audit fix moved Next 15.5.15 to 15.5.18, the patched release. 0 vulnerabilities remain at any severity. The repo security-scan workflow now passes on every push instead of failing on the dependency-audit step.

uxseo

Standalone /disclaimer, /story, /contact and /changelog.rss

New public pages and a real RSS feed for the changelog so readers can subscribe in any feed reader.

/disclaimer collects the legal footing (not investment advice, model-output caveats, past-performance, data-lag, no warranty, geographic scope, AI explainer note) on its own canonical surface and is linked from the dismissable bottom banner. /story indexes the case-study series (NVDA 65 -> 87 live, two planned post-2026-05-16). /contact lists the five inboxes (research, enterprise, founder, support, security) in one place. /changelog/rss.xml emits a real RSS 2.0 feed with content:encoded so Feedly and other readers can subscribe; the page also adds a link rel="alternate" for autodiscovery.

/disclaimer/story/contact
enginedata

Commodity sensitivity layer + macro catalyst calendar

Per-ticker beta vs oil / gold / copper / DXY is now refreshed weekly, and the macro catalyst calendar widens conformal prediction intervals around FOMC / CPI / NFP days.

Phase 1: a weekly GitHub Actions cron (Mon 06:30 UTC) computes 228 tickers' regression beta against four commodity reference series and stores it on stock_universe.beta_*. Phase 2: a monthly cron-job.org task pulls 47 upcoming macro events into the macro_catalysts table; the scoring pipeline now widens conformal half-widths the trading day before any FOMC / CPI / NFP release. The widening factor is calibrated and proprietary. The two layers run together but are governed by separate calibration windows so we can land them at different paces. Commodity factor enters the prior weight vector only after 2026-05-17.

ux

Google sign-in + onboarding rebuild

Google OAuth is wired end-to-end and the onboarding flow now uses spotlights, a celebration card and an explicit 4-page-hierarchy explainer slide.

Google sign-in landed on /auth/login and /auth/register, gated by NEXT_PUBLIC_GOOGLE_OAUTH_ENABLED. The callback exchanges OAuth ?code= for a Supabase session via auth.exchangeCodeForSession(). The onboarding checklist grew from 4 to 5 steps with a sticky "tried Quant view" flag, every step now spotlights its next-click target via a CSS pulse selector, and the welcome tour gained a slide that explains the page hierarchy (Signal Feed / All Stocks / Watchlist / Track Record) explicitly so first-time users don't miss the seven hidden dashboard pages behind the Discover dropdown.

dataengine

Universe expanded to 1,000 tickers (US + EU + Asia)

Added 626 names beyond the original 374 US-only set: London, Paris, Frankfurt, Milan, Zurich, Tokyo, Hong Kong, Korea, plus mid-cap US. Engine now multi-currency end-to-end (USD-equivalent P&L on virtual portfolio).

Expansion ran in three phases: first new mid-cap US batches, then EU native listings (.L .DE .PA .AS .MI .BR .SW), then Asian (.HK .T .KS). Migration 038 added currency + fx_to_usd_at_open to virtual_trades and formalised virtual_prices with currency/fx_to_usd, so paper-trade P&L stays USD-correct even when FX moves. Migration 039 cleaned 13 confirmed-delisted tickers (SGEN/QTS/STOR/SQ/GPS/CDAY/PKI/CEIX/TTM/BDEV.L/SOL.BR/AVST.L/DARK.L) and renamed bare symbols to the form Yahoo trades them under.

/dashboard
dataengine

100% daily coverage on the 374-stock universe (at the time)

Cron architecture redesigned: every ticker in the universe gets a fresh Framler score every trading morning, plus a recompute-zscore pass on the full cross-section.

cron-job.org runs nine 10-stock batches between 06:00 and 06:16 UTC. A GitHub Actions workflow then sequences another 29 batches for offsets 90 through 370. After all 38 batches land, recompute-zscore rebuilds the cross-section μ/σ that the z-score composite relies on. The universe at the time was 374 US tickers; it has since grown to 1,000+ — see the 2026-05-07 entry above for the expansion details.

/status
seo

Schema.org JSON-LD on every ticker, blog post, and pattern page

Centralised typed schema library emits FinancialProduct, Article, and BreadcrumbList graphs on ~395 URLs.

A typed builder library (src/lib/seo/json-ld.ts) generates Schema.org JSON-LD for ticker pages (FinancialProduct with sector, exchange, Framler score, verdict, price as PropertyValues), blog posts (Article + Breadcrumb via the shared BlogShell), and pattern pages (Article + Breadcrumb). Site-wide Organization + WebSite schema rides in the root layout. Result: rich-snippet eligibility on Google + structured citation hooks for ChatGPT, Perplexity, Claude.

security

Public security page + RFC 9116 disclosure

/security publishes the live security posture; /.well-known/security.txt gives bug-bounty researchers an instant contact.

The /security page lists 13 in-place controls (HSTS preload, CSP whitelist, DENY frame-ancestors, EU data residency, cron Bearer auth, admin role checks, cookie isolation, scheduled key rotation, public math invariant battery) and 7 roadmap items (2FA + WebAuthn, CSP nonce, per-IP rate limits, CSRF tokens, SOC2 Type I readiness, audit log, bug-bounty programme). Each claim is independently verifiable via headers, code, or endpoints — nothing is a marketing assertion.

/security
api

Public API documentation page

/docs catalogues seven read-only JSON endpoints anyone can hit without an API key.

Live (/api/health), pipeline freshness (/api/status), aggregate track-record (/api/track-record), 60-day HMM regime history (/api/regime-history), 46-invariant math battery (/api/diag/engine), single live quote (/api/quote), 30-day sparkline (/api/sparkline). Each entry shows method, path, parameters, sample response, cache policy. Covers the read surface only — authed and admin endpoints are intentionally excluded.

/docs
engine

Engine v2.0 — forward-return composer + invariant battery

A unified Bayesian engine composes every layer into one coherent forward-return distribution per horizon, with an internal invariant battery that runs on every request.

The math symphony itself: a single composer that fuses the 13-factor composite, regime conditioning (BOCPD posterior), confluence patterns, conformal intervals (Vovk 2005), Schmidt-Stadtmüller tail-dependence, Kalman DLM exposures, and Mondrian binning into one forward-return distribution per 1d / 7d / 30d / 90d horizon — one mean, one 90% confidence interval, one probability of positive return. Each layer reduces to a no-op when its evidence is absent so missing data degrades gracefully. A battery of mathematical invariants (graceful degradation, monotone variance growth, distributional ordering, multiplier symmetry, CI containment) runs on every internal request and gates deployments. This release consolidates what previous notes labelled separately as v6, v7, v8, v9.1–v9.6, v10 and v11 — only two real architectural shifts have happened: v1.x factor scoring → v2.x the math symphony.

Methodology
mobileux

Mobile-first card layout for /dashboard/stocks

Phone width below 600px collapses each row into a 3-line card — ticker pills + action top, name middle, change + score + sparkline bottom.

Previous responsive rule kept a 6-column grid even at 320px, which overflowed on iPhone SE. The CSS rewrite uses nth-child grid-area assignments — JSX is unchanged, the row stays a single Link, tap target preserved across the whole card.

/dashboard/stocks
Changelog | Framler