<!-- https://staircase.co/factory -->
# How this was built

# How this was built

A fixed five-artifact shape for every vendor integration, a build rule that failed any endpoint without documentation, and a generator for new jurisdictions.

Three machines, each doing the same thing in a different domain: fixing the shape of a unit of work so that adding the next one costs less than the last.

The integration factory fixed the shape of a vendor. The build rule fixed the shape of a service. The jurisdiction generator fixed the shape of a data source.

## The integration factory

A vendor integration is five artifacts in a fixed shape, none of which touches product code.

- **connection configuration** — The wire protocol. One repository per vendor API call, not per vendor.

- **language configuration** — The field mapping between the vendor's vocabulary and the canonical model.

- **flow file** — The four-way translation: canonical request, vendor request, vendor response, canonical response.

- **waterfall priority** — One line naming where this vendor sits in the vertical's ordering.

- **status call** — Activating the vendor for a lender, optionally with the lender's own credentials.

## Translation, the artifact that makes one contract serve every vendor

A translation is declared, not coded. Each one names four vocabularies: what the canonical model sends in, what the vendor's request expects, what the vendor's response returns, and what comes back out as canonical. Each row below is one such declaration, read from the committed configuration for that integration.

The consequence is that a required-field rule stated once at the model propagates into every caller's own field names. A caller working in its own dialect receives the validation error against its own paths, with no per-caller implementation, and a product's contract never changes when a vendor is added.

| Provider | Flow | Canonical in | Vendor in | Vendor out | Canonical out | Lender credentials |
| --- | --- | --- | --- | --- | --- | --- |
| Argyle | `income` | `staircase-graph` | `income-input` | `income-argyle-output` | `staircase-graph` | supported |
| Essent | `essent-insurance-flow` | `staircase-graph` | `essent-input` | `essent-output` | `staircase-graph` | not enabled |
| Plaid | `asset` | `staircase-graph` | `asset-input` | `asset-plaid-output` | `staircase-graph` | not enabled |
| Xactus | `credit-verify` | `staircase-graph` | `credit-input` | `credit-mismo231` | `staircase-graph` | — |

A language configuration carries a ruleset in each direction, rules with stable identifiers, and an error-message mapping — so two vendors returning different codes for the same condition surface as one code to the caller.

Test requirements are part of the contract: a case per rule, a case per enumeration value, warning assertions, coverage assertions, and validation of the resulting canonical collection against the model.

Bulk translation runs on Spark. The recorded measurement is 7,848 translations in 299 seconds across 20 executors.

## The endpoint documentation rule

A rule in the build gate failed any build containing an endpoint that did not appear in its own specification. Not a warning, not a report — a failed build. Every service therefore carries a specification that matches what it actually serves, at a canonical path.

A documentation rule enforced by the build is a different thing from a documentation culture. One of them survives six years without anybody maintaining it.

The gate itself: Assess · the pipeline it runs in: Build

## How the waterfall runs

- A vendor that answers asynchronously by webhook resumes the same execution as one that answers synchronously. The waterfall does not distinguish between them.
- On a vendor failure with attempts remaining, a new result collection is persisted, stamped with the vendor name and a failure result, before the next vendor is tried. The complete sequence of who was tried, in what order, and what each returned is queryable after the fact rather than reconstructed from logs.
- A lender routes through Staircase's own vendor contract or supplies its own credentials, per vendor, with no code change.
- Every invocation carries a dry-run branch, and individual vendor flows carry their own dry-run state returning a recorded fixture.

## The jurisdiction generator

The same discipline, pointed at public records. One sample page in, an extraction pipeline out, validated against the same canonical classes every other jurisdiction uses. Coverage stops being a vendor negotiation and becomes a compute question.

1. One sample page in — A single record from the jurisdiction's own portal is the whole input. No schema negotiation, no vendor conversation, no bulk file.
1. Retrieve the worked examples — Every jurisdiction already solved is retrieved as a worked example, so the marginal cost of the five-hundredth source approaches the marginal cost of the fifth.
1. An agent writes the extraction pipeline — The output is a jurisdiction-specific extraction pipeline, not a one-off scrape — versioned, testable, and re-runnable.
1. Validate against the canonical model — Extracted records are validated against the published class definitions before they enter the dataset, so a new jurisdiction cannot introduce a shape no downstream consumer understands.
1. Publish as a queryable, agent-callable surface — The jurisdiction becomes a query table an agent can reach directly, and a completeness report says what it does not have.

The onboarding contract is five scripts per jurisdiction — owner, structure, layout and utility mapping, then an assembler that reads the raw capture plus those four outputs and writes the canonical record set. The four mappings run in parallel; the assembler runs last.

The scripts are generated. A graph agent reads one sample page plus the seed address record and writes the five, then the output is validated against the class schemas and the resulting error list is fed back in as context for the next attempt. The recorded cost of a generation run is about an hour of wall clock and roughly 10 dollars of tokens.

Generation retrieves from jurisdictions already solved: every function in the existing transform corpus is indexed by embedding, so authoring the next jurisdiction's structure mapping starts from the closest working implementations rather than from nothing.

Enumerations are the failure surface. A generated script throws on an unmapped source value rather than assigning a default, which is what keeps a plausible wrong value out of the dataset.

## Where it reaches today

Coverage is reported from the deployed configuration rather than from a record count. A jurisdiction appears here when a configuration names it; a surface it does not name is absent rather than estimated.

Property records and permits are separate surfaces with separate coverage. Permits are issued by municipalities rather than counties, so a jurisdiction serving property records does not automatically serve permits, and the two are reported apart.

| Jurisdiction | State | Property records | Permits |
| --- | --- | --- | --- |
| Lee | Florida | served | — |
| Palm Beach | Florida | served | — |
| Miami-Dade | Florida | served | — |
| Orange | Florida | served | — |
| Santa Clara | California | served | served |

### Florida, as the reference case for coverage

The Florida jurisdictions above are what a served market looks like: records validated against the canonical classes, a query surface an agent calls directly, and completeness reported per source rather than assumed. A tool that says what it does not have is the strongest available credibility signal, and it is already how this surface behaves.

### Texas, as the reference case for the mechanism

No Texas jurisdiction is configured. Serving one means running the same five steps above against a jurisdiction with no committed configuration behind it, which is what entering a market costs when the constraint is data rather than desire.
