HQ held only the to-be. Every reader had to already know the system the decisions were about, and an as-is claim had nowhere to live except inside an intention. Adds 02-DESIGN/00-as-is — eleven documents written from the implementation and the operational record, not from intent, including the parts nobody would choose again. The two existing designs move under 01-to-be. Layers are declared in frontmatter and never mix: a design that ships does not move, its as-is counterpart is written, and both stand. Back-fills adr/0001-0014 for decisions taken in implementation and never recorded — the broker, the module abstraction, the mesh database, managed files, provisioning, migrations, the workspace removal, failing loudly, the constitution, application placement, linking, the employee model, the artifact, the three silos. Each marked reconstructed, dated from the history, and citing the evidence it was recovered from. The two existing records renumber to 0015 and 0016 so the ledger runs oldest first; 0017 extends 0015 to modules outside the core, principle only — the domain list is deliberately not invented here. how-we-build.md becomes the source of the mesh constitution, with a sync playbook, so the enforced copy stops being the only one that is true. Process becomes explicit: five playbooks, eight thin skills that defer to them, a repository map, and AGENTS.md with CLAUDE.md as its include. The five Observations become 04-ISSUES 001-005 where they can be owned and closed. 006 is new and uncomfortable: HQ is not indexed into the knowledge base. That claim is what decision 27 rests on, it was never checked, and the README now says so instead of repeating it. Also corrects the ADR index into something generated, the "02-DESIGN is empty" claim, the VISION.md pointer that did not survive the repo split, and a note asserting the symlink rule was contradicted — it was a misreading; the rule forbids hand-made links, the installer links by design.
75 lines
3.7 KiB
Markdown
75 lines
3.7 KiB
Markdown
---
|
|
status: accepted
|
|
date: 2026-07-12
|
|
deciders: jochen
|
|
reconstructed: true
|
|
---
|
|
|
|
# 12. 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
|
|
|
|
1. **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.
|
|
2. **Keep the pool, make instances sticky.** Rejected as a pool pretending to be identities —
|
|
identity by scheduling accident, lost on any restart.
|
|
3. **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.
|
|
|
|
## 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 0015](0015-mesh-brokers-nodes-host-agents-think.md) 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)
|
|
and `feat(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).
|