Merge pull request 'ADR 0193: every bundle the runtime serves is launched, and the runtime knows no language; design 38 WP4d' (#325) from decision/0193-every-served-bundle-is-launched into main
This commit was merged in pull request #325.
This commit is contained in:
+4
@@ -87,6 +87,10 @@ that lets one bundle in that language be built, delivered and answer one tool on
|
|||||||
Anything beyond that is added when a module needs it. A skeleton that is not proven by one bundle
|
Anything beyond that is added when a module needs it. A skeleton that is not proven by one bundle
|
||||||
answering is not a skeleton; it is a promise.
|
answering is not a skeleton; it is a promise.
|
||||||
|
|
||||||
|
> **The mechanism changed — 2026-10-03, by ADR 0193.** §2's allowance that a TypeScript bundle may
|
||||||
|
> be imported into the runtime's own process is withdrawn: every served bundle is launched, and the
|
||||||
|
> build makes each served entrypoint executable. The rest of §2 stands.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
- The runtime gains a launcher beside its loader. The loader, the memberships, the seats and the
|
- The runtime gains a launcher beside its loader. The loader, the memberships, the seats and the
|
||||||
|
|||||||
+4
@@ -88,6 +88,10 @@ tools answering from the runtime, and the registration gate of
|
|||||||
[to-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP2 then refuses the
|
[to-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP2 then refuses the
|
||||||
container shape for every module, as ADR 0188 already provides.
|
container shape for every module, as ADR 0188 already provides.
|
||||||
|
|
||||||
|
> **The mechanism changed — 2026-10-03, by ADR 0193.** Decision 3's imported path — the environment
|
||||||
|
> handed to an imported bundle's contributor — has nothing left to do: every served bundle is
|
||||||
|
> launched, and a launched bundle's environment is its own. The decision stands.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
- The manifest gains one field on one artifact kind; the composer gains one more thing to resolve
|
- The manifest gains one field on one artifact kind; the composer gains one more thing to resolve
|
||||||
|
|||||||
+88
@@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
topic: what runs on it
|
||||||
|
status: accepted
|
||||||
|
date: 2026-10-03
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 02-DECISIONS/0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 193. Every bundle the runtime serves is launched, and the runtime knows no language
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
[ADR 0188](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)
|
||||||
|
§2 made a served bundle a process that speaks MCP over stdio, and kept one exception: a TypeScript
|
||||||
|
bundle may be imported into the runtime's own process, "a shortcut over the same contract". Every
|
||||||
|
module's tools today take the shortcut, and it is where the day's defects came from:
|
||||||
|
|
||||||
|
- [Issue 209](../04-ISSUES/209-a-bundles-own-sdk-copy-registers-into-a-registry-the-runtime-never-reads/00-report.md):
|
||||||
|
an imported bundle's own copy of the SDK registered into a registry the runtime never read; fixed
|
||||||
|
by a resolve hook that redirects every bundle's SDK import to the runtime's copy.
|
||||||
|
- [ADR 0192](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md):
|
||||||
|
thirty modules' environments in one process had to be kept apart by an SDK change and runtime
|
||||||
|
bookkeeping, where a process of its own has an environment of its own by construction.
|
||||||
|
- One faulty module can block or crash every other module's tools on its node.
|
||||||
|
|
||||||
|
And the shortcut ties the runtime to Node.js: only a JavaScript runtime can import JavaScript. The
|
||||||
|
operator's direction on 2026-10-03: *a module's tools are written in any language and the builder
|
||||||
|
builds them; the runtime runs them all and announces them; it should be fully language agnostic,
|
||||||
|
and node-tools can be rewritten in Go.*
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
1. **Keep the shortcut.** Rejected: it is the cause of the three defects above, and it pins the
|
||||||
|
runtime's language.
|
||||||
|
2. **Launch every served bundle; the runtime knows how to start each language** (`node` for a
|
||||||
|
`.js`, exec for a binary). Rejected: the runtime would hold a table of interpreters, and a
|
||||||
|
runtime in Go would carry Node.js's knowledge for nothing.
|
||||||
|
3. **Launch every served bundle, and the build makes each served entrypoint executable.** Chosen.
|
||||||
|
A compiled language's binary is executable already; for an interpreted one the toolchain writes
|
||||||
|
a launcher beside the entrypoint — for TypeScript, a file that imports the entrypoint and serves
|
||||||
|
what it registered over stdio, using the bundle's own SDK. The runtime execs what it is given.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**1. Every bundle the node's runtime serves is a child process speaking MCP over stdio.** The
|
||||||
|
in-process shortcut of ADR 0188 §2 is withdrawn. Everything else ADR 0188 §2 says — `tools/list`,
|
||||||
|
`tools/call`, `<seat>.<verb>` naming a seat's verb, the runtime serving each on the bus — stands.
|
||||||
|
|
||||||
|
**2. The runtime knows no language.** It is given an executable per served entrypoint and starts
|
||||||
|
it, with that bundle's environment ([ADR 0192](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md))
|
||||||
|
over its own words, and the module it serves it as. What makes an entrypoint executable is the
|
||||||
|
toolchain's business: a binary is one; an interpreted language's toolchain writes a launcher.
|
||||||
|
|
||||||
|
**3. A launched bundle is told the module it serves as**, so that what it lists unprefixed is that
|
||||||
|
module's own tools and a seat's verbs are always `<seat>.<verb>`, whichever it registered first.
|
||||||
|
|
||||||
|
**4. The runtime may be written in any language.** Nothing it does needs it to share a language
|
||||||
|
with a bundle; the mesh's runtime moves to Go, against this contract, once the contract is proven
|
||||||
|
in the runtime that exists.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- A process per served module per node. On the busiest machine that is a score of small children
|
||||||
|
where there was one process; a Go bundle costs a fraction of a Node.js one.
|
||||||
|
- The SDK resolve hook (issue 209) and the per-registration environment hand-off (ADR 0192 §3,
|
||||||
|
imported bundles) have nothing left to do and go; a launched bundle's environment is its own.
|
||||||
|
- A module's TypeScript tool code does not change: it registers as before, and the generated
|
||||||
|
launcher serves what it registered.
|
||||||
|
- A bundle that crashes or hangs takes only its own tools down, and is started again on its next
|
||||||
|
call, as ADR 0188 already provides for a launched bundle.
|
||||||
|
|
||||||
|
## How it is checked
|
||||||
|
|
||||||
|
| Rule | Checked by |
|
||||||
|
|---|---|
|
||||||
|
| Every served bundle is launched | the runtime's tests: a TypeScript bundle and a bundle in a second language, both launched, both answering over a real bus; a non-executable entrypoint is refused by name |
|
||||||
|
| The runtime knows no language | the runtime holds no interpreter: it execs the path it is given (code review; the Go runtime has no Node.js dependency at all) |
|
||||||
|
| A TypeScript served entrypoint is executable | the builder's test: a TypeScript bundle's served entrypoint has a launcher beside it, mode 0755 |
|
||||||
|
| A seat's verbs are named as the seat's whichever registers first | the SDK's test: a bundle registering its seat first and its own tools second lists `<seat>.<verb>` and its own tools unprefixed |
|
||||||
|
| Live | the packet filter's and intrusion prevention's seat verbs and the moved tools answer from launched bundles on every machine |
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md),
|
||||||
|
[ADR 0188](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md),
|
||||||
|
[ADR 0192](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md)
|
||||||
|
- [to-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP4d
|
||||||
@@ -292,6 +292,7 @@ python3 00-META/checks/index.py fail if stale
|
|||||||
- **0183** — [The Anthropic licence manager is a module holding a seat; it hands each node's agent its token over the bus, sealed; the controller and the host have no part](0183-the-anthropic-licence-manager-is-a-module-and-hands-tokens-to-the-agent-over-the-bus.md)
|
- **0183** — [The Anthropic licence manager is a module holding a seat; it hands each node's agent its token over the bus, sealed; the controller and the host have no part](0183-the-anthropic-licence-manager-is-a-module-and-hands-tokens-to-the-agent-over-the-bus.md)
|
||||||
- **0188** — [A module's own code is bundles in any language, and a tools bundle speaks MCP to the runtime](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)
|
- **0188** — [A module's own code is bundles in any language, and a tools bundle speaks MCP to the runtime](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)
|
||||||
- **0192** — [A tools bundle declares what it is given, and the runtime hands it to that bundle alone](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md)
|
- **0192** — [A tools bundle declares what it is given, and the runtime hands it to that bundle alone](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md)
|
||||||
|
- **0193** — [Every bundle the runtime serves is launched, and the runtime knows no language](0193-every-bundle-the-runtime-serves-is-launched-and-the-runtime-knows-no-language.md)
|
||||||
|
|
||||||
### How it is built
|
### How it is built
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ decisions:
|
|||||||
- 02-DECISIONS/0149-the-live-mesh-is-the-test-bed.md
|
- 02-DECISIONS/0149-the-live-mesh-is-the-test-bed.md
|
||||||
- 02-DECISIONS/0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md
|
- 02-DECISIONS/0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md
|
||||||
- 02-DECISIONS/0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md
|
- 02-DECISIONS/0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md
|
||||||
|
- 02-DECISIONS/0193-every-bundle-the-runtime-serves-is-launched-and-the-runtime-knows-no-language.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 38. Building the operator's machine
|
# 38. Building the operator's machine
|
||||||
@@ -261,6 +262,21 @@ Two traps met on the way: a tools bundle whose module declares no `tools` list m
|
|||||||
the composer delivers it nowhere while the build reports success; and a module whose builds are
|
the composer delivers it nowhere while the build reports success; and a module whose builds are
|
||||||
pinned to an old commit is left out of a merge's plan and must be built from `main` by hand.
|
pinned to an old commit is left out of a merge's plan and must be built from `main` by hand.
|
||||||
|
|
||||||
|
## WP4d — Every served bundle is launched; the runtime in Go
|
||||||
|
|
||||||
|
*mesh-sdk, mesh-controller, mesh-tools. [ADR 0193](../../02-DECISIONS/0193-every-bundle-the-runtime-serves-is-launched-and-the-runtime-knows-no-language.md).*
|
||||||
|
|
||||||
|
**In order.** The SDK's stdio loop serves what a bundle registered under the module it is told it
|
||||||
|
serves as. The builder writes, beside every TypeScript entrypoint, an executable launcher that
|
||||||
|
imports it and serves what it registered; the composer names the launcher where it named the
|
||||||
|
entrypoint. The runtime launches every served entrypoint and imports none; the resolve hook and the
|
||||||
|
per-registration environment go. Proven live on all four machines. Then the runtime is rewritten in
|
||||||
|
Go against the same contract — the bus, the memberships and seats, the launcher, the console's MCP
|
||||||
|
over HTTP — and replaces the TypeScript one, proven the same way.
|
||||||
|
|
||||||
|
**Proof.** The tests ADR 0193 names; live, every moved module's tools and both node seats answer from
|
||||||
|
launched bundles on every machine, and then do again from the Go runtime.
|
||||||
|
|
||||||
## WP4c — The module's own long-running code moves
|
## WP4c — The module's own long-running code moves
|
||||||
|
|
||||||
*Not yet broken down.* Twenty-three containers carry code that is not a tool: event handlers,
|
*Not yet broken down.* Twenty-three containers carry code that is not a tool: event handlers,
|
||||||
|
|||||||
Reference in New Issue
Block a user