---
name: alert-bot-fleet
description: Change log and corrections for Ben's Telegram alert bot fleet on the kookwatch droplet. Read alongside the alert-bots skill whenever a bot, alert, tracker or watcher comes up.
sources: [cowork]
aliases: [crashbot, crash-sol, botctl, alert bots, kookwatch droplet]
---
- 28 Aug 2026, crash-sol (crashbot, /opt/crash-bot on 159.223.88.168): CqWFAqmikYvzZmLp7xZMKgQWGpQ1ikyRhRg2YLG4XnwB was listed as "Crash W7 (conduit / seller)" and is NOT a wallet. getAccountInfo returns null because it is a transient wrapped-SOL account created, funded and closed inside W6's own Jupiter swaps. Every "W7" line in its history was W6's own SOL leg, which is what produced the duplicate "sent 2,078.24 SOL" alert alongside the real USDC buy on 28 Aug
- Fix applied 28 Aug 06:16 UTC: entry relabelled "DISABLED — NOT a wallet: transient wrapped-SOL account..." and enabled set to false, config backed up to /opt/crash-bot/config.json.bak-20260828-061612, service restarted, Telegram restart notice sent clean. The Mac registry description at ~/botctl/registry.json was also corrected from "9 Crash Solana wallets" to 7 of 9 entries (backed up alongside)
- Useful detail confirmed in the code: crash-bot filters on w["enabled"] at lines 228 and 439, and the startup Telegram notice lists only enabled wallets, so a disabled entry is fully inert. Baseline log lines only appear for wallets with no prior state, so their absence after a restart is normal, not a failure
- Known remaining noise in crash-sol, NOT fixed because Ben has not decided he wants alert semantics changed: (1) the SOL leg of a swap fires a second transfer alert on top of the buy/sell alert; (2) spam airdrops of zero-liquidity tokens fire "position UP" alerts, eg SOLPUMP 65Nt7TdisdcQrhr33N2vuFgoapdRosC7cwCiHiLUV18f blasted to 9,798 wallets on 18 Aug. He previously said he wants raw untruncated events on the RH tracker, so ask before suppressing anything here
- NEW BOT 28 Aug 2026: ansem-rh, unit ansem-rh.service at /opt/ansem-rh, fleet is now 14. It is the EVM leg of the Ansem bot: rh-tracker's tracker.py with chain_slug robinhood, the single wallet 0xb2b7e6563c2eba9113979aa04ad1b77313701a8c (Ansem RH bridge landing), buys >=$5,000 and sells any size, and ansemtracker's Telegram token and chat so Solana and Robinhood Ansem alerts land in one thread. Same pattern as rhtracker-base. Ansem was removed from /opt/rh-tracker/config.json in the same change, so rhtracker is back to the swing trader only. Verified zero duplicate chain+wallet pairs across rh-tracker, rh-tracker-base, ansem-rh and strongtrader. Registered in ~/botctl/registry.json and in watchdog.py UNITS
- [stated] Ben's rule, learned from this: an alert about a person belongs in that person's bot, even when the chain belongs to another bot. Do not put a wallet in whichever tracker already speaks that chain
- PRICING BUG FIXED 28 Aug 2026, the cause of the "$3,930,948,583 of PVE" alert: Prices.usd() in tracker.py trusted ANY ETH or WETH quoted DexScreener pair with no liquidity floor, then took the deepest one. PVE 0xbCCc7Ad3051Ea3221166cEDa4529fE15048Aba37 had just launched with several v4 pools holding $0 to $4.55 of liquidity quoting nonsense (one PVE/USDG pool showed $3,322.68 on $0.01 liquidity), so it priced 29.6M tokens at $132.79 each. The existing 5x-versus-median guard did not help because every trusted pair was garbage, so the median was garbage too
- The fix, applied to /opt/rh-tracker/tracker.py and /opt/strongtrader/tracker.py and copied to /opt/rh-tracker-base and /opt/ansem-rh: new module global MIN_PAIR_LIQ_USD set from config key min_pair_liquidity_usd (default 2500), applied to trusted pairs as well as fallback ones; when every trusted pair is under the floor the token prices as None and ignore_unpriced_tokens skips the alert, logging "unpriced: all N trusted pairs under $X liquidity". Second guard: config key max_alert_usd (default 5,000,000) makes emit() drop any alert whose notional exceeds it, logging "skip implausible notional". All four configs carry both keys. Backups are tracker.py.bak-20260828-063348 and config.json.bak-20260828-06*
- Regression tested after the patch: STONKBROKER, CASHCAT, PONS, NET and INDEX all priced within 2% of DexScreener, so the floor does not blind the bot to real tokens
- NEW BOT 29 Aug 2026: **rbt3k**, unit rbt3k.service at /opt/rbt3k, fleet is now 15, all healthy. Built for Ben's ask to follow rb3k (@rbthreek) on Robinhood Chain. It is /opt/strongtrader/tracker.py cloned and patched four times: Chain.total_supply() with a cache, a fmt_mcap() helper, mcap computed in emit() as price x totalSupply, an alert body that leads with MARKET CAP AT BUY and drops price entirely (Ben: "I don't care about the price, but about the market cap"), a "This tx: N TOKEN for $X" line, a "Holds in total" line with percent of supply, a configurable header tag, and a commands_enabled flag so it can run without getUpdates polling
- Config /opt/rbt3k/config.json: 4s poll, buys >=$200, sells >=$400, transfers OFF, tag RBT3K, commands_enabled false. Buttons are the token chart and DexScreener filtered to that wallet's own fills via ?maker=, plus holdings and wallet on Blockscout. dex_maker_url() already existed in strongtrader and does exactly what Ben asked for
- [stated] Ben wanted it named after the user (RBT3K) and set up as its own separate bot like the others, and asked me to make the bot in BotFather myself. I declined: registering a bot account is account creation and would mean authenticating as him through the kookwatch user session. He created it and pasted the token, which is the working pattern for this. RESOLVED same day: rbt3k now runs on its own bot **@RBT3K_bot**, commands_enabled true, no longer sharing @bk_rhwatch_bot. botctl ping resolves all 14 tokens OK
- /opt/rbt3k/settoken.py is the reusable token-swap tool and worth copying for any future bot: reads the token from a file, verifies it with getMe BEFORE committing, backs up config.json, writes it, deletes the token file, flips commands_enabled, restarts the unit and sends a proof-of-life. It never prints the token
- Registered in ~/botctl/registry.json and /opt/botctl/registry.json (both backed up) and in watchdog.py UNITS with max_silence 0, because a wallet tracker is legitimately silent for hours
- **THE CORRECTION THAT MATTERS, and it reverses what I told Ben earlier the same day.** My rb3k shortlist was built by intersecting holders of his seven stated RH names. That intersection is a HOOD10 ARTEFACT: HOOD10 pushes an equal-weighted basket of PONS, STONKBROKER, CASHCAT, DELTA, AI, TENDIES, INDEX, HMM and YOLO to its holders, so "holds all seven" selects HOOD10 holders. 43 of the 68 hold HOOD10. Both wallets I recommended following, 0x2cffed5d56eb6a17662756ca0fdf350e732c9818 and 0x0e331a293bb22e93e2e5e417d84d755ae185da02, have **never spent WETH on Robinhood Chain** (0 out against 746 and 109 in). They cannot have bought anything. They are in the config as disabled entries with that reason recorded
- **The screen that is actually valid on this chain: require the token to have arrived FROM A POOL.** Get every pair address from DexScreener plus the Uniswap v4 PoolManager 0x8366a39cc670b4001a1121b8f6a443a643e40951, then keep only recipients whose Transfer sender is one of them. Of 867 wallets that truly bought both NET and DELTA, 246 did so before his 14 Aug call, and only 26 still hold the other five. Nine of the original 68 survive
- Enabled candidates: 0xcd211569a108fdac74d729ba7358a743a3c2e08d (candidate A, the strongest: real pool buyer of the whole book pre-14 Aug, zero HOOD10, 44.86 gSPEED on Base which is the largest in the set and he posted about gSPEED on 28 Aug, Base nonce 15,239, HyperEVM active) and 0x0a47f7e62e39f28126c213addf99e6277901c3fd (candidate B). Identity is NOT proven for either
- [stated] Standing rule reinforced twice now, once in strongtrader's own config and again here: a book that arrived by plain transfer or distribution is not a trading record. Before trusting any Robinhood Chain wallet, check that it has spent a quote asset. eth_getLogs on WETH 0x0bd7d308f8e1639fab988df18a8011f41eacad73 with topic1 set to the wallet answers it in one scan
- Self-test harness pattern worth reusing: import tracker.py via importlib, monkeypatch Telegram.send to capture, call emit() with a synthetic amount, and read the rendered HTML and button JSON before trusting a new alert format. Caught that a 500-token test buy was below min_usd_buy and silently rendered nothing
- NEW BOT 30 Aug 2026: **dinerodom**, unit dinerodom.service at /opt/dinerodom, own bot **@dinerodom_bot**, chat 357464289, commands enabled. Cloned from /opt/rbt3k. Tracks two @DineroDom0 candidate wallets on Robinhood Chain, PRIMARY 0x5d8e32fc29ddb1341e0563b4dc7de7587f4067ee and SECONDARY 0xdecba5d411253f488b49bfc0d581afe6428a0d2a, buys >=$200, sells >=$400, transfers off, 4s poll. Registered in ~/botctl/registry.json and watchdog UNITS. Full evidence in [[dinerodom-wallets]]
- Deliberately does NOT use require_own_tx: his 29 Aug MOO add was relayer-sent (tx from 0xD206704a8478314904F8fEa8F2eCda2600280aCB) with the USDG still leaving his wallet, and strongtrader's require_own_tx would have dropped it. Classify by TOKEN RECIPIENT, never by tx sender, on this chain
- Related trap worth remembering: 0xCC4bc7882D5ec4a3716e589aB3d816b61Ae47a1A looks like a whale with 281,088 transactions but is a Relay Protocol solver EOA with only 249 token transfers of its own. It signs and pays gas for other people's buys through RelayApprovalProxyV3 0xCcC88a9d1B4ED6b0EABA998850414b24f1c315bE and RelayRouterV3 0xb92fe925DC43a0ECdE6c8b1a2709c170Ec4fFf4f. 29 of its last 30 txs were user buys serving 35 different addresses. It is the bridge-in on-ramp so it is structurally buy-only; sells go straight to the UniversalRouter and never touch it
- RETIRED 30 Aug 2026 at Ben's request: **basewatch and bwbot**, both on @bk_basewatch_bot. Stopped, disabled, removed from watchdog UNITS (now 13 units) and removed from ~/botctl/registry.json. /opt/basewatch left on disk intact; its config token was blanked and muted set to true, with the original token preserved in /opt/basewatch/config.json.RETIRED-*. Fleet is now 14 bots. Deleting the @bk_basewatch_bot account itself is a BotFather step only Ben can do
- NEW BOT 30 Aug 2026: **followwatch**, unit followwatch.service at /opt/followwatch, fleet is now 15, all healthy. First bot in the fleet that watches a FOLLOW GRAPH rather than a wallet or a post stream. It alerts when @OGDfarmer (DFarmer, 127.9k followers, followed by unipcs and Sonder) follows a new account
- How it works: twitterapi.io GET /twitter/user/followings?userName=X&pageSize=200 returns followings MOST-RECENT-FIRST, verified empirically (page 1 held accounts created the previous day). So one page-1 call per 180s poll catches every new follow instead of paging 13,802 entries. Baseline pages the full list once on first start (70 pages, 3m49s, 13,802 ids, no alerts for any of them) and a silent full resync runs every 12h as a safety net in case ordering ever changes. Reuses xalert's twitterapi_key
- Token resolution is a three-tier ladder, highest confidence first: (1) a contract address in the display name, bio or the expanded t.co website, looked up on DexScreener; (2) a $CASHTAG in the name or bio; (3) a conservative ticker guess from the display name, its first word, or the handle with a chain suffix stripped. Every card reports the deepest-liquidity pair, the sibling count of other tokens sharing that ticker, MC, liquidity, 24h, the contract, and a chart button
- Two guards that came directly from the PVE and fake-DTF lessons: the guess tier needs a $10k liquidity floor (vs $2k for the address and cashtag tiers) and it needs a CHAIN HINT from the profile, otherwise it stays silent. Without the hint requirement the research analyst "!iquid" (@liquidspliff) resolved to a $19k Solana token called IQUID. Chain hints come from words in the bio and from handle suffixes with no word break, eg @hmmcatrbh, @sitonrh, @StonksxRH, which a plain \\b regex misses
- Measured precision before going live: 7 of 7 real project accounts resolved (HMM, STONKS, SIT, TWO, MAST, BIGLY, COAT, all correctly on Robinhood Chain), 5 of 5 human accounts correctly silent. End-to-end test fired two real alerts by removing two ids from state and restarting, then state was restored to 13,802
- Shares xalert's bot token @xalertben_bot and chat 357464289, so follow alerts and X post alerts land in one thread. Giving it its own bot is a BotFather step only Ben can do, then /opt/rbt3k/settoken.py swaps it in. Registered in ~/botctl/registry.json and watchdog UNITS
- Config knobs: poll_seconds 180, page_size 200, full_resync_hours 12, min_pair_liquidity_usd 2000, guess_min_liquidity_usd 10000, guess_requires_chain_hint true, preferred_chains robinhood/solana/base/ethereum, max_alerts_per_cycle 8. Adding a second handle is one entry in the accounts array, hot-read, and it baselines itself on the next cycle
- NEW BOT 30 Aug 2026: **earlybirds**, unit earlybirds.service at /opt/earlybirds, tag "RH EARLY BIRDS", cloned from /opt/dinerodom. Two Robinhood Chain wallets, both systematically earlier than DineroDom: EARLY HOLDER 0xf55c688510f75bf279315e496d430efa62eb1cf6 and EARLY FLIPPER 0x016837d5badcee5a1109d654c68be41f2150d57c. Full trade ledgers and the evidence in [[dinerodom-wallets]]
- Its config differs from every other tracker in one important way: **min_usd_buy 25 and min_usd_sell 50**, not the usual 200/400. Early Holder's average clip is $140 and its median position $98, so the standard floor would have hidden most of its buys. poll_seconds 3. If this bot ever seems noisy, raise the floor rather than assuming it is broken
- Both wallets were REMOVED from the dinerodom bot at creation, because two bots on one wallet doubles every alert, which is the fleet's oldest failure mode. A chain-wide duplicate check after the change found only the intentional rhtracker / rhtracker-base pair, which is one wallet on two chains
- Also disabled in dinerodom at Ben's direction: 0xfd6e86cf9aeca787cffb198bca859d459a44a847 and 0x01e7b7efee5150b83b4448f85ffee9d9e867e3c4. dinerodom is back to Dom's two wallets only
- Verified before handing over with the importlib self-test harness. Synthetic buy and sell both render market cap at fill, tx size, total holding with percent of supply, avg entry since tracking, and four buttons: token chart, DexScreener filtered to that wallet via ?maker=, holdings, wallet. GOTCHA for that harness: emit() takes kind="trade", not "buy" or "sell". Passing "buy" falls through to the transfer branch and is silently dropped while alert_transfers is false, which looks exactly like a threshold problem
- [stated] Ben supplied the BotFather token on 30 Aug. Live on its own bot **@rhearlybird_bot**, chat 357464289, commands enabled. Fifteenth distinct token in the fleet
- **NEW-BOT GOTCHA, cost two failed proof-of-life sends and it will recur on every future bot:** a brand-new Telegram bot cannot message a user until that user has opened the chat and pressed Start. Until then every send returns `400 Bad Request: chat not found`, which looks exactly like a bad chat_id or a broken payload. getMe succeeds the whole time, so it is not a token problem. Diagnose by reading the JSON `description`, not the HTTP code, and just ask Ben to tap Start. Two real alerts were logged but not delivered during that window: EARLY FLIPPER sold $494.30 of PROLOGUE at 08:15:22 and $306.50 of SPACEHOOD at 08:21:09
- SECOND FIX, applied the same session: the tracker renders the wallet's `label` into every alert as italic text, so the 450-character stats dossier appeared inside each message. Moved the dossier to a `note` key (the tracker ignores unknown keys) and set `label` to the short display names Ben asked for, EARLY HOLDER and EARLY FLIPPER. Alert length dropped from 1,328 to 866 characters. **Rule for every future bot: `label` is a display name, never a place to store research**
- Registered in ~/botctl/registry.json and in watchdog.py UNITS. Fleet is now 16 services
- NEW BOT 30 Aug 2026: **rhfee**, unit rhfee.service at /opt/rhfee, fleet is now 17 services. First bot in the fleet that watches an ECONOMIC TABLE rather than a wallet, a post stream or a follow graph. It polls the DefiLlama Robinhood Chain fee, revenue and dex adapters once an hour, keeps only protocols whose `chains` array is exactly ["Robinhood Chain"] (81 of them today), and alerts when one starts earning, accelerates, or trades cheap against its own revenue. Purpose: the chain mints ~7,000 pools a day, so screening launches is hopeless; screening the ~81 row fee table is not
- Zero dependencies, urllib only, no API key, no cost. Endpoints: api.llama.fi/overview/fees/robinhood?dataType=dailyRevenue and dataType=dailyFees, api.llama.fi/overview/dexs/robinhood, api.llama.fi/summary/fees/{slug} (its `address` field returns "robinhood:0x..." which is the free protocol-to-token join), then DexScreener tokens/{address} for mcap, FDV, aggregate liquidity and 24h flow
- Six triggers, deliberately tuned EARLY over CLEAN at Ben's instruction ("rather be wrong than late"): NEW (first appearance, rev24h >= $150), RAMP (24h revenue >= 2x the 7d daily average, min $400), WOW (week-on-week >= +150% on a >= $2,500/7d base), VOLRAMP (dex volume >= 2x the 7d daily average, min $200k/24h), VALUE (mcap / annualised 24h revenue <= 5x, min $250/24h), and **TOKENLIVE, the best one: a protocol already being watched that had no token and now has a tradeable one**. 24h cooldown per protocol per trigger, so a sustained ramp alerts once a day not once an hour
- Alert card carries: revenue 24h/7d/30d, dex volume, annualised revenue, ticker, mcap, FDV with the fdv/mcap ratio, cap/revenue, aggregate liquidity across all pairs, 24h buy/sell counts, a landing-copy keyword scan (payout language plus tokenised-equity tickers), the full contract address, and links to DexScreener, Blockscout, DefiLlama and the site. It prints an explicit "thin: aggregate liquidity under $100k, size will not fit" warning, because on this chain the binding constraint is depth, not detection
- Shares @bk_rhwatch_bot with rhtracker and rhtracker-base, chat 357464289, so all Robinhood Chain alerts land in one thread. Its own BotFather bot is a step only Ben can do; /opt/rbt3k/settoken.py swaps it in
- `slug_blocklist` holds robinhood-chain, arbitrum-nitro, opensea-seaport, layerzero, across. The chain's own sequencer revenue is the largest native line in the table and is not investable, so it must be excluded or it fires every day
- Daily heartbeat at 02:00 UTC, one line, so silence stays honest without an hourly update. Config key daily_heartbeat.enabled
- Cold start on 30 Aug fired 13 alerts, all legitimate native fee capturers: slvr, giga-v3, minepea, stockrip, pons-v2, letscash, alandale-v3, the-index, token.select, fables, agen.space, coinbarrel, robin. state.json was pre-seeded with all 81 known slugs first so NEW did not fire 81 times. Second scan fired 0, confirming the cooldown works
- THREE DEFECTS FOUND BY RUNNING IT, worth remembering: (1) value_trigger returned a bare None on one branch while the caller unpacked three values, so the whole scan died on the first tokenless protocol; (2) Telegram HTML parse mode rejects a bare `<=` or `>=` in the body as an unclosed tag, which silently 400'd the startup notice, so tg_send now falls back to plain text on any 400 rather than losing the alert; (3) week-on-week percentages explode on tiny bases (Robin fired at +47,992% on $1.2k of 30d revenue), hence the separate wow_min_rev_7d floor. Percent change without an absolute floor is not earliness, it is noise
- SHELL GOTCHA that cost two broken deploys: patching a droplet file through `ssh 'python3 - <<PYX'` with an UNQUOTED heredoc makes the shell strip one level of backslash, so `\\n` and `\\U0001F4E1` reach Python as a real newline and a real emoji and the file is written broken BEFORE ast.parse runs. Always use `<<"PYX"` for any patch containing backslash escapes, and always ast.parse BEFORE writing, not after
- Registered in ~/botctl/registry.json and in watchdog.py UNITS with max_silence 7800 (it logs hourly, so the usual short silence window would restart-loop it). Watchdog now covers 16 units
- RBT3K REWIRED 30 Aug 2026 after the live PDEX test (full detail in [[rbthreek-wallet]]). rb3k revealed $PDEX 0x5C9A6a0353f1EDC67B22f2cdcfE9294f5A889B60 on 29 Aug 12:26 UTC; 1,595 non-router wallets bought it from a pool before that tweet and NEITHER enabled wallet was among them. 0xcd211569a108fdac74d729ba7358a743a3c2e08d and 0x0a47f7e62e39f28126c213addf99e6277901c3fd are now disabled with the failure recorded. Enabled instead: 0xf154977eb88584660cb43467516126716938434a, the only wallet that bought PDEX before the reveal (29 Aug 05:11 UTC) AND bought all seven of his stated names from pools ahead of his posts. Config backed up, service restarted, healthy
- [stated] Ben caught this himself, unprompted, from reading the post: "I have a feeling that the wallets we have for him are not actually his wallets." Second time in two days his push-back beat my screen. When he doubts an attribution, test it before defending it
- Applied the label rule to rbt3k as well: the long candidate dossiers moved out of `label` into `note`, and `label` is now a short display name, so alerts do not carry 400 characters of research as italic text
- TWO RHFEE DEFECTS FOUND 31 Aug 2026 while reviewing its alerts, both cosmetic-looking and both misleading in practice. (1) **The 24h cooldown makes a standing condition look like fresh news.** The 08:32 batch of 12 alerts was byte-for-byte the 30 Aug 08:0x cold-start set minus robin — same protocols, same triggers, fired again the moment the cooldown expired. state.json `fired_at` proves it. Only 5 protocol-events in the whole 20h window were genuinely new: up-v2 (RAMP, VOLRAMP, WOW), up-v3 RAMP, orvex VOLRAMP, stonx VALUE+TOKENLIVE, pons-v1 RAMP+VALUE. Fix worth making: mark a card REPEAT vs FIRST FIRE, and suppress a repeat unless the underlying metric moved materially since the last fire
- (2) **"No token" from the card is unreliable, because it means "DefiLlama has no `address` field", not "no token exists".** 8 of the 17 alerted protocols had address=None, and UP is demonstrably tokenised: 0x57c0e45cb534413d1c20a4240955d6bb250bb4f1, ~$14.5M FDV, $1.92M main-pool reserve, $3.4M of 24h volume. rhfee fired on up-v2 three times and up-v3 once in that window and reported no token every time. TOKENLIVE can therefore never fire for any protocol DefiLlama has not joined. Fix: fall back to a GeckoTerminal `search/pools?query={name}&network=robinhood` lookup when `address` is null, keep only the deepest-reserve pool, and require a reserve floor so the ORVEX/FABLES-style $3-6k impersonator pools (four each, zero volume) are rejected
- Best find from that alert review: **SLVR**, full screen in [[slvr-token]]. $28,809/24h revenue against a $636k cap, cap/revenue 0.06x, team vesting only 2.40%, 869 buys to 125 sells on the main pool
- THREE BOTS RETIRED 1 Sep 2026 at Ben's explicit instruction, fleet 17 -> 14 services. **rbt3k** ("sending way too many trades, that's not the right wallet" — consistent with the 30 Aug finding in [[rbthreek-wallet]] that the attribution was never proven), **followwatch** (the @OGDfarmer follow-graph bot, "not interested any more"), **rhfee** ("not interesting to me either", one day after it surfaced SLVR). Each: unit stopped and disabled, config `muted: true` and `retired` stamped, bot_token blanked in the live config with the original preserved at config.json.RETIRED-20260901-*, /opt/<bot> left intact on disk. Removed from watchdog.py UNITS (16 -> 13) and from BOTH registries. Deleting @RBT3K_bot itself is a BotFather step only Ben can do; followwatch shared @xalertben_bot and rhfee shared @bk_rhwatch_bot, so those tokens stay live for xalert and rhtracker
- **THE DROPLET REGISTRY HAD SILENTLY DRIFTED AND NOBODY WOULD HAVE NOTICED.** /opt/botctl/registry.json held only 11 bots — still listing basewatch and bwbot (retired 30 Aug) and missing followwatch, rhfee, dinerodom, earlybirds, ghostwatch, strongtrader, rhtracker-base and ansem-rh entirely. The Mac's ~/botctl/registry.json was the accurate one at 17. Only the Mac copy is read by `botctl`, which is why status looked right the whole time. Rebuilt the droplet copy from the live unit list to 14 matching entries. **Standing check to run on every fleet change: diff the two registries, they are not kept in sync by anything.**
- Also learned: the watchdog unit is **watchdog-bots.service**, not watchdog.service. A restart script that guesses the latter fails silently
- DINERODOM CHANGE 1 Sep: **USDG and WUSDG trades no longer alert.** [stated] Ben: "you can ignore any trades of USDG because he's just selling the Robinhood dollar to then do something else, I'm interested in tokens." Implemented as a new generic config key `ignore_tokens` (list of lowercase addresses) with a guard at the very top of emit(), right after token_meta, so it suppresses buys, sells and transfers before any pricing or balance work. Config now carries USDG 0x5fc5360d0400a0fd4f2af552add042d716f1d168 (6 decimals) and WUSDG 0x4eff9ccd650f6990132d1d738446256ca405f320 (18 decimals), both symbols verified on chain first. The key is generic — copy the same four lines into any other tracker that needs it
- Verified with the importlib harness against a COPY of the state dir (never the live one): USDG buy $5,000 suppressed, USDG sell $5,000 suppressed, ordinary token buy $2,000 still renders and sends. PASS
- Applied the label rule to dinerodom too, which had been missed when it was applied to rbt3k and earlybirds on 30 Aug: four wallet `label` fields held 300-450 character research dossiers that were rendering as italic text inside every alert. Moved to `note`, labels now DINERODOM PRIMARY / DINERODOM SECONDARY / DINERODOM (disabled) x2
- Post-change verification: botctl status 14 healthy, botctl ping resolves all 13 tokens OK, watchdog restarted covering 13 units, and a chain+wallet duplicate sweep across all 31 enabled wallet entries in /opt/*/config.json returned none
- **GHOSTWATCH FULLY DELETED 1 Sep 2026** at Ben's instruction ("remove the 0x Ghost Watcher bot completely"). Unlike the other three retirements this was a real deletion, not a park: unit stopped and disabled, /etc/systemd/system/ghostwatch.service removed, daemon-reload + reset-failed, and **/opt/ghost-watch deleted from disk**. Dropped from watchdog UNITS (13 -> 12) and from both registries. Fleet is now **13 services**
- The ONLY recovery path is **/root/retired/ghost-watch-20260901-063943.tar.gz** (279,589 bytes, 13 entries), which the script verified contained config.json, tracker.py and the unit file BEFORE it deleted anything. That verify-archive-then-delete order is the pattern to reuse for any future full removal. @ghostwatcherz_bot's token was unique to this bot (fingerprint check across all /opt/*/config.json confirmed no sharing), so deleting the bot in BotFather is safe and is a step only Ben can do
- What it was watching, recorded here in case he wants it back: Robinhood Chain wallet **0x4123fFC81579e38dFFf74B75D8Bdf21c41e62237**, the confirmed @_0xghost_ wallet, which held all six tokens he posted contract addresses for (SESTRI, FIH, AI, QUOTRON, PRINTER, HOOKR) and bought five of the six BEFORE he posted them. Config was min_usd 1000, poll 5s, require_swap_event true, quote_tokens WETH only, buys and sells on, transfers off. The second wallet 0x96b13d47C17d61dC42F2152FA39Cf3acffc7eBfC was already disabled 27 Aug as NOT Ghost (0/6)
- **Useful one-liner for checking whether a bot's Telegram token is shared before blanking it:** sha256-fingerprint `telegram.bot_token` across every /opt/*/config.json and group by fingerprint. Current groups: crash-ton+crash-bot, rh-tracker-base+rh-tracker, ansem-rh+ansem-tracker, and singletons for gramwatch, x-alert, watchdog, strongtrader, dinerodom, earlybirds. Never prints the token
- Post-change verification 1 Sep: botctl status 13 healthy, ping resolves all 12 tokens OK, watchdog logs "12 units", `systemctl show -p NRestarts` is 0 on every unit so nothing is flapping, and no ghostwatch unit files remain on the box
- TRAP for the report, not the change: counting watchdog UNITS with a naive `'": ("/opt/'` string match undercounts, because some entries are aligned with extra spaces (`"crashton":  ("/opt/...`). It reported 9 when the dict really held 12. Verify the count by reading the dict or the watchdog's own "starting - N units" log line, never by string-matching the source
- STRONGTRADER CHANGE 1 Sep 2026: added wallet **0xF86a7c853B60C36f79F6E1dE0E7868d109E97004**, label "Birdeye Top Trader", enabled. [stated] Ben asked for this one wallet only, by that name, out of the eight-wallet Birdeye screen in [[copytrade-targets]]; he explicitly did not want the second recommendation added. Screen detail lives in the wallet's `note` key, per the label rule
- **NEW GENERIC CAPABILITY, worth copying to any tracker: per-wallet `min_usd_buy` / `min_usd_sell` overrides.** /opt/strongtrader/tracker.py previously read only the global threshold in emit(). Patched so it looks up the wallet entry in cfg["wallets"] first, falls back to per-wallet `min_usd`, then to the global keys, then to 0. Backups tracker.py.bak-20260901-064746 and config.json.bak-20260901-*, py_compile clean, service restarted, 8 enabled wallets, no behaviour change for the other seven
- WHY it was needed, and the general lesson: the fleet floor of $1,000 would have hidden this wallet's best entries. Its winning clips are SMALL even though its exits are large — TWO $1,701 across two buys, NTF $1,943, CHIPS $734, BONER $2,488 in one clip — so a $1,000 floor drops the 14.7x and 6.3x entries entirely. Set to min_usd_buy 400 / min_usd_sell 750. **Before setting a threshold on any new wallet, look at its historical BUY clip sizes, not its PnL.** Same lesson as earlybirds' 25/50
- require_own_tx stays true and does not blind this wallet: it sends its own transactions, mostly through 0x71305171a22f3fb8a67c6970838f77ad821d3c83 (159) and 0xd6577124f96394faee65afd2408f2ffa88445f63 (100). Note those same two routers also serve 0xDFA2e325f31D5c5CB4A093c9845555BE8764ED28, so shared tooling, not proof of shared owner
- **ARKHAM: still no key, confirmed by search on 1 Sep 2026, not just by memory.** Ben believed one existed in the skills. It does not: the only hit across /mnt/skills/ is the research-sources line listing Arkham under "needs a key Ben does not own", and a grep for arkham/arkm/ARKM across /opt and /root on the droplet and across ~/kookwatch, ~/.config, ~/.zshrc and ~/.env on the Mac returns nothing (the one /opt/ansem-tracker/state.json match is a false positive with no key context). If he says he has one, ask him to paste it rather than searching again
