2026-09-22 · 34 min · explainer · architecture · agents · llm · systems
Three days ago I wrote that the middle tier is not in the middle: a decision model is not a cheap reasoning model, it is a different kind of component, and the interesting engineering is the deterministic code that feeds it. That was an argument from three codebases. Five more landed this week, four of them younger than the argument, and together they are a better instrument than the argument was, because their authors did not read each other.
- SystemOneHarness — the explicit one. "Observes an environment, compiles its finite action space into typed questions, gates each decision by confidence, executes the chosen action, and records the complete trace. One model call per step. No generated actions. A probability on every transition."
- webctl — a web-search CLI. Its author ran out of Codex and Claude tokens, audited where the context went, and found that reading academic papers was the bill.
- fastbrowse — a browser agent. "Jev picks each action from what is on the page, an LLM reads and plans, and every claim in an answer cites a quote from the page."
- djev — DiffusionGemma serving the Jev decision API, split out of a vLLM pull request so the PR would not hold it up.
- cadence — which, as I will get to, is not one of these at all, and is more interesting for it.
I cloned all five and read the code. Every one of them has to build four things: an enumerator that turns a situation into a finite option list, a question format, a confidence gate, and a composer for the strings the model cannot emit. Four decisions, five answers each. Where they agree is the shape of the pattern. Where they disagree is where the design is still open.
enumerator
what turns a situation into a finite option list?
SystemOneHarness
declared, or compiled
YAML action space, MCP tool schemas, Browser Use's DOM index, a 4-bit canvas quantiser
webctl
search + LSH
3 engines fused by reciprocal rank, MinHash over 3-word shingles, a 2,000-char chunker
fastbrowse
page capture, then Jev
capture.js indexes ≤320 controls; past 160 a noul per control shortlists them
djev
none — it is the server
the caller enumerates; for a span the enumerator is the text's own token ids
cadence
a callback
actions(S) → Sequence[A], supplied by the application
→ four of five contain no model at all
question format
what shape does one question take, and how many options fit?
SystemOneHarness
choice / noul / score
≤255 options; over that, truncate and record the drop in the trace
webctl
choice / noul / score
score over a 4-level rubric by default; a custom rubric is a CLI flag
fastbrowse
choice / noul / score
≤240 for headroom; over that, groups of 30 and a second call
djev
choice / noul / score / span
≤26 options — the labels are A…Z, one token each
cadence
a float
evaluate(S) → float; no language model anywhere
→ everyone took TypeSafe's three primitives; the ceilings differ by 10x
confidence gate
what happens when the answer is not confident enough?
SystemOneHarness
refuse
per-risk thresholds on the weakest judgment; 3 refusals in a row ends the run with a handoff packet
webctl
drop
one cut derived from the rubric size; the result never reaches the agent's context
fastbrowse
hand it to the LLM
under 0.55 the step goes to LLM recovery; 11 named thresholds, each with a calibration note
djev
look again
first-read entropy over 0.1 buys three more noise draws, averaged, with a standard error
cadence
spend more nodes
the budget is visited nodes per tick; no probability is involved
→ five different answers — this is where the projects actually disagree
composer
who writes the strings a decision model cannot emit?
SystemOneHarness
nobody — the operator
a parameter needing free text fails to compile; text is supplied by name before the run
webctl
nobody by default
--summarize adds a small general model over the kept chunks
fastbrowse
an LLM, then code
the reader cites block ids; code slices the quote and builds the #:~:text= link
djev
the model, bounded
a span returns character offsets, so the string is a substring by construction
cadence
n/a
the output is an action from the supplied set and a predicted cost
→ the only axis where a second model is the common answer
First, which of these are real
Four of the five did not exist a week ago, so the honest thing is to say which ones I would deploy and which are sketches. This matters for reading the rest: a convention three repositories share means something different if two of them are two commits deep.
| first commit | code | tests | CI | published measurement | |
|---|---|---|---|---|---|
| fastbrowse | 2026-09-17 | 9,269 lines Python | 401 | yes | 14 tasks × 3 passes against Browser Use, per-run data committed |
| webctl | 2026-09-20 | 19,600 lines Go | 226 | yes | 30 questions × 7 arms, blind judge, cells.jsonl per cell |
| SystemOneHarness | 2026-09-19 | 4,485 lines Python | 50 | no | 15 live runs on its own toy environment |
| djev | 2026-09-21 | 1,526 lines Python | 0 | no | one table on an unmerged branch |
| cadence | 2026-09-07 | 254 commits, NumPy | 503 | yes | not a decision-model project |
fastbrowse and webctl are real systems. Both ship binaries, both have CI, both publish per-run evidence rather than a headline. fastbrowse is on PyPI and its README labels itself pre-alpha, which given 401 tests and 189 commits in six days is the right amount of modesty. webctl is in Homebrew and apt.
- license
- MIT
- branch
- main
- tests
- 56 files
- source
- 1.5 MB
- commit date
- 2026-09-22
by size of tracked source at this commit, file counts in brackets; docs, data and vendored trees excluded
local clone, 2026-09-22 at 3588580 — branch, commit, commitDate, fileCount, hasTests, languages, license, licenseFile, testFileCount
- license
- MIT
- branch
- main
- tests
- 29 files
- source
- 626.8 kB
- commit date
- 2026-09-22
by size of tracked source at this commit, file counts in brackets; docs, data and vendored trees excluded
local clone, 2026-09-22 at e9bc54a — branch, commit, commitDate, fileCount, hasTests, languages, license, licenseFile, testFileCount
SystemOneHarness is a real design with a thin benchmark. The code is
careful — the loop, the compiler, the gate and the encoder are each one tight
file, and docs/design.md is 551 lines of measured reasoning rather than
marketing. But pytest runs against recorded model answers, there is no CI at
all, and the benchmark is five runs each of three scenarios against an
order-fulfilment state machine the project wrote itself. It proves the loop
executes. It does not prove the loop is good. The README says exactly that:
"The benchmark proves the controller, compiler, gate, and model can complete
these small deterministic tasks. It does not claim the same result for ambiguous
state, arithmetic, dates, or long irrelevant context."
- license
- Apache-2.0
- branch
- main
- tests
- 10 files
- source
- 247.4 kB
- commit date
- 2026-09-21
by size of tracked source at this commit, file counts in brackets; docs, data and vendored trees excluded
local clone, 2026-09-22 at ab8e8f0 — branch, commit, commitDate, fileCount, hasTests, languages, license, licenseFile, testFileCount
djev is a sketch, and a good one. Two commits, one file, no unit tests. What it has instead is a live battery you point at a running server, and the thing it demonstrates is worth more than tests would be.
- license
- Apache-2.0
- branch
- main
- tests
- none found
- source
- 55.8 kB
- commit date
- 2026-09-21
by size of tracked source at this commit, file counts in brackets; docs, data and vendored trees excluded
local clone, 2026-09-22 at 442eab6 — branch, commit, commitDate, fileCount, hasTests, languages, license, licenseFile
cadence is not a decision-model harness. I looked for one: there is no
jev, no typesafe, no systemone, no language model anywhere in 254 commits.
It is a NumPy research library about equilibrium world models, with a bundled
Lean development of 169 theorems. Somebody put it on a list of decision-model
projects and it does not belong there. It stays in this piece for one reason,
which I will come back to at the end.
- license
- MIT
- branch
- main
- tests
- 79 files
- source
- 1.2 MB
- commit date
- 2026-09-22
by size of tracked source at this commit, file counts in brackets; docs, data and vendored trees excluded
The repository's own language breakdown and topics are the quickest check on the claim: this is a NumPy research library, not a harness.
local clone, 2026-09-22 at ffe0ba0 — branch, commit, commitDate, fileCount, hasTests, languages, license, licenseFile, testFileCount
The enumerator, and why it is almost never a model
A decision model picks from a list. Something has to build the list, and that something is the part nobody advertises.
On jev-linkmap I said the best thing in the project was its candidate generator: TF-IDF cosine with headings weighted 4x, a chrome filter dropping any page half the site already links to, anchors restricted to text the author actually wrote. No model touches any of it, and it is what makes the model's job small enough to be a yes/no question. Five more repositories later, that generalises harder than I expected.
webctl's enumerator is three search engines and a hash function. Up to three providers are queried, their rankings fused by reciprocal rank, exact duplicates collapsed on a normalised URL. Then near-duplicates are proposed by MinHash LSH over three-word shingles — 64 hashes in 16 bands of 4, with the band arithmetic documented in a comment that tells you what collision probability that buys:
// internal/dedupe/dedupe.go
// 64 hashes in 16 bands of 4 rows makes a pair with Jaccard similarity 0.5
// land in a shared bucket with probability ~0.65 and one at 0.8 with ~0.99,
// while pairs at 0.2 collide about 1% of the time.Jev never proposes a pair. It is handed the few pairs that collided and asked, one noul each, whether they are the same document. The page chunker is the same shape: fixed 2,000-character chunks with 20% overlap shown as context, and Jev answers one yes/no per chunk. Every option Jev ever sees was produced by code with no model in it.
fastbrowse's enumerator is a page capture, capture.js and snapshot.js
running in the tab, indexing interactive controls with role and name. That is
also model-free — until three days ago, when it stopped being. Commit 3588580
says why:
A page denser than 160 controls was cut in document order, so a result or filter drawn after a long header and sidebar never reached Jev's choice. The browser now indexes up to 320 controls (120 offscreen), and past 160 the policy asks Jev one Noul per control, batched under the token budget and sent together, then offers the highest-scoring controls that fit.
This is the one place in five repositories where a model entered the enumerator, and the reason is worth reading twice: a DOM-order cut dropped the control a task needed. The fix was not a better heuristic, it was a shortlisting pass. And note the primitive chosen — one noul per control, not one choice over all 240. The commit explains: "its two-decimal probabilities leave all but a handful of 240 options tied at zero." A choice over 240 options quantises to nothing useful. A yes/no per option does not.
SystemOneHarness's enumerator is a compiler, and the striking thing about it is what it refuses. Every parameter must be one of four declared kinds, and a fifth — free text — fails at load time:
# systemone_harness/actions.py
if p.kind == "free":
raise ActionSpaceError(
f"parameter {name!r} would need free text: declare `choices`, `from`, "
"`flag` or `levels`. A System One model chooses; it does not write.")That single raise is the most consequential line in the repository, and I will
come back to it, because it is what buys the headline.
djev has no enumerator at all, because it is a server. The caller enumerates.
Its one exception is the most interesting enumerator in the set, and it is also
model-free: for a span question the option list is the token ids of the state's
own text, so the answer is a substring by construction.
So: four of five put no model in the enumerator, and the fifth added one only after a truncation lost the answer. That is as close to consensus as five independent codebases get.
The enumerator is also where it fails, measured
SystemOneHarness ships something I have not seen anyone else publish: a probe that measures its own enumerator failing.
The browser environment reads a page's DOM. A page drawn on a <canvas> has no
DOM to read, so the project wrote a quantiser — draw the canvas onto an
offscreen canvas of cols × rows, read the pixels back, quantise each to 4
bits a channel, keep the K commonest colours, one character per cell. Then it
asked Jev six questions about the resulting ASCII, over 30 frames of Super Mario
Bros., at four resolutions, each plain and with the operator's colour names
attached. Eight encodings, 48 measured cells, and the raw rows are in
docs/reports/canvas-probe-2026-09-19.json.
Read the base row first. The frames were lopsided — 0 enemies in 30 frames, 1
gap, 29 of 30 with "run right" as the correct control — so always answering the
commonest label scores 0.97 to 1.00 on every question. That is the bar.
Thirteen of 48 cells clear it, and none of them is in the column that matters.
next_control is the only question the loop actually needs: which key do I hold
now. Its best score across every encoding is 0.50 at 96x48 named, against a
baseline of 0.97. Quadrupling the resolution from 32x16 to 128x64 more than
triples the token cost, from 1,038 to 3,442, and moves next_control from 0.03
to 0.27 — still a quarter of what you get by not looking. Attaching colour names
helps on some questions and destroys others: on_ground is 1.00 plain and 0.00
named at the same resolution.
None of that is a fact about Jev. It is a fact about the encoding. The design doc says so in plain words: "a page drawn on a canvas gives a DOM observation nothing but its DOM… Driving such a page well needs the page's own state as text, which is a page-side adapter."
Which is exactly what the project's headline demo is:
The demo is real and the loop in it is real. But the thing making it work is the sentence generator on the page side, not the model and not the general browser environment. Which is the finding: the enumerator is the product. When it is good, a 1.13-generation decision model plays a platformer in real time. When it is a pixel quantiser, the same model cannot beat a constant.
The question format: everyone took the same three, then disagreed about the ceiling
All four decision-model projects use TypeSafe's primitives, because TypeSafe's
wire defines them: choice (pick one of N named options, get a probability per
option and a confidence), noul (yes or no, get a probability), score (a level
on an ordered scale). There is no fourth, and there is no multi-select — a set is
composed from one noul per member.
The agreement stops at the ceiling.
| options per choice | levels per score | over the ceiling | |
|---|---|---|---|
| SystemOneHarness | 255 | unbounded | truncate to 255, record the drop in the trace |
| fastbrowse | 240 | 10 | groups of 30, then a second call inside the chosen group |
| webctl | n/a (one item per question) | 4 by default | pack questions by token budget, halve a rejected batch |
| djev | 26 | 26 | split the answer template into chunk rows |
djev's 26 is the one that tells you something. Its labels are A through Z,
one token each, because the answer is read out of a single canvas position:
# structured_server.py
elif kind == "choice":
opts = q.get("options") or []
labels = [chr(ord("A") + i) for i in range(len(choices))]
...
if len(choices) > 26:
raise SchemaError(f"question {qid!r}: at most 26 alternatives")Jev's 255 is a serving limit somebody chose. djev's 26 is the alphabet. The option names never enter the readout at all; they appear in the system prompt, mapped to letters, and the model's answer is a distribution over those letters.
The other ceiling difference is which end you protect. SystemOneHarness truncates the option list and writes the truncation into the trace, which is honest and lossy. fastbrowse refuses to lose an option: past 240 it asks a choice over groups of 30, then a second choice inside the winner, and multiplies the two confidences, "because the element was only ever chosen from inside the group, so a doubtful group is a doubtful target." That is a correct treatment and it costs a round trip.
One quieter disagreement, and I think it is the deepest one in this section:
webctl scores one result per request by default. Batching every result into
one call is --batch, opt-in. Look at the state each mode sends and the reason
is obvious. In per-result mode the state is {query, goal, result} — the model
cannot be influenced by result 7, because result 7 is not in the request. In
batch mode every result is in the state and the prompt says "judge only that one
result", which is an instruction, not a guarantee.
SystemOneHarness batches everything into one call and is right to: its questions all read one observation, so sharing the state is not contamination, it is the point. webctl's questions are about different documents, and sharing the state is a leak. Whether to batch is not a performance decision. It is a question about whether your questions share a subject.
The confidence gate: five answers, no two alike
Every project reads the same probability and then does something completely different with it.
Nineteen named thresholds across three codebases, chosen independently for unrelated jobs, and almost all of them land on 0.30, 0.50, 0.70 or 0.90. That is either convergence or round numbers, and I think it is round numbers. The one project that derives its threshold rather than picking it is webctl:
// internal/jev/qualify.go
// DefaultCut is the default keep threshold for a rubric with levels
// labels: 1.2 levels below the top, on the 0–ScaleMax scale. For the
// built-in four-level rubric that is 6.0 ("useful" or better).
func DefaultCut(levels int) float64 {
top := float64(levels - 1)
return (top - 1.2) / top * ScaleMax
}That is a rule, not a constant: change the rubric's size and the cut moves with it. It is the only threshold in the five that survives a rubric rewrite.
What happens below the threshold is where the disagreement is total.
SystemOneHarness refuses. The gate compares the weakest judgment the action
depends on — not the product; "the question is 'is there one shaky judgment'" —
against a threshold picked by the action's risk class. Below it, nothing runs,
the step is recorded as refused, and three refusals in a row end the run. The run
record then carries a handoff: the state it refused on, the questions, its
answers with their probabilities, the weakest judgment and the threshold it
missed. That is the best artefact in the repository. It turns "the model was
unsure" into a packet a reasoning model or a person can pick up.
webctl drops. A result under the cut never reaches the agent's context, and
that is the whole product. Note what it does not gate on: Jev returns a
confidence alongside every score, webctl prints it under --verbose, and no
code path anywhere reads it for a decision. The keep rule is q.Value() >= minScore
on the score itself. For a filter that is defensible — a confidently mediocre page
and a doubtfully mediocre page are both pages you did not want — but it means the
calibrated quantity the model is famous for is decoration here.
fastbrowse escalates. Under recover_below, 0.55, the step does not stop and
does not run: it goes to LLM recovery, which looks at the page and directs the
next action itself. The gate is a routing decision between two models, not a stop.
And its thresholds are the best-documented numbers in any of these repositories,
because each carries the run that set it:
# src/fastbrowse/config.py
done_accept_from: float = 0.85
"""With every action requirement confirmed, the holistic done answer that accepts
without the LLM verifier. Jev scores a page that is right 0.49 to 0.91 as done and
a near miss (the organisation instead of the repository, search results, a related
article) 0.04 or less, so doubt between the two is not a near miss."""That docstring is how a threshold should be justified: here is the distribution of the right answers, here is the distribution of the near misses, here is the gap I put the number in.
djev resamples. Its gate is not a probability at all. It takes one read,
computes the entropy at each answer slot, and if the largest exceeds
auto_threshold — 0.1 nats, by default — it takes three more reads with different
noise seeds and averages them. Clearing the gate buys another look, not
permission to act. It is also the only one of the five that reports uncertainty
about its uncertainty: with more than one read it returns a stderr over the
top probability and an agreement fraction across draws.
cadence's gate is a node budget. No probability is involved anywhere.
Are they tunable without a re-run?
This is the question jev-linkmap made me care about, where a threshold sweep bought 19 of 20 recall points while a $15.51 rubric rewrite bought the rest. Sweeping is free if you kept the scores.
- webctl keeps them. Its eval report carries
Judged []KeptResult— "every result with Jev's relevance value, best first, including the ones the threshold dropped." A sweep over a saved run is arithmetic on a file you already have. No command does it, which is a five-line addition. - SystemOneHarness keeps them. Every step in the trace holds the full
answersmap with distributions intact;trace.pyis explicitly forbidden from "summarising away the distribution". Replaying a trace at a different gate is offline work. - fastbrowse keeps them per step in
--json, and its config docstring says the defaults are "starting points to be recalibrated from recorded decision packets" — the intent is stated, the sweep is not shipped. - djev has no concept of a stored decision; it is a server.
So all three that record decisions kept the data, and none of them ships the sweep. If I were contributing one thing to any of these repositories, it would be that.
There is one place where SystemOneHarness and jev-linkmap flatly disagree, and I
think SystemOneHarness is right. Its §7.3 records a failure where the order-desk
observation said "packed" and named the carrier but never said "not shipped";
the model put a tenth of its belief on finish and its probability on ship
sat at 0.73 to 0.84 against a 0.8 gate. Two runs in five ended
no_confident_action. The fix was adding "Shipped: no. Cancelled: no." to the
observation — five in five, with no change to the gate. The rule they drew:
The gate is never tuned to fit the environment; the environment is made legible to the model.
Sweep the threshold to find out what your data supports. Do not sweep it to paper over a state the model cannot read. Those are different acts and they look identical in a diff.
One model call per step: it holds, and here is the bill
SystemOneHarness's headline is "one model call per step." I checked it against
controller.py and it is exactly true. One self.provider.decide(enc.state, compiled.questions) per iteration, no other model anywhere in the loop, and the
compiler packs the whole step into that one request: next_action over every
feasible action plus finish and escalate, goal_reached as a second reading,
every declared guard, and one question per parameter of every feasible action —
not just the chosen one, because you do not know which one is chosen yet.
Compare WindTunnel, which needed two: Jev picks the tool, Mercury 2.5 writes the arguments, at a measured 0.93 Mercury calls per step. A decision model cannot emit a string, so somebody has to.
So what did SystemOneHarness give up? Not arguments — it fires actions with
parameters all day. It gave up strings. Param.from_dict refuses at load time
anything that is not a fixed choices map, a from reference to a candidate
list the environment enumerates, a boolean flag, or ordered levels. There is
no fifth kind, on purpose. So the text a form needs does not get written at run
time; it is supplied by the operator, by name, before the run starts:
s1 run --browser --headless --start-url https://example.com/book \
--text name=Customer --text email=user@example.com \
--goal "Book a table at 19:30 with a window seat."The model chooses type_text(field=…, value="name"). It never sees the string
"Customer" as something to produce; name is an option in a list. WindTunnel
pays a second model call at run time. SystemOneHarness pays a human at
configuration time. That is the trade, stated plainly, and it is a real trade
rather than a free lunch: it means the loop cannot handle a task whose text is
not known in advance, and a search query, a message body or a file's contents are
exactly that.
The project knows. docs/design.md lists it as open decision #2 — "parameter
questions for every action every step, or two requests" — and recommends one
request with a switch. The switch is even half-built:
# systemone_harness/actions.py
two_request: bool = FalseIt is parsed from YAML, serialised back out in to_dict, and read by nothing.
Four references in the repository, all of them plumbing. The escape hatch from
the headline is in the data model and not in the loop, which is either discipline
or a TODO depending on how charitable you feel. I read it as discipline: the
property is load-bearing enough that they would rather not have a code path that
quietly gives it up.
The other cost is tokens, not calls, and the benchmark shows it. Every parameter
question of every feasible action rides every request, and the answers for the
five actions you did not pick are thrown away. ship_cheapest spends 6,304 input
tokens over 6 steps — about 1,050 per step — for an action space of six actions
with at most one parameter each. This is the same shape as the
json-render finding, where an eight-element
dashboard was two calls carrying the entire component catalog in both. The cost of
one-call-per-step is that every call carries the whole space.
At Jev's $0.042 per million input tokens that is $0.000265 a run, so nobody cares yet. At 200 actions with four parameters each, somebody will.
fastbrowse: the composer done by code
fastbrowse is the one that takes the composer problem seriously, and its answer is the best in the set: the LLM names the evidence, code produces the string.

The reader is an LLM. It reads a page capture whose blocks carry ids, and it cites block ids. It does not retype the page. Code takes the id, slices the capture at that block's character offsets, and that slice is the quote:
# src/fastbrowse/retrieval.py
quote=capture.text[start:end],Then the citation link is derived, also without asking a model, as a
#:~:text= scroll-to-text fragment built from the quote's first and last five
words. src/fastbrowse/citations.py is 21 lines and its docstring is the design:
"Links derived from captured quotes, without asking a model for a URL."
The last frame of that clip is the architecture. The number $43.18 arrives with
"Total: $43.18" attached, and the quote was copied, not recalled. Whether the
model hallucinated is not a question you have to ask, because the model was never
holding the string.
Its published numbers, same 14 tasks three times each on 2026-09-22 against the Browser Use agent on the same class of cloud browser: 41/42 against 39/42, at $0.0041 median per task against $0.3668 — $0.36 for the whole suite against $26.01. It is also honest about the shape of that win: "Jev selects actions through classification; planning, field text and reading can still require LLM generation," and "the Browser Use agent is faster on four of the seven lookups." The cost ratio is real; the time ratio is only real on the long tasks.
djev: a diffusion model as a decision model
I argued in any model can be Jev that the System One readout is a serving feature, not a model feature — you need an endpoint that scores caller-supplied candidates in one forward pass, and any checkpoint with logprobs can be made to do it. djev is a direct test, because it is the same server wrapping two completely different models.
The diffusion mechanism first. DiffusionGemma denoises a whole canvas per forward pass. The vLLM patch adds four request fields that let you control that canvas: seed it with specific token ids, pin positions so denoising cannot rewrite them, cap the step count, and return temperature-1 logprobs at every position without decoding. The server then does this:
- Build the answer template as text —
urgent: yes\nbucket: A\ntone: 2— and tokenize it. - Find each question's slot: the one token position that changes when that question's label changes. If a label is not a single token, or two labels of one question move different positions, the schema is rejected outright.
- Seed the canvas with the template, replace each slot with random noise, pin everything else.
- One denoise step. Read the logprobs at each slot, restricted to that question's label ids. Softmax over those. That is the distribution.
The readout is the pinning, and it is beautiful: the answer's fixed text is
held, so the only thing the model is allowed to change is the labels, and the
only thing you read is where they were. diffusion_read_only returns the
logprobs and ends the request. No text is generated at any point.
Two branches take it further, and both matter.
span-answer-type adds a question whose answer is a piece of the state's text,
returned as character offsets. The canvas gets a label and a blank, the blank is
left free, four denoise steps run, and the logprobs of the text's own token ids
are read at every blank position. The decode walks the text with those tokens, so
the answer is a substring by construction and cannot be a value the text does not
contain. That is the same guarantee fastbrowse gets by slicing a capture, except
it is enforced inside the read instead of after it. If it holds up, it collapses
the second model call for the entire extraction case — which is most of what
WindTunnel's 0.93 Mercury calls are buying.
ar-engine is the one that settles the argument. It runs the identical schemas on
an ordinary autoregressive model with no diffusion machinery: one restricted
next-token read per question, in order, earlier answers prefilled behind a cached
prefix. And it publishes a measured comparison:
GLM 5.3, --engine ar | DiffusionGemma, diffusion | |
|---|---|---|
| 22 label questions (noul, choice, score) | 21 | 20 |
| 49 span fields, 11 texts | 48 | 49 |
| 5 list questions | 5 | 5 |
| a 1,096-character text in 4 windows, 4 fields | 3, in 89 s | 4, in 4 s |
| reads per label question | 1, about 0.27 s | one joint read for the schema, about 0.1 s |
The claim survives, and the table says exactly what the diffusion model is worth. On accuracy it is a wash — 21 against 20 on labels, 48 against 49 on spans, which is one item either way on small batteries. The difference is amortisation: the autoregressive engine pays one read per question, the diffusion engine pays one read per schema. Ask one question and they are equivalent. Ask forty and the canvas answers them in a single pass. That is the whole advantage, and the README states it without decoration: "The diffusion engine's edge is cost… The AR engine's edge is that it needs nothing beyond stock vLLM."
The gate changes too, and this is the part that made me stop and reread. On the
AR engine, "reads are deterministic, so samples collapses to one" — no noise
seed, nothing to resample, auto_threshold is meaningless. djev's confidence gate
only exists because the diffusion read is stochastic. The uncertainty machinery is
a property of the serving path, not of the decision.
Cadence, which does not belong here
There is no decision model in cadence. There is no model of any kind that speaks a wire protocol. It is a research library about bounded patches with local state, written in NumPy, with a Lean development beside it.
It is in this piece because when I went looking for its decision loop, out of diligence, I found this:
# src/cadence/circuits/deliberation.py
class Deliberator(Generic[S, A]):
def __init__(
self,
actions: Callable[[S], Sequence[A]],
transition: Callable[[S, A], S],
evaluate: Callable[[S], float],
terminal: Callable[[S], bool],An enumerator. A scoring function. A budget that stops the search. A transition that executes what was chosen. The same four parts, with no language model anywhere, in a codebase whose author has never used the word Jev.
That is not a coincidence and it is not profound. It is what a controller over a finite action space looks like, and it is what SystemOneHarness's own design doc says it is building: "the harness is a controller over a finite state machine: the environment is the transition function, the model is the policy, the action space is the alphabet." The decision model is a policy you can swap into a structure that predates it by fifty years. The interesting engineering is the other three callbacks, which is the same conclusion I reached from a completely different direction three days ago.
What I would build
If I were putting a decision model behind something tomorrow, from five codebases:
- Spend the effort on the enumerator. It is the only part whose quality is not bounded by a vendor. SystemOneHarness's own probe is the proof: the same model scores 0.03 or 0.50 on the same question depending on how the state was written, and neither number beats a constant. It is also gate 2 of the four-question procedure this corpus arrived at from the deployment side.
- Make the observation state its predicates, including the false ones. Adding "Shipped: no. Cancelled: no." moved two failures in five to zero, with no gate change. It is the cheapest fix in any of these repositories.
- Batch questions only when they share a subject. One observation, many questions: batch. Many documents, one question each: do not, or you have turned a guarantee into an instruction.
- Keep the distributions, then ship the sweep. All three loops record every probability. None of them lets you re-threshold a saved run. That is a free axis on data you already have.
- Decide where your strings come from before anything else. They come from the operator (SystemOneHarness), a second model (WindTunnel), an LLM naming spans that code copies (fastbrowse), or the model itself bounded to a substring (djev). The fourth is the newest and, if it survives contact, the best of them.
What would change my mind
6 claims above, and what would falsify each
SystemOneHarness really does make exactly one model call per step, and the price is that no parameter can be a string.
Read from
controller.pyandactions.pyatab8e8f0, not from a run — I have no OpenRouter key pointed at a decision model. The loop callsprovider.decideonce per iteration andParam.from_dictraises on a free-text parameter, so the claim is structural rather than empirical. It falls if someone wirestwo_requestto something, or if an environment slips a string in through a candidate list computed per step, which the compiler would happily allow:from: <list>accepts whatever the environment enumerates, including 255 generated strings. If a real deployment turns out to generate its candidate lists with an LLM, the second call moved upstream rather than disappearing, and "one model call per step" is bookkeeping.Four of five enumerators contain no model, and that is the pattern rather than an accident of small samples.
Five repositories in six days is a sample of one week, and the four that are model-free are the four whose candidate sets are cheap to compute: URLs, DOM controls, YAML, token ids. The counterexample is already in the set — fastbrowse put a noul pass into its enumerator the moment a page got dense. The cleanest test is a domain where candidate generation is genuinely hard: retrieval over a corpus with no link structure, or action selection in a space that must be constructed rather than observed. If projects there reach for an LLM enumerator as a matter of course, "the enumerator is model-free" is a fact about easy domains and I have generalised from them.
The canvas probe shows the encoding, not the model, is the ceiling — no encoding beats a constant on the question that matters.
Thirty frames, one game, one model version, and baselines of 0.97 to 1.00 because the frames were lopsided. That last part cuts both ways: a baseline that high is nearly unbeatable, so "does not beat the baseline" is a weak bar to have set. Rerun the probe on a balanced frame set — equal numbers of enemy-ahead, gap-ahead and clear frames, and a
next_controldistribution that is not 29 of 30 one answer — and the accuracies become informative rather than degenerate. Ifnext_controlthen lands above a 0.33 baseline at the higher resolutions, the encoding is workable and what I read as a null result is an artefact of the frames they happened to sample.djev's ar-engine table shows the readout is a serving feature: an ordinary model matches a diffusion model on accuracy and loses only on amortisation.
It is 22 label questions and 49 span fields, on one branch, unmerged, with no unit tests, run by the author. 21 against 20 is not a difference. The load-bearing part is the per-schema-versus-per-question read count, which is structural and does not need a big sample — but the accuracy wash does. Run both engines over a few hundred questions with calibration measured, not just top-1 accuracy, and the picture could change completely: a next-token read on a chat-tuned model has a well-known prior over
Aandyes, and if GLM 5.3's probabilities turn out badly calibrated while DiffusionGemma's are not, "any model can be Jev" holds for the argmax and fails for the number, which is the half that matters.webctl never gates on Jev's confidence, so the calibrated quantity is decoration in that pipeline.
Read from
rank()andbackfill()incmd/webctl/cli/search.goate9bc54a; the keep rule is a comparison on the score, and confidence reaches only--verboseand the JSON. If that is deliberate it is defensible, and the test is whether it costs anything: take a saved eval run, and re-rank withscore × confidenceor with a confidence floor. If quality moves, the field was load-bearing and ignoring it is a bug rather than a design. If it does not, confidence genuinely adds nothing to a relevance filter and every threshold in this article that reads a confidence is doing work that a score would do.fastbrowse and webctl are shippable and SystemOneHarness and djev are not yet.
I judged this on tests, CI, release channels and whether the published numbers come with per-run data — proxies, all of them, and none of them is running the thing in anger. SystemOneHarness in particular could be better than its benchmark suggests; a toy environment is a weak instrument, not evidence of weakness. Point all four at one task that all four could do — a form fill with an enumerable field set, say — and measure completion, cost and refusal rate. If SystemOneHarness matches fastbrowse there, "thin benchmark" was a statement about the benchmark and I have let it stand in for the code.
Media: every image and clip here is the project's own, transcoded or
rasterised and committed under /articles/system-one-harnesses/, with terms
and source revisions in
NOTICE.txt. fastbrowse publishes
an architecture diagram and a demo recording; SystemOneHarness publishes a
gameplay recording. webctl and djev publish neither — I read both trees at
e9bc54a and 442eab6 and found no diagram, screenshot or recording in
either, only text — so nothing of theirs is shown above, and the sections on
them rest on the code and the tables they do publish.