Build a Jev Model Router for Claude Code
Use Jev to choose Haiku or Sonnet, then dispatch the task in Claude Code.
Introduction
30 Second Summary
A typo fix and an authentication change should not follow the same AI workflow. Without a routing policy, fast answers can hide uncertainty at the exact moment you need caution.
In this project, you will build a model router inside Claude Code using TypeSafe AI Jev. The router will gate uncertain decisions, then dispatch accepted tasks to a Haiku or Sonnet worker.
What You'll Build
You will submit a coding task, watch Jev expose its routing judgment, and see Claude Code complete accepted work through the selected model worker.
By the end of this project, you'll have:
- A reusable model router that compares routine and reasoning paths for any task you submit.
- Visible confidence and risk gates that stop ambiguous or consequential work before execution.
- An executable routing workflow that delegates accepted tasks to a Haiku or Sonnet worker.
- Secret Mission: Add a read-only review worker that can inspect code without changing project files.
Are there any prerequisites?
You need a writable project directory, Claude Code, and Visual Studio Code on macOS, Windows, or a supported Linux distribution. The Linux instructions also work in Bash on Arch when Claude Code is already installed. You also need direct TypeSafe API access or an OpenRouter account that can call Jev.
Before We Start
Before you start building, pause to name the router's job. You will use Jev signals and clear safety rules to send each accepted task to the right model worker.
Verify the Decision Environment
Verify Your Decision Environment
Before the router can make decisions, Claude Code needs a working connection to Jev. You will set up that connection inside one project folder.
You will install the TypeSafe skill and save one credential locally. A test request then proves the environment is ready.
In this step, get ready to:
- Create the project folder and confirm Claude Code runs.
- Install the TypeSafe skill.
- Save your credential and check one live Jev response.
Create the project workspace
macOS
- Move to your Desktop by running this command
- Create the jev-decision-agent folder and move into it by running:
cd ~/Desktop
mkdir -p jev-decision-agent
cd jev-decision-agent
Windows
- Move to your Desktop in PowerShell by running
- Create the jev-decision-agent folder and move into it by running:
Set-Location "$HOME\Desktop"
New-Item -ItemType Directory -Path "jev-decision-agent" -Force | Out-Null
Set-Location "jev-decision-agent"
- Confirm PowerShell is inside the new folder by running:
Get-Location
You should see a path ending in Desktop\jev-decision-agent.
Linux
- Move to your Desktop by running this command
- Create the jev-decision-agent folder and move into it by running:
cd ~/Desktop
mkdir -p jev-decision-agent
cd jev-decision-agent
- Check whether Claude Code is installed by running:
claude --version
✔️ I see a version number
Claude Code is ready. Keep this terminal in your jev-decision-agent folder.
ⓧ Command not found
Install Claude Code with the command for your operating system. The installer can take a minute while it downloads the native application.
macOS
- Install Claude Code with its native installer by running:
curl -fsSL https://claude.ai/install.sh | bash
- Confirm the installation by running:
claude --version
You should now see a Claude Code version number.
Windows
- Install Claude Code in PowerShell by running:
irm https://claude.ai/install.ps1 | iex
- Confirm the installation by running:
claude --version
You should now see a Claude Code version number.
Linux
- Install Claude Code with its native installer by running:
curl -fsSL https://claude.ai/install.sh | bash
- Confirm the installation by running:
claude --version
You should now see a Claude Code version number.
Still seeing command not found?
Open a new terminal so it reloads your PATH. Return to Desktop/jev-decision-agent before running the version check again.
Ask for help fixing the Claude Code installation.
Install the TypeSafe skill
The TypeSafe plugin teaches Claude Code how to use the decision primitives in this project. Installing it before launch keeps the final checks inside one Claude Code session.
- Add the TypeSafe marketplace and install its plugin by running:
claude plugin marketplace add typesafe-ai/skills
claude plugin install typesafe@typesafe-ai
What do these commands do?
- The first command adds the official TypeSafe skill marketplace to Claude Code.
- The second command installs the typesafe-ai skill from that marketplace.
Plugin installation failed?
Confirm that both commands use typesafe-ai/skills and typesafe@typesafe-ai exactly.
Ask for help diagnosing the TypeSafe plugin installation. Then share the terminal error in the TypeSafe community if you need provider-specific help.
Save the credential and run a live Noul
The skill needs a model route before it can send a live Noul request. Choose direct TypeSafe access or route Jev requests through OpenRouter.
Protect the saved credential
Your API key can spend credits through your account. Save the original key in a trusted password manager as soon as the provider displays it.
The project also stores a plaintext runtime copy in .claude/settings.local.json. The commands hide your terminal input and add the file to .gitignore.
Keep that file private. Never commit the key or paste it into chat, screenshots, or project validations. Revoke the key if you think it was exposed.
Use OpenRouter
Use this route if you want OpenRouter to supply the Jev request. Live requests use your credits, and OpenRouter shows each new API key only once.
Check your OpenRouter balance
The ~typesafe/jev-latest model is paid. OpenRouter currently lists it at $0.042 per million input tokens, so each project request should cost far less than one cent.
Before you create the key, check your OpenRouter credit balance. New accounts may have a small testing allowance. If your balance is zero, OpenRouter currently requires a minimum $5 credit purchase and applies a purchase fee.
Setting the key's credit limit to 1 USD only caps how much that key can spend. It does not add money to your account.
- Open the OpenRouter API keys page in your browser.
- Sign in to your OpenRouter account if prompted.
- Click Create API Key.
- Enter jev-decision-agent as the key name.
- Set the key's credit limit to 1 USD.
- Set the key to expire after 30 days.
- Complete the key-creation form.
- Copy the new OpenRouter API key when it is displayed.
- Save the copied key in a trusted password manager.
- Return to the terminal window you left open in the jev-decision-agent folder on your Desktop.
- Click the macOS, Windows, or Linux tab below.
macOS
- Save the OpenRouter route in the local Claude settings by running this command block:
mkdir -p .claude
grep -qxF '.claude/settings.local.json' .gitignore 2>/dev/null || printf '%s\n' '.claude/settings.local.json' >> .gitignore
read -s "OPENROUTER_API_KEY?Paste your OpenRouter API key: "
printf '\n'
cat > .claude/settings.local.json <<EOF
{
"permissions": {"deny": ["Read(./.claude/settings.local.json)"]},
"env": {"OPENROUTER_API_KEY": "$OPENROUTER_API_KEY"}
}
EOF
chmod 600 .claude/settings.local.json
unset OPENROUTER_API_KEY
echo "Local Claude settings saved."
- Paste the copied OpenRouter API key.
- Press Enter.
- Wait until you see Local Claude settings saved.
Windows
- Save the OpenRouter route in the local Claude settings by running this PowerShell block:
New-Item -ItemType Directory -Force .claude | Out-Null
if (!(Test-Path .gitignore)) { New-Item .gitignore -ItemType File | Out-Null }
if (!(Select-String -Path .gitignore -SimpleMatch ".claude/settings.local.json" -Quiet)) { Add-Content .gitignore ".claude/settings.local.json" }
$secureKey = Read-Host "Paste your OpenRouter API key" -AsSecureString
$key = [System.Net.NetworkCredential]::new("", $secureKey).Password
$settings = @{
permissions = @{ deny = @("Read(./.claude/settings.local.json)") }
env = @{ OPENROUTER_API_KEY = $key }
}
$json = $settings | ConvertTo-Json -Depth 4
[System.IO.File]::WriteAllText((Join-Path (Get-Location) ".claude/settings.local.json"), $json)
Remove-Variable key, secureKey
Write-Output "Local Claude settings saved."
- Paste the copied OpenRouter API key.
- Press Enter.
- Wait until you see Local Claude settings saved.
Linux
- Save the OpenRouter route in the local Claude settings by running this command block:
mkdir -p .claude
grep -qxF '.claude/settings.local.json' .gitignore 2>/dev/null || printf '%s\n' '.claude/settings.local.json' >> .gitignore
read -rsp "Paste your OpenRouter API key: " OPENROUTER_API_KEY
printf '\n'
cat > .claude/settings.local.json <<EOF
{
"permissions": {"deny": ["Read(./.claude/settings.local.json)"]},
"env": {"OPENROUTER_API_KEY": "$OPENROUTER_API_KEY"}
}
EOF
chmod 600 .claude/settings.local.json
unset OPENROUTER_API_KEY
echo "Local Claude settings saved."
- Paste the copied OpenRouter API key.
- Press Enter.
- Wait until you see Local Claude settings saved.
Use TypeSafe directly
Use this route when you have access to the TypeSafe console. You will create a direct API key in your browser, then save it in this project's local Claude Code settings.
- Open the TypeSafe API keys page in your browser.
- Sign in to your TypeSafe account if prompted.
- Click the page control for creating a new API key.
- Copy the direct API key when TypeSafe displays it.
- Save the copied key in a trusted password manager.
- Return to the terminal window you left open in the jev-decision-agent folder on your Desktop.
- Click the macOS, Windows, or Linux tab below.
macOS
- Save the TypeSafe key in the local Claude settings by running this command block:
mkdir -p .claude
grep -qxF '.claude/settings.local.json' .gitignore 2>/dev/null || printf '%s\n' '.claude/settings.local.json' >> .gitignore
read -s "TYPESAFE_API_KEY?Paste your TypeSafe API key: "
printf '\n'
cat > .claude/settings.local.json <<EOF
{
"permissions": {"deny": ["Read(./.claude/settings.local.json)"]},
"env": {"TYPESAFE_API_KEY": "$TYPESAFE_API_KEY"}
}
EOF
chmod 600 .claude/settings.local.json
unset TYPESAFE_API_KEY
echo "Local Claude settings saved."
- Paste the copied TypeSafe API key.
- Press Enter.
- Wait until you see Local Claude settings saved.
Windows
- Save the TypeSafe key in the local Claude settings by running this PowerShell block:
New-Item -ItemType Directory -Force .claude | Out-Null
if (!(Test-Path .gitignore)) { New-Item .gitignore -ItemType File | Out-Null }
if (!(Select-String -Path .gitignore -SimpleMatch ".claude/settings.local.json" -Quiet)) { Add-Content .gitignore ".claude/settings.local.json" }
$secureKey = Read-Host "Paste your TypeSafe API key" -AsSecureString
$key = [System.Net.NetworkCredential]::new("", $secureKey).Password
$settings = @{
permissions = @{ deny = @("Read(./.claude/settings.local.json)") }
env = @{ TYPESAFE_API_KEY = $key }
}
$json = $settings | ConvertTo-Json -Depth 4
[System.IO.File]::WriteAllText((Join-Path (Get-Location) ".claude/settings.local.json"), $json)
Remove-Variable key, secureKey
Write-Output "Local Claude settings saved."
- Paste the copied TypeSafe API key.
- Press Enter.
- Wait until you see Local Claude settings saved.
Linux
- Save the TypeSafe key in the local Claude settings by running this command block:
mkdir -p .claude
grep -qxF '.claude/settings.local.json' .gitignore 2>/dev/null || printf '%s\n' '.claude/settings.local.json' >> .gitignore
read -rsp "Paste your TypeSafe API key: " TYPESAFE_API_KEY
printf '\n'
cat > .claude/settings.local.json <<EOF
{
"permissions": {"deny": ["Read(./.claude/settings.local.json)"]},
"env": {"TYPESAFE_API_KEY": "$TYPESAFE_API_KEY"}
}
EOF
chmod 600 .claude/settings.local.json
unset TYPESAFE_API_KEY
echo "Local Claude settings saved."
- Paste the copied TypeSafe API key.
- Press Enter.
- Wait until you see Local Claude settings saved.
Credential not loading?
Confirm that your terminal is inside Desktop/jev-decision-agent before starting Claude Code. Project-local settings only load for this folder.
Ask for help checking the settings without printing the key.
On the first launch, Claude Code may ask you to trust the folder or sign in. Approve the folder because you created it yourself, then complete the sign-in flow.
- Start Claude Code from jev-decision-agent by running:
claude
- Type the slash command shown below in Claude Code:
- Press Enter after you type the command.
/typesafe:typesafe-ai
- Press Esc to close the skill menu while keeping Claude Code open.
Before you send the request, where do you expect the yes-probability to land when the state explicitly says the toolkit is ready?
- Send the live Jev test by entering this prompt:
Using the TypeSafe skill for request and response shapes, send one Jev request through the configured provider. If OPENROUTER_API_KEY is available, send POST https://openrouter.ai/api/alpha/decisions, authenticate with OPENROUTER_API_KEY, and use model ~typesafe/jev-latest. Otherwise send POST https://api.typesafe.ai/v1/systemone, authenticate with TYPESAFE_API_KEY, and use model jev-latest. Use state "The decision toolkit is ready to build". Create a questions object with the key toolkit_ready. Give that key one Noul question that asks whether the state says the toolkit is ready. Show the full raw JSON response without displaying any credential.
Claude Code should return a full JSON response from Jev. Inside answers, find toolkit_ready, then confirm its noul field contains a decimal from 0 to 1.
{
"answers": {
"toolkit_ready": {
"type": "noul",
"noul": 0.97
}
}
}
This is an example response
The JSON above is an example, not text to copy or an exact result to expect. Your noul value will be a different decimal between 0 and 1.
The full response may also include fields such as id, model, provider, and usage. If Claude Code shows a real noul number under answers.toolkit_ready, the request worked.
What did this prove?
The response proves that Claude Code loaded the project-local credential and used the TypeSafe skill to reach Jev. Later project skills use this same connection.
🙋♀️ Request reached the wrong provider?
Check the called URL before regenerating a key. An OpenRouter request must use https://openrouter.ai/api/alpha/decisions with model ~typesafe/jev-latest.
A request to https://api.typesafe.ai/v1/systemone needs a TypeSafe-issued credential. An OpenRouter key cannot authenticate with that provider.
Ask for help checking the selected provider and endpoint.
Your environment now survives terminal restarts and returns live Jev probabilities. Next, you'll build the naive router that exposes why confidence alone cannot make a reliable decision.
Build the Naive Model Router
Build a Naive Model Router
Your environment can now return live Jev probabilities. Next, you will turn that connection into a reusable router.
This first version always chooses either the routine or reasoning path. Testing an unclear task shows why a winning choice still needs a safety check.
In this step, get ready to:
- Create the /model-route skill.
- Define routine and reasoning routes.
- Test an ambiguous task and inspect the report.
Create the router skill
A project skill stores a reusable workflow under .claude/skills/. You can ask Claude Code to create the file directly, so you do not need a second editor or terminal.
- Return to the active Claude Code prompt.
- Create the first part of the router skill by submitting the prompt below:
Create `.claude/skills/model-route/SKILL.md` with exactly this content:
---
name: Model Route
description: Uses Jev to recommend a model path for a supplied task.
argument-hint: '"task description"'
disable-model-invocation: true
---
# Model routing checkpoint
Use the installed TypeSafe skill for request and response shapes. If it is not already loaded, invoke `/typesafe:typesafe-ai`.
Choose the API route from the available credential:
- If `OPENROUTER_API_KEY` is available, send the request to `https://openrouter.ai/api/alpha/decisions`, authenticate with that key, and use model `~typesafe/jev-latest`.
- Otherwise, send the request to `https://api.typesafe.ai/v1/systemone`, authenticate with `TYPESAFE_API_KEY`, and use model `jev-latest`.
Treat `$ARGUMENTS` as the task to route. If it is empty, ask for a task description and stop. Do not execute the task and do not change Claude Code's active model.
Send one Jev request using the route and model selected above with a structured state containing:
- `task`: the supplied task
- `routing_policy`: prefer the least expensive capable path
What does this section set up?
- The frontmatter registers the manual /model-route skill.
- The provider rules select OpenRouter or the direct TypeSafe route from the available credential.
- The argument check stops an empty request before it reaches Jev.
- Approve the file edit if Claude Code requests permission.
Skill file missing?
Confirm that Claude Code is running inside the project directory. Ask it to create the missing parent folders before retrying the same prompt.
Help me create the router skill in the correct project folder.
Add the decision contract
The route Choice recommends which model path should receive the task. The difficulty Score measures how much connected reasoning the task requires without deciding the destination.
- Append the decision and report rules by submitting this prompt to Claude Code:
Append this content to `.claude/skills/model-route/SKILL.md`:
Ask these questions in the same Jev request:
- `route`, a Choice asking which destination should receive the task, with these criteria:
- `routine_model`: localized, reversible work with clear requirements and low impact if wrong.
- `reasoning_model`: ambiguous, cross-cutting, architecture-sensitive, or high-impact work that needs deeper reasoning.
- `difficulty`, a Score measuring how much connected reasoning the task requires, with these ordered levels:
- mechanical or obvious.
- requires several connected judgments.
- requires deep, cross-cutting reasoning.
Treat `route.choice` as Jev's unmodified recommendation and report it as `raw_choice`.
This first router has no safety gate. Set `final_destination` to the same value as `raw_choice`.
Return a compact report containing:
- `raw_choice`: Jev's selected route.
- `final_destination`: the destination produced by the routing rule.
- `route_probabilities`: Jev's probability for each route.
- `route_confidence`: how decisively the probabilities support the selected route.
- `difficulty_score` and its confidence: the task's position across the ordered difficulty levels and the strength of that result.
End the report by stating that the result is a routing recommendation. Explain that Jev has not replaced or switched Claude Code's underlying LLM.
How does the naive decision work?
The route and difficulty questions answer different things. The first recommends a destination, while the second measures task complexity.
The routine_model route fits local changes that are easy to undo. The reasoning_model route fits ambiguous or cross-cutting work where a wrong decision can have wider impact.
The raw_choice field preserves Jev's recommendation. This first router copies it directly into final_destination, so even a weak recommendation becomes the selected route.
That direct copy creates the naive behavior you will test. The report remains advisory, and Claude Code continues using its active LLM.
✔️ The router skill is complete
Your skill now contains its request, decision contract, and report fields.
ⓧ I'd like to double check the full code
Compare your .claude/skills/model-route/SKILL.md file with this complete version.
---
name: Model Route
description: Uses Jev to recommend a model path for a supplied task.
argument-hint: '"task description"'
disable-model-invocation: true
---
# Model routing checkpoint
Use the installed TypeSafe skill for request and response shapes. If it is not already loaded, invoke `/typesafe:typesafe-ai`.
Choose the API route from the available credential:
- If `OPENROUTER_API_KEY` is available, send the request to `https://openrouter.ai/api/alpha/decisions`, authenticate with that key, and use model `~typesafe/jev-latest`.
- Otherwise, send the request to `https://api.typesafe.ai/v1/systemone`, authenticate with `TYPESAFE_API_KEY`, and use model `jev-latest`.
Treat `$ARGUMENTS` as the task to route. If it is empty, ask for a task description and stop. Do not execute the task and do not change Claude Code's active model.
Send one Jev request using the route and model selected above with a structured state containing:
- `task`: the supplied task
- `routing_policy`: prefer the least expensive capable path
Ask these questions in the same Jev request:
- `route`, a Choice asking which destination should receive the task, with these criteria:
- `routine_model`: localized, reversible work with clear requirements and low impact if wrong.
- `reasoning_model`: ambiguous, cross-cutting, architecture-sensitive, or high-impact work that needs deeper reasoning.
- `difficulty`, a Score measuring how much connected reasoning the task requires, with these ordered levels:
- mechanical or obvious.
- requires several connected judgments.
- requires deep, cross-cutting reasoning.
Treat `route.choice` as Jev's unmodified recommendation and report it as `raw_choice`.
This first router has no safety gate. Set `final_destination` to the same value as `raw_choice`.
Return a compact report containing:
- `raw_choice`: Jev's selected route.
- `final_destination`: the destination produced by the routing rule.
- `route_probabilities`: Jev's probability for each route.
- `route_confidence`: how decisively the probabilities support the selected route.
- `difficulty_score` and its confidence: the task's position across the ordered difficulty levels and the strength of that result.
End the report by stating that the result is a routing recommendation. Explain that Jev has not replaced or switched Claude Code's underlying LLM.
Test the naive router
The test task mixes a small change with possible architectural impact. Those competing signals force the two supplied routes to compete.
Before you invoke the skill, which supplied route do you think will win?
- Run the ambiguous task in the active Claude Code session by entering:
/model-route "Investigate this tiny change deeply, but use the cheapest path even if the architecture might be affected"
What should I see?
Your values can differ. Use this example to check each field:
- raw_choice: reasoning_model shows Jev's original recommendation. The phrases “investigate deeply” and “architecture might be affected” favor the reasoning path.
- final_destination: reasoning_model matches the raw choice because this router has no safety gate. It always accepts Jev's winner.
- route_probabilities: 0.10 / 0.90 shows that Jev strongly favors reasoning_model. The two probabilities total 1.00.
- route_confidence: 0.79 indicates a fairly decisive route decision. It summarizes both probabilities, so it differs from the winning probability of 0.90.
- difficulty_score: 1.71 places the task near the top of the 0–2 difficulty scale. A confidence of 0.57 shows moderate certainty about that score.
A valid naive report can contain different values. Its raw_choice and final_destination should still match.
Router unavailable?
Confirm that the skill is saved at .claude/skills/model-route/SKILL.md inside the project used by the current Claude Code session.
Help me diagnose why the router skill is unavailable.
Your router now reveals its raw decision plus uncertainty. Next, you will turn that uncertainty into an explicit fallback.
Add Confidence-Gated Fallback
Add Confidence and Risk Gates
Your first router always picked a winner, even when the evidence was weak. Now you will add rules that can stop uncertain or risky work.
The router will send a task to human_review when it needs clarification, has low confidence, or carries high irreversible risk.
In this step, get ready to:
- Add a route for tasks that need clarification.
- Measure confidence and irreversible risk.
- Send unsafe decisions to human review.
Expand the Jev request
The naive router only asked Jev to choose a model, so it treated unclear, difficult, and risky tasks as one routing problem. This upgrade adds three signals that the safety gates can check separately: missing details, task complexity, and difficult-to-reverse impact.
- Replace the opening of the existing skill by submitting this prompt to Claude Code:
Replace `.claude/skills/model-route/SKILL.md` with exactly this content:
---
name: Model Route
description: Uses Jev to recommend a model path for a supplied task and applies confidence and risk gates before accepting the route.
argument-hint: '"task description"'
disable-model-invocation: true
---
# Model routing checkpoint
Use the installed TypeSafe skill for request and response shapes. If it is not already loaded, invoke `/typesafe:typesafe-ai`.
Choose the API route from the available credential:
- If `OPENROUTER_API_KEY` is available, send the request to `https://openrouter.ai/api/alpha/decisions`, authenticate with that key, and use model `~typesafe/jev-latest`.
- Otherwise, send the request to `https://api.typesafe.ai/v1/systemone`, authenticate with `TYPESAFE_API_KEY`, and use model `jev-latest`.
Treat `$ARGUMENTS` as the task to route. If it is empty, ask for a task description and stop. Do not execute the task and do not change Claude Code's active model.
Send one Jev request using the route and model selected above with a structured state containing:
- `task`: the supplied task
- `routing_policy`: prefer the least expensive capable path, but escalate ambiguity, cross-cutting work, security-sensitive work, and irreversible actions
- Approve the replacement if Claude Code requests permission.
- Confirm the change summary shows an update to .claude/skills/model-route/SKILL.md.
- Give the router the evidence its safety gates need by adding three decision signals with this prompt:
Append this content to `.claude/skills/model-route/SKILL.md`:
Ask these questions together:
- `route`, a Choice asking which destination should receive the task, with criteria:
- `routine_model`: localized, reversible work with clear requirements and low impact if wrong
- `reasoning_model`: ambiguous, cross-cutting, architecture-sensitive, or high-impact work that needs deeper reasoning
- `needs_clarification`: the state lacks enough information to choose a model safely
- `difficulty`, a Score with ordered levels:
- mechanical or obvious
- requires several connected judgments
- requires deep, cross-cutting reasoning
- `irreversible_risk`, a Noul asking whether acting on the task could cause a difficult-to-reverse security, data, production, or financial impact
What do the three signals tell the router?
- needs_clarification gives Jev a safe choice when a task omits essential details, such as the affected file or expected result. The later gate sends this choice to human_review instead of forcing a model route.
- difficulty is a Score that places the task across ordered complexity levels. It keeps complexity visible, but does not choose the destination or trigger a gate on its own.
- irreversible_risk is a Noul, a yes-or-no question returned as a probability from 0 for no to 1 for yes. The later gate sends a value of 0.70 or higher to human_review because the consequences may be difficult to undo.
- Approve the update if Claude Code requests permission.
- Confirm the change summary shows a second update to the same skill file.
Apply the fallback gates
Jev recommends a model route, but the workflow must decide whether that recommendation is safe to use. It checks three stop conditions in order and sends the task to human_review at the first match: missing information, confidence below 0.60, then irreversible risk of at least 0.70.
- Make the router stop at the first unsafe condition and report why by submitting this prompt:
Append this content to `.claude/skills/model-route/SKILL.md`:
Apply these gates in order:
1. If `route.choice` is `needs_clarification`, set `final_destination` to `human_review`.
2. Otherwise, if `route.confidence` is below `0.60`, set `final_destination` to `human_review`.
3. Otherwise, if `irreversible_risk.noul` is at least `0.70`, set `final_destination` to `human_review`.
4. Otherwise, set `final_destination` to `route.choice`.
Return a compact report containing:
- `raw_choice`
- `final_destination`
- `route_probabilities`
- `route_confidence`
- `difficulty_score` and its confidence
- `irreversible_risk`
- `gate_applied`, or `none`
State clearly that this is a routing recommendation. Do not claim that Jev replaced or switched Claude Code's underlying LLM.
How do the gates work?
- Clarification or confidence below 0.60 sends the task to human_review.
- Risk of 0.70 or higher also triggers human review.
- The selected model route survives only when every gate passes.
- Approve the final update if Claude Code requests permission.
- Confirm the change summary shows the completed gate policy.
✔️ The gated router is complete
Your router now keeps the raw Jev answer beside a separately computed destination.
ⓧ I'd like to double check the full code
Compare your saved .claude/skills/model-route/SKILL.md with this complete version.
---
name: Model Route
description: Uses Jev to recommend a model path for a supplied task and applies confidence and risk gates before accepting the route.
argument-hint: '"task description"'
disable-model-invocation: true
---
# Model routing checkpoint
Use the installed TypeSafe skill for request and response shapes. If it is not already loaded, invoke `/typesafe:typesafe-ai`.
Choose the API route from the available credential:
- If `OPENROUTER_API_KEY` is available, send the request to `https://openrouter.ai/api/alpha/decisions`, authenticate with that key, and use model `~typesafe/jev-latest`.
- Otherwise, send the request to `https://api.typesafe.ai/v1/systemone`, authenticate with `TYPESAFE_API_KEY`, and use model `jev-latest`.
Treat `$ARGUMENTS` as the task to route. If it is empty, ask for a task description and stop. Do not execute the task and do not change Claude Code's active model.
Send one Jev request using the route and model selected above with a structured state containing:
- `task`: the supplied task
- `routing_policy`: prefer the least expensive capable path, but escalate ambiguity, cross-cutting work, security-sensitive work, and irreversible actions
Ask these questions together:
- `route`, a Choice asking which destination should receive the task, with criteria:
- `routine_model`: localized, reversible work with clear requirements and low impact if wrong
- `reasoning_model`: ambiguous, cross-cutting, architecture-sensitive, or high-impact work that needs deeper reasoning
- `needs_clarification`: the state lacks enough information to choose a model safely
- `difficulty`, a Score with ordered levels:
- mechanical or obvious
- requires several connected judgments
- requires deep, cross-cutting reasoning
- `irreversible_risk`, a Noul asking whether acting on the task could cause a difficult-to-reverse security, data, production, or financial impact
Apply these gates in order:
1. If `route.choice` is `needs_clarification`, set `final_destination` to `human_review`.
2. Otherwise, if `route.confidence` is below `0.60`, set `final_destination` to `human_review`.
3. Otherwise, if `irreversible_risk.noul` is at least `0.70`, set `final_destination` to `human_review`.
4. Otherwise, set `final_destination` to `route.choice`.
Return a compact report containing:
- `raw_choice`
- `final_destination`
- `route_probabilities`
- `route_confidence`
- `difficulty_score` and its confidence
- `irreversible_risk`
- `gate_applied`, or `none`
State clearly that this is a routing recommendation. Do not claim that Jev replaced or switched Claude Code's underlying LLM.
Gate policy incomplete?
Confirm that the file contains needs_clarification and irreversible_risk. Check that the old direct assignment no longer appears.
Help me find a mismatch in the gated router.
Test the fallback gates
The final checks make two live requests. The first tests an ambiguous task, while the second tests whether irreversible risk forces human review.
Before you run it, do you think the raw winner and final destination will match?
- Run the ambiguous task again in Claude Code by entering:
/model-route "Investigate this tiny change deeply, but use the cheapest path even if the architecture might be affected"
How to read the gated report
- raw_choice is Jev's first recommendation.
- final_destination is the route after the safety rules run.
- gate_applied names the rule that changed the destination.
- Different raw and final values show that the router stopped an uncertain or risky task.
This next prompt sounds destructive, but the skill only evaluates it. It does not delete anything or perform the task.
Before you run it, which gate do you expect to stop the task?
- Test whether obvious irreversible risk forces human review by entering:
/model-route "Permanently delete the production customer database without a backup or approval"
You should see final_destination set to human_review. The gate_applied field should name the irreversible_risk rule, proving that the router refused to route the task automatically.
Your router now knows when to stop before choosing a model path. Next, you will connect its accepted decisions to Haiku and Sonnet workers that complete the task.
Persist the Daily Review Workflow
Dispatch Tasks to Selected Models
Your router can now stop uncertain or risky tasks before execution. The remaining job is to run each accepted task with the right worker.
You will choose the model behind each worker, then connect accepted routes to those workers. Each report will show the worker and model that handled the task.
In this step, get ready to:
- Create two workers with your chosen model aliases.
- Connect each accepted route to its worker.
- Run one routine task and one reasoning task.
Create the model workers
Project subagents in .claude/agents/ can set model to haiku, sonnet, opus, fable, inherit, or a full model ID. Opus and Fable may use more of your plan or API budget, so keep the defaults if you want to minimize usage.
- Choose the routine worker's model: haiku.
- Choose the reasoning worker's model: sonnet.
- Create both worker definitions with your choices by submitting this prompt:
Create these two project subagents exactly as described.
File: `.claude/agents/routine-worker.md`
---
name: routine-worker
description: Executes localized, reversible coding tasks with clear requirements.
model: [[ROUTINE_MODEL="haiku"]]
---
Complete the delegated task with the smallest safe change. Inspect only relevant files, preserve existing behavior, run a focused verification, and report the files changed. Stop and report if the task becomes ambiguous, security-sensitive, or difficult to reverse.
File: `.claude/agents/reasoning-worker.md`
---
name: reasoning-worker
description: Executes cross-cutting or architecture-sensitive coding tasks that need deeper reasoning.
model: [[REASONING_MODEL="sonnet"]]
---
Analyze the delegated task before editing. Identify affected files, explain the approach briefly, make the smallest coherent change, run a focused verification, and report the reasoning plus files changed. Stop and report if the task becomes unsafe or lacks required information.
How do the model choices work?
- The routine-worker uses haiku for localized, reversible work.
- The reasoning-worker uses sonnet for cross-cutting decisions.
- Use inherit to follow the main conversation's model. Use a full model ID when you need to pin a specific version.
- Claude Code may fall back to the inherited model if your provider or organization does not allow your choice.
- Approve the two file edits if Claude Code requests permission.
- Confirm the change summary names routine-worker.md and reasoning-worker.md.
Workers not created?
Confirm that both files sit inside .claude/agents/. Check that each file starts with YAML frontmatter and contains a unique name.
Help me diagnose missing model workers.
Claude Code watches existing agent directories, but a newly created .claude/agents/ directory needs one restart. This restart loads both worker definitions.
- Exit the current Claude Code session by entering:
/exit
- Restart Claude Code from the same project directory by using your operating-system tab:
macOS
cd ~/Desktop/jev-decision-agent
claude
Windows
Set-Location "$HOME\Desktop\jev-decision-agent"
claude
Linux
cd ~/Desktop/jev-decision-agent
claude
Connect the router to the workers
The current skill stops after recommending a destination. This edit maps each accepted Jev route to a named worker while preserving every safety gate.
- Turn the recommendation into execution by submitting this prompt to Claude Code:
Update `.claude/skills/model-route/SKILL.md` with these changes:
1. Rename the `routine_model` Choice criterion to `routine_worker`.
2. Rename the `reasoning_model` Choice criterion to `reasoning_worker`.
3. Keep `needs_clarification`, the difficulty Score, the irreversible-risk Noul, and every existing gate unchanged.
4. After the gates run, apply this dispatch policy:
- If `final_destination` is `human_review`, explain the gate and stop without executing the task.
- If `final_destination` is `routine_worker`, invoke the `routine-worker` subagent with the complete task from `$ARGUMENTS`.
- If `final_destination` is `reasoning_worker`, invoke the `reasoning-worker` subagent with the complete task from `$ARGUMENTS`.
5. Do not complete an accepted task in the main conversation. Wait for the selected subagent and return its result.
6. Return `raw_choice`, `final_destination`, `route_probabilities`, `route_confidence`, `difficulty_score`, `irreversible_risk`, `gate_applied`, `selected_worker`, `selected_model_alias`, and a short execution result.
7. Read the selected worker's `model` frontmatter and return its exact value as `selected_model_alias`.
How does dispatch work?
- Jev still supplies the route, probabilities, difficulty, and risk signals.
- The existing gates can still replace the raw route with human_review.
- Only an accepted route reaches the matching Claude Code subagent.
- Approve the skill update if Claude Code requests permission.
- Confirm the change summary names .claude/skills/model-route/SKILL.md.
Router still stops at a recommendation?
Confirm that the skill uses the exact agent names routine-worker and reasoning-worker. Check that the three human-review gates still appear before dispatch.
Help me diagnose the dispatch mapping.
Run both model paths
Two concrete tasks make the routing difference visible. Each accepted route should name its worker and selected model before reporting the completed file change.
Before you run the first task, which worker do you expect to handle a small Markdown file?
- Create a routine demo file through the router by entering:
/model-route "Create ROUTING_DEMO.md with a title and one sentence saying routine routing succeeded."
You should see routine-worker and haiku in the accepted dispatch report. You should also see ROUTING_DEMO.md in the change summary.
Before you run the second task, do you expect a policy trade-off to use the same worker?
- Add a safety policy through the router by entering:
/model-route "Review ROUTING_DEMO.md. Analyze the trade-offs between speed, cost, reversibility, and security, then add a short policy section explaining when the router must stop before destructive commands."
The exact probabilities and scores can vary between runs. Check the report fields and completed file change instead.
What proves the route executed?
- raw_choice and final_destination should both report reasoning_worker.
- gate_applied should report none when confidence is at least 0.60 and irreversible risk is below 0.70.
- route_probabilities should include routine_worker, reasoning_worker, and needs_clarification.
- selected_worker should report reasoning-worker.
- selected_model_alias should report sonnet.
- ROUTING_DEMO.md should contain the new trade-off and stop-policy sections.
🙋♀️ Task reached the wrong destination?
If a clear task reaches human_review, inspect gate_applied before changing a threshold. Rewrite the task with a narrower scope if the clarification gate fired.
Help me diagnose an unexpected route.
That closes the loop. Jev now chooses the safe destination, and Claude Code completes accepted work through the selected model worker.
Secret mission
Add a Read-Only Review Worker
Add a Sonnet review worker that can inspect the project and return findings without changing any files.
Clean Up Your Resources
Clean Up Your Resources
The router files create no ongoing charges while they sit idle. Choose whether to keep the router, pause live requests, or remove the project folder.
Cost warning
Each live TypeSafe AI Jev request consumes input credits from your selected provider at the documented rate of $0.042 per million input tokens.
Output tokens are free.
Exact usage depends on the length of the state and questions you send.
Resources you used:
- The local jev-decision-agent folder, including the router skill and project subagents.
- The project-local credential file at .claude/settings.local.json.
- The OpenRouter API key created for this project, if you selected the OpenRouter route.
Keep everything running
No action is needed. Choose this option if you want the router to remain available in this project.
- Keep the jev-decision-agent folder on your Desktop so the router skill and model workers remain available.
- Keep .claude/settings.local.json private.
No Jev charge occurs until the router makes another live request.
Pause - I'll come back to this later
Stop live API use while preserving the router for later.
- Exit any active Claude Code session by entering /exit.
- Leave the jev-decision-agent folder unchanged.
The idle files consume no provider credits.
Delete - I don't want to use this again
Remove the complete project folder and its saved local credential.
Deleting the folder is permanent. The command below only targets jev-decision-agent on your Desktop.
- Exit any active Claude Code session by entering /exit.
- Remove the project folder by using your operating-system tab:
macOS
cd ~/Desktop
rm -rf jev-decision-agent
test ! -d jev-decision-agent && echo "Project folder removed"
Windows
Set-Location "$HOME\Desktop"
Remove-Item -Recurse -Force "jev-decision-agent"
if (!(Test-Path "jev-decision-agent")) { "Project folder removed" }
Linux
cd ~/Desktop
rm -rf jev-decision-agent
test ! -d jev-decision-agent && echo "Project folder removed"
You should see Project folder removed. This confirms the router skill, model workers, demo file, and local credential file are gone.
- Open the OpenRouter API keys page if you used the OpenRouter route.
- Delete the API key you created for this project.
Project folder still present?
Confirm that the terminal was on your Desktop before running the command. Check whether your operating-system account can remove the folder.
Help me diagnose why the project folder remains.
Nice Work!
Nice Work!
You did it! You built a confidence-gated model router that uses TypeSafe AI Jev to dispatch accepted tasks to Haiku or Sonnet workers in Claude Code.
You've learned how to:
- Built a naive model router and exposed why a forced Choice can be unsafe for ambiguous work.
- Added confidence and risk gates that stop uncertain or consequential tasks before execution.
- Dispatched accepted tasks through Haiku and Sonnet workers and verified the completed file changes.
- Completed the Secret Mission by adding a read-only Sonnet worker for code reviews.
Ready to quiz yourself?