E2E harness re-run = ALL PASS: TX returns real availability via the open-data API, NV returns unknown (available=None) to flag manual verification, both flow through to correct ERPNext Sales Orders. Removed one-off portal-probe scripts.
262 lines
17 KiB
Markdown
262 lines
17 KiB
Markdown
# Readiness: corporate orders + "move a company" (conversion / domestication)
|
|
|
|
Honest assessment as of 2026-06-09. Question: are we ready to take Nevada/Texas
|
|
incorporation orders end-to-end (name search, accept into ERPNext, etc.), and the
|
|
mechanics of moving a corp out of Delaware + annual report + EIN.
|
|
|
|
## Short answer
|
|
- **New NV/TX formation orders - order intake + ERPNext VERIFIED e2e; name search
|
|
now FIXED for TX, and honest for NV.** The e2e harness (`scripts/e2e-formation-order.mjs`)
|
|
confirms: live name search -> formation_orders insert -> ERPNext customer -> Sales
|
|
Order with the correct `BUSINESS-FORMATION` + `STATE-FILING-FEE` line items and
|
|
totals ($254 NV LLC, $479 TX corp) -> DB linkage, all PASS.
|
|
- **TX name search now works** via the Texas open-data API (no scraping). Returns
|
|
real availability (exact match => taken; no rows => available; error => unknown).
|
|
- **NV name search returns "unknown"** (available=None) by design: the NV portal is
|
|
behind Incapsula bot protection with no public API, so we flag NV names for a
|
|
manual admin check rather than fake a result. Never a false "taken".
|
|
- Still pending before a self-serve checkout: the actual NV/TX *filing* automation
|
|
(SOSDirect login flow / SilverFlume) is unverified, and NV name search is manual.
|
|
The DEXIT page points at a contact form, not this checkout.
|
|
- **"Move a company" (DE -> NV/TX conversion/domestication):** **NOT built.** There
|
|
is no order type, no SKU, and no fulfillment for a conversion. This is the core of
|
|
the DEXIT promise and is the biggest gap.
|
|
- **Annual report filing in the new state:** **NOT built** as automation. There is an
|
|
`annual-report-filing` slug, but it is wired to the trucking admin-assisted handler
|
|
(`MCS150UpdateHandler`), not a corporate state-filing flow.
|
|
- **EIN:** for a move you generally do NOT get a new EIN (see mechanics below); our
|
|
`ein_worker` only obtains a NEW EIN, which is the wrong operation for a conversion.
|
|
|
|
So: **do not turn on a "buy now" DEXIT checkout yet.** Keep the page as a lead-gen
|
|
"get my estimate" CTA (which it currently is) until the flow below is built + tested.
|
|
|
|
## E2E test results (2026-06-09) and the name-search bug
|
|
Ran `scripts/e2e-formation-order.mjs` inside the api container against live prod
|
|
(real DB + real ERPNext, no real Stripe charge, no real state filing).
|
|
|
|
**PASS - order intake + ERPNext Sales Order (both NV and TX):**
|
|
- live formation_orders insert,
|
|
- ERPNext customer find/create,
|
|
- Sales Order created with `BUSINESS-FORMATION` + `STATE-FILING-FEE` line items,
|
|
correct totals ($254 NV LLC = $179 + $75; $479 TX corp = $179 + $300),
|
|
- `formation_orders.erpnext_sales_order` linkage written,
|
|
- cleanup (cancel+delete SO, delete order row).
|
|
|
|
Bugs the harness caught and we fixed:
|
|
- The formation Sales Order referenced ERPNext Items `BUSINESS-FORMATION` and
|
|
`STATE-FILING-FEE` (and `FOREIGN-QUAL-SINGLE/MULTI`) that **did not exist** -> the
|
|
SO creation was silently failing on every formation order. Created the Items.
|
|
- `entity_type` check constraint requires lowercase (`llc`, `corporation`, ...).
|
|
- The API's `GET /states/:code/name-search` called `WORKER_URL/name-search`, but the
|
|
worker had **no such route** (404 -> silent fallback to stale `entity_cache`).
|
|
Added a synchronous `/name-search` worker route, and fixed `handle_name_search`
|
|
(both referenced a nonexistent `search_name_sync`).
|
|
|
|
**FAIL (open) - live name search silently returns "unavailable" for everything.**
|
|
After wiring the route, a deliberately unique nonsense name still returns
|
|
`available=false` with no similar names. Root cause: the **NV state-portal adapter
|
|
times out** (`Page.fill: Timeout ... waiting for locator("input[type=text], ...")`)
|
|
because the Nevada SOS / SilverFlume search page no longer matches the adapter's
|
|
input selector, and `search_name()` swallows the exception and **defaults to
|
|
`available=False`**. So:
|
|
- We would wrongly tell customers an available name is taken (or never validate it).
|
|
- This is a **portal-scraping maintenance task**: inspect the current NV (and verify
|
|
TX) name-search DOM, update `states/nv/adapter.py` (and `tx`) selectors/flow, and
|
|
make `search_name()` distinguish a real "taken" result from an adapter error
|
|
(return an explicit error/unknown state, never a false "taken"). NOTE the adapter
|
|
also MIS-PARSES on its happy path: via the route a unique name still returns
|
|
available=false (the search page content does not match the adapter's "no results"
|
|
phrases), so the fix must update both the input selector AND the results parsing.
|
|
We did harden the error path: search_name() now returns available=None on a thrown
|
|
adapter error. The harness re-run is the acceptance test.
|
|
|
|
**Conclusion:** the ERPNext/order plumbing is sound and verified; **do not enable a
|
|
self-serve formation checkout until name search is fixed** (a customer must be able
|
|
to trust the availability check), and the "move a company" flow is still unbuilt.
|
|
|
|
## What already exists (the good news)
|
|
The corporate/formation machinery is real and reusable:
|
|
- **Checkout + order intake:** `api/src/routes/checkout.ts` has `order_type: "formation"`,
|
|
builds a Stripe line item `Business Formation (<state> <LLC|CORP>)`, and the
|
|
`formation_orders` table carries `stripe_session_id`, `payment_status`,
|
|
`erpnext_sales_order`, etc. ERPNext SO creation is wired for formation orders.
|
|
- **Name search:** `GET /api/v1/states/:code/name-search` (24h cache in
|
|
`name_search_cache`) -> calls the worker -> per-state adapter. **TX** uses the
|
|
Comptroller Taxable Entity Search (free, no login); **NV** has an adapter too.
|
|
- **Filing automation:** `scripts/formation/` is a full subsystem -
|
|
`formation_worker.py` polls `formation_orders`, `states/tx/adapter.py` +
|
|
`states/nv/adapter.py` implement `search_name` / `file_llc` / `file_corporation`
|
|
via Playwright (TX = SOSDirect, requires login + ASP.NET viewstate handling),
|
|
plus `ein_worker.py`, `operating_agreement.py`, `document_delivery.py`,
|
|
registered-agent via Northwest RA, and ~55 state adapters scaffolded.
|
|
- **The `FormationOrder` model** carries entity name/alt, members, RA, addresses,
|
|
shares_authorized, par_value, expedited, payment card (Relay virtual debit), and
|
|
result fields (filing number, confirmation, documents).
|
|
|
|
## What's missing for NEW NV/TX formation (smaller gap)
|
|
1. **E2E verification.** The TX/NV adapters target live state portals (SOSDirect
|
|
needs an account login; both are ASP.NET/viewstate + possible CAPTCHA). We have
|
|
not confirmed a clean run recently. Need: a staged dry-run (name search ->
|
|
formation_orders insert -> worker pick-up -> ERPNext SO -> filing in a sandbox or
|
|
a real low-stakes filing) with screenshots, and CAPTCHA handling confirmed.
|
|
2. **ERPNext SO for formation** exists in code; verify it actually creates the SO
|
|
with the right items + state gov fee line (we hit a gap like this on the trucking
|
|
compliance_batch flow - SOs weren't being created). Add NV/TX formation Items if
|
|
missing (we created LLC-FORMATION / CORP-FORMATION recently).
|
|
3. **Pricing/SKU sanity:** TX/NV LLC = $300 gov fee, Corp = $300; expedite +$25/$50.
|
|
Our `corp-formation` / `llc-formation` catalog entries need gov_fee plumbed.
|
|
|
|
## What's missing for "MOVE a company" (the big gap)
|
|
This is a different operation from formation. Real-world mechanics:
|
|
|
|
### The legal mechanic (two paths)
|
|
A company changes its state of incorporation by either:
|
|
- **(A) Statutory conversion / domestication** (preferred, cleaner): the entity
|
|
re-domiciles. **Delaware** files a **Certificate of Conversion** to convert OUT
|
|
(DGCL 266) and the **destination state** files a conversion/domestication in:
|
|
- **Texas:** TBOC Ch. 10, Subch. C - "Certificate of Conversion" + new Certificate
|
|
of Formation. The TX entity is a continuation of the DE entity (same legal person).
|
|
- **Nevada:** NRS Ch. 92A.105+ "conversion"; file Articles of Conversion + new
|
|
Nevada charter (NRS 78).
|
|
- **Florida:** F.S. 607.11921+ conversion.
|
|
Both DE-out and new-state-in filings are required. The entity keeps its identity,
|
|
contracts, and history.
|
|
- **(B) Reincorporation merger** (older method, what FG Financial used): form a new
|
|
NV/TX subsidiary and merge the DE parent INTO it. Requires an Agreement and Plan
|
|
of Merger + stockholder vote. More moving parts; still common.
|
|
|
|
### Steps a real DEXIT order involves (none automated yet)
|
|
1. **Diagnose**: pull the entity's current DE status, authorized shares (to estimate
|
|
the franchise tax saving), good-standing, foreign qualifications.
|
|
2. **Board + stockholder approval**: a board resolution and (usually) a stockholder
|
|
vote/consent approving the conversion or merger. **This needs the client's counsel
|
|
- it is NOT something we file.** Our role is to prepare the plan-of-conversion /
|
|
plan-of-merger documents for their counsel to review and their board to adopt.
|
|
3. **Pay DE to leave**: DE requires the franchise tax to be **current** before it
|
|
will accept the Certificate of Conversion (you can't leave owing tax). So step 0
|
|
is often "file/pay the final DE franchise tax + annual report."
|
|
4. **File the conversion**: Certificate of Conversion OUT of DE (DGCL 266) +
|
|
Certificate of Conversion/Formation INTO the destination state. Both have fees.
|
|
5. **New registered agent** in the destination state (recurring; we use Northwest RA).
|
|
6. **First annual report / state list** in the new state (NV requires an Initial List
|
|
+ State Business License at formation/domestication; TX has the Public Information
|
|
Report / franchise tax with the ~$2.47M no-tax-due threshold).
|
|
7. **Update downstream**: foreign-qualification re-registration in states where the
|
|
company operates (the domestication may need to be reflected), update the transfer
|
|
agent / DTC, update EDGAR (state-of-incorporation on the next cover page), bank, etc.
|
|
|
|
### EIN reality
|
|
- A **conversion/domestication generally KEEPS the same EIN** - the IRS treats a mere
|
|
change of state of incorporation (same entity continuing) as not requiring a new
|
|
EIN in most cases. So our `ein_worker` (which obtains a NEW EIN) is the wrong tool
|
|
for a move; for a conversion we typically do nothing with the EIN, or at most file a
|
|
name/address change with the IRS.
|
|
- A **reincorporation MERGER into a new subsidiary** can be different: if the surviving
|
|
entity is genuinely new, the IRS may require a new EIN. This is a fact-specific,
|
|
counsel-driven determination - **we should not auto-decide it.**
|
|
- Net: EIN handling for a move is **advisory + occasionally a name/address update**,
|
|
not the automated SS-4 flow we have.
|
|
|
|
### Why we can't fully automate the move
|
|
Unlike a fresh formation, a conversion is **counsel-gated** (board/stockholder
|
|
approval, plan-of-conversion review) and **DE-clearance-gated** (must be current on
|
|
franchise tax). The honest product is **admin-assisted**: we prepare and file the
|
|
state paperwork and set up RA + first annual report; the client's lawyer handles the
|
|
corporate-approval documents. That matches how the DEXIT page is already written
|
|
("your counsel just reviews the board and stockholder consent").
|
|
|
|
## Complication: the company has foreign qualifications in other states
|
|
|
|
This is common and important. A Delaware corp that operates in California, New York,
|
|
Texas, etc. is "foreign qualified" (registered as a foreign entity / has a Certificate
|
|
of Authority) in each of those states. When it domesticates DE -> NV/TX, every one of
|
|
those foreign registrations is affected. Getting this wrong means the company is
|
|
suddenly doing business unregistered in states where it operates - default judgments,
|
|
loss of court access, penalties. So this is part of the core offer, not an afterthought.
|
|
|
|
### What actually has to happen to the foreign registrations
|
|
After a conversion/domestication, the entity is the *same legal person* but its
|
|
**home (domestic) state changed**. In each state where it was foreign-qualified:
|
|
- **The state where it is moving TO** (say it domesticates to Texas but was foreign
|
|
qualified in Texas): the foreign registration must be **withdrawn/cancelled** because
|
|
the company is now a *domestic* Texas entity - you cannot be both foreign and
|
|
domestic in the same state. (This is exactly why ~zero of our OTC sample were
|
|
TX-incorporated even when TX-based: they were DE corps foreign-qualified in TX.)
|
|
- **Every OTHER state it was qualified in** (CA, NY, FL, etc.): the foreign
|
|
qualification generally **stays in place but must be updated** to reflect the new
|
|
state of incorporation and (often) a new formation date / charter document. States
|
|
differ:
|
|
- Some accept an **amendment to the foreign registration** (file an amended
|
|
Application for Authority / Statement of Change reflecting the new domestic state).
|
|
- Some require you to **withdraw the old foreign registration and re-file a new one**
|
|
from the new home state.
|
|
- A handful treat the domestication as a non-event if the name + identity are
|
|
unchanged, requiring only an informational update at the next annual report.
|
|
- **Name conflicts** can surface: a name available to a DE corp as "foreign" in a state
|
|
might collide on re-domestication; we should run name availability in each qualified
|
|
state as part of the move.
|
|
|
|
### Good news: we already have the building block
|
|
We have a working **foreign-qualification** capability:
|
|
- SKUs `foreign-qualification-single` ($149 + state fee) and `foreign-qualification-multi`
|
|
(discounted per-state), `ForeignQualificationHandler` that **fans out per state**,
|
|
a `state_registrations`/foreign-qual schema (migration 066/073), and the same
|
|
formation state-adapter pool. So re-qualifying or amending in N states reuses
|
|
existing plumbing - we don't build it from scratch.
|
|
- What's missing is the **amend / withdraw** modes (the handler today is oriented to
|
|
*new* registration), and an intake step that asks "which states are you currently
|
|
foreign-qualified in?" so we can fan the move out across them.
|
|
|
|
### Product implication
|
|
The conversion offer should be **multi-part and priced per state touched**:
|
|
1. Core domestication (DE-out + new-state-in) - the base fee.
|
|
2. For each state the company is foreign-qualified in: an **amend / re-qualify /
|
|
withdraw** line item (reuse foreign-qualification per-state pricing).
|
|
3. Withdraw the now-redundant foreign registration in the destination state if one
|
|
existed.
|
|
4. Update the registered agent in each affected state where we maintain it.
|
|
|
|
This is also a **revenue multiplier**: a single DE corp qualified in 5 states is one
|
|
domestication + ~5 foreign-qual amendments + recurring RA/annual-report in each. But
|
|
it must be scoped at intake - we have to ASK for the list of states up front, estimate
|
|
per-state, and disclose that government fees vary and are billed at cost.
|
|
|
|
### Intake + data we must capture for a move
|
|
- current domestic state (DE), destination state (NV/TX/FL), entity type
|
|
- entity name + EIN (kept), current good-standing + franchise-tax status in DE
|
|
- **the full list of states where the company is foreign-qualified** (and its DBA/
|
|
assumed names in each) - this drives the per-state fan-out
|
|
- where it actually operates / has nexus (to advise whether to keep each qualification)
|
|
- whether counsel is handling the board/stockholder consent (always yes)
|
|
|
|
## Recommended build plan (generic corporate flow + a "move" capability)
|
|
Keep it generic, not DEXIT-specific:
|
|
|
|
1. **Catalog SKUs** (in `api/src/service-catalog.ts`):
|
|
- `entity-conversion` (move a company; admin-assisted; flat service fee + state
|
|
gov fees billed at cost; destination state chosen at intake).
|
|
- Confirm `corp-formation` / `llc-formation` carry per-state gov_fee.
|
|
- `annual-report-filing` for corporate (today it points at the trucking handler).
|
|
2. **Order type**: add `order_type: "entity_conversion"` to checkout + a
|
|
`from_state` / `to_state` / `entity_type` intake, persisted in `formation_orders`
|
|
(or a sibling table). Reuse the Stripe + ERPNext SO path.
|
|
3. **Fulfillment**: a `ConversionHandler` (admin-assisted) that:
|
|
(a) runs name availability in the destination state (existing name-search),
|
|
(b) generates the plan-of-conversion + new-state charter draft for client/counsel,
|
|
(c) queues the DE-out + destination-in filings for the formation_worker once the
|
|
signed board/stockholder consent + DE good-standing are confirmed,
|
|
(d) sets up RA + files the first annual report/state list.
|
|
4. **E2E test harness**: a scripted run that does name search -> creates a paid test
|
|
order -> verifies `formation_orders` row + ERPNext SO + worker pickup, with the
|
|
actual state filing stubbed/sandboxed so we don't make a real filing during tests.
|
|
5. **Verify NV/TX formation e2e FIRST** (smaller scope) before layering conversion on
|
|
top, since conversion reuses the same filing + ERPNext + worker plumbing.
|
|
|
|
## Bottom line
|
|
- The page is fine to ship as **lead-gen** (estimate request), which is what it does.
|
|
- We are **not** ready for a self-serve "buy a DE->NV/TX move" checkout. New NV/TX
|
|
formation is close but unverified e2e; the conversion ("move") flow and corporate
|
|
annual-report automation do not exist yet.
|
|
- Next concrete step (if you want to proceed): verify NEW NV/TX formation e2e, then
|
|
build the generic `entity-conversion` SKU + admin-assisted handler on the existing
|
|
formation plumbing.
|