The decision model
The decision model
For a state, DefaultNexusEngine::evaluate() resolves a NexusStatus in this
precedence:
- Registered — the seller already holds a registration there (obligation handled).
- Triggered (physical) — asserted physical presence establishes nexus on its own, whatever the sales.
- Not applicable — the state levies no general sales tax (DE, MT, NH, OR). There is no threshold to cross and nothing to register for.
- Unknown — the engine cannot evaluate. See below; this is the important one.
- Triggered (economic) — cumulative activity crosses the threshold
(
combinator-aware: sales-only, sales-or-transactions, sales-and-transactions). - Approaching — activity is within the configured band (default 80%) of the threshold. A watch signal, not an obligation.
- Below — under the band.
"I don't know" is not "you're fine"
Unknown exists because the two are different answers and must not share a case. A state resolves to Unknown when:
- the threshold cannot be resolved — the dataset is fetched over the network, so a firewalled or misconfigured install resolves null for every state;
- the ledger cannot answer —
SalesLedger::activityFor()returned null, meaning unbound, unreachable, not yet populated, or — for a host serving several sellers — asked without a subject to answer for. To assert that nothing was sold, a ledger returnsnew SellerActivity(0, 0), which is what a real one does (aSUMover no rows is 0); - the totals were counted against a different question — the host declared a
basisorperiodon its activity that disagrees with what the state measures. Florida measures taxable sales over the previous calendar year; comparing gross calendar-YTD totals to its $100,000 is not an approximation of the answer, it is a different number.
Reporting Below in any of those cases reads as compliance. A seller could cross
every threshold in the country and see a clean board.
Acting on the result
Triggered and Unknown both return needsAction() === true. Unknown does
not need a registration — it needs an operator to find out why the engine could
not answer. Treating it as inert is what let the gap stay invisible.
Each NexusEvaluation carries the threshold, activity, a progress ratio
(0–1+ toward crossing), physicalPresence, a human-readable reason, and
caveats.
Caveats
caveats records what the verdict rests on that the engine could not check —
chiefly a host that fed totals without declaring which sales it counted or over
what window, or a state the dataset carries no basis for. A verdict with caveats
is still the best answer available, but it is not one the engine verified end to
end:
$e = $engine->evaluate($state);
if ($e->isQualified()) {
// "Register" — but which sales that rests on was never verified.
foreach ($e->caveats as $caveat) { … }
}
Reporting
A NexusReport buckets a set of evaluations into triggered(), approaching(),
registered(), unknown() and notApplicable().
Prefer needingAction() for a dashboard's primary list. It rolls up triggered
and unknown in one call, so a consumer cannot ship a reassuringly empty board by
rendering three buckets and forgetting the fourth.
$report = $engine->report($states);
$report->needingAction(); // crossed obligations AND states it could not evaluate
$report->notApplicable(); // DE, MT, NH, OR — nothing to comply with