From 16855ade02c22521c84d98f4372240c7b545ab37 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 30 Sep 2026 17:13:09 +0200 Subject: [PATCH 1/2] The console shipped: design 34 implemented, as-is 13, issue 147 verified live --- 03-DESIGN/00-as-is/13-the-console.md | 59 +++++++++++++++++++ 03-DESIGN/00-as-is/README.md | 1 + 03-DESIGN/01-to-be/34-the-console.md | 19 +++++- .../00-report.md | 7 +++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 03-DESIGN/00-as-is/13-the-console.md diff --git a/03-DESIGN/00-as-is/13-the-console.md b/03-DESIGN/00-as-is/13-the-console.md new file mode 100644 index 0000000..2895b20 --- /dev/null +++ b/03-DESIGN/00-as-is/13-the-console.md @@ -0,0 +1,59 @@ +--- +layer: as-is +status: implemented +code: [mesh-catalog modules/mesh-console, mesh-tools src/mesh.ts, mesh-tools src/http.ts, mesh-tools src/runtime.ts, mesh-controller internal/broker, mesh-controller cmd/mesh-controller/check.go] +updated: 2026-09-30 +decisions: + - 02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md + - 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md + - 02-DECISIONS/0037-where-a-module-lives.md +--- + +# The console, as it runs + +**The mesh's tools reach a person through a module the mesh assigned to their machine.** Since +2026-09-30 a workstation that is a node can be assigned `mesh-console`; the mesh mints a bus account +`.mesh-console`, seals its credential to the machine, and the container binds +`127.0.0.1:` with the port the mesh assigned for the manifest's declared one. An agent on the +machine is pointed at `http://127.0.0.1:/mcp` and sees the mesh's tools; a person uses the same +endpoint. Nothing on the machine holds a credential a person had to carry. + +## What it answers + +`initialize`, `tools/list`, `tools/call`, over HTTP, one JSON body per request, no session and no event +stream. `tools/list` is what the running modules answered: every tool runtime built on or after that day +serves a `tools` verb for its module, and the console asks the catalogue for the roster and each module +for its tools. A module that did not answer is named in the list's `_meta.notAnswering`. On the day it +shipped that was 36 of 51 modules — those that serve no tools at all, and those whose rebuilt runtime the +mesh records rather than rolls out — and 62 tools from the rest. + +`tools/call` reaches any tool by `.`, listed or not. The console's grant is `*`, so what it +may call is every tool on the mesh; its account may publish nothing else and subscribes nothing. + +## What it does not answer + +The mesh's own verbs. `status`, `push`, `assign` and the rest are not served on the bus — they are the +`mesh-controller` seat's tools under ADR 0132, whose three prerequisites are not built — so a person +still opens a shell on the control node for them. The console's handshake says so. + +## Around it + +- **`invokes`** in a manifest is the grant. It is composed into the bus's user list exactly as a + person's account is; the console is the only module that declares it. +- **`module check …`** on the controller's binary judges a manifest with no mesh: the strict + parse, every per-manifest problem, and the rules between the manifests given. It prints what it cannot + judge without a store rather than refusing. The console's own manifest was the first thing checked + with it, and the whole catalogue passes. +- **The person's client remains.** `operator issue` and `mesh tools|call|mcp` with a credential file + still work, for a machine that is not a node and for a mesh not yet able to assign anything. + `mesh tools --console ` goes through a running console with no credential; it is covered by the + runtime repository's tests and was not exercised on the live mesh. + +## What shipped bent + +- A module registered by hand from the catalogue with `--source --path modules/` records a URL, + not a place on the git seat: `--self` takes the forge path form (`/`), which the + operator did not pass. The rebuild-on-merge matched the URL anyway. +- Modules whose upgrade policy is *record* — the forge among them — answered `tools` only once + something pushed their rebuilt runtime; until then they are listed as not answering while still + callable. That is the policy doing what it says, not a fault of the console. diff --git a/03-DESIGN/00-as-is/README.md b/03-DESIGN/00-as-is/README.md index 041652a..dd766fd 100644 --- a/03-DESIGN/00-as-is/README.md +++ b/03-DESIGN/00-as-is/README.md @@ -21,6 +21,7 @@ Where the two disagree, the implementation wins and the disagreement is stated. | [`10-module-catalogue.md`](10-module-catalogue.md) | The catalogue's shape, and what its shape says | | [`11-the-lab.md`](11-the-lab.md) | The lab — the first piece of the new shape that exists, and what it does not yet do | | [`12-the-seats.md`](12-the-seats.md) | The seats the mesh defines, who holds one, and where a seat changes resolution | +| [`13-the-console.md`](13-the-console.md) | The mesh's tools on the machine a person sits at, served by a module the mesh assigned there | ## What these documents are not diff --git a/03-DESIGN/01-to-be/34-the-console.md b/03-DESIGN/01-to-be/34-the-console.md index 653e477..e6e08d1 100644 --- a/03-DESIGN/01-to-be/34-the-console.md +++ b/03-DESIGN/01-to-be/34-the-console.md @@ -1,6 +1,6 @@ --- layer: to-be -status: in-progress +status: implemented code: [mesh-catalog, mesh-tools, mesh-controller] updated: 2026-09-30 decisions: @@ -100,6 +100,23 @@ address gets a refused connection, which is the truthful answer. | on the live mesh: the console assigned to a workstation answers `tools/list` on loopback and a call to the forge returns repositories | the exit of work-order step 3 | | the composed filter for a machine carrying the console opens no port for it | ADR 0144 | +## What shipped, 2026-09-30 + +Everything above, the same day: mesh-controller PR 164 (`invokes`, `module check`), mesh-tools PR 20 +(`mesh serve`, the `tools` verb), mesh-catalog PR 181 (`mesh-console`). Verified on the live mesh: the +console assigned to a workstation answered `tools/list` on its loopback with 62 tools from the modules +whose runtimes had been rebuilt to answer `tools`, named 36 modules as not answering (modules that serve +no tools, and modules whose new runtime the mesh records rather than rolls out), and a `tools/call` of +the forge's `gitea_list_repos` returned repositories. An agent on that machine reaches it as an HTTP +MCP server and reports it connected. The mesh assigned the declared port unchanged, which is what a +machine with nothing else on it does; the console binds whatever it is given. + +Two things shipped bent, both stated in [`00-as-is/13-the-console.md`](../00-as-is/13-the-console.md): +the person's client through the console (`--console`) exists and was exercised in the test suite, not +on the live mesh; and a module registered from the catalogue by hand recorded its source as a URL rather +than as a path on the git seat, because `--self` takes the forge path form — the rebuild-on-merge still +matched it by URL. + ## What this does not settle - Narrowing a console's grant per assignment. ADR 0046 makes it a setting; nothing reads one yet. diff --git a/04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md b/04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md index 71479ed..20b7c20 100644 --- a/04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md +++ b/04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md @@ -93,3 +93,10 @@ yet; for those a shell is still the way, and design 33 is where that closes. The predecessor's program on the workstation is not replaced by the mesh; it is left where it is and the assistant is pointed at the console beside it. The `hal` entry in the assistant's configuration still names things that are not the mesh's. + +**Verified live, 2026-09-30 evening.** The four pull requests merged; the console was registered +(checked first with `module check`), built, assigned to a workstation, issued a bus account, and pushed. +On that machine `tools/list` answered on loopback with 62 tools and named 36 modules as not answering, +and a call to the forge's `gitea_list_repos` returned repositories. The assistant on that machine now +lists the console as a connected MCP server beside the predecessor's program, which was left where it +is. As-is: [`13-the-console.md`](../../03-DESIGN/00-as-is/13-the-console.md). -- 2.54.0 From 37b46d5349de8122347cd3f8646133e026bc65ac Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 30 Sep 2026 17:47:49 +0200 Subject: [PATCH 2/2] ADR 0153 and ADR 0154: the record is read by a module, and the mesh's verbs are its seat's tools Design 33 in progress against ADR 0154 (the twelve verbs, the prerequisites built); design 35 for the records module under ADR 0153, extending 0025; as-is 07 rewritten to a mesh that keeps no store; as-is 12 and 13 updated; issue 006 built and waiting on its live check. --- ...25-the-design-record-is-read-not-copied.md | 6 + ...ad-by-a-module-and-the-console-lists-it.md | 113 +++++++++++++++ ...wn-verbs-are-the-controller-seats-tools.md | 133 ++++++++++++++++++ 02-DECISIONS/README.md | 2 + 03-DESIGN/00-as-is/07-knowledge.md | 100 ++++++------- 03-DESIGN/00-as-is/12-the-seats.md | 16 +++ 03-DESIGN/00-as-is/13-the-console.md | 14 +- 03-DESIGN/00-as-is/README.md | 2 +- 03-DESIGN/01-to-be/15-the-agent-session.md | 4 + .../01-to-be/33-the-tools-the-mesh-answers.md | 19 ++- 03-DESIGN/01-to-be/35-reading-the-record.md | 85 +++++++++++ .../00-report.md | 13 +- 12 files changed, 437 insertions(+), 70 deletions(-) create mode 100644 02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md create mode 100644 02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md create mode 100644 03-DESIGN/01-to-be/35-reading-the-record.md diff --git a/02-DECISIONS/0025-the-design-record-is-read-not-copied.md b/02-DECISIONS/0025-the-design-record-is-read-not-copied.md index 7d4e5b3..0197237 100644 --- a/02-DECISIONS/0025-the-design-record-is-read-not-copied.md +++ b/02-DECISIONS/0025-the-design-record-is-read-not-copied.md @@ -72,6 +72,12 @@ answers from it. Nothing flows back: this repository is public, the mesh is not, would be how installation-specific detail arrives into documents that must not carry it ([`README.md`](../README.md)). +> **The mechanism changed — 2026-09-30, by ADR 0153.** What stands: read where it is written, no copy, +> one-way. What moved: the reader is a module the mesh assigns (`records`, a checkout at a commit every +> answer names) rather than the agent session of design 15, which is not built; and "the search consults +> the agent" has no store to consult since the cut-over — the console's tool list is where the record +> appears beside everything else. [ADR 0153](0153-the-record-is-read-by-a-module-and-the-console-lists-it.md). + ## Consequences **This repository stops being a fourth knowledge system, properly.** The original objection was diff --git a/02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md b/02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md new file mode 100644 index 0000000..d105a79 --- /dev/null +++ b/02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md @@ -0,0 +1,113 @@ +--- +topic: how we work +status: accepted +date: 2026-09-30 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0025-the-design-record-is-read-not-copied.md +--- + +# 153. The record is read by a module the mesh assigns, and the console lists it + +## Context + +[ADR 0025](0025-the-design-record-is-read-not-copied.md) decided that this repository is **read where +it is written, never copied to be found**: an agent reads it directly, and a search of the mesh's +memory consults that agent so its answers appear beside ordinary results. It named the check that +closes [issue 006](../04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md): search +for a phrase that appears only in a design document here, and get it back. It gated the build on an +agent that did not exist — the mesh session of +[15 — The agent session](../03-DESIGN/01-to-be/15-the-agent-session.md) — and on a search that +does not exist either, now: the knowledge base 0025 meant was the predecessor's, and since the +cut-over nothing reaches it ([issue 147](../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md)). + +**So the two halves of 0025 have no home.** There is no store to be "beside", and no session to be +the reader. What the mesh has instead, since today: a tool model in which every module answers what +it serves, and a console on the machine a person sits at that lists every tool the running modules +answer ([ADR 0152](0152-the-operators-surface-is-a-module-the-console.md)). An agent holding the +console does not search a store; it reads a tool list and calls what fits the question. + +**What 0025 could not tolerate was a derived copy** — the enforced copy winning while the reasoned one +quietly stops being true. It rejected a sync for that reason and for no other. A git checkout is not a +derived copy: it is the same bytes at a commit the answer names, and the only way it can differ from +the source is by lagging behind it, which is measurable and stated. 0025's own words allow it — +*retrieval is an agent reading this repository, not a copy living in a second store* — and the +transformation that makes a copy dangerous is exactly what a checkout does not do. + +## Considered Options + +**1. Wait for the mesh session.** Rejected. Design 15 is `designed` with nothing built, its model +access is a provisions question with no consumer identity yet, and 006 has waited since 2026-08-23. +A record whose check cannot run is a rule enforced by nothing. + +**2. The console reads the repository itself.** Rejected. The console holds nothing and decides +nothing (ADR 0152, ADR 0035); a reader inside it would be a second implementation of a thing that +should be one module, unavailable to a person's client and to any other module. + +**3. A module that keeps a checkout of the repository and answers questions about it, listed by the +console like any tool.** Chosen. + +## Decision + +**The reader is a module: `records`.** It requires the `git` provision — the forge — clones the +repository its settings name, keeps the checkout current on every merge the forge announces and on a +timer, and answers over the bus: where a phrase appears as written (document, line, nearest heading), +one document whole, what a folder holds, and where the checkout stands — always with the commit it +read. Nothing is indexed, ranked or summarised: a design record is found by its own words, and a +reader deciding which words matter would be a second opinion about somebody else's document. + +**The repository is a setting, not a manifest field.** The module names no mesh +([ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md)): which repository it reads is +the assignment's business, and an installation that keeps its record elsewhere sets that. Until a +repository is set it serves no tools and says why. Public repositories only; it asks for no +credential, because a secret it did not need would be one more thing to seal. + +**"Beside everything else" is the console's tool list.** 0025's second half — the search consults the +agent — has no store to consult and needs none: the console lists `records_search` beside the forge's +tools and the mesh's own verbs, with a description that says when to call it, and an agent choosing +tools for a symptom is the search. That is surfacing, not merely reaching: nobody has to know this +repository exists to be offered it. + +**Reading stays one-way.** The module reads the forge and answers; nothing flows back into the +repository. It holds no credential that could write. + +**The mesh session, when it exists, is a caller of this module, not a replacement for it.** Design 15's +*it holds the design record by reading it* is satisfied by asking `records`; the session brings +judgement, this brings the text. + +## Consequences + +- **Issue 006 closes on 0025's own check**, run through the console: `records_search` for a phrase + that appears in one design document here returns that document. The module's test does the same + against a repository it makes. +- **The as-is knowledge document is rewritten.** [`07-knowledge.md`](../03-DESIGN/00-as-is/07-knowledge.md) + described the predecessor's two stores; neither is reachable from the mesh, and what the mesh knows + is now what its modules answer. Saying otherwise is the failure this repository exists to name. +- **A checkout lags.** Between a merge and the next sync — seconds when the forge announces it, + minutes when it does not — an answer is the previous commit's, and says which. That is the cost of + no copy, and it is a number rather than a silence. +- **The reader depends on the forge module's event, by name.** `consumes: gitea.pull.merged` names a + module rather than the `git` seat, because the seat declares no events. A forge that is not gitea + leaves the timer as the only refresh, which still works. +- **What got harder:** the record is now reachable from every machine holding a console, which is what + was wanted, and a reader must remember that this repository is public and the mesh is not — the + module reads the public repository and nothing about the installation. + +## How this is checked + +| Rule | Checked by | +|---|---| +| A phrase in one document comes back from where it is written, with the commit | the module's test against a repository it makes; and live, through the console | +| A merge on the origin is pulled and the next answer names the new commit | the same test | +| A path outside the checkout is refused, not resolved | a test per shape | +| A failed sync leaves the checkout standing and is said | a test against an unreachable origin | +| Without a repository set, no tools are served and the log says why | the module's own start | +| The console lists `records_search` beside every other tool | the console's listing, live | + +## References + +- [ADR 0025](0025-the-design-record-is-read-not-copied.md) — extended: the reader is a module, the search is the console's list +- [ADR 0152](0152-the-operators-surface-is-a-module-the-console.md) — what lists it +- [issue 006](../04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md) — what closes +- [35 — Reading the record](../03-DESIGN/01-to-be/35-reading-the-record.md) — the design +- mesh-catalog `modules/records` — the module (PR 183) diff --git a/02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md b/02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md new file mode 100644 index 0000000..5baaebf --- /dev/null +++ b/02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md @@ -0,0 +1,133 @@ +--- +topic: the mesh +status: accepted +date: 2026-09-30 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md +--- + +# 154. The mesh's own verbs are the mesh-controller seat's tools, and which verbs those are + +## Context + +[ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md) decided that a seat's protocol +carries its tools in full, that holding a seat means serving them, and that the mesh's own verbs are +the `mesh-controller` seat's. It named three prerequisites, none in place: the protocol in the store +rather than in compiled defaults; a protocol richer than a list of verbs; a node-scoped seat's subject +carrying the node. And it left one thing to a decision per seat: **which verbs each seat serves**, +because a seat's tools bind every future holder. + +The console shipped the same day ([ADR 0152](0152-the-operators-surface-is-a-module-the-console.md)) +and made the gap visible from the operator's chair: a person on a workstation could call every tool a +*module* serves and none of the mesh's own. What a node runs, what is assigned, whether a push +applied — the questions issue 147 opened with — still meant a shell on the control node. The console's +own handshake said so. + +The control plane already answers every one of those questions, as commands: `status --json`, +`node show`, `plan --json`, `assign`, `push`. [ADR 0035](0035-one-implementation-several-surfaces.md) +says a surface is an adapter over those with no decisions in it, and the `api` verb proves the shape: +every route calls the function the command line calls. + +## Considered Options + +**1. Leave the mesh's verbs to the shell until an identity provider authenticates the HTTP API.** +Rejected. The authenticated network surface is for a browser on another machine; the console is +already behind the machine's login (0152), and the bus already carries every other tool call under an +account whose permission list says what it may ask. Waiting would keep the one surface the mesh has +from answering the mesh's own questions, for a reason that does not apply to it. + +**2. Serve the verbs as the mesh-controller *module's* tools, `mesh.mod.mesh-controller.tool.`.** +Rejected; 0132 rejected it already. The controller holds a seat, and the verbs must keep their address +while the control plane is being replaced, which is the moment they are most needed. A module's name +would change with the implementation; the seat's does not. + +**3. Call each command's function inside the serving process.** Rejected on two facts: the commands +print, to the process's standard output, and two calls answered at once would read each other's +words; and each command opens and closes its own stores, which the serving process holds open. Making +every command return a value is the larger refactor, and it would give the tools a second code path to +keep in step with the command line — the thing ADR 0035 forbids. + +**4. The holder of the seat runs the command it names, in its own binary, and answers what it +printed.** Chosen. + +## Decision + +**The `mesh-controller` seat serves twelve verbs**, and these are its interface, additive within a +version ([33 — The tools the mesh answers](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) §7): + +| verb | answers with | takes | +|---|---|---| +| `tools` | every seat's tools, from the mesh's records | nothing | +| `status` | what is wrong, quiet, behind, waiting — `status --json` | nothing | +| `nodes` | every machine and its mode | nothing | +| `node` | what one machine reported, what it is assigned, why | `node` | +| `modules` | every module, its version, commit and machines | nothing | +| `seats` | every seat, what it delivers, who holds it — `seats --json` | nothing | +| `builds` | what was built lately and what came of it | `module` (optional) | +| `plan` | the declaration a machine would be sent — `plan --json` | `node` | +| `assign`, `unassign` | the mesh's own words, refusal included | `node`, `module` | +| `push` | that it was sent; `status` says what the machine did | `node` (optional: every machine behind) | +| `build` | that the build machine was asked; `builds` says what came of it | `repository`, `path`, `ref` | + +**Each verb runs the command it names, in the controller's own binary, and answers what the command +printed** — the output, whether it succeeded, and, where the command speaks JSON, the same as data. A +refusal is the command's refusal in the command's words, because it is the same output. A verb takes +only the arguments its schema names; nothing reaches a flag the schema did not declare. `push` and +`build` are sent and not waited for: a call that blocked for a whole apply would time out on every +machine that takes a minute and say nothing about the others. + +**The three prerequisites are built.** A seat's protocol is three columns on its row, seeded from the +compiled defaults where a row had none and additively thereafter, so a verb a release adds joins the +row and nothing an operator wrote is taken away. A served verb is its name, what it does, and the +schema of its arguments and answer; a manifest may still write a bare name. A node-scoped seat's tool +carries the node it is asked of, as the last token of its subject; a mesh-scoped seat's stays flat. + +**Holding a mesh seat requires serving its verbs**, judged where the store's set is loaded, and the +refusal names the missing verbs. The controller's own manifest lists the twelve under `tools`. + +**Discovery reads the records, through the seat.** `tools` is one of the twelve because the console +cannot read the store and should not: the mesh answers for its own records through the role that owns +them, and the answer is true while any *other* holder restarts. It is not true while the control plane +itself restarts, and the console says so rather than hiding the modules' tools with it. + +**A grant of `*` reaches a role's tools; `seat:.` grants one.** The console's `*` needed no +change to reach the mesh's verbs, which is what a grant meaning *every tool* should mean. + +## Consequences + +- **The console answers the mesh's own questions.** Issue 147's first paragraph closes: what a node + runs, what is assigned, whether a push applied, from the machine the person sits at, over the bus, + under an account whose permission list says so. +- **Whoever may call `mesh-controller.push` may change the mesh.** That is the console's `*` on a + machine whose login owns the mesh (0152), and a person's account only if `operator issue` says so. + A grant reviewer reads `*` and `seat:mesh-controller.` with the same care. +- **A verb here binds every future controller.** Twelve is deliberate: what an operator asks weekly, + and nothing that is still finding its shape (`take`, `converge`, `settings`, `secret` stay commands). +- **A command's text is the answer**, and text changes. The three verbs that speak JSON carry it as + data; the rest are read by a person or an agent, not parsed. Anything that needs a shape asks for + `--json` to be added to the command first, which is the right order. +- **What got harder:** the mesh-controller seat's row now carries a protocol an operator could edit, and + a verb removed from the row is a verb the controller stops serving without a build. That is + ADR 0122's arrangement applied to tools, and `seats` shows the row. + +## How this is checked + +| Rule | Checked by | +|---|---| +| Every declared verb is one the binary can run, with the arguments its schema names | a test walks the table and derives a command line for each | +| A verb missing a required argument is refused in its own words, before anything runs | a test per shape | +| A holder that does not serve a mesh seat's verbs cannot hold it, and the refusal names them | a catalogue test against a seat with two verbs and a holder with one | +| A node-scoped seat's tool carries the node; a mesh seat's does not | the bus composition test: two nodes derive two addresses | +| The controller subscribes its seat's tools and may answer | the composition test, and the golden user list | +| `*` reaches a role's tools; `seat:` grants one and refuses a name with no verb | the composition test | +| The protocol is seeded into the row and widened additively | the store-backed seat test | +| Live: the console lists `mesh-controller.status` and a call answers what `status --json` prints | the rollout of this record | + +## References + +- [ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md) — extended: the prerequisites built, the verbs decided +- [ADR 0152](0152-the-operators-surface-is-a-module-the-console.md) — the surface that lists them +- [ADR 0035](0035-one-implementation-several-surfaces.md) — a surface is an adapter with no decisions in it +- [ADR 0122](0122-a-seat-is-data-a-rename-is-a-database-update.md) — the row is the mesh's, and now carries the protocol +- [33 — The tools the mesh answers](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) — the design this completes diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index a7c4d99..97d1482 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -168,6 +168,7 @@ python3 00-META/checks/index.py fail if stale - **0132** — [A seat carries the tools its holder must serve](0132-a-seat-carries-the-tools-its-holder-must-serve.md) - **0134** — [The mesh says what it applied](0134-the-mesh-says-what-it-applied.md) - **0142** — [The mesh delivers its own components as binaries, not as container images](0142-the-mesh-delivers-its-own-components-as-binaries.md) +- **0154** — [The mesh's own verbs are the mesh-controller seat's tools, and which verbs those are](0154-the-meshs-own-verbs-are-the-controller-seats-tools.md) ### Its tiers, from the bottom up @@ -298,5 +299,6 @@ python3 00-META/checks/index.py fail if stale - **0034** — [The local account owns the mesh, and a web application's login is not that](0034-the-local-account-owns-the-mesh.md) - **0080** — [The development cycle is checked, not trusted](0080-the-development-cycle-is-checked.md) - **0081** — [A decision nothing cites is not yet in the chain](0081-a-decision-nothing-cites-is-not-yet-in-the-chain.md) +- **0153** — [The record is read by a module the mesh assigns, and the console lists it](0153-the-record-is-read-by-a-module-and-the-console-lists-it.md) diff --git a/03-DESIGN/00-as-is/07-knowledge.md b/03-DESIGN/00-as-is/07-knowledge.md index fb09147..7f25af6 100644 --- a/03-DESIGN/00-as-is/07-knowledge.md +++ b/03-DESIGN/00-as-is/07-knowledge.md @@ -1,78 +1,58 @@ --- layer: as-is status: implemented -code: [hal] -updated: 2026-08-23 -decisions: [] +code: [mesh-catalog modules/records, mesh-catalog modules/mesh-console] +updated: 2026-09-30 +decisions: + - 02-DECISIONS/0025-the-design-record-is-read-not-copied.md + - 02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md + - 02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md --- # Knowledge -The mesh keeps two knowledge stores. They are not redundant, and knowing which is which is the -difference between finding an answer in one search and rediscovering it over several hours. +**The mesh keeps no knowledge store.** What it knows is what its modules answer, and the way a person +or an agent asks is the console's tool list on the machine they sit at +([13 — The console](13-the-console.md)). This document used to describe two stores; it is rewritten +because neither exists from the mesh's side, and an as-is document that describes what is gone is a +brochure. -## The operational memory +## What was here, and where it went -A store of operational notes, written and read by whoever — human or agent — is working. Each -note is a slug and a body: how something works, what went wrong, what the fix was, what -assumption turned out to be false. +Until the cut-over of 2026-09-28 the predecessor ran two stores: an operational memory of notes +indexed on symptoms, and a structured archive of governed documents with a librarian approving +promotion. Both were reached through the predecessor's tool server over the bus the mesh removed +([issue 147](../../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md)). +Nothing in the mesh reaches them now, and nothing in the mesh has replaced them: there is no note +store, no archive, no librarian, and the lessons of the last days were written into this repository by +hand. That is a gap, and it is stated here rather than papered over. What replaces a symptom-indexed +memory, if anything does, is undecided. -It is indexed on **symptoms**. The entry someone needs is usually titled after the error they -are staring at, which is why the standing instruction is to search the literal error text -before forming a hypothesis rather than after one fails. +## The record -Its content is overwhelmingly the record of previous debugging: a large body of -troubleshooting entries, module conventions, and standing notes about work that is open. It is -the mesh's institutional memory of *what has already gone wrong*. +**The design record is read where it is written.** Since 2026-09-30 a module, `records`, keeps a +checkout of this repository from the forge — cloned from the `git` seat, reset to the origin on every +merge the forge announces and every ten minutes — and answers over the bus: where a phrase appears as +written, one document whole, what a folder holds, and where the checkout stands, each naming the +commit it read. Which repository it reads is a setting on its assignment; the module names no mesh. -The cost of skipping it is documented in the mesh's own record: entries have been rediscovered -from scratch, over hours, in sessions where the search was skipped because the trail felt -confident. It fires hardest on familiar ground, not unfamiliar ground. +It is listed by the console beside every other tool, with a description that says to search the +literal words of a symptom before forming a hypothesis. That is what +[ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md) meant by *beside +everything else*, in a mesh with no store to be beside +([ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md)). -## The structured archive +Nothing is copied. A checkout lags the source by seconds after an announced merge and by minutes +otherwise, and says so. -A second store, structured rather than flat: spaces, pages, revisions, tiers, and full-text -search. Where the operational memory is a note, this is a document with an owner and a -lifecycle. +## The constitution -Content is promoted through tiers — private, then team, then platform — with a librarian agent -owning approval and promotion at the boundary. Proposals to edit are reviewed rather than -applied. - -This is where the mesh's **governed** documents live, including the constitution injected into -design sessions ([ADR 0020](../../02-DECISIONS/0020-the-mesh-is-governed-by-a-constitution.md)). - -## Why both - -The distinction is by lifecycle, not by subject. - -| Operational memory | Structured archive | -|---|---| -| Written the moment something is learned | Written deliberately, reviewed | -| Flat, symptom-indexed | Structured, tiered, owned | -| Anyone writes; nothing approves | Promotion is approved | -| Truth is "this happened" | Truth is "this is agreed" | - -Collapsing them would cost one of the two properties: either every hard-won note waits for -review, or governed documents can be changed by anyone mid-incident. +[`00-META/how-we-build.md`](../../00-META/how-we-build.md) is the source of the mesh constitution +([ADR 0021](../../02-DECISIONS/0021-hq-is-the-source-of-the-constitution.md)). The page it used to be +synchronised into lived in the predecessor's archive and is unreachable; the constitution today is +read from this repository, through the same module, and playbook 05's sync has nothing to write to. ## Where this repository sits -This repository is a third thing, and the objection was raised when it was created: a fourth -knowledge system repeats the mistake the split was made to fix. - -The answer given was **indexing, not location** — that these documents are indexed into the -knowledge base so that a symptom search returns them alongside everything else. One source, -many surfaces. - -**That indexing does not currently exist.** A search for this repository's content returns -nothing. The claim is load-bearing for the decision to separate the repository at all, and -until it is true, this repository is exactly the fourth knowledge system the objection -described. Recorded here because it is a statement about how the mesh's knowledge actually -works today, and as [`04-ISSUES/006`](../../04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md). - -## The librarian - -A single agent owns the archive's approvals and promotions. Its approval capabilities have at -times not been reachable as tools, which does not affect the operational memory but does mean -promotion stops silently — the store keeps accepting proposals that nothing can approve. +A third thing beside two that are gone, which makes it the first: the one governed record the mesh +has, public, read by a module the mesh assigns, and edited nowhere else. diff --git a/03-DESIGN/00-as-is/12-the-seats.md b/03-DESIGN/00-as-is/12-the-seats.md index 025b6b6..5ec5209 100644 --- a/03-DESIGN/00-as-is/12-the-seats.md +++ b/03-DESIGN/00-as-is/12-the-seats.md @@ -82,6 +82,22 @@ to clone. The schema column added for this defaults to empty rather than null, because "not on a seat" is a real answer, so every row recorded before the change keeps exactly the meaning it had. +## A seat's protocol is on its row, and the controller serves its own + +*Since 2026-09-30 ([ADR 0154](../../02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md)).* +The `seat` table carries `accepts`, `emits` and `serves`; `serves` holds each verb with its description +and input schema. The rows were seeded from the compiled defaults the first time a controller with the +columns migrated, and each later migration adds any verb the defaults name that a row lacks, never +removing one. `UseSeats` still falls back to the compiled protocol for a row with none, which after the +first seeding is no row. + +The `mesh-controller` seat serves twelve verbs — `tools`, `status`, `nodes`, `node`, `modules`, `seats`, +`builds`, `plan`, `assign`, `unassign`, `push`, `build` — on `mesh.seat.mesh-controller.tool.`, +each answered by the controller running that command in its own binary and returning what it printed. +A module claiming a mesh seat with verbs must list them under `tools` or registration refuses it by +name. A node-scoped seat's tool is `mesh.seat..tool..`; no node-scoped seat declares +one yet. + ## Where this differs from the design **Capacity is not implemented.** The design's vocabulary has a seat with a capacity, and a diff --git a/03-DESIGN/00-as-is/13-the-console.md b/03-DESIGN/00-as-is/13-the-console.md index 2895b20..c24d94b 100644 --- a/03-DESIGN/00-as-is/13-the-console.md +++ b/03-DESIGN/00-as-is/13-the-console.md @@ -1,12 +1,13 @@ --- layer: as-is status: implemented -code: [mesh-catalog modules/mesh-console, mesh-tools src/mesh.ts, mesh-tools src/http.ts, mesh-tools src/runtime.ts, mesh-controller internal/broker, mesh-controller cmd/mesh-controller/check.go] +code: [mesh-catalog modules/mesh-console, mesh-tools src/mesh.ts, mesh-tools src/http.ts, mesh-tools src/runtime.ts, mesh-controller internal/broker, mesh-controller cmd/mesh-controller/check.go, mesh-controller cmd/mesh-controller/seatverbs.go] updated: 2026-09-30 decisions: - 02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md - 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md - 02-DECISIONS/0037-where-a-module-lives.md + - 02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md --- # The console, as it runs @@ -30,11 +31,14 @@ mesh records rather than rolls out — and 62 tools from the rest. `tools/call` reaches any tool by `.`, listed or not. The console's grant is `*`, so what it may call is every tool on the mesh; its account may publish nothing else and subscribes nothing. -## What it does not answer +## The mesh's own verbs -The mesh's own verbs. `status`, `push`, `assign` and the rest are not served on the bus — they are the -`mesh-controller` seat's tools under ADR 0132, whose three prerequisites are not built — so a person -still opens a shell on the control node for them. The console's handshake says so. +*Since 2026-09-30 evening ([ADR 0154](../../02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md)).* +The console asks the `mesh-controller` seat's `tools` verb beside the modules and lists every role's +tools as `.` — `mesh-controller.status`, `mesh-controller.push` and the other ten. A call +to `.` reaches the seat when the prefix is a seat declaring that verb, and the module +otherwise; `seat:.` says so outright. When the control plane does not answer, the list +names `mesh-controller (seat)` as not answering and carries the modules' tools regardless. ## Around it diff --git a/03-DESIGN/00-as-is/README.md b/03-DESIGN/00-as-is/README.md index dd766fd..ee23f9f 100644 --- a/03-DESIGN/00-as-is/README.md +++ b/03-DESIGN/00-as-is/README.md @@ -15,7 +15,7 @@ Where the two disagree, the implementation wins and the disagreement is stated. | [`04-delivery.md`](04-delivery.md) | Push to running: the three silos, levels, and what a green pipeline proves | | [`05-runtime-and-installation.md`](05-runtime-and-installation.md) | The node runtime, its modes, and how a node comes into being | | [`06-configuration-and-secrets.md`](06-configuration-and-secrets.md) | Managed files, value resolution, and where secrets live | -| [`07-knowledge.md`](07-knowledge.md) | The two knowledge stores, and what each is for | +| [`07-knowledge.md`](07-knowledge.md) | The mesh keeps no store: what it knows is what modules answer, and the record is read by one | | [`08-agents-and-work.md`](08-agents-and-work.md) | Agents as employees, tasks, workflows, and the meeting model | | [`09-interfaces-and-observability.md`](09-interfaces-and-observability.md) | How the mesh is reached and watched — tools, board, proxy, health, thoughts | | [`10-module-catalogue.md`](10-module-catalogue.md) | The catalogue's shape, and what its shape says | diff --git a/03-DESIGN/01-to-be/15-the-agent-session.md b/03-DESIGN/01-to-be/15-the-agent-session.md index b1e80ab..5fc216f 100644 --- a/03-DESIGN/01-to-be/15-the-agent-session.md +++ b/03-DESIGN/01-to-be/15-the-agent-session.md @@ -148,6 +148,10 @@ than reproduced from a declaration — because there is nothing to reproduce 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. +*2026-09-30:* the reading is a module's — `records`, [35 — Reading the record](35-reading-the-record.md), +[ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md). The +session, when built, asks it rather than reading for itself; what it adds is judgement, not text. + **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 diff --git a/03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md b/03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md index 0d8b599..9e0d28a 100644 --- a/03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md +++ b/03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md @@ -1,9 +1,10 @@ --- layer: to-be -status: designed -code: [] -updated: 2026-09-28 +status: in-progress +code: [mesh-controller, mesh-tools] +updated: 2026-09-30 decisions: + - 02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md - 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md - 02-DECISIONS/0129-a-seat-carries-the-protocol-of-its-role.md - 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md @@ -126,6 +127,18 @@ by side until nothing is bound to the old one. - **Two nodes holding one node-scoped seat derive two addresses.** Checked by the same test as the rest of the subject table. +## What is built, 2026-09-30 + +Under [ADR 0154](../../02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md): §2's +two constraints (the protocol in the store's row, seeded additively; a verb with description and +schema, a bare name still accepted), §3 for the mesh's seats (holding refused by naming the missing +verbs), §4 (a node-scoped seat's tool carries the node as its last subject token; a user publishes +`*`), and the third family — twelve verbs on the `mesh-controller` seat, each running the command it +names in the controller's own binary. §5's first half is served rather than read: the seat's `tools` +verb answers every seat's tools from the records, because the console cannot read the store; the +console lists a role's tools beside the modules' own and resolves `.` to the seat when the +seat declares that verb. Which verbs each *other* seat serves stays a decision per seat, still untaken. + ## What this does not settle - Which verbs each seat should serve. That is a decision per seat, and the reason to do it slowly: a diff --git a/03-DESIGN/01-to-be/35-reading-the-record.md b/03-DESIGN/01-to-be/35-reading-the-record.md new file mode 100644 index 0000000..40d8416 --- /dev/null +++ b/03-DESIGN/01-to-be/35-reading-the-record.md @@ -0,0 +1,85 @@ +--- +layer: to-be +status: in-progress +code: [mesh-catalog modules/records] +updated: 2026-09-30 +decisions: + - 02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md + - 02-DECISIONS/0025-the-design-record-is-read-not-copied.md + - 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md +--- + +# 35 — Reading the record + +**The design record, answered from a checkout the mesh keeps, at the commit it read.** A module, +`records`, holds a working copy of a repository of markdown — this one, for this mesh — and answers +where a phrase appears, what a document says, what a folder holds and where the copy stands. The +console lists those answers beside every other tool +([ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md)). + +## 1. What it keeps, and why that is not a copy + +A git checkout, cloned from the forge that holds the `git` seat, brought up to date on every merge the +forge announces and every ten minutes besides. The bytes are the repository's; nothing is derived +from them and stored. What [ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md) +refused was a second store that is *searched* while the first is *edited*, drifting silently. A +checkout cannot drift; it can lag, and the lag is in every answer as the commit it was read at and +in `records_status` as when it was last brought up to date. + +The checkout is reset to the origin on every sync, never merged: it is the mesh's, so a local change +is nobody's. + +## 2. What it answers + +| tool | answers | +|---|---| +| `records_search` | every place a phrase appears, as written and case-insensitively: document, line, the nearest heading above it; bounded, and says when it was | +| `records_read` | one document, whole, or its first part with a note when very long | +| `records_list` | what a folder holds: sub-folders and documents | +| `records_status` | repository, forge, commit and its date, last sync, document count, last error | +| `records_sync` | bring the checkout up to date now | + +No ranking and no summary, on purpose: a record is found by its own words, and the reasoning is in +the document, not in the tool. + +## 3. What it is told, and what it refuses to guess + +Three things, none from a manifest ([ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md)): +the directory its checkout lives in (a resource the mesh gives it), the forge's address (the `git` +provision's binding, written into a file as `scheme://host:port`), and the repository's path on the +forge (a **setting**, `{"repository": "/"}`). Without the third it serves no tools and its +log says so. Public repositories only; it holds no credential. + +## 4. How it is found + +The console asks every module what it serves and lists `records_search` with a description that says +when to call it — *search the literal words of a symptom or a term before forming a hypothesis*. That +is 0025's second half in today's mesh: there is no store to be beside, and an agent choosing from a +tool list is the search. + +## 5. Where it runs + +Anywhere a node has the forge in reach; one assignment is enough, and a second on another machine is +harmless. The mesh session of design 15, when it exists, calls this rather than reading for itself. + +## How it is checked + +| Check | Defends | +|---|---| +| a phrase in one document of a repository the test makes comes back from that document, with the commit; a second commit on the origin is pulled and the next answer names it | ADR 0025's check, ADR 0153 | +| a path outside the checkout is refused; an empty search is refused | the reader reads the repository and nothing else | +| a sync against an unreachable origin leaves the checkout standing and says why | silence and success never look alike | +| live: through the console, `records_search` for a phrase that appears only in a design document here returns it | issue 006's closing check | + +## What this does not settle + +- Ranking or meaning. A search that understands a question is the session's job, not the reader's. +- A private repository. That is a credential the module would have to hold, and a decision about + what may read what. + +## References + +- [ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md) +- [ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md) +- [34 — The console](34-the-console.md) — what lists it +- [issue 006](../../04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md) diff --git a/04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md b/04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md index 2860a01..08eba24 100644 --- a/04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md +++ b/04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md @@ -1,7 +1,7 @@ --- status: located opened: 2026-08-23 -located-in: [hq README.md, and the agent ADR 0025 names — not built] +located-in: [mesh-catalog modules/records, mesh-catalog modules/mesh-console] fixed-by: amended-design: 02-DECISIONS/0025-the-design-record-is-read-not-copied.md --- @@ -182,3 +182,14 @@ a module ([ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-t reader ADR 0025 describes is the mesh session of design 15, which would answer through that surface like any tool. What closes this is still the check 0025 names: search for a phrase that appears only in a design document here, and get it back. + +## Built, 2026-09-30 + +[ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md): the +reader is a module, `records` (mesh-catalog PR 183), keeping a checkout of this repository from the +forge and answering `records_search`, `records_read`, `records_list`, `records_status` and +`records_sync` at the commit it read; the console lists them beside every other tool, which is where +"beside everything else" lives in a mesh with no store. Design +[35 — Reading the record](../../03-DESIGN/01-to-be/35-reading-the-record.md). The module's test runs +0025's check against a repository it makes; this record closes when the same check passes through the +console on the live mesh, and says so below. -- 2.54.0