For the past nine months, I’ve been building Grinta, a local-first autonomous coding-agent runtime.
A lot of the work has gone into the
obvious hard problems: recovery, long-running execution, context management, code navigation, editing, validation, and preventing the agent from confidently declaring success after producing a plausible but broken diff.
But one of the most noticeable improvements I saw recently came from something much less glamorous.
I simplified the tools.
Not the workflow.
Not the model.
Not the autonomy level.
I did not remove important capabilities.
I changed the way the action space was presented to the model.
And the agent became noticeably better.
The problem was not missing capability
Grinta already had a fairly broad code interaction stack.
The agent could:
search the repository,
read files and ranges,
inspect symbols,
use LSP-backed navigation,
work with Tree-sitter,
perform AST-aware edits,
run commands,
inspect test output,
and validate its own work.
From a traditional software-engineering perspective, this sounds fine.
The API surface is powerful.
The agent has what it needs.
But LLM agents do not experience tools the same way human developers experience libraries.
A human developer can inspect documentation, understand subtle overlap between functions, form abstractions over time, and remember why two nearly identical APIs exist.
An agent sees a serialized tool schema inside its context.
That schema is part of its reasoning environment.
I think I underestimated the importance of that for a long time.
My tool layer had become too clever
As Grinta evolved, I kept adding more precise operations.
Different editing cases had different primitives.
Navigation, reading, raw editing, symbol editing, and multi-file operations gradually accumulated their own abstractions.
Each addition made sense locally.
That is how complex systems usually grow.
You solve one failure mode, add a more specialized path, and move on.
Eventually, however, I noticed something uncomfortable.
The agent had many capable tools, but the boundaries between them were not always obvious.
The problem was not:
“Can the agent perform this action?”
The problem was:
“Is it immediately obvious which action the agent should choose?”
Those are very different questions.
A tool can be technically well designed and still create unnecessary reasoning overhead for an LLM.
I simplified the action space
I reworked Grinta’s code manipulation interface around a smaller and clearer set of responsibilities.
The current high-level editing stack looks roughly like this:
create for creating files or symbols,
read for files, ranges, or symbols,
replace_string for controlled textual replacements,
replace_symbol for AST-aware symbol replacement,
multiedit for atomic multi-file changes,
edit_symbols for structured AST-aware editing.
The important part is not the exact names.
The important part is that each tool now has a clearer conceptual boundary.
I also improved separation of concerns in the implementation and simplified the schemas exposed to the model.
The underlying capabilities were largely preserved.
I did not introduce a new planner.
I did not add another agent.
I did not change the overall execution workflow.
I mainly made the action space easier to understand.
Then I ran Grinta on a Raft task
I tested the updated system on a Raft-based key-value store task.
This was not a toy “write a function” benchmark.
The agent had to navigate an existing codebase, understand multiple components, make coordinated modifications, run tests, inspect failures, and continue iterating.
I had already run Grinta on similar work before.
After simplifying the tools and cleaning up the responsibility boundaries, the execution felt different.
The agent was more decisive.
It spent less time bouncing between equivalent operations.
Tool selection looked more coherent.
The execution path contained less unnecessary friction.
And it completed the task successfully.
I ran it again.
It succeeded again.
What surprised me was that I had not changed the high-level workflow.
The agent was still following the same general autonomous loop.
The improvement seemed to come from making the interface between the model and the runtime less ambiguous.
Tool schemas are part of the reasoning environment
This changed the way I think about agent tools.
In traditional software design, we often evaluate an API through concepts like:
capability,
composability,
abstraction,
reusability,
and implementation correctness.
Those still matter for agents.
But there is another dimension:
reasoning cost.
Suppose an agent has five tools that can all partially solve the same task.
A human engineer may consider that flexibility.
For a model, it may mean an additional decision problem before the real problem even begins.
Which tool should I use?
Are these two operations semantically different?
Will one preserve structure?
Should I read the file first?
Can this tool resolve symbols automatically?
Does this operation accept a range or the entire file?
Every ambiguity consumes reasoning capacity.
The model is not only solving the user's task.
It is also continuously interpreting the runtime you built around it.
This led me to a simple idea:
The tool schema is part of the prompt, even when we do not think of it as prompt engineering.
In some ways, it may be more important.
The system prompt tells the model what it should do.
The tool layer defines what the model believes it can do.
Maybe we over-optimize prompts
The agent ecosystem spends a huge amount of time discussing system prompts.
I have done it too.
Grinta has gone through multiple prompt revisions, autonomy rules, routing instructions, examples, critical constraints, and model-specific variants.
Some of those changes genuinely mattered.
But I am becoming increasingly skeptical of endlessly polishing prompt prose while ignoring the structure of the action space.
If a tool interface is ambiguous, a stronger instruction may only partially compensate for it.
You can write:
“Always prefer the most precise structured editing tool.”
But if three tools appear equally precise to the model, the instruction does not solve the underlying problem.
The runtime itself is creating uncertainty.
I now think agent developers should ask more often:
Can I remove a decision from the model?
Not by reducing autonomy.
By making the correct operation structurally obvious.
This is not a controlled result
I want to be careful here.
I have not proven that simplified tool schemas improve coding-agent performance.
The Raft runs are not a controlled benchmark.
There are many possible confounding variables:
model nondeterminism,
task-state differences,
context variation,
execution timing,
and other runtime changes.
It would be dishonest to take a few successful runs and claim a general result.
What I have is an engineering observation.
A strong enough observation that I now want to test it properly.
The next step is to run the same task set with the same model across different harness variants.
For example:
Current simplified tools
vs.
Previous broader tool interface
while keeping the rest of the runtime as stable as possible.
I want to measure more than pass rate.
I am interested in:
tool-selection entropy,
redundant operations,
repeated reads,
edit retries,
validation failures,
recovery events,
context growth,
time to first meaningful edit,
and premature completion attempts.
Grinta already produces a structured runtime event stream, so most of this should be measurable.
The harness may be doing more work than we think
The longer I build autonomous agents, the less comfortable I am with attributing behavior entirely to the model.
The model matters enormously.
Obviously.
But between the model and the final result sits an entire software system:
context construction,
tool schemas,
execution policy,
memory,
code navigation,
editing primitives,
recovery,
validation,
and completion logic.
Change that system and the same model can behave very differently.
I experienced this directly.
I did not make the model smarter.
I made the environment easier to reason about.
And the agent got better.
At least, that is what the current evidence suggests.
Now I want to measure how much of that improvement is real.
I’m building Grinta in public here:
GitHub: github.com/josephsenior/Grinta-Coding-Agent
I’ll probably write next about a 4h33 autonomous run that produced 16,393 runtime events, and what those traces revealed about long-horizon coding-agent failures.
Top comments (0)