Automation That Refuses to Speak When It's Out of Date
A coordination layer where every automated participant has an identity it cannot fake and refuses to post on stale context
- Result:
- Several automated participants coordinate in shared threads where authorship cannot be spoofed and a participant working from an outdated view is refused rather than allowed to overwrite.
- Stack:
- Python, MCP, Gmail API, pytest
- Published:
- Updated:
Context
Several automated systems working on the same thing need somewhere to talk. The informal version here was already working: tagged email threads, one note per update, a loose house style of what changed / what is blocked / what you need to know / next.
Informal works right up until it doesn’t. Two failure modes were waiting:
- Anyone can sign anything. If the author is a string in the message, an automated participant can put any name on it — by accident as easily as otherwise. An audit trail where authorship is decorative is not an audit trail.
- Anyone can act on a stale view. A participant that read the thread ten minutes ago and posts now will confidently contradict a decision it never saw. In a human thread someone notices. Between automated participants, nobody does.
The approach
Authorship is environment, not argument. Each client declares who it is through its environment configuration. There is no parameter, tool argument or CLI flag that can set the author — the code reads the configured identity and nothing else. An unrecognised or unset identity gets read-only access: it can follow the thread and cannot post to it.
This is the whole trick, and it is deliberately boring. The moment authorship is something a caller passes in, it is something a caller can get wrong.
A post carries the last thing its author saw, and is refused if that is out of date. Every post declares the most recent message its author had read. If newer trusted notes have landed since, the post is rejected — and the rejection returns the notes that were missed, so the participant can read them and try again.
That is optimistic concurrency, borrowed from databases and pointed at a conversation. It converts the worst failure mode — an automated actor confidently talking past a decision — into a retry with the missing context attached.
The house rules are enforced, not remembered. Claiming something is “locked” without citing the specific version or message that locked it is refused. There is a per-author daily cap. Bodies have a size limit. Every post is appended to a log. Rules that live only in a style guide get followed until the day they matter.
Why the refusal is the valuable part
Most automation is built to always succeed. Retries, fallbacks, defaults — each one a small decision to proceed without being sure.
The useful inversion is a system that declines to act when its information is stale, and says exactly what it is missing. A refusal with the reason attached is recoverable. A confident write on stale data is a corruption you find out about later, usually from the person it inconvenienced.
That pattern transfers directly to ordinary business systems: an integration that will not update a record it has not re-read, a sync that stops instead of overwriting, a bot that declines rather than guesses.
Result
Several automated participants coordinate in shared threads with authorship that cannot be spoofed, a stale-context refusal that returns what was missed, enforced posting rules and an append-only log of every post. 16 modules, 6 test files, and the rules under test rather than under documentation.