Skip to content

Fallible Sensor Containment

JacqOS is a physics engine for business logic. In that frame, this pattern is the staging room every fallible sensor reading must sit in before the engine is allowed to treat it as part of the world. The reading does not “happen” until a promotion rule you wrote lets it in.

Taco Bell experimented with an AI drive-thru that confidently submitted orders it had mis-heard. A customer joked-requested 18,000 waters; the order propagated to the POS, the kitchen started pulling cups, and the video went viral. The failure was not that the model made a mistake — every sensor makes mistakes. The failure was that a mistake propagated directly into an action with no gate between transcription and execution.

Every fallible sensor has this shape. Voice parsers mis-hear. Vision models mis-label. OCR mis-reads. LLM extractors hallucinate fields. The model sounds confident; the data is wrong; something downstream treats it as fact.

JacqOS makes sensor output structurally incapable of becoming accepted truth without an explicit promotion step you wrote and can inspect.

Every sensor output lands in a staging area. Staged evidence can be read, counted, and graphed — but nothing downstream that depends on accepted truth can fire until a promotion rule says so. A reasonable promotion rule might be:

  • “Accept this parse if the customer confirmed it.”
  • “Accept this extraction if a clinician approved it.”
  • “Accept this bounding box if confidence is above 0.95 and no conflicting box exists at the same pixel region.”

You write those rules in plain Datalog. JacqOS enforces at load time that no rule tries to bypass them.

This means:

  • A sensor producing nonsense is isolated to the staging area. It never reaches an accepted fact, never derives an intent, never causes an effect.
  • When a sensor produces something reasonable and the confirmation arrives, the promotion fires deterministically.
  • A replay of the same input stream always produces the same acceptance decisions. Provenance lets you trace any accepted fact back to the exact sensor observation and the exact confirmation that promoted it.

Run the Drive-Thru demo in Studio. Scenario tiles inject synthetic voice-parse observations; the deterministic parser produces a structured parse result; the containment plays out live.

  • Normal order → the Done tab shows a row such as accepted_order: cola x2, no ice — confirmed by customer, submitted to POS. Drill into it and the six inspector layers tell the full story from POS submission back to the voice-parse observation.
  • 18,000 waters → the Waiting tab shows a staged parse that never accepts. Drill in and the inspector surfaces the acceptance rule, the missing confirmation, and the bounds invariant that would fail if the parse were promoted. The POS submission never derives.
  • Correction turn → a second parse arrives for the same order; the first staged candidates retract and the second takes their place. Activity animates the transition.

At no point does JacqOS need to trust the parser. The parser can be as bad as you like. Containment does not depend on its quality.

The mapper declares that certain sensor-produced atoms must be routed through the candidate.* relay namespace before they can support any accepted fact:

mappings/inbound.rhai
fn map_observation(obs) {
match obs.kind {
"voice.parse_result" => [
atom("order.id", obs.payload.order_id),
atom("turn.seq", obs.payload.seq),
atom("parse.item", obs.payload.item), // requires_relay
atom("parse.quantity", obs.payload.quantity), // requires_relay
],
// ...
}
}
fn mapper_contract() {
[("voice.parse_result",
["parse."],
"candidate")]
}

A staging rule lifts those atoms into the reserved candidate.* namespace:

rule assert candidate.requested_item(order, item, seq) :-
atom(obs, "order.id", order),
atom(obs, "parse.item", item),
atom(obs, "turn.seq", seq).

A promotion rule requires the confirmation signal before lifting a candidate into the accepted space:

rule accepted_order_item(order, item) :-
candidate.requested_item(order, item, _),
customer_confirmed(order).

And a bounds invariant ensures that absurd accepted quantities are a model-theoretic impossibility:

invariant accepted_quantity_in_bounds(order) :-
accepted_quantity(order, q),
q >= 1,
q <= 200.

If someone tries to derive an accepted fact directly from parse.* atoms without routing through candidate.*, the platform rejects the program at load time. The relay boundary is enforced mechanically — not by convention.

The Drive-Thru example is one kind of fallible sensor. The same pattern fits:

  • Medical intake — an LLM extracts conditions and medications; a clinician must approve before any accepted fact enters the patient’s chart. See examples/jacqos-medical-intake/.
  • Invoice OCR — an OCR engine extracts line items; a human reviewer must confirm before they post to the ledger.
  • Image classification — a vision model tags an object; a bounds check and human review must agree before the tag enters downstream analytics.
  • Compliance scraping — an LLM summarises a regulation; a lawyer must approve before the summary drives any automated filing.

Any time you have a noisy input and a downstream action, the fallible sensor pattern fits. To start building, pick up Build Your First App and scaffold with jacqos scaffold --pattern sensor.

Both of these examples are fallible-sensor pipelines you can run end- to-end:

  • Drive-Thru Ordering — voice parser staged behind a customer-confirmation promotion rule. The 18,000-waters scenario, fixed.
  • Smart Farm — soil and weather enrichment from multiple sensor agents that converge into a shared derived model before any irrigation intent fires.

For the underlying mechanics — candidate.* namespaces, promotion rules, and the relay boundary the loader enforces — see Using Fallible Sensors Safely, which covers the product pattern, the mapper contract, and the acceptance-rule reference in one place.