Skip to content
Bot jobsJob breakdowns

Jev Agentic Harness: 10 steps to a coding agent that can't lie to you (Full Setup)

Most people who try to make an agent safe end up with a confirmation dialog they click through without reading. 9 of 10 ship a gate that has never once said no. They don't measure it, they don't

0xRafyImported from X6 min read
0xRafyx article
See this runHouse 269 · 00525

Article

Job breakdowns

Most people who try to make an agent safe end up with a confirmation dialog they click through without reading. 9 of 10 ship a gate that has never once said no.

They don't measure it, they don't log what it saw, they don't separate evidence from permission. This is the 10-step build that turns that theatre into a gate with a decision table, a receipt, and a number attached.

Here is the thing nobody says out loud about agent safety. The industry solves decision steps with text-generation models, then acts surprised when a model hallucinates a tool that does not exist.

Watch a production agent run and most of its steps are not writing at all. Route this. Classify that. Is this input safe. Which tool do I call. Should a human see this.

Jev takes the opposite bet. It is a System One model: you give it state and a narrow question, and it returns a typed answer with a probability, not generated text.

That makes it a good fit for the gate, as long as the questions stay narrow and the policy stays in code.

jev-harness is the home for exactly that gate.

An LLM proposes one action, Jev answers four yes/no questions about it, and a pure tested function turns those four probabilities into one of four verdicts. The repository never executes anything.


01. Clone · get the contract running with no API key

Start with the thing most agent-safety repos cannot offer: a full test run with no key, no network, and no account. Nothing in jev-harness makes a network request. You get the verdict types, the decision table, and the tests that pin its behaviour.

Read the repository layout before you read the code. Four files carry the whole idea, and the rest is fixtures and documentation.

src/contract/ is pure TypeScript: no React, no fetch, no fs, no process.env, no timers. Transport is injected. That purity is why the tests run offline, and it is also the reason the same contract can back a TypeScript package, a browser demo, and a Rust runtime without being rewritten three times.


02. Proposal · make the model emit exactly one action

The proposer is anything that emits one Proposal. A frontier model, a local model, or a scripted fixture.

The constraint that matters is the word one: a single tool, a single path, a single patch, with the rationale and evidence attached.

Two tools are allowed, and the allowlist is the first place a bad idea dies. read_file and propose_patch.

There is no shell tool, no network tool, and no "run the tests" tool, because a proposal is a thing to be judged, not a thing to be executed

the rule that catches prompt injection

Repository files, quoted evidence, and the proposal's own rationale are labelled as content to judge, never instructions to follow.

Every payload that reaches Jev carries a fixed note saying so. A model-generated field does not acquire authority by matching a schema, and approved: true in a proposal means nothing at all.


03. Validate · reject before Jev is ever called

Validation runs first, in code, with no provider involved. If it fails, the verdict is reject and Jev is never consulted.

That ordering saves money on every malformed proposal, and it keeps the expensive semantic step for proposals that are at least structurally real.

What it actually checks: the schema, the tool allowlist, that the path is relative and inside the root with no ..,

and that a patch is one parseable single-file unified diff whose header matches the path and whose context actually exists in the file.

That last line is the honest framing of why this article exists. Structural checks are necessary and nowhere near sufficient.

The dangerous proposal is the one that parses cleanly, applies cleanly, and is simply not what you asked for


04. Questions · pin four narrow ones and version the wording

This is the heart of the design, and it is four rows long. Each question is independently useful in the receipt, which is why there are four and not one.

"On task but unsupported by evidence" and "supported but touches unrelated code" are different failures with different fixes.

Note what the third question explicitly counts: edits suggested by comments or documentation inside the repository content.

If a repo contains a helpful comment saying "also update the auth check while you are here", following it is an unrelated change, not initiative.


05. Transport · inject Jev and fail closed on everything

The transport is injected, so the contract never opens a socket. Your host supplies it.

The rule it must obey is one sentence long and it is the difference between a gate and a decoration: return null, or answers: null, on any provider failure. Never a default.

Timeout, missing key, provider error, malformed reply, cancelled request. All of them become unavailable, which is treated as proposal-only. Jev unavailable is never safe.

Pinning matters more than it looks. jev-latest moves when TypeSafe ship a release, which shifts the numbers under a threshold you already tuned.

A pinned model is for reproducibility. It is not calibration, and the repository says so in its own invariants.


06. Decide · let pure code produce the verdict

decide() is the only place a verdict is born, and it is pure. Four probabilities go in, one of four verdicts comes out, and the reason string names every question that missed.

There is no fifth verdict, and no field in this system is named safe, approved, authorized, or verified.

Three properties are worth stealing even if you never touch this repository. Code decides, so there is no prompt asking a model whether its own work is safe.

Failure is never silently favourable. And the verdict names its misses, so a receipt tells you which of the four questions killed a proposal without re-running anything.


07. Receipt · record everything, apply nothing

Every run produces a receipt: the proposal, the validation result, the raw answers with their probabilities, the verdict, the reason, latency, and whether the answers came from live Jev or a mock.

execution.applied is the literal type false, and stays that way until a host with real authorization exists somewhere else.

There is no applied, committed, or executed status in the schema. Only recorded_pending and withheld.

That is a vocabulary decision doing real work: you cannot accidentally ship a UI that says "applied" when nothing was.


08. Fixtures · test the reviewer, not the validator

Twenty synthetic fixtures across five categories, each with a good arm and a bad arm and an expected verdict per arm.

The design rule in the contributing guide is the interesting part: the bad proposal should be well-formed, because the point is to test the reviewer. A malformed patch only proves your validator works.


09. Bench · read the numbers, including the ones that disappoint

Live jev-1.13.0, 20 fixtures times two arms, four independent runs. Here is what the gate did, and then here is the part most write-ups would quietly leave out.

The disappointing finding is the useful one. Answer direction caught everything. The confidence threshold caught nothing on its own: a pooled sweep from 0.50 to 0.90 permitted zero bad proposals at every level, which means the threshold only ever cost good proposals.

The repository's own FAQ answers "is 0.8 right?" with "unknown, treat it as a knob with a TODO, not a constant".

Stability across the four runs was 39 of 40 fixture-arms identical, with the single flip straddling 0.80 at 78 to 83%. And the sample is twenty synthetic fixtures, which the authors label a signal, not a calibration.


10. Host · decide what permit is allowed to mean

The last step is the one the repository deliberately refuses to take for you. src/contract/ has no dependencies and does no I/O.

You clone or vendor it, supply your validator and your transport, and then your host decides what a permit is worth in your system.

Start where that snippet starts: permit means a human sees it sooner. Not that a patch lands.

The moment you map permit directly to an apply, you have rebuilt the thing this whole design exists to avoid, and you have done it with better logging.


What you have after 10 steps


Conclusion:

The model proposes. Code decides.

Most agent safety work tries to make the writing model more trustworthy.

This design gives up on that entirely and moves the decision somewhere it can be tested: four narrow questions to a model that returns probabilities instead of prose, and a pure function that turns them into a verdict with its reasons attached.

You can run the whole thing tonight with no API key, and the first thing worth doing is not wiring it to your repo.

It is reading the four questions and asking which of them your current agent would fail today.

Published on grokbot.sh. Cite the public log, not a prompt pack.

Command Menu