Settles the design repository now that the self-upgrade build is on main: - Records the two decisions that shipped without a record — ADR 0077 (the controller/foundation/node vocabulary) and ADR 0078 (the store and broker are ordinary modules); accepts ADR 0075 and 0076, which shipped work rests on. - Fills issue 051's amended-design and wires ADR 0078 into 07-the-foundation. - Sweeps the repo rename (mesh-control -> mesh-controller) into the mutable docs now that the forge repo is renamed; updates the glossary note and repos.md. - Fixes the six broken links from the design-doc renames, indexes the glossary, regenerates the decisions reading order. Both checks (records.py, index.py) are green. Statuses stay honest: the build is on main and lab-proven but not deployed as the production mesh, so the to-be docs remain in-progress and the as-is layer (the hal mesh) is unchanged — graduation to implemented + as-is belongs to deployment, not merge. https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
227 lines
12 KiB
Markdown
227 lines
12 KiB
Markdown
---
|
|
layer: to-be
|
|
status: designed
|
|
code: [mesh-controller, mesh-host]
|
|
updated: 2026-08-31
|
|
decisions:
|
|
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
|
|
- 02-DECISIONS/0026-the-mesh-has-a-session-of-its-own.md
|
|
- 02-DECISIONS/0024-model-access-is-a-provision.md
|
|
- 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
|
---
|
|
|
|
# The agent session
|
|
|
|
**One mechanism, started twice.** A node's session and the mesh's session are the same thing
|
|
pointed at different context. This document describes the mechanism; where the two differ it says
|
|
so, and the differences are few enough to list here:
|
|
|
|
| | node session | mesh session |
|
|
|---|---|---|
|
|
| **address** | the node's name | the mesh |
|
|
| **context root** | the node's | the mesh's |
|
|
| **engram** | that node's | the mesh's |
|
|
| **licence** | bound in its own right | bound in its own right |
|
|
| **runs on** | that node | the node holding the controller |
|
|
| **how many** | one per node | one |
|
|
|
|
Everything below applies to both unless it says otherwise.
|
|
|
|
## What a session is, restated for what is being built
|
|
|
|
[ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md) settled the behaviour: permanent,
|
|
remembering across callers, its own tools, reachable over the broker, and — switched off — still
|
|
answering *I am switched off* rather than falling silent.
|
|
|
|
**Nothing said how one is set up**, and that gap is what this document closes. It was invisible
|
|
until [ADR 0026](../../02-DECISIONS/0026-the-mesh-has-a-session-of-its-own.md) needed to describe
|
|
a second instance, because *the same as that one, elsewhere* means nothing until the first has
|
|
been written down.
|
|
|
|
## The context root is the whole of the difference
|
|
|
|
**A session is defined by the directory it starts in.** That directory holds the engram, the
|
|
session's tools, and whatever standing instruction it works under. Two sessions differing only in
|
|
their root are two different agents, and nothing else has to differ to make them so.
|
|
|
|
This is deliberately a **small** definition. The alternative — a session type, with the mesh
|
|
session as a distinct kind — would mean two implementations of one mechanism, and
|
|
[ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md) records what that
|
|
costs: two things nearly the same, built twice, until neither word means one thing.
|
|
|
|
**The root is delivered as declared state, not carried by the session.** It is files on a machine,
|
|
which is precisely what the host applies
|
|
([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)). A session's context therefore changes the
|
|
way anything else changes — the mesh declares it, the host writes it — and there is no second
|
|
mechanism for shipping an engram.
|
|
|
|
**Changing the engram is changing a file.** So it is versioned, reviewable, and rolled back like
|
|
any other declared state; and a node whose engram was changed reports having applied it, the same
|
|
as it reports anything else.
|
|
|
|
## Where each one runs
|
|
|
|
**A node's session runs on that node**, and cannot be moved. Moved, one machine is answering as
|
|
another (ADR 0004).
|
|
|
|
**The mesh's session runs on the node holding the controller.** The reasoning is in ADR 0026
|
|
and is worth carrying here because it is easy to get backwards: this is not *the important agent
|
|
goes on the important machine*. It is that the controller node is already the one place
|
|
excepted from *compromise of a node is compromise of that node*, and an agent able to reach
|
|
everything, placed anywhere else, would create a second such place.
|
|
|
|
## Messages
|
|
|
|
**A session is reached over the broker**
|
|
([ADR 0002](../../02-DECISIONS/0002-nodes-communicate-over-a-broker.md)). There is no second
|
|
transport, nothing is dialled at a session, and the mesh session is not a hop: node-to-node
|
|
messages continue to travel directly, and nothing is routed through it.
|
|
|
|
**A message becomes a prompt; a reply travels back the same way.** Whoever asked — a person at a
|
|
surface, another session, a worker — is a caller, and the session remembers what each of them
|
|
asked, together, over time.
|
|
|
|
**Being asked something it does not have, a session may ask another.** How it does so is its own
|
|
business and follows from its engram rather than from a message format: it may say who wants to
|
|
know, or simply ask. A person relaying a question makes the same choice.
|
|
|
|
**A switched-off session answers.** The queue is still read and the state is the reply, with no
|
|
model involved. This is the same rule the host follows about a service that does not exist, and
|
|
it is the rule this repository has now paid for three times: **absence must never be
|
|
indistinguishable from a failure to answer**
|
|
([005](../../04-ISSUES/005-pipeline-test-harness-unbuildable/00-report.md),
|
|
[008](../../04-ISSUES/008-the-documented-node-rescue-does-not-exist/00-report.md)).
|
|
|
|
## Model access is bound to the session, not to the machine
|
|
|
|
**A session is a consumer in its own right.** This is the gap
|
|
[`14-model-access.md`](14-model-access.md) records as *a consumer that is not a machine*, and it
|
|
is the one part of this design that cannot be built from what exists: the provisions model has no
|
|
consumer identity other than a node, so today a binding can only say *this module on this
|
|
machine*.
|
|
|
|
That is not sufficient here, and the shortfall is concrete rather than theoretical:
|
|
|
|
- the controller node hosts **two** sessions, which must be able to hold **different**
|
|
licences — a per-machine binding cannot express it at all;
|
|
- *this node's session uses the personal licence, the mesh's uses the company one* is the ordinary
|
|
case, not an exotic one.
|
|
|
|
**What is delivered still lands on a machine. What is chosen is chosen per session.** Delivery
|
|
follows the session's context root, which is where its credentials belong — the same rule ADR 0001
|
|
states for an agent's config directory, with the root standing in for it.
|
|
|
|
**Switching a licence remains a reaction, not a declaration** (ADR 0024). A session that exhausts
|
|
a licence is observed and its binding changed; the binding is then declared as usual. Nothing here
|
|
grows a conditional in the declaration language.
|
|
|
|
## What it remembers, and where
|
|
|
|
**A session's memory lives in its context root**, beside its engram and its tools. That is the
|
|
same rule as everything else here rather than a new one: the root is the whole of what makes one
|
|
session a different agent from another, and memory is part of what makes it *that* agent.
|
|
|
|
**The mesh session's memory is its own.** It is not assembled from the node sessions on demand,
|
|
and it is not a view over theirs. What the mesh has been asked, and what it worked out, is held
|
|
in the mesh's root — not in the root of the node that happens to host it.
|
|
|
|
**That distinction is the point of putting it there.** The controller node runs two sessions
|
|
on one machine. If memory belonged to the machine rather than to the root, they would share it,
|
|
and the mesh's recollection of a fortnight of questions would be indistinguishable from that
|
|
node's own — which is the collision ADR 0026 exists to avoid, arriving through the back door.
|
|
|
|
**The root holds two kinds of thing, and confusing them destroys the memory.** The engram and the
|
|
tools are **declared**: the mesh says what they are and the host writes them, so editing one on
|
|
the machine survives until the next heartbeat and no longer
|
|
([ADR 0011](../../02-DECISIONS/0011-managed-files-are-generated-never-edited.md)). The memory is
|
|
**written by the session itself** and is declared by nobody — the mesh does not get to say what a
|
|
session remembers, and a mechanism that regenerates the root wholesale would erase a fortnight of
|
|
it on the next pass, silently, while reporting success.
|
|
|
|
So the root is not uniformly managed, and **which parts are must be explicit rather than
|
|
inferred**. A session's memory is its own output, kept across restarts, backed up as data rather
|
|
than reproduced from a declaration — because there is nothing to reproduce it from.
|
|
|
|
## What the mesh session knows
|
|
|
|
**It holds the design record by reading it**
|
|
([ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md)), not by holding a
|
|
copy. It is that reader; there is not a second agent for it.
|
|
|
|
**It answers into a symptom search**, so what it knows appears beside ordinary results rather than
|
|
only when it is asked. **And when it cannot be reached, the search says so.** A result set that
|
|
silently omits this material looks identical to one where nothing matched — the same rule as the
|
|
switched-off session above, at a different layer.
|
|
|
|
**Reading is one-way.** It reads the repository and answers from it; nothing flows back. The
|
|
repository is public and the mesh is not, and a return path is how installation-specific detail
|
|
arrives into documents that must not carry it.
|
|
|
|
## What it does with work
|
|
|
|
**It dispatches; it does not hold a queue.** Asked for something that belongs elsewhere, it asks a
|
|
node session or hands the work to a worker. It is not an employee
|
|
([ADR 0003](../../02-DECISIONS/0003-agents-are-persistent-employees.md)): nobody hires it, it is
|
|
never drained, and it is not reassigned.
|
|
|
|
The distinction is the one ADR 0001 was written to keep: a session comes with the thing it belongs
|
|
to and goes when that thing goes; a worker is hired, holds tasks, and moves. Built from the same
|
|
parts, run on entirely different terms.
|
|
|
|
## The surface is separate
|
|
|
|
That a person usually reaches the mesh session through a board is likely and is not settled here.
|
|
The session is reachable over the broker like everything else; what puts a text box in front of it
|
|
is a different design, and the session does not know which surface asked.
|
|
|
|
**Several sessions open at once is a property of the surface, not of the sessions.** A board
|
|
showing the mesh's session beside one per node, switchable, leaves *one per node, permanent*
|
|
untouched: each is still one conversation remembering all its callers together. Only *concurrent
|
|
conversations with the same session* would touch ADR 0004, and that is not what is wanted.
|
|
|
|
Such a surface is a **client** and holds nothing — it sends prompts and shows what the sessions
|
|
themselves remember, which is what keeps it inside `11-a-board.md`'s rule that the board is not a
|
|
second implementation of anything. Prior art to draw the interaction from: impire's *soulstream*
|
|
(already cited in [ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md)) and
|
|
`herdrdev/herdr`. **Not yet designed, and not on the path to replacing provisioning** — recorded
|
|
here so the shape is not rediscovered.
|
|
|
|
## Consequences
|
|
|
|
**Two sessions on one node, and no ambiguity.** The controller node hosts its own node session
|
|
and the mesh session. ADR 0004's *one per node* forbids ambiguity about who answers when a **node**
|
|
is addressed; these answer to different addresses.
|
|
|
|
**A new node gets a session by being declared, not by being set up.** The context root is declared
|
|
state, so a joining node's session arrives the way its packages and services do.
|
|
|
|
**The mesh session is a single point of convenience, and must never become one of reach.** Every
|
|
node stays directly addressable. If that ever stops being true, the failure is quiet in the worst
|
|
way: every machine keeps working and nobody can ask about it.
|
|
|
|
## How it is checked
|
|
|
|
**A test names the decision it defends** ([ADR 0017](../../02-DECISIONS/0017-a-test-defends-a-decision.md)),
|
|
and these run in the lab on real machines:
|
|
|
|
| Check | Defends |
|
|
|---|---|
|
|
| a node is asked something and its session answers | ADR 0004 |
|
|
| the mesh is asked something and the mesh session answers, on the controller node | ADR 0026 |
|
|
| both sessions on that node answer, to their own addresses, without ambiguity | ADR 0026 |
|
|
| a session switched off replies saying so, rather than timing out | ADR 0004 |
|
|
| a session whose engram was changed reports having applied it, like any declared file | ADR 0005 |
|
|
| the two sessions on one node hold different licences, and each uses its own | ADR 0024 |
|
|
| a node is messaged directly while the mesh session is stopped, and answers | ADR 0001 |
|
|
| a search consulting an unreachable mesh session says it was not consulted | ADR 0025 |
|
|
|
|
The last two are the ones worth writing first. Both defend properties that are invisible while
|
|
everything works, and both describe a mesh that looks entirely healthy at the moment it has
|
|
stopped telling the truth.
|
|
|
|
## Deliberately not decided
|
|
|
|
**How a person's identity reaches a session.** Callers are distinguished, but who a caller *is*,
|
|
and whether a session should act differently for different people, is the human-agent question
|
|
ADR 0001 leaves open and this does not close.
|