Skip to content

DocsWhere HEY is going

The Shipping Signal and its agent

The architecture plan: what fires the Meaningful Shipping Signal, what stops it being gamed, and how the Telegram agent delivers it without ever holding a key.

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):

  1. Published, approved, not demo (PUBLISHED_SCOPE).
  2. At least one meaningful ship in the last 7 days from a source that is corroborated (context_only = false) and not DISCOVERED-only.
  3. 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).
  4. The repository is not marked fork or template.
  5. 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: signals with kind = 'shipping_meaningful', importance = score / 100, before_value / after_value = ships in the two windows, evidence[] = the ship URLs, and a metadata JSON with every component and the reasons above. The dedupe key is shipping_meaningful:<project>:<ISO week>, so a project signals at most once a week.
  • The comparison read: a new scoreHistoryFor(projectId, windows) over project_scores and builder_score_days returning 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 same CODE_ACTIVITY path 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, minProjectAgeDays
  • minLiquidityUsd, minVolume24hUsd, marketStatus (e.g. only ACTIVE_MARKET), maxTop10SharePct, requireVerifiedToken, excludeSharedDeployer, excludeProxy
  • allow / block lists of slugs, cooldownHours per project, dailyMax alerts

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

  1. 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.
  2. Q1shipping_meaningful kind and rule with the explanation; scoreHistoryFor and /api/projects/{slug}/history; signals in watchlist updates and the digest; a signals feed and MCP tool; signal_subscriptions with webhook and Telegram alert-only delivery through the ledger.
  3. Q2 — the bot: chat binding, rules, the four-button message, paper trading, corrections.
  4. 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.