Ainsworth Program — Roadmap

Cross-project status for the website, merchant portal, and ticketing system
Updated 2026-08-07 (a second five-shipment day landed 2026-08-06 — the iBill → Esquire channel with a sixth MPA paper and its guided e-sign, abandon closing in-flight placements, the partner activation fix with W-9 + payout account name, and the executed agent-agreement download; nothing is left unmerged)
Phases 1-9 + Residuals + Intake API + pricing + CRM P1–P5 shipped · next is the first real processor month through /admin/import (the P4 exit gate) and the P6 grill · two engineering items now carry a hard pre-North ordering: the MPA-queue entry gate and the abandon/create race residual
Delivered / Live
In Progress
Ready / No Dependencies — can start now
Deferred / Blocked
Public Website LIVE ✓
✓ Done

Agent-focused site built (Next.js 16)

Four pages live: /, /agents, /merchants, /about. Positioning locked, compliance pre-flight integrated.

✓ Done

Launch polish

Favicon, OG card, Vercel Analytics, Resend-backed apply forms, /thank-you confirmation. Forms verified delivering to mark@.

✓ Done

Jeff's review — approved

Positioning + content signed off 2026-05-27.

✓ Done

Vercel project moved out of QuickRefund

Project relocated to Mark's personal Vercel ownership before the domain swap.

✓ Done

ainsworthpayments.com is LIVE

Shipped 2026-06-06. Domain now points at the new Next.js site; old static page retired. Closed workstream.

Merchant Portal Phases 1-9 + Intake API + pricing shipped

✓ Shipped — production

✓ Done

Phases 1–4 — foundation

Auth (magic-link, AES-256-GCM sessions, 5 roles), Postgres + PII encryption, Tier 1/2/3 intake, dashboard, uploads, admin flow. In production at merchants.ainsworthpayments.com since 2026-05-17.

✓ Done

Phase 5 — canonical-schema MPA generation

Full loop verified end-to-end in prod: signup → 9-step canonical wizard → admin generates filled MPA → send-for-signature → embedded Dropbox Sign → webhook finalize → signed PDF auto-links to I.1 checklist. Banks wired: Merrick, Maverick/TSYS, Synovus, PB&T.

✓ Done

Phase 6 — partner financial reporting

ISO residual portal shipped 2026-05-30. Single + bulk entry, month/range/CSV views, edit + list pages, atomic CTEs with diff-aware audit. Dogfooded in prod against ATR + QuickRefund books.

✓ Done

Phase 7 — Utah automation tracer + docs-ready

Tracer shipped: extraction → 7 deterministic rules → readiness verdict (GO/HOLD/NO-GO). Docs-ready workflow trigger shipped 2026-06-04 (email Mark+Jeff inline + iMessage to Mark via local launchd poller every 15 min). 57 fixture tests.

✓ Done

Synovus + PB&T MPA generators

Corduro/Priority template family — 497-of-499 fields shared via corduro-base.ts. Full e-sign loop verified in prod 2026-06-03. Iron Peak's path to bank submission once he finishes the wizard.

✓ Done

Residual Automation tracer

Schema + entry pivot + agent CSV + colorblind-safe PDF shipped 2026-05-30. Agent statement = net interchange + net fee × split %. Full vertical dogfooded in prod.

✓ Done

Admin intake card + canonical migration (this week)

"Application intake" status card on admin merchant page. Migration tool moved 4 merchants from legacy → canonical (Iron Peak, Deep Water, Alpha Peptides, test). 2 legacy remaining by design (Enhanced Wellness, Paragon — already transcribed).

✓ Done

Partner Intake API v1 — all 6 slices

Shipped + live 2026-07-05. Full /api/v1/merchants surface for partner-programmatic intake. Open follow-ups: webhooks, spec v0.2, integration guide.

✓ Done

Partner org structures — master merchants, agent hierarchy, referrer visibility

Live 2026-07-06 → 07-10. Master-merchant orgs with commission-attribution invariants; sub-agents under partners with per-category splits (Model 1); referrers see their referred partner's in-progress pipeline read-only (status-only, no bank identity).

✓ Done

Merchant abandon/restore + Statement Analyzer

Both live 2026-07-07. Admin abandon/restore archives merchants out of every active + partner surface, restores to prior status. Partner-facing statement→savings analyzer at /partner/analyzer (PDF upload, interchange audit, flat-rate/tiered estimates).

✓ Done

Phase 9 — placement consolidation

Merged + live 2026-07-08. Placements now own the merchant-placement lifecycle end to end; the legacy bank_submissions path is retired/archived. Deferred list closed empty.

✓ Done

TRX MPA generation — trx-v2 paper + full fill fidelity

Live 2026-07-10 → 07-13. Remapped to the 6.2026 5-page TRX paper (stale 4-page blobs 409 on send); radio groups, checkboxes, and deal pricing now fill; Acrobat "error 18" fixed for good (MuPDF bake instead of pdf-lib flatten); generated MPAs ship clean (empty-field highlighting opt-in); ISO-level template resolution defaults the picker.

✓ Done

E-sign hardening — two-signer TRX + party-signer resolution

Live 2026-07-13, closed 07-15. TRX packages now sign in parallel: merchant (3 signature lines) + Ainsworth inspector (survey line); Personal Guarantee dropped per TRX paper. Signer resolves from the application's primary-contact party (all banks), with login fallback. First three TRX MPAs fully executed by both signers.

✓ Done

Pricing models — partner defaults + per-merchant editor + cost-plus / flat / tiered

Live 2026-07-12 → 07-15. Partner-level default deal pricing auto-seeds new merchants (trigger, all create paths); admin per-merchant Pricing card overrides it; three models flow into the trx-v2 MPA pricing sections. Shared fieldset + save hook — both editors can't drift. Surcharge / cash-discount stay manual on the MPA.

✓ Done

Post-mortem patterns 1/3/4/5 — fully remediated

Merged + live 2026-07-12. Bank-paper mapping fixes, 17 DB CHECK constraints + pricing trigger, load-bearing verify scripts, dual-flow (legacy vs canonical) owner surfaces. Every stored MPA now requires regenerate-before-send (canonical shape versioning).

✓ Done

Underwriting completeness — sponsor-bank fields, Section B KYC fix, post-submit resurface

Live 2026-07-11. Statement phone + card-present% + refund stats collected self-service in the portal and hard-gate TRX MPA generation; owner photo-ID/KYC checklist items now generate for canonical merchants (backfilled); pre-gate submitters see "Update needed" instead of a false "Complete ✓".

✓ Done

Admin ops surface — signature status, "Mark application submitted", internal resources

Live 2026-07-12 → 07-15. Merchants list gains a Signature column + Awaiting/Executed filters (third status axis); admin-only button catches up act-as-stranded merchants behind a two-layer current-executed-MPA gate; staff-only internal resource audience (also closed a download-route leak).

✓ Done

Ops tracker boards — MPA Tracker + URL Tracker

Live 2026-07-16 → 07-24. /admin/mpa-tracker began as the per-merchant 8-step checklist from intake review → executed MPA → bank submission (steps 1–5 derived live, 6–8 manual, every mutation a single audited CTE, generation-token guard against stale re-adds). Superseded 2026-08-05 — see the re-key card above; the manual steps are gone and the board is derived from placements. /admin/url-tracker is the matching ops board for the Add Location lifecycle.

✓ Done

Secure file delivery — bank packages + Secure Send

Live 2026-07-17, deep-link 07-27. Password-gated download links at /secure-files/[token] (7-day expiry, download receipts) replace ad-hoc encrypted-zip email. /admin/secure-send handles one-off sends to bank contacts. The download-receipt email now deep-links back to its own row (?send= highlight + anchor scroll) instead of the bare creation form.

✓ Done

Add Location workflow — MCC capture, batched e-sign, admin-API domain gate

Live 2026-07-23 → 07-24. Existing merchants can add locations without a fresh full application: MCC captured at intake (trx-v2 has no MCC field — the bank assigns it), locations modeled first-class, and all location addenda go out as ONE multi-page Dropbox Sign request rather than N separate ones. Same pass closed the repo-wide gap where /api/admin/* enforced role but not the proxy's admin-domain gate.

✓ Done

Wet Signature Sent — first-class status for paper-signed MPAs

Live 2026-07-27. New wet_signed e-sign status for MPAs signed on paper outside the portal and delivered to the bank by hand — its own admin badge + filter chip, executed-PDF download, bank-package attach, and signed-equivalent treatment in the MPA tracker. Driver was the Venture Payment Solutions TRX portfolio — now 10 entities / 30 DBAs after Flask Creative and Hillside Drift were added 07-28 — all backfilled with their real executed applications and placed TRX → Esquire.

✓ Done

Left-sidebar navigation — admin + partner shells

Live 2026-07-27. The overflowing 11-link top bar is replaced by a layout-level left rail on both /admin (24 pages) and /partner, sharing one SidebarShell with the rail picked by real session role. Six previously URL-only surfaces gained nav entries. The mobile drawer is a real modal dialog (focus trap, Escape, focus return, breakpoint reset). Colorblind rule honored — every item is icon + label, never color alone.

✓ Done

Merchant Oversight bank demo

Live 2026-07-21. Admin-only sample-data demo at /admin/oversight-demo for walking sponsor banks through the monitoring story. Source of truth is the standalone HTML under Merchant Monitoring/ — re-copy and redeploy to update it.

✓ Done

Multi-channel placements — a merchant on two bank channels at once

Live 2026-07-31. The VPS portfolio entities were also submitted through a second channel on signed paper applications while still holding in-flight TRX placements — and the old one-in-flight-per-merchant index made that unrepresentable. Migration 2026-07-30-01 (applied to prod before the deploy, since the widened index is what the new conflict arbiter targets) moves it to one in-flight per (merchant, ISO); the placement panel renders every in-flight card and offers a second channel with already-in-flight ISOs disabled; executed e-sign rows displaced by a newer channel keep their download link. Review caught a real leak — the backfill's e-sign title named the ISO and bank, and titles are merchant-visible, so wet-sign titles are now generic and channel identity lives only in placement notes + audit rows. Backfill ran post-deploy and re-verified idempotent: three second-channel placements, three added DBA locations, three paper-signed applications attached.

✓ Done

Pend Tracker — bank underwriting conditions as tracked state (CRM P1)

Live 2026-08-01. First slice of the CRM portal-extension plan. Bank pends stop living in email threads: placement_conditions makes each one placement-scoped state (open → answered → cleared/waived, optionally tagged to a single location), with response threads that carry an author, a timestamp, and an optional document. Open or answered pends now block both approve and activate — a friendly pre-check first, then the same predicate inside the atomic write so a race can't slip a placement past a live condition. /admin/pend-tracker is the work queue, sorted by due date with Eastern business-day overdue math; partners see the bank's verbatim ask on their merchant page and can respond in text (clearing stays admin-only). Placements also gained decision reason codes, reserve %, and an approved monthly cap — soft flags for now, wired to enforcement later. Fixture was the real VPS pend batch; Mark logged the first live pend on Hillside Drift.

Policy: bank/ISO identity disclosure relaxed the same day (v2) — once a placement exists, the bank may be named to partner, agent, and merchant; only pre-pick deliberation stays admin-only. New surfaces disclose from day one; retrofitting the older scrubbed paths is a later pass.
✓ Done

MID registry + Accounts board (CRM P2)

Live 2026-08-02. A live merchant account is now a first-class row rather than an implied consequence of an approved placement: merchant_accounts holds one row per merchant account at a processor, unique on (ISO, MID), with at most one live account per merchant/DBA/processor. It carries the processor ids parsed off the VAR sheet, and informal monthly caps that require a provenance note — a cap with no stated source isn't accepted. The registry seeded from the seven Highwire TRX VAR sheets, which also flipped those seven queued TRX → Esquire placements active with their real MIDs; Mark then set all seven caps to $500k/mo. /admin/accounts is the board — live-MID tiles per processor, per-row cap editor, and an automatic flag when a statement descriptor doesn't match the DBA (the Zen Oil lesson, now caught by the tool). The Active book on /admin/placements collapsed to headline counts that click through, and the partner merchants list now shows DBA — four "Members America LLC" rows were otherwise indistinguishable.

Scope note: the registry is forward-only — the two legacy Corduro/Hagen statement-history merchants deliberately get no rows.
✓ Done

Payout ledger — recording what we actually paid (CRM P3)

Live 2026-08-02. The residual side of the CRM plan: runs, per-payee items, statement-level slices, payability gates, a carry chain, and post-close adjustments over frozen statements. It records payments only and never initiates one — Mark executes the transfer at the bank and keys the reference in. Every amount is integer cents, no floats anywhere. Gate order is agreement → W-9 → floor (a $100 org default, editable in settings); held items wait inside the run, and sub-floor items carry forward visibly to the payee rather than vanishing. A statement side can only be claimed once, which is the structural guard against double-paying. Surfaces: an /admin/payouts board and run detail with a reconciliation strip, W-9 receipt and encrypted payee ACH capture, and partner-visible payout lines that state a floor rollover explicitly.

Verification: a 49-check rollback battery plus a read-only trace against real historical months, which reconciled to the cent. Exit gate still open: stage 2 is a re-verify on a real period around late September — before any real payment reference gets entered.
✓ Done

Ingestion + monitoring (CRM P4)

Live 2026-08-03. The machinery that turns processor files into watched numbers. /admin/import takes a normalized CSV all-or-nothing — any bad row or a control total that doesn't match refuses the whole file with every problem listed — archives the original, and applies it in one transaction. Metrics land as revisioned snapshots that are never edited in place, with the grain (daily vs monthly) part of the row's identity so the two can't be silently mixed in a view; a feed that doesn't report a number leaves it null rather than faking a zero. Thresholds are sourced data, not constants in code — the TRX 1%-or-50 line is seeded with its page reference, and the Esquire program row ships deliberately empty because no bank has stated numbers in writing. Five alert kinds (cap utilization, volume swing, chargeback/refund acceleration, ratio headroom, auth-decline spike) feed an /admin/alerts queue with frozen evidence and a required note to call anything a false positive; critical ones email Mark and Jeff. The Accounts board now prefers per-MID snapshot volume over statement-derived figures.

Built generic-first on purpose: Ainsworth receives no transaction data yet, so this ships the machinery and waits on a real file — processor-specific parsers get written against one in hand, never guessed. Exit gate open: one real month ingested, reconciled, and its alerts reviewed for false positives.
✓ Done

Bank surface — /bank (CRM P5)

Live 2026-08-03. The portal's fifth audience: sponsor-bank risk officers get read-only, program-scoped visibility instead of an email thread. A bank_viewer role plus per-program grants resolved fresh on every request, so revoking access beats a live cookie. The surface is a portfolio view (volume against cap, headroom, a standing chip), a merchant deep-dive with trailing-six-month metrics and the resolution history of closed alerts, and frozen monthly standing reports with a PDF. Admins get a thresholds editor, grant management, and a banner-disclosed single-program preview that audits as an admin action. What a bank cannot see is enforced by tests, not habit: economics, partner identity, other-processor volume, and the free-text internal resolution notes are all denied at the SQL level. Standing says "no data" rather than "good" when a number is missing, and an interim watch rule is labeled as Ainsworth's own, never as the bank's.

Demo-grade until two gates clear: the only live grants are internal demo accounts. Real bank logins need (1) the bank-auth expectations survey answered — draft is written, Mark sends it — and (2) the P4 exit gate, a real month ingested and reconciled.
✓ Done

Fiserv Bank Credit App — a fourth artifact type

Live 2026-08-04. Admin-only surface that assembles Fiserv's New Account Credit Package Transmittal from Ainsworth intake and renders it for Fiserv's credit desk. It is deliberately not an MPA, not a bank package, and not a secure send: never signed, never merchant-facing, keyed to the placement rather than the merchant, and regeneration replaces in place. The merchant never sees Fiserv paperwork — Fiserv issues its own application separately, which is why the new fiserv ISO row carries no MPA template at all. The two gates differ on purpose: completeness is soft (Fiserv's own form allows a stated-gaps package, so it generates with an INCOMPLETE banner printed on the PDF — a screen-only marker would vanish the moment the file left the portal — and names the file -INCOMPLETE), while the SSN guard is hard (422): an incomplete package may be sent knowingly, a Social Security number may not be sent at all. Built from two real hand-filled transmittals rather than a spec.

Open: what Fiserv means by "Clearing Bank" (their two real transmittals disagree), whether they require the SSN at all, and a phase-2 conditional attachment-rules engine — both real packages had gaps those rules would have caught.
✓ Done

Self-hosted PDF rendering — no portal PDF touches a third party

Live 2026-08-04. Headless Chromium in-process replaced the external PDF vendor behind the identical conversion seam, so all four PDF surfaces (Fiserv credit app, bank standing reports, statement analyzer, website screening) changed one import line each. There is no external fallback by design — a render failure returns an error, and the HTML is never shipped off-box. The trigger was the Fiserv credit app: it is the first portal PDF carrying principals' dates of birth and home addresses, and the vendor's data-processing agreement covered only account-level data, not people named inside submitted documents. Page geometry parity is pinned by a script that renders through the real module and asserts the actual page size.

Same-evening prod incident, fixed: the Chromium binary didn't ship in the first deploy — the bundler can't follow a runtime filesystem read — so the four functions had no browser. A hotfix that explicitly includes the binaries went out, was proven by a real 2-page PDF rendered on the live function, and the vendor API key has since been removed from the environment. Lesson: verifying locally against installed Chrome cannot see how the deployed function is packaged.
✓ Done

VAR sheet import — the processor's own boarding document takes a placement live

Live 2026-08-05. The MID registry had no creation path since it shipped three days earlier — every row came from a one-time seed, and activating a placement stamped a MID while creating nothing. Importing a bank's VAR sheet now does the whole motion at once: it creates the merchant account with its descriptor and processor ids, seeds the cap, files the PDF, and flips the placement live. That makes it the real replacement for CRM P2 slice 2 rather than a companion to it, and it arrives from the direction the work actually does. Mark's corrections shaped the scope: every channel issues VARs (not just TRX), caps are seeded per MID at the full requested amount (multi-MID exists precisely to obtain higher aggregate capacity), and pends neither block an import nor auto-resolve — a VAR is the bank's own proof the account is boarded, and conditions legitimately outlive activation. The sheet is downloadable by admin, the merchant, and the merchant's own partner; referrers are excluded by construction.

Five adversarial review rounds, all fixed — a placement could go live while the registry write was refused, revoked act-as sessions outlived revocation on the download route, and a re-issued MID could be relocated off another DBA. Deferred: parsing the TRX PDF to pre-fill the form (the same extraction question behind the Chromium packaging incident); manual confirm covers every layout indefinitely. The same session finally merged the domain glossary onto main, where the SOP has been telling everyone to read it since 08-01.
✓ Done

Pends at book scale — surfaced where the work actually happens

Live 2026-08-05. The Corduro/Chesapeake batch (86 pends across nine lists) proved the P1 board doesn't scale on its own: a pend is only useful where someone is already looking. The list was sorted — on legal name, while rendering "DBA (Legal Name)" — so one string had two writers and merchants filed under the wrong letter; one function now owns the label and the sort that matches it. Search and paging landed with a tiebreak on row id, because a single bank email produces a dozen rows sharing a due date, and without a total order paging repeats one row while skipping another. Pends now appear on the admin merchant page and read-only on the merchant's own dashboard. Two governance calls shaped it: merchant-facing pend text is authored, never the bank's verbatim ask — no summary means the pend is never published, enforced so that the raw ask is a compile error on that surface rather than a convention — and the merchant card shows exactly one placement, since merging across ISOs would let a merchant infer they're being shopped to several banks at once.

Ops decision: Mark is not loading the 86 Corduro pends — "better to start with anything new." The tracker begins from new pends going forward. Lesson worth keeping: the second test lane had been silently skipped repo-wide since a Node upgrade; both lanes are green again under the corrected runner.
✓ Done

MPA tracker re-keyed onto the placement — a derived queue with zero manual steps

Live 2026-08-05, both slices. The 8-step per-merchant checklist is gone; the board is now a fully derived work queue where a row is a placement — the same merchant at TRX and at Corduro are separate chases — and nothing is ticked off by hand. A deal enters when its latest real MPA signature is executed (merchant-scoped by necessity: one signed paper serves every concurrent channel), sits in Awaiting send / Awaiting decision / Awaiting VAR buckets read straight off placement status, and exits when the VAR import takes it live. Day one it surfaced three signed-and-packaged deals that had never been sent — exactly the failure mode the board exists to catch. Slice 2 added notes and dismiss/restore, written by one planned statement shared by the route and its verifier, and removed the hand-typed Activate control entirely: VAR import is now structurally the only way a placement goes live. The legacy tracker's stubs and vocabulary were deleted later the same day; its table survives as history, read and written by nothing.

Same-day companion: the merchants board now defaults to In flight (21 today) with live merchants one chip away on Accounts, plus a derived "Dead (banks declined)" view — deliberately not a status value, since banks saying no everywhere is the fact, and revival means routing a new placement. One open item on the whole arc: North API placements can't enter the queue (they create a placement with no signature row). Moot while North is uncredentialed — but it must be resolved before North is activated.
✓ Done

iBill → Esquire channel — a sixth MPA paper, e-signable the same day

Live 2026-08-06, both slices. iBill is a new placement channel that fronts TRX's Esquire rails — deals actually run Ainsworth → iBill → TRX → Esquire, modeled as iBill → Esquire with the processor in the middle deliberately invisible. New Highwire deals now default to it; existing TRX placements stay put. The channel brings the portal's sixth MPA paper (a 4-page, 165-field application, SHA-pinned) and, hours later, guided e-sign for it: two signers like TRX — the merchant primary contact on pages 2–4, the sending admin on the page-3 survey inspector line — so iBill deals never needed the wet-sign path the first slice shipped behind. Every print name for the signer of record resolves strictly to the primary-contact party with no owner fallback, which is the first deliberate divergence from the TRX gate. Two traps are worth remembering: the paper's "Return Policy" radio options are misnamed in the form itself (the widget called Home prints NONE, Office prints EXCHANGE) so the mapper selects by render-verified position and must never be "corrected" to match the names; and the send-for-signature allowlist is now derived from the bank registry rather than hand-copied, killing a whole class of skew.

New shared mechanism: mapper revisions are now paper-scoped — bumping one paper's revision invalidates only that paper's stored hashes, so completed signatures on the other five stay valid. The first attempt bumped the global canonical version and would have forced re-signing already-executed non-iBill MPAs; it was reverted in-branch. Eleven adversarial rounds across the two slices (5 + 6) caught, among others, a paper that would have asserted office numbers as home phones and a card-present-dominant merchant being asked to sign a card-not-present claim. Ops: any iBill MPA generated before the e-sign deploy 409s at send — regenerate first; other papers unaffected.
✓ Done

Abandoning a merchant now closes its in-flight placements

Merged 2026-08-06, backfill applied. Found by a memory sweep rather than a bug report: Enhanced Wellness sat at placement submitted for two months after the bank rejected it and the merchant was abandoned, so the portal believed a live submission existed. Twelve of sixteen abandoned merchants had the same shape. Grilling the real rows changed the design — all twelve were queued, meaning the bank had never seen those deals, so the honest close is withdrawn, not declined. That one word deleted an entire subsystem: the notification outbox fires on submitted/approved/declined/active, and withdrawn sits outside it, so the "suppress the merchant email" machinery the feature was originally framed around was never needed — and a test now counter-proves it by failing if declined ever rejoins the notify list. One writer, three callers: both abandon routes had carried separate inline SQL, so a rule implemented in one was a rule the bulk bar silently broke. Live accounts are never touched — a real MID has a registry row behind it.

Four adversarial rounds, and rounds 2–4 each bled from the previous fix — ending with the round-3 database trigger being cut: it never actually closed the race it was written for, and it made the North submission path worse, 500ing after the application had already left the building. Accepted residual risk, and the second item that must be revisited before North activation: a concurrent create in a millisecond window can still strand one placement. Never observed, three-person admin team, and the backfill script re-runs safely at any time to sweep one up. Lesson worth keeping: a two-connection test covering one ordering does not license a claim about both.
✓ Done

Partner activation, W-9 + payout account name, and the executed agreement download

Live 2026-08-06. Mark hit an "Activate partner" button on a partner that already existed, and it errored. The grill contradicted all three premises behind the obvious fix: activation had never once succeeded in production (all ten partners were hand-created), the payout engine has never run a single row, and the W-9 field everyone assumed existed was a dead foreign key pointing at a merchant-hardwired table. So activation now resolves three ways — create, link to an existing partner (no second org, no second invite), or blocked outright — and the W-9 is the actual PDF held on the partner record. A payout account name is always required with banking and never defaulted from the org name, because ACH returns on a mismatch. Nothing gates payouts yet: capture first, gate when the first real run happens. The same pass closed an orphan class where a refused activation stranded a partners row — two had reached production and were deleted here. A follow-on shipped hours later: Phase 6.5 had been storing the countersigned agreement PDF since July but nothing ever served it back, so a copy had to be pulled from Dropbox Sign by hand. It is now an audited admin-only download — admin-only because the agreement carries Schedule A, i.e. the partner's whole commission structure.

Two lessons with teeth. BEGIN/ROLLBACK is inert over the HTTP database driver — it is stateless and every statement autocommits, so a verify script that believed it was rolling back wrote straight to production (artifacts cleaned up; rollback verification now uses a real client). And a verify harness must execute the production statement rather than a paraphrase: writing a literal where the route binds a parameter hid a bug that 500'd every banking save. Also: the blob library's get() throws on a missing object instead of returning a status, so status-only checks leave an unhandled 500 — the same shape still exists in the VAR-sheet route and should be fixed next time that file is open.

▶ In flight

▶ In Progress

Phase 7 — remaining slices

Shipped since: docs-ready choke point + MPA data-staleness gate (2026-07-11); local poller now also detects + texts act-as strands (2026-07-16, runs 3×/day). Still specced, not built: external re-upload nudges, Utah LLM judgment pass + standards.ts, scheduled sweep-job backstop, daily digest, bank-match sheet, auto-MPA trigger.

Dependencies: auto-MPA trigger slice waits on nothing (Phase 5 is shipped)

○ Ready — no dependencies

✓ Done

Phase 6.5 — agent agreements

Merged + live 2026-07-04. Editable Schedule A splits, ordered Dropbox Sign agreement flow, manual activation gate. Closes the agent onboarding + payment loop. Remaining: a real signed completion + prod Dropbox Sign plan follow-ups.

⏸ Blocked / held

⏸ Blocked

Esquire MPA mapper

Schema reconciliation can't start without a fillable Esquire PDF. Need to chase Esquire for one. Less urgent since 2026-08-06: the iBill paper now covers the iBill → Esquire channel end to end, including e-sign, so the deals actually flowing to Esquire today do get a portal-generated MPA. This card is for Esquire's own paper on a direct channel.

Blocker: Mark/Jeff to request fillable PDF from Esquire
✓ Done

Guided e-sign — verified end-to-end in production

Shipped + verified 2026-06-10. Hosted signing (Dropbox Sign emails the merchant a secure link; API Essentials plan, $75/mo, 50 docs/mo — embedded-in-portal signing is gated to the $250/mo tier, deliberately skipped). Synovus + PB&T merchants are walked through 8 guided fields — 4 signatures (officer, personal guarantee, Program Guide confirmation, FinCEN cert) + 4 auto-dates — with print names / title / legal name pre-stamped on the PDF. Mark signed the ZZ TEST package live: all 4 signatures on their lines. Merrick + Maverick keep freeform signing until they get their own placement pass.

⏸ Held

Residual: CSV ingest connector

Next residual-automation slice — input automation (currently admin enters by hand). Framework first, then per-bank mapping. Note: CRM P4 shipped a generic CSV landing/apply console for metrics, which is a template for this one but not a substitute — residual statements are a different shape.

Blocker: needs a real upstream CSV/Excel export sample
⏸ Held

IRIS v1.5b — push side

Three numeric IDs still needed (Tab ID, Label IDs, owners catId). All API-discoverable during v1.5a build — Aaron email is fallback only.

Dependencies: v1.5a built first (gives auth to self-serve discovery)
⏸ Held

Phase 6 deferred: PDF statement parsers

Corduro / TRX parsers — highest-leverage Phase 6 next step. Reuses Phase 7's lib/utah/extract.ts primitive.

Blocker: sample PDFs from Jeff
Ticketing System Deferred by design
▶ In Progress

Planning in Co-Work

Persistent project home in Co-Work. Reads PORTAL_CONTEXT_FOR_TICKETING_PROJECT.md. Resolves 9 open questions → produces PLAN.md.

Dependencies: none for planning
✓ Done

Handoff doc prepared

Context dump at Ticketing System/PORTAL_CONTEXT_FOR_TICKETING_PROJECT.md for the Co-Work session to read cold.

⏸ Build Deferred

Ticketing v1 build

Merchant + partner support tickets, role-isolated views, reuses portal's notification + UI primitives.

Trigger gates (all must be true):
1. Phase 7 notification slice fully shipped — still open
2. All active phases at clean committed state
3. At most 1 other portal build session active
Forcing date LAPSED: the June 15, 2026 reopen date passed without a decision. Gate 1 is still the real blocker — needs an explicit build-or-defer call from Mark.
✓ Done

Discoverability nudge (related work)

"Invite a teammate" inline nudge on /dashboard for primary contacts with no collaborators. Retires once they invite anyone.

⚠ Action items requiring Mark (not coding)

These are blockers that no amount of engineering will clear — Mark needs to chase the artifact or sign a contract.

Dependency map — what's free vs. what's blocked

Website + Phases 1-9 + Intake API + pricing models + the 2026-07 admin-ops layer (tracker boards, secure delivery, Add Location, sidebar nav) all shipped. The CRM portal-extension sequence ran five slices in three days (P1 08-01, P2 + P3 08-02, P4 + P5 08-03); 2026-08-05 closed the whole tracker arc in a single day; and 2026-08-06 landed five more — the iBill → Esquire channel and its guided e-sign, abandon closing in-flight placements, the partner activation fix with W-9 capture, and the executed agreement download. Nothing is sitting finished-but-unshipped. The shape of the remaining work hasn't changed: the live gates are data and access, not engineering — P4 and P5 are both waiting on a real processor month rather than on more code. Two pieces of engineering now carry a hard ordering rather than a priority, and both point at the same event: the MPA-queue entry gate and the abandon/create race residual must precede North activation.

No dependencies — can start anytime

  • Phase 7 remaining slices (LLM judgment, nudges, scheduled sweep, daily digest, bank-match, auto-MPA trigger). → Phase 5 stable, so auto-MPA-trigger slice is now also unblocked.
  • Phase 6.5 (agent agreements). → SHIPPED 2026-07-04 — see Done cards above.
  • VAR sheet import. → MERGED + LIVE 2026-08-05. Superseded CRM P2 slice 2 — importing the sheet creates the registry row and takes the placement live in one motion.
  • Pends at book scale. → MERGED + LIVE 2026-08-05. Board search/paging/A–Z plus pends on the admin and merchant records.
  • MPA tracker re-key (both slices) + merchants-in-flight cleanup. → MERGED + LIVE 2026-08-05. The tracker-grill arc is fully closed; the legacy stubs are deleted.
  • iBill → Esquire channel + guided e-sign. → MERGED + LIVE 2026-08-06, both slices. Sixth MPA paper; Highwire's new default channel.
  • Abandon closes in-flight placements. → MERGED 2026-08-06, backfill applied (12 stale placements withdrawn, 0 notifications queued).
  • Partner activation + W-9 / payout account name + agreement download. → MERGED + LIVE 2026-08-06. Activation now links to an existing partner instead of minting a duplicate.
  • Pre-North checklist (two items). → Nothing blocks building either, but both must land BEFORE North activation — North placements carry no signature row so they can't enter the re-keyed queue, and the abandon/create race residual is worst on the one path with an irreversible external call.
  • The P6 grill. → P1–P5 all shipped 2026-08-01→03. Largest remaining CRM phase; grilled before anything is built.
  • Oakville MPA mapper. → Form on hand. ~1-2 hours.
  • IRIS v1.5a (webhook listener). → Aaron's answers cleared the path.
  • Maverick API integration. → Account activated, research done.
  • Ticketing planning in Co-Work. → Planning has no build dependencies.

Has dependencies — needs a gate to open first

  • P4 exit gate + processor-specific import parsers. → Needs one real month from TRX (Craig, still pending) or NMI (arrived 08-03, Mark setting up). Parsers are written against a real file, never guessed.
  • Real bank logins on /bank. → Two gates: the bank-auth expectations survey answered (Mark sends), and the P4 real month above. Internal demo grants only until both clear.
  • Esquire MPA mapper. → Needs fillable AcroForm PDF from Esquire.
  • Phase 6 PDF parsers. → Needs Corduro / TRX sample PDFs from Jeff.
  • Residual CSV ingest. → Needs upstream CSV/Excel sample.
  • IRIS v1.5b (push). → Needs v1.5a built first (provides API auth for self-serve ID discovery).
  • Ticketing build. → Needs Phase 7 notification slice shipped + clean shared-file state + lower concurrent build pressure. June 15 forcing date has LAPSED — needs a fresh build-or-defer call.

Recommended next — prioritized

Ordered by leverage × ease. Each item explains why it earns its rank.
1
First real processor month through /admin/import an upload, once access lands Two shipped surfaces are both parked on this one file. It clears P4's exit gate (ingest → reconcile → false-positive review on the alerts it fires) and is one of the two gates holding /bank at demo-grade. It also decides what the processor-specific parsers look like — those get written against a real file, never guessed. NMI access arrived 08-03; TRX is still pending with Craig.
2
Continue Phase 7 active slices ongoing Notification + nudge slice also unblocks the Ticketing gate. LLM judgment + scheduled sweep are the highest-leverage automation gains. Already running in its own session.
3
The P6 grill a grill session, then a build Now the largest remaining CRM phase, and it inherits this slot because the 08-05 arc emptied the queue ahead of it — the MPA-regeneration item that sat here is effectively closed, and both branches that were unmerged are live. Grilled before anything is built, per the SOP.
4
Oakville MPA mapper 1-2 hrs Quick win. Form on hand. Adds the 5th bank template and removes "Oakville-placed merchants need manual MPA" from the operations list.
5
The pre-North checklist (before North is switched on) small, but ordered Ranks here on size, not urgency — both items are inert while North is uncredentialed. What earns the slot is the ordering. North submissions create a placement with no signature row, so those deals would be invisible on the re-keyed MPA queue from the moment North goes live; and the abandon/create race residual accepted on 08-06 is worst on exactly this path, where the placement write sits downstream of an irreversible external call. Cheap to fix now, silent gaps if they're fixed after.
6
IRIS v1.5a — webhook listener ~1 week Buildable. Validates the full integration stack (API key, IP allowlist, signing). Sets up v1.5b push-side. Hagen is the primary API-banked path; this opens the lane.
7
Maverick API integration ~1-2 weeks Second of two API banks. Replaces the PDF pipeline for Maverick-placed merchants. Account active, research done — just needs a focused build slice.
8
Chase the Mark-action blockers phone calls / a credit card TRX portal access, the bank-auth survey, Esquire fillable PDF, Dropbox Sign account email, Corduro/TRX statement samples, residual export sample. Each one is a short decision or email that unblocks ~1-2 weeks of work later.
9
Ticketing build (gate needs a decision) ~1-2 weeks Trigger fires when Phase 7 notification ships + workload allows. The June 15 forcing date lapsed unactioned — either ship the notification slice or set a new dated decision point.
10
QBO payout integration ~1 week The natural follow-on to Residual Automation. Approval flow → QBO export. Waits on the CSV ingest connector for the input half to be automated.

Engineering hygiene — known deferred issues

Items that should land eventually but aren't blocking anything. Tracked so they don't get forgotten.

Auth / sessions

  • Session revocation on user removal Pre-existing app-wide gap. Removed users keep API access until 7-day cookie expires. Fix is per-request DB revalidation. Covers merchant_collaborator and partner_admin removals.
  • Email-change after sign-in Same root cause — gated to never-signed-in users until session revocation lands.

Repo / infra

  • Repo on iCloud Desktop — RESOLVED 2026-06-15 iCloud sync was creating * 2.tsx / * 2.ts duplicates and corrupting .git. Repo moved to ~/dev/ainsworth-merchant-portal, with feature worktrees as siblings under ~/dev/. Closed.
  • VAR-sheet route still has the throwing-blob shape (2026-08-06) The blob library's get() throws on a missing object rather than returning a non-200, so a status-code-only check leaves an unhandled 500 where a typed error was intended. Fixed in the two routes shipped 08-06; api/merchant-accounts/[id]/var-sheet was deliberately not touched. Fix it next time that file is open.
  • Cross-surface dedup Date utils ↔ statements.ts, checklist-def ↔ nudge route. Deferred as a coordinated pass post Phase 7 review.

Wizard / data

  • Wizard step 2.6 — id_date_issued not collected in UI Field is in the canonical schema + DB, preserved on edit, but no UI input. Add when a sponsor bank flags it as required.
  • Free-text rendering safety card_types_not_accepted / refund_policy_description / product_storage_location in PDF fill + admin views must use safe text APIs (not raw HTML). Flagged at every commit.

E-sign / placements

  • Dropbox Sign finalize webhook error (2026-05-24) One ALL_SIGNED webhook 500'd during dogfood. Status stuck at 'signed', no Download button. Likely test-mode PDF retrieval or Blob put. Next-session triage: re-run the loop, capture full stack. The affected row can be manually fetched from Dropbox Sign UI.
  • Webhook terminality is enforced in TS, not in the DB predicate The DECLINED / FILE_ERROR / finalize write predicates don't list wet_signed in their terminal exclusions. Unreachable today (synthetic wet-sign IDs never match a Dropbox event) but the DB, not the pre-read, should own it. Fold into the next webhook touch as one transition-policy/CAS helper.
  • Multi-channel edges left open at ship (2026-07-31) A bank package attaches the newest executed application regardless of channel; a merchant stays at ready_for_review while any channel is in flight; each channel emails its own submitted/approved notice. All logged as decide-later, none regressions. See the deferred list in PROJECT_STATE's multi-channel section.
  • Pend Tracker deferrals (2026-08-01) None load-bearing: row-creating pend POSTs have no idempotency key, so a client retry can duplicate a pend (the CAS routes are already replay-safe); reopen should be tightened to in-flight placements; the location tag can't be lane-aware until the P2 MID registry carries both foreign keys; and the edit UI for decision fields / caps plus pend email notifications are still unbuilt.

Out of scope (v1) — deliberate decisions

Items deliberately excluded from v1. Documented so future-Mark knows this was a choice, not an oversight.

Product surface

  • Mobile apps Web-only for v1. Revisit if/when usage patterns demand it.
  • Multi-language English-only v1.
  • Card-present / physical merchant intake v1 wizard is e-commerce only. Card-present columns + intake reserved for Phase 2.

Integration

  • Direct submission to sponsor bank portals (PDF banks) Out of v1 — admin submits to the bank manually. API banks (Hagen via IRIS, Maverick via onboarding API) auto-submit.
  • KYB lookup provider call Data model wired, provider call deferred to v2.

External

  • Fintech-compliance attorney review Revisit before scaling beyond v1 volumes (10-20 apps/month → bigger).

Killed (not deferred)

  • Merchant-facing statement view Killed during Phase 6. The data is agent-economics (net→split→residual), not a merchant statement. Merchants get the real thing (volume/rate/chargebacks/reserves) from the processor.

Living docs — where the canonical content lives

Source-of-truth documents for each surface area. Refresh these, not the roadmap, when implementing a change.

Notion-synced handbooks

  • PARTNER_HANDBOOK_v1.mdAinsworth Payments root
  • PORTAL_HANDBOOK_v1.mdAinsworth Payments root
  • TECHNICAL_DOC_v1.mdAinsworth Payments root

In-repo operating docs

  • docs/PROJECT_STATE.mdcanonical project state
  • docs/POSITIONING_STRATEGY.mdaudience + pillars
  • docs/AGENT_DASHBOARD_SPEC.mdUX spec
  • docs/BANK_MPA_OPERATIONS.mdrunbook for new banks
  • docs/RESIDUAL_AUTOMATION_PLAN.mdPhase 8 plan
  • docs/PHASE_7_BUILD_SPEC.mdUtah automation
  • docs/PHASE_6_5_AGENT_AGREEMENTS_PLAN.mdnext workstream

Planning docs (Merchant Onboarding/)

  • CANONICAL_APPLICATION_SCHEMA_v2.md7-bank reconciled superset
  • STEP_B_PLAN.mdPhase 5 sequence
  • IRIS_INTEGRATION_v1.5.mdHagen API spec
  • MAVERICK_INTEGRATION_v1.mdMaverick API research
  • ESIGN_OPTIONS_v1.mdDropbox Sign decision
  • PRD_Ainsworth_MerchantPortal_v1.mdv1 PRD
  • DEVILS_ADVOCATE_v1.mdstress-test of v1.0