# Five harnesses for a model that cannot write

> Satyajit Ghana — Head of Engineering @ Inkers Technology
> canonical: https://ai.thesatyajit.com/articles/system-one-harnesses
> date: 2026-09-22
> tags: explainer, architecture, agents, llm, systems
Three days ago I wrote that [the middle tier is not in the middle](/articles/three-tiers):
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](https://github.com/HarnessRouter/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](https://github.com/dorkitude/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](https://github.com/agent-labs-dev/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](https://github.com/mmastrac/djev)** — DiffusionGemma serving the Jev
  decision API, split out of a vLLM pull request so the PR would not hold it up.
- **[cadence](https://github.com/muellerberndt/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.

<FourAxes />

## 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.

<RepoCard repo="agent-labs-dev/fastbrowse" />

<RepoCard repo="dorkitude/webctl" />

**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."*

<RepoCard repo="HarnessRouter/SystemOneHarness" />

**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.

<RepoCard repo="mmastrac/djev" />

**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.

<RepoCard repo="muellerberndt/cadence" note="The repository's own language breakdown and topics are the quickest check on the claim: this is a NumPy research library, not a harness." />

## 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](/articles/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:

```go
// 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:

```python
# 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`.

<CanvasProbe />

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:

<Video
  src="/articles/system-one-harnesses/s1-gameplay"
  poster="/articles/system-one-harnesses/s1-gameplay-poster.jpg"
  alt="A browser window playing Super Mario Bros., with a side panel logging one decision per step. The log fills with entries reading Jump Right, Jump Right, Run Right, Wait, each with a probability beside it. Mario runs right, jumps a pipe, collects coins, and the score climbs from 0 to 1,100 while the world stays 1-1. A status strip under the game reads LIVE, 11 FPS, with a step count, a seconds figure and a running cost. The panel's captions read: reads the game, not the screen; decides, never writes; plays a real browser."
  caption="Jev playing a live browser game through System One Harness — one typed decision per step, no generated control text. The project's own recording at ab8e8f0, transcoded; terms in /articles/system-one-harnesses/NOTICE.txt. Note the caption under the game: the environment states where Mario is and what is ahead in tiles, as plain sentences. That is a page-side adapter, not the DOM environment and not the canvas quantiser the probe above measures."
/>

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:

```python
# 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.

<ThresholdLine />

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:

```go
// 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:

```python
# 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](/articles/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
  `answers` map with distributions intact; `trace.py` is 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](/articles/webmcp-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.

<CallsPerStep />

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:

```bash
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:

```python
# systemone_harness/actions.py
two_request: bool = False
```

It 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](/articles/generative-ui-by-decision), 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.**

<Figure
  src="/articles/system-one-harnesses/fig1.png"
  alt="A flow diagram of a browser agent. A Task box, plain English plus a start URL, feeds two boxes that run at T=0 in parallel: Plan, checkable requirements from the task alone, and Open, the start page or a direct address. Open feeds a shaded panel headed 'each step: one batched Jev request' containing four boxes in a row — Observe, page indexed into candidates; Jev picks, operation and target with odds; Gate, irreversible? secrets by name only; Act, settle on DOM quiet — with an arrow looping from Act back to Observe. Two branches leave Jev picks: READ goes to Read, Jev picks a quote, LLM reads if none fits, then to Notes, facts with verbatim quotes from the page; DONE goes to Done?, Jev checks, LLM verifies when unsure, then to Answer, every claim cited or why it stopped. A legend colours each box by owner: Jev picks from what is on the page, LLM plans reads and writes, Code owns every gate and every quote."
  caption="fastbrowse's own diagram of a run, and the division of labour this section is about: the coloured bar on each box says who owns it. Read and Notes are the composer — the model picks a quote, code carries the text. (agent-labs-dev/fastbrowse, assets/architecture.svg at 3588580; MIT, terms in /articles/system-one-harnesses/NOTICE.txt.)"
/>

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:

```python
# 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."*

<Video
  src="/articles/system-one-harnesses/fastbrowse-checkout"
  poster="/articles/system-one-harnesses/fastbrowse-checkout-poster.jpg"
  alt="A browser signs in to the Sauce Labs demo shop as standard_user, adds a backpack at $29.99 and a bike light at $9.99 to the cart, opens the cart, checks out as Ada Lovelace with a postcode, and reaches a Checkout Complete page reading Thank you for your order. The recording ends on the agent's answer panel: the total for the completed order is $43.18, followed by a numbered citation reading www.saucedemo.com, quote, Total: $43.18, and a status line reading complete in 20.9 seconds, 13 steps, $0.0034."
  caption="fastbrowse doing a sign-in, a two-item cart, a shipping form and a checkout in 13 steps. The project's own recording at 3588580, transcoded; MIT terms in /articles/system-one-harnesses/NOTICE.txt. The last frame is the point: the answer carries a verbatim quote that code copied out of the page capture, not a sentence a model wrote about the page."
/>

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](/articles/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:

1. Build the answer template as text — `urgent: yes\nbucket: A\ntone: 2` — and
   tokenize it.
2. 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.
3. Seed the canvas with the template, replace each slot with random noise, pin
   everything else.
4. 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:

```python
# 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:

1. **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](/articles/where-to-use-jev) this corpus arrived
   at from the deployment side.
2. **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.
3. **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.
4. **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.
5. **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.

<ChangeMyMind>

<Falsifier claim="SystemOneHarness really does make exactly one model call per step, and the price is that no parameter can be a string.">
Read from `controller.py` and `actions.py` at `ab8e8f0`, not from a run — I have no OpenRouter key pointed at a decision model. The loop calls `provider.decide` once per iteration and `Param.from_dict` raises on a free-text parameter, so the claim is structural rather than empirical. It falls if someone wires `two_request` to 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.
</Falsifier>

<Falsifier claim="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.
</Falsifier>

<Falsifier claim="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_control` distribution that is not 29 of 30 one answer — and the accuracies become informative rather than degenerate. If `next_control` then 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.
</Falsifier>

<Falsifier claim="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 `A` and `yes`, 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.
</Falsifier>

<Falsifier claim="webctl never gates on Jev's confidence, so the calibrated quantity is decoration in that pipeline.">
Read from `rank()` and `backfill()` in `cmd/webctl/cli/search.go` at `e9bc54a`; the keep rule is a comparison on the score, and confidence reaches only `--verbose` and 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 with `score × confidence` or 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.
</Falsifier>

<Falsifier claim="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.
</Falsifier>

</ChangeMyMind>

---

*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](/articles/system-one-harnesses/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.*
