Issue 117: a module's own code is a container in one record and a process in another #110
@@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
status: open
|
||||||
|
opened: 2026-09-24
|
||||||
|
located-in: [mesh-controller module.json, mesh-host internal/apply]
|
||||||
|
fixed-by:
|
||||||
|
amended-design:
|
||||||
|
---
|
||||||
|
|
||||||
|
# 114 — Should the controller run as a container, or as a process the host supervises directly?
|
||||||
|
|
||||||
|
## What was observed
|
||||||
|
|
||||||
|
On the control-node, 2026-09-24, over a long session of operating the mesh through
|
||||||
|
`mesh-controller`'s CLI (build, push, plan, status, module moved). Every mutating step reached the
|
||||||
|
binary the same way: `docker exec mesh-controller /mesh-controller <command>` — because
|
||||||
|
`mesh-controller`'s own manifest declares its one resource as:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "id": "server", "type": "container", "name": "mesh-controller", "network": "host", "args": ["serve"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Two things about that declaration are worth naming together, because neither is a problem on its
|
||||||
|
own and the combination is what raises the question:
|
||||||
|
|
||||||
|
- **`network: host`.** The controller does not use container network isolation, which is the
|
||||||
|
property a `container` resource type usually buys over a `process` one. It runs with the node's
|
||||||
|
own network namespace either way.
|
||||||
|
- **It is the mesh's single point of coordination.** [`03-DESIGN/01-to-be/06-the-controller.md`](../../03-DESIGN/01-to-be/06-the-controller.md)
|
||||||
|
is explicit: "one node runs it, and nothing takes over" — no election, no quorum, no failover;
|
||||||
|
recovery is restore, not failover.
|
||||||
|
|
||||||
|
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) gives the host — the one thing tier 0 requires
|
||||||
|
to be a real system daemon — exactly this reasoning for refusing to run in a container: *"installing
|
||||||
|
the container runtime is a step of the bootstrap, so a host inside a container would need the thing
|
||||||
|
it exists to install."* The controller is one tier up and does not install the runtime, but it
|
||||||
|
shares the profile that argument turns on: something the rest of the mesh's operation depends on,
|
||||||
|
sharing fate with a runtime that is not itself.
|
||||||
|
|
||||||
|
## Why it matters beyond this instance
|
||||||
|
|
||||||
|
Practically, tonight: every controller interaction was raw shell into a container (`docker exec`),
|
||||||
|
not a first-class surface — no logs command beyond `docker logs`, no `systemctl status`, and a
|
||||||
|
session permission classifier that (correctly) treats arbitrary shell into a container as needing
|
||||||
|
sign-off every time, unlike an ordinary supervised process. That friction is a symptom, not the
|
||||||
|
issue itself.
|
||||||
|
|
||||||
|
The actual question is whether `type: container` is buying the controller anything here besides
|
||||||
|
image-based delivery and a restart policy — both of which [ADR 0005](../../02-DECISIONS/0005-the-node-host.md)'s
|
||||||
|
launcher pattern already describes as buildable directly into the host's own supervision (restart on
|
||||||
|
exit, count consecutive failures, roll back after too many, halt after that), for the host's own
|
||||||
|
unit. If the controller were declared `type: process` instead — still built and versioned through
|
||||||
|
the same delivery pipeline, just executed on the node and supervised by the host the way the host
|
||||||
|
supervises itself — it would stop sharing fate with the container runtime's health (restarts,
|
||||||
|
upgrades, disk pressure evicting containers) for the one piece of software whose absence the rest of
|
||||||
|
the mesh is designed to tolerate but nothing is designed to *want*.
|
||||||
|
|
||||||
|
This is squarely a question, not a claim that today's shape is wrong: [ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md)
|
||||||
|
already tolerates the controller being down by construction (nodes reconcile from their own
|
||||||
|
last-applied state), which may make the container-runtime coupling moot in practice. Nobody has
|
||||||
|
checked.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
- Does `mesh-host`'s `process` resource type already support the restart/failure-counting semantics
|
||||||
|
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) describes for the host's own launcher well
|
||||||
|
enough for something this central — or would this need host-side work first?
|
||||||
|
- With `network: host` already in use, what does `type: container` provide the controller today that
|
||||||
|
`type: process` would not?
|
||||||
|
- Is there a real circularity risk — the controller's own health depending on the container runtime
|
||||||
|
it (indirectly, via the host) manages — or does "one node runs it, nothing takes over" already make
|
||||||
|
a controller outage tolerable regardless of which resource type it is?
|
||||||
|
- If the answer is "keep it a container," what does that answer, precisely, that this issue asked —
|
||||||
|
so the next person who notices the same asymmetry finds it answered rather than open again?
|
||||||
|
|
||||||
|
## The general case
|
||||||
|
|
||||||
|
[Issue 117](../117-a-modules-own-code-is-a-container-and-a-process/00-report.md) is the same
|
||||||
|
question asked of every module rather than of the controller: a module's own code is a `container`
|
||||||
|
in [ADR 0047](../../02-DECISIONS/0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md)
|
||||||
|
and a `process` in the to-be design, and no record moves it. Its
|
||||||
|
[diagnosis](../117-a-modules-own-code-is-a-container-and-a-process/01-diagnosis.md) answers the
|
||||||
|
first open question above: the host's `process` shape is built, applied and tested, including the
|
||||||
|
restart and run-to-completion semantics — so this would not need host-side work first.
|
||||||
|
|
||||||
|
The two do not collapse into one. The controller is not a code-carrying sidecar, and `network: host`
|
||||||
|
is what makes the asymmetry visible here and nowhere else.
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
status: located
|
||||||
|
opened: 2026-09-25
|
||||||
|
located-in: [hq, mesh-catalog modules/showcase, mesh-sdk src/tools/index.ts, mesh-tools]
|
||||||
|
fixed-by:
|
||||||
|
amended-design:
|
||||||
|
---
|
||||||
|
|
||||||
|
# 117 — A module's own code is a container in one record and a process in another
|
||||||
|
|
||||||
|
## What was observed
|
||||||
|
|
||||||
|
Asked what the "sidecar" is — the second container a code-carrying module runs beside its
|
||||||
|
service — and whether a supervised process would do instead. Reading the records to answer it,
|
||||||
|
the repository answers both ways, and nothing reconciles them.
|
||||||
|
|
||||||
|
| record | status | what runs a module's own code |
|
||||||
|
|---|---|---|
|
||||||
|
| [ADR 0047](../../02-DECISIONS/0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md) | **accepted**, 2026-09-04 | "a **container**, the tool runtime carrying that module's compiled code" — one module, one process, one account; events and tools in that same process, "not a second one to scope and seal" |
|
||||||
|
| [`01-to-be/18-building-a-module.md`](../../03-DESIGN/01-to-be/18-building-a-module.md) | proposed, 2026-09-21 | a resource type table in which `container` is "an image" and **`process`** is "**its own code**, in three modes", whose default mode is "a unit restarted when it exits", supervised by the machine |
|
||||||
|
| [`01-to-be/20-writing-a-module.md`](../../03-DESIGN/01-to-be/20-writing-a-module.md) | proposed, 2026-09-21 | one module declaring **four** `process` resources — events, tools, provisioner, a scheduled ingest — each with its own `run` argv, and the sentence "it is why these are `process` rather than four containers" |
|
||||||
|
|
||||||
|
Three disagreements, not one:
|
||||||
|
|
||||||
|
1. **Container or unit.** ADR 0047 chose a container and said why: a node-wide runtime loading
|
||||||
|
every module's code could not hold a per-module account, so the runtime is per-module. The
|
||||||
|
design docs choose a supervised unit running an argv and give no reason, because they do not
|
||||||
|
record that they are choosing.
|
||||||
|
2. **One process or several.** ADR 0047's "one module, one process, one account" is the whole
|
||||||
|
content of its second and third sections. The worked guide declares four for one module and
|
||||||
|
presents four as the point.
|
||||||
|
3. **Whether the record was consulted at all.** Neither design doc names ADR 0047 in
|
||||||
|
`decisions:`. No record supersedes or extends it on this. **The string `process` as a resource
|
||||||
|
type appears in no decision record** — the shape exists only in two `proposed` design docs.
|
||||||
|
|
||||||
|
Meanwhile the thing as built is the container. [ADR 0029](../../02-DECISIONS/0029-a-network-is-a-shape-because-an-action-cannot-be-undone.md)
|
||||||
|
records that "anything that is a service plus a sidecar currently has to publish a port to talk
|
||||||
|
to itself," which is one of the things the host's `network` shape was added for.
|
||||||
|
[Issue 113's diagnosis](../113-the-object-stores-images-were-withdrawn-upstream/01-diagnosis.md)
|
||||||
|
found a catalogue module declaring "two container resources," the second a runtime sidecar
|
||||||
|
"pinned at an all-zeros digest, meaning nothing was ever published for it."
|
||||||
|
[Issue 095](../095-a-module-assigned-after-genesis-has-no-broker-account/00-report.md) is a
|
||||||
|
sidecar crash-looping on a credential while its service served correctly.
|
||||||
|
[ADR 0093](../../02-DECISIONS/0093-a-fixture-that-runs-a-modules-runtime-carries-its-name.md)
|
||||||
|
records that a bed wanting "a sidecar without its server raises the server."
|
||||||
|
|
||||||
|
### And the word is in no glossary
|
||||||
|
|
||||||
|
"Sidecar" appears sixteen times across five records — two decisions and three issues. It is
|
||||||
|
absent from [`00-META/glossary.md`](../../00-META/glossary.md), and absent from every document
|
||||||
|
under [`03-DESIGN/`](../../03-DESIGN/), in both layers. ADR 0047, which creates the thing, never
|
||||||
|
uses the word; it says "runtime process" and "runtime container". The glossary's own rule is that
|
||||||
|
"a new name for an existing thing lands here first, in the same change that introduces it in
|
||||||
|
code," and the page exists because "the terms kept drifting in conversation." A reader asking
|
||||||
|
what the sidecar is has nowhere in the design layer to look, which is how this was found.
|
||||||
|
|
||||||
|
## Why it matters beyond this instance
|
||||||
|
|
||||||
|
- **A module author reading the current guide writes a `process`; the catalogue as built declares
|
||||||
|
a `container`.** [`20-writing-a-module.md`](../../03-DESIGN/01-to-be/20-writing-a-module.md) is
|
||||||
|
a worked guide with a manifest in it. Whichever of the two is wrong, somebody follows it.
|
||||||
|
- **The cost of the container shape is paid in four places and totalled in none.** A published
|
||||||
|
image per code-carrying module, a network so a module can reach itself, a bed that cannot run a
|
||||||
|
runtime without raising the server it manages, and a credential failure that presents as the
|
||||||
|
module's own bug. Each record argues its own piece is worth paying. No record puts them beside
|
||||||
|
the alternative.
|
||||||
|
- **Both shapes carry a cost the other does not, and neither is written down.** A container
|
||||||
|
carries its own interpreter; a `process` declaring `run: ["node", "index.js"]` needs an
|
||||||
|
interpreter present on the machine, which is the machine dependency the statically linked host
|
||||||
|
([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)) exists to avoid. And `run` is an argv,
|
||||||
|
where [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) refuses `action` because the link may
|
||||||
|
not carry a command — a refusal [`18-building-a-module.md`](../../03-DESIGN/01-to-be/18-building-a-module.md)
|
||||||
|
restates on the same page that it introduces `process`.
|
||||||
|
- **This is the repository's own named failure mode, in its own records.** `cycle.py` enforces
|
||||||
|
that a to-be doc names *at least one* decision. Both docs do, so both pass, while introducing a
|
||||||
|
resource type no decision records and contradicting an accepted one. The rule is "no design
|
||||||
|
without a decision"; the check is "no design without *a* decision." An unenforced rule is
|
||||||
|
indistinguishable from a wrong one, and these two documents are what that gap looks like when
|
||||||
|
something walks through it.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
- Which is the decision — container or supervised unit? If the design docs are right, ADR 0047
|
||||||
|
needs superseding rather than quietly outliving. If ADR 0047 is right, two proposed documents
|
||||||
|
and a worked manifest describe a resource type that does not exist.
|
||||||
|
- Is one account per module satisfied by a per-module *unit* as well as a per-module *container*?
|
||||||
|
ADR 0047's argument rules out a node-wide runtime sharing one account. It does not appear to
|
||||||
|
rule out a unit holding one scoped credential, and nothing has said so either way.
|
||||||
|
- If several processes for one module are right, what holds the accounts? ADR 0047 refused "a
|
||||||
|
second one to scope and seal" for events beside tools. Four processes are four somethings.
|
||||||
|
- How does a `process` get its interpreter, and does declaring one reintroduce the machine
|
||||||
|
dependency the host is built to avoid?
|
||||||
|
- Is `run` an argv the link may carry, given `action` is refused for being one? If the answer is
|
||||||
|
that a `process` reconciles and an `action` does not, that distinction is not written down.
|
||||||
|
- What is the thing called, and where does the design layer describe it? Whichever shape wins, no
|
||||||
|
document in either layer currently says a code-carrying module runs a second thing beside its
|
||||||
|
service.
|
||||||
|
- **How would this have been caught?** A decision and a design doc disagreeing on a resource type
|
||||||
|
is mechanically checkable: the resource types a design doc names are a closed set, and every
|
||||||
|
member of it either appears in a decision or does not. Whether that check is worth writing is
|
||||||
|
part of this issue, not settled by it.
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
# Diagnosis — 117
|
||||||
|
|
||||||
|
## Which trees were searched, 2026-09-25
|
||||||
|
|
||||||
|
Named first, because [issue 113](../113-the-object-stores-images-were-withdrawn-upstream/01-diagnosis.md)
|
||||||
|
is the record of reporting absence in one repository as absence in the mesh.
|
||||||
|
|
||||||
|
| Searched | At |
|
||||||
|
|---|---|
|
||||||
|
| `mesh-host`, `mesh-catalog`, `mesh-tools`, `mesh-sdk`, `mesh-controller` | `main`, fresh shallow clones |
|
||||||
|
| `hq` | `main`, and the two branches named under finding 7 |
|
||||||
|
|
||||||
|
**Not searched:** the private migration repository; the open pull requests on the catalogue and
|
||||||
|
the controller; any branch of a code repository other than `main`. A statement below about "the
|
||||||
|
catalogue" is a statement about its `main`.
|
||||||
|
|
||||||
|
## The report's central question is answered: the shape exists
|
||||||
|
|
||||||
|
`mesh-host` `internal/declaration/declaration.go` defines `TypeProcess Type = "process"`.
|
||||||
|
`internal/apply/process.go` applies it — it writes the unit, writes the timer for a scheduled one,
|
||||||
|
and gates what follows a run-once one. It has tests of its own in both packages. The resource
|
||||||
|
carries a bundle `source` with a `digest`, a `run` argv, `env` and `env-file`, a `user`,
|
||||||
|
`restart-on`, and the `run-once` and `schedule` modifiers.
|
||||||
|
|
||||||
|
So the report's alternative — "if ADR 0047 is right, two proposed documents and a worked manifest
|
||||||
|
describe a resource type that does not exist" — is **disproven**. It exists, it is implemented, it
|
||||||
|
is tested, and the host's vocabulary is now **twelve** shapes rather than the nine
|
||||||
|
[ADR 0029](../../02-DECISIONS/0029-a-network-is-a-shape-because-an-action-cannot-be-undone.md)
|
||||||
|
counted.
|
||||||
|
|
||||||
|
## The enforcement ADR 0029 asked for is intact, and it recorded this gap rather than closing it
|
||||||
|
|
||||||
|
ADR 0029 said "the vocabulary is nine, and the count moves with a record. The test that asserts it
|
||||||
|
names this one." That test exists — `internal/declaration/declaration_test.go` asserts the count is
|
||||||
|
twelve and fails with the reason rather than a number. Above the assertion, a paragraph per
|
||||||
|
addition names what made it one:
|
||||||
|
|
||||||
|
| shape | the test names |
|
||||||
|
|---|---|
|
||||||
|
| `network`, ninth | ADR 0029 |
|
||||||
|
| `access`, tenth | ADR 0051 |
|
||||||
|
| **the eleventh** | **`03-DESIGN/01-to-be/18-building-a-module.md`** — a design document, `status: proposed` |
|
||||||
|
| `opening`, twelfth | ADR 0100 |
|
||||||
|
|
||||||
|
The eleventh is this one. The test still calls it `daemon`, the code calls it `TypeProcess`, and
|
||||||
|
its paragraph is the only one that names a design document where the others name a decision.
|
||||||
|
Independently: in `declaration.go`, `TypeProcess` is the **only** shape in the vocabulary whose doc
|
||||||
|
comment cites no ADR — `network` cites 0029, `access` 0051, `opening` 0100, `user` and the refusal
|
||||||
|
of `action` cite 0005.
|
||||||
|
|
||||||
|
**So ADR 0029's mechanism worked exactly as designed and was not enough.** It requires every
|
||||||
|
addition to name something. It does not require that something to be a decision, and the one
|
||||||
|
addition that named a proposed design document instead is the one this issue is about.
|
||||||
|
|
||||||
|
### A correction to this trail, recorded because it was one grep from being a finding
|
||||||
|
|
||||||
|
The first search here was for `len(Vocabulary())` and found nothing, and the working conclusion for
|
||||||
|
two steps was that no count assertion existed any more — which would have been written up as "the
|
||||||
|
mechanism ADR 0029 relied on is gone." It is not gone. The test binds the slice to a local variable
|
||||||
|
first, so the assertion reads `len(speaks) != 12`. The claim was wrong, it was caught by reading the
|
||||||
|
file rather than by grepping it, and the shape of the error is the same one issue 113 recorded: a
|
||||||
|
negative search result read as a fact about the world.
|
||||||
|
|
||||||
|
## The argument the report asked for already exists, in a test comment
|
||||||
|
|
||||||
|
The report asked why a container rather than a supervised process, and said the reasoning was not
|
||||||
|
written down. It is — in `declaration_test.go`, as the eleventh shape's paragraph:
|
||||||
|
|
||||||
|
> Running code of one's own meant a `container` and therefore an image; running a script meant a
|
||||||
|
> `service` and a unit somebody else had to install. One intent — run this and keep it running —
|
||||||
|
> expressed two unrelated ways, with the hosting chosen before anything could be declared. […] It
|
||||||
|
> is a full-host shape rather than a portable one: it needs a process supervisor to install into.
|
||||||
|
> It does NOT need a container runtime, which is the point — only software that genuinely needs
|
||||||
|
> isolation asks for a container.
|
||||||
|
|
||||||
|
That is a decision's Context and Consequences, in a Go comment, in another repository. Nothing in
|
||||||
|
`02-DECISIONS/` contains it. `TypeProcess`'s own doc comment adds the rest — that three modes beat
|
||||||
|
three kinds, and that a first draft added a `daemon` for the long-running case alone.
|
||||||
|
|
||||||
|
## The catalogue is containers, and the one exception is the reference module
|
||||||
|
|
||||||
|
71 modules on `main`. Counting the `type` of every declared resource:
|
||||||
|
|
||||||
|
| `container` | `process` |
|
||||||
|
|---|---|
|
||||||
|
| 115 | **3** |
|
||||||
|
|
||||||
|
All three `process` resources are in **one** module: `showcase` — the module
|
||||||
|
[`20-writing-a-module.md`](../../03-DESIGN/01-to-be/20-writing-a-module.md) is a worked guide for.
|
||||||
|
|
||||||
|
### And in that module, the tools do not run
|
||||||
|
|
||||||
|
`showcase` declares its migrate, server and reporting steps as `process`. Its fourth resource, the
|
||||||
|
one for tools, is a **`container`** — and its image is the module's `helper` artifact, which the
|
||||||
|
same manifest declares as `kind: upstream` from a bare distribution base. Its command is
|
||||||
|
`sleep infinity`. It mounts the broker credential and sets the variable naming it, and runs nothing.
|
||||||
|
|
||||||
|
Meanwhile the module's `code` bundle declares six entrypoints. Three are run by the three `process`
|
||||||
|
resources. The tools entrypoint and the provisioner entrypoint are **run by no resource in the
|
||||||
|
manifest.**
|
||||||
|
|
||||||
|
Two consequences worth stating separately:
|
||||||
|
|
||||||
|
- **The worked guide does not match the module it documents.** The guide shows four `process`
|
||||||
|
resources, the fourth being `{"id": "tools", "type": "process"}`. The module has three and a
|
||||||
|
container.
|
||||||
|
- **This is the condition ADR 0047 was written to end, in a new shape.** That record's Context says
|
||||||
|
the conversion "produced tools and events that, as it stands, never execute," and its first
|
||||||
|
Consequence is that they become runnable. In the reference module they do not execute again —
|
||||||
|
not for want of a runtime this time, but because nothing declares one that runs them.
|
||||||
|
|
||||||
|
## The harness has no per-module boundary, and nothing refuses a second module
|
||||||
|
|
||||||
|
This is where ADR 0047's isolation argument is load-bearing, so it was checked rather than assumed.
|
||||||
|
|
||||||
|
- `mesh-sdk` `src/tools/index.ts`: `serveTools` iterates `collectTools()` over a module-level
|
||||||
|
registration array and serves **every registered module's** tools over the **one** `broker` it
|
||||||
|
was handed.
|
||||||
|
- `mesh-tools` `src/main.ts`: the modules to load come from one variable as a **comma-separated
|
||||||
|
list**, and the runtime sets its module and node identity from the **single** credential.
|
||||||
|
- `mesh-sdk` `src/events/index.ts`: an emitted event's `x-source` is stamped from that single
|
||||||
|
module identity.
|
||||||
|
|
||||||
|
Put together: load two modules into one runtime and everything the second emits is attributed to
|
||||||
|
the first, because there is one credential and the identity comes from it. That is precisely the
|
||||||
|
failure ADR 0047 predicted — "able to emit as any of them" — reached by a different route, since
|
||||||
|
the credential is correct and there is only one of it for two modules. **Nothing in either
|
||||||
|
repository refuses the second module**, and no test asserts that a runtime serves one.
|
||||||
|
|
||||||
|
### Ruled out, in fairness to the implementation
|
||||||
|
|
||||||
|
- **The serving key conforms.** ADR 0047 replaced a single `tools.invoke` dispatch with a per-tool
|
||||||
|
key, and the SDK does that: a tool is served on `<module>.<tool>` with the account scoped
|
||||||
|
`serve.<module>.*`. The superseded `tools.invoke` survives only in **prose** — the doc comment
|
||||||
|
directly above the conforming code, and the `mesh-tools` README, which also describes the runtime
|
||||||
|
as per-node. The code is ahead of its own documentation.
|
||||||
|
- **The credential shape conforms.** The sealed per-module credential file is preferred in code, and
|
||||||
|
the plain URL is documented as the bootstrap case before a module has an account — not the
|
||||||
|
ordinary path.
|
||||||
|
|
||||||
|
So the account is the right shape and the key is the right shape. It is the **process boundary**
|
||||||
|
that is declared nowhere and enforced by nothing.
|
||||||
|
|
||||||
|
## An unmerged report already asks the narrow version of this
|
||||||
|
|
||||||
|
Branch `issue/113-controller-container-or-process`, one commit, 2026-09-24, adds a report titled
|
||||||
|
**"Should the controller run as a container, or as a process the host supervises directly?"** with
|
||||||
|
`located-in: [mesh-controller module.json, mesh-host internal/apply]`. Its observation is that the
|
||||||
|
controller is declared a `container` with `network: host` — so container network isolation, the
|
||||||
|
property that resource type usually buys, is not in use — and it asks what `type: container` buys
|
||||||
|
that `type: process` would not.
|
||||||
|
|
||||||
|
It was unmerged and numbered 113, which is taken. A sibling branch,
|
||||||
|
`issue/113-record-the-repin-and-fold-114`, is why `114` was free.
|
||||||
|
|
||||||
|
**That report and this one are the instance and the general condition**, and they do not conflict:
|
||||||
|
it asks about one module that is not a code-carrying sidecar at all, and reaches the same question
|
||||||
|
from the opposite end. So it lands in this change as
|
||||||
|
[issue 114](../114-should-the-controller-be-a-container-or-a-process/00-report.md), its commit and
|
||||||
|
authorship intact, with a section pointing here — rather than being folded in and losing the
|
||||||
|
`network: host` observation, which is its own and is not reproduced above.
|
||||||
|
|
||||||
|
This diagnosis answers its first open question. The host's `process` shape does support what
|
||||||
|
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) describes for the host's own launcher — the
|
||||||
|
unit, the timer, restart, and run-to-completion gating are implemented and tested — so that report
|
||||||
|
does not need host-side work before it can be decided.
|
||||||
|
|
||||||
|
## What is located, and what is not
|
||||||
|
|
||||||
|
**Located — and it is not a code defect.** The implementation and the design layer agree with each
|
||||||
|
other; the **decision record is what is missing**, and the accepted record that occupies its place
|
||||||
|
says the other thing. ADR 0047 is `accepted`, cited by the module protocol, and unsuperseded, while
|
||||||
|
the host it describes has had a purpose-built shape for a module's own code since the eleventh
|
||||||
|
vocabulary entry.
|
||||||
|
|
||||||
|
| Owner | What is theirs |
|
||||||
|
|---|---|
|
||||||
|
| `hq` | the missing record for the `process` shape; ADR 0047 left standing; the worked guide that does not match the module |
|
||||||
|
| `mesh-catalog modules/showcase` | tools and provisioner entrypoints that no resource runs; a tools container that sleeps |
|
||||||
|
| `mesh-sdk src/tools/index.ts` | several modules served over one credential, unrefused and untested; a doc comment describing a superseded dispatch |
|
||||||
|
| `mesh-tools` | a README describing a per-node multi-module runtime the code no longer prefers |
|
||||||
|
|
||||||
|
**Not located, and deliberately open:** whether `process` or `container` is *right* for a module's
|
||||||
|
own code. This diagnosis establishes that the question was answered in practice and never recorded
|
||||||
|
— not which answer is correct. The arguments on both sides now exist in writing; they exist in a
|
||||||
|
test comment and a proposed design document, and one of them contradicts an accepted decision.
|
||||||
|
|
||||||
|
## What would close it
|
||||||
|
|
||||||
|
1. A decision record for the `process` shape, carrying the argument currently in
|
||||||
|
`declaration_test.go`, and saying what becomes of ADR 0047 — superseded in whole, or in the part
|
||||||
|
that names a container.
|
||||||
|
2. `18-building-a-module.md` and `20-writing-a-module.md` naming that record in `decisions:`, and
|
||||||
|
the worked manifest agreeing with the module.
|
||||||
|
3. The eleventh shape's paragraph in the vocabulary test naming a decision, like the other three.
|
||||||
|
4. **How the rule is checked, since a rule states how it is checked:** every shape in the host's
|
||||||
|
vocabulary names a decision, asserted where the count is already asserted — which turns "no
|
||||||
|
design without a decision" into something stronger than "no design without *a* decision" for
|
||||||
|
the one vocabulary where each entry is a security decision.
|
||||||
|
5. Whether a runtime may serve more than one module answered either way, and asserted — a refusal
|
||||||
|
if not, a test that two modules' events keep their own source if so.
|
||||||
Reference in New Issue
Block a user