Ship: anti-cache RPC probe, keyed bench 069, indexing-freshness 070, Monad+MegaETH 071-072, explorer coverage 068 - #976
Closed
Flotapponnier wants to merge 167 commits into
Closed
Ship: anti-cache RPC probe, keyed bench 069, indexing-freshness 070, Monad+MegaETH 071-072, explorer coverage 068#976Flotapponnier wants to merge 167 commits into
Flotapponnier wants to merge 167 commits into
Conversation
…740) After dev → main reconciliation (PRs A-E), dev was hard-reset to main. This commit re-introduces the 4 production-ready benchmarks that lived on the old dev branch as staging-only content. They will NOT be promoted to main until they pass staging review. Benches: - bridge-revenue.yml (188L, status: live) — bridge revenue tracking - evm-quote-latency.yml (194L, status: live) — EVM aggregator quote latency (Mobula vs Codex vs GeckoTerminal etc.) - solana-dex-quote-latency.yml (192L, status: live) — Solana DEX quote latency (Jupiter vs Mobula vs others) - solana-tx-landing.yml (218L, status: live) — Solana tx landing benchmark Companion editorial content (for SEO long-tail): - answers/which-evm-aggregator-has-the-fastest-quote.yml - answers/which-solana-dex-aggregator-is-the-fastest.yml - alternatives/jupiter.yml Workflow going forward: - main = prod (openchainbench.com), source of truth - dev = staging buffer (auto-deploy to staging Preview URL) - These 4 benches live on dev. When approved, open a dedicated PR cherry-picking them to main. - Future feature work: open a feature branch from dev (or main), PR to dev for staging review, then promote to main. Co-authored-by: Florent Tapponnier <contact@mobula.io>
… 043) Adds 8 venues sourced via Mobula's CEFI funding-rate aggregator (perp_venue_funding_24h_bps, freshness via perp_venue_last_refresh_unix): - Bitget, Coinbase, Deribit, Gate, Kraken, KuCoin, Lighter, MEXC Same stddev-over-time semantics on a 24h window, BTC/ETH/SOL split per asset. Cohort jumps from 7 to 15 venues. Logos: 5 PNGs sourced from CoinMarketCap exchange CDN (bitget, deribit, gate, kraken, mexc); 2 SVGs from simpleicons (coinbase, kucoin); 1 SVG from simpleicons for binance (was reusing /logos/bnb.png, the chain logo, not the exchange brand). Bench copy + manifest entries updated.
Dev was force-pushed after PR #730 and the venue support in BenchmarkFilters (materialize/filters.ts) was lost in the spec/snapshot refactor PR #739. The worker still references f.venue and dims.venue, so typecheck failed. Re-add venue across: - BenchmarkFilters union (load.ts) - parseFilterSig allowlist - Spec.dimensions schema (spec-schema.ts) so YAML venue dim parses
feat(perp-funding-stability): scale to 15 venues (+8 via Mobula) + logos
…ble in dark mode) simpleicons SVGs are monochrome (renders black) so coinbase and kucoin logos vanished in dark mode. Replace with the colored 200x200 PNGs from the CoinMarketCap exchange CDN, same source as bitget/deribit/gate/kraken/mexc.
…ored fix(logos): coinbase + kucoin colored PNG (was invisible in dark mode)
PR #743 created src/app/benchmarks/category/[cat]/page.tsx + benchmark-grid.tsx referencing @/lib/categories but the helper module was not cherry-picked to main, breaking typecheck. Also re-apply binance: /logos/binance.svg (was reverted by auto-merge against pre-existing main entry pointing to /logos/bnb.png).
* feat(sample-health): wire UI badge + API surface + insufficient silencing (follow-up A) (#741) Completes PR #732 (sample-health system) by surfacing the computed dataConfidence/sampleHealth/expectedN to users + machine-readable consumers. Was dead-code on the data layer until this PR. UI (ledger-table.tsx): - Filter rows tagged 'insufficient' out of the sorted ranking. - 'Low sample' pill on rows with dataConfidence='low' + tooltip showing the actual sample-health ratio ('45% of expected'). APIs: - /api/citable: returns expectedN + dataConfidence per row. When bench-wide aggregate is 'insufficient', returns value=null + leader=null (refuses to crown a leader from undersized samples). - /api/stat/[slug]: same treatment + drops 'insufficient' rows from the machine-readable ranking array. Per-row sampleHealth + dataConfidence exposed for downstream tooling. Library (citation.ts): - citationCandidates() drops 'insufficient' rows from the leader candidate set. - fieldValue/leader/headlineSentence return null/'insufficient data' when bench-wide is insufficient. - isInsufficient() helper preserved from main (used by hub cards + /api/citable hot path). Effect: bench pages where YAML declares expected_n now visually communicate sample confidence; agents/journalists hitting the citable API can't accidentally quote a leader drawn from 3 data points; the methodology page's 'n >= 1000' claim becomes honest. Co-authored-by: Florent Tapponnier <contact@mobula.io> * feat(seo): /benchmarks/category/[cat] hub pages + BenchmarkGrid lockedCategory (D.2) (#743) Final cherry-pick from dev-archive — adds 7 new SEO-indexable hub pages (one per benchmark category) that target long-tail queries like 'best crypto aggregator benchmarks 2026'. D.2 was deferred from PR D because the BenchmarkCard ↔ MiniChart ↔ BenchmarkCardData type chain had cascading TS errors. Follow-up A (citation.ts isInsufficient → InsufficientCheckInput structural subset) silently fixed those, so this lands cleanly now. Files: - src/app/benchmarks/category/[cat]/page.tsx (new) — server route that renders BenchmarkGrid pre-filtered to one category + breadcrumb + ItemList JSON-LD. generateStaticParams builds the 7 hub routes from CATEGORIES. Each page is fully indexable (canonical, OG, etc.). - src/components/benchmark-grid.tsx (updated) — adds two optional props (lockedCategory, allCategories). Backward compatible: when unset the grid behaves exactly as before (used on /benchmarks). When set (category hub routes), the grid pre-filters and shows the full category pill list so users can hop between hubs. URLs that ship after deploy: - /benchmarks/category/aggregators - /benchmarks/category/bridges - /benchmarks/category/blockchains - /benchmarks/category/trading - /benchmarks/category/wallets - /benchmarks/category/rpcs - /benchmarks/category/nft-apis Each renders the canonical bench grid filtered to that category with its own H1, meta description, and structured data. Sitemap pickup automatic on next regen. Co-authored-by: Florent Tapponnier <contact@mobula.io> * fix(seo): blacklist hex builder slugs and shared composite filter Drop 15 anonymous 0x... HL builder slugs and 5 dead composite slugs from /products listing, sitemap and individual /products/<slug> routes. DEAD_COMPOSITE_SLUGS now lives in providers.ts as the single source of truth; related-providers.ts imports it. Bumps providers cache key v3->v4. --------- Co-authored-by: Florent Tapponnier <contact@mobula.io>
* feat(sample-health): wire UI badge + API surface + insufficient silencing (follow-up A) (#741) Completes PR #732 (sample-health system) by surfacing the computed dataConfidence/sampleHealth/expectedN to users + machine-readable consumers. Was dead-code on the data layer until this PR. UI (ledger-table.tsx): - Filter rows tagged 'insufficient' out of the sorted ranking. - 'Low sample' pill on rows with dataConfidence='low' + tooltip showing the actual sample-health ratio ('45% of expected'). APIs: - /api/citable: returns expectedN + dataConfidence per row. When bench-wide aggregate is 'insufficient', returns value=null + leader=null (refuses to crown a leader from undersized samples). - /api/stat/[slug]: same treatment + drops 'insufficient' rows from the machine-readable ranking array. Per-row sampleHealth + dataConfidence exposed for downstream tooling. Library (citation.ts): - citationCandidates() drops 'insufficient' rows from the leader candidate set. - fieldValue/leader/headlineSentence return null/'insufficient data' when bench-wide is insufficient. - isInsufficient() helper preserved from main (used by hub cards + /api/citable hot path). Effect: bench pages where YAML declares expected_n now visually communicate sample confidence; agents/journalists hitting the citable API can't accidentally quote a leader drawn from 3 data points; the methodology page's 'n >= 1000' claim becomes honest. Co-authored-by: Florent Tapponnier <contact@mobula.io> * feat(seo): /benchmarks/category/[cat] hub pages + BenchmarkGrid lockedCategory (D.2) (#743) Final cherry-pick from dev-archive — adds 7 new SEO-indexable hub pages (one per benchmark category) that target long-tail queries like 'best crypto aggregator benchmarks 2026'. D.2 was deferred from PR D because the BenchmarkCard ↔ MiniChart ↔ BenchmarkCardData type chain had cascading TS errors. Follow-up A (citation.ts isInsufficient → InsufficientCheckInput structural subset) silently fixed those, so this lands cleanly now. Files: - src/app/benchmarks/category/[cat]/page.tsx (new) — server route that renders BenchmarkGrid pre-filtered to one category + breadcrumb + ItemList JSON-LD. generateStaticParams builds the 7 hub routes from CATEGORIES. Each page is fully indexable (canonical, OG, etc.). - src/components/benchmark-grid.tsx (updated) — adds two optional props (lockedCategory, allCategories). Backward compatible: when unset the grid behaves exactly as before (used on /benchmarks). When set (category hub routes), the grid pre-filters and shows the full category pill list so users can hop between hubs. URLs that ship after deploy: - /benchmarks/category/aggregators - /benchmarks/category/bridges - /benchmarks/category/blockchains - /benchmarks/category/trading - /benchmarks/category/wallets - /benchmarks/category/rpcs - /benchmarks/category/nft-apis Each renders the canonical bench grid filtered to that category with its own H1, meta description, and structured data. Sitemap pickup automatic on next regen. Co-authored-by: Florent Tapponnier <contact@mobula.io> * fix(seo): drop dead /alternatives links from perp/PM venue footers + dead compare pair Two SEO regressions feeding 404s into Google's crawl: 1. /products/<slug> for perp/PM venues without an alternatives YAML (lighter, manifold, vertex, grvt, ostium, variational, pacifica, aevo, edgex, paradex, extended, myriad, limitless, aster) hardcoded a /alternatives/<slug> footer link in PmVenueSection and PerpVenueSection. Now gated on loadAlternativeSlugs() membership so only venues with an actual YAML emit the link. 2. /compare hub rendered jupiter-vs-raydium even though raydium has no bench appearances, so /compare/jupiter-vs-raydium 404s downstream. Pre-filter COMPARE_PAIRS against getProvider() so dead pairs disappear from the list and the JSON-LD ItemList until both sides resolve. Plus a defensive guard in buildAlternativesReverseMap() so the emitted alt.slug is always a known live YAML, even if the alt list source ever changes shape. --------- Co-authored-by: Florent Tapponnier <contact@mobula.io>
Co-authored-by: Florent Tapponnier <contact@mobula.io>
chore(dev): sync main → dev (bring SEO hotfixes + canonical fixes)
…s to sitemap (#764) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ge (#766) Drop in #701 (2026-06-25) was driven by harness offline + 404 SEO. The Railway harness still runs and Prom keeps emitting solana_landing_probe_enabled=1 plus the slot/latency series, so restoring the YAML brings the page back live (or in 'awaiting samples' until a funded SOLANA_PROBE_KEYPAIR_BASE58 is set on Railway). Validate clean (34 specs).
When a chain rename rolls through Prom while the 24h window still holds samples on the legacy label (e.g. ton → gram during the harness redeploy lag), the loader rewrote each ton row to gram but did not dedupe — so /benchmarks/l1-finality rendered Gram twice (one row per source series). Two-pass: rewrite then dedupe by canonical slug, preferring the row whose original slug already matched the canonical (current harness emit) over the alias-rewritten historical row.
fix(spec): dedupe results after legacy chain slug rewrite
Numeric column headers in LedgerTable now toggle the table's sort on click. First click on a header descends on its value, second click toggles to ascending. An up/down chevron next to the active header's label marks the direction; hover lifts ink-muted to ink so the headers read as interactive. Sortable columns: Name (A-Z), every customCols entry (per-index key), Value (when a panel tab is active), p50/p90/p99/Mean, Delta field, Success, and Slot delta p50 (when hasSlots). Trend and Number columns stay static. The default sort (sortKey null) preserves the exact prior comparator so OG renders, share cards, and snapshot tests keep matching. A non-null sortKey routes through a new pickSortValue helper instead. Missing values land at +/-Infinity so empty cells consistently sort to the bottom regardless of direction.
feat(ledger): clickable column headers to sort leaderboard
feat(hl-frontends): long-window archive UI toggle + history API
…dev-hl-frontends # Conflicts: # src/components/ledger-table.tsx
…rontends chore(dev): sync main → dev (HL frontends + cache bumps)
…ab click (#777) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…i/series (#779) Co-authored-by: Florent Tapponnier <contact@mobula.io>
The shared Upstash free-tier KV hit its 500k/day request cap; the Go service's PushUpstash silently failed with 'max requests limit exceeded' and the bench page stayed on 'archive_pending' for the long windows. Swap the reader to call the hl-archive Railway HTTPS API (/v1/aggregates?window=all) directly. Same payload shape, one less moving part, no quota constraint, no JSON-encoded-string detour. Next.js unstable_cache (60s revalidate) still bounds the per-region request load to the Railway service. Two new env vars on Vercel: - HL_ARCHIVE_API_URL (e.g. https://hl-archive-production.up.railway.app) - HL_ARCHIVE_API_KEY (matches the Go service's HL_ARCHIVE_API_KEY) Existing Upstash env (KV_REST_API_URL / UPSTASH_*) is no longer read by this module.
feat(hl-archive): read directly from Railway service (skip Upstash)
Fold the standalone HlHistoryToggle into the chart pills so there is a single source of truth for the active window. Short windows (1H..30D) keep the existing Prom-sourced chart + ledger. Long windows (90D..ALL) swap the chart data source to the archive's per-builder daily timeseries and replace the ledger with the archive-ranked leaderboard. - chart pills extended with 90D 180D 1Y ALL behind a hairline separator - archive fetch lifted to BenchmarkBody, payload reused for chart + ledger - /history API now passes through timeseries_daily on archive responses - HlHistoryToggle removed, behaviour split into HlArchiveLeaderboard + the chart's new longRangeSeries prop - long-range pills disable with a 'soon' hint when the archive is down
…ts 20k credits/month) (#949) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…es (#950) Co-authored-by: Florent Tapponnier <contact@mobula.io>
* rpc: anti-cache probe — eth_getBlockByNumber(latest) with rotating id replaces eth_blockNumber * remove WIP explorer-chain-coverage spec, included by mistake --------- Co-authored-by: Florent Tapponnier <contact@mobula.io>
…top-50 view, new Explorers category (#952) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…d broke) (#953) Co-authored-by: Florent Tapponnier <contact@mobula.io>
… square mark), keyless registered for etherscan+subscan (#954) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…955) Co-authored-by: Florent Tapponnier <contact@mobula.io>
… access) (#956) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…nce, etherscan chain-id allowlist, disclosed ceilings (#957) Co-authored-by: Florent Tapponnier <contact@mobula.io>
* ecc: validity fixes — freshness ladder 60m/5m, quiet-chain second chance, etherscan chain-id allowlist, disclosed ceilings * rpc-keyed-latency: free-tier keyed RPC harness (env-driven matrix, 90% quota guard, anti-cache probe) * prom: scrape rpc-keyed-latency (3 regions) * bench 069: rpc-keyed-latency spec (draft until 24h of data) --------- Co-authored-by: Florent Tapponnier <contact@mobula.io>
… days) (#959) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…e specs) (#960) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ed bench product pages) (#961) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ure) + product page/logo (#962) Co-authored-by: Florent Tapponnier <contact@mobula.io>
Co-authored-by: Florent Tapponnier <contact@mobula.io>
…lan disclosure extended) (#964) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…965) Co-authored-by: Florent Tapponnier <contact@mobula.io>
… fleet) (#966) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…bility lag, 5 providers, quota guards (#967) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…exing bench product pages) (#968) Co-authored-by: Florent Tapponnier <contact@mobula.io>
… on Base) (#969) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…9) (#970) Co-authored-by: Florent Tapponnier <contact@mobula.io>
….0s) (#971) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ive-verified (#972) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ider pages + logos (bloxroute, onfinality, officials) (#973) Co-authored-by: Florent Tapponnier <contact@mobula.io>
… + restore original logo (#974) Co-authored-by: Florent Tapponnier <contact@mobula.io>
…975) Co-authored-by: Florent Tapponnier <contact@mobula.io>
Collaborator
Author
|
Remplacée par la branche de réconciliation ship/dev-to-main (main avait 5 hotfixes jamais ff-back dans dev) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contenu du ship
Post-merge
vercel --prod manuel (ce PR ne déploie rien automatiquement)