The starting problem

VizMail is a MAUI .NET desktop app built for one specific purpose: classifying emails semantically so you don't have to sort them manually. The original idea has nothing to do with exposing an API — it's about eliminating inbox chores.

The HTTP API came later, as a natural extension: if an AI agent can read and act on emails directly, why keep a human in the loop for repetitive actions? The data — inbox, threads, labels, attachments — lives inside a local .NET process. For an agent to access it, an API surface is required.

The question wasn't whether to build this API, but how to build it without spending three weeks on it.

The method: one ticket, one feature

Every feature was broken down into an atomic ticket in KittyClaw. One ticket = one feature = one endpoint or one fix. No catch-all tickets, no "global refactoring."

The workflow for each ticket:

  1. Programmer picks up the ticket, writes the C# code in LocalHttpServer
  2. QA-tester runs the existing tests, writes new ones, checks for regressions
  3. Committer commits and opens the PR
  4. Evaluator scores the deliverable quality

KittyClaw orchestrates this chain automatically. When the programmer finishes, the qa-tester is triggered. When tests pass, the committer takes over. Zero manual coordination.

Ticket KittyClaw programmer C# code qa-tester tests committer PR Done ✓

1. trigger 2. implement 3. verify 4. ship

The numbers

Thirty-four tickets delivered. Six hundred and forty-two tests. Zero regressions detected after merge.

What stands out is the steady progression. From the initial scaffolding (ticket #1) to batch operations (ticket #34 — mark_read/mark_unread in bulk), each iteration laid a brick without breaking the previous ones. The qa-tester replayed the full test suite on every PR — that's not a principle, it's what the history shows.

Average time from ticket open to PR? Under an hour for standard features. Bug fixes (tickets #7, #25) sometimes took longer, but because of diagnosis, not implementation.

The patterns that emerged

A few conventions stabilized over the course of the tickets — not by upfront design, but by necessity.

Pagination with before=. Unbounded email listings can blow an agent's context. The before={timestamp} parameter allows paginating backward in time with no server-side state. Simple and predictable.

Batch max 500. Batch endpoints (/batch/mark_processed, /batch/mark_read) accept at most 500 IDs per call. An arbitrary limit at first, it became convention — large enough to be useful, small enough to stay traceable.

MailState vs SQLite. MailState is the in-memory view of emails synced from IMAP — fast, volatile. SQLite holds local metadata (labels, processed, starred). An endpoint modifying a label writes to both. An endpoint reading a list queries MailState. This separation held from ticket #4 to ticket #34.

Label idempotency. Adding an already-present label returns no error. Removing an absent label doesn't either. Agents call without tracking current state — the API absorbs duplicates.

What this changes

The programmer didn't "code" VizMail. It executed precise specs in a controlled environment. The real intellectual work was in the ticket descriptions — the angle, the edge cases, the expected behavior. Once the spec was clear, the implementation was mechanical.

This isn't a criticism — it's an observation. The intellectual work shifted toward ticket design. Automated QA caught regressions that fast implementation would have otherwise missed. And the committer kept the git history clean without any human involvement.

Thirty-four features in a matter of days, with solid test coverage. The method works when the specs are honest.


VizMail is a desktop email management app built by Ekioo. Discover the Ekioo method →