I Banned My Agent from Writing Code from Memory
A year of tooling around an AI agent: a source ladder instead of memory, two reviewers, a gate on 'done', and the open source gor-mobile.
A few years ago, I trusted a couple of online “gurus.” Then I finally opened the official documentation and found something unpleasant: a person I trusted had been selling complete nonsense.
The worst part wasn’t that he was wrong. Everyone gets things wrong. It was how confidently he said it. I never even felt the need to double-check.
Since then, my rule has been simple: don’t take anyone’s word for it. Check the primary source.
A few years later, I caught my AI agent doing exactly the same thing 🤷
It writes with the same confidence. The problem is that its training cutoff is somewhere in the past, while Compose, Media3, Navigation, and Room have all moved on. So the agent gives me a method signature that no longer exists. Or one that never existed at all. Then it explains why this is obviously the correct way to do it.
Same pattern. The “guru” just lives in my terminal now.
Over the past year, I’ve built a toolchain around the agent that stops it from reasoning about Android APIs from memory. Some of it is open source. Some still exists only in my setup. This is the whole process, from checking documentation to the final review on a device.
The main rule
I phrase it deliberately bluntly: writing code from memory is forbidden.
Not “check the docs if you get stuck.” Not “prefer the latest documentation.” The agent has to open a source before it starts proposing a solution.
I check sources in a fixed order.
First comes official documentation. Google now has an Android CLI: the agent runs android docs search, follows it with android docs fetch, and gets a live page from developer.android.com. For Firebase, Maps, Play, and other Google products, I also have Google’s Developer Knowledge MCP connected.
If the docs lag behind the library version used by the project, the agent goes one level deeper. It opens the Gradle cache, unpacks the artifact, and inspects the JVM view of the API with javap. That isn’t always enough for Kotlin details such as suspend, extension functions, or default arguments. In those cases, it needs metadata or source code.
Sometimes the signature is fine, but the behavior is unclear. In one case, the documentation claimed that a component “controls the aspect ratio,” while the implementation I found boiled down to fillMaxSize().wrapContentSize() with no ratio being set. Only the source for that exact version explained what was actually happening.
So the source ladder is fairly simple:
- official documentation;
- the artifact version actually used by the project;
- source code.
The agent also has to say how far down the ladder it went and what it relied on. “This is usually how it’s done” is no longer an acceptable source.
First, I had to give the agent eyes
On its own, an agent is a very smart brain in a jar. It can reason about code, but it can’t see the screen, doesn’t know what’s in Figma, and has no idea whether the app still launches after its changes.
I didn’t build the surrounding toolchain from a beautiful master plan. I built it from pain. Something broke, and another tool appeared next to it.
Device and screen
Through Android CLI, the agent can build the project, install it on a device, launch the app, and take a screenshot. It can inspect the layout tree or ask Android Studio to render a Compose Preview.
A physical phone isn’t required. If there is no device, the agent can start an emulator itself.
Screenshots are where things get more interesting. The agent labels elements in the image, turns those labels back into coordinates, clicks the right button, and checks what changed. Verifying that “this button opens the correct screen” is no longer a text-only guess.
Figma
Figma is connected directly.
I used to send the agent a screenshot and then spend another ten minutes explaining what the designer meant. Now the agent opens the design itself. The result improved more than I expected. Apparently, my retelling was the leakiest interface in the whole system 😅
Browser
The browser is under agent control as well. Sometimes it is easier to open the web side once, look at the real backend response, or walk through the flow than to keep guessing from code.
Code
Language servers for Kotlin and Swift provide definitions and usages instead of approximate text matches.
I also use ast-index, a Rust tool that stores a project index in SQLite and can search symbols, calls, and implementations, as well as build call trees. In the author’s benchmarks, search was 17–69 times faster than grep. I don’t treat that number as universal, but the difference is very real on a large codebase: instead of dragging a wall of files into context, the agent gets a handful of relevant symbols.
There is a hook to keep the agent honest. In an indexed repository, a bare grep for an identifier is blocked. If you want to find a class, use the index.
That sounds like a minor optimization, but it turns into real money on a large project. Every unnecessary file the agent pulls into context tends to travel through the next requests as well.
Everything else
I have a separate MCP for iOS builds. The issue tracker is connected too: issues, comments, worklogs, sprints, and boards. Commits, releases, and release notes have their own commands and skills.
Google also publishes domain-specific skills for particular Android areas. For example, its Navigation 3 skill ran 11 times in my setup over the previous week.
I deliberately don’t hardcode that catalog into gor-mobile. Google updates it on its own schedule. There is a command that shows what is available right now and lets me install what I need.
One important caveat: giving an agent tools does not mean giving it unlimited access to everything. Permissions for devices, browsers, Figma, and the tracker still need to be scoped separately. Otherwise, all that careful work around documentation stops meaning very much.
I had plenty of tools. I still didn’t have a process
At some point, the agent could see the documentation, the device, Figma, and the codebase. It still worked in whatever order felt convenient: write half the feature today, think about the design tomorrow, and declare it done the day after without running a build.
That’s how gor-mobile started.
It is an MIT-licensed open source tool that adds my mobile development workflow to Claude Code and Codex CLI: brainstorm → plan → implement → review → verify.
I built it on top of Jesse Vincent’s superpowers. One important correction: superpowers is not an Anthropic project, although the plugin is available through the official Claude Code marketplace. I take the upstream skills and add my Android overlays, rules, and gates. The original plugin is disabled for that repository so the same skills don’t show up twice.
It isn’t a fork or a rewrite. The base workflow comes from superpowers. The Android-specific behavior and constraints are mine.
Yes, I removed TDD
The first thing I removed from the base workflow was mandatory TDD.
The original flow used the classic RED → GREEN → REFACTOR loop: write a failing test, then write the smallest implementation that passes it. It makes perfect sense on paper. In the hands of an agent, it turned into a factory for pointless tests.
Need to verify one button? The agent extracts its behavior into a separate helper because a helper is easy to test. The architecture starts serving the test instead of the task.
I’m not saying tests are useless. I removed the automatic requirement to write them every time. A test now appears when I ask for one or when the task genuinely needs one. Reviewers are not allowed to file an issue simply because a test is missing.
I fully expect to get yelled at in the comments for this section. I’ll survive 🙂
Brainstorming produces a document, not just a conversation
The workflow starts with brainstorming. The name is slightly misleading: we don’t chat for a while, pick an idea, and start writing code.
The output has to be a dated specification file. Until that file exists, brainstorming is not done.
Before it can be saved, the spec goes through two gates.
The first one looks outside the project. Every external library API used by the proposed solution must be backed by official documentation and a link. This happens before comparing approaches, not after.
That order matters. If the agent invents the “correct” architecture from memory first, documentation turns into evidence collected to defend it. Sometimes the installed version already has the composable we need, and the agent simply didn’t remember it.
The second gate looks inside the project. The agent must open reference implementations from the layer it is about to change. It cannot just list file names. It has to read the files.
Suppose the project’s data source is a one-line query. The new spec should not suddenly introduce its own retry protocol, three interfaces, and a factory factory because the agent once saw that pattern somewhere online.
Brainstorming stays on the main model. This is where the agent has to compare, doubt, and make decisions. I don’t want to optimize for cost here.
Once the plan is written, stop
This is probably my favorite part of the process.
After the spec, the agent writes the plan to a file. Then it stops.
It doesn’t create a couple of classes “while it’s here.” It doesn’t sneak in the first tiny implementation step while the context is still warm. It saves the plan, writes the current phase to progress.md, and clears the context.
On the next run, a hook picks up the progress file and knows where we stopped.
Why bother? Because the agent should not remember a long conversation. It should remember a document.
After compaction, a conversation can easily become “I think we decided to use the other approach.” A file is less ambiguous: here is the decision, here are the constraints, and here is the next step.
One model thinks, another writes
The roles are split for fairly practical reasons.
The main session owns decisions: the spec, architecture, debugging hypotheses, and the final call. Routine work can go to Sonnet: read twenty files, collect stack traces, or implement one specific plan step.
But a subtask never gets a prompt that says “do a good job.”
It gets:
- the exact plan step;
- a list of files it is allowed to change;
- one to three reference implementations;
- the command that will be used to verify the result.
It has exactly as much freedom as the implementation needs.
During debugging, Sonnet is read-only. No “I noticed a possible issue and fixed four files while I was there.”
Find the root cause first. Fix it second.
Before proposing its first hypothesis, the agent checks the documented behavior again. A useful hypothesis looks like this: “the docs promise X, while the logs and code show Z.” “Something feels wrong here” is not evidence.
Review happens while the work is in progress
I used to run one reviewer at the end and get a long list of problems, some of which had been introduced five steps earlier. Now review is part of plan execution.
After every task, Sonnet reviews the diff. In one pass, it checks both spec compliance and code quality. Mechanical changes such as DI wiring, resources, flags, and ordinary plumbing can drop down to Haiku. There usually isn’t much philosophy involved there.
The reviewer gets the same layer-specific reference implementations. If the new code’s shape conflicts with the accepted example, that is an Important finding, not a matter of taste.
If there is no example for a layer, the prompt says so explicitly. Otherwise, the agent will happily spend time searching for a file that has never existed.
When I use the heavier reviewer
Not all diffs deserve the same treatment.
Changes larger than roughly 400 lines, along with anything involving security, authentication, payments, cryptography, or IPC, automatically go to a reviewer running on the main model. It costs more, so I use it where the cost of a mistake is actually higher.
After all plan tasks are complete, the same reviewer looks at the full implementation. At that point, the question isn’t whether step 4 is correct. It is whether all the steps fit together.
Why Codex is there
At the final gate, Codex can run a separate pass with a different model.
I don’t need another reviewer that makes the same mistakes. I need one that fails differently. Claude may miss something Codex notices immediately, and the opposite is true as well.
Codex doesn’t replace the primary review. It produces a second set of findings, and the two lists are merged.
I tried running Codex after every task. It was expensive and mostly pointless. The second model kept reviewing half-built states and reporting problems that the next plan step was already supposed to remove.
Now Codex runs once, at the end. The review result cannot be shown until both passes return. Saying “I’ll call Codex now” and never actually calling it counts as a failed review.
Yes, I had to write that rule down. Agents are perfectly capable of producing a beautiful status report for work they haven’t done yet.
You have to prove “done”
The most annoying agent response looks something like this:
Done! ✅
And underneath it sits code that nobody even tried to compile.
gor-mobile cannot finish a task that way. Before the final response, the agent has to verify the result: the project builds, the app is deployed, and the relevant screen has been opened and inspected.
If the result exists only on a device, it has to be verified on a device. The screenshot isn’t there to decorate the report. It forces the agent to look at what it actually built.
That small distinction separates “the code was written” from “the task works.”
An agent can’t guess team rules
Every Android team has its own conventions. Some are fine with passing a ViewModel deeper into the tree. Others will reject that on sight. In some projects, a data source is a thin API wrapper. In others, half the application lives there.
That is why project rules are not hardcoded into gor-mobile.
They live in a separate Git repository: manifest.json, Markdown rules, and layer-specific reference implementations. You can fork the default pack, adapt it to your team, and connect it with:
gor-mobile rules use <repository-url>
From that point on, the agent compares its work to your code, not to an imaginary “average Android project” from the internet.
I think this is the only honest approach. Universal best practices end exactly where a real team’s conventions begin.
Separate rules for Compose
I went further with Compose.
There is a book called Jetpack Compose Internals that explains how Compose works under the hood. I turned my notes into a digest: nine properties of composable functions, parameter stability, state hoisting, side effects, modifiers, and code examples.
I am obviously not distributing the book itself. The repository contains only my notes and rules.
Before touching an @Composable, the agent reads that skill. After that, I see far less improvisation such as passing a ViewModel through half the component tree.
Then I looked at the numbers
Claude Code can show how often each skill ran and how many tokens it used. I opened the report out of curiosity and got a fairly honest review of my own work.
At the time of this snapshot, the top of the list was exactly what I built the workflow for:
brainstorming— 205 calls;writing-plans— 178;systematic-debugging— 162;subagent-driven-development— 118;requesting-code-review— 91;- the Android CLI skill — 72.
So the process is actually running. It isn’t just a nice diagram in a README.
The bottom of the list was interesting too.
The parallel agents skill ran once in two months. The skill-writing skill ran twice.
It is mildly painful to look at a tool you carefully built and discover that nobody needs it, including you. But now it is a number, not a feeling. I can delete it or redesign it without telling myself stories.
Not every tool needs to run every day. Figma matters when a new design arrives. A language server becomes especially useful in an unfamiliar module. These tools sit ready instead of generating the appearance of activity.
One more number made me happy: in this version, all fourteen skills occupy about 1,300 tokens in the persistent context. Each individual skill costs between 22 and 250 tokens.
That works because of progressive disclosure. Only a short “when to call me” description stays in context. The full skill body is loaded when the skill is invoked.
Without that, the whole setup would eat the context window before the first line of code.
What you can try today
There are currently two open source parts.
gor-mobile
This is the workflow itself: fourteen skills, Android overlays, two reviewers, gates, Android CLI, ast-index, project rules, and the Compose skill.
It works with Claude Code and Codex CLI. At the time of writing, the current version is 0.3.6. The honest status is pre-release. I use it actively on my own projects and keep changing things.
Install it on macOS with:
brew install gorban-dev/gor-mobile/gor-mobile
gor-mobile setup
cd ~/code/my-android-app
gor-mobile init
Or use npm:
npm install -g gor-mobile
gor-mobile setup
gor-mobile init
setup prepares the machine once: Android CLI, rules, hooks, and Codex integration. init installs the Claude Code workflow into a specific repository.
There are deliberately no automatic commits. Every change stays in the working tree. You review git diff and decide what to commit.
gor-dev-plugins
This is a separate marketplace for tools that aren’t part of the workflow itself but save time in day-to-day work.
swagger-android turns an OpenAPI specification into Kotlin models: data classes using kotlinx.serialization, data-to-domain mappers, and enum mappers. It follows your naming conventions instead of whatever the agent happened to dream up that day.
yandex-tracker is a local MCP server with 30+ tools covering the Yandex Tracker API. Issues, comments, worklogs, checklists, sprints, boards, transitions, and attachments. It also includes an agent for standups and sprint planning.
Figma, browser control, language servers, iOS builds, and Firebase are not part of these repositories. They are existing third-party plugins and MCP servers that I connected to my own environment.
The next problem is project memory
Right now, the agent remembers the current task fairly well because it has files: the spec, the plan, progress.md, and the project rules.
It still doesn’t remember the project.
Six months ago, we might have rejected an approach and carefully documented why. Then a new task arrives, and the agent proposes the same thing again. Or we debug the same crash twice because the conclusions from the first investigation are buried in an old chat.
I want a different kind of memory:
- we already tried this approach and rejected it;
- we have seen this crash before, and this was the cause;
- this team does things differently;
- this solution worked in a similar module.
I borrowed the idea from Hermes Agent. Their memory is implemented through pluggable providers, ranging from local SQLite with full-text search to a knowledge graph. Before a turn, the provider retrieves relevant context. After a session, it stores new conclusions.
I like that there isn’t one hardcoded implementation. One project may be fine with a local database. Another may need shared memory for the whole team.
I want to build something similar for gor-mobile. To be clear, this is only a plan. There is no code yet.
Firebase is another item on the list. The MCP is already connected, but the workflow doesn’t know how to use it. I want the agent to open a crash, verify the expected behavior in the documentation, and then search for issues with the same stack trace.
I’ll get to it eventually. I just have to avoid asking the agent to implement it from memory 😂
If you have an Android project and you’re also tired of agents inventing APIs, try gor-mobile and tell me what breaks.
A useful bug report is genuinely more valuable to me right now than another star.