Merge pull request 'Issue 354: a decided rename was never scheduled, and a record that defers work names its issue' (#255) from issues/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live into main
This commit was merged in pull request #255.
This commit is contained in:
@@ -61,7 +61,19 @@ number is how a record is cited, and `main` lags every open pull request, so two
|
||||
allocate the same one). And a core issue — one opened from 2026-10-07 whose `located-in` names a core
|
||||
repository — resolves only with `replay:` (an id in mesh-lab's replays register) or `replay-none:` saying
|
||||
why none is possible ([ADR 0237](../../02-DECISIONS/0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md));
|
||||
it failed on a resolved core issue carrying neither before it passed. `python3 00-META/checks/cycle.py`
|
||||
it failed on a resolved core issue carrying neither before it passed. And an accepted record that puts work
|
||||
off — "until renamed", "left for later", "not decided here", "postponed", "future work", "deferred", "a later
|
||||
change" — names the issue that tracks it, as tracking it ("tracked by issue N", or "issue N tracks"), in that
|
||||
paragraph or list item, or in a dated progressive insight that speaks about it: one in the same section, or one
|
||||
that quotes it. An issue cited for another reason tracks nothing, and one insight does not excuse every deferral
|
||||
in a record. A rejected option is not work put off and is not held
|
||||
([issue 354](../../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md):
|
||||
ADR 0244 deferred the code renames with no issue, and two days later the operator believed them done). Held to
|
||||
it: a record dated from 2026-10-09, and an older one corrected under the rule. Measured from ADR 0244's date
|
||||
it found deferrals without an issue in ADR 0241, 0244 and 0270; held from 2026-10-09 it failed on 0270 and 0273
|
||||
(whose "not decided here" sits beside a citation of issue 340 that tracks none of it), and on 0244's four
|
||||
deferral places once its insight brought it under the rule — on three of them while the insight, in §6, named
|
||||
the issue without quoting them — before each named its issue (355, 357, 354). `python3 00-META/checks/cycle.py`
|
||||
|
||||
## words.py
|
||||
|
||||
|
||||
@@ -56,6 +56,72 @@ CORE = re.compile(r"\b(mesh-controller|mesh-host|mesh-tools|mesh-sdk|node-tools|
|
||||
REPLAYS_FROM = "2026-10-07"
|
||||
REPLAY_ID = re.compile(r"^R\d+\b")
|
||||
|
||||
# A decision that defers work names the issue that tracks it (issue 354). ADR 0244 left the code
|
||||
# renames of the node-engine and the tool runner "for a later rename", with no issue, owner or date;
|
||||
# two days later the operator believed them done, and nothing in the record said otherwise. A deferral
|
||||
# nothing tracks is indistinguishable from work that was done. Held to it: an accepted record dated
|
||||
# from DEFERRALS_FROM, and an older one corrected under the rule (a progressive insight dated from then
|
||||
# on): an edit made under the rule is held to it. The phrases are the ones records use to put work off;
|
||||
# "for later" alone is not one, because records say "never queued for later" and "too narrow for later".
|
||||
# The tracking issue is named as tracking it, beside the deferral or in a dated insight in the same section
|
||||
# or quoting it (ISSUE_REF, covers).
|
||||
DEFERRALS_FROM = "2026-10-09"
|
||||
DEFERRAL = re.compile(r"(?i)until (?:it is |they are |the code (?:is )?)?renamed|until the code rename|"
|
||||
r"\ba later rename|not renamed here|identifier to rename|"
|
||||
r"left (?:for|to) (?:a )?later|not decided here|\bpostponed\b|future work|"
|
||||
r"\bdeferred\b|later work|not built here|\ba later change")
|
||||
# The issue that tracks a deferral is named as tracking it -- "tracked by issue N", or "issue N tracks" --
|
||||
# because a record cites issues for many reasons, and an issue cited as context beside a deferral (ADR 0273
|
||||
# names issue 340 in the very sentence that leaves a channel undecided) tracks nothing.
|
||||
ISSUE_REF = re.compile(r"(?i)tracked (?:by|in|as)\s+\[?issue (\d{3})|\[?issue (\d{3})\]?(?:\([^)]*\))?\s+tracks\b")
|
||||
INSIGHT = re.compile(r"\*\*Progressive insights?[ \t]*[—–-][ \t]*(\d{4}-\d{2}-\d{2})")
|
||||
|
||||
|
||||
def paragraphs(path):
|
||||
"""A record's body as (first line number, section heading, text) paragraphs: frontmatter and fenced
|
||||
code left out, a block quote's markers stripped, so a quoted insight reads as the paragraph it is. The
|
||||
section is the nearest heading above the paragraph; a heading is its own paragraph and its own section."""
|
||||
text = open(path, encoding="utf-8").read()
|
||||
m = re.match(r"^---\n.*?\n---\n?", text, re.S)
|
||||
body, first = (text[m.end():], text[: m.end()].count("\n") + 1) if m else (text, 1)
|
||||
body = re.sub(r"(?ms)^\s*```.*?^\s*```", lambda x: re.sub(r"[^\n]", " ", x.group(0)), body)
|
||||
out, line, section = [], first, ""
|
||||
for chunk in body.split("\n\n"):
|
||||
stripped = "\n".join(re.sub(r"^\s*>\s?", "", l) for l in chunk.split("\n")).strip()
|
||||
if stripped:
|
||||
heading = re.match(r"^#{1,6}\s+(.*)", stripped)
|
||||
if heading:
|
||||
section = heading.group(1).strip()
|
||||
# A list's top-level items are units of their own: a Consequences list is one paragraph, and an
|
||||
# issue named in one item tracks nothing in the next.
|
||||
items = re.split(r"\n(?=[-*] |\d+\. )", stripped)
|
||||
at = line + chunk[: len(chunk) - len(chunk.lstrip())].count("\n")
|
||||
for item in items:
|
||||
out.append((at, section, item))
|
||||
at += item.count("\n") + 1
|
||||
line += chunk.count("\n") + 2
|
||||
return out
|
||||
|
||||
|
||||
QUOTE = re.compile(r"\"([^\"]{12,})\"|\u201c([^\u201d]{12,})\u201d")
|
||||
|
||||
|
||||
def flat(text):
|
||||
"""Text compared for a quotation: markup and case dropped, whitespace collapsed."""
|
||||
return re.sub(r"\s+", " ", re.sub(r"[*`_]", "", text)).strip().lower()
|
||||
|
||||
|
||||
def covers(insight_section, insight_text, section, text):
|
||||
"""An insight speaks about a deferral paragraph when it stands in the same section, or quotes it."""
|
||||
if insight_section == section:
|
||||
return True
|
||||
body = flat(text)
|
||||
return any(flat(a or b) in body for a, b in QUOTE.findall(insight_text))
|
||||
|
||||
|
||||
def issue_refs(text, issues):
|
||||
return {n for a, b in ISSUE_REF.findall(text) for n in (a, b) if n and n in issues}
|
||||
|
||||
|
||||
def rel(path):
|
||||
return os.path.relpath(path, ROOT)
|
||||
@@ -233,6 +299,37 @@ def main():
|
||||
bad(path, "an accepted decision nothing in the cycle cites -- give it a home in a "
|
||||
"design doc's decisions:, a research became:, an issue, or 00-META")
|
||||
|
||||
# ---- deferrals -------------------------------------------------------------------
|
||||
# A paragraph or list item that puts work off names the issue that tracks it, or a dated
|
||||
# progressive insight that speaks about it (its section, or a quotation of it) does. An issue that has
|
||||
# since resolved still counts: the deferred work landed, which is what the pointer was for.
|
||||
for name, (path, status) in records.items():
|
||||
front = frontmatter(path) or {}
|
||||
decided = str(front.get("date") or "")
|
||||
if status != "accepted":
|
||||
continue
|
||||
paras = paragraphs(path)
|
||||
insights = [(d, sec, text) for line, sec, text in paras for d in INSIGHT.findall(text)]
|
||||
held = decided >= DEFERRALS_FROM or any(d >= DEFERRALS_FROM for d, _, _ in insights)
|
||||
if not held:
|
||||
continue
|
||||
# A dated insight naming an issue covers the deferrals it speaks about -- those in its own section,
|
||||
# or ones it quotes -- and no other: one insight must not excuse every deferral in the record.
|
||||
tracking = [(sec, text) for d, sec, text in insights if issue_refs(text, seen)]
|
||||
for line, section, text in paras:
|
||||
m = DEFERRAL.search(text)
|
||||
# A rejected option is a decision not to do it, not work put off.
|
||||
if re.match(r"(?i)(options? rejected|considered options|rejected)", section):
|
||||
continue
|
||||
if not m or issue_refs(text, seen):
|
||||
continue
|
||||
if any(covers(isec, itext, section, text) for isec, itext in tracking):
|
||||
continue
|
||||
bad(path, "line %d defers work (%r) and names no issue that tracks it -- a deferral nothing "
|
||||
"tracks reads as done (issue 354). Name the issue beside it, with its owner, or in a "
|
||||
"dated progressive insight that says what it tracks"
|
||||
% (line + text.count("\n", 0, m.start()), m.group(0)))
|
||||
|
||||
checked = (
|
||||
len(glob.glob(os.path.join(ROOT, "03-DESIGN", "0*", "*.md")))
|
||||
+ len(glob.glob(os.path.join(ROOT, "04-ISSUES", "*", "00-report.md")))
|
||||
|
||||
+4
-2
@@ -135,14 +135,16 @@ every other domain has something to run on. **Recorded by** the controller and t
|
||||
in the tools' descriptions it is also an SSH server's or a container runtime's own word, so a
|
||||
description that means the node-engine by it is found by review.
|
||||
*Not:* ~~host agent~~, ~~the host~~ (hq), ~~node host~~
|
||||
*Identifier until renamed:* `mesh-host` — the repository, its binary and its service unit
|
||||
*Identifier until renamed:* `mesh-host` — the repository, its binary and its service unit; the rename is
|
||||
[issue 354](../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md)
|
||||
- **tool runner** — the one program on each node that loads every assigned module's tools bundle and
|
||||
serves every tool and every held seat's verb on the subjects the memberships issue; the node-engine
|
||||
supervises it as a process beside the services it runs, never a container
|
||||
([ADR 0175](../02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md),
|
||||
which called it "node tools"). Its mode on loopback is the mesh MCP server.
|
||||
*Not:* ~~node tools~~, ~~tool runtime~~ (hq)
|
||||
*Identifier until renamed:* `node-tools` — the module, its unit and its bus account
|
||||
*Identifier until renamed:* `node-tools` — the module, its unit and its bus account; the rename is
|
||||
[issue 354](../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md)
|
||||
- **foundation** — the store and the bus, raised at genesis before any module system exists. The
|
||||
foundation is not a third thing beside the store and the bus — it *is* those two, named together.
|
||||
*Not:* ~~substrate~~
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
2. **Record the decision.** Write a record in [`02-DECISIONS/`](../../02-DECISIONS/) taking the next free
|
||||
number. Format and rules are in [`02-DECISIONS/README.md`](../../02-DECISIONS/README.md). State evidence,
|
||||
not assertion, and record the options that were rejected — that is the half worth keeping.
|
||||
**A record that puts work off** — a rename, a check, a case it does not handle — names the issue that
|
||||
tracks it and the owner who will do it, in the sentence that defers it ("tracked by issue N"), or the
|
||||
deferral is not accepted:
|
||||
a deferral nothing tracks reads as done
|
||||
([issue 354](../../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md),
|
||||
ADR 0244's code renames). [`cycle.py`](../checks/cycle.py) holds every accepted record dated from
|
||||
2026-10-09, and an older one corrected under the rule, to it.
|
||||
3. **Write the design.** Create the document under `03-DESIGN/01-to-be/` with frontmatter:
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
## Rules
|
||||
|
||||
- Every merge is a human checkpoint, without exception.
|
||||
- What the handoff leaves for later is an issue with an owner, named in the design or the decision that
|
||||
leaves it — never a sentence saying "later". The design's `code:` names who owns what is built; the issue
|
||||
names who owns what is not. [`cycle.py`](../checks/cycle.py) checks the decision record
|
||||
([issue 354](../../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md)).
|
||||
- Never open a pull request unprompted. A permissions list saying it is allowed is not a
|
||||
request.
|
||||
- Work in an isolated worktree, never a shared checkout. A failed `cd` in a shared checkout
|
||||
|
||||
@@ -182,6 +182,18 @@ work:
|
||||
descriptions call a build request an *ask*;
|
||||
- the nftables module's tool `firewall_rules`, which serves the packet filter.
|
||||
|
||||
> **Progressive insight — 2026-10-09.** This record deferred the code renames in four places and named no
|
||||
> issue, owner or date for them: this section, §2 ("listed in §6 for a later rename and is not renamed here"),
|
||||
> §3 ("Code that still carries an old name is named on an *Identifier until renamed* line") and §4's table
|
||||
> ("`mesh-host` an identifier until the code rename"). Two days later the operator believed them done, and
|
||||
> nothing in this record said otherwise. They are tracked by
|
||||
> [issue 354](../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md),
|
||||
> which holds the inventory of what carries the old names, the risk of renaming the paths of a self-updating
|
||||
> daemon on live nodes, and the staged plan; each stage is owned by the repository it names, and the
|
||||
> glossary's *Identifier until renamed* lines point at the issue. The decision stands. From this date a
|
||||
> record that defers work names the issue that tracks it, with an owner, or the deferral is not accepted
|
||||
> (playbooks 02 and 04; `cycle.py` checks it).
|
||||
|
||||
## Options rejected
|
||||
|
||||
- **Machine as the one word, node only as an identifier** — the research's proposal, and what the newest
|
||||
|
||||
+5
@@ -154,6 +154,11 @@ The table of ADR 0239 decision 2 gains these rows:
|
||||
held merge of it stays held for a person, as before this record. Reading "every machine running it" from
|
||||
the first machines when they are all the machines would close it, and is left for a later change.
|
||||
|
||||
> **Progressive insight — 2026-10-09.** The one-machine case above was left for a later change with no
|
||||
> issue, owner or date, which is how ADR 0244's deferred renames went unnoticed (issue 354). It is tracked by
|
||||
> [issue 355](../04-ISSUES/355-a-held-delivery-of-a-module-that-runs-on-one-machine-only-is-never-covered/00-report.md),
|
||||
> owned by mesh-delivery in the controller's repository. The decision stands.
|
||||
|
||||
> **Progressive insight — 2026-10-09.** *The gap was wider than one machine.* A build the mesh held back for
|
||||
> a person and sent on the operator's word travels by a backlog walk, whose record gives one step per
|
||||
> machine and no "every machine running it" or "the rest running it"; and that walk's last record keeps only
|
||||
|
||||
+6
@@ -377,6 +377,12 @@ Recording a proposal needs no yes: it writes the register and nothing a session
|
||||
for an agent the mesh runs rather than the operator's; and a channel of the operator's own that can
|
||||
tell the operator from an agent (issue 340's "once we build a UI"), which would become a third route.
|
||||
|
||||
> **Progressive insight — 2026-10-09.** The four things this section leaves undecided — clearing old copies
|
||||
> of hand versions, a per-project lesson, lessons for an agent the mesh runs, and a channel of the operator's
|
||||
> own — were left with no issue, owner or date, which is how ADR 0244's deferred renames went unnoticed
|
||||
> (issue 354). They are tracked by
|
||||
> [issue 357](../04-ISSUES/357-what-adr-0273-left-undecided-has-no-owner/00-report.md). The decision stands.
|
||||
|
||||
## How it is checked
|
||||
|
||||
| Rule | Checked by |
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
---
|
||||
status: located
|
||||
opened: 2026-10-09
|
||||
located-in: [mesh-host (the node-engine: its repository, module, binary, units, launcher and paths), mesh-tools node-tools (the tool runner: its module, unit and bus account), mesh-controller (the names it keys the core on, and the words it shows), mesh-catalog (the registrations, and the modules that name the two), hq (the designs' code lines, repos.md, the glossary's identifier lines)]
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# 354. A decided rename was never scheduled, and the operator believed it live
|
||||
|
||||
## Symptom
|
||||
|
||||
[ADR 0244](../../02-DECISIONS/0244-the-mesh-is-described-in-domains-and-one-word-names-one-thing.md)
|
||||
(2026-10-07) settled the words: the program on every node is the **node-engine**, and the program that serves
|
||||
every module's tools on a node is the **tool runner**. It left the code renames out on purpose — "`mesh-host`
|
||||
an identifier until the code rename", "`node-tools` an identifier until renamed", and its §6 *What is not
|
||||
renamed here*: "code is renamed by the repositories that own it, each with its own change" — and named no
|
||||
issue, no owner and no date for them.
|
||||
|
||||
On 2026-10-09 the operator took the rename as long implemented and live. It is not. As of that day:
|
||||
|
||||
- **The repositories**, at their heads (the node-engine's at `b0d03be`, the controller's at `e6b00e2e`, the
|
||||
catalogue's at `6ae112d`): the node-engine's repository and module are `mesh-host`; its binary is
|
||||
`nox-mesh-host`; its service units are `nox-mesh-host.service`, `nox-mesh-host-resume.service` and
|
||||
`nox-mesh-host-roused.service`; its launcher is `/usr/lib/nox-mesh-host/launch`; it is delivered under
|
||||
`/usr/lib/nox-mesh-host/versions/<version>` and keeps its state under `/var/lib/mesh-host`. The tool
|
||||
runner is the module `node-tools`, the unit `node-tools.service` and the bus account `node-tools`.
|
||||
- **The live mesh**, read through the controller's `node` verb for the laptop at 18:05: `mesh-host` and
|
||||
`node-tools` stand among its assigned modules, and `node-tools.runtime process node-tools.service` among
|
||||
what it runs.
|
||||
- **The conditions.** An open condition on the control-node (messenger not healthy) says that
|
||||
`mesh-host hand-over` at the machine hands a directory to the mesh; every hand-act record of the day
|
||||
names its caller as `<the laptop>.node-tools`.
|
||||
- **The record.** `records_search` for "until the code rename" finds ADR 0244's own table row and nothing
|
||||
else; for "rename mesh-host", nothing. No issue, design or record tracks the rename.
|
||||
|
||||
## Why it is an issue
|
||||
|
||||
- **A deferral nothing tracks reads as done.** ADR 0244 is accepted; the glossary says node-engine; the
|
||||
governing documents were reworded the same day. Everything a reader meets says the new name, and the one
|
||||
sentence that says the code is not renamed says "later" with nobody's name on it. Two days later the
|
||||
operator believed it done. This is the rule of `AGENTS.md` — an unenforced rule is indistinguishable from
|
||||
a wrong one — in its deferral form: a record that puts work off without an issue is indistinguishable
|
||||
from one whose work landed.
|
||||
- **It is not one record's slip.** The check this issue adds, run over every accepted record from the
|
||||
date ADR 0244 was accepted, found deferrals without an issue in ADR 0241 ("a later change requires them
|
||||
once both holders serve them"), ADR 0270 ("left for a later change"), ADR 0273 ("not decided here", four
|
||||
items) and ADR 0244 itself. ADR 0270 and 0273 are dated 2026-10-09, are held to the rule, and have
|
||||
[issue 355](../355-a-held-delivery-of-a-module-that-runs-on-one-machine-only-is-never-covered/00-report.md)
|
||||
and [issue 357](../357-what-adr-0273-left-undecided-has-no-owner/00-report.md) now; ADR 0241 is dated
|
||||
before the rule's start and stands as found.
|
||||
- **Impact.** The operator plans against a mesh that does not exist: in conversation the mesh says
|
||||
node-engine, while every file, unit, package and condition on a node says `mesh-host`, so a person at a
|
||||
machine (`systemctl status nox-mesh-host`, `ls /var/lib/mesh-host`) and a person reading this repository
|
||||
do not describe the same thing. The glossary's *Identifier until renamed* lines tell an agent the name is
|
||||
temporary and never say when.
|
||||
|
||||
## The fix, in two parts
|
||||
|
||||
1. **This rename** is tracked here. The inventory of what carries the old names, the risk of renaming the
|
||||
paths of a self-updating daemon on live nodes, and a staged plan are in
|
||||
[`01-diagnosis.md`](01-diagnosis.md). Acceptance: **`mesh-cli node show <node>` and every condition say
|
||||
node-engine; no file, unit or package on a node is named `mesh-host`; the tool runner likewise.**
|
||||
2. **The rule**, from 2026-10-09: a decision that defers work names the issue that tracks it, with an
|
||||
owner, or the deferral is not accepted. Playbooks [02](../../00-META/process/02-graduation.md)
|
||||
(graduation) and [04](../../00-META/process/04-build-handoff.md) (handoff) say it;
|
||||
[`cycle.py`](../../00-META/checks/cycle.py) checks it: an accepted record dated from 2026-10-09, or an
|
||||
older one corrected under the rule, whose text puts work off ("until renamed", "left for later", "not
|
||||
decided here", "postponed", "future work", "deferred", "a later change") names the issue as tracking it
|
||||
("tracked by issue N"), in that paragraph or list item, or in a dated progressive insight in the same
|
||||
section or quoting it. ADR 0244 carries the insight that points here.
|
||||
|
||||
This resolves when the acceptance sentence holds on every node. It is a core issue
|
||||
([ADR 0237](../../02-DECISIONS/0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md)):
|
||||
it resolves with a replay, or with `replay-none:` saying why none is possible.
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
# 354. Diagnosis
|
||||
|
||||
## 2026-10-09
|
||||
|
||||
**The record.** `records_search` for "until the code rename" finds only ADR 0244's table row; for the
|
||||
old name with "rename", nothing. Issues 142, 194 and 210 carry the old name in their titles and keep it: an issue keeps
|
||||
its words (ADR 0244 §5).
|
||||
|
||||
### What carries the old names
|
||||
|
||||
**This repository**, on `main` at `c613cf0`:
|
||||
|
||||
| Where | What | How much |
|
||||
|---|---|---|
|
||||
| `03-DESIGN/01-to-be/` | `mesh-host` and `node-tools` in prose and in `code:` lines | 24 documents, 97 lines; 19 designs name `mesh-host` in `code:`, which is the repository's name and stays right until the repository is renamed |
|
||||
| `03-DESIGN/00-as-is/` | `code:` lines | 2 documents |
|
||||
| `00-META/` | `repos.md` (the table says `mesh-host` exists, tier 0), playbook 07's list of repositories, the glossary's two *Identifier until renamed* lines | 3 documents |
|
||||
| `02-DECISIONS/`, `01-RESEARCH/`, `04-ISSUES/` | the old names as written | 38, 9 and 135 documents; kept, because a record keeps its words |
|
||||
|
||||
**The code**, in the checkouts at the heads the report names:
|
||||
|
||||
- **The node-engine's repository, `mesh-host`.** Its manifest: module `mesh-host`, slug `host`, the
|
||||
artifact's binary `nox-mesh-host`, the resource `launcher` at `/usr/lib/nox-mesh-host/launch`, the
|
||||
resource `next` (the delivered archive) at `/usr/lib/nox-mesh-host/versions/<version>`. Its packaging:
|
||||
`nox-mesh-host.service` (whose start command is the launcher), `nox-mesh-host-resume.service`,
|
||||
`nox-mesh-host-roused.service`, `nox-mesh-host-launch`, `nox-mesh-host-rollback`,
|
||||
`nox-mesh-host-network.sh` and an OpenRC script. The launcher's environment (`MESH_HOST_STATE_DIR`,
|
||||
`MESH_HOST_LIBEXEC`, `MESH_HOST_BIN`, and its limits) and its fallback binary `/usr/bin/nox-mesh-host`.
|
||||
The state directory `/var/lib/mesh-host`: the node's state, the foundation lock, and the launcher's
|
||||
files (known-good, rolled-back, trial, the start counter). The commands `cmd/mesh-host` (its flag set is
|
||||
named `mesh-host`) and `cmd/mesh-bootstrap`. The placement guard's `engineModule` (`mesh-host`) and
|
||||
`engineTrees` (both directories). The words a report says, which conditions quote: `mesh-host hand-over` at
|
||||
the machine. The Go module path, which the controller's builder check vendors. The
|
||||
report names a socket under `/run/nox-mesh-host`; I did not find one in the checkout at `b0d03be`, so
|
||||
the diagnosis in that repository confirms it or strikes it.
|
||||
- **The tool runner**, in the `mesh-tools` repository under `node-tools/`: the module `node-tools`, the
|
||||
unit `node-tools.service`, the bus account `node-tools`, the mesh MCP server's description of itself
|
||||
("reached as <node>.node-tools"), and the caller's address every hand-act record keeps,
|
||||
`<node>.node-tools`.
|
||||
- **The controller, `mesh-controller`**: 51 lines naming `mesh-host`, 18 naming `node-tools`. The core
|
||||
modules the merge gate and the checks key on (`coreModules`: module to repository), the module the probes
|
||||
key on (`hostModule`), the runtime module (`RuntimeModule`, in the catalogue and the broker packages),
|
||||
the bus account (`BusNodeTools`), the broker's kind (`KindNodeTools`), the lease witness
|
||||
(`NodeToolsService`, `ComponentNodeTools`, and a component named `mesh-host`), the seat dependencies, the
|
||||
reconnects, and the put-back ordering of a send, keyed on the strings `mesh-host` and `mesh-controller`.
|
||||
The words it shows: `"mesh-host": "the node-engine"` and `"node-tools": "the node tools"` — the second
|
||||
is a retired word.
|
||||
- **The catalogue, `mesh-catalog`**: 20 files name one or both — the README, the desktop session code of
|
||||
five modules, the container runtime's tools and tests, the bus's tools, the service manager's client,
|
||||
the SSH client's manifest, the lab's tools and the forge's tests. And the registrations: a catalogue row
|
||||
for `mesh-host` (built from its own repository, ADR 0069) and one for `node-tools` (from `mesh-tools`),
|
||||
and every node's assignment of both.
|
||||
|
||||
The owner of each area is the repository named. For the symptom's words, the whole of the rename, and the
|
||||
check, this repository.
|
||||
|
||||
### The risk
|
||||
|
||||
**Renaming the paths of a self-updating daemon on live nodes.** The node-engine delivers itself: its
|
||||
launcher, placed by the module's own declaration under `/usr/lib/nox-mesh-host`, runs the newest delivered
|
||||
version under that directory's `versions/` on trial, and goes back to the known-good one recorded under
|
||||
`/var/lib/mesh-host` when the trial fails
|
||||
([to-be 45 §8](../../03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md),
|
||||
[ADR 0227](../../02-DECISIONS/0227-the-core-holds-nine-rules-each-checked-and-is-built-to-them-in-six-phases.md)
|
||||
rule 8). Its unit is placed when the machine joins, not by the mesh
|
||||
([to-be 21](../../03-DESIGN/01-to-be/21-the-installation-in-full.md): the installer does not install the
|
||||
unit). So a build that writes under new paths while the launcher reads the old ones is a build the launcher
|
||||
cannot find; and a launcher that reads only new paths cannot go back to a known-good version under the old
|
||||
ones — it halts the node ("this node needs a person"), which on a server is a person at a keyboard. The
|
||||
module's name is keyed in the controller's core handling (the put-back ordering, the gate's core modules,
|
||||
the lease witness), so a renamed module is, to a controller that knows one name, a new module beside an old
|
||||
one. The tool runner has the same shape at a smaller scale: the bus account and the unit are named in the
|
||||
controller's user list and in every node's assignment.
|
||||
|
||||
Hence: **one release in which both names are accepted, on every side, before any release in which the old
|
||||
one is dropped**, each release walked one node first — the laptop, where a halted node is the operator at a
|
||||
keyboard — and then the rest. I think no stage should move before the delivery fixes of issues 351 and 352
|
||||
are live, because a core walk is exactly what those broke; that is a guess from their symptoms, not a
|
||||
measurement.
|
||||
|
||||
### The staged plan
|
||||
|
||||
0. **This repository** (this change): the issue; the glossary's identifier lines point here; the rule and
|
||||
its check.
|
||||
1. **The words shown**, safe at any time and in any order: the controller's shown words ("the node tools"
|
||||
becomes the tool runner), the node-engine's report words (the hand-over as the node-engine's command),
|
||||
the mesh MCP server's description of itself. No path changes.
|
||||
2. **Both names accepted, for one release.** The controller treats `mesh-host` and the node-engine's new
|
||||
module name as one core component (core modules, witness, put-back ordering), and `node-tools` and the
|
||||
tool runner's as another (runtime module, bus account, kind). The node-engine's launcher and upgrade
|
||||
package read the old and the new library and state directories, and the node-engine moves its state
|
||||
once, the first time it runs under the new name. Both unit names run the same launcher. The conditions
|
||||
and the `node` verb say node-engine. The merge gate composes every node green; walked one node first.
|
||||
3. **The rename proper.** The new module names registered, assigned beside the old on the first node, the
|
||||
old unassigned there, the acceptance sentence checked on that node, then the rest. The repository
|
||||
renamed on the forge, with its build source and its Go module path; the packaging renamed. The tool
|
||||
runner the same: module, unit, bus account — and the caller's address in hand-act records changes with
|
||||
it, which whoever reads those records must know.
|
||||
4. **The old names dropped**, one release later: the controller's acceptance of them, the launcher's old
|
||||
paths, the glossary's two identifier lines (the old names move to *Not:* lines, and `words.py` then
|
||||
fails on them in prose), the designs' `code:` lines and `repos.md` following the repository.
|
||||
|
||||
**Acceptance:** `mesh-cli node show <node>` and every condition say node-engine; no file, unit or package on a
|
||||
node is named `mesh-host`; the tool runner likewise.
|
||||
|
||||
### Open
|
||||
|
||||
- **The new identifiers.** The glossary's words are node-engine and tool runner; the `nox-` prefix on the
|
||||
binary and the units is the packaging's. Whether the binary is `nox-node-engine`, the directories
|
||||
`/usr/lib/node-engine` and `/var/lib/node-engine`, and the tool runner's module `tool-runner`, is the
|
||||
operator's call before stage 2 is built.
|
||||
- **Whether the state directory moves at all.** What is under it is the node's own (the report sequence,
|
||||
the known-good version): a new name beside a kept path is an option the diagnosis in the node-engine's
|
||||
repository weighs.
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
---
|
||||
status: open
|
||||
opened: 2026-10-09
|
||||
located-in: []
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# 355. A held delivery of a module that runs on one machine only is never covered
|
||||
|
||||
## Symptom
|
||||
|
||||
[ADR 0270](../../02-DECISIONS/0270-a-held-delivery-that-moves-nothing-or-that-a-later-delivered-merge-contains-ends-by-itself-and-a-hold-whose-reason-is-gone-lifts.md)
|
||||
(2026-10-09) ends a held delivery by itself when a later merge that contains it was delivered to every
|
||||
machine running its modules — the delivery is *covered*. Its consequences say: "A module that runs on one
|
||||
machine only is never covered. A walk records 'the rest running it' only after its first machines passed
|
||||
and the rest were sent. A module on one machine has no rest, so its steps never say it reached every
|
||||
machine. mesh-delivery itself, on the control node alone, is such a module: a held merge of it stays held
|
||||
for a person, as before this record. Reading 'every machine running it' from the first machines when they
|
||||
are all the machines would close it, and is left for a later change."
|
||||
|
||||
That later change had no issue, no owner and no date. The check added with
|
||||
[issue 354](../354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md)
|
||||
found the sentence on the day the record was accepted. This issue is the pointer the record lacked; the
|
||||
record carries a dated insight naming it.
|
||||
|
||||
## Why it is an issue
|
||||
|
||||
A held delivery of mesh-delivery — or of any module placed on one machine — that a later merge covered
|
||||
stays held for a person, which is the hand-work ADR 0270 set out to remove; and nothing told the operator
|
||||
this case was excluded except one sentence in the record's consequences. A deferral nothing tracks reads as
|
||||
done (issue 354).
|
||||
|
||||
## Open
|
||||
|
||||
- Whether a walk whose first machines are all the machines should record that it reached every machine
|
||||
(the record's own suggestion), or whether *covered* should read the machines running a module from the
|
||||
placement rather than from the walk's steps.
|
||||
- The owner is mesh-delivery, in the controller's repository, as the record says; `located-in:` is filled
|
||||
when the diagnosis confirms where the walk's record is written.
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
status: open
|
||||
opened: 2026-10-09
|
||||
located-in: []
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# 357. What ADR 0273 left undecided has no owner
|
||||
|
||||
## Symptom
|
||||
|
||||
[ADR 0273](../../02-DECISIONS/0273-the-agents-home-is-the-modules-and-the-operators-personal-rules-and-lessons-are-registered-through-it.md)
|
||||
(2026-10-09: the agent's home is the module's, and the operator's rules and lessons are registered through it)
|
||||
says in its consequences that four things are not decided there, and names no issue, owner or date for any of
|
||||
them. The check added with
|
||||
[issue 354](../354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md) found
|
||||
them on the day the record was accepted. This issue is the pointer the record lacked; the record carries a
|
||||
dated insight naming it.
|
||||
|
||||
1. **Clearing old copies of hand versions.** The agent module keeps the hand version of every file it undoes,
|
||||
in its state, and "clearing old copies is not decided here, as ADR 0243 left it". Without it the module's
|
||||
state only grows.
|
||||
2. **A per-project lesson** — one bound to a repository rather than the operator.
|
||||
3. **Lessons for an agent the mesh runs**, rather than the operator's own agent.
|
||||
4. **A channel of the operator's own that can tell the operator from an agent** — what issue 340 calls
|
||||
"once we build a UI" — which would become a third route for registering into the operator's home.
|
||||
|
||||
## Why it is an issue
|
||||
|
||||
A deferral nothing tracks reads as done (issue 354). Each of the four is a gap a reader of ADR 0273 can only
|
||||
find by reading its consequences to the end; the first is also a store that grows without a bound.
|
||||
|
||||
## Open
|
||||
|
||||
- Whether each item needs a decision record of its own, or whether item 1 is a setting of the agent module
|
||||
that its owner (the `claude-code` module in the catalogue) can decide in a change.
|
||||
- Item 4 waits on the operator's channels (ADR 0259) and the controller's terminal; its owner is not known
|
||||
until the channel is designed.
|
||||
- `located-in:` is filled when each item's owner is confirmed. The item resolved first is struck through here
|
||||
with its pull request, and the issue resolves when all four are decided or declined.
|
||||
Reference in New Issue
Block a user