Issue 354: a decided rename was never scheduled, and a record that defers work names its issue
ADR 0244 left the code renames of the node-engine and the tool runner for later with no issue, owner or date, and two days on the operator believed them live. Open the issue with the inventory, the risk and a staged plan; add the rule to playbooks 02 and 04, as a dated insight on ADR 0244, and as a cycle.py check that found the same gap in ADR 0270 (issue 355).
This commit is contained in:
@@ -61,7 +61,14 @@ 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", "deferred", "a later change" — names the issue that tracks it, in that
|
||||
paragraph or in a dated progressive insight that says what it tracks
|
||||
([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 on
|
||||
0244 once its insight brought it under the rule, before each named its issue. `python3 00-META/checks/cycle.py`
|
||||
|
||||
## words.py
|
||||
|
||||
|
||||
@@ -56,6 +56,41 @@ 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".
|
||||
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(?! record)|"
|
||||
r"\bdeferred\b|later work|not built here|\ba later change")
|
||||
ISSUE_REF = re.compile(r"\bissue (\d{3})\b|04-ISSUES/(\d{3})-")
|
||||
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, text) paragraphs: frontmatter and fenced code left out,
|
||||
a block quote's markers stripped, so a quoted insight reads as the paragraph it is."""
|
||||
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 = [], 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:
|
||||
out.append((line, stripped))
|
||||
line += chunk.count("\n") + 2
|
||||
return out
|
||||
|
||||
|
||||
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 +268,33 @@ 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 that puts work off names the issue that tracks it, or the record carries a
|
||||
# dated progressive insight that names the issue and says what it tracks. 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, text) for line, 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
|
||||
covering = set()
|
||||
for d, text in insights:
|
||||
if DEFERRAL.search(text):
|
||||
covering |= issue_refs(text, seen)
|
||||
for line, text in paras:
|
||||
m = DEFERRAL.search(text)
|
||||
if not m or issue_refs(text, seen) or covering:
|
||||
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,12 @@
|
||||
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, 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
|
||||
|
||||
Reference in New Issue
Block a user