---
name: casig-gmgn-overlay-2
description: Tech Score stack part 2, from 14 Sep 2026 06:00 UTC after casig-gmgn-overlay.md neared its cap. The GitHub check, the categoriser rebuilt around the project's own words, the directory-site fix, the two re-rating loops, and the star-scale renaming. Read with casig-gmgn-overlay.md before touching /opt/casig.
sources: [cowork]
aliases: [casig part 2, tech score part 2, github check, categoriser v3, directory rule]
---
Continues [[casig-gmgn-overlay]], which neared its 48KB cap. Everything there still applies.

# ROBINA: A SITE THAT LISTS 194 CONTRACTS WAS CALLING TOKENS SCAMS
- [stated] Ben: "@RobinaFamily does link to 0x44792... The website itself does not have a contract address, and the website is also not linked to this Twitter account, so that might be the scam that's happening here."
**Counted it.** robina.family carries **194 distinct contract addresses**, 19 of the first 20 checked are real traded tokens. It is a DIRECTORY, not a project page. The red rule assumes a project site publishes ONE contract, so "publishes a DIFFERENT contract" meant "it published someone else's instead of yours" - on a directory the scanner published 194, picked whichever sorted first, and issued a **false SCAM verdict**, the most expensive mistake this system can make.
**THE RULE: a channel that publishes many real tokens is a LISTING, and a listing's silence about one of them proves nothing.** `DIRECTORY_AT = 3` in casig.py; at or above it the verdict is grey, never red.
Verdict now, which is exactly Ben's three facts in one sentence: *"not verified: the only account that published this CA is @robinafamily, which is the account this token itself links to; robina.family lists 20 different tokens and not this one, so it vouches for nothing; and the site does not link @robinafamily."* Nothing verifies it and nothing refutes it. **Ben's instinct that something is off is right, and "off" is not "proven scam": the scanner has to say which of the two it has.**

# TWO RE-RATING LOOPS, THE SECOND ONE CAUSED BY MY FIX FOR THE FIRST
- [stated] Ben: "GMGN Go takes forever to get a rating. It keeps blinking."
GMGNGO was rated **46 times in 20 minutes**, every one "no website on this token". Most of the board has no website, so this was consuming most of the scanner's capacity and it looked like slowness.
1. **`fresh()` refused any record with `stars is None` after 20s**, because None used to mean only "the star pass has not finished". Then stars became None for the opposite reason - THERE IS NOTHING TO SCORE, a final answer. Fixed: `star_unrated` set means finished.
2. **That was not enough, and the second cause was my own PAIREX guard from the same morning**: `if links and not (rec["site"] or rec["x"]): return None`. Written for "a record cached from a call with NO links, served to a caller who HAS a website". But "the record has no site and no handle" is not the same statement as "the record was built from no links": GMGNGO's card links are all third-party hosts, `channels()` correctly keeps none, so the record ends site="" x="" and every poll re-scanned it. Fixed by storing **`from_links`** on the record and re-scanning only on a strict superset. **Same fact, stated about the INPUTS rather than guessed from the outputs.**
**Third time in one day: a proxy for the thing you mean will eventually be true when the thing is false.** (The others: "empty shell" standing in for "no chain code", and a word count standing in for "somebody wrote copy".)

# SPEED, measured before and after
Before: p50 5.5s, p90 29s, p99 99s, worst 308s (zora.co). After: **p50 8.7s, p90 17.7s, max 22s**, and zero repeat ratings. Three changes:
- **Every site was fetched TWICE.** `casig.scan_site` downloaded the page, subpages and bundles for the dot; `stars.evidence` downloaded the same bytes again seconds later for the stars. `claimcheck` now has a URL-keyed fetch cache (`_FETCH_CACHE`, 10 min, capped at 60) and scan_site reads through it. **It must use `STARS.cc`, the same module object stars.py loaded** - importing claimcheck again would create a second instance with its own cache and change nothing.
- **`SCAN_BUDGET = 45s`** hard deadline on the chunk fetch; past it the scan stops and reports `got < found`, which the system already reads as PARTIAL, so a truncated scan is honest rather than wrong.
- The loop fixes above.

# THE GITHUB CHECK, `/opt/casig/github.py`
- [stated] Ben: "if the project has a GitHub and it's written anywhere on their page, the GitHub is checked."
Three API calls max, cached 12h by org, hard stop at 12 remaining of GitHub's 60/hour unauthenticated. Returns org, repos, last push, contributors, stars, languages, forks-only, and a verdict word: **active / quiet / cold / empty / forks only / dead link / none**.
- **IT MUST READ THE PAGE, NOT THE BUNDLE.** First run gave PAIREX and OPTIONSPAD both "active, zloirock, 10 repos, last push today" - zloirock is the author of **core-js**, whose name is in every bundle shipping a polyfill. A bundle names DEPENDENCIES; the page names the PROJECT. No blocklist is ever long enough; reading the right file is. (A `VENDOR_ORGS` list exists as a second line of defence.)
- **Absence is never scored.** Most working products here are closed source. An active repo LIFTS a 3 to a 4 when everything else holds; a missing or dead one is reported and costs nothing. Same doctrine as the dot: absence is never painted red.
- Verified: aerodrome.finance -> org `aerodrome-finance`, 6 repos, 120 stars, last push 47 days, 10 people, Solidity, in 0.95s.

# THE CATEGORISER REBUILT AROUND THE PROJECT'S OWN WORDS
- [stated] Ben: "this project didn't have a categorization. It's clearly visible what it is. You just need to skip the intro on his website, and then there's text there about what he's trying to accomplish." And: "I have a theory that the project category plays a role in how good it will perform. We need to do the categorization very well, so that it feeds accurate, precise, and fast data into Price Predict."
The receipt: fernwhisper.xyz has a literal **"Skip intro"** button, 70 visible words, and in its head `description: "A quieter way to follow the chain. Configure event rules, thresholds and quiet hours..."`. That is an ALERTS tool, stated plainly. It came back **unknown / unclassified**.
Three causes, three fixes:
1. **The focus zone.** Title, meta description, og:description and headings are written to answer "what is this"; everything else is prose that happens to contain words. `cc.page_meta()` + `cc.self_description()` extract them; a hit there is worth `FOCUS_WEIGHT = 3`, elsewhere 1, and `CATEGORY_SCORE = 2` so two body hits still win exactly as before. **Setting the threshold to 3 first silently dropped categories two body hits used to carry (PAIREX went launchpad -> unclassified); caught by re-running the cases.**
2. **The list was too short** - there was no alerts category at all. Now 37, plus whatever the learner adds. **An absent category looks exactly like an unclear project.**
3. **"Substantial" was a 250-word count**, so a site that says everything in 70 words was "unknown". Now: a clear self-description of 8+ words plus any real content counts as substantial.
- **The ticker is NOT in the focus text**: OPTIONSPAD classified as "derivatives" because its NAME contains "options". A name is not a statement of purpose.
- **Every rating now carries the project's own one-line description verbatim** (`summary`), shown in the popup and the tooltip. A label we assign is a guess about their words; their words are not a guess. This is the highest-value single addition for Ben's "what kind of project is it" question.
- Ledger gained `summary, gh_org, gh_repos, gh_last_push_days, gh_people, gh_stars, gh_verdict` so the category theory can be tested against outcomes.
Verified: FERN -> **alerts**, PROLOGUE -> dex, OPTIONSPAD -> launchpad, PAIREX -> launchpad, BOW -> lending, all with their own sentences.

# THE STAR SCALE'S WORDS
- [stated] Ben asked: "would it be okay to say that 3 stars is solid? 4 would be good, 5 very good?"
**Pushed back and he has not yet answered.** Measured: **21 of 31 mature projects score 3**, and the reason is almost always "a real site, but nothing working behind it, and it never names the token you would be buying". Calling that "solid" makes the single most common grade read as an endorsement on the exact band where the answer is "we could not find the product". Shipped instead:
`1 scam / 2 bad / 3 **unproven** / 4 solid / 5 something special` - 4 keeps "solid" because solid is what a 4 has to prove, and 3 gets an honest name. "Good" and "very good" are graded adjectives that say nothing about what changed between them.

# OPTIONSPAD 4 -> 3, honestly unresolved
- [stated] Ben: "I believe Options Pad was rated 4 stars yesterday and is rated 3 stars now. Why is that?"
**The ledger holds exactly ONE OPTIONSPAD rating ever** (14 Sep 05:40, 3 stars), because the ledger itself was only built that morning. So the 4 cannot be diffed and I will not invent a cause. What IS certain is why it is 3 today: optionspad.site ships **no wallet or chain library at all** (`wallet_code` empty) and never prints its own contract. Worth building: a `whychanged` tool that diffs two ledger rows for one CA and names the signal that moved - the columns are all there, nothing reads them yet.

# GATE 93 CHECKS, 0 FAILED. Deployed 2026-09-14T06:35:40Z.
New cases: the directory rule ordering, both loop conditions (a finished no-score record is served, a pending one is re-queued), a dependency author is never read as the project, the github check reads the page, one line of a project's own words classifies it, the ticker alone never sets the category, and the label wording.

# 14 Sep 2026 07:00. THE RUG STUDY, AND IT SAYS THE OPPOSITE OF WHAT PANIC LOOKS LIKE
- [stated] Ben, on PANIC (0x0ba6f765109e1ff0b37ab78fae80b22ea5208d66) raging then dying at ~$50k: "Just checking if there was a way to know this before it happened... You could also look at some other projects that rugged and see if there are any clear indicators before."
`/opt/casig/rugstudy.py`. **700 tokens read, 82 DRAINED (once traded above $12k, pool now under $1k), 126 ALIVE (pool still funded), 492 never traded enough to judge.**

| signal, all knowable at minute one | drained | alive | lift |
|---|---|---|---|
| **no smart money at all** | **68%** | **9%** | **7.8x** |
| dev sold out | 84% | 62% | 1.36x |
| 5+ sniper wallets | 51% | 42% | 1.22x |
| top ten hold 30%+ | 4% | 8% | 0.46x |
| dev still holds 10%+ | 1% | 2% | 0.51x |
| **5+ bundler wallets** | 13% | **43%** | **0.31x** |
| **deployed outside a launchpad** | 1% | **7%** | **0.17x** |

**ONE SIGNAL WORKS AND IT IS `wallet_tags_stat.smart_wallets == 0`.** PANIC had zero; NORA has 12; the older PANIC contract had 26.
**AND EVERY SIGNAL PANIC'S CARD SCREAMS IS BACKWARDS.** Its dev held 21%, 21 bundler wallets, deployed outside the pad, top ten at 34% - and bundling, no-pad and dev-hold are all MORE common among survivors. I was one commit from building rug detection on exactly the wrong features. **A story that fits one token is not a signal; the denominator is.**
CAVEAT kept attached: `smart_wallets` is read TODAY, not at minute five, so some of that 7.8x is smart money arriving because a token was already working. A rigorous version needs the value at snapshot time, which Price Predict's `snap` table already stores. Until then it is a strong hint, not a proven leading indicator.

# NORA SCORED 5 AND IT WAS MY OWN REGRESSION
- [stated] Ben: "Why was Nora scored as something special? I'm not saying it's not. It's probably not, though."
He is right. The ledger row: **4 visible words on the page**, 0 HTML routes, 0 buttons, 5,876 BUNDLE words, and a printed reason of "5880 words of its own copy... a deep app: 8 distinct routes and 5880 words". The day before I had made `routes` read the SPA's route literals from the bundle while `words` was already page + bundle, and the five-star branch is `routes >= 8 and words >= 2000`. **Every React app ships eight route strings and two thousand bundle words, so I did not widen the scanner's sight, I lowered the top grade.** Its self-description was a viem error: *"Failed to parse the decoded payload as JSON"*.
NORA is now **3 unproven**: a five-hour-old Robinhood social app with a real route skeleton (/activity, /create/content, /discover, /docs, /profile), ethers + eth_requestAccounts, four visible words, no API answering, no repo, a days-old domain. Not special, not nothing.

# THE SUBSTANCE LADDER, `/opt/casig/substance.py`
- [stated] Ben: "we need to make sure we avoid stuff that is built in a very short time and is copied from other places, or is just a nice frontend. Today, in the AI age, we need to make sure that things have substance... how much effort went into it... how easily replicable is it? An example would be something that's quite complex on the backend, but it's very simple and elegant on the frontend."
**The mechanism: score only what is EXPENSIVE in September 2026.** Cheap now: a frontend, copy, eight routes, a docs page, a whitepaper - one prompt each, and every signal the old rubric leaned on was in that column. Expensive now, and these are the ladder:
1. **TIME** - domain age (RDAP), an active GitHub history, and the best one because it is ours: **a site that CHANGED between two of our own scans**. Already happened or did not.
2. **A LIVE BACKEND** - an API that answers with data. `substance.backend()` probes **the endpoints the app itself fetches**, read as string literals out of its own bundle, then a guessed list. (Guessing only cost COLT a star before I read its bundle.)
3. **ORIGINALITY** - `clone_check()` fingerprints every site scanned (route list + trigram shingles of its own copy) into `fingerprints.json` and reports the closest twin by Jaccard overlap. **The corpus IS the detector and it improves with every scan.** >=60% = clone (capped at 2), >=35% = near-clone.
4. **READS THE CHAIN'S OWN PLUMBING** - a page that publishes the Pons factory, launcher or fee hook is a tool that reads them. A marketing page has no reason to know the fee hook's address or to get it right. Note the symmetry: those addresses used to give a FALSE five ("code they wrote"); they were never worthless, they were filed under the wrong claim. **Integration, not authorship.**
5. **OWN COPY** - `cc.own_copy()` = product sentences minus developer prose. A library's error strings are prose too; that is what served NORA its description.
**BEN'S ELEGANCE TEST AS A RATIO, not a size**: a large front end with nothing answering behind it is the AI signature; a small one with a live backend is the engineering signature. Size alone reads them backwards, which is why every word-count threshold has failed.
**THE GATE: 4 = ONE expensive thing. 5 = TWO, and not a clone.** Passing only the cheap checks is 3, not 4 - otherwise the scale does not mean what it says. Verified: PAIREX 5 (own contract code + 43 own sentences), COLT 4 (reads this chain's contracts), HOOKR 4/5, NORA 3.

# PANIC'S "WEBSITE" IS TIM URBAN'S 2015 BLOG POST
waitbutwhy.com/2015/05/elon-musk-introduction.html scored **3 stars** because `own_site()` accepted a TICKER MENTION as proof of ownership, and that article is literally about the Panic Monster. **A common word as a ticker matches any page on the internet.** A ticker now needs a second thing a stranger's page will not have: the ticker in the DOMAIN, or the page naming this chain or this contract. PANIC is 2, "the page never names this token", and its summary reads "A post about how Elon Musk became the real world Iron Man", which tells the whole story at a glance.

# THE HABIT THAT KEEPS COSTING, now five for five in one day
Every significant error today was **a proxy standing in for the thing it meant**, which stayed true after the thing became false: "empty shell" for no-chain-code, a word count for somebody-wrote-copy, "no site and no handle" for built-from-no-links, bundle weight for effort, and a ticker mention for ownership. **Name the thing you mean, and measure THAT.**

# GATE 104 CHECKS, 0 FAILED. Deployed 2026-09-14T07:05:51Z.

**Continued in [[casig-gmgn-overlay-3]]**, which rebuilt this substance ladder from 380 measured outcomes: copy and route counts turned out to be noise, domain age turned out to be BACKWARDS, and "the site publishes its own contract" turned out to be the strongest mark there is. Read part 3 before acting on the ladder described above.


