DEV Community

Cover image for I Stopped Testing APX in the Wrong Checkout
Manuel Bruña
Manuel Bruña

Posted on

I Stopped Testing APX in the Wrong Checkout

I Stopped Testing APX in the Wrong Checkout

I like worktrees. They keep changes isolated, make review cleaner, and let me move fast without turning one branch into a junk drawer.

But APX made me stop trusting a worktree as a runtime witness.

That sounds like a small distinction. It is not. In APX, the daemon is the product. The CLI, the web panel, Telegram, and the other surfaces all talk to that one long-running process. And that process runs from the MAIN checkout, not from whichever worktree I happen to be editing.

That changes the meaning of "I tested it."

A green unit test in a worktree tells me the code can compile and behave in isolation. It does not tell me the running daemon loaded that code. A fresh restart tells me the daemon restarted. It still does not prove the path I changed actually executed. If I stop there, I can spend an hour convincing myself a fix is broken when the real issue is simpler: I am looking at the wrong checkout.

The trap is easy

Worktrees feel like they should be the truth because they are the place where the edit lives. In a normal app, that assumption is often good enough. In APX, it is wrong more often than I want.

The reason is structural. The daemon is long-lived. It boots once, keeps state, and serves every surface. A code change does nothing until the daemon restarts. That rule is already expensive enough. The worktree/main-checkout split makes it worse, because now I can have two versions of the repo on disk and only one of them matters to the live process.

That means I can easily do this:

  1. Patch code in a worktree.
  2. Run tests there.
  3. Restart the daemon from the wrong place.
  4. See no change.
  5. Blame the patch.

The patch was never the problem. My mental model was.

What I check now

I keep the flow boring on purpose.

First I check which checkout the daemon is actually running from:

ps -o command= -p "$(pgrep -f 'src/host/daemon/index.js' | head -1)"
Enter fullscreen mode Exit fullscreen mode

That tells me whether the live process points at the main checkout or something else. If I do not know that, I am guessing.

Then I restart from the main checkout:

apx restart
Enter fullscreen mode Exit fullscreen mode

That step matters because APX keeps the JS it booted with. Restarting from the wrong directory is how I end up testing stale code while feeling productive.

Then I verify that the restart actually happened:

curl -s 127.0.0.1:7430/api/health
apx daemon logs --tail 30
Enter fullscreen mode Exit fullscreen mode

The health check tells me the process is alive again. The logs tell me whether it booted cleanly and whether the pieces I care about actually came up. I do not treat either one as proof by itself.

Only after that do I exercise the real path I changed. If I touched the agent loop, I run apx exec or apx run. If I touched an API route, I hit the route. If I touched the web panel, I open the screen and click through it. The point is to make the live system answer the question, not the worktree.

Why this matters more in APX than elsewhere

APX is opinionated about where things live.

Project context belongs in .apc/.
Runtime state belongs in ~/.apx/.
The daemon owns the state that matters.
The other surfaces are clients.

That is good design, but it also means mistakes can hide in different layers at once. A worktree may contain the code I want. The daemon may still be serving older JS. The runtime state may still reflect a previous session. If I do not separate those layers in my head, I waste time debugging the wrong layer.

This is also why the repo keeps repeating the same warning in different places: the daemon runs from the main checkout, a worktree-only fix never reaches it, and a clean restart is not enough if I restarted the wrong copy. Those are not philosophical notes. They are the scars.

What changed in my day-to-day

The main change is that I stopped using worktrees as if they were runtime truth.

Now I use them for what they are good at:

  • isolating a branch
  • keeping review clean
  • preventing half-finished edits from bleeding into other work

But I no longer assume they are the place where verification happens.

Verification happens where the daemon lives.

That shift is small enough to miss and large enough to save time every week. It turns a vague feeling of "maybe the fix did not stick" into a concrete checklist. It also makes reviews better, because I can say exactly what I ran and what returned, instead of hand-waving about a branch that never touched the live process.

The deeper lesson

I think this is the real value of building your own runtime: it punishes vague thinking.

A spec can let you stay abstract. A runtime cannot. The runtime asks where the process starts, which checkout it sees, which files it reads live, which files it only reads at boot, and what state survives a restart. It does not care about my intent. It cares about what is actually wired up.

That sounds annoying until it saves you from a bad conclusion.

APX has made me more suspicious of any workflow that feels clever but cannot answer one blunt question: "which process is actually running this code?" If I cannot answer that, I do not know enough yet.

So I keep the workflow plain. Main checkout for the daemon. Worktrees for editing. Restart before I trust anything. Health check. Logs. Then the real path.

It is not fancy. It is just hard to fool.

And for APX, that is the point.

Top comments (0)