Right now every set scores the same low investor grade — because it rests on a value signal that isn't measuring anything. Here's exactly why, and the honest, data-backed way to make it real.
The investor grade leans 30% on a "hold value" score that is a frozen 5.0 for all 1,000+ graded sets. It's a placeholder wearing a letter grade — so the investor read is both uniformly low and tells you nothing. We're going to feed it the one rich signal we actually have: part-out value.
A LEGO buyer doesn't have one "value" question — they have three. The engine only answers the first one well.
Questions 2 and 3 are the investor read. Both are broken today, for the same reason.
The engine has seven scoring clusters. Six feed every grade; the seventh — holdValue — is a side signal that only the investor lens weights (at 30%). That cluster is built from two dimensions, and both are dead:
| Hold-value dimension | How it's supposed to score | What it actually does |
|---|---|---|
| scarcity50% of holdValue | Base 5 +2 if the set is exclusive +2 if it's retired. |
Flat 5.0. The retired and exclusive flags are never populated (0 of 3,639 sets), so the bonuses never fire. |
| resale50% of holdValue | Current market value ÷ MSRP, on a growth curve (1×→3.5, 2×→8.5, 3×→10). | Always null. It needs current_value, which we have for 0 sets (no market-value pipeline). |
With resale null and scarcity frozen at 5, holdValue = 5.0 for every set — yet it's marked "scored," so it reads as a real verdict. The investor lens then spends 30% of the grade on this constant, which both drags every set down and erases the differences between them. In the live data, 746 of 1,000 investor grades are C− or lower, and the hold-value number is literally 5.0 on all of them.
Same pipeline, two states. Follow it left-to-right: the inputs feed two dimensions, which roll into the hold-value cluster, which the investor lens weights at 30% to produce the grade.
Note the weighting (30%) and the cluster structure don't change — we're replacing what feeds the two dead dimensions, not re-architecting the engine.
It'd be easy to shrink the investor read down to just a "floor" and give up on the appreciation dream. That would be underselling what part-out value actually is.
When a set's individual parts sell for more than the sealed box, that's a sign of high component demand — and historically those are exactly the sets whose sealed price climbs after retail supply dries up. So the same number answers both investor questions: it's a downside floor (your money is backed by bricks) and a legitimate, community-standard proxy for upside. We don't need a crystal ball to say something true here.
The only thing part-out alone can't see is timing — a set only appreciates after it leaves shelves. We can't read the retirement flag (it's empty), but we can read release year, and age is an honest proxy: a 2019 set is almost certainly retired and into its appreciation window; a 2025 set almost certainly isn't yet. That's a transparent heuristic we attach confidence to — not a fabricated number.
We rebuild the two dead hold-value dimensions with live inputs. The cluster and the 30% lens weight stay exactly as they are.
Replaces the dead resale dimension. It's the part-out premium: the summed BrickLink part value ÷ the set's price, mapped to 0–10. Roughly: parts worth ~1× the price is thin protection (low), ~1.75× is a healthy floor (mid), 2.5×+ is strong, 3.5×+ exceptional. This is the "if it flopped tomorrow, the bricks still back $X" read — and it varies from 0.05× to 6.35× across the catalog, so it actually separates sets.
Replaces the dead scarcity dimension. Derived from the set's age (today − release year): a brand-new set is early in its life (upside not proven yet → lower, low-confidence); a set several years past release is likely retired (appreciation window open → higher). Where the old exclusivity/retired bonuses were meant to live, this puts a real, always-available signal.
High part-out floor and likely-retired = the genuine "this holds and probably grows" set (a real investor buy). High floor but brand-new = great safety net, upside "too early to call." Low floor = weak on both, and we say so. The two dimensions split the holdValue cluster 50/50, exactly as the structure already expects.
This isn't aspirational — it's grounded in a live count across the 3,639 graded sets:
| Field | Coverage | Verdict |
|---|---|---|
| part-out value | 3,579 / 3,639 · 98% | The backbone of the new signal — nearly universal. |
| street price | 2,861 · 79% | Price anchor for the floor ratio (falls back to MSRP). |
| MSRP | 2,416 · 66% | Secondary price anchor. |
| release year | present | Powers the retirement clock. |
| current value | 0 | Why real appreciation can't be measured directly (yet). |
| retired / exclusive flags | 0 | Why the old design was dead on arrival. |
Part-out premium isn't just present — it's expressive: it ranges from 0.05× (a postcard whose parts are worth almost nothing) to 6.35× (Alien Pizza Planet, parts worth 6× the box), and 69% of sets sit at 1.0× or above. That's exactly the kind of spread a real grade needs.
One honest wrinkle: part-out value is already used — inside Smart Price — so we have to make sure the investor read isn't just Smart Price in a different shirt.
Same raw ingredient, genuinely different question — and the investor read brings in a time dimension (age/retirement) that Smart Price doesn't have. A set can absolutely be a great deal and a great store of value; that's not redundancy, that's two true things. We just make sure the words on each read stay distinct.
The whole point is to stop dressing up "we don't know" as a confident grade, so confidence degrades gracefully:
src/lib/facts/factScores.ts: resaleScore → part-out-premium curve; scarcityScore → age/retirement curve. New anchor constants in constants.ts.factInputs.ts passes part-out value, price, and year into the two scorers (it already has them in hand at grade time).The two core dimensions above (Value Floor + Retirement Clock) are definitely in. The open question is whether to add a third ingredient — theme desirability (Star Wars / UCS / Ideas tend to appreciate more than generic City sets). It's a richer, more "investor-real" signal — but it's a curated opinion (a hand-ranked theme tier list), where the other two are computed from hard data. We can ship the honest two-signal version now and add desirability later, or bake it in from the start.