---
name: trench-model-3
description: Price Predict part 3, from 14 Sep 2026 05:30 UTC after trench-model-2 neared its cap. The birthday investigation, the correction to the leak alarm, and the mixed-population sample found inside the only learned model. Read with trench-model-2.
sources: [cowork]
aliases: [trench model part 3, price predict 3, late tokens, population rule, birthday bug]
---
Continues [[trench-model-2]]. Everything there still applies.

# THE BIRTHDAY INVESTIGATION, 14 Sep 2026. `/opt/trench/birthday.py`

## FIRST, A CORRECTION TO WHAT I TOLD BEN
I said the wrong `created_ts` might be flattering winners across the whole model. **That was wrong and the number that misled me was 19%.** The real figure is 12.9% of tokens with a measured peak, and far more importantly:

**Every single token with a buy recorded before its own launch is `late=1`. Zero watched tokens have the problem.**

| group | tokens | reached $1M | died |
|---|---|---|---|
| watched (late=0), no pre-launch buy | 474 | **0.2%** | 57.2% |
| late-discovered, no pre-launch buy | 195 | 10.3% | 60.5% |
| late-discovered, pre-launch buy | 156 | **53.8%** | **0.0%** |

The live trainer already excludes `late=1`, so **the live training set was never contaminated**. `open_timestamp` is not a fix either: it is present on only 102 tokens and always sits AFTER `created_ts` by weeks, so it is a later event, not the birthday.

## AND THE SAME CORRECTION APPLIES TO THE WALLET TEST
The 11x leader/follower result was late-token contamination, twice over: `lead_test.py` did not filter `late=1`, and that group is 53.8% winners with a zero death rate by construction. Re-run clean (`lead_test2.py`, watched tokens only, bar dropped to $100k because the clean sample holds exactly ONE token above $1M):

| composition, 3 to 12 scored wallets in by minute 5 | tokens | cleared $100k |
|---|---|---|
| originators beat followers | 136 | 5.1% |
| followers beat originators | 43 | 2.3% |
| no originator present | 5 | 0.0% |

**No usable separation on clean data.** Crowd size itself reads non-monotonically (29.7%, 4.7%, 3.0%, 11.5%), which is what noise looks like. LEAD_FEATS stays built and unwired. The binding constraint is winners in the clean sample: 412 watched tokens with a scored wallet, about 35 above $100k, one above $1M.

# THE REAL FIND: THE ONLY LEARNED MODEL WAS FITTED ON TWO POPULATIONS
`recon_training_rows` had no `late` filter, so **42.6% of reconstructed training rows came from late-discovered tokens, carrying nearly all the winners**:

| age 6h | rows | reached $1M |
|---|---|---|
| watched | 182 | **1** |
| late-discovered | 253 | **131** |

A late token is in the database BECAUSE it ran: it was found on a rank list sorted by size. So the sample was winners from one population and losers from another, and **`rc-run-r21600` at brier 0.1433, our best-looking model, was mostly learning to tell the two populations apart.** `prior_shift` cannot fix this: it corrects the level from the recorded sample rate, not a feature acting as a proxy for which population a row came from, and `pick_res` guarantees one exists (older tokens get coarser candles, coarser means older means late means winner).

**patch80: the reconstructed trainer now uses watched tokens only.** Cost: the recon run models no longer fit (7 positives in 75 rows at 5m, 4 in 56 at 15m). That is the honest state, and cheap, because the scoreboard already showed rules-v0 carrying every live call while the recon champion had scored three. Three `rc-` champions demoted. Live rug models `lr-rug-1h` and `lr-rug-6h`, fitted on watched data, remain.
Late rows are NOT deleted: they are the right sample for peak distributions and base rates, which is what `peak_dist` and `pyramid.py` already use them for.

**patch81/82: `population_check` in the gate.** Seventh appearance of one error (a sample assembled from whatever was in front of me, read as if it described the world), so it gets code. It asserts both trainers carry their late filters and that no champion fitted on the mixed sample is still live, and it prints the watched/late split per age. **Written twice**: the first version called the trainers and pushed the gate past 3 minutes, so it was rewritten as counts. **A gate nobody runs is worse than no gate.**
Gate now passes end to end in 80s; the time is SQLite lock contention with the live service, not the checks.

# WHERE PRICE PREDICT ACTUALLY STANDS, 14 Sep 2026 05:45 UTC
- **One validated signal: attention.** The GMGN eye count separates a $100k+ token from a dud by about 50x at minute two, measured on watched tokens only. 38 velocity features live, backfilled, gated against leakage.
- **No validated wallet signal yet.** Built, tested, not shipped.
- **No learned run model.** By choice. Two rug models, both honest.
- **The scoreboard refuses a verdict**: 4 winning tokens in hard mode against a required 10.
- **The binding constraint on everything is winners in the watched sample.**

# CORRECTION, SAME DAY: COVERAGE IS NOT THE PROBLEM. `/opt/trench/coverage.py`
- [stated] Ben asked: "What if we opened it up to Solana as well? Should watch the 3,200 instead of the 1,300 that you mentioned. I assume this is Robinhood only."

I told Ben to raise collector coverage because we watch ~1,322 launches a day against ~3,241 that happen. **The 3,241 was stale.** It came from extrapolating one 2.51-hour window days earlier. Measured properly:

| window | tokens born | first seen within 5 min | median lag |
|---|---|---|---|
| last 6h | 465 | **85%** | **8 seconds** |
| last 24h | 1,335 | 72% | 10 seconds |

**We are not missing launches. Robinhood Chain is simply launching about 1,335 a day now, not 3,241.** The 24h figure is lower only because `risers` back-fills older tokens with a long lag, which is discovery working, not collection failing. So there is no coverage win to be had, and the winner shortage is a property of the chain, not of our pipe.

## SOLANA: PROBED, AND IT IS THE REAL ANSWER
One rate-limited call to `gmgn.trenches("sol")` (the client is already chain-parameterised: `trenches`, `token_info`, `token_security`, `kline`, `rank` all take `chain`).
- Works. 180 rows in three columns, same shape as Robinhood. **90 fields against 100.**
- **89 of those 180 rows launched within the last TEN MINUTES.** Roughly ten times the Robinhood launch rate, which is exactly the constraint we are stuck on.
- `is_honeypot` is absent. `visiting_count` is present but **sparse and far smaller: populated on 67 of 180 rows, maximum 84, against 985 on Robinhood.** So the one signal we have validated may not transfer, and must be re-measured on Solana rather than assumed.
- Launchpad mix is different (`meteora_virtual_curve`, pump.fun), so `launchpad` becomes a much more informative feature there than here.
- Rate limit is not the obstacle: current usage is roughly 60k calls a day against a 20/s budget of about 1.7M.

**The design rule, and it is the same one that just cost us a model: never pool chains.** A model fitted on Robinhood and Solana rows together can win by telling the two chains apart, which is the mixed-population error in a new costume. Separate models, separate base rates, separate pyramids, separate wallet reputation (addresses do not cross chains).

# THE SOLANA LANE IS LIVE. 14 Sep 2026 06:00 UTC. Patches 83-86.
- [stated] Ben: "Okay, build it."

## ONE CODEBASE, TWO DATABASES, TWO SERVICES
Not a `chain` column: **two separate sqlite files**, because pooling populations is the error this project keeps making and a second file makes it impossible rather than merely discouraged. Not two copies of the code either, so a fix still lands on both at once.
- **`/opt/trench/cfg.py`** is the only place that says which chain a process is. `TRENCH_CHAIN`, `TRENCH_DB`, `TRENCH_HTML` from the environment, Robinhood defaults, plus `NAME`, `RO`, `EVM`, `RATES_MEASURED` and `addr()`.
- **`trench-sol.service`**: same `/opt/trench/trench.py`, `TRENCH_CHAIN=sol`, `TRENCH_DB=/opt/trench-sol/trench.db`, `MemoryMax=300M`, `Nice=10`. Both instances share `/opt/common/gmgnlimit`, so the rate limiter is one bucket and neither can get the IP banned.
- Patched off hardcoded values: `store.DB`, `trench.CHAIN`, `rebuild.kline`, `wallets.top_traders`, `audit`, `peek`.
- Memory is fine: trench 241MB cgroup, **trench-sol 36MB**, 385MB free. The box has 4GB of swap, 3.5GB unused.

## THE BUG THAT WOULD HAVE MADE THE WHOLE LANE WORTHLESS
The collector stored `(r.get("address") or "").lower()`. On EVM that is required (hex is case-insensitive, and mixed-case checksums would split one token into two rows). **On Solana it is fatal: base58 is CASE-SENSITIVE, so a lowercased address is not the token.**
Caught within minutes by asking rather than assuming: a kline call with the stored address returned **0 candles, and raised nothing.** The labeller would have fallen back to our own observations, reconstruction would have found no candles, and the lane would have collected data it could never label. **Same silent-zero shape as the dead Tech Score column and the frozen volume slope, for the third time.**
`cfg.addr()` now decides by chain and every normalising call site goes through it; the EVM infra denylist is skipped entirely off-EVM. The Solana database was wiped and restarted clean, because 474 tokens with destroyed addresses are worth less than nothing.

## A CHAIN WITH NO MEASURED ODDS DOES NOT PRINT ODDS (patch84)
`TRUE_RATE` and the pyramid were measured on Robinhood Chain and describe Robinhood Chain only. `strength()` divides by `TRUE_RATE[100000]`, so running Solana on borrowed rates would print a confident tier for a chain nobody has measured. `bucket()` now returns **"could run"** and stops there when `cfg.RATES_MEASURED` is false. Ranking is unaffected, and the self-evident market-cap tiers still apply (a token already at $8M is still called 5-50M). Verified live on the Solana board: 107 no run, 23 scam, 21 could run, and the two mcap tiers, with nothing claimed above them.

## FIRST NUMBERS FROM THE SOLANA LANE
- **About 18,450 launches a day**, against 1,335 on Robinhood Chain. **Roughly fourteen times the flow**, which is the exact constraint every validation was stuck on.
- Collecting cleanly: 487 tokens, 3,958 observations, 153 snapshots and predictions in the first four minutes, no errors.
- The "seen within 5 minutes" figure reads low on day one only because the opening board load includes the near-completion and completed columns, which are full of older tokens. Re-measure once that backlog washes through.
- `is_honeypot` is absent on Solana and `visiting_count` is sparse and much smaller (max 84 against 985). **The attention finding must be re-measured there before it is believed.**

## GATE IS NOW 74 CHECKS, ALL PASSING
New: an EVM address is lowercased, a base58 address survives unchanged, Solana is not treated as EVM, an unmeasured chain is marked unmeasured, `bucket()` withholds the tier for one, and the check restores its own instance's chain afterwards.

## BOTH LANES VERIFIED, `/opt/trench/bothcheck.py`
One read-only script that checks each instance against the same list and flags what is true on one chain and not the other. **BOTH LANES CLEAN.**
- Robinhood: 1,838 tokens, 673,996 observations, 4,048 snapshots, 77,309 roster rows, 3,111 recon rows. 188MB.
- Solana at 20 minutes old: 573 tokens, 9,717 observations, 324 snapshots, 2,297 roster rows. 45MB.
- Vectors are 231 long on both. Addresses correct on both (400 of 400 Solana addresses carry capitals, none look like hex).
- **No 429s since the Solana lane started.** The last rate limiting was 13 Sep evening. Two instances on one shared bucket is comfortable.
- **The infra-wallet problem solved itself on Solana**, which is the argument for generic rules over hardcoded lists: `is_trader`'s `MIN_BUY_USD = 25` already excludes the program accounts (`BwWK17cbHxwWBKZkUYvzxLcNQ1YVyaFezduWbtm2de6s` appears in 18 tokens with $0 bought, is in `wal`, and is correctly absent from `walrep`). **61% of Solana roster rows are under $25**, against a much smaller share on Robinhood, so that filter is doing far more work there.
- Solana `outcome` is still 0 and that is correct: its oldest snapshot is 9 minutes old and nothing can be settled yet.
- **The eye is weak on Solana as feared**: max 18 across 200 recent snapshots against 365 on Robinhood in the same window. The attention finding probably does not transfer, and must be re-measured rather than assumed.

## THE IMPROVEMENT PLAN, proposed 14 Sep 2026 06:15 UTC
1. **Measure Solana's own pyramid** so it can print real tiers instead of stopping at "could run". Existing `pyramid.py`, chain parameter, one run.
2. **THE FREE EXPERIMENT, and the best idea in this list: fit on Solana, TEST on Robinhood.** We refuse to pool chains for fitting, and that stands. But a model fitted on Solana's enormous sample can be SCORED against Robinhood tokens without training on them. If it ranks above chance, the physics generalise and our effective sample just grew fourteen-fold. If it does not, we have learned the chains are genuinely different objects. Both answers are worth having and it costs one scoring run.
3. **Narrative features: which words are winning right now.** The largest untouched axis, and it is Ben's own framework (his DD process says that on a meme the name and lore ARE the product). A rolling table of name and ticker terms weighted by recent outcomes, then score each new launch by overlap. This is the meta idea made concrete and it is chain-specific by nature.
4. **Wire the Tech Score categoriser (`kinds.py`) into Price Predict** and finally run the test he asked for days ago: category hit rate BY TIME WINDOW.
5. **Re-measure attention on Solana** after a day of data, and drop it there if it does not hold.
6. **Solana report page and scoreboard**, so the second lane is visible rather than only in the database.
7. **Predict the SHAPE, not just the peak.** A second target, time-to-peak, turns the board from a buy signal into a buy-and-exit plan. Same data, no new collection.
8. **Held back on purpose: a stronger model class.** Gradient boosting would capture interactions a logistic regression cannot, but models are not the bottleneck, data was, and the scoreboard has not yet shown the linear model beating chance. Revisit only after it does.

# ITEMS 1-6 EXECUTED, 14 Sep 2026 06:10-06:45 UTC. Patches 87-91.
- [stated] Ben: "Execute all of these... the category tagger is now up and running from Tech Society... Make sure everything is correct when you do it. Double-check."

## 1. SOLANA'S OWN PYRAMID, MEASURED (`pyramid.py`, now chain-parameterised)
| out of every launch | Robinhood | **Solana** |
|---|---|---|
| launches in 30 days | ~40,000 | **~591,000** |
| reaches $100k | 1 in 227 | **1 in 2,181** |
| reaches $1M | 1 in 409 | 1 in 3,457 |
| reaches $5M | 1 in 726 | 1 in 6,094 |
| reaches $50M | 1 in 4,420 | 1 in 32,840 |
| of those reaching $100k, go on to $1M | 56% | **63%** |
**Solana is roughly ten times harder per launch and has fourteen times the launches**, so slightly more winners a day in absolute terms, and the SHAPE above $100k is nearly identical (63% against 56%), which is the first hint that the physics may be the same everywhere. **Caveat, and it is much stronger here**: the rank list surfaces the biggest first, and on a chain with 591,000 launches it sees a tiny slice, so Solana's bottom rows are floors with far less confidence than Robinhood's.

## 3. THE NAME IS NOW A FEATURE, and it earned it (`narrative.py`, 10 columns)
Terms from each token's name and symbol, scored by what the tokens carrying them actually did, written per day from tokens launched BEFORE that day. Structural stop words only: "cat", "dog" and "inu" are exactly the references worth measuring, and `meta.py`'s old stop list threw them away.
**First run was contaminated and I caught it**: without the `late=0` filter, "robinhood" read 74% and "strategy" 70%, which is the discovery method talking, not a narrative. Watched tokens only now.
Clean term table reads sensibly: **openai 50%, alien 43%, anthropic 33%, claude 25%** against **cat 12.5%, dog 11%, inu 9%, fun 0%**. The AI cluster is working and the animal memes are not.
**The test, before wiring** (951 watched tokens, base rate 10.5% clear $100k):

| best term's historical rate | tokens | cleared | vs base |
|---|---|---|---|
| unknown | 810 | 8.8% | 0.8x |
| 0.10 to 0.25 | 45 | **2.2%** | 0.2x |
| 0.25 and up | 82 | **34.1%** | **3.2x** |
| top 27 by that rate | 27 | **51.9%** | **4.9x** |

Also: a symbol already used by exactly one earlier coin cleared 30.3% (2.9x). **And note the BEND**: the middle band is worse than unknown. The relationship is not monotone, so a logistic regression will under-use it. That is the first concrete argument for a model class that can express a curve, recorded rather than acted on.

## 4. THE CATEGORY, AND THE TIME HALF (`kindstat.py`, 8 columns)
Tech Score's tagger is live and answered for **ten of twelve** recent trench tokens (pure-meme, gaming, dex, rwa). `stars_client` was already asking it about every token and throwing away everything except the star count; it now keeps `shape` and `category` in a `kindtag` table. `kindstat` turns those into the category's all-time rate, its rate in the last day, and **the ratio between them, which is Ben's meta question in one number**.
Shape is three flags, never one ordered number, because meme and tech are kinds and not degrees. An untagged token reads `kind_known=0`, so "no data" can never be mistaken for "meme".
**Coverage is currently thin** (4 tokens tagged and climbing): the tagger only answers for tokens with a site or X, and older tokens were never scanned. It fills forward. `audit.py`'s dead-column check is the safety net if it ever stops.

## 2 and 5. BUILT, SCHEDULED, CANNOT RUN YET, and that is stated rather than hidden
- **`crosschain.py`**: fit on one chain, SCORE the other. Not pooling, which stays forbidden. It ran and correctly reported "not enough to fit on sol": the Solana lane is an hour old and has 2 settled outcome rows. Robinhood already has 472 to 582 test rows per band waiting.
- **Solana attention re-measure**: `attn_test.py` is chain-aware now. The eye there is weak as feared, max 32 against 365 on Robinhood in the same window.
- Both run nightly at 07:41 UTC via `/opt/trench/daily.sh` into `daily.out`, so the answers arrive without anyone asking.

## 6. TWO BOARDS, TWO SCOREBOARDS
`report.py` wrote its HTML and `stats.json` to hardcoded paths, so **the two instances would have fought over one file and the second to run would have silently overwritten the first**. Now per chain. `scoreall.sh` scores both lanes hourly, `rollup.sh` rebuilds terms, wallet lead scores and category rates on both hourly, both via cron.

## FEATS 231 -> 241 -> 249 IN ONE HOUR, AND THE FILL THAT MAKES THAT SAFE
Each growth would silently discard the entire stored sample. `tailfill.py` replaces the one-off backfills: it works out which trailing blocks a short row is missing and rebuilds only those, **each at that row's own cutoff**, and is safe to re-run. 3,571 Robinhood and 991 Solana snapshots topped up, in about a second each time. 537 very old Robinhood rows (120 and 168 long, pre-dating the info and wallet blocks) remain unreachable and were already dead before today.

# BEN'S RELAY THEORY IS THE BEST RESULT OF THE PROJECT SO FAR. 14 Sep 2026, patch97.
- [stated] Ben: "There might be some type of a chain reaction that happens in successful coins. Maybe we get a lot of attention in the beginning, then certain smart wallets buy in, then it's spreading to socials, and so on... and this chain might break, right? If we can identify this chain, then we can predict things better."

**He is right, and it is the cleanest ladder anything in this project has produced.** 725 watched tokens, first fifteen minutes, base rate 1.4% clear $100k:

| how far the chain got | tokens | cleared $100k | vs base | died |
|---|---|---|---|---|
| 0, nobody looked | 540 | **0.0%** | 0.0x | **95.9%** |
| 1, eyes only | 124 | 0.8% | 0.6x | 76.6% |
| 2, eyes + smart money | 57 | **12.3%** | **8.9x** | 21.1% |
| 3, + social | 4 | **50.0%** | **36.2x** | **0.0%** |

**Monotone on the hit rate AND on the death rate**, which is what a real relay looks like and what no single feature has managed. Ten winners in the sample and only four tokens at depth 3, so the top rung is thin, but the shape is not ambiguous.

## THE STAGES, and why the ORDER is the whole point
```
S1 EYES    the page gets busy relative to everything launching beside it
S2 SMART   smart money arrives AFTER the eyes
S3 SOCIAL  callouts start AFTER the smart money
S4 CROWD   holders accelerate AFTER the callouts
```
Each must fire after the one before. The model already reads all four as levels and learns almost nothing from them; a relay is a SEQUENCE, and that is the information the levels throw away.

## WHY THIS ONE IS CLEAN WHEN THE WALLET VERSION WAS NOT
These stages come from **`obs`, our own fixed-interval poll of every token**, not from `token_top_traders`. There is no top-100 truncation, so arrival shape means what it appears to mean. **That single distinction is why this works and the roster-based `pull` measure did not**, and it is the practical form of the roster-artifact rule below: our own time series is safe, someone else's ranked snapshot is not.

## THE BY-PRODUCT IS WORTH AS MUCH AS THE FEATURE
`relay.why(depth)` puts **where it broke** on the board, live now:
- "nobody ever looked at it"
- "people looked, no smart money followed"
- "smart money came, it never reached social"
- "social picked it up, the crowd never arrived"
That turns "no run" into a reason Ben can act on, which is the actual product goal (a fast buy or skip with something behind it). Confirmed live in `pred.why` on the Robinhood board within minutes of shipping.

10 features (`RELAY_FEATS`: depth, the four stage flags, minutes to stage 1, the gaps between stages, how long it has been stalled, has_data), appended at the END of FEATS per the rule. **FEATS 265.** `eyefill`'s window widened from 15 minutes to an hour because in the 15-minute test **stage 4 never fired once**, so the ladder could not reach its top rung.

## WHAT THIS CHANGES ABOUT THE PLAN
The relay is a better organising idea than "more features". Next, in order: (1) let stage 4 fire by measuring over the full hour now that the columns are filled, (2) re-run the ladder on Solana once it has outcomes, (3) tune each stage's threshold against outcomes rather than the round numbers used here (3x median, +2 smart, +1 callout, 1.5x holders), (4) make the board's headline the DEPTH rather than a probability, because a depth is legible and a calibrated probability on four winners is not.

# THE SOCIAL-CAPITAL IDEA, TESTED. 14 Sep 2026. `/opt/trench/pull_test.py`
Ben shared @spacepixel's thread ("Token Social Capital = holder capital + 0.5x 1st-degree + 0.25x 2nd-degree... compare Social Capital / Market Cap") and proposed the on-chain version: "we might have wallets that are being tailed by other people. So if one wallet buys, many other wallets buy, so we can track these chain reactions."

**Right idea, and our data cannot measure it. Here is exactly why, and it is a rule worth keeping.**

Measured `pull(A)` = entrants in the ten minutes after A enters, minus what that token was doing anyway, per wallet per day, watched tokens only, pre-launch entries dropped. Result was inverted and the control killed it:

| strongest puller present | tokens | cleared $100k |
|---|---|---|
| 0 to 1 (weak) | 79 | **48.1%** |
| 3 and up (strong) | 783 | **3.8%** |
| control, same crowd size | 255 vs 10 | 1.6% vs 0.0%, 4 winners total |

## THE ROSTER ARTIFACT, and it has now cost three features
`token_top_traders` returns the **top 100 BY VOLUME as of the pull**, not everyone. For a token that lived for hours those 100 are spread across hours; for a token that died in three minutes all 100 sit inside three minutes. **So any feature built on the SHAPE of roster arrival times is partly measuring how long the token lived, which is the outcome.**
High pull = clustered arrivals = short life = dead. Low pull = spread arrivals = survivor. The 48% was the artifact, not the signal.

**THE RULE: from the roster, MEMBERSHIP is safe (who is in by minute five) and a wallet's CROSS-TOKEN history is safe. WITHIN-TOKEN ARRIVAL SHAPE IS NOT.** Already burnt: `ld_cascade_s` (the 600-second bucket that read 66.7% $1M), this pull measure, and **`w_arrival_accel` in WALLET_FEATS is the same shape and should be re-checked or dropped.**

## WHAT WOULD MEASURE IT PROPERLY
Not the roster: **every swap in order**, which means Dune (key owned, `robinhood.transactions`, `uniswap_v4_robinhood`, and Solana all indexed, standing query id 8562361). One SQL statement gives the full ordered buy list for thousands of tokens, and a chain reaction is then measurable without the truncation. Bigger job, correct answer, and it is the only route that makes the wallet influence question answerable at all.

## FOMO: CHECKED, NOT ASSUMED. No public API.
- `prod-api.fomo.family` returns **430 on every path** including `/ping`, an auth wall.
- On the main site every route returns the **same 9,254-byte SPA shell**: `/api/tokens`, `/api/token/solana`, `/api/profile/<handle>`, `/api/trpc/tokens`, `/api/leaderboard`, `/api/users`, `/api/graph` are all the HTML page, not JSON.
So the fomo social graph is reachable only one rendered page at a time through the Mac browser, which is fine for a single profile read and useless for a graph.
**The owned substitute is X**: twitterapi.io gives the follower count of every account posting a contract address, so "capital socially close but not yet bought" becomes "big accounts posting it while the cap is still small", from a source we already pay for.

## AND ONE PUSH BACK ON THE ORIGINAL THREAD
Social Capital / Market Cap is a valuation screen, and it needs a real denominator. At minute ten every launch is worth about $4k, so the ratio is dominated by noise in the numerator and says nothing. The idea earns its keep as a PREDICTION feature on a fresh launch, and as a valuation ratio only on something already sized, where market cap means something.

# BEN WAS RIGHT ABOUT THE EYE, AND THE FIX IMPROVES BOTH CHAINS. Patches 92-96.
- [stated] Ben: "there are just way more launches, and so there are going to be fewer eyes on each individual thing... I would assume that it's just proportional... things that have a lot of potential are going to break through and maybe get more attention at some point."

**He was right, and the absolute count was the wrong feature on EVERY chain, not only Solana.** Measured on Robinhood, 730 watched tokens judged in their first ten minutes, base rate 1.8% clear $100k:

| the eye, measured how | tokens | cleared | vs base |
|---|---|---|---|
| absolute count 50 to 200 (best absolute bucket) | 36 | 8.3% | 4.7x |
| **top 5% of the SAME HOUR's field** | 32 | **28.1%** | **15.8x** |
| 10x the hour's median or more | 76 | 14.5% | 8.1x |

AUC: percentile 0.896, median-multiple 0.884, absolute 0.881. Close on AUC but **three times the lift at the top**, and the top is what gets traded. The percentile asks the question that actually matters, is anyone looking at THIS one rather than the others beside it, and **it means the same thing on a chain with 1,335 launches a day and one with 19,700**. 13 winners in the sample, so thin, but the direction is unambiguous.
Six `REL` features added: eye percentile, eye as a multiple of the hour's median, peak-eye percentile, smart-money percentile, callout percentile, and the field size so a thin field can be discounted. Live on both lanes, nonzero on about a quarter of recent snapshots, max 1.00 on Robinhood and 0.99 on Solana.

## HIS OTHER QUESTION, CHECKED: IS THERE A BETTER SOLANA COUNTER?
**pump.fun's own `reply_count` is dead on new coins.** Every coin launched in the last day reads 0; only coins from a year or more ago carry one (Fartcoin 14,017, ALON 2,964, BILLY 1,658). The comment section is not where Solana attention lives any more. So there is no better launch-page counter to switch to, and the relative eye is the Solana answer.

## TWO MISTAKES I MADE AND CAUGHT WITHIN MINUTES
1. **I broke my own append-only rule.** The six relative features first went INSIDE the attention block, which now sits in the middle of FEATS. That shifts every later column, so 3,571 stored vectors would have had their narrative and category values read at the wrong indices, silently, because a stored vector is read BY POSITION. Moved to its own block at the END. **Gate check added: the relative block is at the end, never inserted.**
2. **The field query was ruinously expensive.** It scanned an hour of `obs` and decompressed every payload, once per five-minute bucket: fine live, hundreds of millions of rows across a backfill. It stalled tailfill, and I stacked five copies of it before noticing. Fixed properly: `obs` now denormalises `eye`, `smart` and `call` alongside the mcap and holder columns it already kept, so the field is one indexed query with no decompression. `eyefill.py` backfilled 23,941 Robinhood and 13,253 Solana young observations in under a minute each.

## THE GATE CAUGHT ITS OWN STALENESS, TWICE
"the attention block is the TAIL of FEATS" went red, correctly: narrative and category were appended after it. The invariant that actually matters is that it kept its POSITION, because models fitted while it was last read those columns by index. Check rewritten. **Gate is now 89 checks and all pass**; `bothcheck.py` reports BOTH LANES CLEAN with 249-length vectors on both.

# THE RELAY, RE-RUN OVER THE FULL HOUR, AND STRESS TESTED. 14 Sep 2026, patches 98-101.

## THE LADDER HOLDS AT EVERY WINDOW (RH, watched tokens only)
| window | tokens | base | d0 nobody looked | d1 eyes | d2 + smart | d3 + social |
|---|---|---|---|---|---|---|
| 15 min | 730 | 1.4% | **0.0%** | 0.8% | 12.3% | 50.0% (4 tokens) |
| 30 min | 736 | 2.0% | **0.0%** | 1.6% | 15.0% | 66.7% (6 tokens) |
| 60 min | 740 | 2.2% | **0.0%** | 2.4% | 12.1% | **75.0% (8 tokens, 34.7x)** |

## THE STRONGEST CHECK: IT IS NOT A THRESHOLD ARTIFACT
Swept eye 2x/3x/4x/6x median, smart +1/+2/+3, callouts +1/+2. **All 24 combinations are monotone, and depth 0 is 0.0% in every single one** (470 to 635 tokens, zero winners). The relay is a property of the data, not of the constants picked.

## STAGE 4 CANNOT FIRE, AND THE REASON IS DATA NOT TIME
Checked the eight tokens that reach depth 3: after the social handoff the best holder growth inside the hour is **1.32x** and most SHRINK. `HOLD_X = 1.5` is unreachable, and even a bare 1.0x splits 2 of 8. **The crowd arrives later than we watch.** Depth 3 is the top rung we can honestly measure, and `BROKE[3]` now reads "social picked it up: the whole chain fired" rather than blaming a crowd that never had time to show up.

## patch99: THE BACKFILL WAS GOING TO TAKE TWENTY HOURS
`relay.field_median` is a grouped scan of `obs` joined to `tok` over an hour, and it ran **once per stored row**. Measured: **2 rows per 45 seconds**. The median of an hour of launches does not move inside that hour, so it is memoised by hour bucket. **0.044 to 25.4 rows a second, a 575x speedup**; RH finished 3,267 rows in 68 seconds and Solana 3,947 in 16. Bucketing also makes the live path and the backfill agree by construction. **Every snapshot on both chains is now at length 265** (537 ancient RH rows of length 120/168 remain dead, as they already were).

## patch98: `late=0` WAS NOT ENOUGH ON ITS OWN
A token first SEEN at minute forty still carries a "15m" snapshot built from numbers that did not exist when that band went past. **I first reported this as 15% of winner rows and that was wrong**: I measured against a flat hour instead of each band's own age. Measured correctly it is **42 rows of 4,337 and one winner row of 86**. Small, but it is the same population bug that faked the 11x wallet result, so it is closed in SQL (`fs.first_ts - tk.created_ts <= s.age`) rather than remembered. Verified it removes nothing usable today: before and after row counts are identical.

## patch100: THE GATE BROKE FOR THE THIRD TIME THE SAME WAY, SO IT GOT CODE
Four position checks went red because each hardcoded its distance from the end of FEATS, and `tailfill.py` kept a second hand-maintained copy of the same block list. **`feats.TAIL_BLOCKS` is now the single source of truth**; the gate walks it and `tailfill` derives its BLOCKS from it. Appending a sixth block is one edit and nothing can drift. FEATS still 265, HEAD 193, order byte-identical.

## patch101: A RED LIGHT FOR A BUG THAT WAS NOT THERE
"the v4 PoolManager is not treated as a trader" failed on Solana because `wallets.is_trader` applies the INFRA list only when `cfg.EVM`, and those addresses cannot occur on Solana. Now chain-aware. The real gap is named in the gate output rather than hidden: **there is no Solana infra list** (pump.fun program, AMM vaults), so a Solana wallet score can still be plumbing. `MIN_BUY_USD` is doing that work for now.
**Both chains: all gate checks pass.**

## THE BOTTLENECK IS NOW WINNERS, MEASURED EXACTLY
No run model can fit on either chain, and the numbers say why precisely:

| band | live rows | cleared $100k | needed (MIN_SIDE) |
|---|---|---|---|
| 1m | 634 | **4** | 12 |
| 5m | 762 | **4** | 12 |
| 15m | 729 | **5** | 12 |
| 1h | 608 | **3** | 12 |
| 6h | 515 | **4** | 12 |

The whole RH snapshot table is **12.7 hours old**, so `ready_24h` is 0 everywhere and `horizon_for` is pinned at 6h for every band. That lengthens on its own as the table ages, which is the design working. Solana has **7 outcome rows**; its lane is younger still. **Nothing here is fixable with more features. It is a calendar problem, and the relay is the only thing that separates in the meantime.**

# BEN'S SECOND RELAY QUESTION, AND IT IS THE BETTER ONE. 14 Sep 2026. `handoff.py`, `handoff2.py`
- [stated] Ben: "Can we predict the NEXT handoff? Eyes plus tags, eyes plus type of project, eyes plus tech check. View plus something else, can it predict 2? Then 1 and 2 plus some other stuff can predict 3. We're trying to get as early as possible, so I can pull the trigger at the lowest market cap possible and ride it through stages 2, 3 and 4."

**He is right that this is the better question, and the reason is pure arithmetic: the final outcome has 16 positives in the whole database, the S1 to S2 handoff has 14 in a sample of 100 and grows four times faster.** A question with more positives can actually be answered.

**And I had it backwards in my previous reply.** I told Ben stage 4 "cannot be measured" as though it were a limitation. It is not: **stage 4 IS the outcome.** When the crowd arrives the price moves, and that move is the $100k we are predicting. Measuring it inside the watch window would be reading the answer, not predicting it. Stages 1 to 3 are the predictors, stage 4 is the prize.

## THE PRICE OF EACH RUNG. THIS IS THE WHOLE ANSWER TO "AS EARLY AS POSSIBLE".
| rung | tokens | median market cap | reach $100k |
|---|---|---|---|
| stage 1, eyes | 100 | **$23,558** | 7.0% |
| stage 2, smart money | 14 | **$25,220** | **35.7%** |
| stage 3, social | 5 | **$333,334** | 80.0% |

**Waiting from stage 1 to stage 2 costs 7% in price and multiplies the hit rate by five.** Stage 2 is the cheapest honest trigger there is. **Stage 3 costs 14x the entry**, because by the time callouts start the move has already happened: stage 3 is confirmation, not an entry.

## WHAT PREDICTS THE HANDOFF, read at the moment the eyes fire (base 14.0%)
| rule at stage 1 | tokens | reach stage 2 | lift |
|---|---|---|---|
| big eyes (>14) **+ any smart money already in** | 15 | **66.7%** | 4.8x |
| eyes 6x+ the hour's field + any smart in | 14 | 64.3% | 4.6x |
| busy (>12 swaps) + any smart in | 15 | 60.0% | 4.3x |
| any smart money already in | 26 | 46.2% | 3.3x |
| big eyes alone | 23 | 43.5% | 3.1x |
| fresh wallets present | 12 | 41.7% | 3.0x |
| a renowned wallet present | 13 | 38.5% | 2.8x |

**Checked for circularity and it is clean**: the S2 bar is `min(smart before t1) + 2`, so a token that already shows smart money faces the SAME bar, not an easier one. It is momentum, not definition.

## WHAT DOES NOT PREDICT IT, and this is worth as much
**Every static token attribute is noise at this handoff.** Website present reads 6.7% against 15.3% for no website; duplicate image, duplicate twitter, telegram, open source, renounced, launchpad, creation tool and the Tech Score category all land within a whisker of the base rate. **The handoff is decided by live flow (eyes, swaps, smart wallets, fresh wallets), not by what the project IS.** That is the opposite of what Ben guessed ("eyes + tags, eyes + type of project, eyes + tech check") and it should be said plainly rather than softened.

## HONEST LIMITS
14 tokens at stage 2 and 5 at stage 3, so the S2-to-S3 table (50% on 10 tokens) is not yet a result. The stage 1 combos rest on 15 tokens each. **Everything here needs the sample to grow before it is traded on**, and the S1 to S2 handoff is the one that will get there first.

# BEN CAUGHT ME MEASURING AN EMPTY COLUMN, TWICE. 14 Sep 2026. `handoff3-6.py`, `xpilot.py`
- [stated] Ben: "I'm very surprised that you say that a tech check doesn't improve that. It might be that our current tech check is just not good yet, but there's no way that a solid tech cannot predict that smart money will get into it later."

**He was right both times, and the second error was worse than the first.**
1. First test: the Tech Score category column is **5% filled**. I reported "the tech check does nothing" from 4 tagged tokens.
2. Second test: I re-read the raw GMGN payload out of `obs.j`, which is kept on only **15% of observations to save disk**. It silently dropped **23 of the 31 positives** and printed a table of zeros that looked like a finding.

**THE RULE, and it now belongs at the top of every sweep: PRINT COVERAGE BEFORE PRINTING RESULTS. An empty column is not a failed column.** `handoff3.py` prints fill rates first and flags how many of the 265 columns are under 5% filled (currently 102 of 265). Third test rebuilt from the 265-column stored vector, which is complete by construction.

## THE TECH CHECK STILL CANNOT BE ANSWERED, AND THE REASON IS STRUCTURAL
- Tech Score answered for **65 of 1,244** watched tokens. A backfill asked about 900 more and gained **3**.
- Only **209 of 751** launches have a website at all. **A tech check needs something to check, and 72% of trench launches have nothing.**
- So the tech check is not a minute-one trigger on this population by construction. It may well be right for project launches; it cannot be right for this game.

## AND THE PROXIES POINT THE OTHER WAY (751 tokens, base 3.7%)
| signal at minute one | tokens | smart money later | lift |
|---|---|---|---|
| **no socials at all** | 234 | **7.3%** | **1.95x** |
| no X | 261 | 6.5% | 1.75x |
| original X handle | 401 | 6.0% | 1.61x |
| **has a website** | 209 | **1.4%** | **0.38x** |
| has X | 490 | 2.2% | 0.60x |
| duplicate X handle | 350 | 1.1% | 0.31x |

**A polished launch is a NEGATIVE signal for the smart-money handoff on this chain.** Plausible mechanism: the website-and-X launches are serial-operator templates aimed at retail, while the wallets GMGN scores as smart hunt raw ones. Needs more data before it is traded on, but the direction is consistent across four separate columns.

## THE MINUTE-ONE TRIGGERS THAT DO WORK, all flow, none static (base 3.7%)
| rule at minute one | tokens | smart money later | lift |
|---|---|---|---|
| curve filling, top tenth | 34 | **41.2%** | **11.0x** |
| fresh wallets, top quarter | 30 | 36.7% | 9.8x |
| buys 5m, top tenth | 34 | 35.3% | 9.5x |
| swaps 5m, top tenth | 34 | 35.3% | 9.5x |
| smart wallets already in, top half | 42 | 33.3% | 8.9x |
| eye count, top tenth | 43 | 30.2% | 8.1x |
| **insiders suspected** | 19 | **26.3%** | **7.1x** |
| buying + curve both above median | 90 | 24.4% | 6.6x |
| ticker used by an earlier coin | 38 | 10.5% | 2.8x |
AUC at minute one: buys_5m 0.932, buyvol_5m 0.930, lp_progress 0.927, vol_5m 0.922, swaps_5m 0.921, visiting 0.900. **Twenty-eight positives, so these are directions, not calibrated numbers.**

# THE BIGGEST FIND OF THE DAY: X IS A MINUTE-ONE SIGNAL AND IT IS NOT IN GMGN. `xpilot.py`
Every one of the 265 columns comes from GMGN. X is the one owned source (twitterapi.io, key in the research-sources skill) carrying information GMGN cannot see. Pilot: 76 tokens where smart money arrived against 70 random tokens where nothing did, searched by **contract address**.

| | smart money came (76) | nothing came (70) |
|---|---|---|
| anyone ever posted the CA | **56.6%** | **1.4%** |
| posted within the first 10 minutes | **21.1%** | **0.0%** |
| a 5,000+ follower account posted it | 3.9% | 0.0% |

**16 of 16 tokens with an X post in their first ten minutes went on to get smart money. Zero of 70 controls had one.** The "ever posted" row is contaminated by posts made after the run and must not be used; the ten-minute row is causally clean.

**This is the next feature to build, and it is the first one in the whole project that adds information rather than re-cutting GMGN's.** Wiring: a background poller searching each new contract address once at minute one and once at minute five, storing post count, first-post age and max follower count, appended as a new tail block per `feats.TAIL_BLOCKS`. Cost is trivial (about $0.15 per 1,000 tweets).
