Written 2026-09-18 against the platform as it runs. This is a plan for what to build and in which order, grounded in what already exists. Nothing here is implemented unless a section says so.
The thesis it serves: HEY detects meaningful builder activity, corroborated and sourced, before the market necessarily notices it. The subscriber decides what to do with that. A commit alone is nothing. Market data is context. Execution, if it ever exists, lives somewhere the research engine cannot reach.
1. What exists today, and what is reused
Everything a first version needs is already a table, a job or a service. The plan adds one signal kind, one rules table, one chat registry, one delivery sink and one comparison read. It adds no second copy of anything that exists.
| Need | Exists as | Where |
|---|---|---|
| The meaningfulness ladder | significanceOf, isMeaningful, BUILDING_EVENT_TYPES, WITHDRAWN_VERIFICATION_STATUSES, with SQL twins |
packages/scoring/src/significance.ts, packages/domain/src/queries/evidence.ts |
| Corroboration and attribution | quality-gate.ts, qualify.ts (context_only, repo_declared_only, repo_shared_with_published), repo-match.ts (fork cap 84, DO_NOT_MAP), shared host and deployer rules |
packages/domain/src/candidates/**, packages/domain/src/builders/** |
| Dated, sourced activity | ship_events + ship_event_evidence, unique on (project, source_kind, external_id), published_at and detected_at |
packages/db/src/schema/ships.ts |
| Contributor breadth | commitCount, contributorCount, activeDays, commitDays[] on every weekly CODE_ACTIVITY evidence row (counts, never names) |
packages/domain/src/ships/normalize.ts |
| Releases and deployments | GITHUB_RELEASE (with release_version), CONTRACT_DEPLOY_FOLLOWUP, contract_deployers (creation tx, block, shared) |
packages/domain/src/contracts/deployer-watch.ts |
| On-chain use | contract_activity(day, callers, calls, event_count) |
packages/db/src/schema/contracts.ts |
| Score history | project_scores (append on change, versioned, unpruned), builder_score_days (daily, 400 days) |
packages/domain/src/scoring/service.ts, packages/domain/src/radar/builders.ts |
| The signal pipeline | signals + signal_state, dedupe_key, per-kind cooldowns, first sighting seeded silently, importance, review path |
packages/domain/src/signals/** |
| Market and risk context | token_market_current, tokens.market_status, token_market_days, token_pool_days, token_holder_summaries, contract_implementations, tokens.verification |
see §5 |
| Identity and tiers | LinkedWalletService, EntitlementService, HeyTiersSettingsService, tierBenefits, apiAccess |
packages/domain/src/hey/**, apps/web/src/lib/api-auth.ts |
| Consent and delivery | MailPreferencesService (double opt-in), MailDeliveryLedger (claim → send → settle, unique (user, kind, subject_key)), signed tokens with digest-only storage |
packages/domain/src/mail/** |
| Fan-out to followers | WatchlistService.updates() (five readers, 200-follow bound, neutrality-tested) |
packages/domain/src/watchlist/service.ts |
| A Telegram client | TelegramSink (AlertSink, one sendMessage), transition-only firing in alertMessage() |
packages/domain/src/reliability/alerts.ts |
Production coverage that bounds a first version (2026-09-17 readings): 4,720 published projects; 1,493 verified builders, of which 1,411 rest on commit summaries, 246 have a release and 82 a follow-up deployment; 2,909 weekly commit summaries all carrying contributor counts, 920 carrying day-by-day commits (every new one since 2026-09-17); 14,270 signals, 15 of 20 kinds having fired; 2 watchlists and 0 confirmed email addresses; 1 API key, unused.
2. The Meaningful Shipping Signal
2.1 What it says
One sentence, always with the reasons: "Project X shipped something meaningful this week from a
corroborated repository, and its activity accelerated against its own last 30 days." It is a
signal kind, shipping_meaningful, in the existing engine — a new SignalKindSpec in
vocabulary.ts and a new pure rule in rules.ts. Not a new table.
2.2 Inputs, and what each is worth today
Every input is a fact HEY already stores. The column on the right is the honest coverage, so the model is weighted by what can carry weight.
| Input | Read from | Reliable today? |
|---|---|---|
| Repository corroborated (not declared-only, not context, not shared) | project_sources.context_only = false, gate derivations |
Yes — the live rule since 2026-09-11 |
| Not a fork or template | intake refusal + mapping cap; the scheduled re-check is missing | Partly — mapped after 2026-09-04 only; a RECHECK_REPO_FORKS job is Q4 work |
| Project ownership confidence | tokens.verification (317 VERIFIED), claims (2), chain_verification |
Thin — treat as a bonus, never a requirement |
| Meaningful ships in the window | ship_events through buildingEvidenceSql |
Yes |
| Distinct kinds of work (release + code + deploy + docs) | ship_events.event_type |
Yes — the strongest single discriminator against commit-only activity |
| Contributor count and its change | evidence.metadata.contributorCount per week |
Yes for the level; the change needs the Q4 weekly series |
| Commit volume and active days | commitCount (censored at 100), activeDays, commitDays |
Yes; treat 100 as "at least 100" |
| A release in the window | GITHUB_RELEASE, release_version |
Yes |
| A deployment or upgrade in the window | CONTRACT_DEPLOY_FOLLOWUP, CONTRACT_UPGRADE |
Follow-ups yes (82 projects); upgrades never fired |
| Acceleration against the prior 30 days | the engine's ships CTE already computes 7/prior-7 and 30/prior-30 counts |
Yes for ship counts; for HBM and contributors after Q1's comparison read |
| Consistency | components.consistency (six ISO weeks since hbm-v7) |
Yes |
| Project age | first_seen_at (a fact about HEY's sweeps), contract_deployers.creation_at (5,085 projects) |
Partly — use creation_at when present, otherwise say unknown |
| Source confidence | project_sources.confidence, is_verified, match_confidence |
Yes, but VERIFIED is empty in production; DISCOVERED vs AUTHORITATIVE_LINK is what varies |
2.3 The rule, as a gate and a score
The rule is a gate first and a score second. A project that fails a gate gets no signal, whatever its score — that is what stops a burst of commits from being a signal.
Gates (all must hold):
- Published, approved, not demo (
PUBLISHED_SCOPE). - At least one meaningful ship in the last 7 days from a source that is corroborated
(
context_only = false) and notDISCOVERED-only. - At least two distinct kinds of building in the last 30 days, at least one of them not
CODE_ACTIVITY(the Under the Radar rule, reused). - The repository is not marked fork or template.
- Activity accelerated: meaningful ships in the last 30 days ≥ 1.5 × the 30 before, or the first release or deployment the project has ever recorded.
Score (0–100, explained component by component, stored with the signal):
| Component | Weight | Reads |
|---|---|---|
| Breadth of work | 30 | distinct building kinds in 30 days, capped at four |
| Acceleration | 25 | ratio of the two 30-day windows, log-scaled, capped at 4× |
| Contributors | 15 | distinct contributors in the latest weekly summary (1 → low, 2–3 → mid, 4+ → high); the change once the series exists |
| Release or deployment | 15 | a release in 30 days; a follow-up deployment doubles it |
| Consistency | 10 | active ISO weeks of six |
| Confidence | 5 | verified token, verified claim, or an AUTHORITATIVE_LINK repository |
Not in the score, by rule: price, market cap, liquidity, volume, holders. They are filters a
subscriber applies afterwards (§5), and context the alert carries. The neutrality test that fences
signals and radar from the economy (packages/domain/src/hey/neutrality.test.ts) fences this
too.
2.4 Why this signal fired — the explanation
The alert never says score = 87. It says what the score is made of, with the sources:
Shipping signal · Quorum · 74/100
Why: 3 kinds of work in 30 days (release, code, docs) · 2.6× the ships of the prior 30 days
· 3 contributors this week · release v0.9.1 on 12 Sep · repository corroborated by the site
Sources: github.com/…/releases/tag/v0.9.1 · code-activity 2026-W37 · docs commit 2026-09-14
Not counted: 41 commits on a repository HEY holds as context (declared by a listing, never
corroborated)
The "not counted" line is mandatory: it is how a subscriber sees the gate working, and how a project learns what would make its activity count.
2.5 Storage
- The signal row:
signalswithkind = 'shipping_meaningful',importance = score / 100,before_value/after_value= ships in the two windows,evidence[]= the ship URLs, and ametadataJSON with every component and the reasons above. The dedupe key isshipping_meaningful:<project>:<ISO week>, so a project signals at most once a week. - The comparison read: a new
scoreHistoryFor(projectId, windows)overproject_scoresandbuilder_score_daysreturning HBM, contributors and ships at 24 h / 7 d / 30 d / 90 d — the Q1 "comparative windows" item, exposed as/api/projects/{slug}/history. - The contributor series: a
project_code_weeks (project_id, iso_week, commits, active_days, contributors, repositories, censored)table, written by the sameCODE_ACTIVITYpath that writes the evidence row. Counts only; no login is ever stored.
3. Manipulation, and what stops it
| Vector | Defence | Status |
|---|---|---|
| Commit spam (bots, empty commits, a script that pushes hourly) | bot authors excluded at summary time; commit volume is capped and is the smallest component; gate 3 demands a non-commit kind; a commit alone can never fire | Exists (exclusion, cap); gate 3 reuses the live Under the Radar rule |
| Fork spam (fork a big project, point a token at it) | forks refused at intake, capped below auto-map, and demoted on re-check; the signal requires a corroborated, non-fork source | Intake and cap exist; the scheduled re-check is Q4 work |
| Attaching an official upstream repository (MetaMask, a wallet, a protocol) | shared-repository rule (repo_shared_with_published), owner-identity and reciprocal-homepage corroboration, the upstream multi-chain decision (open) |
Rule exists; the upstream definition is a founder decision |
| Mirrors and re-uploads (the same code under a new name) | repository fingerprinting (tree hash of the default branch, README digest, first-commit hash) matched across candidates | Missing — Q4 planned |
| Monorepos (one repository, many packages, one of them a chain project) | package decomposition with per-package markers; only the package that carries a chain marker counts | Missing — Q4 planned |
| Private → public (a repository made public with two years of history) | record the first time HEY saw the repository; activity before that date counts toward history, never toward acceleration | Missing — Q4 planned (first_seen_public_at on the source) |
| Many repositories per project | ships are per project already; the signal takes the union, and the breadth component counts kinds, not repositories | Exists |
| One repository behind many contracts (a factory, a template) | shared-deployer rule (3+ projects' tokens from one address attribute nothing); the missing repo↔contract edge (Q4) makes it explicit | Deployer rule exists; the edge is planned |
| Release theatre (tag a version with no change) | a release counts once; the breadth and acceleration components need other kinds of work; commit significance (RESEARCH) would read the diff | Partly |
| Fake corroboration (a site that names the token, built for the purpose) | the site must resolve, be crawled by HEY, and not be shared with other projects; a token verified by a signature outranks a site | Exists |
| Timing (ship right before a launch) | the signal states project age from the creation block and says "unknown" when it has none; a subscriber's rule can require age | Partly (5,085 creation dates) |
| Gaming the subscriber's rules | rules filter the signal; they cannot raise a score; every rule is the subscriber's own and never a HEY default | By design |
4. The Shipping Signal Agent (Telegram)
4.1 Position
An alert channel for a signal that already exists, in HEY's own words, with four buttons. It computes nothing. It is positioned as a Builder Signal Agent, never as a pump bot: it reports building, it carries the market and risk context beside it, and it never says "buy".
4.2 Flow
signal engine (hourly) existing
→ shipping_meaningful row §2
→ SubscriptionMatcher new: which chats asked for this project/kind/threshold
→ subscriber rules new: thresholds, filters (§5)
→ delivery ledger reuse MailDeliveryLedger shape: claim → send → settle
→ TelegramSink reuse, one sendMessage per delivery
→ the message project · token · what changed · sources · confidence ·
shipping score · market context · risk context ·
[VIEW RESEARCH] [WATCH] [PAPER TRADE] [BUY ELSEWHERE]
BUY ELSEWHERE is a link to the venue, never an action HEY performs, until §6 exists — and
then only in a mode the subscriber chose.
4.3 Binding a chat
The mail pattern, reused: a signed, expiring token (issueMailToken-style, action = 'telegram')
shown on the account page; the subscriber sends /start <token> to the bot; the bot's inbound
webhook resolves the token, stores the chat as a digest (hashFeedToken-style; the chat id is
recoverable only through the bot's own lookup), and writes telegram_chats (user_id, chat_digest, chat_ref_encrypted, confirmed_at, revoked_at). /stop revokes. One account may bind several chats;
one chat binds to one account.
Inbound is a Telegram webhook route under apps/web/src/app/api/telegram/webhook with the bot
secret header checked, rate-limited, and accepting exactly /start, /stop, /watch <slug>,
/rules, /help. Nothing else is parsed.
4.4 Subscriptions and rules
signal_subscriptions (id, user_id, channel: 'telegram' | 'webhook' | 'mail', target_ref, scope: { projects?: slug[], narratives?: slug[], kinds?: kind[] }, rules: jsonb, mode: 'alert' | 'paper', created_at, paused_at).
Rules the subscriber can set (each optional; absent means no filter):
minScore(0–100),minConfidence(the confidence component),minContributors,requireRelease,requireDeployment,minProjectAgeDaysminLiquidityUsd,minVolume24hUsd,marketStatus(e.g. onlyACTIVE_MARKET),maxTop10SharePct,requireVerifiedToken,excludeSharedDeployer,excludeProxyallow/blocklists of slugs,cooldownHoursper project,dailyMaxalerts
Rules are evaluated against the signal's stored context at delivery time. They filter; they cannot change the signal's score or its text. The context they read is exactly the set in §5.
4.5 Delivery guarantees
- Idempotent:
signal_deliveries (subscription_id, signal_id)unique; a retry never sends twice. - Ordered per chat; a chat that blocked the bot is paused, not retried forever.
- Rate-limited per Telegram's own limits; the sink already backs off.
- Every delivery carries the signal id, so a later correction (a retracted ship, a demoted repository) can be followed by a correction message to the same chat — the record is kept, and the withdrawal is said out loud.
4.6 Paper trading
A ledger, not a broker: paper_positions (subscription_id, project_id, token, opened_at, opened_price_usd, size_usd, rule_snapshot, closed_at, closed_price_usd). Opened by the subscriber's
rules when a signal passes them in paper mode, priced from HEY's own token_market_days close
(never a live quote), closed by the subscriber or by a rule (holdDays, takeProfitPct,
stopLossPct). A weekly message reports the ledger honestly, including the signals the rules
skipped and what those did. This is how a strategy is judged before it costs anything.
5. Risk filters available today
Each is a stored field with the coverage it has; a rule that names a filter with no reading for the token is unmet, not passed.
| Filter | Field | Coverage (2026-09-17) |
|---|---|---|
| Market status | tokens.market_status, market_status_reason |
every published token |
| Liquidity, market cap, volume | token_market_current.* |
4,915 current readings; liquidity on 1,949 |
| Launch stage | tokens.launch_stage |
2,173 tokens |
| Pool age | token_market_current.pair_created_at |
1,635 |
| Deploy age | contract_deployers.creation_at |
5,085 projects |
| Pool depth at 1% | token_pool_days.depth_one_pct_usd |
662 tokens, four days of history |
| Holder concentration | token_holder_summaries.top10_share_pct, nakamoto_half, gini |
604 tokens |
| Burned / pooled share | token_holder_summaries.burned_share_pct, pooled_share_pct |
604 tokens |
| Proxy | contract_implementations.status |
10,050 checked, 1 proxy |
| Shared deployer | contract_deployers.shared |
5,135 rows |
| Token verification | tokens.verification |
317 verified, 9 mismatch |
| Trade breadth | token_market_days.distinct_addresses, trades |
3,114 token-days |
| Reading sanity | sanitizeMarketWrite at the write boundary |
all sources |
What a subscriber cannot filter on today, because HEY does not hold it: token age for the 99% of tokens with no pool or creation date; holder concentration for 81% of published tokens; anything cross-chain.
6. Optional execution — isolated by construction
Not built, and not to be built until alert-only and paper trading have run for a quarter. When it is, the shape is fixed now so nothing built before it has to move:
- A separate service (
apps/executor, its own container, its own database or schema, its own secrets) that holds the only keys. The research engine, the signal worker and the web app have no network route to it and no credential for it. - One-way data flow. The executor reads signals and subscriptions through the public API with a key like any other client. It writes nothing HEY reads except an execution ledger the account page displays.
- Per-subscriber limits, enforced in the executor, not in the signal: max position size, max slippage, daily exposure, cooldown, allow and block lists, and a hard cap on total open exposure. A rule the executor cannot verify from its own reading is a refusal.
- Modes: alert-only (default, forever), paper, manual confirmation (the subscriber presses confirm on a message that quotes the limits), auto (opt-in per subscription, per project, with an expiry).
- Kill switch: a single flag in the executor's own settings, flipped by an admin action that writes an audit row, that turns every mode into alert-only within one tick; a second, independent switch is the revocation of the executor's API key on the HEY side, which stops it reading signals at all.
- A compromised signal worker cannot trade. It can only write signals; the executor applies the subscriber's own limits to every signal, and a burst of signals hits the daily exposure cap. A compromised executor cannot forge a signal, because it has no write path into HEY.
- Explainable, twice. Every execution ledger row carries the signal id and its explanation, and the subscriber's rule snapshot that admitted it.
7. Build order
- Q4 — corroboration hardening the signal reads: scheduled fork re-check, the reviewer surface
for held matches, project flags, repo↔contract linkage,
project_code_weeks, fingerprinting,first_seen_public_at. - Q1 —
shipping_meaningfulkind and rule with the explanation;scoreHistoryForand/api/projects/{slug}/history; signals in watchlist updates and the digest; a signals feed and MCP tool;signal_subscriptionswith webhook and Telegram alert-only delivery through the ledger. - Q2 — the bot: chat binding, rules, the four-button message, paper trading, corrections.
- Q3 — hosted MCP, structured feeds, agent watchlists; execution only after the quarter of alert-only and paper results is published on the site.
8. What was found while auditing, beyond the ask
- Three of the five signal kinds that have never fired are history-depth problems, not rule
problems:
usage_broadened(contract callers have three days of history),concentration_rose(holder summaries kept fourteen days, most tokens one day),contract_upgraded(one proxy exists). - 6,921 of 14,270 signal rows belong to projects outside the published scope and are invisible on every surface; scope is applied at read time, never at write time.
- The Builder Radar's 30-day comparison cannot return anything until the table is 30 days old (it is five).
- Of the four benefits promised to $HEY holders, one has been exercised (the holders-first claim window). The monthly vote has never had a round; the API tiers have one unused key; early access guards zero research notes.
- Reviewer throughput, not token design, is the binding constraint on the research economy: five bounty submissions unreviewed since 2026-09-12, two bond refunds due since the same day.