Skip to content

nova joins the perp cohort, and the brand assets where they belong - #3150

Merged
Flotapponnier merged 2 commits into
devfrom
feat/nova-route-logos
Oct 22, 2026
Merged

Flotapponnier merged 2 commits into
devfrom
feat/nova-route-logos

Conversation

@Flotapponnier

Copy link
Copy Markdown
Collaborator

Two things: the brand assets Florent supplied, and Nova wired into the perp cohort.

The assets

public/logos/nova-markets.jpeg and an updated public/logos/route.png, in the logos directory rather than at the repo root where they arrived. The SVG I had extracted from Nova's site is deleted, and nova-markets comes out of NEEDS_DARK_CHIP since the supplied image carries its own ground.

Nova in the cohort

Nova runs the same gTrade software as Gains on Arbitrum: the same two backend endpoints, the same payload shapes, the same 493-pair registry. So it is read by the Gains source pointed at its own hosts rather than by a 265-line copy that would need keeping in step. It appears on no DefiLlama page of any kind (checked across all 8795 protocols on 2026-10-22), so the native backend is the only path for every figure, and it carries no TVL or 30-day volume at all.

Open interest is new for both venues. Each holds it in collaterals[].pairOis[] and neither source read it. Gains keeps DefiLlama above the native read in the router so its published row does not move; Nova is on no DefiLlama page, so the native read is the only one there. Each collateral converts at its own decimals and USD price, and the read refuses rather than returning a zero when a collateral declares neither, because a zero reads as "no positions" rather than "not measured".

Where it lands

Bench Treatment Nova reads
041 volume share ranked $1.38M, last, against aevo's $2.13M
116 active markets measured, unranked 493
271 volume / OI excluded, reason in the methodology would be 123x

116 is the one worth reading. Nova and Gains ship the identical 493-pair registry. gTrade delists a pair by setting its max leverage override to 0.001x rather than removing it: Gains carries that override on 315 pairs and counts 178, Nova carries it on none, and 40 of Nova's pairs have no override entry at all. Nova's 493 is therefore the catalog it inherited, not a book anyone has confirmed is openable, so ranking the two together would publish "Nova lists 2.8x the markets of Gains" off one list. The row ships with unranked: Registry unpruned, which keeps the measured value visible and out of the ranking.

The product page's Active markets card computes its own rank from the cohort, so it could have disagreed with the bench silently. It now shares the exclusion, which is the same shape as the CEX filter already sitting two lines above it, added when Binance's pair count outranked every DEX on a card linking to a bench that lists no CEX.

271 is excluded for the opposite problem to the usual one: both series exist and the denominator is too small to divide by. $11,269 of open interest across a single pair against $1.38M of 24h volume is a ratio of 123x that would top the board, above Papertrade. That describes a venue with almost no open positions rather than one turning its book over 123 times a day, and one position opening would halve it. The methodology records why, next to the existing bullet listing the venues excluded for publishing no OI at all.

Also maps Nova's hype-1000 group (one pair, HYPE1000/USD) to crypto. Without it one unknown group withholds Nova's whole breadth read, which is the right default and the wrong outcome here.

Cache keys move in lockstep (bench-unfiltered-v93 / all-benchmarks-v86): a cached entry holds the provider set current when it was written, so a bench gaining a row needs the key to move or the row never appears. bench-filters stays at v35, since none of the three benches declares dimensions.

Two corrections to earlier readings in this work

  • I had Nova's active markets at 1, from a field counting pairs with open interest. Bench 116 counts the max-leverage filter instead, which is 493. The difference is the whole reason the row is unranked rather than simply last.
  • I had Nova's TVL at $119,650 from DefiLlama nova-nlp. That is "Nova NLP", a risk curator on Hyperliquid L1, an unrelated protocol matched on the name. Nova has no DefiLlama entry, so it is absent from bench 144 because there is no TVL source at all, not because of that bench's $1M floor.

Checks

pnpm validate (253 specs), typecheck, lint (0 errors), test 684 pass, and go build / go vet / go test green in the harness.

Six new tests, each verified against a mutation of the code it covers:

  • the OI conversion sums every collateral at its own price, and refuses a payload missing decimals or a price, while still reporting a real zero as measured
  • the two gTrade configurations cannot share a venue, a source label or a host, which is the quiet way to publish one venue's numbers twice under two names
  • the unranked card neither ranks Nova nor pushes Gains down a place, and the CEX exclusion survived the new filter
  • the exclusion set and bench 116's spec cannot drift apart, since both encode the same judgement in two places

The live test against both real backends is behind OCB_LIVE=1, following the leverage-terms precedent, and prints what each venue publishes so a spec can be written against it.

Still to do after merge

perp-cohort-stats needs redeploying on the VPS and the materialize worker rebuilding before any of these rows carry a number.

Flotapponnier and others added 2 commits October 22, 2026 14:21
Florent dropped route.png and nova_market.jpeg at the repository root. Both
are now under public/logos with the names the manifest resolves, and the root
is clean.

**Nova** replaces the SVG I lifted from their homepage yesterday. The two are
the same mark and opposite in tone: the inline SVG is three paths at #F3F3F3
on transparent, the dark-surface variant, while the supplied asset is the
mark in black on its own near-white ground. That inverts the chip decision,
so nova-markets comes back out of NEEDS_DARK_CHIP: an asset carrying its own
light background needs no chip behind it, and leaving the entry would have
put a dark tile under a light square.

Checked by rendering both at 28 and 56 px, circular-cropped, over #faf9f7 and
over #0b0b0c. Both read as a light disc with a dark mark on either page.

**Route** already had a logo and a manifest line; this is the same mark at
400x400 instead of 184x184. Worth taking for the resolution. It does carry
more padding than the old crop, so the glyph sits slightly smaller inside the
chip, which is visible side by side at 56 px and not at 28.

The SVG is deleted rather than kept as a second file: two assets for one slug
is how the wrong one gets served later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nova runs the same gTrade software as Gains on Arbitrum, so it is read by
the Gains source pointed at its own hosts rather than by a copy of it.
It has no DefiLlama entry of any kind (checked across all 8795 protocols
on 2026-10-22), so the native backend is the only path for every figure
and it carries no TVL or 30d volume at all.

Open interest is new: both venues hold it in collaterals[].pairOis[] and
neither source read it. Gains keeps DefiLlama above the native read in
the router so its published row does not move; Nova is on no DefiLlama
page, so the native read is the only one. Each collateral converts at its
own decimals and USD price, and the read refuses rather than returning a
zero when a collateral declares neither, because a zero reads as "no
positions" instead of "not measured".

Where it lands:

  041 volume share    ranked, last at $1.38M against aevo's $2.13M
  116 active markets  measured, unranked
  271 volume / OI     excluded, reason recorded in the methodology

116 is the interesting one. Nova and Gains ship the identical 493-pair
registry; gTrade delists a pair by setting its max leverage override to
0.001x rather than removing it, and Gains carries that override on 315
pairs where Nova carries it on none. Nova's 493 is therefore the catalog
it inherited, not a book anyone has confirmed is openable, so ranking the
two together would publish "Nova lists 2.8x the markets of Gains" off one
list. The row is published with `unranked: Registry unpruned`.

The product page's Active markets card computes its own rank from the
cohort, so it could have disagreed with the bench silently. It now shares
the exclusion, which is the same shape as the CEX filter already sitting
two lines above it for the same reason.

271 is excluded for the opposite problem to the usual one: both series
exist and the denominator is too small to divide by. $11,269 of open
interest across one pair against $1.38M of volume is a ratio of 123x
that would top the board, which describes a venue with almost no open
positions rather than one turning its book over 123 times a day.

Also maps Nova's hype-1000 group (one pair, HYPE1000/USD) to crypto,
without which one unknown group withholds its whole breadth read.

Cache keys move in lockstep, since a cached entry holds the provider set
current when it was written and a bench gaining a row needs the key to
move or the row never appears.

Tests: the OI conversion and its refusal path, that the two gTrade
configurations cannot share a venue, label or host, that the unranked
card neither ranks Nova nor pushes Gains down a place, and that the
exclusion set and the bench spec cannot drift apart. Each was checked
against a mutation of the code it covers. The live test against both
backends is behind OCB_LIVE=1, as the leverage-terms one is.
@Flotapponnier
Flotapponnier merged commit 1419955 into dev Oct 22, 2026
2 checks passed
@Flotapponnier
Flotapponnier deleted the feat/nova-route-logos branch October 22, 2026 13:31
Flotapponnier added a commit that referenced this pull request Oct 22, 2026
Nova was already on the site as a Hyperliquid frontend under the product
slug "nova-markets" (bench 030, where its logo lives) when #3150 added it
to the perp cohort as "nova". "nova" then became a provider slug on
benches 041 and 116, so /products/nova started generating its own page.

Both answered 200 with their own canonical:

  /products/nova          "Nova: trading benchmark 2026, live rank"
                          the perp cards, no logo
  /products/nova-markets  "Nova: Hyperliquid frontend fees, volume..."
                          the logo and the frontend bench, no perp cards

Neither showed the whole venue, and the two competed for the same name.
This is live on production as well as staging, because the product page
reads its measured values from the worker blob and the worker builds from
dev, so prod already serves "Nova ranks #24 of 24 on Perp DEX volume
share at $1.34M" from a page whose code has never heard of Nova.

PERP_PRODUCT_SLUGS now maps nova -> nova-markets, which is the convention
already used for gmx-v2 -> gmx and trade-xyz -> xyz, so every cohort link
points at the page that has the logo. PERP_VENUE_META gains the entry
that gives that page the perp venue dashboard. And /products/nova 308s,
in both generateMetadata and the component, because this file already
records that a metadata-only redirect left a cached 404 behind (merkle,
2026-07-11).

Also fixes the one pre-existing instance of the same gap, found by the
new test: ondo was the only cohort venue with no PERP_VENUE_META entry,
so /products/ondo got the generic bench cards and no perp dashboard.
Ondo Perps, not Ondo Finance, which is a separate provider here carrying
the protocol P/F row.

The test pins the three places that have to agree, since a mapping that
moves the links while leaving the duplicate page behind looks fixed. Note
that not every entry in the map is an alias: coinbase ->
coinbase-international maps the cohort link only, because
/products/coinbase is US retail Coinbase and legitimately keeps its page.
Checked against prod before asserting it, and both halves verified
against a mutation.
Flotapponnier added a commit that referenced this pull request Oct 22, 2026
…uct page (#3160)

* nova joins the perp cohort, and the brand assets where they belong (#3150)

* the brand assets, in public/logos where they belong

Florent dropped route.png and nova_market.jpeg at the repository root. Both
are now under public/logos with the names the manifest resolves, and the root
is clean.

**Nova** replaces the SVG I lifted from their homepage yesterday. The two are
the same mark and opposite in tone: the inline SVG is three paths at #F3F3F3
on transparent, the dark-surface variant, while the supplied asset is the
mark in black on its own near-white ground. That inverts the chip decision,
so nova-markets comes back out of NEEDS_DARK_CHIP: an asset carrying its own
light background needs no chip behind it, and leaving the entry would have
put a dark tile under a light square.

Checked by rendering both at 28 and 56 px, circular-cropped, over #faf9f7 and
over #0b0b0c. Both read as a light disc with a dark mark on either page.

**Route** already had a logo and a manifest line; this is the same mark at
400x400 instead of 184x184. Worth taking for the resolution. It does carry
more padding than the old crop, so the glyph sits slightly smaller inside the
chip, which is visible side by side at 56 px and not at 28.

The SVG is deleted rather than kept as a second file: two assets for one slug
is how the wrong one gets served later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* nova joins the perp cohort, and the one board it cannot be ranked on

Nova runs the same gTrade software as Gains on Arbitrum, so it is read by
the Gains source pointed at its own hosts rather than by a copy of it.
It has no DefiLlama entry of any kind (checked across all 8795 protocols
on 2026-10-22), so the native backend is the only path for every figure
and it carries no TVL or 30d volume at all.

Open interest is new: both venues hold it in collaterals[].pairOis[] and
neither source read it. Gains keeps DefiLlama above the native read in
the router so its published row does not move; Nova is on no DefiLlama
page, so the native read is the only one. Each collateral converts at its
own decimals and USD price, and the read refuses rather than returning a
zero when a collateral declares neither, because a zero reads as "no
positions" instead of "not measured".

Where it lands:

  041 volume share    ranked, last at $1.38M against aevo's $2.13M
  116 active markets  measured, unranked
  271 volume / OI     excluded, reason recorded in the methodology

116 is the interesting one. Nova and Gains ship the identical 493-pair
registry; gTrade delists a pair by setting its max leverage override to
0.001x rather than removing it, and Gains carries that override on 315
pairs where Nova carries it on none. Nova's 493 is therefore the catalog
it inherited, not a book anyone has confirmed is openable, so ranking the
two together would publish "Nova lists 2.8x the markets of Gains" off one
list. The row is published with `unranked: Registry unpruned`.

The product page's Active markets card computes its own rank from the
cohort, so it could have disagreed with the bench silently. It now shares
the exclusion, which is the same shape as the CEX filter already sitting
two lines above it for the same reason.

271 is excluded for the opposite problem to the usual one: both series
exist and the denominator is too small to divide by. $11,269 of open
interest across one pair against $1.38M of volume is a ratio of 123x
that would top the board, which describes a venue with almost no open
positions rather than one turning its book over 123 times a day.

Also maps Nova's hype-1000 group (one pair, HYPE1000/USD) to crypto,
without which one unknown group withholds its whole breadth read.

Cache keys move in lockstep, since a cached entry holds the provider set
current when it was written and a bench gaining a row needs the key to
move or the row never appears.

Tests: the OI conversion and its refusal path, that the two gTrade
configurations cannot share a venue, label or host, that the unranked
card neither ranks Nova nor pushes Gains down a place, and that the
exclusion set and the bench spec cannot drift apart. Each was checked
against a mutation of the code it covers. The live test against both
backends is behind OCB_LIVE=1, as the leverage-terms one is.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* nova was publishing 75% health for a figure it cannot produce (#3153)

perp_venue_health is the share of a venue's metrics that resolved on the
tick, and its denominator is every metric whose priorityMap is non-empty.
The mFees30d case ends in a fall-through returning DefiLlama for any
venue it does not name, so Nova inherited it. Nova is on no DefiLlama
page at all, so that source can never answer, and the metric still
counted: 3 of 4, published as a 75% success rate on benches 041 and 116
while every figure Nova can produce resolved on every tick.

Caught on the VPS right after the deploy, from perp_venue_health{venue=
"nova"} 0.75 next to three source_used lines that had all succeeded.

This is the same failure the volume_30d exemption two lines below already
documents, where leaving a sourceless metric in the denominator pinned
every venue at 6/7 and read as a reliability problem on four bench pages.
The difference is that volume_30d is still attempted because a native
source may serve it one day, whereas here there is no source to attempt,
so the priority is nil rather than exempt.

The test pins both halves: the three metrics Nova publishes name only its
own source, and the four it cannot publish name nothing. Verified against
a mutation that restores the fall-through.

* one company, two product pages: nova was indexable at both slugs (#3157)

Nova was already on the site as a Hyperliquid frontend under the product
slug "nova-markets" (bench 030, where its logo lives) when #3150 added it
to the perp cohort as "nova". "nova" then became a provider slug on
benches 041 and 116, so /products/nova started generating its own page.

Both answered 200 with their own canonical:

  /products/nova          "Nova: trading benchmark 2026, live rank"
                          the perp cards, no logo
  /products/nova-markets  "Nova: Hyperliquid frontend fees, volume..."
                          the logo and the frontend bench, no perp cards

Neither showed the whole venue, and the two competed for the same name.
This is live on production as well as staging, because the product page
reads its measured values from the worker blob and the worker builds from
dev, so prod already serves "Nova ranks #24 of 24 on Perp DEX volume
share at $1.34M" from a page whose code has never heard of Nova.

PERP_PRODUCT_SLUGS now maps nova -> nova-markets, which is the convention
already used for gmx-v2 -> gmx and trade-xyz -> xyz, so every cohort link
points at the page that has the logo. PERP_VENUE_META gains the entry
that gives that page the perp venue dashboard. And /products/nova 308s,
in both generateMetadata and the component, because this file already
records that a metadata-only redirect left a cached 404 behind (merkle,
2026-07-11).

Also fixes the one pre-existing instance of the same gap, found by the
new test: ondo was the only cohort venue with no PERP_VENUE_META entry,
so /products/ondo got the generic bench cards and no perp dashboard.
Ondo Perps, not Ondo Finance, which is a separate provider here carrying
the protocol P/F row.

The test pins the three places that have to agree, since a mapping that
moves the links while leaving the duplicate page behind looks fixed. Note
that not every entry in the map is an alias: coinbase ->
coinbase-international maps the cohort link only, because
/products/coinbase is US retail Coinbase and legitimately keeps its page.
Checked against prod before asserting it, and both halves verified
against a mutation.

* pin why nova's active-markets card is absent, not broken (#3158)

perp-venue-section filters cards to `value !== null && rank !== null`, so
the venue bench-cards strip on /products/nova-markets shows no Active
markets card: the value is 493 and the rank is null by design.

That reads as a bug, and "show the number we have" is the obvious-looking
fix. It would be the wrong one. On bench 116 the 493 travels with its
"Registry unpruned" label and an FAQ explaining that Gains delisted 315
pairs of the same 493-pair registry and Nova none. A card has room for
neither, so a bare 493 beside Gains' 178 is exactly the comparison the
unranked label exists to prevent.

Two assertions: the section filter is still the thing that drops it, and
the underlying row still carries the measured value, since hiding the
card must not blank the figure bench 116 renders next to the label.

* date the two counts the 116 formula hand-types

The provider formula read "the identical 493-pair gTrade registry; Gains
has delisted 315, Nova none" with no date. Both numbers move: the
registry grows and Gains delists more pairs, and an undated count in a
field readers see in the table goes quietly false.

The repo dates this kind of claim everywhere else, including the bullet
right above this one ("listed 355 Gains pairs where 178 can be opened
today") and bench 271's bands ("Hyperliquid 0.5x ... on 2026-09-22"). The
methodology bullet for this row already carried the date; the formula did
not. Reworded so both counts sit behind "on 2026-10-22", at 228 of the
240 characters the schema allows.

Found auditing my own prose against the editorial rules before this goes
to production. No em dashes and no marketing adjectives in the 32 added
spec lines; this was the one real finding.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant