Build AdaL
KINDAreplaces $20/mosaves $240/yrback to the verdict
A terminal coding agent whose hard problems are not the model calls but the harness around them: tools that cannot leak a secret or escape the repository, sessions that resume after a crash mid-tool-call, delegation that gives each sub-agent only what it needs, and a verification loop the agent cannot satisfy by weakening the tests.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the agent, its tools and the test suite.
Get it python.org or your package manager. open ↗
Verify
python3 --version prints 3.12 or higher - API keyper token, and a long task can be several pounds
Why Routing across model families is the point, and a single provider hides every seam the design depends on.
Get it Per-token billing accounts with two of Anthropic, OpenAI and Google, keys in .env, and note the current published prices for the price file.
Verify
chat() returns from both providers and the recorded cost matches the price file - have readyfree
Why The verification loop is only meaningful against tests that can actually fail, and a toy project with one passing test exercises none of it.
Get it A project of your own with at least fifty tests and a configured test command.
Verify
The test command runs from the project's configuration and at least one test can be made to fail - have readyfree
Why One where the implementation is wrong and the test is right, and one where the only way to go green is to weaken the test, are the two cases the loop must tell apart.
Get it Write both by hand in the repository's test style.
Verify
The first ends fixed and the second ends stopped with the diff shown - decidefree
Why Cost is computed from it and a ceiling is derived from it, and a number remembered from a blog post is wrong more often than not.
Get it Copy each provider's current published prices per million tokens into prices.json with the date checked, and decide a per-session and a daily ceiling.
Verify
prices.json has a checked_on date within the last month and both ceilings are set
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- sessions/<id>.jsonl · one event per line: kind, at, step, role, content, tool, args, result, tokens_in, tokens_out, cost_minor, model - plan(step_n, description, acceptance_command, tools_allowed, state, attempts) · written by the planner, run one step per sub-agent - scratchpad.md · the shared record sub-agents append to, and the only context a later sub-agent inherits - approvals(at, command, cwd, decision, session_id) · every shell command shown, and what you said - denied_paths · .env, keys, credentials and anything matching your patterns, refused by read_file before the model sees a byte - prices.json(provider, model, input_per_million, output_per_million, checked_on) · maintained by you from the providers' published prices, dated, never assumed - A tool call and its result are two events, so a crash between them shows on resume as a call with no result rather than as a call that never happened.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
ANTHROPIC_API_KEYsecret | optional | sk-ant-...redacted | One of at least two providers. Never read by the agent's own tools. |
OPENAI_API_KEYsecret | optional | sk-...redacted | One of at least two providers. Never read by the agent's own tools. |
GOOGLE_API_KEYsecret | optional | AIza...redacted | One of at least two providers. Never read by the agent's own tools. |
AGENT_DEFAULT_MODEL | required | anthropic:claude-sonnet-4-5 | Overridden per session by --model. Provider prefix selects the module. |
SESSION_DIR | required | .agent/sessions | Append-only JSONL, flushed per event. Add it to .gitignore. |
DENIED_PATTERNS | required | .env,*.pem,*.key,**/credentials* | Refused by read_file before the model sees a byte. Err wide. |
SESSION_COST_CEILING_MINOR | required | 500 | The run refuses the call that would exceed it, before making it. |
PRICES_PATH | required | ./prices.json | Your dated copy of the published prices. Stale beyond a month warns. |
The build, in order
Tools that cannot reach what they should not
The agent reads, writes and runs inside the repository and nowhere else, and never sees a secret.
Keys from .env, model switch by flag, no provider code anywhere else.
Files
providers.pyconfig.pyResolve symlinks first; test with a planted fake key.
Files
tools.pyredact.pytests/test_secrets.pyPrint it, wait for y or n, record the answer.
Files
shell.pyapprovals.py
done when · tick each as it passeswatch out- The first thing a model does in an unfamiliar repository is read the configuration, and .env is configuration, so within minutes your provider keys are in a prompt on somebody else's server and in your own session log. Deny the files by name and pattern inside read_file, redact key-shaped strings from every tool result, and test both with a planted fake key.
Sessions that resume after the worst moment
A crash mid-tool-call is visible and recoverable, and every turn's cost is known.
The crash that matters is the one after forty minutes.
Files
session.pyA call without a result did not happen, as far as you can know.
Files
resume.pytests/test_resume.pyNever a number in code; you maintain the file and its date from the published prices.
Files
cost.pyprices.json
done when · tick each as it passeswatch out- Writing the session to disk at the end of the run means the run that crashed after forty minutes of work leaves nothing, and the one that crashes is always that one. Append and flush per event, and on resume treat a tool call without a result as never having happened, because you cannot know whether it did.
A planner whose steps can be checked
Work is split into steps that each state how you would know they are done, and each runs in a fresh context.
A step that cannot fail cannot succeed either.
Files
planner.pytests/test_plan_shape.pyThe step, the scratchpad, the named files. Assert it.
Files
subagent.pytests/test_context.pyAn agent that retries forever is spending your money on the same mistake.
Files
orchestrator.py
done when · tick each as it passeswatch out- Handing each sub-agent the entire conversation so far defeats the reason for splitting the work, costs a full context per step, and lets step six get confused by step two's abandoned approach. Give it the step, the scratchpad and the named files, and make the planner produce steps you can refuse when an acceptance command is missing or amounts to 'looks good'.
Verification the agent cannot game
Edits are tested automatically, and the only way to pass is to make the code work.
From the project's own configuration, not a guess at the command.
Files
verify.pyA loosened assertion is the pass that is not a pass.
Files
verify.pytests/test_no_test_weakening.pySo the final report is what happened rather than what was planned.
Files
report.py
done when · tick each as it passeswatch out- Given a failing test and permission to edit, a model will sometimes loosen the assertion, mark the test as skipped or delete the file, and the run then reports success with the tests green. Diff the test files after every retry, show the diff, and treat a retry whose only change is to the tests as a failure that needs a human.
Ceilings, a fourth provider, and where this losesproduct builder
A run cannot outspend you, another provider is one file, and the README says where a tuned harness wins.
Estimate from context size; a warning after the bill is not a ceiling.
Files
cost.pytests/test_ceiling.pyIt proves the provider seam is real and documents the shape.
Files
providers.pytests/fake_provider.pyREADME.mdRecovery rate on long tasks is where this loses, and saying so is the point.
Files
README.md
done when · tick each as it passeswatch out- A ceiling checked after the call has already been made is a bill with a warning attached, and on a long planning step with a large context a single call can be most of the budget. Estimate the next call from the context size and refuse before making it, and make the refusal say what it would have cost.
That is the whole plan for AdaL. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Browser-based verification of a running application.
- A graphical interface.
- Team accounts and organisation controls.
- Any hosted service.
- harness tuning: the planning, recovery and verification work that separates a demo agent from one that finishes long tasks
- browser-use verification of the app you just changed
- review clustering that groups a large diff into risk-ranked units
- one bill across every frontier provider instead of six metered API accounts
- SSO, SAML/SCIM, zero data retention and org-level model deny lists
- A read-only exploration mode for an unfamiliar repository at a fraction of the cost
- Parallel sub-agents for independent steps, once the scratchpad has locking
- A local model as a provider for the cheap steps, which the one-function seam already allows
Need the files? The project pack on the verdict page hands your agent the whole brief · more dev tools.