A decision trace says WAIT. Did the risk check actually return false, or did its service time out?
Both paths may submit no order, but they need different fixes. An observed rejection means inspect the rule and its inputs. A timeout means restore the source of evidence. If the trace calls both WAIT, the second problem can sit there unnoticed.
My earlier example kept false separate from an absent field. Raju's follow-up question points to the next boundary: an observation can be present in the record but unavailable or invalid. The decision itself must respect that status.
Give each input an evidence state
This small example has two required checks: routeConnected and riskAllowed. It is a decision-trace example, not an order executor.
| Recorded input | Meaning | Decision state |
|---|---|---|
observed, value: true
|
The check ran and passed | Continue to the other checks |
observed, value: false
|
The check ran and failed |
WAIT, if all required observations are valid |
unavailable, with reason and attempt ID |
The source did not provide a result | BLOCKED |
invalid, with reason and source ID |
A result arrived but failed validation | BLOCKED |
| No input record | The trace is incomplete | BLOCKED |
The important order is: inspect all required inputs for missing or unusable evidence, then evaluate the observed booleans. Otherwise an early false can hide a later timeout.
A small decision boundary
const gateNames = ['routeConnected', 'riskAllowed'];
function decide(trace) {
if (!trace || typeof trace.decisionId !== 'string' || !trace.decisionId) {
return { state: 'BLOCKED', reason: 'missing_decision_id' };
}
const inspected = [];
for (const gate of gateNames) {
if (!trace.observations || !Object.hasOwn(trace.observations, gate)) {
return { state: 'BLOCKED', gate, reason: 'missing_observation' };
}
const observation = trace.observations[gate];
if (!observation || typeof observation !== 'object') {
return { state: 'BLOCKED', gate, reason: 'invalid_envelope' };
}
if (observation.status === 'observed') {
if (typeof observation.value !== 'boolean' || !observation.sourceEventId) {
return { state: 'BLOCKED', gate, reason: 'invalid_observed_value' };
}
inspected.push({ gate, value: observation.value, evidenceId: observation.sourceEventId });
continue;
}
if (observation.status === 'unavailable' || observation.status === 'invalid') {
if (!observation.reason || !observation.evidenceId) {
return { state: 'BLOCKED', gate, reason: 'missing_failure_evidence' };
}
return {
state: 'BLOCKED', gate,
reason: `${observation.status}:${observation.reason}`,
evidenceId: observation.evidenceId
};
}
return { state: 'BLOCKED', gate, reason: 'unknown_status' };
}
const failed = inspected.find(observation => observation.value === false);
if (failed) {
return { state: 'WAIT', gate: failed.gate, reason: 'observed_false', evidenceId: failed.evidenceId };
}
return { state: 'READY', reason: 'all_observed_true' };
}
For example, riskAllowed: { status: 'unavailable', reason: 'risk_service_timeout', evidenceId: 'poll-21' } produces BLOCKED. The poll-21 reference names the failed attempt. It does not pretend there was a risk observation. By contrast, { status: 'observed', value: false, sourceEventId: 'risk-20' } produces WAIT and points to the actual result.
Tests that make the distinction visible
I ran this boundary with Node's built-in test runner. All six local cases passed:
- Two observed
trueinputs produceREADY. - An observed
falseproducesWAITwith its source event ID. - An unavailable risk service produces
BLOCKED, neverWAIT. - An invalid risk value produces
BLOCKEDwith a reason. - A missing required observation produces
BLOCKED. - A false route check cannot hide an unavailable risk check. The result remains
BLOCKED.
The sixth case matters most. A system can decline to place an order in both cases while still telling its operator the wrong reason. The trace should preserve whether it saw a negative result or lacked evidence to decide.
This example only tests the decision mapping. It does not prove that a source event ID exists, that an observation is fresh, or that a broker accepted or filled anything. A production trace should retain observation time and source identity, reject stale records, and join the decision to any later order request and fill as separate events.
To check your own boundary, remove one required input, replace another with false, then make its source unavailable. If all three cases show the same state and reason, the trace still loses a failure mode.
Reference: Node.js test runner.
Top comments (0)