Files
hq/04-ISSUES/117-a-modules-own-code-is-a-container-and-a-process/00-report.md
T
jschoubben 53b94c51bb The pointers back from what yesterday's records changed, which I missed twice
Three records were left describing a mechanism a new record had moved,
and a reader arrives at them by following a citation: 0066 still said a
routed name is written into every container after 0148 replaced that with
resolution; 0016 still read as though the lab were the test bed after
0149; and issues 109 and 135 said nothing about 0148 ending the copying
that 135's own fix made comparable. Each was a citation leading to the
wrong answer in a record that was not wrong about anything it decided.

This is the second time in one session. The playbook rule I added last
round did not stop it, so the convention is now written where the record
conventions live, with the shape to use and three worked examples — and
with the honest note that it is NOT machine-checked and cannot be from
`extends:` alone: 102 records extend another, 87 have no back-reference,
and that is correct, because extending usually means building on a
context. Making it mechanical means a record declaring the relationship
in frontmatter, which is a schema change and is not mine to decide.

Also: designs 18 and 20 claimed `updated:` dates from before I edited
them, and 117's `fixed-by` gained the commit beside the record.
2026-09-30 00:47:19 +02:00

9.3 KiB


status: resolved opened: 2026-09-25 located-in: [hq, mesh-catalog modules/showcase, mesh-sdk src/tools/index.ts, mesh-tools] fixed-by: hq 83791f0 (PR 196) — ADR 0150: a module's own code runs as supervised processes under the module's one account; designs 18 and 20 now cite it, and ADR 0047 carries a dated note pointing at it 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 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 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 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 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 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 is a sidecar crash-looping on a credential while its service served correctly. ADR 0093 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, and absent from every document under 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 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) exists to avoid. And run is an argv, where ADR 0005 refuses action because the link may not carry a command — a refusal 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.

Answered (2026-09-30)

ADR 0150 settles all three disagreements, and the design documents win two of them:

  1. Container or unit — a supervised process. ADR 0047's argument never required a container. It argued for a runtime per module, because a node-wide one could not hold a per-module account and per-module runtimes on one tool key would be handed calls for tools they do not have. A unit per module satisfies that exactly, and a unit runs as an account. The container was the mechanism to hand. ADR 0142 had already gone the same way for the mesh's own components, and a module is not a container.
  2. One process or several — several, under one account. 0047's "one module, one process, one account" carried its weight in the last clause; its stated worry was "not a second one to scope and seal", which is about a second identity. Processes sharing the module's one account create none. What a module may not have is two accounts.
  3. Whether the record was consulted — fixed rather than answered. Designs 18 and 20 now name 0150 in decisions:, and 0047 carries a dated note saying where its hosting form was settled, so neither door leads to the wrong answer any more.

What this does not fix. A module's code delivered as a binary is behind the same gap as the host's own (ADR 0141, accepted and not built): a container's code arrives by docker pull and this does not, so until delivery exists such a module is one somebody places by hand. 0150 records that as the cost of the decision, unpaid.