Testa
Loan Origination TriageAutomating a lending workflow does not remove the human, it changes what they are for. This is a study of how that workflow fails operationally, and a working environment for the people who have to diagnose it: what failed, what the evidence says, and what the operator can do about it.
Why this exists
Testa came out of a technical product analyst interview process at a mortgage origination software company. The written portion of that process asked for a specification and a product improvement proposal. Testa is the part nobody asked for.
The gap it was closing was mine. I had worked around property finance, doing acquisitions and portfolio operations at Divvy Homes, but never inside a loan origination system as a daily user. Reading about that job and doing it are different things. So I built somewhere to do it: a pipeline of loan files, a set of things going wrong inside them, and the tools someone would need to work out what happened. Most of what follows is a decision about who owns a problem, which is usually the harder half of an operational product.
A loan that stops moving does not say why
Modern origination systems hand work to software agents: classify this document, calculate qualified income, link this liability to the property it belongs to, submit the file for automated underwriting. Most of the time this works. The interesting question is the rest of the time.
When one of those steps fails, the loan stops. What the operator sees is a file that is not progressing. What they need to know is which step failed, what the agent concluded before it stopped, and what data it was working from.
Above all they need to know which kind of problem this is. A missing document, a wrong calculation, and a system that never ran are three different problems with three different owners.
That story is assembled by hand today, out of task logs, condition records, document records, and audit events. The reconstruction is the cost, and it is the part a product can absorb.
Five stages, and what can go wrong in each
Before designing anything I wrote the pipeline down: each stage's trigger, the rules that run in it, its failure modes, what counts as success, and what it advances to. That specification ships inside the product as a reference document, because the operator and the person diagnosing a failure need the same definition of correct.
- 1
Application received
- 2
Identity verified
- 3
Income validated
- 4
Underwriting review
- 5
Approved / denied

Six failures that are not the same problem
The queue is organised by what broke rather than by which loan it broke on. That is the central decision in the product, and everything else follows from it.
The agent ran, hit a real data gap, and stopped.
Liability linking found two mortgages on the credit report and a statement for only one of them.
The agent ran, completed, and got the wrong answer.
Social Security income annualized at face value; the 25% gross-up permitted for non-taxable income was never applied, understating qualified income by $1,200.
The work was done but never recorded against the thing it satisfies.
The borrower’s letter of explanation arrived and was classified correctly, but was never linked to the open condition, which stayed open.
A job started and never came back. No error, no completion.
A bank statement sat in PROCESSING for two hours while every other document classified in under ninety seconds.
Nothing failed. Something is absent.
Asset review flagged one large deposit and missed a second one over the same threshold, so no condition was ever created for it.
An upstream event fired and the thing that should have followed it did not.
Intent to proceed was recorded; the underwriting submission that depends on it was never attempted, and two downstream tasks sat blocked behind it.
They differ in who fixes them. A missing document is borrower work. A misapplied rule is a correction the analyst can make now and a defect engineering has to fix so it stops happening. A trigger that never fired is not the analyst's to fix at all. Sorting the queue by loan hides that distinction; sorting by failure type is what surfaces it.
What the operator needed to see
Show the agent’s reasoning, not just its status
A status of FAILED tells an operator nothing they can act on. Each task carries the steps the agent actually took: what it found, what it matched, where it stopped. Reading “searching for mortgage statement for 123 Birchwood Ln, no document found matching this address” ends the investigation immediately. Reading “FAILED” starts one.
Put the rule next to the number
For the income misfire, the view shows the agent’s figure, the corrected figure, and the Fannie Mae guideline that governs the difference, together. An operator should not have to know from memory that non-taxable income can be grossed up 25% in order to recognise that this loan was underwritten on the wrong income.
Name the downstream consequence
The stuck classification is not interesting on its own. What matters is that asset review completed while skipping that document, so a large deposit inside it may never have been reviewed. The failure is small; what it silently permitted is not.
Give each failure its own resolution actions
There is no generic resolve button. A missing document becomes a borrower task. A rule misfire becomes a correction the analyst applies to the file. A stuck classification can be retried or classified by hand. A trigger that never fired can be run manually. The actions differ because the underlying problems differ, and collapsing them into one control would hide exactly the distinction the queue exists to make.
Make the analyst write the diagnosis
Every issue has a free-text diagnosis field, and what is written there travels into the resolution note, into the audit trail, and into the escalation if it becomes one. It is the cheapest way to keep the reasoning attached to the file instead of in someone’s head.
Make escalation a structured handoff
When something is not the analyst’s to fix, the escalation captures issue type, affected loan, investigation summary, steps to reproduce, a pre-filled data snapshot, recommended action, and severity. The point is that engineering receives a reproducible report rather than “this loan is broken.” The analyst also does not have to know what a good bug report looks like in order to file one.

What happens after the diagnosis
Detection is the easy half. The product is only useful if the operator can finish the job, so each failure resolves along the path that fits it:
- Create a task for the borrower when a document is genuinely missing.
- Apply the correction when the system computed the wrong figure.
- Satisfy a condition or classify a document by hand when the agent could not.
- Re-run a task that deserves another attempt.
- Trigger the submission that never fired.
Some of those are human overrides of an automated decision. That is exactly why they are recorded as such, rather than applied silently.
When none of them is right, the issue leaves the queue as a structured escalation. The diagnosis the analyst already wrote is carried into it, along with a data snapshot. Filing a usable report costs almost nothing at the moment they decide to.

How the file got here
In a regulated workflow the current state is only half the question. The other half is how the file reached it, and it gets asked long after the fact by quality control, by an investor, by an auditor.
Every event lands on one timeline, attributed to whoever produced it: the system, an agent, the rules engine, the borrower, the loan officer, the analyst. Analyst resolutions append to that same trail with the diagnosis attached, so a manual correction is as visible and as explainable as an automated one.

What this deliberately is not
Everything is simulated
There is no real origination system underneath. Loan files, agent runs, and integrations are modeled data; state lives in memory and resets on reload. The environment demonstrates the interaction model, not a production integration.
Logic is per-issue, not a general engine
Each failure’s evidence and actions are written for that case rather than produced by a rules engine. Enough to show what the operator needs; not the machinery a real version would need behind it.
Six failure types, not a complete taxonomy
These were chosen to span distinct resolution paths (data gap, wrong output, missing output, silent system failure) rather than to enumerate everything that can go wrong in origination.
Resolution actions are optimistic
Applying a correction assumes the downstream system accepts it. A production version needs failure handling on the resolution itself, which is its own design problem.
Thresholds and timings are illustrative
The ninety-second classification expectation and similar figures are there to make “this is late” legible. Real ones come from operators and production data.
What I would validate, and what I would measure
- Whether an operator can act on the explanation without opening the loan file. The whole premise is that they can.
- Whether the six categories match how operations teams actually route work, or whether they cut across real ownership boundaries.
- Which failures consume the most time, which is what should drive the queue’s ordering rather than severity alone.
- Whether the structured escalation reduces round-trips with engineering, and what engineering still has to ask for.
Time to identify
From issue appearing to a recorded diagnosis. This is the number the product is designed to move.
Time to resolution
From appearing to closed, split by failure type. The types should behave differently.
Resolved without escalation
Share of issues an analyst closes alone. Rising means the evidence is sufficient.
Escalation round-trips
Follow-up questions per escalation. A good handoff needs none.
Repeat failures by type
The same rule misfiring across files is a product defect, not a queue of separate issues.
Implementation
React and Vite, deployed as a static build with no backend. Application state lives in a single store, and resolutions are mutations against the loan record. That is what keeps a correction, its audit entry, and the issue's new status consistent with each other.
The domain model carries the weight. Six loan files are modeled with liabilities, income sources, assets and transactions, conditions, documents with classification state, agent tasks with their reasoning and errors, and a full audit trail. Issues reference those records rather than restating them, so an investigation view reads from the same data the loan file does.
A test-case register defines expected behaviour for ten scenarios. The six failing ones link directly into the queue. It is a small thing, but it makes the environment feel like a system being tested rather than a demo being performed.