- accounts/db: positions table + JSON store (add/list/owner/remove; main-wallet and cross-account guards)
- auth verify: asPosition links a second wallet without touching the session; position wallets can't mint a session
- /api/my/positions (chain-refreshed member ids, buyer counts, credits) + remove
- credits pooled across main + positions on /api/me, dashboard, campaigns; campaign charged to the best-funded position
- My line: own positions listed on level 1 as 'You · position N'
- Buy pane: Qualified Start card + Add a position flow + Buy-from picker with connected-wallet guard
- Wallet pane: Your positions card; chatbot answer updated
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- accounts.wall_offers (JSON, up to 2 offers: label, https link, banner) set from
Profile > Your wall; upload supported; locks show the buyer threshold.
- /api/wall assembles: position 1 = own line banner; positions 2-3 = own offer
when unlocked and set, else upline banners (only uplines with a live banner),
else house ads. Response carries unlocked + buyerCount; wall labels show
"this wall" / "their line" / "InstantAdPay".
- Chatbot canned answer + facts, follow-up email 7 mention the ladder.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Headless Playwright checks that boot a throwaway local copy for anything that
signs in or writes; npm run qa / qa:public / qa:member / qa:earn.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- /api/my/line sums TierPaid events per buyer for the signed-in member; the
downline rows show "+X POL" (what that person has paid you so far).
- AppKit featuredWalletIds: MetaMask, Phantom, SafePal, Coinbase first; Trust
Wallet stays available under All wallets.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Stops early when the wallet lacks the POL. Only Trust Wallet users see the
proportion warning (smaller package / add POL / other wallet); everyone else
goes straight to the wallet confirmation. Buy-pane tip + chatbot reworded.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Probe the session after connect and retry once after a "disconnected" send
error by wiping the stale session and re-opening the picker (Trust kills the
session after its own security stop).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Drafted through the Branded Voice engine (gain, logic, PAS, logic, honest
fear of loss, AIDA, gain + newsletter hand-off), checked against the contract
facts. {{paid:a|b}} resolves per reader from on-chain credits. Defaults now
live in drip-defaults.json; admin overrides still in the volume.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- /join/<token>[?v=angle] now serves a capture page (email first, wallet
later) with angle-matched hook copy, sponsor line, worked-example ledger,
how-it-works, live package ladder, and per-angle og tags (og:url keeps ?v=).
The sponsor cookie is set exactly as before; ?v= is remembered and stored
on the account as joined_via (shown in admin Members).
- drip.js: 4-step getting-started sequence (24h/48h/96h/168h) queued when a
free account is created with the pre-checked opt-in; ticker every 10 min;
signed /unsubscribe link in every email; MySQL + JSON storage.
- Admin > Settings: edit the sequence as JSON, reset to defaults, send any
step to the admin inbox; Overview shows follow-ups in flight.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Social posts for X / Facebook / LinkedIn / Telegram-WhatsApp with post and
share buttons; 5 SMS-sized Text-a-friend messages (Text it / WhatsApp /
Telegram / Copy); email swipes short, standard, long, follow-up.
- Banner kit adds 1080x1080, 1080x1920 and 1280x720 (tools/gen-social-banners.cjs)
with Download buttons.
- Printable half-sheet handouts at /flyers with the member's QR + invite link
(qrlib.js, two per letter page, cut line, print-one).
- Objection handling bank: truth + ready-to-send reply per objection.
- Invite-link strip, Branded Voice CTA, pill menu covers every kit; chatbot
canned answer + prompt facts updated. Angle links carry ?v= for the
upcoming hook pages.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Remove the "sign in with just your wallet" door from the sign-in card.
- A wallet-only session (no account) is walked to the email card with a
finish-setup note; verifying the code links that wallet to the account
and retires the wallet-only session, so member #, purchases and payouts
stay attached and username/profile work.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- /admin: email magic-code sign-in allowlisted to ADMIN_EMAIL, 12h admin
session (cookie iap.adm, persisted in the volume). Bearer ADMIN_PASSWORD
API access still works. Member area shows an Admin link for that email.
- House ads: admin places banner/text/login/solo/video/featured/visits
campaigns owned by house@instantadpay.com that cost nothing; budget is
only a delivery cap, spend is never charged or burned.
- Admin APIs: overview, all campaigns (+pause/resume any), members
(+re-point sponsor), reports (+resolve), pending burns, rates/site
config get+patch, creative upload.
- fmtPol rounds to two decimals everywhere (dashboard, toasts, prices).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The email fired at signup, before the member picked a username, so it always
said "A new member." Fire it instead when the username is first set (onboarding
/api/my/profile, empty->set transition only) so the sponsor's email names them
(@username). Removed the signup/verify triggers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Members were activating with sponsorId 0 (their own root) whenever their sponsor
couldn't be resolved — dead pre-wipe links (dkain) or no link at all — so their
purchases rolled to admin instead of building the tree. Default an unresolvable
sponsor to the configured catch position (siteConfig.defaultSponsorId, default
1) in both the buy path (/api/sponsor) and the free-activation path (/api/me),
guarded so #1 itself is never self-sponsored.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The prior commit changed wallet.js but the HTML still referenced ?v=r, so
cached browsers kept the old file. Point the HTML at ?v=t.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AppKit's modal.disconnect() can stall on the WalletConnect relay (mobile),
leaving the Disconnect button spinning forever ("just loads and says
disconnect") and never reaching the reload. Race the disconnect against a
1.2s timeout, and fire-and-forget from the button with a guaranteed reload
that drops in-memory wallet state so the picker returns.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Testers were tapping the highlighted "Most Popular" $50 tile's Buy thinking it
was a general buy, and getting a $50 charge they couldn't afford. Each tile
already buys its own package; label the button "Buy $20" etc. so it's
unambiguous which package a tap purchases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>