Field notes

Decisions · Chapter 04

The assistant is a user, not a feature

The chat box is the least interesting way to put AI in a product. We gave the assistant the same door everyone else uses.

By the time we got to the AI work, the product already existed. Boards, notes, habits, expenses, sharing, a family suite, all of it running offline on a real local database.

That order was not an accident, and I think it is the reason the assistant turned out to be worth anything.

The shape almost everyone builds

The default way to add AI to an existing product is to bolt a chat panel onto the side and wire it to a private back door. The panel gets its own endpoints, its own queries, its own slightly different idea of what a card is. It can do maybe fifteen things, chosen by whoever built the panel.

It demos beautifully and it rots immediately, for one structural reason: the assistant's path through your system is not the path your UI takes. Two implementations of "create a card" drift apart the moment either one changes. The panel starts lying by omission, because nobody updated its private copy of reality when a field was added last week.

I did not want to maintain two products.

The other shape

So the assistant does not get a back door. It gets the front door, and the front door is a tool surface that anything can call.

Every capability in the product is exposed as a named tool with a schema. Around a hundred and sixty of them now. Creating a card, splitting an expense, logging a habit, drawing on a whiteboard, reading a note by pages when it is too long to read at once. The built-in assistant calls those tools. So can anything else that speaks the same protocol, which in practice means any AI client the user already trusts.

That reframing does more work than it looks like.

It means the assistant is a user of the product, with the same permissions model, the same entitlement gates, the same workspace scoping as a person sitting at a keyboard. When it tries something it should not be allowed to do, it is refused by the same code that refuses a human. There is no privileged path to forget to secure.

It means the surface is testable. A tool with a schema can be called deterministically, without a model in the loop, which turns "does the AI work" from a vibe into a test suite.

And it means the product does not have to be the only place you use it. If somebody prefers to drive their workspace from an editor or a terminal, that is not an integration we have to build. It is the same door.

The law that keeps it honest

There is a rule here that has bitten us more than once, so it is now written down in the repository as a law rather than a convention.

Every capability must ship in both tool servers and the runtime's own allowlist. Miss any one of the three and the tool exists, passes review, is documented, and cannot be called. Nothing fails. The capability is simply absent, and the assistant, asked to use it, will tell the user with total confidence that the product does not have that feature.

That exact thing happened with whiteboards. The drawing tools were implemented, registered on one server, permitted by the allowlist, and available to custom agents. The one agent every user actually talks to had never been given them. Asked to lay out a diagram, it replied that there was no whiteboard capability in this workspace, which was true of its own toolset and false of the product it was speaking for.

A feature that exists everywhere except where it is used is worse than one that was never built. The second is on the roadmap. The first is on the changelog.

What it cost, and what it did not

The cost is discipline. A capability is not done when it works, it is done when it is reachable from every surface that should have it. That is more steps per feature, permanently.

What it did not cost is a second product. There is one implementation of creating a card. The assistant and the interface disagree about nothing, because there is nothing for them to disagree about.

The part I did not anticipate is how much this changed what the assistant is for. When it can reach everything, the interesting requests stop being "add a task" and start being the ones that cross modules: the questions a specialised tool structurally cannot answer, which is the reason the whole thing was built as one system in the first place.