Blog

An assistant you can hold to account

Letting software act on your agreements is a trust problem, not a model problem. Nine things we built before we let it send anything.

XDocs Engineering5 min read

An assistant that can read your documents is a convenience. An assistant that can send them is a different category of thing. The question stops being *how good is the model* and becomes *what happens when it is confidently wrong at the worst possible moment* — and that question is answered in product design, not in a prompt.

So the interesting part of the XDocs assistant is not the tools. It is everything between asking and done. Here is what is there, and why each piece exists.

1. The approval states the consequence, not the function

A card that says send_envelope is asking you to approve a function call. A card that says which document, which four addresses it will reach, that sending freezes the text and that the draft stops being editable is asking you to approve an act. The second is the one worth clicking, so the approval is resolved against the real rows before it is drawn: the actual recipients, the actual addresses, the actual state that is about to change.

2. A rewrite shows its diff first

Most document edits are small and running them freely is the whole point of having an assistant. But "tidy up the termination clause" and "replace the body with a tidier version" look identical from the outside until afterwards. So an edit that would discard most of a body stops and shows the change line by line. Appends and small edits still just happen.

3. Size escalates, and standing permission does not inherit

One reminder to one person is an ordinary action. A bulk send to forty recipients, a public link with no gates on it, and an irreversible void are not the same action at a larger size — they are a different risk, and they ask harder: a typed confirmation rather than a button. This matters most for the person who has sensibly granted standing permission for the ordinary case. That permission is not quietly inherited by the unusual one.

4. Dry run is a toggle, not a mode you hope you are in

Turn it on in the composer and every mutating tool describes precisely what it would do and writes nothing. It is the honest answer to "what would you do here", which is otherwise a question you can only ask by letting it happen.

5. Undo restores a snapshot taken before the call

Every edit the assistant makes snapshots the previous body to version history first, so undo puts back the thing that was there rather than a reconstruction of it. Undo sits in the transcript and in the activity log, one click from the action it reverses.

And where there is no honest way back, it says so instead of offering a button. An email that has left cannot be recalled. A tool that pretends otherwise is worse than one that admits the limit, because the person has already stopped worrying by the time they find out.

An undo button that cannot undo is not a safety feature. It is a second failure wearing the costume of the first one's remedy.

6. Ceilings are counted on the server, per conversation

Emails sent, links published, documents changed: each has a ceiling for the conversation, counted from what actually happened and enforced server-side. The failure this prevents is specific — a loop that discovers an expensive move and repeats it — and it cannot be talked past, because the limit is not part of the conversation.

Approvals are signed server-side for the same reason. A click in a browser is a claim about a click; the signature is what makes it a permission.

7. Every tool is Runs, Asks or Off

The gated list belongs to you, at Settings → Assistant, tool by tool. Teams differ about where the line sits — plenty of people are happy for links to be published without a prompt and want every reminder held — and a product that picks one answer for everybody is wrong for most of them.

8. The log records the calls that never happened

The activity log holds declined calls, calls blocked by a ceiling, and calls that ran dry. This is deliberately *not* the audit trail: that one is evidence about signers and viewers, and belongs to the document. This one is a record of what your assistant did on your behalf, including the moments it was stopped — which is the half you need when you are deciding how much rope to give it next week.

In the chat itself, a line under each answer says what was actually read and changed, computed from the tool calls rather than from the prose above it. When a model writes a confident paragraph with nothing behind it, that paragraph sits directly above a line saying nothing was read.

9. Refusing is cheap on purpose

There is a tool whose only job is to report that the product cannot do the thing being asked. It costs nothing, it is never gated, and it writes a row naming what was missing. Making refusal cheap is how you get a refusal instead of an improvisation — and "what do people keep asking for that we cannot do" is a question only a log of the failures can answer.

The floor that cannot be moved by talking

The assistant remembers preferences between conversations, including preferences about approvals. Tell it to stop asking about reminders and it will stop asking about reminders.

It will not extend that to sending an agreement, publishing a document, changing who is party to one, or minting a credential — whatever it is told, however reasonably it is phrased. That floor is a list in lib/memory/approvals.ts, not a sentence in a prompt, because the difference between a rule and a suggestion is whether it is enforced somewhere the conversation cannot reach.

More like this, by mail

One mail when something new is published. No sequence, no digest.

One place for every important document.

Create, sign, share and understand your documents with XDocs.