# Trusting Agent Commits
In my [previous post](./reconnecting-dots.md), written shortly after I left clinical training to work as a software engineer, I mentioned that a major refactoring of ZotLit was under way. Seventeen months later, ZotLit 2.0 is out, and version 2.2 shipped at the end of October. But there is a fact about how this version came together that might make some developers uncomfortable: I did not write most of the code. Coding agents did. In October 2026 alone, roughly 600 commits landed on our main branch. An even more uncomfortable fact is that I do not review every pull request myself anymore.
For an open-source tool that researchers and students rely on to manage their literature, handing the keyboard over to AI might sound reckless. If I am not reading every diff, why should anyone trust the software? How do I trust it myself? The answer is not that I found a way to inspect everything faster. Instead, I stopped trying to police agents with human attention, and spent that effort building an environment where bad code cannot easily survive.

## What learning used to cost
To explain why I work this way now, I have to go back to how ZotLit started. In early 2022, I was still a medical student in China. My days were consumed by hospital routines, exams, and rote memorization, and writing code at night was my sanctuary. I wanted to see if it was possible to read my local Zotero library directly inside Obsidian so I could take literature notes without constantly switching windows. The immediate obstacle was that Zotero locks its SQLite database while running. Whenever I tried to query it from Obsidian, SQLite threw a busy error. There were no LLMs back then to suggest workarounds. My first approach was crude: whenever a read failed, ZotLit copied the entire database to a temporary location and queried the duplicate. It worked, but it was slow and clumsy. I still remember staying up late in my dorm room, digging through SQLite documentation and forum threads until I found an obscure post mentioning the immutable flag. Even then, our Node driver did not support URI filenames, so I had to fork it, patch it, and recompile native binaries for five different platforms every time Obsidian updated Electron.
I learned an enormous amount through that manual struggle, but it took months of tedious labor to solve what was essentially a plumbing issue. With coding agents, exploring new APIs takes minutes. More than that, if you are curious, you can dig into the details of how each component works and let the agent act as a companion and coach, explaining internals and helping you understand new territory faster than ever before. The key issue is always the person behind the agents. When you pair curiosity with these tools, the learning curve flattens dramatically. But the moment you start using agents to implement entire features, you run into an entirely different problem: human exhaustion from sheer throughput.
## Tautological tests and layered rules
When I started rebuilding ZotLit from an empty repository for v2, I tried to manage agents the traditional way. I grilled them on designs, reviewed pull requests line by line, and merged them myself. It quickly exhausted me. Human attention cannot keep pace with agent throughput. If an agent opens three pull requests while you are having lunch, reading every diff turns you into a permanent bottleneck. Worse, green test suites turned out to be deceptive. Agents are very good at writing tautological tests, tests that basically state that one plus one equals two, mirroring the implementation so closely that they can never fail. At the company I work for, there is a legacy codebase written mostly with AI where the unit test suite takes twenty to forty minutes to run and proves very little. In October, when I audited the test suite in ZotLit's query package against our testing policy, I found many tests of this kind. Some calculated expected results by running the query engine a second time and asserting that it matched itself. Others asserted that values were merely defined when the exact rows were known, or passed a mock into a function and checked that the same mock came back. I deleted over nine thousand lines of test code and wrote about thirteen hundred lines in their place that verified real behavior.
```ts
// Before: passes as long as the query returns anything at all
expect(rows.length).toBeGreaterThan(0);
// After: the seed data says exactly which papers have no usable PDF
expect(rows.map((row) => row.indexedKey)).toEqual([
"ALS2CNFL", "BK2MNTH2", "CHP2YEAR", "CNF2TEXT", "RPT2NDTE",
"TIE2AAAA", "TIE2BBBB", "TIE2CCCC", "UNI2CDE2",
]);
```
That audit made it clear that you cannot rely on human review to catch bad code, and you cannot rely on agents to verify themselves through unit tests alone. A post by Lauren ([@poteto](https://x.com/poteto/status/2118613050462728659)) captures what I had to learn: you do not need an elaborate software factory, you need good software engineering and to spend tokens in the right place, pushing enforcement to the most deterministic layer possible. The post visualized this as an inverted pyramid, placing the codebase design and architecture at the widest level, followed by static analysis, automated verification, prose rules, and finally a narrow band of human review.
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">hot take: you don't need a software factory, and i don't love the term. it's just marketing<br><br>all you need is good software engineering and investing your tokens in the right places. the more deterministic the better (and token efficient) <a href="https://t.co/MYZZHfulQt">https://t.co/MYZZHfulQt</a> <a href="https://t.co/k2Mm4Jl106">pic.twitter.com/k2Mm4Jl106</a></p>— lauren (@poteto) <a href="https://x.com/poteto/status/2118613050462728659?ref_src=twsrc%5Etfw">November 6, 2026</a></blockquote>
In an agentic workflow, code quality is the context that agents learn from. Agents are fast learners and relentless copycats. If a codebase has messy abstractions, they copy the mess and multiply it across new features. But if you constantly refactor and keep module boundaries clean, they copy the clean patterns instead. About one in nine commits on ZotLit v2 is a refactor, and our core services are organized into deep modules with single entry points. Alongside the architecture, we rely on different layers of rules rather than stuffing everything into one giant instructions file. Root configuration files stay short, while specific policies and custom lint rules enforce constraints deterministically. When an agent made the mistake of bumping an internal format version three times during one feature, that became an automated lint check. In our setup, tests are forbidden from mocking internal modules or reading source files as text. If a check can run in the compiler or linter, it never belongs in a prompt.
## Real applications and orchestrators
The same principle applies to testing. Isolated unit tests still have their place for pure logic like our query builder and syntax parser, but user-facing bugs almost always happen at the seams between Obsidian, Zotero, and the filesystem. In the past, plugin developers rarely wrote end-to-end tests because driving desktop applications was exhausting manual labor. Because agents can write the test code, I invested in an end-to-end testing suite that drives real applications on my local machine. It connects to a running Obsidian instance through its socket and talks to a real, pinned Zotero instance through Firefox's remote debugging protocol, running without stealing window focus. When an agent adds a feature, it executes real commands inside Obsidian and pulls data from live Zotero database instances. If an API contract breaks or a query fails against live data, the test actually fails.
Running multiple agents against a single desktop environment introduced its own problems. At first I used a simple lock file, but after I accidentally held the lock for four hours with a background sleep command, an agent timed out and reported it was blocked with no idea for how long. I replaced that with an admission queue, so an agent that cannot acquire the desktop is told who holds it and exits cleanly rather than thrashing code that was already working.
This coordination problem and the exhaustion of managing throughput are exactly what ideas like DeepSeek's [superagent prototype](https://github.com/deepseek-ai/dsh-superagent-prototype) and Matt Pocock's [chief-of-staff skill](https://github.com/mattpocock/skills/blob/5beb35b040e3de97e54fa9e386d7e42d15c295c2/skills/in-progress/chief-of-staff/SKILL.md) are trying to address. The concept is to let an orchestrating agent arrange subagents to carry out the work, removing the human as the operational bottleneck. But removing the human bottleneck makes the underlying infrastructure, the clean architecture, the lint rules, the real-app tests, and the coding standards, matter even more. Without those constraints, an orchestrator simply multiplies errors faster.
Building this infrastructure required significant effort, but it paid off immediately. At the beginning of October, I began working on ZotLit Query, an engine to query the local Zotero database directly from Obsidian, complete with a custom filter syntax parser, a table view, and live query blocks inside markdown notes. Less than four weeks later, on October 30, it shipped in version 2.2. Building a fast, reliable query engine and user interface in a single month as a solo maintainer would have been impossible without this harness. The agents could write code and verify it against live software while I focused on higher-level problems.

## Decisions that belong to humans
If agents write the code, run the tests, and review each other against the standard, it is reasonable to ask what is left for the person behind them. The short answer is judgment. An agent can produce code at astonishing speed, but without clear steering it will race in the wrong direction just as quickly. Writing syntax is no longer the bottleneck. ZotLit is built for academic users, which means it has to be intuitive, dependable, and genuinely useful in daily research workflows. Deciding how to achieve that, which architecture and framework to choose, and when to refactor are choices that still belong entirely to a human.
The origin of ZotLit Query is my clearest example of this. Earlier this autumn, I asked an agent to help build an easier way to handle literature note templates. I had it grill the design, and it produced a working prototype of a template directory. In practice, it was verbose, difficult to search, and never satisfied our original purpose. Seeing that failure pushed me to step back and examine how people actually worked. Academic users were spending hours writing complex templates because they wanted their Zotero metadata available inside Obsidian for Dataview queries, status tracking, and dashboards. The template was merely a fragile bridge between two apps. Because ZotLit already had direct access to SQLite, we did not need a better template workbench. We could eliminate that labor entirely and let users query their library straight from their notes.

Interrogating an agent goes poorly when you do not understand the core problem yourself. If you give it a superficial goal, the model will simply grill you on narrow implementation details that might not even matter. But once you look at the problem from first principles and know the tension you want to solve, the agent becomes an exceptional partner. It can dig out the real conflicts with you, suggest alternative solutions, and think outside the box.
We still build software for humans, and it is humans who use it. That leaves the engineer with two fundamental responsibilities. The first is constructing a reliable system that produces output you can trust: the architectural boundaries, the real-app tests, and the deterministic rules that keep agents on track. The second is making the product and architectural decisions that cannot be delegated: choosing the stack, weighing the trade-offs, and deciding what is worth building. Saving the labor of writing code does not make the human irrelevant. It simply frees us to focus on what senior software engineering was always supposed to be.