The Lab · 01 · MIT licensed
loop.js
The rules I argue for in design reviews, written as functions so they can be tested instead of debated. Four decisions that sit between a model and a person: which verb a score becomes, whether its confidence has earned belief, when to say nothing at all, and what to show while it thinks.
A design opinion becomes falsifiable the day it compiles.
Use it
No install step, because there is nothing to install
One file, no dependencies, standard ES module. Copy it into a project or import it from here.
// A score never reaches a screen naked — it becomes a verb first.
import { decide, calibrate, abstain, disclose } from './loop.js';
const d = decide(0.92, {
reversible: true,
signals: [{ name: 'margin', value: '12%' }, { name: 'stale data' }],
});
d.verb // → 'act'
d.requires.override // → true (an act you cannot undo is not an act)
d.explain // → 'Confidence 0.92 at or above 0.85: safe to act, with an override.'
The four decisions
Every control below runs the real module
Nothing here is mocked. The page imports loop.js and calls it as you move the inputs, so what you read is what the function returns.
decide(score, options)
Pattern · Act / Review / Ignore + Reversibility
A naked number ends in a shrug: an unexplained 87% reports the model's mood and hands the user all of the work. So the score resolves to exactly one verb before it reaches the screen, and each verb carries an obligation — an act must be overridable, a review must show its reasons, an ignore must admit it is below the line. Reversibility outranks confidence: if the action cannot be undone, the model does not get to auto-act at any score.
calibrate(history, options)
Pattern · Calibration & Track Record
"80% sure" only means something if, across every time it said 80%, it was right about 80% of the time. This computes the standard measures of that — Brier score, expected calibration error and the worst single bin — from a history of confidence/outcome pairs. Below thirty observations it refuses to render a verdict at all, because a track record you cannot support is just another confident claim.
abstain(evidence, policy)
Pattern · Provenance, Citations & Honest Refusal
In a capital-allocation workflow one fluent, wrong claim costs more than a hundred refusals, so the default policy fails toward humility: every claim carries a source, two sources corroborate, and a clear majority agree before the system speaks. When it refuses, it returns what is missing — so the interface can name the gap instead of shrugging.
disclose(elapsedMs, options)
Pattern · Latency as a trust surface
Latency stops being a performance problem and becomes a trust problem at the moment silence becomes indistinguishable from failure. Under about 200ms a spinner reads as a flicker, so show nothing. Inside the budget, show the shape of the answer. Past the budget the user is owed a sentence, not another rotation.
Decisions, on the record
What overrides what
The module encodes a precedence — when two rules disagree, one is written to win. That order is the design; the functions just enforce it.
decide() demotes act to review.calibrate() refuses a verdict — a track record you cannot support is just another confident claim.abstain() defaults fail toward refusal: every claim carries a source, two corroborate, a clear majority agree — or the system says what is missing instead of speaking.disclose() owes the user words, not a spinner — silence indistinguishable from failure is a trust cost, not a performance one.Key decisions, and what each one buys
decide() reports review-without-reasons as a violation. A principle has no failing state; a function does.Proof
The tests, running now, in your browser
The same file runs under Node with node lab/loop.test.js and exits non-zero on failure. There is no test framework, because adding a dependency to a zero-dependency library would be the joke telling itself — the runner is about forty lines at the top of the file.
Two kinds of proof, kept separate on purpose. Implementation proof is below: the assertions, in this tab and under Node in CI on every push. Product proof lives elsewhere — each of these patterns ran in a production product, documented in the pattern library and the case studies. Passing tests prove the rules hold; the case studies argue the rules are worth holding.
- Enable JavaScript to run the suite, or read the assertions in the source.
Why this exists
“It’s a hundred lines of if-statements.” Exactly — and that is the entire point. The same decisions usually live in a slide deck where they can only be believed or ignored. Here they are versioned, testable, and able to fail loudly. The day a threshold is wrong, a test goes red — a deck has never once done that.
A rule you can run beats a rule you can argue with
Every one of these decisions used to be a slide. "Don't show a naked confidence score." "Fail toward humility." "Reversibility outranks confidence." Reasonable people nodded, and then shipped the naked number anyway, because a principle has no failing state.
Written as functions, the same rules get a failing state. decide() reports review-without-reasons as a violation rather than a suggestion. calibrate() returns insufficient-data instead of a flattering number. That is the whole argument for this file existing: human-in-the-loop design is a set of decisions, and decisions can be tested.
The eight patterns these come from are documented, with live demos, in the pattern library — and each one is running in a production product. This is their logic, extracted and made checkable.