@Av1dlive: everything you need to start building with jev, in one article. code, architecture, diagrams... everything you need to …
Summary
This article provides a step-by-step guide to building an agentic harness using Jev, including code, architecture, and decision-making processes for AI development.
View Cached Full Text
Cached at: 09/24/26, 08:22 AM
everything you need to start building with jev, in one article.
code, architecture, diagrams… everything you need to follow the build and make it your own. https://t.co/0hy2KdKUNI
How to Build Agentic Harness using Jev (Builder’s Guide)
i’m going to walk you through how i built an agentic harness with jev, where i put the decision layer….step by step, with the code and diagrams behind each choice.
TLDR; if you don’t wanna read all this 3,500+ words of yap just go and use this GitHub repo and give it to your agent➡️ https://github.com/codejunkie99/keel
let it begin (the article begins now)
before the code: give the decisions a shape
i want a coding agent that gets better. i also want to know what changed when it does.
the system still needs to choose a model and limit its tools. then it needs to check the choice and judge the result.
that’s where jev engineering comes in. give the system a choice you can inspect, then keep enough evidence to judge whether that choice helped.
what a useful decision needs
each small decision needed three things:
-
named options.
-
a result the code could check.
-
a record of what happened afterward.
that last bit matters. an extra model call still has to earn its place.
the build we’re walking through
this article walks through three parts:
-
how we used jev as a decision layer.
-
why a coding harness is a useful place to put that layer.
-
what we built in keel, and how the pieces fit together.
then there’s a fourth part, because the title promises self-improvement: what the decision records make possible, and what they don’t prove yet.
a model can suggest the next move. the host still owns the move.
that distinction sounds small. it changes the architecture.
01 / how we used jev
give jev a small, clear job
in this build, jev chooses from a list the host prepares. “host” means the application code around the model.
that code decides which options are available before jev gets a say.
it returns a typed result: a fixed format the code can check. the result means “pick this one” or “i can’t choose.”
that’s the whole contract.
keep the menu under host control
the host prepares the menu; jev picks from it; the host checks the pick again.
there’s a practical reason for keeping the interface narrow.
let a model return any instruction it likes, and every part of the app that calls it has to work out what that instruction means.
make the result easy to check
a fixed format makes the job pleasantly dull: read the result, check it against the current state, accept it or use the fallback.
think of it like a dispatcher at a busy workshop. the dispatcher can choose which bench gets a job.
they can’t quietly rewrite the safety rules for the saw.
two places where the selector can act
there are two coding-workflow decisions where the selector can act. they sound similar, but they happen at different moments.
1. choose a route for a new task
when a fresh task has no pinned provider or model, the host can prepare eligible provider/model candidates.
jev or local laya can choose among those candidates, or abstain.
a pinned route is one the user has already chosen. the selector preserves that choice, along with live and resumed sessions.
filter the options before asking
“pick the best model” needs a boundary. the host must be ready to run every option it offers.
filter the options before jev sees them. the host removes routes it can’t offer:
-
the provider isn’t installed or enabled.
-
the model can’t be found.
-
the requested reasoning level isn’t supported.
the provider still checks its own login when the session starts. that can fail even after a route passes the host’s checks.
2. choose the next step inside deepseek
the loop offers four focus choices:
-
inspect
-
implement
-
verify
-
answer
each choice maps to a host-defined tool bundle.
the selector returns a focus id. the host finds its allowed tools, then checks them against the current tool definitions before use.
answer means no tools
“answer” is a useful example. it gets no tools.
if the selector says answer, the system doesn’t hand it a shell and hope it behaves.
the allowed bundle is empty because that’s what answer means here.
these are different from asking jev to control every coding agent in the app. it doesn’t.
a small example makes the split clearer
imagine asking the app to fix a parser bug. this is an illustration, not a measured result from the build.
if you pinned a provider, that choice stays put.
if it’s a fresh unpinned task and automatic selection is enabled, the host can offer its eligible routes.
the selector chooses within that list; it can’t summon a missing provider.
now choose the next step
later, inside the embedded deepseek loop, the decision is smaller: inspect, implement, verify, or answer. choosing verify changes the admitted tool bundle.
it doesn’t prove the patch is correct. a check still has to run, and its result still has to mean something.
this is where the design clicks for me.
“which worker?” and “which tools next?” deserve separate boundaries, because they’re different jobs with different failure modes.
the important boundary: external agents keep their own loops
keel can host several coding providers through the agent client protocol, or acp.
those providers run their own tool loops. they may also expose controls for tools, context size, or handoffs.
keel doesn’t control all those steps from start to finish.
in this build, jev and laya don’t take over those internals.
a listed command still needs a working api
we can read a provider’s capability descriptions. a description alone doesn’t give the host a way to run that action.
a listed slash command doesn’t automatically become an action that jev can call.
in the current path, a slash command may just become prompt text for the provider.
name who owns each loop
that boundary is easy to blur in a diagram. the word “agent” makes everything look like one agent.
in practice, there’s a host, a decision layer, and possibly a provider-owned agent loop. the host can route a new task to a provider.
that says nothing about who controls the provider’s next tool call.
the capability audit helped draw that line. keel lists commands, skills, modes, and tools.
draw the boundary where the code can enforce it
those descriptions tell us what a provider advertises. the host still needs a real way to call the action and check what happened.
that’s how the original jev-first ambition became a narrower implementation. the boundary follows the code we control.
drawing another arrow on the architecture sheet doesn’t create an api.
one adjacent surface: computer use
there’s also keel computer-use decide. it lets laya or jev select a host-prepared, low-risk action id or abstain.
the command doesn’t execute desktop actions.
that distinction is useful when reading the code. choosing a proposed click and carrying out a click are separate responsibilities.
compatible acp agents can use installed desktop tools through mcp, a tool-connection protocol. those agents still own their internal loops.
choice and permission are different things
jev can select a candidate. it cannot approve a dangerous action.
if a tool needs permission, the normal permission path still applies. user approval still applies where the app requires it.
model selection doesn’t grant authority, and neither does a high confidence value.
check the state again before acting
before it applies that choice, it checks that the action still matches the current task state.
if the provider list changed or the route is out of date, the host can reject the action and use a fallback.
check tools again before use
-
inside the deepseek loop, the host checks the tool set against current tool definitions. it checks the named tool again before running it.
-
both checks serve the same purpose: keep an old decision from acting on a changed system.
-
that’s the boring part. boring is what you want from safety checks.
give uncertainty a clear path
“i can’t choose” is a valid result. the host still needs to know what to do next.
the diagram shows the route-selection paths. none of them grants permission to run a tool.
define the fallback before you call the selector. then record when it was used, so a fallback doesn’t get counted as a selector win.
what the records show
the application can show activity while a selector call is happening.
after the host validates the result and observes the outcome, it can write a decision event or receipt.
those records are useful because they let us ask ordinary engineering questions.
-
what were the candidates?
-
did the selector choose or abstain?
-
did validation accept it?
-
did a fallback happen?
-
what tool outcome followed?
record the action and result
they are not private chain-of-thought.
we need to know whether the choice was allowed, whether the host accepted it, and what happened next.
that’s a deliberate trade. collect enough to evaluate the system.
a model’s explanation doesn’t prove why something happened.
02 / why put it in a coding harness?
coding is a chain of decisions
people often talk about a coding agent as if it were one call: prompt in, patch out.
real work is more like a series of forks.
-
which model should handle this task?
-
should the agent inspect files first or start editing?
-
which tools are relevant right now?
-
did the repo change while the decision was in flight?
-
should the system retry, abstain, or ask the person?
-
did the change compile, pass the focused checks, and preserve the user’s intent?
those decisions have different costs. a slow model on a tiny rename wastes time.
account for the cost of a bad choice
a wrong choice can cost more than a slow one. a fast model may need more retries on a large migration.
a tool choice can be harmless in one workspace and consequential in another.
the harness holds the details that shape each action:
-
the current repository state.
-
available providers and tool definitions.
-
permissions and task status.
-
the results of checks.
the model is one part of that system.
it isn’t the system.
the harness gives decisions somewhere to land
a standalone decision demo can show a model choosing between three labels.
that’s a start, but it doesn’t tell you whether the choice was usable.
inside a coding harness, the host can answer concrete questions:
-
were these choices actually available?
-
was the task new, pinned, or already running?
-
did the selected tool still exist when execution began?
-
did the permission layer allow the action?
-
what result came back from the tool?
-
did verification find a regression?
those details let us test the decision. they also help the host keep the choice within its limits.
here’s my opinion: show me the options, the choice, and the result. without those, a claim that the agent improved is mostly vibes.
three modes, three different choices
laya and jev are separate selectors. normal mode skips selection. the diagram shows what each path needs and where the host checks the result.
core ml is apple’s local model runtime. jev uses a protected credential already on the machine; this build has no key-entry screen.
“always use the smartest model” ignores latency, privacy, credentials, and user intent. the mode choice gives those tradeoffs a place in the app.
the extra call has to earn its place
a short tangent: routing can become its own little bureaucracy.
if choosing a worker takes longer than the task, we’ve made a very elaborate waiting room. the comparison i’d want is total task cost: selector time, worker time, retries, and review effort.
a cheaper decision call is useful only if the whole job benefits.check whether the extra call helps
we haven’t demonstrated that payoff with a coding benchmark here.
there’s another limit hiding in plain sight. the selector can’t choose a good option the host forgot to offer.
a good harness draws a hard line between deciding and doing.
-
for route selection, the model receives candidate ids: labels that refer to the options the host prepared.
-
those ids don’t let the model invent a new command or provider. the host keeps the details needed to run the choice.
-
inside the embedded deepseek loop, the selector returns one focus id. the host turns that id into a specific set of allowed tools.
keep the decision layer replaceable
then the host checks again.
-
that means the decision layer is replaceable. the caller doesn’t need to trust its prose.
-
it relies on the host’s checks. those checks need to be clear enough for someone to inspect.
-
this is why i like the small interface. you can point to the exact choice it made and the exact tools that choice admitted.
-
add more freedom and you’ve also added more behavior to explain when a run goes sideways.
the harness can show uncertainty without hiding it
model systems often produce tidy summaries even when the operational story is messy. the harness should preserve the messy parts that matter.
-
if a selector abstained, show that. if the host rejected a stale action, show that.
-
if the host fell back to the user’s original route, record the fallback.
those details aren’t clutter.
they let us trace the decision:
-
the host offered these options.
-
the selector returned this id.
-
the host accepted or rejected it under these rules.
what the harness can’t guarantee
putting a decision layer inside a harness doesn’t make every result correct. it makes the boundaries easier to specify and the outcomes easier to inspect.
it doesn’t prove that jev chose the best model. it doesn’t mean a successful build was caused by the selector.
trace where each model runs
-
those questions still need evidence, and some need more than a unit test.
-
also, “local laya” describes the selector. your coding worker may still call a hosted model.
-
a locally running acp client can do that too. i’d trace each hop before describing the whole workflow as local or private
03 / what we built
the build is keel 0.2.0
this build is keel 0.2.0, a local-first mac coding app.
-
base: an avid-derived coding workspace.
-
language: rust.
-
interface framework: gpui.
-
platform: apple silicon, with macos 15 or later.
this detail matters because there are two kinds of work in the story.
one is the application: a real coding workspace with sessions, provider connections, tools, and local decision modes.
the other is the jev engineering research and evaluation material.
they’re related, but they aren’t the same deliverable.
start from a working coding workspace
we started from a coding environment. it already had tasks, repositories, provider sessions, and tools.
that gave the work a practical center: how decisions behave when attached to tasks, repositories, provider sessions, and tool execution.
the design process: keep the choices legible
the useful design question was simple: where can jev make a choice that the application can still check?
that question gave us a way to decide where it belonged.
it also gave us a reason to stop adding it where the host couldn’t enforce the result.
1. identify the decision boundary
which choice can the selector make without owning the whole agent? new-task routing and a bounded next-step focus were both plausible.
provider-internal actions weren’t, because the host doesn’t own those loops.
2. make the options explicit
the host needs to know which providers and models are installed, enabled, and valid for the current task. the selector receives candidates; it doesn’t make up candidates.
3. preserve user intent
if someone pinned a route, we keep it. if a session is already running, don’t quietly reroute it.
fresh unpinned work is the narrow place where automatic route choice belongs.
4. build the ordinary path
local laya is the default. jev is a deliberate opt-in.
normal mode remains available for people who want the existing route behavior.
5. give failure a shape
badly formed output, invalid ids, old task details, and abstention each need a clear outcome.
the host can reject the choice, use a fallback, or ask the user.
no one needs to pretend every model response is usable.
conservative? sure.
i’d take a narrow choice i can debug over an impressive promise i can’t trace through the code.
follow the files where choices become actions
read the code that applies the choice. this map gives you a starting order and shows the main application layer
the arrows across the file cards show a reading order, not function calls. the source notes link to the key implementations.
a settings toggle tells you a mode exists. the code that accepts a route or dispatches a tool tells you what it can do.
where jev sits in the request path
here’s the route path in plain language:
-
a new task arrives without an explicit pinned route.
-
the host reads current provider and model availability.
-
it constructs a finite list of eligible candidates.
-
the selected decision mode calls local laya or hosted jev, if enabled for this path.
-
the selector returns a candidate id or abstains.
-
the host checks that the choice remains eligible and current.
-
the application starts the route or falls back according to policy.
-
a decision record captures what happened.
pinned routes and existing sessions bypass the automatic choice. that’s not a footnote; it’s a central product rule.
the step selector has its own checks
for the embedded deepseek loop, the sequence is different.
the host offers focus ids and candidate tool sets, the selector returns a focus, and the host prepares the corresponding bundle against current tool schemas.
at dispatch, it checks the named tool again. same principle; different lifecycle.
04 / what “self-improving” should mean
the shipped app records decisions; it doesn’t train itself
this is the line i want to keep clear.
keel records selector activity and outcomes that can support evaluation. it doesn’t automatically train laya from chat history.
it doesn’t silently rewrite its own policy after a failed run. the release doesn’t prove a self-improving agent in the sci-fi sense.
turn records into reviewed changes
what it gives us is the start of a feedback loop. a receipt can become a scenario.
a scenario can be replayed against a changed prompt, policy, or model. a human can review the difference and decide whether to keep the change.
less cinematic, yes. but i can work with a changed rule, an old result, and a new result.
“the agent learned something” leaves me guessing.
turn a failure into a case you can run again
suppose a route selector chooses a provider that became unavailable a moment later.
don’t just say “jev got confused.” save the details:
-
the offered options and task state.
-
the chosen id.
-
the host’s check result.
-
the fallback it used.
then create a replayable scenario:
-
reproduce the same candidate set and relevant task flags.
-
run the baseline selector or policy.
-
run the candidate change under the same conditions.
-
compare valid selection, abstention, fallback, latency, and downstream result.
-
have a person review whether the change improved the intended behavior.
replay a changed tool definition
for a tool-focus choice, the case might be different: the model selects a bundle whose schema changed before dispatch.
check that rebuilding the tool set filters out the old tool. also check that the host rejects it if requested anyway.
that’s the value of a structured record. it gives you the conditions to reconstruct the decision.
“the agent seemed confused” gives you a mood.
compare the change against the ordinary harness
a passed task doesn’t tell us which component deserves credit. maybe the worker would have solved it on the original route.
keep the task set and scoring rules fixed. changes to the worker, prompt, repository, or reviewer can affect the result too.
keep the failed examples
use a separate task set for the final comparison. don’t tune on every case, then call the familiar ones proof.
the awkward run can reveal a missing rule that clean demos hide. record the failures, fallback rate, total time, and review effort.
this is a proposed evaluation design. we’re not claiming measured coding-quality gains.
keep a human gate on production changes
an improvement loop can propose changes to:
-
candidate construction rules.
-
selector prompts or typed schemas.
-
fallback policy.
-
the local model or hosted route.
-
the tool bundle attached to a focus choice.
for each change, keep the old version, replay the relevant scenarios, and inspect regressions. then let a human approve the version that becomes the new baseline.
no silent self-training. no “it changed because it learned” with no diff and no rollback path.
where i’d start
start with one choice the host can check.
-
jev can choose among host-prepared options; it doesn’t own the host.
-
laya and jev are separate modes, not two names for one model.
-
provider-owned inner loops stay provider-owned.
-
selection doesn’t grant permission.
-
records support a future evaluation loop; they don’t mean the app trains itself.
-
the public jev engineering repo contains the framework and examples, while keel is the application build discussed here.
that’s the part i like about this kind of engineering. the model can be clever.
the surrounding system can still be clear.
start with one decision
if you’re building a coding harness, write down four things:
-
one decision it’s allowed to make.
-
the exact choices it can see.
-
what happens when it abstains.
-
how you’ll know whether the outcome helped.
then build the narrowest path that proves those rules.
start small enough that a bad choice has nowhere to hide.
a better agent needs a system that can remember what went wrong.
Notes and Thoughts
keel is now public. these links point to the article’s exact 0.2.0 source snapshot; main retains the earlier prototype.
source notes
-
keel architecture
-
keel guide
-
keel build report
-
decision mode implementation
-
route selection implementation
-
embedded deepseek agent loop
-
keel readme
Similar Articles
Jev – a curation of Jev demos on X, tools, skills, and integrations
This article curates demos, tools, skills, and integrations for Jev, a typed decision model from TypeSafe AI, providing a starting point for developers.
@zodchiii: Jev Founder, Diogo Amogo, just released a PDF on building a Jev Harness for coding agents this is a blueprint on how to…
Diogo Amogo, founder of Jev, released a PDF blueprint for building a Jev Harness to enhance coding agents, claiming to make them 200× faster and 400× cheaper.
@dair_ai: A beginner's introduction to Jev. Plus we also built a Jev Playground for you to test out several use cases.
This post introduces beginners to Jev and provides a playground for testing its use cases.
@_avichawla: Another insane Jev use case! Jev is making it dramatically cheaper to evaluate what actually happened inside an agent r…
Beacon is an open-source memory layer for AI coding agents that uses Jev to evaluate agent runs and turn useful workflows, corrections, and debugging patterns into reusable skills across multiple harnesses.
@omarsar0: Just published a few ideas for using Jev and Pi to build a custom harness. This is the first of the series. Some of the…
The author shares initial ideas for using Jev and Pi to build a custom harness, focusing on gates, routing, and verifiers, with plans for deeper exploration in follow-up posts.