~/satyajit

AgentJev gets both halves

mdjsonmcp

2026-09-22 · 40 min · explainer · llm · architecture · calibration · agents

malevrigns/agent-jev is a 0.6B open-weights decision model with a claim list I have seen most of before. Beats Laya on Typed Decisions, 79.25% against 77.00%. Sixty-four candidates in one forward pass with KV prefix reuse, 92.4% of the computation removed, about 2x faster. Sixty-eight milliseconds per decision, zero decoded tokens, no JSON to parse. Twice Laya's context.

One item on that list is not like the others, and it is the reason this article exists:

Every other claim needs a benchmark, a denominator and an argument about whose hardware it ran on. That one is a claim about a type signature, and this site has spent four articles building the experiment that tests it. Reverse the option list. A per-option scalar scorer must flip exactly none of them by construction; a vocabulary readout cannot. openjev's letter readout flips 10 of 36 reversed questions and carries a +1.71 logit prior for slot A. The hosted model was measured this week flipping 12 of 100 under permutation.

The weights are Apache-2.0 and 1.2 GB, which is small enough to run on the four CPU cores I have. So I ran it.

the same option list, sent twice in two orders · how often the answer changed
modelanswers changedhow far anything moved
openjev — Qwen3.5-4B
vocabulary readout, letters in one prompt
10 / 36 · 27.8%
+1.71 logits for slot A
Jev 1.13 — hosted
per-option scorer, two-stage above threshold
12 / 100 · 12.0%
vector moves up to 0.69
kev — Qwen2.5-0.5B + LoRA
pointer head over a shared prompt
1 / 36 · 2.8%
not reported
Jev 1.13 — hosted, kev's probe
same model, 36-item perturbation set
0 / 36 · 0.00%
not reported
AgentJev-0.6B
shared prefix, private branches, set head
0 / 50 · 0.00%
9.5e-07 logits — a few float32 ULPs
measured here
The two hosted rows are the same model on different item sets, and they are in mild tension: a true 12% rate produces 0 of 36 about one time in a hundred. The likeliest reconciliation is cardinality — the 12% suite permutes 77 Banking77 labels, kev's 36 items carry a handful of options each. The AgentJev row is a different kind of number: the answer did not change because nothing in the arithmetic moved by more than a few units in the last place of a float32. Its denominator is the 50 questions I could afford to run end to end; the permutation sweep in the receipts below covers 2,520.

The answer changed on 0 of 50 reversed questions, and on 0 of 2,520 permutations of those same option sets. The largest movement of any single logit anywhere is 9.5e-07 — a few units in the last place of a float32. Every candidate's backbone vector comes back bit-identical between the two orders. This is not a training result and it is not a number that got small: nothing in this model has anywhere to put a position prior, so the only thing left to measure is the width of the float.

That is a real result. Other models in this category have been measured at zero flips on a small set; this is the first one where the invariance has been measured at the level of the arithmetic rather than at the level of the answer. What makes it worth an article rather than a footnote is how it is arranged, because it is not the arrangement that was supposed to buy this — and because the same model's score for an option does depend on what the other options say: a median of 0.088 logits, where a per-option scorer moves by exactly zero.

malevrigns/agent-jev@b66a8ce · snapshot 2026-09-22
tracked files
64
license
Apache-2.0
branch
main
tests
4 files
source
239.5 kB
commit date
2026-09-22
source by language
Python183.7 kB(31)HTML55.8 kB(3)

by size of tracked source at this commit, file counts in brackets; docs, data and vendored trees excluded

local clone, 2026-09-22 at b66a8ce branch, commit, commitDate, fileCount, hasTests, languages, license, licenseFile, testFileCount

The third arrangement

The fork this site has been describing since the cua-s1 piece is a genuine one, and I have stated it as a law more than once: the shape does not say how options reach the scorer, and the two answers fail in opposite directions. Isolate the options and order becomes inexpressible, but a question spanning two options has no answer to find — Jev scores 0 of 100 on exactly that. Share the context and relational questions become answerable, but position acquires a prior. Nobody gets both halves.

(The hosted model's 12 of 100 does not contradict that. Order-invariance is a property of the shape; what a hosted API does to it on the way through a batched fleet is a different question, and the determinism piece takes that apart. What follows is a model you can run yourself, where there is no fleet in the middle.)

AgentJev is arranged a third way, and it gets both halves. Here is the whole of it.

three arrangements of one option set · what each one buys
Isolated pathscua-s1-forms · open-jev-deberta · Jev, by argument
prefix recomputed per candidate
options meet
at the softmax, as scalars
order-invariant
yesexact in the shape; the hosted model still flips 12 of 100
candidate sees candidate
nonone — 0 of 100 on relational choice
One shared sequenceopenjev · Laya
one sequence · every candidate has an index
options meet
in the backbone's attention, at a position
order-invariant
no+1.71 logits for slot A; 27.8% flip
candidate sees candidate
yesfull, at token level
Shared prefix, private branches, set headAgentJev-0.6B
cached onceset headbranches never attend each other · head has no positions
options meet
after the backbone, in a head with no positions
order-invariant
yesmeasured below
candidate sees candidate
yesthrough one 256-d summary per candidate
The first two rows are the fork this site has been describing for four articles. Each pays exactly one of the two prices, and I have written more than once that you have to pay one. The third row does not sit on the fork and pays neither, because the only place its candidates meet is a two-layer transformer that was given no positional embeddings to meet at.

Read the serving file first, because its docstring is the clearest statement of the design anywhere in the repository:

# agent-jev · jev_service/prefix.py @ b66a8ce — the module docstring, in full
"""Inference-only shared-prefix KV branches, preserving independent causal paths.
 
Each branch receives a private cache. Sibling candidates never attend one
another. The trained candidate-set head runs after all branch vectors exist.
"""

Four sentences, and between them the whole design. The prefix is encoded once and cached — it is whatever the candidate paths agree on, which is the state, the question, the [CANDIDATE] marker and any leading words the candidates happen to share. Each candidate is then a private branch off that cache, so no candidate is in any other candidate's context. And every branch resumes at the same position index, because they all continue the same prefix, so candidate 7 and candidate 40 are encoded at identical rotary positions. There is no slot for a prior to attach to. That last property holds in the fallback route too, where each candidate gets its own full sequence: the candidate text still begins at token len(prefix) in every row.

# agent-jev · jev_service/prefix.py @ b66a8ce — the shared branch, reflowed from its one-line-per-statement original
prefix = torch.tensor([entries[0][0][:prefix_length]], device=device)
encoded = backbone(input_ids=prefix, attention_mask=torch.ones_like(prefix), use_cache=True)
root_cache = encoded.past_key_values
for start in range(0, len(entries), engine.path_batch):
    chunk = entries[start:start + engine.path_batch]
    suffixes = [seq[prefix_length:] for seq, _ in chunk]     # candidate text only
    cache = copy.deepcopy(root_cache)
    cache.batch_repeat_interleave(len(chunk))                # one private copy per candidate
    hidden = backbone(input_ids=ids, attention_mask=attention,
                      past_key_values=cache, use_cache=True).last_hidden_state

batch_repeat_interleave is the whole trick: the cache is copied across the batch axis, and the batch axis carries no attention. This is the same observation the cua-s1 piece made about score_pair_batch — stacking sequences into one tensor looks like sharing and is not — used deliberately rather than incidentally.

It is worth being precise about what the prefix sharing does and does not buy, because the obvious reading is wrong. Sharing the prefix is an efficiency device. It changes no property of the answer: the fallback route, which gives every candidate its own full sequence, computes the same thing more slowly, and the repository measured the two against each other and got a maximum probability difference of 0.000508. Order-invariance does not come from the sharing.

So far this is an efficient per-option scalar scorer. The options would meet at the softmax, four scalars long, and a relational question would be unanswerable. The departure is one module further on:

# agent-jev · agentjev/model.py @ b66a8ce
class CandidateSetEncoder(nn.Module):
    """Transformer over the candidate set of one question.
 
    No positional embeddings, so the mapping is permutation-equivariant:
    permuting the candidates permutes the outputs identically.
    """

The candidates do meet, in a two-layer transformer over the set of them, and they meet before the scalar is read rather than after. A transformer with no positional embeddings is a set function: permuting its inputs permutes its outputs and changes nothing else. That is the property the whole article turns on, and it is bought in the only place where cross-candidate information does not cost you a position.

The whole scoring path is seven lines:

# agent-jev · agentjev/model.py @ b66a8ce — AgentJevModel._score, annotations mine
def _score(self, cand_vecs: torch.Tensor, cand_mask: torch.Tensor) -> torch.Tensor:
    x = self.proj_in(cand_vecs)                  # [Bq, C, 1024] -> [Bq, C, 256]
    x = x * cand_mask.unsqueeze(-1).to(x.dtype)
    enc = self.set_encoder(x, cand_mask)         # candidates see each other here, and only here
    rep = cand_vecs + self.proj_out(enc)         # residual back onto the isolated vector
    logits = self.scorer(rep)                    # [Bq, C] — one scalar each
    return logits.masked_fill(~cand_mask, -1e4)

Note what the residual does. cand_vecs is the isolated per-candidate reading; proj_out(enc) is the correction that knowing the rest of the set buys you. Zero the set encoder's output and what is left is the shape of open-jev-deberta — a learned scalar head over an isolated encoding, which is the per-option family exactly. The whole departure from it is one additive term.

The channel is real, and I can put a number on it

A residual term is easy to write and easy to have contribute nothing. So the second half of the experiment below asks the complementary question: hold one candidate's text fixed, change which other candidates are on the list, and see whether that candidate's raw logit moves. Not its probability — that moves for free through the softmax when the set changes size — its logit, the number the softmax is taken over.

A per-option scalar scorer must give exactly the same logit. Its scalars exist before anything knows the set. AgentJev's move by a median of 0.088 logits and by as much as 0.71.

how far one candidate's raw logit moves, under two perturbations · log scale
1e-81e-71e-61e-51e-41e-31e-21e-112⁻²³reposition an option — median 4.8e-7, worst 9.5e-7change which options are on the list — median 0.088, worst 0.709change in a single candidate's logit, absolute →
Both perturbations leave the candidate's own text and the state untouched, and both leave its backbone vector bit-identical, so everything here happens in the 2.4M parameters of the head. The upper band is summarised per question — each question contributes its worst permutation out of all of them — so it is the pessimistic reading. Six decades still separate the two. That separation is the whole claim: position carries no information and composition carries a lot.

More than five decades separate the two perturbations — 5.3 comparing medians, 5.9 comparing worst cases. That is the article in one picture: the score depends on what else is on the list and not at all on where on the list it is.

One control, because dropping an option changes the size of the set as well as its contents, and a length effect would look the same. Take a candidate and two other candidates, and compare the two subsets that drop one of those others. Both subsets have the same cardinality and both still contain the candidate you are watching, so the only difference is which one of its neighbours is present. That is a pure content perturbation with the size held fixed: median 0.081 logits, worst 0.83, the same order of magnitude as before. It is the contents, not the count.

And the proportionate qualifier, because a tenth of a logit is not a lot: on this sample, removing a losing option changed the winner in zero of 183 drops. The channel is measurable and it is not, on these questions, decisive. That is roughly what you would expect from a 256-dimensional summary competing with a 1024-dimensional reading of the candidate itself — and it is why the open question is whether the channel can carry a reference, not whether it exists.

What the third arrangement costs

Two things, and neither is order.

The cross-candidate channel is narrow. A candidate arrives at the set encoder as one 256-dimensional vector, projected down from the 1024-dimensional hidden state at its last token. Option B does not get to read option D's text; it gets to read a 256-number summary of it. So the channel exists — the bar above is not zero — but whether it is wide enough to resolve "ship to the address in option D", the case spelled out in the jev-scores-zero piece, is untested by them and by me. It is the obvious next experiment and there is a falsifier for it at the bottom.

Equivariance is exact in the maths and approximate in the arithmetic. A permuted set changes the summation order inside the attention, and floating-point addition is not associative. The training code knows this: there is a permutation_invariance_kl term that re-runs the head on a shuffled order and penalises the divergence, with a comment saying it exists to "guard the property under bf16 numerics". Its weight is zero in the shipped protocol, which I think is the right call — this does not need training, it needs a wide enough float. What I measured on CPU is float32, and 9.5e-07 is what that costs. The repository's own check ran on GPU under bf16 autocast and reported exactly 0.0 — but that is a probability, on one three-option question, read off a head running in bf16, whose significand is eight bits against float32's twenty-four. A difference of the size I measured could not have shown up there. It is a smaller number on a coarser instrument.

The measurement, and how I got the numbers

Their check is jev_service/verify_http.py. It is one question, three options, reversed once:

# agent-jev · jev_service/verify_http.py @ b66a8ce — the entire permutation check
permuted['questions'][0]['options'] = dict(reversed(list(choice['questions'][0]['options'].items())))
shuffled = evaluate(permuted)
perm_error = max(abs(a[k] - b[k]) for k in a); assert perm_error < .02, perm_error

permutation_max_absolute_error: 0.0, on n = 1. True, and thin. So I ran it at scale, on the benchmark's own data, through the shipped code, on four CPU cores.

There are two runs, and the first one licenses the second.

Run one is strict and end-to-end. Take a question from the Typed Decisions test split, build the caller's payload twice — once with the option list as published, once reversed — and push each independently through jev_service.contract.prepare, encode_paths, the Qwen3 backbone, AgentJevModel._score, the per-primitive temperature and the softmax. Nothing is reused between the two: they are re-tokenised, re-batched and re-encoded, so the rows of the tensor that reaches the backbone are in a different order in the two runs. Realign the reversed answer and compare. Boolean questions are excluded, because contract.prepare hardwires their candidate order and leaves the caller nothing to reverse:

# agent-jev · jev_service/contract.py @ b66a8ce — a boolean has no order for the caller to reverse
keys = ['true', 'false']
candidates = [semantic(criteria[k], f'{k} criterion') if k in criteria else k.upper() for k in keys]

Two things came out, and the second is the one I did not expect to be able to state so bluntly.

The answer never changed. 0 of 50, Choice and Score questions with four or five candidates. On the same test the openjev readout changes its mind on 27.8% and the hosted model on 12%.

The backbone vectors are bit-identical. max|Δv| = 0.0 on every question, on every candidate, to the last bit. Reversing the list reverses the order of the rows in the batch that goes into the backbone and changes not one number that comes out. This is the load-bearing fact, and it is what makes the second run exact rather than an approximation: the vector for candidate j is a function of candidate j and the shared prefix, and of nothing else in the request.

Run two is the stronger perturbation, for the price of the first. Because the vectors do not depend on the arrangement, scoring a permuted set of the same vectors through _score is precisely what the service computes for a permuted request. So: one backbone pass per question, then every permutation of the candidate set — all 24 for a four-option question, all 120 for five — scored at the head and realigned. 57 questions, 2,520 permutations, zero answer changes, and the largest realigned movement of any single logit anywhere in the sweep is 9.5e-07.

Two to the minus twenty-three is 1.19e-07, so that worst case is a handful of units in the last place of a float32 mantissa, arriving in the set encoder's attention sum and nowhere else. To put it on the scale this corpus has been building: kev reports probabilities "within 4e-6" when the same questions are asked together or separately in fp32, and calls that four parts in a million.

The whole test is short enough to paste. This is the core of it, against the repository's own modules:

# reverse one question and compare, using the shipped serving code
from jev_service.contract import prepare, encode_paths
from agentjev.model import AgentJevModel
 
def logits(state, question):                      # question is the caller's dict
    paths, _, _ = encode_paths(prepare({"state": state, "questions": [question]}), tok, 2048)
    batch = pad_into_a_batch(paths)               # ids, attention_mask, cand_end_pos, …
    vecs = model.encode_candidates(batch)         # [1, C, 1024], one per candidate
    return vecs[0], model._score(vecs, batch["cand_mask"])[0].tolist()
 
forward = {**q, "options": dict(pairs)}
reversed_ = {**q, "options": dict(reversed(pairs))}
vf, zf = logits(state, forward)
vr, zr = logits(state, reversed_)
 
assert (vf - vr.flip(0)).abs().max() == 0          # the backbone never saw the order
print(max(abs(a - b) for a, b in zip(zf, reversed(zr))))

The assert is the line worth running on any model in this category. It passes here.

receiptscaptured 2026-09-22

Reversing a question's option list does not change AgentJev's answer, and neither does any other permutation of it. The answer changed on 0 of 50 reversed questions and on 0 of 2,520 permutations. The candidate vectors that come out of the backbone are bit-identical between the two orders, so the residual movement — worst case 9.5e-07 logits — is a few units in the last place of a float32, arriving inside the head's attention sum. The same head moves a candidate's logit by a median of 0.088 when you change which OTHER options are on the list. Position carries nothing; composition carries a lot.

perturbationnmeasureda per-option scorer gives
reverse the list — answer changed5000, by construction
reverse the list — worst |Δ| on a backbone vector500.00, exactly
reverse the list — worst |Δlogit|504.8e-070, exactly
reverse the list — worst |Δprobability|506.0e-080, exactly
every permutation — answer changed2,52000, by construction
every permutation — worst |Δlogit|2,5209.5e-070, exactly
drop an option — median |Δlogit| on a survivor7800.0880, exactly
drop an option — worst |Δlogit| on a survivor7800.710, exactly
swap which neighbour is present, set size fixed — median |Δlogit|9000.0810, exactly

The end-to-end run is 50 questions because a 0.6B model in float32 on four contended CPU cores is a minute or two per reversed pair; the permutation sweep is large because it reuses one backbone pass per question. Cardinality is 4 and 5 only, which is what this benchmark contains — the contract accepts 255 and I have not tested there. Nor have I tested bf16, which is what the GPU service runs and whose significand is eight bits against float32’s twenty-four. The questions are a seeded shuffle of the Choice and Score half of the split, so the draw is random but it is not stratified: top-1 agreement with the teacher on this subsample is 0.720 against 0.7925 on the full 2,000, which at this N is within sampling noise and is not a claim that the slice is representative.

method Weights from aimeigaoshou/agent-jev, code from malevrigns/agent-jev at b66a8ce, float32 on CPU — the dtype jev_service/engine.py uses when --device is not cuda. Per question: build the caller's payload twice, once as published and once with the option list reversed, and push each independently through jev_service.contract.prepare, encode_paths, the Qwen3 backbone, AgentJevModel._score, the per-primitive temperature from temperatures.json and the softmax. Compare the two candidate-vector tensors. Because they come back bit-identical, scoring a permuted set of the forward vectors through _score is exactly what the service computes for a permuted request, so every permutation — 24 at four candidates, 120 at five — was then scored at the head and realigned. Leave-one-out drops each candidate in turn and re-scores the survivors; the size-controlled variant compares two different leave-one-out subsets that both contain the candidate being watched. Boolean questions are excluded: contract.prepare hardwires their candidate order to ['true', 'false'], so a caller has nothing to reverse.
data /articles/agent-jev-06b/data/order-invariance.json (9 rows, 4.2 KB)

598,418,689 parameters, and 2.4 million of them are the model

"0.6B" is a claim, so I pulled the safetensors header for aimeigaoshou/agent-jev with range requests and summed the shapes. 343 tensors, all BF16, 598,418,689 parameters. The README's own latency table says 598M, so the claim and the artifact agree. That is the same check DiffusionGemma's 25.2B needed and it costs the same five minutes; it is worth doing every time, and it is worth saying when a project passes it.

The split is the interesting part:

blockparameters
path_encoder.backbone — Qwen3-0.6B, no LM head596,049,920
set_encoder — 2-layer transformer, d=2561,579,520
scorer — RMSNorm → 1024 → 256 → SiLU → 256 → 1263,681
proj_out — 256 → 1024263,168
proj_in — 1024 → 256262,400
decision head, total2,368,769

Everything that makes this a decision model rather than a language model is 2,368,769 parameters — 0.4% of the checkpoint. The cross-candidate channel accounts for 1,579,520 of them, and the order-invariance accounts for none: it is a positional embedding table that was never created. For scale, the whole of cua-s1-forms is 706,048 parameters, so this head is about three and a third cua-s1s bolted to a Qwen3.

aimeigaoshou/agent-jev@b3bf6b6 · snapshot 2026-09-22
announced
0.6B
measured
598,418,689
parameters
598.4M
repo size
1.20 GB
architecture
AgentJevModel
safetensors
1 shard
largest file
1.20 GB
files
16
downloads
32
likes
11
parameters by dtype
BF16598.4M

Weights only: a flat safetensors state dict with no modelling code, which is why the README has you wrap it in a torch checkpoint before the server will load it. AutoModelForCausalLM will not open this file — the keys are prefixed path_encoder.backbone, and there is no LM head to find.

repo last modified 2026-09-22

One detail the README's "language-model head removed" glosses. Qwen3-0.6B sets tie_word_embeddings: true, so the LM head is the embedding matrix transposed. Removing it frees exactly zero parameters. The saving is that no logit vector 151,936 wide is ever materialised, which is real and is a saving of compute and bandwidth, not of weights.

The option ceiling that is not there

This corpus has now found an option count welded into an export twice: logits[batch_size, 25] in the only open ONNX export, and K = 32 frozen into a Core ML bundle along with batch and sequence length. A third would make it a pattern, so I went looking.

It is not there — and part of the reason is that there is nothing here to freeze it into. Both earlier ceilings were properties of an export: a graph compiled to a fixed-shape target. AgentJev ships a PyTorch state dict and a Python server. No ONNX, no Core ML, no traced graph, so no shape got baked.

receiptscaptured 2026-09-22

Nothing in AgentJev's weights knows how many options a question has. Every limit the model appears to have is a `raise ValueError` in one 90-line file, `jev_service/contract.py`, plus one argparse default. The 64-candidate load the README times is a workload, not a ceiling — the contract accepts 255, the same number TypeSafe's Choice accepts, and the scoring head has no parameter that depends on the count at all.

limitvalueenforced byin the weights?
choice candidates2–255contract.py — `if not 2 <= len(keys) <= 255`no
score levels2–10contract.py — `if not 2 <= len(levels) <= 10`no
boolean candidatesexactly 2contract.py — `keys = ['true', 'false']`no
candidate paths per call1024contract.py — `total_paths > 1024`no
questions per call128contract.py — `total_questions > 128`no
states per call1–32contract.py — `not 1 <= len(requests) <= 32`no
tokens per candidate path2048server.py — `--max-tokens` defaultno — config says 32,768
candidates per backbone microbatch16engine.py — `path_batch=16`no

Two open decision models have now been found with an option count welded into an export — N=25 in an ONNX graph, K=32 in a Core ML bundle. This is the third one I have looked for it in and the first where it is genuinely absent, so 'decision models cap out at N options' remains a claim about particular files rather than about the shape. The one bound that is not in this file is the training distribution: the typed-decisions fine-tune saw questions with 2, 4 and 5 candidates and nothing wider, so 255 is what the contract accepts, not what the checkpoint has been shown.

method Read every bound in jev_service/contract.py at commit HEAD of github.com/malevrigns/agent-jev, then checked each against the weights. Parameter shapes come from the safetensors header of aimeigaoshou/agent-jev, pulled with HTTP range requests and summed: the scorer ends in `scorer.fc2.weight [1, 256]` and the candidate-set transformer carries no positional embedding table, so neither has a dimension indexed by the option count. The 2,048-token path limit is `--max-tokens`, an argparse default on jev_service/server.py; the published config.json declares max_position_embeddings 32,768.
data /articles/agent-jev-06b/data/ceilings.json (8 rows, 3.2 KB)

The scorer ends in scorer.fc2.weight [1, 256] — output dimension one, evaluated once per candidate, the same "N is a loop bound, not a weight" property the cua-s1 piece drew. And the set encoder, which is the one module that does see the whole set at once, carries no positional embedding table at all; that is the same absence that buys the order-invariance. A transformer with no positions has no maximum length.

So every limit AgentJev appears to have is a raise ValueError in jev_service/contract.py, plus one argparse default. The 64 candidates in the headline is a workload, not a ceiling; the contract takes 255, which is the number TypeSafe's own Choice takes. Even the 2,048-token context is --max-tokens, against a published config.json that declares max_position_embeddings: 32768. Doubling Laya's 1,024 is a real operational difference and it is a launch flag, not an architecture.

The one bound that is not in that file is the training distribution. The typed-decisions fine-tune saw questions with 2, 4 and 5 candidates and nothing wider. 255 is what the contract accepts, not what the checkpoint has been shown.

92.4% of the tokens, 51% of the clock

The efficiency claim is the one with the most committed evidence behind it and the most room to be misread, so I rebuilt it.

The project's own comparison panel. On the left, standard inference with no prefix cache: 610 ms for 64 options, 33,547 backbone token operations, state computed 64 times, redundant computation 92.4 percent. On the right, AgentJev shared prefix with key-value reuse: 299 ms marked 2.04 times, 2,551 backbone token operations marked minus 92 percent, state computed once across 64 options, numerical parity with a maximum difference of 0.000508 and the same choices. A footer claims evaluation of up to 255 candidates without linear slowdown.
The repository's own figure for the efficiency claim, and every number on it except two is in its committed prefix_verification.json. The two that are not: the state is labelled 1,500 tokens and is 472, and no committed measurement backs the 255-candidate footer — the only 255-option run in the repo executed with prefix sharing switched off. (malevrigns/agent-jev, assets/agentjev_shared_prefix.gif.)

jev_service/verify_prefix.py builds one fixed request — 64 Choice options and one Boolean over a repeated state — and times it under all three encoder modes, median of three after warmup. I re-ran its tokenisation and got the repository's numbers exactly: 66 candidate paths, 33,547 path tokens, a 492-token shared prefix on the Choice question and 493 on the Boolean. Sixty-four candidates paying 492 tokens each for the same state is the whole of the redundancy, and removing it is the whole of the 92.4%.

64 Choice options + 1 Boolean · 66 candidate paths · median of 3, after warmup
encoderbackbone input tokenswall clock
path
every candidate re-encodes the prefix
0 of 2 questions cached
33,547
609.65 ms
shared
both questions cache their prefix
2 of 2 questions cached
2,058 · −93.9%
348.56 ms · 1.75×
auto
cache it only where it pays
1 of 2 questions cached
2,551 · −92.4%
298.91 ms · 2.04×
From jev_service/prefix_verification.json. I rebuilt the token accounting from the same request and got the same 33,547: the Choice question's shared prefix is 492 tokens, the Boolean's is 493, and 64 candidates paying 492 tokens each is the whole of the 92.4%. What the clock says is that removing 92.4% of the tokens removes 51% of the time, because the branched pass stops being compute-bound.

So: "92.4% computation reduced" is measured in backbone input tokens, and it is exact. What it buys on the clock is 2.04x. The gap between those two ratios is the interesting engineering, and the middle row of that table is the proof that it is real rather than a rounding: forcing every question to share its prefix feeds the backbone 19% fewer tokens than the automatic mode and takes 17% longer.

The reason is that the branched pass stops being compute-bound. auto sends 2,551 tokens through a 0.6B model — about 2.2 TFLOP, which is nothing — but it does so as four sequential microbatches of sixteen rows and roughly sixteen tokens each, after copying a 492-token KV cache sixteen times per microbatch. Twenty-eight layers of kernel launches over a batch that is 256 tokens wide does not fill a GPU. The auto heuristic is one line and it is the right line:

# agent-jev · jev_service/prefix.py @ b66a8ce — share the prefix only where it pays
use_shared = mode == 'shared' or (mode == 'auto' and len(entries) >= 8 and prefix_length >= 128)

Fewer than eight candidates, or a prefix under 128 tokens, and it falls back to independent paths.

While I am in that loop: "64 candidates in one forward pass" is the one phrase in the claim list that is not literally true. path_batch is 16, so a 64-candidate question is one prefill of the prefix plus four batched branch passes — five backbone invocations, not one. What happens once is the encoding of the state, which is the part that matters and the part the 92.4% measures. It is a small thing, and it is also exactly the four sequential microbatches that keep the speedup at 2.04x instead of something larger.

That eight-candidate threshold is why the repo's own 255-candidate smoke test reports shared_prefix_questions: 0: its state was short, so nothing was cached, and 10,090 tokens went through the backbone at 684 ms. The figure's "up to 255 candidates without linear slowdown" is the one line on it that no committed measurement supports.

Worth saying what the sharing does not cost: the maximum probability difference against the independent-path route is 0.000508 and the chosen option does not change. That is a checked claim in a category where people usually assert parity.

The 68 ms is in a GIF

The per-decision latency figure is the one number in the claim list with no file behind it. It comes from assets/agentjev_reflex_demo.gif.

The project's own reflex demo panel. On the left, standard LLM autoregressive 27B plus: latency 3600 ms, generated tokens 485, showing a thinking trace and a JSON object with a reasoning field. On the right, AgentJev-0.6B parallel reflex: latency single forward 68 ms, generated tokens 0, showing a direct probability distribution over four actions with read_failed_test at 54.3 percent. A summary bar reads: AgentJev-0.6B delivers 50x faster latency, 68 ms versus 3.6 s, 0 output tokens, zero JSON syntax failures.
Where the 68 ms comes from. The baseline is an unnamed 27B-plus model emitting 485 tokens on unstated hardware, so the 50x is a token-count ratio of the kind this site took apart for a 63x claim — a fair comparison of two ways to get an answer, not a speedup of the same computation. (malevrigns/agent-jev, assets/agentjev_reflex_demo.gif.)

The README's own latency table is more careful than its GIF, and it is worth noticing which way the rounding runs:

loadLaya (421M)AgentJev (598M)
P50, 5 questions over 1 state41.53 ms~60–70 ms
P90, same47.14 ms~85 ms

Laya's figures are measured to two decimals. AgentJev's are a tilde and a range, and they are the slower of the two. That is a project rounding against itself, which is the opposite of the usual direction and part of why I trust the rest of the table. Note also the unit: 60–70 ms is per case, five questions over one state, so the per-decision figure is nearer 13 ms — while the GIF's 68 ms is for a single four-option Choice. Same magnitude, different workloads, and neither of them is in a committed artifact the way every other number in this repository is.

The benchmark, and the line its own card draws

Typed Decisions is LocalLLaMA/typed-decisions: Apache-2.0, public, 400 test cases and 2,000 questions across four workflows, five typed questions over one shared state. It is independent — its card says so outright, "not affiliated with TypeSafe and it does not reproduce their Jev model" — and it is the best-documented benchmark in this category, because it publishes its own ceilings.

Who made it, I cannot tell you beyond the handle. The dataset is owned by a Hugging Face account called LocalLLaMA, uploaded on 2026-09-16, with no paper, no author list and no repository behind it; it has 2,599 downloads and eleven likes. That is worth stating rather than glossing, because two open releases are now being ranked against each other on it. What it does have is a build procedure written down in full, four reference rows, two ceilings, and a Jev measurement with a date, a request count and a bill attached — which is more provenance than most benchmarks with an author list.

receiptscaptured 2026-09-22

The Typed Decisions table in AgentJev's README is three different measurements stacked in one grid, and the README says so. Two rows were run in this repository, five were copied from the dataset card. The Brier column survives that mixing — recomputing it here with AgentJev's own code reproduces the card's uniform baseline to three decimals. The ECE column does not: the same code on the same uniform predictor returns 0.046 where the card's table says 0.169, so the 0.1687 that sits next to Jev's 0.144 is not the same statistic.

table rowkindtop-1measured by
AgentJev-0.6Bspecialist, fitted here79.25%this repo — typed_decisions/experiment.py, 400 cases
Laya, published checkpointspecialist, fitted by its authors77.00%this repo — laya-typed-decisions @ f9ab0b2, 19.2 s for 2,000 questions
TypeSafe Jev 1.13.0generalist, zero-shot72.7%the dataset card — TypeSafe API, 2026-09-18, all 2,000 decisions, $0.016
ModernBERT-base 149Mspecialist64.6%the dataset card — Adaptive Classifier 0.2.0, frozen encoder
MiniLM-L6 22Mspecialist58.7%the dataset card — same harness
Prior (label frequency)reference47.0%the dataset card — reads nothing
Uniformreference30.8%the dataset card — 1/C on every option
Teacher self-agreementceiling, not in the README73.5%the dataset card — a fresh teacher sample against gold built from the others
Perfect scenario understandingceiling, not in the README70.4%the dataset card — the latent factors fitted to gold, cross-validated

Uniform is the check that settles it, because 1/C on every option leaves nothing to a fitting choice. Its Brier comes out at 0.2382 against the card's 0.238. Its ECE comes out at 0.046 with a first-index tie-break and 0.015 with a random one; no bin count between 10 and 20 and no soft/hard accuracy variant I tried gets within a factor of three of the card's 0.169. My prior is a reconstruction rather than the card's own, so its 0.1978 against 0.189 is the looser of the two checks.

method Rebuilt the 400-case / 2,000-question test split from LocalLLaMA/typed-decisions at revision ea93064, using typed_decisions/prepare_data.py's own convert() and jev_service.contract.prepare(), and checked that every question id and gold target matches the committed test_calibrated_predictions.json. Then ran typed_decisions/experiment.py's metrics() over a uniform predictor (1/C on every option) and over a per-question label-frequency prior fitted on the train split, and compared against the dataset card's reference rows.
data /articles/agent-jev-06b/data/provenance.json (9 rows, 3.5 KB)

Two rows of AgentJev's table were measured in AgentJev's repository. The Laya row is a real re-measurement — convaiinnovations/laya-typed-decisions at a pinned revision, all 2,000 questions in 19.2 seconds — not a quoted figure, which is better practice than the three-for-three pattern the jev-scores-zero piece catalogued. The Jev row is quoted, but from a card that ran the model itself: all 400 cases through the TypeSafe API on 2026-09-18, p50 710 ms per case, $0.016 total. So the 72.7% is a measurement by a third party with no stake in either specialist.

Now the part the README leaves out.

Typed Decisions test split · 400 cases, 2,000 questions · top-1 agreement with the teacher
30%40%50%60%70%80%Uniform30.8Prior — label frequency, reads nothing47.0MiniLM-L6 22M — specialist58.7ModernBERT-base 149M — specialist64.6Perfect scenario understanding70.4TypeSafe Jev 1.13.0 — zero-shot72.7Teacher self-agreement73.5Laya — specialist, fitted here77.0AgentJev-0.6B — specialist, fitted here79.25teacher self-agreement
Every number here is the dataset card's except the last two, which AgentJev measured in its own repository on the same split. The card's own instruction for reading the right-hand end: “Read 0.52 as the floor. Around 0.70 is strong. Around 0.75 is saturation.”

The card publishes a saturation point as well as a floor. A fresh teacher sample, scored against gold built from the other samples, agrees 73.5% of the time. A model that recovers the latent factors each case was generated from exactly scores 70.4%. And the card states the reading in its own words:

AgentJev scores 79.25%. Laya scores 77.00%. Both are above the noise floor of the labelling process, and the gap between them — 2.25 points, bootstrap interval [+0.65, +3.90] — sits entirely in the region the benchmark's author says is teacher idiosyncrasy rather than task. Neither project mentions this. AgentJev's README is careful in every other respect about what its number is — "agreement with the public teacher argmax… not a measured coding-agent success rate", "beating a row here does not mean a pull request merged" — and the one sentence it does not carry is the one the card wrote for it.

The README's own by-workflow table sharpens it further, and I do not think anyone has done this arithmetic. The four workflows are 500 questions each, so the overall delta is their mean:

workflowAgentJevLayadelta
Invoice processing86.20%81.20%+5.0
Customer service82.20%76.40%+5.8
Security incidents76.80%77.60%−0.8
Agent-trace observability71.80%72.80%−1.0
overall79.25%77.00%+2.25

The whole margin is two workflows. On the other two, the 421M encoder wins. A result that is +5 here and −1 there is not a model that is 2.25 points better; it is two models that are better at different things, averaged.

This is not a reason to disbelieve the result. A specialist fitted on 960 cases from four workflows, evaluated on 400 cases from the same four workflows, should learn the teacher. The hygiene on both sides is good — better than the usual. The benchmark generates its two splits in separate runs at different seeds and refuses to package if a case id or a state hash appears in both. AgentJev splits by case id, reserves 120 development and 120 calibration cases out of the 1,200, picks the checkpoint on development soft cross-entropy before the test split is opened, and pins the seed and the dataset revision. It is a reason to read +2.25 points over Laya as what it is, which is two specialists agreeing with the same teacher to within its own sampling noise.

The training run, incidentally, is 600 steps and 841 seconds. Fourteen minutes on one GPU takes the phase-4 checkpoint from 38.70% to 79.25%, which says rather more about how much of this task is in the four workflows than about the architecture.

Calibration: published, unusual, and not what the number looks like

The cua-s1 article had to be corrected for saying nobody published a calibration number when two projects had, so I went looking carefully here before saying anything.

It publishes plenty. Expected calibration error, sum-of-candidates Brier, soft cross-entropy and score MAE, per primitive and per workflow, before and after temperature scaling, for its own checkpoint and for the Laya checkpoint it re-measured, with all 2,000 per-question probability vectors committed. The temperature is one positive scalar per primitive, fitted on 180 boolean, 180 choice and 240 score questions from cases reserved for exactly that and used for nothing else. Only Bespoke's thirteen-dataset public suite goes further, and nobody in this category has better calibration hygiene than a split that reserves 120 cases for temperature fitting before the test set is opened. That is the protocol the RLCD piece asked for, done without being asked.

The part nobody else in this category has

Before the numbers, the thing I did not expect to find in the training code. agentjev/synth.py is a generator of decision questions whose gold distribution is exactly computable: biased coins with a Beta prior, loaded dice, urn draws, card draws, small Markov chains, Bayesian updates. Not a teacher's opinion of the probability — the probability.

# agent-jev · agentjev/synth.py @ b66a8ce — gen_coin, with the state string and four
# of its six questions elided
a0, b0 = rng.randint(1, 4), rng.randint(1, 4)
p = rng.betavariate(a0, b0)          # the world's actual bias, drawn from the prior
n = rng.randint(0, 20)
k = sum(1 for _ in range(n) if rng.random() < p)   # heads observed
K = rng.randint(1, k + 6)            # how many heads the goal needs in total
R = rng.randint(0, 12)               # flips left
post_a, post_b = a0 + k, b0 + (n - k)
if k >= K:
    p_goal = 1.0
else:
    # future flips share one unknown p, so this is the Beta-binomial posterior predictive
    p_goal = beta_binom_tail_at_least(K - k, R, post_a, post_b)
 
qs.append(_q("stop", "boolean",
             f"If we keep flipping for the remaining {R} flip(s), will the goal be reached?",
             BOOL, {"distribution": _round_dist([p_goal, 1 - p_goal])},
             "known_distribution", 1.0))

And agentjev/losses.py gives that label a supervision type, with a weight attached to how much the label is worth believing: known_distribution and deterministic at 1.0, binomial_counts and multiclass_counts scored as a count NLL with the weight scaling in the number of trials, and empirical/heuristic/teacher at 0.1–0.3. A teacher's distribution is explicitly the cheapest kind of supervision in this loss.

That is the most serious answer to this category's standing complaint that I have read. Every project here returns a probability; the criticism, restated at least four times on this site and once by kev in its own model card, is that the number is a statistic about a distribution rather than a verified frequency. Training on a closed-form posterior is the obvious fix, and this is the first repository I have seen do it.

Two honest limits. The phase checkpoints and the training configs are gitignored, so I can read the generator and the loss but I cannot verify what mixture was actually used or at what weight. And the generator's world is arithmetic — coins, urns, Markov chains — so whether calibration learned on "will this reach five heads in three flips" survives the trip to "should this invoice be paid" is precisely the open question. The answer visible in this repository is: partly. The phase-4 checkpoint that came out of that pipeline is the one over-confident row in the whole comparison, and it is 38.70% accurate.

Two things about the numbers themselves.

The Brier column is comparable and the ECE column is not. The README says plainly that the Jev, ModernBERT and MiniLM rows are copied from the dataset card and "use the card's definitions". So I checked whether the definitions agree, by running AgentJev's own metrics() over a uniform predictor — 1/C on every option, nothing fitted, nothing to disagree about — on the same 2,000 questions. Brier comes out at 0.2382 against the card's 0.238: the same statistic, so AgentJev's 0.0448 really does sit on the same scale as Jev's 0.148. ECE comes out at 0.046 against the card's 0.169. No bin count between 10 and 20, and no hard/soft accuracy variant I tried, gets within a factor of three. The 0.1687 next to Jev's 0.144 is two different statistics in one column.

And the model is under-confident, not over-confident. Both releases committed their per-question probabilities and neither drew the curve, so here it is.

Typed Decisions test split · 2,000 questions each · 10 equal-width confidence bins
000.250.250.50.50.750.7511perfectly calibratedteacher 0.659AgentJevECE 0.169LayaECE 0.217circle area= bin countstated confidence →observed accuracy →
Drawn from typed_decisions/test_calibrated_predictions.json and typed_decisions/laya_test_predictions.json — both committed, both on the same 2,000 rows. Above the diagonal is under-confidence. Neither model is over-confident in any populated bin, which is why an ECE of 0.169 here does not mean what an ECE of 0.169 usually means.

Every populated bin of both models sits above the diagonal. AgentJev's mean stated confidence is 0.624 and it is right 0.793 of the time; Laya's is 0.553 against 0.770. Neither is over-confident anywhere. That inverts the warning this category usually needs — kev's model card putting "a confidence: 0.92 from this model is a statistic about its own distribution" on the wall — because a threshold at 0.5 here escalates decisions the model gets right 78% of the time.

There is a clean reason, and it is in the benchmark rather than the model. The training target is a teacher distribution, the mean of three samples at temperature 0.7, and the mean top mass of that gold across the test split is 0.659. A model trained on soft cross-entropy against those targets reports 0.624. It is reproducing the teacher's spread almost exactly. The 0.169 is measuring the gap between the teacher's spread and the teacher's argmax, which is a property of the labels, not a miscalibration of the model.

Two consequences follow, and both are visible in the repository's own tables.

Temperature scaling made the ECE worse: 0.1573 uncalibrated, 0.1687 after fitting. The fitted temperatures are all above 1, so they soften a model that was already too soft. This is not an error — the temperature was fitted to minimise soft cross-entropy, which it does, from 0.85064 to 0.84943. It is the correct move if you consume the distribution and the wrong move if you threshold the top-1, and the two objectives genuinely disagree here.

And the best ECE in AgentJev's whole table belongs to the checkpoint it replaced. Phase 4, before the fine-tune, scores 0.1050 — against the calibrated model's 0.1687 — while being 38.70% accurate instead of 79.25%, and it is the only row that is over-confident. The dataset card made the same point about its own Prior baseline: "Prior also has the best ECE on the table, at 0.088, while knowing nothing… That is the clearest argument for reading KL and Brier here instead of ECE." AgentJev's own table contains the demonstration, one row below the headline.

One more caveat on the Brier, since it is the column that survives. The loss is soft cross-entropy plus 0.1 times sum-of-candidates Brier, so the model is optimised directly on the metric it is reported against, on a distribution drawn from the same generator. That is not a hidden fact — protocol.json states it — but 0.0448 against Jev's 0.148 is an in-domain model trained on the scoring rule against a cold model that was not.

What the repository does that I wish more of them did

A note on evidence, because this is the most thoroughly self-audited release in this category and the contrast with its own GIFs is instructive.

verification.json records the HTTP smoke tests with their failures spelled out: question isolation moves an answer by up to 0.00282 under bf16, and the file says so rather than claiming bit-exactness. serving_verification.json checks the HTTP path against the offline evaluation path on 4 cases and 20 questions and gets a maximum probability difference of 1.07e-07. selection.json records that the checkpoint was chosen before the test split was opened. transfer_verified.json is ten SHA-256 comparisons. The Chinese serving notes go further than any English README I have read this month — my translations, so read them as such: Phase 5 was abandoned because "policy value did not improve and the stall judgment collapsed", the weights loaded are "not a random head or a 27B pretending to be Jev", and the unimplemented TreeEncoder is explicitly "not falsely claimed as a completed training speedup" — which is true, and which I only noticed because they said it.

One thing those files do not say loudly enough. Both verification.json and prefix_verification.json were produced against runs/phase4/final.pt, not the typed-decisions checkpoint on Hugging Face — you can see it in the "model": "phase4" field of every response they record. The architecture and the tensor shapes are identical so the timings carry over, but the 2.04x and the 92.4% were measured on a different set of weights from the ones you download. serving_verification.json is the one that used agentjev_v1.

That last one is worth pulling out, because it is a real limitation stated plainly. The shared-prefix runtime exists in serving only. In training, every (state, question, candidate) triple is still an independent sequence that recomputes the prefix, and TreeEncoder.forward raises NotImplementedError. The 92.4% is an inference property.

The shipped integration is a Claude Code PreToolUse hook: agentjev_hook.py posts the tool payload to the local server for Bash, Write and Edit, asks one boolean and one four-level score, and blocks only when the score is level 3 and the boolean says unsafe. It fails open on any error, which is the right posture for a gate in front of your own tools and also means the gate is advisory. It is a demo, and it has the tell — the audit log path is hardcoded to somebody's C:\Users\… directory, inside a try/except pass.

So what is actually new here

Four things, in order of how much I think they matter.

A decision model can have both halves, and the place to buy them is the head. The fork I described as a law is a law about the backbone. Isolate the candidates there — which the shared prefix does more cheaply than anyone else in this category — and then let them meet in a function that has no positions. Position carries nothing, composition carries 0.088 logits at the median, and both properties come out of the same 2.4M parameters. What I still do not know is whether that channel is wide enough to carry a reference rather than a comparison, and that is now the most interesting open question in this category, because it is the one the 0/100 was supposed to have settled architecturally.

Order-invariance is now measured somewhere, not just claimed. Zero flips across 2,520 permutations, with bit-identical backbone vectors and residuals at float32 epsilon. The hosted model the category is named after was measured this week at 12 of 100. An open 0.6B that anyone can download has the property the closed one is described as having.

Somebody trained on probabilities that are actually true. synth.py builds decision questions out of coins, urns and Markov chains whose gold distribution is a closed-form posterior, and losses.py weights that supervision at 1.0 while weighting a teacher's distribution at 0.1–0.3. Every project in this category returns a probability and every one of them has been asked what it means. This is the first answer to that question that is not a temperature fitted after the fact. Whether it transfers out of the arithmetic is unsettled, and the phase-4 row says not completely.

Everything else on the claim list is a real number with a denominator you have to read. 92.4% is tokens and buys 2.04x. 79.25% is above the benchmark's own saturation line. 0.0448 Brier is optimised for and in-domain. 68 ms is a GIF, against an unnamed 27B. None of that is dishonest — almost all of it is disclosed somewhere in the repository, and this repository discloses more than most — but the claim list and the evidence live in different files, and the claim list is the one that travels.

What would change my mind

6 claims above, and what would falsify each

  1. AgentJev's answer is exactly invariant to the order of the option list.

    Zero flips across 2,520 permutations of 57 questions, worst movement 9.5e-07 logits, in float32 at cardinality 4 and 5. Two things would break it. Cardinality: run the sweep at 16, 64 and 255 options, where the set encoder's attention has far more terms to sum in a different order. Precision: run it under torch.autocast(bfloat16), which is what the GPU service does and whose significand is eight bits against float32's twenty-four — the repository's own permutation_invariance_kl term exists because someone was worried about exactly that. I can name the place a real order dependence would live, and my test never reached it. path_batch is 16, so a question with more than sixteen candidates is split into microbatches in the order the caller sent them, and each microbatch is padded to its own longest suffix. Reverse a 64-option list and candidate 3 moves from a chunk with one set of neighbours to a chunk with another, which changes the padding it sits next to and therefore the reduction order in its attention. Below seventeen candidates there is one chunk and this cannot happen; above it, it happens on every request. The sweep at 64 and 255 options is the experiment, and it is the one that would actually find something.

  2. The cross-candidate channel is wide enough to answer a relational question.

    I have shown the channel exists — dropping an option moves a surviving option's raw logit by a median of 0.088 logits, where a per-option scorer would move it by exactly zero — and I have not shown it carries a reference. Those are different claims and the gap between them is the most interesting open question in this category, because the 0/100 on relational choice was supposed to have settled it architecturally. Build the "ship to the address in option D" case from the jev-scores-zero piece at n = 100, in AgentJev's own request schema, and score it. A per-option scorer gets 0 by construction. If AgentJev also gets 0, the channel is real and too narrow to carry a pointer — a 256-number summary of an option is not its text — which would be the most informative negative result available here. If it scores materially above chance, the fork this site has been describing for four articles is not a fork at all.

  3. 79.25% on Typed Decisions is above the benchmark's own saturation point, so the margin over Laya is teacher idiosyncrasy.

    The dataset card puts teacher self-agreement at 0.735 and says a score much above 0.75 means the teacher's quirks have been learned. Both specialists are above it. The experiment that settles what the margin means is a transfer run: score AgentJev and Laya on typed decisions from a workflow neither was fitted on — a fifth generator, or Bespoke's contrastive holdout, or kev's 764-record out-of-domain set. If the 2.25-point gap survives out of domain, it is a model difference and I am being too cautious. If it collapses or inverts, it was the teacher.

  4. AgentJev's ECE of 0.1687 is not the same statistic as the 0.144 printed next to it.

    Run the dataset card's own scorer over a uniform predictor on the test split and publish the number. AgentJev's metrics() gives 0.046 for that predictor; the card's table says 0.169. One of those is wrong about the other, and a uniform predictor has no free parameters to argue about. If the card's 0.169 reproduces under some binning I did not try, the ECE column is comparable after all and this objection dissolves.

  5. Training on closed-form posteriors is what makes this model's probabilities mean something.

    I found the generator and the supervision weights; I did not find an ablation, and the repository does not publish the phase checkpoints or the configs, so I cannot tell how much of the mixture those questions were. The experiment is cheap and nobody in this category has run it: train the same head twice from the same base, once on the synthetic exact-posterior families and once on a teacher-labelled set of the same size, and report reliability on a third, held-out domain with real outcome labels. If the exact-posterior run is no better calibrated out of domain, then closed-form supervision buys arithmetic rather than honesty and the argument above is decoration. I would genuinely like to be wrong about this one, because the alternative is that the fix is this easy and nobody had bothered.

  6. Removing 92.4% of the backbone tokens buys 2.04x because the branched pass stops being compute-bound.

    That is my reading of somebody else's GPU, which I have not touched. Profile it: report the time split between the prefix prefill, the copy.deepcopy plus batch_repeat_interleave of the KV cache, and the branch forward, at 8, 64 and 255 candidates. If the cache copy is a small fraction and the branch passes dominate, my explanation is wrong and the ceiling is elsewhere. Either way the fix is the same and the repository already knows it: path_batch is 16, so a 64-candidate question is four sequential microbatches when it could be one.

Cite this article

For attribution, please use the following reference or BibTeX:

Satyajit Ghana, "AgentJev gets both halves", ai.thesatyajit.com, September 2026.

bibtex
@misc{ghana2026agentjev06b,
  author = {Satyajit Ghana},
  title  = {AgentJev gets both halves},
  url    = {https://ai.thesatyajit.com/articles/agent-jev-06b},
  year   = {2026}
}
share