Files
Kevin Codex 1f96280b84 home: rank by buyers, not launch order (live sort + ranking engine)
The home list defaulted to launch order, so the first screen was whatever was launched last:
duplicates from two wallets and zero-holder rows. Replace the Trending tab with Live and make it
the default. One rule, in lib/launchpad/ranking.ts and mirrored in SQL so paging stays correct:

  A token is live when an outside wallet traded it in the last 24h. Outside = not the launcher and
  not a buy inside the sniper window (launch block + 3, the holders panel's definition; one bot
  sniped 121 launches within 2s and would otherwise have made every token live). Live tokens rank
  by outside wallets this hour, then last trade, then holders. Then launches under 1h old, then
  quiet launches under a divider, one row per launcher (the rest are folded into it).

- queries: per-row `traders_1h_ex` / `traders_24h_ex` (outside wallets) in both windows, a `live`
  sort built as a CTE with the tier and a per-launcher row_number, `launcher_collapsed` on the kept
  row; `trending` stays accepted and orders like `live`. Nothing hidden: New, search and the API
  keep every launch. No schema change.
- ranking.ts absorbs trending.ts: tier + chip helpers for the list, strip score now weighs distinct
  outside wallets first (trades log-scaled), day window needs an outside wallet.
- list UI: Live tab first and default, rank numbers only for live rows, a chip per row saying why it
  sits there, a "Quiet launches" divider; the URL writes sort=new explicitly now.
- docs: llms.txt, agents page, rules FAQ ("Why is my token under quiet launches?"); smoke checks the
  live sort and its fields.

Verified against a local production copy: 51 live / 103 quiet rows at a pinned clock, one row per
launcher outside live, no overlap between pages, no self-traded token counted as live.
2026-09-10 14:49:48 +08:00

15 lines
1.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Post-deploy smoke: every page type + the live APIs must answer 200, on both chains. Usage: scripts/smoke.sh [https://basebid.lol]
set -u
B="${1:-https://openlaunch.lol}"
fail=0
check() { code=$(curl -s -o /dev/null -w '%{http_code}' "$B$1"); ok=0; case "$1" in /api/launch/meta/*) { [ "$code" = 200 ] || [ "$code" = 404 ]; } && ok=1;; /t/0x*) { [ "$code" = 307 ] || [ "$code" = 308 ]; } && ok=1;; *) [ "$code" = 200 ] && ok=1;; esac; [ $ok = 1 ] && echo " ok $code $1" || { echo " FAIL $code $1"; fail=1; }; }
for p in / /me /feed "/api/posts?feed=1" "/api/quotes?chain=robinhood&q=AAPL" "/api/quotes?chain=base" "/?filter=gitlawb" /gitlawb-mark.png "/?chain=robinhood" /launch "/launch?chain=robinhood" /rules /agents /llms.txt /api/health "/api/launch/live?sort=live&window=1h" "/api/launch/live?sort=trending&window=1h" "/api/launch/live?sort=new&chain=robinhood" "/?sort=new" /api/launch/feed /api/presence; do check "$p"; done
# live sort (home default): must answer and carry the ranking fields
curl -s "$B/api/launch/list?sort=live&limit=5" | python3 -c 'import json,sys; d=json.load(sys.stdin); l=d["launches"]; assert d["sort"]=="live"; assert all("traders_24h_ex" in r and "launcher_collapsed" in r for r in l), "live sort missing ranking fields"' && echo " ok live sort fields" || { echo " FAIL live sort fields"; fail=1; }
for c in base robinhood; do
T=$(curl -s "$B/api/launch/list?chain=$c&limit=1" | python3 -c 'import json,sys; l=json.load(sys.stdin)["launches"]; print(l[0]["token"] if l else "")')
if [ -n "$T" ]; then check "/t/$c/$T"; check "/t/$c/$T/opengraph-image"; check "/api/launch/meta/$T?chain=$c"; check "/t/$T"; else echo " skip no launches on $c"; fi
done
exit $fail