The app asks a user to approve a plan that gates almost nothing, then sends them across six screens to buy what the plan describes. The plan, the buyer, the domain list and the inbox order never pass information to each other. The fix is one pipeline, goal → domains → aging → inboxes → launch, where every screen shows the next step and the date it happens.
The plan page reads like a gate on everything you buy. In the code it is one number: a weekly dollar cap on queueing domain buys, kept in a collapsed "Advanced" box at the bottom of the older of two planners. Everything else the page shows is commentary. I confirmed each row below in the app and in the code.
| What the screen tells you | What actually happens | Where |
|---|---|---|
| Approve a plan before you buy. | Approval sets the weekly domain budget. It is checked only at the moment a domain buy is queued. Inbox orders and marketplace purchases never read the plan. | routes_buy_plan.py:292 |
| "We'll ask you to confirm again before anything is ordered." | Nothing asks again. There is no step that reads the approved plan at order time. | Plan & buy, approval box |
| Edit inputs, Review, Approve. | Editing drops the page back to draft, but buying keeps using the last approved revision. What you see and what gates you can differ, invisibly. | _weekly_cap |
| Lower the budget to slow buying. | Already queued buys still go through. The worker that buys checks only Pause and the spending switch, never the plan or the budget. | routes_domain_buys.py:559 |
| Pause buying: "The active plan keeps buying until you cancel it." | False for registrar buys. Pause stops every queued and drip buy at once. The sentence refers to an old buy-plan screen that no longer exists. | PauseBuyingDialog.tsx:43 |
| No approved plan: "Set a weekly domain budget in Settings." | Settings › Budgets is read-only and links back to the plan. The real field is Plan & buy › Price and approve › Edit inputs › Advanced. | routes_buy_plan.py:296 |
| Your plan: 40 inboxes on 20 domains. | That number never becomes a purchase. "Use calculated quantities" opens an empty inbox order that cannot continue because no domain is old enough. The domain buyer never shows the plan's count. | actions-v4.ts |
| Two planners on one page. | They disagree. New one: inboxes ready 7 Nov. Old one, same 40 inboxes: cannot start before 21 Nov (14 days aging plus 21 warmup). Reserve is 20% in one sentence and 15% in the next. | /plan |
Recommendation: keep one planner (the newer one, with launch date and sends a day), put the two spending limits on it in plain view, and end it with a real next step: "Buy the N domains you still need". Delete the contacts-and-emails wizard, the 24-hour quote, the review step and the acknowledgement checkbox. None of them protect money; the budget and the Pause switch do.
Money first, then things that make the app say something untrue, then broken screens, then confusion. Each has the fix I'd make.
Buy review, drip setup and Billing show only the year-one price ($3.79 for .pro). .pro renews at several times that. There is no domain renewal forecast in the app at all; Billing's "Upcoming renewals" lists inboxes only.
Fix: show "Year one $3.79 · then $X a year" on every price, and a "Domains renew" line in Billing grouped by month.
Pause buying stops all queued and drip buys, but its dialog says "the active plan keeps buying until you cancel it". A user who wants spending stopped will cancel every group by hand. The same switch is called "Pause new purchases" on Billing.
Fix: one name ("Pause all buying"), one sentence: "Nothing new is bought until you resume. 17 queued domains will wait."
Dynadot card: "Today's limit of 2 is reached." I then queued 3 more Dynadot names and all 3 were scheduled for the same day: 5 buys against a stated limit of 2. "On hand 12 of 10" counts names not yet bought.
Fix: either apply the daily limit to every buy on that registrar, or label it "Drip: 2 a day (manual buys are extra)". Count only bought domains as "on hand"; show queued separately.
Drip "Set up" for Spaceship defaults to 10 on hand, 3 a day, and all six endings ticked including .com. Save starts real, repeating purchases with no estimate, no max price per name and no review. It also allows Spaceship with no registrant contact, so buys would fail. "Switch off" has no confirmation at all.
Fix: a review line before Save ("About $38 year one, $X a year after, 3 a day until 10"), a max price per name, sane defaults (.pro or the plan's endings only), confirm on Switch off.
The demo workspace lists 755+ domains, including other clients' domains, because the same registrar keys sit in two workspaces. Every row says "DNS records added for you" (Wiring proves at least one is not on that registrar's DNS). The "hidden, belong to other workspaces" count changed from 443 to 217 between loads. The form at the bottom skips the 14-day aging rule, the plan and payment.
Fix: move this page to staff tools. Customers make inboxes through the order flow only.
Home, Decisions and Fleet health all say every domain and inbox passed. The Doctor is stuck at "1 of 10 checks done" with no reason. Home also says "Your first Bison scan is running" while Settings says "Never scanned".
Fix: one status line everywhere: "Not checked yet: your Bison workspace has never been scanned. Scan now." Never show "passed" over an empty set.
The only notifications are an engine run code (A45CDD490BB) reporting 2 ghost and 2 missing inboxes in a workspace that has 0 inboxes. It pops as a toast on every page. My 17 queued buys, the plan approval and the cancel produced no notification at all.
Fix: notify on things the user did or must do: domain bought, buy failed, domain ready for inboxes, order delivered, budget reached.
The button links to /domains/move; the page lives at /move, so the domain detail route catches it and says the domain "move" is not in your inventory.
Fix: one-line href change in domains/page.tsx:228.
/domains/dealhq.pro: "not in your latest Bison scan, your domain setup jobs or your engine inventory." It is in a setup job. There is no place to see one domain's price, registrar, buy time and ready date.
Fix: the domain page reads buy groups too and shows the timeline: queued → bought → aging until 23 Oct → ready.
Covered above. Also: the side panel mixes them ("40 new inboxes" next to "$54 a month", which is the other planner's 12 inboxes), and the page is 5.4 phone screens long with no buy button.
Fix: one planner. Mock below.
Domains header "0 of 0". Domain setup "0 aging". Settings "No domains yet". Landing Factory "No domains in this workspace". Inbox order "No fresh domains yet" with no date. Even after outbounddesk.pro and boostsystems.pro actually bought at 08:44 and 08:47, Inventory still said "This account has no domains recorded yet", the header stayed "0 of 0", Billing did not change, and no notification fired. The only place they appear is a row inside "Setup history".
Fix: every one of those reads the same pipeline and says "17 on the way, first ready 23 Oct".
Queued and cancelled domain groups both read "Bought on LG Porkbun". "View buy" drops you on a list of all buys. Live buys are filed under "Setup history", whose first line reads "Cancelled before acquisition." The last action's message is printed as loose text at the top and swaps position after each action.
Fix: tense follows state ("Buying on…", "Bought on…", "Cancelled"); live groups above history; messages as toasts on the group that changed.
Clicking "New inbox order" saves a draft before you choose anything. Billing's "New inbox order" does the same. Home and Orders then show stacks of identical empty drafts. Adding a marketplace item from inside a draft starts a separate cart and orphans the draft.
Fix: save a draft on the first real choice; the marketplace adds to the open draft instead of a second checkout.
Proxy pools and runner tokens (aydan-pc, charles-pc) under Registrars, raw account ids, a "token variable name" field in Agency › General, Failures and Wiring in the sidebar (cross-tenant), the full engine rules spec under Rules, raw dicts in Check history.
Fix: one "Staff" area, hidden unless staff. Customers see a 3-line summary of their rules.
Weekly domain budget (Plan › Advanced), monthly inbox cap (same box), monthly new-purchase limit (Billing), drip "at most a week" (each registrar). Billing says "No purchases yet" beside "$56.03 of new purchases this month" (the registrar buys, paid on your own registrar accounts, counted invisibly). Card payments are not connected, so there is no way to add funds.
Fix: two limits on the plan screen (domains a week, inboxes a month). Billing shows "includes $X of domains on your registrars".
It changes the dates shown and nothing else. Aging is hardcoded at 14 days and warmup sits in a hidden field, so a customer who needs inboxes in a week has no way to say "go faster" and no screen that tells them their date can't be met.
Fix: one pace setting (Careful, Standard, Fast, Custom) that the plan schedules backwards from the launch date, with three ways to close any gap. Mock F.
"Cancel unstarted domains" kills every name left in the group. To drop one weak name you cancel the batch and queue the rest again.
Fix: a Remove link on each row not yet bought; the group button says "Cancel the 2 not bought yet". Mock G.
Every screen answers two questions: where am I in the pipeline, and what is the next thing that happens, on what date. Each step's main button lands on the next step with the right numbers already filled in.
Launch date, sends a day, inboxes per domain, two spending limits. Shows what you have, what is on the way, what is missing.
Buy 3 missing domains →Count prefilled from the plan. Registrar, names, year one and renewal price, buy dates, budget left.
Queue 3 →Queued, bought and aging, ready, has inboxes. Each domain has a date.
Order inboxes for arriving domains →Reserves inboxes on domains still aging; starts each one the day it turns 14 days old.
Approve and pay →Per batch: started, days left, score. Problems become Decisions.
See launch readiness →"40 of 40 ready on 13 Nov, 3 days before launch." Home shows this bar.
Send in BisonNumbers are from Warming Pod as it stands (40 inboxes needed, 17 domains queued). Prices after year one are left as $X because the app doesn't show them yet.
Your plan: 640 a day → 40 inboxes, ready 7 Nov · 20% reserve
Batch assignment: empty table · [Confirm batch assignment]
Price and approve a plan: contacts × emails ÷ days → ready 21 Nov · 15% in the banner
Advanced (collapsed): Weekly domain budget $120, warmup 21, monthly cap
Review → 24h quote → tick acknowledgement → Approve
No buy button. 5.4 phone screens.
Ready by 13 Nov, 3 days before launch (14 days aging + 21 days warmup).
This week: $64.43 of $120 used by queued domains. Domains renew at $X a year each.
Saved 08:31 UTC. Changing a limit applies to the next buy, and queued buys are rechecked against it.
Removed: the second planner, the quote, the review step, the acknowledgement, batch assignment (until inboxes exist), "separate new fleet" (move to an advanced toggle with one sentence). The weekly $ figure "64.43" is illustrative.
Drip cards first: "On hand 12 of 10" (all 12 queued), "Today's limit of 2 is reached"
Then a 3-step wizard: Account → Names → Review
Review: Year one $3.79. No renewal, no dates, no budget.
Queue → silent redirect to Domain setup › "Setup history"
Your plan still needs 3 domains. Budget left this week: $55.57.
| Name | Year one | Then | Buys | Ready for inboxes |
|---|---|---|---|---|
| dealhq.pro | $3.79 | $X/yr | today 10:25 | 23 Oct |
| outbounddesk.pro | $3.79 | $X/yr | today 08:44 | 23 Oct |
| bookeddesk.pro | $3.79 | $X/yr | today 11:08 | 23 Oct |
After queueing: lands on the domain pipeline with these 3 highlighted, toast "3 domains queued. First buys at 08:44 UTC."
The "keep on hand" drip moves to its own small card under the wizard, with a price line and a confirm on Switch off. "On hand" counts bought domains only; queued shows beside it.
Header: Domains 0 of 0
"0 domains waiting for their first inbox setup. 0 aging."
Setup history: "Cancelled before acquisition." then live groups mixed with cancelled ones, newest first
Queued domain detail page: "not in your domain setup jobs"
| Domain | Registrar | State | Next |
|---|---|---|---|
| outbounddesk.pro | Dynadot | Queued, checked | buys today 08:44 |
| boostsystems.pro | Porkbun | Queued | buys today 08:47 |
| handshakeworks.pro | Porkbun | Queued | buys 10 Oct |
Cancelled (19) collapsed at the bottom. Each row opens the domain page with its full timeline.
Draft saved the moment you clicked "New inbox order"
"No fresh domains yet"
Six links to get domains (two destinations)
Marketplace "Add" opens a separate cart; the draft is orphaned
Your plan: 40 inboxes on 20 domains. 0 domains ready now, 17 ready 23 to 25 Oct, 3 not bought yet.
34 inboxes on the 17 domains on the way. Each domain gets its 2 inboxes the day it is 14 days old. Billing starts per inbox when it is created.
From stock, warmed N days. Fills the gap if launch is close.
The draft is saved on the first choice, not on page load. The marketplace adds to this order instead of a second checkout. If reserving on aging domains is not possible yet, the minimum fix is the sentence "17 domains arrive 9 to 11 Oct and can take inboxes from 23 Oct. We'll remind you."
"Scanned Never" + "Your first Bison scan is running" + "Every domain and inbox passed its last check"
"Batch assignment not confirmed"
Tiles: Sending now / Live batch warming / Ready reserve / New reserve
Two identical empty drafts
Next: 3 domains still to buy. Buy 3 domains
Not checked yet: your Bison workspace has never been scanned. Scan now
Launch date is a field that only changes the dates shown. Nothing buys, warms or schedules toward it.
Aging is 14 days, hardcoded in four API files. Nobody can change it.
Warmup (21 days) lives in the old wizard's Advanced box.
Pick a launch a week away and nothing says it can't happen or what to do about it.
| Pace | Aging | Warmup | Ready by, if you buy today |
|---|---|---|---|
| Careful | 30 days | 28 days | 9 Dec |
| Standard | 14 days | 21 days | 16 Nov |
| Fast | 3 days | 14 days | 29 Oct |
| Custom | 0 to any | 0 to any | calculated |
Working back from launch: buy domains today · inboxes go on each domain the day it clears aging · each inbox goes to Bison the day its warmup ends.
Dates allow up to 3 days for setup after aging (DNS, Gmail, DKIM), the most Google allows. It usually takes about an hour, so inboxes are often ready up to 2 days sooner. Each step starts the moment the one before clears.
The launch date becomes the goal and pace becomes how you get there. Pace is one workspace rule that every screen reads, replacing the four hardcoded 14s and the hidden warmup field. When a launch can't be met, the plan offers three ways to close the gap instead of a warning; this is where the marketplace earns its place, as the answer to "I need inboxes sooner".
Customers can go as fast as they like. Every domain has its own tenant, so going fast only puts their own domains and inboxes at risk. Choosing Fast or Custom is a one-time, on-the-record choice ("New domains warmed fast burn more often. Replacements are billed as new inboxes."), logged in Decisions with the date, then never nagged again.
Setup: promise the safe date, deliver early. Google allows up to 72 hours before DKIM can be switched on; our first test had it in about an hour. Ready dates allow the full 3 days so a launch never rests on the best case, but nothing waits out a timer: poll, start each step the moment the last clears, and say what is happening when a step runs long ("DKIM is taking longer than usual: 6 hours so far, Google allows up to 72"). Log real setup times per tenant so the estimate stays honest.
Open question for the supplier: do they track burn or abuse rate per reseller account? If yes, watch it on a staff dashboard rather than limiting customers.
One button per group: "Cancel unstarted domains"
One bad name means cancelling all of them and queueing the rest again.
Confirm reads "Cancel 1 unstarted domains?"
| Domain | When | State | |
|---|---|---|---|
| outbounddesk.pro | ready 23 Oct | Aging | |
| dealhq.pro | today 10:25 | Checked | Remove |
| bookeddesk.pro | today 11:08 | Checked | Remove |
Small API change: the cancel endpoint (routes_domain_buys.py:396) already loops item by item; it takes an optional domain and cancels only that row. The budget release already sums only uncancelled items, and the group already turns "Cancelled" only when every item is. A row already being bought stays locked, as today.
| Thing | Where | Verdict | Why |
|---|---|---|---|
| "Price and approve a plan" wizard (contacts × emails ÷ days) | Plan & buy | Merge | Second planner that disagrees with the first. Keep only its two spending limits. |
| 24-hour quote, Review step, acknowledgement checkbox | Plan & buy | Cut | Approval orders nothing and the quote is never used. |
| "What you have" rows and "Domains you bring" box | Plan wizard | Cut | Bring-your-own domains here "give no purchase credit". It asks for input that does nothing. |
| Batch assignment table and Confirm button | Plan & buy, Home banner | Hide | Shown with zero inboxes. Show it once inboxes exist, or assign automatically. |
| "Build a separate new fleet" checkbox, ticked | Plan & buy | Hide | Default for a first fleet is meaningless; for an existing fleet it needs one plain sentence. |
| Fleet sizing card, "Build the plan" step | Doctor | Cut | Third and fourth sizing tools. Doctor and Plan point at each other in a loop. |
| Provision inboxes page | Domains header | Staff | Shows the agency portfolio, skips aging, plan and payment. |
| Proxies, runner tokens, account ids | Settings › Registrars | Staff | Operator plumbing in a customer setting. |
| Page hosting accounts, "token variable" field | Agency › General | Staff | Asks a customer for an environment variable name. |
| Failures and Wiring links | Sidebar footer | Staff | Cross-tenant data. Fine for LeadGrow, must never render for a customer. |
| "View complete saved rules" engine spec | Settings › Rules | Rewrite | Replace with 3 lines: spare share, when an inbox is replaced, bounce limit. |
| Sending policy tab | Settings | Cut | The tab says it isn't part of this workspace. |
| Do-not-contact list | Settings | Cut or explain | Mail Legion doesn't send campaigns. Keep only if it syncs to Bison, and say so. |
| Budgets tab (read-only) | Settings | Merge | Points to the plan, which hides the field. Two "Reserve" rows the server drops, so always "Not set". |
| Engine daily-run notifications | Notifications, toasts | Cut | Raw codes, wrong scope, repeats on every page. |
| Check history raw tables | Reports | Staff | Dicts like "would_pause: 0 · push_enabled: False". |
| Evidence tab | Reports | Fix or cut | "It did not answer." |
| Recovery pool | Domains, order wizard, BYO | Merge | Third word for spare capacity (reserve, ready reserve, new reserve, recovery pool). |
| Marketplace as a hidden tab | Domains | Decide | Linked from 3 places, absent from the tab bar. Make it a tab or a step inside the order. |
| "Unavailable" rows in Compare supply | Marketplace | Hide | Half the comparison is rows you can't buy. |
| "Keep warming 12 weeks", ticked by default | Marketplace checkout | Explain | On a product sold as pre-warmed, with "warmup age not reported" and an undefined "free period". |
| "Allow exports" banner | Orders | Move | A one-time setting, belongs in Settings › Bison. |
| Sender profiles / Personas / senders | Inboxes | Merge | One thing, three names; tab labels change between views. |
BuyPlans.tsx, BuyPlanRequests.tsx | Code | Delete | Imported nowhere. The pause dialog still talks about them. |
| Use | Instead of |
|---|---|
| Plan (the fleet plan only) | fleet plan, infrastructure plan, capacity plan, buy plan, Plan capacity, Plan & buy, "Build the plan", decision plans |
| Spare inboxes | reserve, ready reserve, new reserve, recovery pool |
| Pack → Rules with its name ("Lean") | Lean pack · revision 1, confirmed pack, mandate |
| Buy domains | Acquire domains, Get domains, Buy new domains, Domain setup |
| Domain pipeline (queued, aging, ready, has inboxes) | Domain setup, Setup history, inventory, "on hand" |
| Pause all buying | Pause buying, Pause new purchases |
| Sender profile | persona, sender, profile |
| Home | Overview (404 page, logo link) |
| Batch means one thing | Live batch on Plan vs "each batch we set up together" on Launches |
Grouped by screen. Open the ones you care about.
?tab=mandate, Members ?tab=team; guessing ?tab=rules silently shows Connections./, not the workspace.Paths are from maillegion-app at 220241b. Quick wins first.
| Fix | Where | Size |
|---|---|---|
Move domains link goes to /domains/move; page is /w/[ws]/move | web/…/domains/page.tsx:228 | Tiny |
| "No sends of sends" when sent is 0 | web/…/inboxes/page.tsx:486-487 | Tiny |
| Pause dialog sentence "active plan keeps buying" is false for registrar buys | web/…/domains/PauseBuyingDialog.tsx:43 (and its check) | Tiny |
| "Set a weekly domain budget in Settings" points to the wrong place | api/routes_buy_plan.py:296 | Tiny |
| "Cancel 1 unstarted domains?" plural | web/…/domains/SetupHistory.tsx:294 | Tiny |
404 and error pages: "Go to Overview" links to / | web/…/(app)/not-found.tsx:26, error.tsx:44 | Tiny |
| Cancel one domain in a buy group (optional domain on the cancel endpoint, Remove link per row) | api/routes_domain_buys.py:396, web/…/domains/SetupHistory.tsx | Small |
| Log real setup times per tenant (verify, Gmail, DKIM) to drive the "usually about 1 hour" estimate | provisioning worker | Small |
| Drip "Switch off" needs a confirm | Buy domains drip card | Small |
| Orders: tense by state ("Buying on", "Bought on", "Cancelled") | Orders list | Small |
| Domain detail reads buy groups (queued, aging) instead of "not in your setup jobs" | web/…/domains/[domain] | Small |
| Inbox order draft created on page load; create on first choice | New inbox order route | Small |
| Idempotency-Key on domain-buy create (double submit can double-queue) | buy wizard action | Small |
| Spaceship names with no quoted price always 422 (no max price field) | buy wizard + routes_domain_buys.py | Small |
| Budgets "Reserve inboxes / Reserve maximum" dropped by the server | Settings › Budgets | Small |
| Launch date sometimes not saved though "Plan saved." shows | web/…/plan/PlanV4.tsx:76-88 (form state vs preview race) | Small |
| Show renewal price next to year one (buy review, drip, billing forecast) | registrar price lookup + UI | Medium |
| Daily drip limit vs manual queues: one rule for a registrar's day | routes_domain_buys.py scheduling | Medium |
| Decide: should the worker recheck budget and plan at buy time? | routes_domain_buys.py:559 run_due | Medium |
| Doctor stalls at 1 of 10 with no reason; "passed" shown over zero checks | Doctor, Home, Fleet health | Medium |
| Provision inboxes: hardcoded "DNS records added for you", unstable hidden count, agency-wide list | web/…/domains/provision | Medium (or move to staff) |
| Notifications scope: engine client code reported in a pod workspace | notifications feed | Medium |
| Wiring pre-purchase check uses fixed names that are now taken | wiring check | Small |
| One planner: merge legacy wizard into v4, budgets on the plan | Plan.tsx, PlanV4.tsx, app.py:7669-7781 | Large |
| Domain pipeline view and counts that include queued and aging everywhere | Domains, Home, Landing Factory, order wizard, Settings | Large |
| Staff area: Provision, proxies, runners, hosting accounts, Failures, Wiring, Check history | nav + settings | Large |
| Pace setting (aging and warmup per workspace) replacing the hardcoded 14 days; plan schedules back from launch and offers ways to close a gap | routes_domain_buys.py:46, fleet_v4.py:60, routes_domain_connects.py:71, routes_orders.py:427, plan UI | Large |
| Delete dead UI | BuyPlans.tsx, BuyPlanRequests.tsx | Tiny |