Replacing the framing I wrote an hour ago. I had the node's own agent sitting outside the lifecycle as an exemption, which is a rule somebody has to remember. Provisioned the ordinary way and constrained is a rule the system enforces, and it is one row like any other rather than a category every query listing agents has to special-case. It also reads the original sentence more carefully. "Exempt from the hiring lifecycle" is exempt from hiring, not from having a lifecycle. Its lifecycle is the node's -- provisioned at enrolment, retired when the node is retired. Same states, a different thing driving them, and no exemption needed. The constraints are now the four nonsense states written as things that cannot happen rather than as an argument: not retirable, reassignable or deletable while its node exists; exactly one per node. And a distinction that was missing -- its existence is immutable, its engram is not. Freezing the personality would remove the way a node is configured. Disabling is the better half of this. A node with no agent is a silence somebody has to diagnose; a node whose agent is disabled answers saying so, immediately, with no model invoked -- the queue is still consumed and the state is the reply. That is the host's own rule about a service that does not exist, applied one tier up: absence must never be indistinguishable from a failure to answer.
8.1 KiB
topic, status, date, deciders, reconstructed
| topic | status | date | deciders | reconstructed |
|---|---|---|---|---|
| the mesh | accepted | 2026-07-12 | jochen | true |
3. An agent is a persistent employee, not an instance of a pool
Reconstructed after the fact from the evidence cited below.
Context
Agents were originally a pool: a named kind of worker, scaled to some number of interchangeable instances. Work went to whichever instance was free.
That model has no place to put the things that turn out to matter. An agent that accumulates knowledge of a domain cannot keep it, because the next task lands on a different instance. An agent cannot own a workspace, because there are several of it. It cannot be held to a policy — warned for a violation, then dismissed — because there is no continuing subject to warn.
Scaling was also solving a problem the mesh does not have. Instances were being multiplied to get concurrency, when concurrency is a property of how much work one agent may hold at once.
Considered options
- Keep the pool, attach memory to the pool. Rejected: shared memory across interchangeable workers is a knowledge base, not an agent's experience, and the mesh already has one.
- Keep the pool, make instances sticky. Rejected as a pool pretending to be identities — identity by scheduling accident, lost on any restart.
- One agent is one persistent identity, with concurrency as a property of it. Chosen.
Decision
An agent is a singular, named, persistent identity: a home node, a workspace on that node, accumulating memory, and a lifecycle — hired, active, draining, retired. Not a pool member.
Concurrency is a property of the agent, not a count of copies: an agent has a cap on how many sessions it may hold at once.
Lifecycle is explicit and has verbs. An agent is hired onto a node; it may be reassigned while idle; it is retired by draining first, and forced only deliberately. Retired agents are not deleted.
Surge capacity is expressed within the model rather than against it: a template agent is a blueprint, cloned into a real agent with a lifetime when a queue grows, drained and retired when it expires. A temporary employee is still an employee.
Some agents are human. What differs is modality — how the agent acts — not category. A node itself is an agent of a kind exempt from the hiring lifecycle.
The node's own session
Written 2026-08-29. The sentence above is the whole of this and had been left as one line, which is why it kept being read as a leftover rather than as the design.
Every node holds one session of its own, permanently. It listens on its own queue, anything in the mesh may prompt it, and it remembers — what it was asked ten minutes ago and what it was asked last week, across every caller, the way any conversation is remembered by both sides. Its system prompt is the node's engram: the personality that makes one node's answers recognisably its own.
Nothing about it is request-response. A caller asks, the node answers, the exchange stays.
It is the same mechanism as a hired agent, and deliberately not the same lifecycle. That distinction is the answer to a question asked repeatedly and worth settling here:
| the same | different |
|---|---|
| a persistent session, accumulating memory, a system prompt, a scoped tool list, addressable by message | how it comes into existence, and whether it can stop |
One implementation, two ways of existing: hired, or inherent to a node. Building the mechanism twice is real duplication and the concern was right; collapsing the lifecycles is the other mistake and it is worse.
It is provisioned the ordinary way and made immutable — not held outside the system. The sentence above says exempt from the hiring lifecycle, and the precision matters: it is exempt from hiring, not from having a lifecycle. Its lifecycle is the node's — provisioned when the node enrols, retired when the node is retired. Same states, a different thing driving them.
That distinction is what keeps it inside the model. A thing genuinely held outside would have to be special-cased by everything that lists agents; a thing provisioned normally and constrained is one row like any other, and the constraints are checkable rather than remembered:
| cannot be retired, reassigned, or deleted while its node exists | it is that machine's voice — retiring it leaves a node nothing can talk to, moving it puts one machine's mind on another |
| exactly one per node | with two, nothing decides which replies when the node is addressed; with none, the node is mute |
| may be disabled and re-enabled | ordinary, and see below |
| its engram may be changed | its existence is immutable, its personality is not — that is how a node is configured |
ADR 0001's the two agent rows per node merge is the same fact from the other side: one per node — not zero, and not two.
Disabled is a state that answers. A node prompted while its agent is disabled replies saying so, immediately, without a model being invoked. It does not time out and it is not silence — the queue is still consumed, and the answer is the state.
That is the whole reason disabling is better than not provisioning. A node with no agent is a silence somebody has to diagnose; a node whose agent is disabled tells you what is wrong in the reply. It is the same rule the host follows about a service that does not exist, applied here: absence must never be indistinguishable from a failure to answer.
What is scoped, and what is not
Its tool list is its own and narrower than a session a person drives. The same scoping any agent has; a different list.
There is no authorisation between nodes. Every node is the operator's own, and a prompt from one is a prompt from the operator. Asking a node something is asking a colleague, and colleagues do not present credentials.
Stated once so it is not discovered later: the mesh boundary is therefore the security boundary. Anything inside can reach whatever any node can reach, which is what makes the token and the overlay the entire perimeter (ADR 0004, ADR 0007).
How a node passes a question on is the node's own choice, not a field in a message. Asked something it must ask a third node about, a node may say who is asking or may simply ask — the way a person relaying a question decides how to phrase it. That follows from the engram, not from a protocol. What it costs is a machine-readable chain of who ultimately asked; what each node was asked, and by whom, remains in that node's own record.
A node thinks about one thing at a time, being one session. Callers queue, and a long answer delays the others.
Consequences
- Memory, workspace and reputation have a subject to belong to. Policy becomes possible: an agent that violates a rule can be warned, and warned agents can be dismissed.
- The mesh gained a hiring model, and with it the question of who may hire.
- Scaling by adding instances is gone. If one agent is saturated, either its session cap rises or another agent is hired — both deliberate acts.
- The transition was not free. Lifecycle columns had to reach every query that selects an agent, and the ones that were missed failed at the moment of hiring rather than at startup.
- This is the decision ADR 0001 generalises: one kind of participant, differing only in modality.
References
docs(adr): agents as persistent employees + MINERVA librarian(#495), 2026-07-12 — the original record, in the code repository.feat(B4): one persistent employee, N sessions — rename max_instances → max_sessions(#547) andfeat(noxflow): B3 — workspace provisioner for agent employee model(#549), 2026-07-20.feat(noxflow): warn-then-fire agents who merge to main without review(#209), 2026-06-01 — policy that presumes a continuing subject, predating the model that provides one.- Knowledge base:
agents/employee-lifecycle,agents/temp-surge,agents/workspace-layout. - The migration cost:
troubleshooting/noxflow-agent-enriched-select-missing-lifecycle-columns(#546).