Answers the question 15 raised: a board showing many sessions leaves one-per-node untouched, because each is still one conversation. Only concurrent conversations with the same session would touch 0004. Records soulstream and herdr as the prior art to draw from, and marks it explicitly off the provisioning path so it stays a note rather than becoming the work.
227 lines
12 KiB
Markdown
227 lines
12 KiB
Markdown
---
|
|
layer: to-be
|
|
status: designed
|
|
code: [mesh-control, 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 control plane |
|
|
| **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 control plane.** 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 control-plane 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 control-plane 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 control-plane 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 control-plane 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 control-plane 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.
|