810f7eb, no migration) — an MPA request that is out for signature now shows who has signed and who has not, read live from Dropbox Sign, with an audited Send reminder button; the package card says when the MPA is still out for signature; the half-signed label reads "Signed — not yet executed" (a1050a8). Inspection signer (5c0dd93, migration applied) — the inspection block on TRX / iBill paper is signed by the agent who brought the deal in (sourcing agent, else the partner's designated MPA signer, else Ainsworth's MPA signers), never whoever clicks Send; the sender can switch to someone at Ainsworth; Reassign moves an outstanding inspection signature; a same-day fix (5f11ec5) makes the card say why Reassign is missing. New for Mark, and the top action item: the signer lists shipped empty — set Settings → MPA signers (Mark + Jeff, default Mark) and each partner's MPA signer (PayByCard first), then Reassign the live merchant's inspection signature off the wrong inspector; until the lists are set, a TRX / iBill send with no sourcing agent is refused by design. First live send under the new rule, first live Reassign, and the live reminder are all still unproven. Still open for Mark: Dropbox Sign's cancel call is asynchronous in both the MPA and Add Location lanes — the cancel probe scheduled for 19:45 ET on 10-02 has no result recorded in PROJECT_STATE as of this refresh. The slice Mark named on 09-30 — a declined or errored re-send keeps the earlier executed MPA — is still built on a branch and unmerged (worktree present, migration filename collision to fix), review next. Still open for Mark: decide whether to rotate the Dropbox Sign API key. The placement-driven MPA form's server-route follow-up remains deferred as Mark's call. The KYC vendor-spend decision with Jeff was due Friday 09-18 and gates the first Plaid tracer — seventeen days past due, still no outcome recorded. The payout ledger's stage-2 re-verify on a real period was due around late September and has no recorded result. Still open for Mark from 09-30: one live merchant's hand-uploaded signed application needs converting through the wet-sign lane. Also still open for Mark: the privacy policy is draft v0.1 with no counsel review, and whether its info@ address is a monitored mailbox is unconfirmed; a removed or converted merchant user's cookie keeps working for up to 7 days (session-revocation slice 2); his reply on the Canadian intake forms (v0.3, EN + FR) plus a French proofread and a named Canadian merchant; the empty production SCRAPINGBEE_API_KEY; the PII key rotation (grilled, GO, needs a morning); and the VPS backfills’ EIN + banking numbers, which only the admin UI can write)/bank to one (the bank-auth survey) · the Fiserv Canada referral lane has approved forms (v0.3 — English + French, Fiserv co-branded) and is waiting on Mark’s reply, a French proofread, plus a named Canadian merchant · the next engineering work is reviewing and merging the standing-MPA branch (a declined re-send keeps the earlier executed MPA), then the peptide B2B vertical, alongside the PII key rotation (grilled, GO, slipped from 08-22 — needs a morning with Mark) and the unblocked Fiserv Marketplace boarding build, then the P6 grill · two items still carry a hard pre-North ordering: the MPA-queue entry gate and the abandon/create race residualFour pages live: /, /agents, /merchants, /about. Positioning locked, compliance pre-flight integrated.
Favicon, OG card, Vercel Analytics, Resend-backed apply forms, /thank-you confirmation. Forms verified delivering to mark@.
Positioning + content signed off 2026-05-27.
Project relocated to Mark's personal Vercel ownership before the domain swap.
ainsworthpayments.com is LIVEShipped 2026-06-06. Domain now points at the new Next.js site; old static page retired. Closed workstream.
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.
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.
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.
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.
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.
Schema + entry pivot + agent CSV + colorblind-safe PDF shipped 2026-05-30. Agent statement = net interchange + net fee × split %. Full vertical dogfooded in prod.
"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).
Shipped + live 2026-07-05. Full /api/v1/merchants surface for partner-programmatic intake. Open follow-ups: webhooks, spec v0.2, integration guide.
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).
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).
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.
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.
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.
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.
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).
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 ✓".
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).
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.
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.
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.
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 12 entities / 35 DBAs after Flask Creative and Hillside Drift were added 07-28, Brave Fenn LLC on 09-11 and Marixor Holdings Corp on 09-15 — all backfilled with their real executed applications and placed TRX → Esquire.
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.
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.
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.
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.
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.
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.
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.
/admin/alerts with the MCC-5411 note — resolve, not false-positive, because they were true positives against the old 50% line. The retune to warn-80 / crit-90 stops them re-firing.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.
/bank would now show real numbers. What still holds it at internal demo grants is the bank-auth expectations survey — the draft is written and Mark sends it, because handing a sponsor bank a login before knowing their MFA/SSO/revocation requirements is the one thing here that can't be undone quietly.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.
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.
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.
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.
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.
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.
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.
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.
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.Live 2026-08-09. Until this shipped, a merchant declining to sign — or a signature request erroring out at Dropbox Sign — was silent. The row changed status and nothing else happened; someone had to notice a request had stopped moving. All three webhook write sites (decline, file error, and the catch around finalize) now queue a coalescing digest to the admin distro through the notification outbox that already existed, with a toggle on /admin/settings. Two gates keep it honest: it only fires when the guarded update actually transitioned the row, so Dropbox's redeliveries can't re-send, and it never fires for test-mode rows. Queueing is best-effort — a failure there never costs the webhook its acknowledgement. This is slice 1 of the usability review: Mark cut the original four-family scope down to e-sign alone, and the other three (pends, account pause/terminate, payout hold/reverse) are parked with explicit triggers — each waits until he has worked a real one of that thing by hand, rather than being designed against an imagined workflow.
Live 2026-08-10, both halves — this closes the item that sat at the top of this roadmap for a week. The Highwire book runs on an NMI white-label gateway, and the read-only Query API keys landed on 08-10. A pull script turns the gateway's XML into exactly the import console's CSV contract, and both accounts reconciled to the cent against the gateway's own Transaction Snapshot before a single row was written — then Mark landed and applied both batches through /admin/import. Hours later the same seam went nightly: a cron pulls both accounts concurrently and lands → applies → evaluates headlessly, with the manual console kept as the correction path. The first automated run independently reproduced the manual import byte-for-byte (both accounts unchanged, every snapshot still at revision 1, zero alert churn), which is the strongest evidence available that the two paths agree. The live data also forced three fixes no synthetic file would have surfaced: the two gateway accounts sit in different timezones so a hardcoded one would have corrupted the other; the API stamps in UTC and has to be converted before day-bucketing; and a narrow date window silently fragments a transaction's action history, orphaning settlements from their sales and reporting $0 settled — the pull now fetches wide and converges on output rows across two sweeps.
Live 2026-08-12. Mark found three of Zen Oil Atelier's wet-signed Hagen Pay URLs displaying as TRX on the board. The cause was structural rather than a typo: merchant_locations was bank-agnostic — Add Location was built when TRX was the only channel — so both the board and the bank-response chip simply printed "TRX" because there was nothing else to print. A location row now records which placement (which ISO/bank channel) it rode, plus whether it was signed on paper. Step 5 names the channel ("Hagen Pay · Chesapeake Bank"), the response chip reads <channel> confirmed and falls back to a generic "Bank" for legacy unlinked rows rather than guessing, and paper-lane rows say "Wet signature" at the signing step. Capture closes the loop: marking a URL sent records the channel silently when the merchant has one open placement and asks with a picker when there's more than one. The three Zen Oil rows were backfilled to the Hagen Pay · Chesapeake placement with audit rows written.
Live 2026-08-15. Fiserv is now selectable as a placement channel at merchant-invite time, with the placement row created in the same atomic statement as the merchant and the user. The wizard grew a conditional step (delivery timing / order mix) whose visibility is derived from the merchant's live placements rather than from a column on the merchant — deliberately, because a merchant can sit on two bank channels at once and a single "channel" field would have to lie about one of them. Every other paper in the repo came from the bank as a fillable PDF; Fiserv had none, so this one was built from their Marketplace sample and is a DRAFT until Fiserv approves it. A revision they ask for ships as a new template plus a mapper-revision bump, never as an edit to blobs merchants have already signed.
Shipped and running 2026-08-16. Until this landed, the portal's database and document store had no independent copy: the only thing standing between Ainsworth and total loss was the hosting provider's own retention. A nightly job at 02:30 now takes an integrity-verified database dump (30 daily, 12 monthly), mirrors the blob store (602 objects / 939 MB, append-only — it never deletes, so a bad sync can't propagate a deletion), and encrypts an offsite copy. A restore drill runs as part of the system rather than living in a runbook nobody executes, and it earned its keep immediately: running it for real surfaced the first genuine finding, which is exactly the point of a drill.
Live 2026-08-16 (slice 1). Portal sessions are stateless seven-day cookies, which means that before this shipped, deactivating a user did not end their session — a departed employee's or partner's browser kept working until the cookie expired on its own. Every request carrying an admin, bank-viewer, partner or act-as session is now checked against the account's live state. The design that survived review is a monotonic counter, not a timestamp: cookies record the generation they were minted under, deactivating increments it, and reinstating never rewinds it — so a deactivate-then-reinstate cycle permanently strands every cookie issued before it, including one stolen during the window. Offboarding is a single audited command (plus closing the mailbox), reversible, with a dry run by default.
Live 2026-08-16. A partner reported not seeing the Fiserv option that admin had gotten the day before. The cause wasn't a bug so much as an omission: the picker was only ever built for the admin invite flow. It now exists on the partner side as well, behind a per-partner permission that defaults to off — admin flips "Fiserv at invite" on a partner's page and the flip is recorded in the audit diff. Channel vocabulary and labels are single-sourced, so a channel added without copy fails to compile rather than rendering a blank card, and the authorization check lives inside the shared invite function rather than in the route — which means the partner API inherits the same gate for free when its spec adds the field.
Live 2026-08-20. A partner's merchant could only be invited by the partner logging in and doing it themselves. Admin now invites into a partner's book straight from the partner's page — carrying the partner's attribution, the sourcing agent, and the channel choice, while the audit trail records the admin who actually acted. The deliberate call: the partner's channel permission binds the admin too. There is no bypass parameter, because one would have made the permission decorative — if admin needs a channel the partner isn't allowed, they flip the partner's flag first, and that flip is itself audited. Same day, a second decision retired the flag's default: the Fiserv channel is now on for every partner, so the checkbox reads as an opt-out.
Live 2026-08-22. Some merchant applications get signed on paper, outside the portal entirely. Until this shipped there was no lane for them — every wet-signed deal on the books got there through a backfill script run by hand, while the send-for-signature screen promised an "upload the executed copy" path that had never been built. Admin now uploads the executed PDF from the same panel that generates the application: the portal files it as that merchant's signed application document, records who confirmed it and when, and the deal becomes eligible to be marked submitted on the strength of it. The confirmation is the attestation — a human vouching that the paper matches what the portal holds. If the underlying data has drifted since the document was generated, the upload warns and requires an explicit accept rather than refusing: the deal is already signed, and blocking the record of it would help nobody.
Live 2026-08-22. The second half of the same lane, and the one with an actual customer behind it: Venture Payment Solutions runs their own signature process, so their merchants only ever see VPS's brand. Because the portal has never generated an application for a VPS merchant, the path built is the blank-template one — the partner downloads the bank's blank paper, gets it signed their way, and uploads the executed copy from the merchant's page. An upload is a queue item, never a status. It lands as pending review, and pending is deliberately invisible to every surface that asks "is this application signed" — so an unreviewed file cannot masquerade as an executed one, and it cannot hide an executed one that already exists. Any admin reviews it: confirm turns it into the executed signature, or reject returns it with a note the partner reads on their own page ("Returned by Ainsworth: … — please re-upload").
Live 2026-08-24. A bank package used to be documents and nothing else — the underwriter got the application, the statements and the corporate papers, and had to infer the story from them. An admin can now write a short bank-facing narrative per merchant in three parts — the opportunity, the business model, and document notes — and it renders as a branded PDF into every generated package, immediately after the executed application. Document notes is the part that earns its place: it is where "this company is six months old, so there are no two-year financials" gets said by us, in writing, instead of arriving at the bank as an unexplained gap. Writing one is never required — a merchant without a summary produces a soft warning at package time, never a block — and partners and merchants never see it. Ad-hoc Secure Send deliberately does not attach one, and the panel says so, because that lane sends a file somebody picked rather than a package the portal built.
Live 2026-08-26. A sign-in link can die three ways — it was used, it was superseded by a newer one, or the account behind it was revoked — and the database recorded only that it had died. All three therefore rendered the same sentence: "This sign-in link has already been used." For supersession that sentence is false, and worse than false, it is self-feeding: it tells the reader to request a new link, and requesting a new link kills the one they were about to click. This was found on a real merchant. EB Trading, on 08-25, never clicked their invite link at all — they burned three tokens going around that loop before getting in. The fix records which death happened (consumed | superseded | revoked) and says so plainly: a superseded link is now a calm blue notice rather than a red error, because nothing went wrong and red framing on a no-fault message is exactly what makes a bounced user start over.
used_at matched the next token's created_at to the millisecond was killed by a reissue, not by a click — the signature of a frozen clock inside the request-link transaction. Identical means reissue; different means a real click. Three design calls carry weight beyond this feature. The reason rides alongside the existing "used" mark rather than in a new timestamp column, so the partial unique index that enforces at most one live link per user is left untouched — that index is the invariant, and it does not get widened. A revoked link now defers to the account reason, so a deactivated user finally reads about their account instead of a lie about a link. And an existing session outranks a dead link — a stale click while already signed in lands in the portal — but an unmatched token earns no such override, because a malformed or expired-out token is no evidence of whose link it is. Adversarial review then caught the cost of that refusal: it stranded a valid session on a page offering only "send me a link", so /login now offers an explicit — never automatic — way back in. Copy stays deliberately silent about email, because supersession proves a link was issued and never that it was delivered; a test now rejects any wording that promises otherwise. Gates: three adversarial rounds to approve, 925 tests, an eight-case live walkthrough on a synthetic login. Known adjacent gap, untouched: a collaborator unlinked from a merchant can still request a link and sign in to nowhere — the same deleted-user session gap already tracked elsewhere.Live 2026-09-18. Until now only a merchant's primary contact could add teammates, from their own Team page and behind the legal collaborator-access acknowledgment — and merchants kept asking Ainsworth to do it for them. The admin merchant page now carries a Team card: primary contact, collaborators, invite and remove. An admin never accepts the primary's acknowledgment for them. Instead the admin gives an attestation — a checkbox plus a required note saying who at the merchant asked and how — and the primary contact is emailed who was added. The same five-collaborator cap applies, and remove is there as the undo for a mistyped invite. No migration.
Live 2026-09-19. The privacy policy went up on the marketing site the day before; the portal's own sign-in and signup pages — the two places it collects a person's details before they have an account — now point at it. On sign-in it is a small centered link under the form, placed so it renders in every state the page can be in: the form itself, "check your email", rate-limited, and each error notice. On signup it reads as a sentence under the consent text — we handle your information as described in our privacy policy — with the link deliberately placed outside the consent label, so clicking it can never toggle the checkbox. Consent enforcement is unchanged: the form blocks submission without it and the server refuses anything else. Three files, no migration.
Live 2026-09-22. A person on a merchant's team was becoming a referral partner in their own right, bringing that merchant with them. The portal allows one login per email address, and every partner-invite path refuses an address already held by a merchant login — so there was no way to do it without making them a second email. Two guarded admin scripts now cover it: one converts a merchant teammate's login into a partner login (same email, sessions revoked, optionally attached to the new partner org in the same write), and one re-homes a merchant into another partner's book, re-notifying the primary contact on the new partner's first Act-as. Both show everything they will touch, dry-run first, and require a written note on the audit record. No app-route change, no migration; used once the same day, with each production write run by Mark.
Live 2026-09-23. TRX sent a single merchant application that serves Chesapeake, Esquire and Metropolitan Commercial Bank, with TRX deciding which bank gets the deal. Recon showed it is the same paper the portal already fills — identical fields, text and render on all five pages — so no new template, mapper, e-sign or hash change was needed. The one real change: when choosing which paper a placement uses, the ISO's paper now wins over the bank's, and that rule is one shared expression read by both the admin picker and the wet-sign gate (the wet-sign copy had been a hand-kept twin). Across every ISO × bank pair in production exactly one result changes — TRX → Chesapeake now gets the TRX paper — and no existing placement, generated MPA or signature is touched. No migration.
Live 2026-09-24 (e002c23). TRX may send one merchant to two or three of its banks (Chesapeake, Esquire, Metropolitan) on its one application. A placement is now one per merchant, ISO and bank, each with its own status, pends, VAR sheet and merchant account. An admin Add bank action creates the sibling placement; a live placement's bank can be named or corrected (its MIDs move with it) but never cleared. Disclosure policy v3: partners see the ISO from creation and the bank once the application is sent; merchants see ISO and bank only once sent, enforced in SQL. Partner decline emails name "ISO · bank"; merchant emails stay bank-agnostic, one per ISO per status. Merchant pends now show from every sent, still-open placement. Two migrations applied to prod by Mark before the code.
Live 2026-09-25 (434992e). With no placement saved, the admin Generate-MPA panel quietly defaulted to the Merrick form — an admin set TRX on an unsaved placement dropdown and generated two Merrick MPAs for a TRX deal (the same trap caught a merchant in July). The panel now reads the form from the placement: one form-bearing placement → that form, shown fixed, with a deliberate "use a different form" override that can be reverted; parallel channels on different forms → choose among only those, ISO named; no placement → nothing preselected and Generate / Send / Upload stay disabled until a form is chosen. At ship, 25 live merchants classified as 18 fixed, 6 none (previously silent Merrick) and 1 choice. The affected TRX MPA was regenerated on the right form, with Amex OptBlue added. No migration.
Live 2026-09-30 (67459a1). Banks — Fiserv asked first — want to see a merchant's documents before the application is signed, but a bank package required an executed MPA. An admin can now opt into a pre-signature package on the Secure bank package card: current documents plus the Executive Summary and no application of any vintage, with the file named as pre-signature. It is offered only while no executed MPA is on file; once one executes the server refuses it and the card says, with a warning mark and words, to regenerate the full package. Separately, checklist item C.5 is now "3 Months of Processing Statements (for merchants with prior processing)" and applies to every merchant. It stays conditional — it never counts toward progress or docs-ready — and a stale statement on it is an advisory warning rather than a block. One migration, applied before the code; the backfill added or renamed the item on 27 live merchants and fired no docs-ready notices.
Live 2026-09-30, four ships in one day (2c41b0b, 7581faf, d538c9b, 4e12bc2). An architecture review found that re-sending an MPA for signature treated any failed cancel of the earlier request as "superseded" — including Dropbox Sign's refusal because the earlier request was already fully signed — and the completion webhook then deleted that superseded request's PDF. A legally signed application could be lost. The fix came in four steps. (1) A re-send only supersedes on a successful cancel; an already-executed prior is saved immediately, and neither finalizer ever deletes an executed PDF. (2) The re-send now reconciles prior requests before creating the new one, and the database allows only one live MPA request per merchant. (3) A send reservation is written before Dropbox Sign is called, so a request orphaned by a crash still blocks the next send and is adopted or cancelled afterwards — an executed orphan is never dropped. (4) Any executed MPA on file stops a new send unless the admin deliberately clicks Send anyway (merchant signs again), and a paper-signature filing and an e-sign send now exclude each other. Three migrations plus one post-deploy index, all applied by Mark.
Live 2026-09-30, five ships (5100439, 5a5f17d, c28cbbb, a524c2c, edbbc4c). "Which application is this merchant's signed one?" was answered by a hand-written rule copied into each place that asked. It is now one database view — the latest real MPA request per merchant, with test-mode signatures invisible everywhere — read by bank packages, the mark-submitted gate, the partner status API, the MPA Tracker, the local poller and the step that files the signed PDF onto the checklist. A per-merchant e-sign version, bumped by a database trigger, makes a package activation and a concurrent signature change collide instead of slipping past each other. Checklist item I.1 (signed MPA) is now system-filled: only the executed application fills it, every upload door refuses it, and delete, N/A and needs-revision are refused on it. And I.1's status follows the view: a re-send un-satisfies it and re-arms the docs-ready notice, a cancelled re-send restores it, and restoring an abandoned merchant re-syncs it. Four migrations, applied by Mark before each deploy.
Live 2026-09-30 (4c0c92f). A Partner column now sits between Merchant and Vertical on the admin Merchants list, showing the partner of record, or "Direct" when there is none — the same label the Placements page uses. A master merchant's sub-merchant shows the master merchant; the referrer is deliberately not shown. The table also scrolls sideways now: its old wrapper clipped the right-hand columns on narrower screens, and the extra column would have made that worse. One file, no migration.
Live 2026-10-01, three ships (bb15b29, 5763ee1, ecfe141). A merchant needed a URL statement descriptor, its own consumer phone and a support email on the bank paper, and nothing in the portal could carry them. Now, on TRX and iBill paper: the application has a statement descriptor separate from the DBA; any merchant listing more than one website is asked for per-website details (brand name required, phone and support email optional); each described additional website becomes a Location created from the application; and the Add Location forms go out for signature automatically with the MPA — the lead website rides the MPA itself. Long DBAs, URLs and emails now print in full on the form instead of being clipped, and the fill refuses a value that still cannot fit. The same evening the lane got the MPA lane's send reservation: every batch send reserves before Dropbox Sign is called, the webhook adopts a request the portal never recorded, and the next send settles a stale reservation first — never on a guess. A signed form that no longer matches its Locations is kept and listed on the admin Locations panel with its download; executed paper is never dropped. Four migrations, applied before each deploy.
Live 2026-10-01 (0e4a4fb). A bank ask turned into a feature the same day. TRX's application has a contact email line and no customer service email line; TRX confirmed the customer service email belongs in that spot. The portal now always asks for both a contact email and a customer service email on the Processing step — they may be the same address, but the merchant states it — because banks ask for it on paper or in their boarding systems. The TRX MPA prints it on the contact email line, the Add Location form falls back to it, and the admin merchant header shows it. Forward only: no backfill and nobody blocked. One migration, applied before the deploy.
Live 2026-10-01 (c21a394). Found in the security review of the Add Location reservation branch, and live on main until this merge: the Dropbox Sign SDK's error objects hold the whole request, so logging a caught error could write the authorization header — and for network errors the key itself — into the runtime logs. Several call sites logged errors whole. Every SDK call now runs through one wrapper that rethrows an error holding only the message, status and the API's three error fields, each bounded and scrubbed of the key. The short-lived signed-PDF download URL is kept out of errors too. A build-time check fails if any other module imports the SDK or any call skips the wrapper.
Live 2026-10-02 (2497e1b), no migration. Named by Mark on 10-01 from findings the adversarial review raised on the reservation branch, two of them pre-existing since 07-24. Re-sending Add Location forms from the admin panel is now cancel-first, the MPA lane's rule: an earlier batch is closed only when Dropbox Sign proves it can no longer be signed, a batch that turns out to be signed is finalized with its PDF saved, and when the answer is uncertain nothing new is sent. Two new admin actions: Cancel signing request (the way to correct a locked detail, with no automatic recall on edit) and Check earlier send (settles a stale reservation without sending). Printed details are locked while a form is out — brand name, phone and support email from the moment it is sent; the MCC only while it is out for signature, so the bank-assigned code can still be recorded afterwards. Dropbox Sign's cancel event now closes an Add Location request, and signed paper that arrives after a cancel is still adopted, never dropped.
Live 2026-10-05 (810f7eb), no migration; the status label fix followed the same day (a1050a8). Found on a live merchant: the Secure bank package card refused with "Needs an executed MPA" although the merchant had signed. The portal keeps one status per signing request and stamps it "signed" on the first signature — so a two-signer TRX or iBill application reads "Signed" while it still waits on the Ainsworth inspector, and nothing on the page said so. Now an application that is out for signature shows each signer — the merchant and the inspector — as Signed or Awaiting signature (icon and words, with the signer's name and the signed, opened and reminded times), read live from Dropbox Sign and never stored; the page never waits on the lookup, and after five seconds the card says the status is unavailable rather than stalling. A Send reminder button re-emails one signer; who may be reminded is decided from Dropbox Sign's live status (awaiting, not reminded inside the hour, request not declined or fully signed, merchant not abandoned), and the reminder is on record before it is sent, so a reminder can never go out unaudited. The package card's hint now reads "the MPA is still out for signature" instead of demanding an executed one, and the admin label for the half-signed state reads "Signed — not yet executed" on the merchant page and the Merchants list.
Live 2026-10-05 (5c0dd93), migration applied before the merge. TRX and iBill paper carry an inspection block (the survey section, "inspector's signature"). Until now its signer was whichever admin pressed Send, which is how a live application went out with the wrong Ainsworth name on it and sat waiting on him. Mark's rule changed twice in one day; the first version ("Mark or Jeff only") was built, reviewed and never released. The rule that shipped: the merchant's sourcing agent signs by default; with no agent named, the partner's designated MPA signer (a new per-partner setting on the admin partner page, one of that partner's own people); for a direct merchant, one of Ainsworth's MPA signers (a new Settings card listing which admins may sign, and the default). The sender can always switch a partner deal to someone at Ainsworth — offered, never preselected while an agent or partner signer exists. A deactivated partner's people sign nothing; API service accounts are never signers. The signer is validated against that merchant's live candidates before any work, again before the earlier request is cancelled, and again right before Dropbox Sign is called — a signer who is gone stops the send with the earlier request intact. The signing email now addresses both people by role ("Sponsor agent: please review and sign the survey section"). Reassign hands an outstanding inspection signature to another candidate without re-sending to the merchant; a follow-up the same day makes the card say why Reassign is missing when nobody can be reassigned to, and what to set.
Plan written 2026-08-18, kickoff call held 2026-08-19 (Acquirers/Fiserv/APIs MarketPlace/INTEGRATION_PLAN.md, outcomes in §8a). US merchants only. This is Fiserv's own boarding platform: create an order, pull their agreement, upload documents, submit to underwriting, then poll — and on approval it hands back the MID and the TID, which is the step that today waits on a VAR sheet arriving by email. The portal turns out to be well placed for it by accident of timing: the Fiserv channel shipped 08-15, and the order-profile fields it added (delivery timing, the volume and acceptance mixes) are exactly what the API asks for. The kickoff settled the open questions, and two of the answers retire other work. Build against V3 boarding, not V1. Fiserv returns the merchant-populated agreement as HTML and sends no emails itself, so we render it and capture the signature — which makes the fillable MPA we authored a paper fallback at best on this lane, not something worth chasing approval on. The NAU credit check is replaced by the boarding flow entirely. Cost-plus at 900–1,000 bps over interchange is acceptable to them. Still open on Fiserv's side: a high-risk routing identifier for the credit team, and the certified gateway list.
2026-08-24 was the day this item stopped being blocked and started being built. The authentication wall that had held it since 08-19 turned out never to have been a signing problem at all — the recipe matched Fiserv's own sample exactly; the stored secret was simply a stale, truncated value, and the real linked pair arrived inside the collection Fiserv sent. With that corrected the sandbox answered on the first try. The exploratory run that followed exercised the whole reachable surface: the category and product lookups return, a full boarding payload validates, and — after the second false alarm of the day — an order boards, and Fiserv returns the agreement. The pricing failure that looked like a missing price list on our profile was actually a product combination nobody had priced; the priced combination works, on cost-plus as well as tiered. That is a build constraint, not a footnote: the portal has to keep merchants inside priced combinations rather than letting an admin assemble one that fails at the bank with an unreadable system error.
The agreement response turns out to be the entire application, not just the terms — business and owner detail, locations, entitlements and the fee schedule that was just boarded, and it arrives with the sensitive fields already masked, so the screen a merchant signs on carries no raw identifiers. It also states exactly which signature blocks it expects and confirms remote signing is enabled on the Ainsworth profile, which is precisely the two-mode design Mark locked the same day: signature captured in the portal by default, and partners who run their own signing process download and sign remotely. A 41-page sample was assembled from a real sandbox order to prove it end to end. The portal build itself has been grilled and is a conditional go.
Drafted 2026-09-09, reviewed by Fiserv Canada 2026-09-10, v0.2 built the same day, v0.3 — Fiserv co-brand plus French — built 2026-09-11. This is a second, entirely separate Fiserv relationship from the boarding API above — a different legal entity with its own underwriting, and a referral lane rather than an integration: Ainsworth fills an intake per merchant, Fiserv Canada drafts the merchant agreement in their own portal, the merchant e-signs from inside Canada, and their credit team reviews. Ten approved accounts come before a residual agreement. The reason it exists at all is a gap in our own product: the portal cannot board a Canadian merchant — employer ID, state, routing number and social security number are all US-shaped gates — so until a Canadian lane is grilled into the portal, this paper lane is the Canadian process, not a workaround around it.
What was built is two fillable, Ainsworth-branded intake forms generated from Fiserv Canada's own spreadsheet — a storefront / multi-location form carrying a column per location, and an e-commerce form covering the online sales profile and the website behind it — plus a machine-readable map from every field back to the row it came from on their sheet. That map is the deliberate seam: it is what a later script would read to turn a filled form straight into their spreadsheet, so the manual lane can become an automated one without redrawing the forms.
v0.3, on 09-11, made the forms bilingual and co-branded — and kept the seam intact while doing it. Fiserv's own wordmark now sits beside Ainsworth's on the masthead, which is not a liberty taken: the logo was requested by Fiserv's senior contact on the thread, so co-branding is authorized rather than assumed. Both forms also exist in Canadian French, four PDFs out of one generator driven by a two-language string table. The part that matters structurally is that field names and export values are identical in both languages, asserted at build time — the French entity-type dropdown shows French labels but exports the English values — so a French return maps onto Fiserv's spreadsheet exactly like an English one and the single field map still covers everything. Translating the forms did not fork the lane. The French is machine-drafted, though, and needs a native proofread before it reaches a merchant; that is the one new item this version put on Mark's list.
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.
Named by Mark on 2026-09-30 as the next slice; built 2026-10-01 on a branch (60406b2), not merged. Today, if a merchant with a signed application is sent a new one and declines it — or the request errors — the declined request becomes the latest row, so the checklist's signed-application item drops to not started and a full bank package is refused, even though the earlier signed application is still valid. The fix is an executed-pick view that skips declined and errored requests and falls back to the earlier executed MPA, with every executed consumer moved onto it: the I.1 sync, bank packages and their activation guard, the mark-submitted gate, the MPA Tracker, the poller and the filing guard. The partner status API keeps the current view, so partners still see "declined".
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.
Grilled 2026-08-21, GO, decisions locked. Booked for Saturday morning 08-22 and it has not run yet — 08-22 went to the two wet-sign slices and 08-23 to the notification step that finished the lane. Nothing about the plan changed and no new blocker appeared; it needs a morning with Mark and no other portal session running. This is the item that turns "the backups restore the system" into "the backups restore the data." Today the key protecting merchant PII exists only inside the hosting provider's runtime — unexportable, with no copy anywhere else — so if it were ever lost, every encrypted column in every backup would be permanently unreadable. The rotation mints a second key, escrows it to the shared "Ainsworth Continuity" vault before anything is encrypted under it (the vault was created and populated 08-21, Jeff added), switches new writes to it, and rewrites the existing rows from inside the runtime — the one place the old key can be read. The old key is not deleted at cutover; it stays until the November access review, because it is the only thing that could ever read a straggler discovered late. The proof is smallest-first: a counts-only dry run, then a single synthetic row re-encrypted and read back through the live system, then the book.
Live 2026-08-23. The lane worked end to end, but both sides had to go looking: a partner uploaded and nothing told an admin there was something in the review queue; an admin confirmed or rejected and nothing told the partner. On a lane whose entire premise is that a human confirmation is the attestation, a confirmation sitting unseen was the weak point. Three emails close it — a new pending upload alerts the internal distro (Mark and Jeff), and confirm or reject reaches the sourcing partner's own admins. The confirmation email says the application was accepted and never promises it is on file with anyone, because filing can still be mid-repair and the portal should not claim a bank send it has not made. A rejection carries Ainsworth's note verbatim plus a re-upload prompt, and the same email covers the case where an admin's own upload replaces a partner's pending copy. Mark approved all three partner-facing bodies word for word before ship.
With P1 through P5 live and P2 slice 2 closed by VAR sheet import, P6 (activities / the next plan phase) is the biggest remaining slice, and it gets grilled before anything is built. The whole tracker arc that ran 08-05 is finished, so nothing is sitting unmerged and nothing is queued ahead of it. As of 2026-08-10 this is also the top engineering item — the operational work that outranked it for a week (getting real processor data in) is done, and what's left of it is minutes of Mark's time rather than a build.
Logged 2026-08-05 and 08-06, both with explicit triggers. (1) The re-keyed MPA queue admits a deal on its executed signature, but a North API submission creates a placement with no signature row — so North deals could never appear on the board at all. (2) The abandon-closes-placements ship left an accepted residual: a concurrent create in a millisecond window can still strand one in-flight placement, and North is precisely the path where a placement write sits downstream of an irreversible external call. Both are harmless today because the North integration is built but uncredentialed and inert. They stop being harmless the moment North is switched on, which is why these carry a hard ordering rather than a priority.
Fourth bank template — own form (not Corduro family). File on hand; field names are noisy auto-generated. ~1-2 hours of mapping work when prioritized.
Hagen API submission. Aaron's responses cleared the path: own production group as sandbox, IP allowlist, 120 req/min. Smallest IRIS slice — validates the integration stack before push (v1.5b).
Account activated, Postman collection saved, research at Merchant Onboarding/MAVERICK_INTEGRATION_v1.md. Integration not yet built. Replaces the PDF pipeline for Maverick-placed merchants.
Research delivered 2026-09-04; the code half started and shipped twice on 2026-09-05. The portal already screened merchant websites, but the rules it screened against were assembled ad hoc. The new rubric replaces that with a written standard: a universal baseline plus fourteen high-risk verticals — supplements, nutra and nutra affiliate funnels, gaming and gambling, MLM, crypto, dating, digital services, firearms, health and medical, subscriptions and continuity, research-use-only peptides sold business-to-business, pharmacy and telehealth including GLP-1, and legal services — 271 checks, each carrying its severity, the condition that makes it apply, the on-page signals that trigger it, the rule behind it, why a sponsor bank cares, and the remediation to hand a merchant. Two slices are now live. The schema eb24f21 gave the engine the shape the rubric needs: three severities (stop / flag / informational, with the old telehealth and required labels demoted to display tags), a manual mode for checks a machine cannot judge — those render as a reviewer checklist whose answers are deliberately never stored — six business-context toggles that set themselves from the detected vertical and can be overridden by the agent, and a new pharmacy vertical. It also closed a quieter hole: a thin crawl can no longer return a clean verdict, because absence of evidence on a page nobody could read is not evidence of compliance. Then the first vertical went in 5a5c0c4 — fourteen pharmacy / telehealth / GLP-1 rules, eleven of them automated — and batch 2 completed it on 09-06 3063715 with six more. A merchant screened today is genuinely and fully screened against the new standard for that vertical; thirteen verticals remain.
3063715 — six more checks, all flags, all of them firing on what a page leaves out: no checkable accreditation, a compounded drug with no named compounder, a drug sold beside a benefit claim with no risk language, a dispensing site with no pharmacist and no reachable line, animal prescription drugs with no vet requirement, and an "FDA-registered" badge with no non-approval disclosure. Each fires with the reviewer’s question attached. The lesson from its sixteen adversarial rounds is the transferable part: three exemptions added after batch 1 merged were cut back out, because each one turned out to be a route an evasive page could take — so the posture is now that a stop fails toward the reviewer, accepting a handful of human-resolved false stops rather than a carve-out an evader can drive through. The crawler was hardened with it: every redirect hop is checked and the checked address is the one fetched, and treatment / medication pages are now read as product pages. Eight live pharmacy sites calibrated it. Next: the peptide business-to-business vertical, then the rest in the written risk order. Scoring — a 0–100 risk number in four bands with a categorical stop overriding the arithmetic — is deliberately deferred until two verticals are encoded, so the numbers get calibrated against real bank decisions rather than guessed. Carried caveats: a handful of existing coded rules still need citation corrections, and the Mastercard citations rest on secondary sources because the rulebooks were bot-walled.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.
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.
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.
Three numeric IDs still needed (Tab ID, Label IDs, owners catId). All API-discoverable during v1.5a build — Aaron email is fallback only.
Corduro / TRX parsers — highest-leverage Phase 6 next step. Reuses Phase 7's lib/utah/extract.ts primitive.
Persistent project home in Co-Work. Reads PORTAL_CONTEXT_FOR_TICKETING_PROJECT.md. Resolves 9 open questions → produces PLAN.md.
Context dump at Ticketing System/PORTAL_CONTEXT_FOR_TICKETING_PROJECT.md for the Co-Work session to read cold.
Merchant + partner support tickets, role-isolated views, reuses portal's notification + UI primitives.
"Invite a teammate" inline nudge on /dashboard for primary contacts with no collaborators. Retires once they invite anyone.
info@ as the address a person writes to about their own data, and whether anyone monitors that mailbox is unconfirmed — a published privacy contact that nobody reads is worse than none. Jeff holds the draft. No mailing address is shown either, which some jurisdictions expect.Compliance/Compliance Research/.6ce55d9b is still sitting at fully signed. Since the activation fix shipped, its page now offers Link agreement to VPS rather than the button that used to error. Mark presses it. (The MDS Ventures agreement was voided in the same ship — it was a real signature used to test the flow, kept as a record.)/admin/import. ingestion_batches and metric_snapshots are no longer at zero, and the feed is now nightly rather than a standing ask. The TRX Manage Portal login is still pending with Craig but is no longer blocking anything — NMI carries the whole live book./admin/alerts, close the six with the MCC-5411 note that's already seeded on the accounts: use Resolve, not False positive — they were true positives against the old 50% threshold, and mislabeling them teaches the wrong lesson to whoever reads the queue next. The retune to warn-80 / crit-90 shipped with the automation, so they won't re-fire below the new line.Applications/Venture Payment Solutions/PENDS_TRACKER_CORDURO_2026-08-03.md), but Mark's call is that we won't be responding to them — the Pend Tracker starts from new pends going forward. The captured file stays as reference. The one item that may still deserve an answer on its own merits is the missing/incorrect MPA on the Outdoor Ready Pro lead. Note the VPS applications mailbox is only reachable from mark@ainsworthpayments.com.Ainsworth Payments/CRM/Bank_Auth_Survey_Draft_2026-08-03.md. It asks the sponsor banks what they require for logins (MFA, SSO, revocation). As of 08-10 this is the only gate holding /bank at demo-grade — the data gate closed when real gateway volume landed, so this one email now stands between a built surface and real bank users on it.eb24f21, batch 1 5a5c0c4 and batch 2 3063715 are all running against real merchants; thirteen verticals remain and peptide business-to-business is next, then the rest in the written risk order. Still no dependency on anything, and the bank calibration ask runs alongside it rather than ahead of it. The owed adversarial pass is done (credits refilled, six reproduced defects fixed, final round approve). One new operational gap instead: the managed-crawler key in production is empty, so bot-walled sites cannot be fetched in prod until Mark sets it./bank. → Down to ONE gate: the bank-auth expectations survey answered (Mark sends). The data gate closed 08-10. Internal demo grants only until it clears.* 2.tsx / * 2.ts duplicates and corrupting .git. Repo moved to ~/dev/ainsworth-merchant-portal, with feature worktrees as siblings under ~/dev/. Closed.
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.
statements.ts, checklist-def ↔ nudge route. Deferred as a coordinated pass post Phase 7 review.
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.
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.
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. Still open after the 08-09 webhook touch — that pass added 'error' and 'superseded' to the predicates it needed, but wet_signed is still guarded only in TypeScript. Next webhook touch, as one transition-policy/CAS helper.
esign-problem-queue event.
event_hash has no freshness window (surfaced 2026-08-09)
Pre-existing, not introduced by the notification slice: event_time is never checked, so a captured hash replays forever. LOW — the transition guards mean a replay can't change state — but a bounded window is the standard fix. Mark's call.
executed column, so the executed-status test is still hand-written in several readers; system-filled is a code constant rather than a checklist input type, and the file doors still accept files onto URL-type items; the Add Location lane still supersedes on a refused cancel; a row stuck at signed with no webhook is finalized only by a re-send; the wet-sign verify script's fixture seeds two live requests, which the one-live index now forbids; the generate, send and wet-upload routes still accept any registered form (UI-only guard, open since 09-23). Update 10-01: the Add Location refused-cancel item is now part of the named next slice (signed-paper integrity).
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.
PARTNER_HANDBOOK_v1.mdAinsworth Payments rootPORTAL_HANDBOOK_v1.mdAinsworth Payments rootTECHNICAL_DOC_v1.mdAinsworth Payments rootdocs/PROJECT_STATE.mdcanonical project statedocs/POSITIONING_STRATEGY.mdaudience + pillarsdocs/AGENT_DASHBOARD_SPEC.mdUX specdocs/BANK_MPA_OPERATIONS.mdrunbook for new banksdocs/RESIDUAL_AUTOMATION_PLAN.mdPhase 8 plandocs/PHASE_7_BUILD_SPEC.mdUtah automationdocs/PHASE_6_5_AGENT_AGREEMENTS_PLAN.mdnext workstreamCANONICAL_APPLICATION_SCHEMA_v2.md7-bank reconciled supersetSTEP_B_PLAN.mdPhase 5 sequenceIRIS_INTEGRATION_v1.5.mdHagen API specMAVERICK_INTEGRATION_v1.mdMaverick API researchESIGN_OPTIONS_v1.mdDropbox Sign decisionPRD_Ainsworth_MerchantPortal_v1.mdv1 PRDDEVILS_ADVOCATE_v1.mdstress-test of v1.0