Each module is checked against the connections snapshot. Nothing here changes a gate (S>=75 spot, S>=88 futures), a weight, or the live-money setting.
Modules
M01 · API Health Dashboard
M01-api-health-dashboard
Status types and fields
Not measured yet. The connections snapshot has not been read, so no line is counted.
One table, one row per API, showing: name, provider, status (CONNECTED, IDLE, DEGRADED, RATE_LIMITED, AUTHENTICATION_ERROR, DISCONNECTED, MAINTENANCE from the /admin/connections supervisor snapshot; supervisor states LIVE, NOT LIVE, UNKNOWN, DEGRADED, STALE, LIVE-HELD, PARTIAL, CONFIG-MISSING, WARMING), latency, last update, auth state, rate limit, errors, reconnect action, backup provider and health score. Reuses the data /admin/connections already reads; no second probe loop.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/admin/connections /admin
Joins APIs
/api/admin/connections /api/admin/api-health /api/admin/api-errors /api/connections /api/health /api/worker-status
Formula
latency = p50 and p95 of measured round-trip ms over a rolling window (null if no sample). age = now - lastSuccessAt. rateLimitHeadroom = 1 - used/limit from response headers only (null if the provider sends none). errorRate = errors/requests over the window with N shown. healthScore H = sum(w_i * c_i)/sum(w_i) over measured components c_i in [0,1] (freshness, 1-errorRate, latency vs. budget, auth ok, headroom); a missing component is dropped and the weights renormalised; with no component H = null (SKIP), never 0 or 50. H is an operational indicator, not a prediction score.
Steps
- Inventory the existing fields in /api/admin/connections rows and the api-health registry; list which of the 11 dashboard fields already exist and which do not.
- Add missing fields to the row type as optional/nullable (typed) with the source of each value documented.
- Compute p50/p95 latency, error rate and rate-limit headroom from the instrumented-fetch ring (no new network calls).
- Compute health score H with renormalised weights and a visible per-component breakdown (founder approves the weights; they are operational, not trading weights).
- Render read-only columns on /admin/connections; reconnect and backup shown as state, actions stay on the existing operator-gated POST route.
- Add unit tests: null in, null out; status mapping is total; no zero-fill.
Best option
Extend the existing /admin/connections snapshot (one source of truth) rather than a new dashboard route.
Risks
Adding probes would burn provider rate limits (GeckoTerminal 429); stay passive-read. Health score weights are subjective, so show components next to the number.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- No Socket.IO on the market path; server-side feeds and REST/SSE only.
- Score gates stay S>=75 (spot) and S>=88 (futures) untouched; weights.json and score formulas untouched; p-star stays blank until N>=200.
Depends on: none
Builds on existing code: src/lib/ibot/api-health/registry.ts, src/lib/ibot/admin-connections/supervisor.ts, src/lib/ibot/connection-health.ts
M02 · API Repair framework
M02-api-repair-framework
A single shared wrapper around outbound fetches that adds: retry, timeout, reconnect, backup providers, response normalization, dedupe and logging, so each provider no longer hand-rolls them.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/admin/connections
Joins APIs
/api/admin/api-health /api/admin/connections /api/admin/api-errors
Formula
retry delay d_n = min(cap, base * 2^n) * (1 + U(0, j)) with full jitter, n = attempt index, max attempts fixed per provider; honour Retry-After when present (hold, never hammer). Circuit breaker: open after f failures in a window of W calls, half-open probe after cooldown. timeout = per-provider p99 latency * m, bounded. dedupe key = hash(method, url, normalised body) with in-flight coalescing (one upstream call serves all waiters). Backup provider is used only if its data is real for the same field; otherwise the field stays null.
Steps
- Audit current retry/timeout code in instrumented-fetch, circuit-breaker.ts and the rails; record gaps per provider.
- Define one RepairPolicy type (retry, timeout, reconnect, backup order, normalizer, dedupe, log level) per provider.
- Wrap fetches with the policy; keep 429 handling as hold/pacing (GeckoTerminal).
- Normalise responses to one typed shape with units; reject, never coerce, malformed fields to 0.
- Coalesce duplicate in-flight requests; add dedupe of repeated events by stable id.
- Structured log of every retry, reconnect and backup switch, visible in /api/admin/api-errors.
- Tests with fake timers: backoff bounds, breaker transitions, Retry-After, no zero-fill on parse failure.
Best option
Build on the existing circuit-breaker and instrumented-fetch modules and migrate providers one at a time behind the same interface.
Risks
A badly tuned retry multiplies load during an outage; the breaker and Retry-After hold are mandatory. Backup providers may disagree, so never average silently.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- No Socket.IO on the market path; server-side feeds and REST/SSE only.
- LIVE_MONEY stays false; ibot-engine, nginx, pod start command and env/deploy settings are not touched.
Depends on: M01-api-health-dashboard
Builds on existing code: src/lib/ibot/circuit-breaker.ts, src/lib/ibot/api-health/instrumented-fetch.server.ts, src/lib/ibot/api-probe.ts, worker/market-normalizer.ts
Exchange data
Not measured yet. The exchange snapshot was not read, so no line is counted.
Sync per exchange: spot and futures markets, orderbook, funding, open interest (OI), liquidations, liquidity, volume, trades, depth, candles and streams. All feeds are server-side; browsers never connect to exchanges.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/ /futures-data /futures-data-v2 /tape /admin/connections
Joins APIs
/api/universe /api/funding /api/futures-book /api/deriv-snapshot /api/klines /api/venue-board /api/flow /api/registry
Formula
mid = (bestBid + bestAsk)/2; spread_bps = 10^4 * (ask - bid)/mid; depth_k = sum of notional within k bps of mid per side; book imbalance OBI = (B - A)/(B + A) over the same depth (null if either side empty). Cross-venue volume = sum of per-venue quote volume converted with a measured quote price, each venue listed with its own freshness; stale venues are excluded and counted, not zero-filled. Funding annualised = rate * (8760 / interval_hours). OI change = (OI_t - OI_{t-w})/OI_{t-w}, null if either point is missing. Liquidation notional = sum(price * qty) of reported liquidation events in the window.
Steps
- Build the venue matrix (rows: venue; columns: spot, futures, orderbook, funding, OI, liquidations, trades, depth, candles, streams) from real worker rails; mark every cell one of measured / REST-only / not offered / not wired.
- Fill gaps one venue at a time via server-side REST or WS in the worker, with the repair framework policy.
- OKX REST slot: OKX ticker, trades (coverage cursor) and top-5 books by REST only, paced; no socket feeds it and it is not added to the hub quotes. REST-only venues: REST polling only, paced; no stream is claimed.
- Normalise symbols and units in market-normalizer; one canonical symbol map.
- Store with timestamps; expose freshness per cell on /futures-data-v2 and /admin/connections.
- Tests: unit conversion, empty side gives null, stale excluded, REST-only venues claim no WS.
Best option
Extend the existing worker rails and venue-board rather than adding a new data service; widen venue by venue with proof of live data.
Risks
Venue rate limits and bans; WS reconnect storms; symbol-mapping mistakes causing wrong-coin data.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- No Socket.IO on the market path; server-side feeds and REST/SSE only.
- REST-only venues stay REST; no browser-side exchange connections.
Depends on: M02-api-repair-framework
Builds on existing code: worker/venues.ts, worker/venue-board.ts, worker/rest-rails.ts, worker/deriv-rail.ts, worker/futures-book.ts, worker/market-normalizer.ts
M04 · Whale Intelligence
M04-whale-intelligence
Whale parts
Not measured yet. The connections snapshot has not been read, so no line is counted.
Audit about 30 whale data sources, classify wallets, and measure exchange flow, stablecoin flow, institutional activity, smart money, dormant activation, cross-chain moves, accumulation and distribution, then combine them into a whale score. Verified addresses only.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/ /predictions /futures-data-v2 /admin/connections
Joins APIs
/api/whales/live /api/flow /api/scan/$symbol /api/admin/connections
Formula
For each measured component i: z_i = clip((x_i - median_i)/(1.4826*MAD_i), -3, 3)/3 in [-1,1] using that asset's own rolling baseline. Exchange net flow x = inflow - outflow (positive inflow = potential sell pressure). Accumulation/distribution x = sum of signed net wallet balance change of verified accumulators over distributors / circulating supply. Dormant activation = count and value of wallets idle for >= D days that move. whaleScore W = 50 + 50 * sum(w_i * z_i)/sum(w_i) mapped to [0,100] over components that are measured; if no component is measured, W = null (SKIP). Never zero-filled; never 50 by default; component weights are proposals for founder approval and do not alter weights.json.
Steps
- Source audit: list ~30 candidate whale sources with chain, auth, rate limit, latency, coverage, licence; mark each LIVE-proven / unproven / rejected, with evidence link.
- Wallet classification: exchange, stablecoin issuer, institutional/treasury, smart money (by realised PnL track), dormant, unknown. Only labelled by a cited source; no guessed labels.
- Address registry: each address stores source URL and verification date; unverified addresses are not stored or shown.
- Compute exchange flow, stablecoin flow, institutional, smart-money, dormant and cross-chain components separately, each with N and freshness.
- Combine into whale score W with the renormalised formula; show per-component contribution.
- Feed W to the existing whale leg only after paper shadow of at least 28 days; leg gate logic unchanged.
- Tests: null in gives null out; zero stays zero; unverified address rejected.
Best option
Build on whale-rail and hl-whales (existing verified rails) and add sources one by one, keeping each source's proof next to it.
Risks
Label errors (a wrong exchange tag flips the sign); chain reorgs; paid APIs. Proxy large-print data must stay labelled PROXY, not wallet LIVE.
Hard-rule checks
- Whale and DEX legs compute-or-SKIP: null, never zero, when inputs are missing; never fabricate a whale address (verified addresses only).
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- Score gates stay S>=75 (spot) and S>=88 (futures) untouched; weights.json and score formulas untouched; p-star stays blank until N>=200.
Depends on: M02-api-repair-framework, M03-exchange-intelligence-sync
Builds on existing code: worker/whale-rail.ts, worker/hl-whales.ts, src/lib/ibot/whale-rail-registry.ts, src/lib/ibot/named-whales.ts, src/lib/ibot/institutional.ts
M05 · DEX Intelligence
M05-dex-intelligence
DEX parts
Not measured yet. The connections snapshot has not been read, so no line is counted.
Token, liquidity, pool, swap and volume scanners; trending tokens, new pairs, liquidity changes, token creation and cross-DEX price difference. GeckoTerminal 429 responses are held and paced.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/ /predictions /admin/connections
Joins APIs
/api/admin/dexscreener /api/scan/$symbol /api/flow /api/admin/connections
Formula
Pool liquidity change = (L_t - L_{t-w})/L_{t-w}. Swap imbalance = (buyUSD - sellUSD)/(buyUSD + sellUSD) from real swap counts/volumes, null when counts are missing. Cross-DEX price difference in bps = 10^4 * (p_A - p_B)/mean(p_A, p_B) only between pools above a minimum liquidity floor, reported net of nothing and labelled gross. New-pair age = now - poolCreatedAt. Trending rank = rank of z-scored volume growth over a rolling window. The DEX score is computed from measured inputs only; if swap counts or liquidity are missing the leg is SKIP (null), never zero.
Steps
- List provider endpoints in use (DexScreener, GeckoTerminal) with real rate limits; keep the 429 hold: on 429, stop that provider for Retry-After or a fixed cool-down and serve last-good with age shown.
- Pool and token scanners by chain with liquidity floor; store creation time and pair age.
- Swap and volume scanners using provider-reported counts; mark windows where provider gives none as missing.
- Trending and new-pair lists with age and liquidity displayed; no ranking of pairs below the floor.
- Cross-DEX price difference only for same-token pools; flag low-liquidity as not tradable.
- Tests: 429 pacing (fake timers), null never zero, below-floor pools excluded.
Best option
Extend dex-rail with the existing gecko-gate pacing; add scanners as read-only views before any score use.
Risks
Rate-limit bans; wash trading and fake liquidity on new pairs; honeypot tokens must not be presented as opportunities.
Hard-rule checks
- Whale and DEX legs compute-or-SKIP: null, never zero, when inputs are missing; never fabricate a whale address (verified addresses only).
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- Score gates stay S>=75 (spot) and S>=88 (futures) untouched; weights.json and score formulas untouched; p-star stays blank until N>=200.
Depends on: M02-api-repair-framework
Builds on existing code: worker/dex-rail.ts, src/lib/ibot/gecko-gate.ts, src/lib/ibot/dexscreener-read.ts, src/lib/ibot/fetch-dex-tape.ts
Prediction inputs
Not measured yet. The connections snapshot has not been read, so no line is counted.
An ensemble of technical analysis (TA), whale, on-chain, DEX, order book, liquidity, pattern, correlation, machine learning (ML), statistics, regime, smart money and multi-timeframe signals. Every prediction carries an explanation. No look-ahead.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/predictions / /book
Joins APIs
/api/predict-v2 /api/predict-gaps /api/scan/$symbol /api/scan/batch
Formula
Each signal j yields a direction-signed score s_j in [-1,1] or null. Ensemble E = sum(w_j * s_j)/sum(w_j) over non-null signals (weighted vote); if none, E = null. Explanation = the top-k signals by |w_j * s_j| with their raw inputs and timestamps. Multi-timeframe: signals computed per timeframe only from candles closed before decision time t (feature timestamp < t) so there is no look-ahead; targets use t+h strictly after. Probability, when shown, is calibrated on out-of-sample data (see M09) and blank until N >= 200. Existing execution gates are unchanged: S >= 75 spot, S >= 88 futures (score points, not percentages).
Steps
- Write the signal contract: name, input fields with timestamps, output in [-1,1] or null, missing reason.
- Map each of the 13 signal families to existing code or mark not built.
- Implement the ensemble as a weighted vote with renormalised weights; weights are proposals through M09, not edited here.
- Attach explanation (top contributors, inputs, timestamps, model version) to every prediction; refuse to publish one without it.
- Look-ahead guard test: replay on stored history and assert every feature timestamp < decision time.
- Walk-forward evaluation only (train on past, test on later) with embargo equal to the horizon.
Best option
Keep P200 predict-v2 as the base and add signal families as optional legs that default to SKIP until proven in paper.
Risks
Overfitting with many correlated signals; leakage via future-aligned candles; presenting the ensemble value as a probability before calibration.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- No fixed win-rate label or percentage; any accuracy shown is measured (Brier / calibration) with its sample size N.
- Score gates stay S>=75 (spot) and S>=88 (futures) untouched; weights.json and score formulas untouched; p-star stays blank until N>=200.
- Every prediction has an explanation; no look-ahead.
Depends on: M03-exchange-intelligence-sync, M04-whale-intelligence, M05-dex-intelligence
Builds on existing code: worker/predict-v2-core.ts, worker/prediction-model.ts, src/lib/ibot/ensemble.ts, src/lib/ibot/mtf-confluence.ts, src/lib/ibot/hmm-regime.ts, src/lib/ibot/shap.ts
Pattern memory
Not measured yet. The connections snapshot has not been read, so no line is counted.
Remember why a coin moved: the order of signals before a move, lagged reactions, sector rotation, historical similarity, the resulting probability and confidence.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/predictions /tape
Joins APIs
/api/predict-v2 /api/klines /api/scan/$symbol
Formula
Snapshot vector v_t of normalised features. Historical similarity = cosine(v_t, v_s) or Euclidean distance on z-scored features for past states s with s < t - horizon. Take the k nearest neighbours; empirical outcome frequency p = (#up outcomes)/k with a Wilson interval; confidence = interval width and N, never a fixed number. Signal order: record the timestamp order of threshold crossings before each move. Sector rotation: rank change of sector returns, sector defined by data (return correlation clusters), not by hand. Return null when k < Nmin.
Steps
- Define move events (e.g. |return| beyond a rolling volatility multiple) from real candles.
- Store the pre-move signal sequence with timestamps, lags and what moved after.
- Build the neighbour search over past states only (no future rows).
- Report p with Wilson interval, N and age of the memory; hide below Nmin.
- Sector rotation from clustered correlations (M08).
- Tests: no future neighbour ever returned; null below Nmin.
Best option
Extend the existing similarity-engine with an explicit time cut-off and a stored event log.
Risks
Regime change makes old patterns stale; small N produces overconfident p; survivorship bias if delisted coins are dropped.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- No fixed win-rate label or percentage; any accuracy shown is measured (Brier / calibration) with its sample size N.
- No look-ahead: neighbours strictly older than the decision time.
Depends on: M06-prediction-architecture, M08-coin-relationship-engine
Builds on existing code: worker/similarity-engine.ts, src/lib/ibot/similarity.ts, worker/price-replay.ts
Coin relationships
Not measured yet. The connections snapshot has not been read, so no line is counted.
Learn coin-to-coin relationships statistically: lagged cross-correlation, Granger-style tests, rolling beta, dominance effects and inverse correlation. Nothing is hard-coded.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/predictions /futures-data-v2
Joins APIs
/api/klines /api/predict-v2 /api/scan/batch
Formula
Lagged cross-correlation rho(k) = corr(r_A[t], r_B[t+k]) over rolling windows of log returns, k in [-K, K]; significance by |rho| > z/sqrt(n_eff) with multiple-testing correction (Benjamini-Hochberg) and a shuffle/permutation test. Granger-style: regress r_B[t] on its own L lags (restricted) and on L lags of r_A as well (full); F = ((RSS_r - RSS_f)/L)/(RSS_f/(n - 2L - 1)); report p-value, never causality claims. Rolling beta beta = cov(r_A, r_M)/var(r_M). Dominance effect: partial correlation of alt returns on BTC dominance change controlling for BTC return. Inverse correlation = significantly negative rho. Pairs with too few observations are null.
Steps
- Use aligned closed-candle log returns only; drop missing bars instead of filling.
- Compute rho(k) per pair on rolling windows; keep only pairs passing the corrected significance test.
- Granger-style F test on stationary returns; record p and lag.
- Rolling beta against BTC and ETH; dominance partial correlation.
- Store relationships with window, N and last-tested time; expire if no longer significant.
- Tests on synthetic series with a known planted lag recover that lag; independent noise yields no significant pairs beyond the false-discovery rate.
Best option
Reuse causal-math.ts and fetch-causal.ts, adding the multiple-testing correction and permutation checks.
Risks
Spurious correlations from common drivers; non-stationarity; look-ahead from misaligned timestamps.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- Nothing hard-coded: no fixed coin-pair lists; relationships are learned and re-tested.
Depends on: M03-exchange-intelligence-sync
Builds on existing code: src/lib/ibot/causal-math.ts, src/lib/ibot/causal-flow.ts, src/lib/ibot/fetch-causal.ts
M09 · AI Learning Loop
M09-ai-learning-loop
AI learning loop
Not measured yet. The connections snapshot has not been read, so no line is counted.
Close the loop: prediction, then outcome, then accuracy, then error analysis, then a weight proposal, then model improvement. Weights change only through a founder-approved process, and paper trading runs at least 28 days before live.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/predictions /book /stability
Joins APIs
/api/predict-v2 /api/predict-gaps /api/worker-status
Formula
Brier score BS = (1/N) * sum (p_i - o_i)^2 with o_i in {0,1}. Calibration: bin predictions by p, compare mean p with observed frequency per bin; ECE = sum_b (n_b/N) * |acc_b - conf_b|. Directional accuracy is reported with N and a Wilson interval, never as a fixed label. Error analysis groups misses by regime, venue, signal family. A weight proposal is a vector delta computed offline on out-of-sample data, shown as a diff; it is NOT applied automatically. p-star stays blank until N >= 200.
Steps
- Log every prediction with model version, features hash, time and horizon (append-only).
- Resolve outcomes at horizon end from real prices; unresolved stay unresolved.
- Compute Brier, ECE, calibration curve and directional accuracy with N and intervals; blank when N < 200 for p-star.
- Error analysis report by regime/venue/signal family.
- Generate a weight proposal as a reviewable diff; founder approves manually; applied only via the normal reviewed change process.
- Promotion rule: at least 28 days paper with measured metrics before any live consideration; LIVE_MONEY remains false.
Best option
Extend learn-loop and shadow-metrics; keep proposals as documents, not automatic writes.
Risks
Feedback loops that overfit to recent data; peeking at the test period; changing weights without audit trail.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- No fixed win-rate label or percentage; any accuracy shown is measured (Brier / calibration) with its sample size N.
- Score gates stay S>=75 (spot) and S>=88 (futures) untouched; weights.json and score formulas untouched; p-star stays blank until N>=200.
- LIVE_MONEY stays false; ibot-engine, nginx, pod start command and env/deploy settings are not touched.
- Weights change only via founder-approved process; paper >= 28 days before live.
Depends on: M06-prediction-architecture
Builds on existing code: src/lib/ibot/learn-loop.ts, worker/shadow-metrics.ts, worker/pstar.ts, src/lib/ibot/conformal.ts, src/lib/ibot/model-ops.ts
M10 · Performance
M10-performance
Performance
Not measured yet. The connections snapshot has not been read, so no line is counted.
Measure and improve API speed, the background refresh store, universe body speed, caching, compression, bundle size and database queries.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/ /predictions /admin/connections
Joins APIs
/api/universe /api/scan/batch /api/admin/connections /api/venue-board
Formula
Latency percentiles p50/p95/p99 from request timing (server timing headers). Payload ratio = compressedBytes/rawBytes. Cache hit ratio = hits/(hits+misses). Budget check: improvement counts only if p95 before vs after is measured on the same endpoint under comparable load; report both numbers. Bundle size = gzip bytes per route chunk from the build output. Query cost = measured ms per query and rows scanned (EXPLAIN).
Steps
- Baseline: record p50/p95 of the heaviest endpoints (universe, scan batch, connections) and route chunk sizes.
- Background refresh store: serve last-good with age; refresh off the request path.
- Universe body: ETag/304, field trimming, compression.
- Caching headers and in-memory TTL per endpoint, honest age shown to the client.
- Bundle: split heavy routes, lazy-load charts, drop unused dependencies.
- DB: EXPLAIN slow queries; add indexes via reviewed migrations only.
- Re-measure and publish before/after.
Best option
Measure first, then fix the top three bottlenecks; reuse refresh-store and json-etag already present.
Risks
Caching can serve stale data; must show age. Compression CPU cost on small bodies.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No Socket.IO on the market path; server-side feeds and REST/SSE only.
- LIVE_MONEY stays false; ibot-engine, nginx, pod start command and env/deploy settings are not touched.
- Cached values always show their age; stale is never presented as live.
Depends on: none
Builds on existing code: src/lib/ibot/refresh-store.ts, worker/json-etag.ts, src/lib/ibot/etag.ts, worker/packed-body.ts
M11 · Security
M11-security
Security
Not measured yet. The connections snapshot has not been read, so no line is counted.
<script>alert(1)</script>
Review authentication, secrets, input validation, rate limits, XSS, CSRF, CORS and security headers.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/login /admin /admin/connections
Joins APIs
/api/admin/session /api/auth/$ /api/tv-alert /api/admin/api-health /api/connections
Formula
Checklist, not a score. Rate limit as token bucket: tokens refill at r per second up to burst b; request allowed iff tokens >= 1. Validation: every external input parsed by a schema; unknown fields rejected. CSRF: state-changing routes require a same-site cookie and an origin check. Each finding records severity, evidence and fix; unresolved findings stay open, none is marked fixed without a test.
Steps
- Inventory every POST/DELETE route (reconnect, test, session, tv-alert, auth) and its auth requirement.
- Secrets: confirm none in the repo or client bundle; rotate through the deploy settings by the founder, not by code.
- Add schema validation to each mutating handler.
- Add rate limits to login and mutating routes.
- XSS: audit any dangerouslySetInnerHTML; CORS: explicit allow-list; CSRF origin check.
- Security headers (CSP, HSTS, frame, referrer, content-type) set in the app layer only; nginx is not touched.
- Tests for each rule and a regression test for the auth invariant script.
Best option
Fix the auth and mutating-route findings first, as they have the highest impact, and keep header work in the app code.
Risks
A strict CSP can break existing inline scripts; rate limits can lock out the operator.
Hard-rule checks
- LIVE_MONEY stays false; ibot-engine, nginx, pod start command and env/deploy settings are not touched.
- No secret values appear in this page, the repo or logs; env/deploy settings and nginx are not touched.
Depends on: none
Builds on existing code: scripts/check-auth-invariant.mjs, src/lib/ibot/admin-session.server.ts, middleware.ts
M12 · UI upgrade
M12-ui-upgrade
UI
Not measured yet. The connections snapshot has not been read, so no line is counted.
Design system, charts, dark mode, responsive layout, and loading and error states. The founder decides the visual design; this entry lists the work only.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/ /predictions /tape /book /stability /futures-data /futures-data-v2 /admin/connections
Formula
No trading formula. Acceptance measures: contrast ratio >= 4.5:1 for body text (WCAG), tap target >= 44 px, layout checked at 360, 768, 1280 px widths, every data view has distinct loading, empty, error and stale states (no blank-as-zero).
Steps
- Audit current tokens and components; list inconsistencies.
- Founder chooses the design direction; document tokens (colour, spacing, type).
- Chart component with real series only and a visible gap for missing data.
- Dark mode via tokens; responsive pass per page.
- Standard loading, empty, error and stale components used across pages.
- Visual review in a browser at the three widths before release.
Best option
Introduce tokens and shared state components first, then restyle page by page.
Risks
Visual regressions on dense tables; charts that interpolate over missing data would hide gaps.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No 50/50 or neutral-50 default anywhere: unknown stays null.
- No fixed win-rate label or percentage; any accuracy shown is measured (Brier / calibration) with its sample size N.
- Charts never interpolate or draw zero for missing values.
Depends on: none
Builds on existing code: src/components/ibot/site-chrome.tsx, src/components/ibot/sparkline.tsx, src/components/ui/modal.tsx
M13 · Repository hygiene
M13-repository-hygiene
Remove dead code, fix type errors, lint, circular dependencies, and strengthen CI.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Formula
Counts that must go down or stay at zero: tsc errors (baseline in typecheck-baseline.txt), eslint errors, unused exports, import cycles. A change is accepted only if the count after <= before and the full test suite and build pass.
Steps
- Record the baselines: typecheck, lint, unused exports and import cycles.
- Dead code: list unused files/exports with a tool, review by hand, then delete in small commits.
- Fix type errors and lint errors in batches; never silence with ts-ignore without a comment.
- Detect import cycles and break them.
- CI: keep the test and build jobs required; add typecheck and lint once they are at zero.
- Archive/attachment folders reviewed with the founder before removal.
Best option
Ratchet: fail CI on new type or lint errors while old ones are burned down.
Risks
Deleting code that only the worker or scripts import; run the full tests and build before each removal.
Hard-rule checks
- Score gates stay S>=75 (spot) and S>=88 (futures) untouched; weights.json and score formulas untouched; p-star stays blank until N>=200.
- LIVE_MONEY stays false; ibot-engine, nginx, pod start command and env/deploy settings are not touched.
- Do not touch ibot-engine, weights.json or score formulas while cleaning.
Depends on: none
Builds on existing code: typecheck-baseline.txt, eslint.config.mjs, .github/workflows/test.yml
Write documentation and extend tests for every module as it is built, one module at a time.
Status: reading…
Last proof: —
Not measured yet.
Data: /admin/connections
This module is not per coin.
Joins pages
/admin/checklist
Formula
Coverage by contract, not by percentage target: each module must have (a) a doc with formula and sources, (b) unit tests for null-handling, bounds and look-ahead, (c) a live check. A module is Done only when all three exist and CI is green.
Steps
- Per module: a doc page with inputs, formula, limits and honest unknowns.
- Unit tests added to the npm test script for each new file.
- Property tests for math helpers (bounds, null in gives null out).
- Post-deploy checklist: version commit, page loads, key numbers non-null.
- Keep PREDICTION_FORMULA.md as the single math source; update on any formula change approved by the founder.
Best option
Docs and tests are written in the same commit as the code they cover.
Risks
Docs drifting from code; tests that assert implementation instead of the contract.
Hard-rule checks
- No fake or placeholder data: a missing input renders as missing (null / SKIP / —), never as a made-up number.
- No fixed win-rate label or percentage; any accuracy shown is measured (Brier / calibration) with its sample size N.
- Score gates stay S>=75 (spot) and S>=88 (futures) untouched; weights.json and score formulas untouched; p-star stays blank until N>=200.
Depends on: none
Builds on existing code: PREDICTION_FORMULA.md, docs/admin/DISK_STORAGE_NOTES.md, .github/workflows/test.yml