The PolHunter launch card, the Five Dollar Friday card and the "you came from
PolHunter" card repeat on a timer, so anyone who has read one had no way to
clear it. Each now carries a small close control in its heading: the card
stays hidden for the rest of that browser session and comes back at the next
sign-in, so a message nobody acted on is never lost for good.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Marty (2026-09-19): partner codes keep working with no monthly cap, but the credits stop
being a dump. A fifth is paid the day the code is redeemed, then a fifth on each day the
member finishes their daily ad set, five days in all, and whatever is unclaimed 30 days after
redemption is never paid. Same headline number for the partner to promote; paid only to
people who show up. 49 of the 52 PARTNER redemptions were still sitting on 500+ unspent.
Every redemption now records the site the ?promo= link was opened from (iap.promoref cookie
set from the Referer at link-open), so a code's traffic can be attributed to the partner page
it was supposed to come from. Member-funded codes charge the funder one installment at a
time; a short funder leaves the installment due, not lost.
Pre-drip redemptions were paid in full up front and are marked paid=credits at start-up, so
nothing is paid twice. Covered by a module test (13 checks: per-day cadence, gaps, rounding,
expiry, pre-drip rows, declined payer) and an end-to-end HTTP test on a local copy (9 checks).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Marty (2026-09-19): once a membership gets large, My line and the other lists scroll forever.
One helper, pageList(), pages what each pane already renders: rows past the current page are
hidden and a solid-mint control bar (row range, Previous/Next 25, page count) goes under the
list. Row markup and bindings are untouched, so it goes on every growing list at once: My
line per level (the Activity drawer pages with its row, and a chip drill-down opens on the
right page), the Overview roster, the Campaigns table and its credit ledger, each Pipeline
column, and the Messages thread list. Asset tags bumped.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Splitting system messages out of the modal was right for payout receipts,
but it would have silenced two that a member genuinely loses money by
ignoring:
- "@someone is trying to buy. Link your wallet so it pays you" — a sale
is blocked right now, and the referral is lost permanently once it
routes to someone else.
- "You missed 43 POL on InstantAdPay" — a payout passed them by, and the
message explains exactly how to stop the next one doing the same.
So the dividing line is not system-versus-human, it is "does this need you
to do something". Those two become kind 'alert' and still interrupt; the
receipts stay kind 'notice' and stay in the inbox.
The modal no longer credits an alert to a person either. It was saying "A
message from @martbost" over machine-generated text, because system mail
is sent by member 1 at ADMIN_EMAIL. Alerts now read "Action needed on your
account".
Also reclassified "X is now in your line for good" as a notice — it is
good news about a referral the member gained, with nothing at stake.
qa/messages-notice.mjs now covers both lanes: 14 checks, including that an
alert interrupts and a flood of 25 receipts does not.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Marty hit this viewing daily ads: a popup after every single return to
the dashboard, each one a different payout notice.
Two causes, both fixed.
The channel was shared. "You just got paid 40.8923 POL" and "Welcome to
my line, here are your first three moves" were stored identically, as
kind 'broadcast' — the kind the sign-in modal is meant to interrupt for.
Dismissing one just promoted the next unread notice, so a backlog became
a carousel. System messages are now kind 'notice': they land in the
inbox, count toward its badge, and never pop. Only a message a person
actually wrote can interrupt.
The modal also had no memory. loadDashboard() runs on far more than
sign-in — after every ad view, campaign edit and chat close — and it
re-popped each time. It now shows at most once per page load and never
twice for the same message.
The 79 existing machine-generated rows are retagged by a migration in
ensureSchema, 15 of them unread and currently popping. Matched on
subject rather than sender on purpose: these come from member 1 at
ADMIN_EMAIL, which is also Marty's own member address, so his genuine
broadcasts sit under the same sender and must be left alone. Verified
against the live data first — "Credits returned: a counting error on our
side" and the broken-banner note are his, and stay as broadcasts.
qa/messages-notice.mjs covers it: a flood of 25 notices produces no
interruption, the human message still does, and chat stays in its own
lane. Member walk clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Campaign #134 (NAS ad 2806) had the member's join-page link in the image field and served a broken
banner 141 times on the network; #102 did the same with an imgbb page link. imageCheck() accepts
/uploads/ and instantadpay.com/banners files and image extensions outright, otherwise HEADs the URL
(one redirect) and requires image/*. Wired into member and house campaign creation for banners and
login-ad creatives. Form placeholder + hint point at Promo tools > Banners > Copy image URL.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Marty, 2026-09-16, after the third "why didn't my sponsor get paid" thread of the day (livedreams / gracie25,
Morten / Terry's linked wallets). GET /api/my/trace?who=<#|username> (yourself, anyone up to 3 levels
below you, or your own 3 uplines) and GET /api/admin/trace?who= list every purchase the member was
part of, newest first: buyer, sponsor, package, then levels 1-3 with the recipient, the skipped
positions and the reason ("not qualified: had N of 2 qualifying buyers then"), the platform share, and
a verify link. Linked extra wallets are named after their owner. Earnings tab card "Trace a payment";
admin member card gets a "Payment trace" section; chatbot canned answer + AI prompt route the
question there.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Marty, 2026-09-16: a purchase must never wait on a sponsor who has not linked a wallet / switched on
payouts. /api/sponsor and /me now walk the site's sponsor line upward to the first upline that is
activated with payouts on (not on the no-payout list) and the buy proceeds under them; nobody
activated -> the catch position (#1). The buyer sees a one-line notice and carries on; admin gets a
heads-up. When the position is created on-chain (MemberActivated), sponsorSyncOnEvent re-points the
buyer's stored sponsor to whoever was paid (#1 included), so the referral leaves the skipped
sponsor's line for good, then sends the pointed notices: the skipped sponsor(s) are told they lost
this referral permanently and how to switch on payouts; the sponsor who was paid is told the
referral is theirs for good and to coach the one who missed it (teach-forward). Admin lookup:
GET /api/admin/sponsor-resolve?ref=. Only a transient chain error still pauses a buy.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Hugh earned a 10-credit sign-in bonus, then bought a 280-credit extension; earned credits spend
first, so Earned read 0 and the bonus looked lost. There was no record a member could check.
Now every real credit movement writes a credit_log row (JSON store in file mode): welcome, sign-in
bonus with streak, daily claim, inbox reads, video watches, verified visits, milestone/leaderboard
bonuses, partner codes, admin/team grants, AI Copy Engine charges + refunds, flat buys (featured
run, extension, visit pack, more visits), login-ad days, and metered delivery rolled up per
campaign per day; on-chain purchased-credit spends are marked. GET /api/my/credits/activity;
dashboard shows the last 40 under Campaigns (also when there are no campaigns) with a link from
Earn credits. Chatbot canned answer + AI prompt updated; my.js tag bumped.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Hugh extended his featured link and his balance fell 1,133 -> 0: the row still carried the 1,400
parked by the old add-credits button, and reactivating it reserved that budget again. Both flat
paths now set budget to spent + accrued + cost. Featured Run column shows "N of M days left"
(Hugh: "says 14 but it's actually 7") and "M days done" after the run.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Hugh and Michael both added credits to a featured run expecting more days. Featured is a flat
daily buy, so the credits sat unspent until the run ended and their balance read 0 meanwhile.
Now: /api/my/campaigns/:id/extend books extra days after the current run (or from today if it
ended) and charges them at once; top-up refuses featured; a visits top-up buys more visits at the
pack rate, charged now; rows show paid up front + day count + runs through <date> / run ended;
explainer under the campaign table; chatbot canned answer + AI prompt updated; my.js v tag bumped.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Spark: one-tap banner/text campaign templates aimed at the member's link (POST /api/my/toolkit/template); printable QR handout at /handout/<username>
- Circuit: videomaker.js renders every promo video/short with the member's end card + QR via ffmpeg (Dockerfile adds ffmpeg + ttf-dejavu), hosted in Spaces; split tester compares join angles
- Nexus: Leader Ops (three-level triage, stalled flags, one-click nudges), AI team broadcast kind + Send to my team, credit grants from the leader's earned pool, co-branded join page (sponsor bio for level-3 sponsors), member-funded partner code (promo_codes.funder; redemption charges the funder) + /partners?ref=<username>
- toolkit ladder all live; chatbot updated; my.js v20260914e, site.css v20260914c, join.js/partners.js v20260914a
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>