How it works
Write the rules. Check every situation. Run it on a chip.
quorum is a reasoning engine and toolchain for the decisions small machines make on their own: stop the pump or let it run, stop the transfer, call a person.
Rulebooks
Rules in plain words
A rulebook (a .cb file) lists what the instruments report, the conclusions those readings support,
and the actions to take. It also lists what can knock a conclusion down:
- Excuses: a benign explanation, such as "the pump changed duty point", that defeats a fault diagnosis
- Doubts: a reason to distrust an instrument, such as "calibration past due"
- Severity: a more dangerous conclusion wins over a milder one
- Voting: "2 out of 3 level switches", written exactly like that
- Time: "pressure high within 10 s after a start", "tripped 3 times in an hour" (more)
Every rule carries the document it came from, so an auditor can follow each one back to the page.
The rulebook languageobserve lt_a_high "transmitter A above setpoint" observe lt_b_high "transmitter B above setpoint" observe lt_c_high "transmitter C above setpoint" claim level_high "two of three above setpoint" vote 2 of lt_a_high lt_b_high lt_c_high action hold idle action trip safe when level_high
The full check
Every situation, before the machine sees it
The compiler, cbc, solves every combination of readings. Not a sample: with 14 readings that is
all 16,384 situations, and with 30 it is about a billion, which takes about two minutes on an 8-core PC.
A rulebook is refused, with the exact situation that breaks it, if any situation gives:
- two actions at once, or no action at all
- an answer that is not reached in time
- an action that can never be chosen
- a conclusion that nothing could ever argue against
| Outcome | What the plant should do |
|---|---|
| Decided | Exactly one action: do it |
| Indeterminate | The evidence is genuinely ambiguous: call a person |
| Unsatisfied | The rulebook rejects every action: fix the rulebook |
| Conflicted | Two actions at once: a rulebook defect |
| Faulted | The computation did not finish: fall back to the hardwired interlock |
Time
Rules that know what happened, and when
Many safety rules are about order and timing. Pressure that rises right after a start means a closed valve; the same pressure an hour later means a blockage. A relay that trips three times in an hour means the motor must not be restarted. A rulebook says this in plain words:
- After: "pressure high within 10 s after the motor started"
- Recently: "the bearing was hot at any time in the last 5 minutes"
- How often: "the overload relay tripped 3 times in an hour"
- Built on each other: "the bearing ran hot twice in an hour"
- For how long: "hot for 5 seconds" before a threshold counts
Each timed reading is one more yes-or-no reading, so the full check still covers every combination. The board works out when to set it, and the bench test plays a script through time on the board's test clock, so an hour-long window is checked in seconds.
The guide to timed readingsreading dead_head "pressure high right after a start" when pressure_high within 10s after motor_started reading repeated_trips "tripped 3 times in an hour" when overload_trip 3 times in 1h claim closed_valve "pumping against a closed valve" needs dead_head claim lockout "do not restart" needs repeated_trips action stop safe when any closed_valve lockout
On the chip
Small, bounded, and the same every time
A rulebook that passes becomes a small image file. One firmware, loaded once, runs any image, and re-checks the image's fingerprint at start-up; a damaged, re-ordered or tampered image is refused on the board.
- About 2.1 KB of flash and 2.7 KB of RAM for the engine with two real rulebooks
- No heap, no recursion, no floating point
- A proven bound on the number of steps; the Pico 2 took exactly the 140 steps the compiler predicted
- The worst full cycle measured: 145 µs on a Pico 2, 284 µs on an Uno R4, against a 1 ms budget
| Board | Core | Status |
|---|---|---|
| Raspberry Pi Pico 2 | Cortex-M33 | Supported, measured |
| Arduino Uno R4 Minima | Cortex-M4 | Supported, measured |
| Pico 1, Nano 33 BLE, Due, Giga R1 | M0+, M4, M3, M7 | Compiles, not measured |
| Raspberry Pi 4 / 5 | AArch64 Linux | Every test suite runs |
| Arduino Uno / Nano | AVR 8-bit | Not supported |
Explanations
Every answer comes with its reasons
Every conclusion is a path through declared rules, so "why did you stop the pump?" has a finite, printable answer. It names the readings it relied on, and the readings it only assumed were absent.
That second list is the one that matters when a sensor is dead. A dead sensor and a quiet one look the same, and quorum says which answers depended on the silence.
dx_bearing_wear holds -- everything that disputes it is defeated:
obs_bearing_temp_high holds
def_calib_stale defeated
obs_calib_overdue defeated -- ASSUMED ABSENT: no sensor asserted it.
This is absence of evidence, not evidence of absence;
a dead sensor reads the same way.
14 steps, depth 5, of which 2 rest on ASSUMED ABSENCE of a readingStarting points
From the paperwork the plant already has
Few plants want to write rules from a blank page. quorum starts from what exists:
- HAZOP, LOPA and FMEA worksheets, imported into a draft rulebook. The importer reports deviations with no protection, and safeguards that name the same instrument as the cause.
- Past records: a first rulebook learned from labelled history, for an engineer to review
- An equipment manual, drafted by an AI model, quoting the manual word for word
Every draft is marked as a draft, with TODOs where the paperwork is silent, and the full check still applies.
