Builds ADR 0489 as amended by ADR 0490 in modules/claude-code.
claude_code_wait (add, watched, done, said, list, adopt): waits kept in the module's new state waits under the session's name and node (by, who@node), the name bound to the current session id; each session id's waits mirrored atomically to <agent home>/.claude/nox-mesh/waits/<session>.json (the union of every name bound to it) through the module's route into the agent's home, plus a node-wide index; adopt takes a name over at once after a /clear (Claude Code gives a new session id there) and the earlier id is told on its next call.
The plugin's session-start hook prints the session's id and every open wait on the node by name, with the adopt call to make.
The plugin's Stop hook refuses a stop while a wait is within its bound and unwatched, or past its bound and not said, telling the session to mesh_wait on its family when the tool runner is granted it, otherwise to start a watcher; it allows silently with no waits, fails open (saying so on screen) on a bad file, a bad session id, a counter it cannot write or a panic, and refuses the same stop at most three times.
hooks.json carries the guard, the session-start hook and the Stop hook only; claude_code_hook_register still refuses every hook.
Tried with mesh-controller.try at 0f159a1: pass (go and typescript parts). Review: 3 rounds; the last remark is a follow-up of its own. The instruction sections that point sessions to the tool come after the delivery.
Builds ADR 0489 as amended by ADR 0490 in modules/claude-code.
- **`claude_code_wait`** (add, watched, done, said, list, adopt): waits kept in the module's new state `waits` under the session's name and node (`by`, who@node), the name bound to the current session id; each session id's waits mirrored atomically to `<agent home>/.claude/nox-mesh/waits/<session>.json` (the union of every name bound to it) through the module's route into the agent's home, plus a node-wide index; `adopt` takes a name over at once after a /clear (Claude Code gives a new session id there) and the earlier id is told on its next call.
- **The plugin's session-start hook** prints the session's id and every open wait on the node by name, with the adopt call to make.
- **The plugin's Stop hook** refuses a stop while a wait is within its bound and unwatched, or past its bound and not said, telling the session to mesh_wait on its family when the tool runner is granted it, otherwise to start a watcher; it allows silently with no waits, fails open (saying so on screen) on a bad file, a bad session id, a counter it cannot write or a panic, and refuses the same stop at most three times.
- hooks.json carries the guard, the session-start hook and the Stop hook only; `claude_code_hook_register` still refuses every hook.
Tried with mesh-controller.try at 0f159a1: pass (go and typescript parts). Review: 3 rounds; the last remark is a follow-up of its own. The instruction sections that point sessions to the tool come after the delivery.
Issues: issue 1629, issue 1743, issue 1744
Reviewed-by: reviewer@g14 at 0f159a166200b45d196da09d8ae96ce6d4bb9d5b
A session can end its turn while an Approve it expects is still open and
nothing then watches it. claude_code_wait (add/watched/done/said/list)
keeps each session's waits in the module's waits state, keyed by its
session id and node, and mirrors them whole and atomically to a file
under the agent's home that the plugin's coming hooks (1743, 1744) will
read without calling back into this module.
A session is never told its session id by any tool, only by a hook's
stdin, so it cannot name itself to claude_code_wait. The plugin's
SessionStart hook (claude-code session-start <waits-dir>) prints the id
and how to give it, plus the session's still-open waits after a /clear,
reading the waits file claude_code_wait mirrors directly rather than
calling back into this module. Carries the Stop hook's command and the
GuardFiles.WaitsDir plumbing MarketplaceWith needs for both (issue 1743
wires the Stop hook's dispatch next).
A session could end its turn while a wait it wrote down with
claude_code_wait had nothing following it. The plugin's Stop hook
(claude-code stop <waits-dir>) refuses the stop while a wait is within
its bound and unwatched, or past its bound and not said, reading the
waits file directly with no network and a 5s bound; it fails open on a
missing or malformed file or a panic, and lets a wait through after
three refusals in a row rather than holding the session forever.
Renames TestThePluginsOnlyHookIsTheGuard to cover the session-start and
Stop hooks too.
A session id reaching the Stop and SessionStart hooks was only checked
for blank, so "../../x" or "a/b" could read or write outside the waits
directory; both hooks now hold every session id to waitSessionID before
building any path, and allow (saying why) rather than fail on a bad one.
claude_code_wait's list/watched/done/said held only add to that same
check, letting a malformed id through as an ordinary, always-empty key;
they all refuse it now.
The Stop hook's three-strikes cap could be bypassed forever if its
counter file could not be written on a refusal: it now allows instead,
saying so, so a wait can never hold a turn open indefinitely. Its advice
offered mesh_wait on a wait's event family unconditionally, ignoring
that the session's tool runner may not be granted it (ADR 0367 §2); it
now offers mesh_wait only "if this session's tool runner is granted it,
otherwise start a watcher", and drops the family entirely when a wait
has none.
Both hooks' fail-open and let-through notices were said only on stderr,
which Claude Code does not show the operator on an allowed (exit 0)
outcome; they now also print the same words as a "systemMessage" JSON
object on stdout, which Claude Code does show.
Claude Code gives a session a new session_id at /clear, with nothing
naming the earlier one, so keying claude_code_wait's state by session id
lost a session's waits the moment it cleared. ADR 0490 changes ADR 0489
§1: waits are now kept under the caller's name and node (by, who@node);
every op also takes session, the caller's current id, and the module
keeps a binding of the name to the session that last called for it. An
unbound name is claimed by the first call for it; a session whose own
id no longer matches the binding is refused on every op, naming the
session that holds it, until it calls the new op adopt, which always
takes the name over at once (no bound protects a recent call) and tells
the earlier session so on its own next call.
The mirrored file a hook reads stays keyed by session id (a hook only
ever has that), holding whichever name's waits are currently bound to
it; adopting rewrites both the old id's file (emptied) and the new id's
(filled) in the same call. A second mirrored file, the node-wide index,
lists every name on the node with an open wait; the SessionStart hook
now also reads it, on every start, and prints the exact adopt call for
each name, so a session with a brand new id can find and reclaim its
own waits without calling back into the module.
mesh/merge-gate head 0f159a166200 merged into trunk 7a7a4034852a — pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes …
mesh/repo-check head 0f159a166200 merged into trunk 7a7a4034852a — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
The Stop hook treated a missing waits file exactly like one that could
not be read: a notice on every stop of every session with no waits —
the normal state, not an exceptional one. DecideStop now treats
os.ErrNotExist as the ordinary case of no open waits, silently; the
notice is still said for a file that exists but cannot be read or does
not parse.
ADR 0490 §1 keeps waits under a name, but the file a hook reads is kept
per session id: writing it from only the name an op just acted on meant
a second name bound to the same session id had its waits silently drop
out of enforcement the moment the first name was touched, and adopting
one name away emptied the id's file outright even when another name was
still bound to it. Every write now rewrites a session's file from the
union of every name currently bound to it (sessionWaitsUnion), adopt's
rewrite of the superseded id included.
Also: each atomic write gets its own temp file name, so two writes to
the same path can never interleave on one; adopt clears the refusal
counter of both the superseded and the adopting session, so a fresh
three-strikes count starts under the new binding; and list is read-only
again — it no longer claims an unbound name or refuses a session that
is not (yet) the one bound to it, and instead says who currently holds
the name.
Deliverynovox/mesh-catalog@0f159a166200 — delivered since 2026-10-21T17:23:44Z
Delivery plan — builds claude-code → ace, g14, novox, shanks; no bus step
tier 0: claude-code
ace: receives claude-code
g14: receives claude-code
novox: receives claude-code
shanks: receives claude-code
claude-code keeps data (ADR 0233): what it holds is backed up before it moves
grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before claude-code is judged on its first machine
Machines
claude-code on every machine running it: sent
Transitions
2026-10-21 17:11 (new) → proposed (announced): novox/mesh-catalog#420's head announced
2026-10-21 17:17 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
2026-10-21 17:17 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-21 17:18 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
2026-10-21 17:19 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
2026-10-21 17:23 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says
The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.
<!-- mesh-delivery:view -->
**Delivery** `novox/mesh-catalog@0f159a166200` — **delivered** since 2026-10-21T17:23:44Z
**Delivery plan** — builds claude-code → ace, g14, novox, shanks; no bus step
- tier 0: claude-code
- ace: receives claude-code
- g14: receives claude-code
- novox: receives claude-code
- shanks: receives claude-code
- claude-code keeps data (ADR 0233): what it holds is backed up before it moves
- grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before claude-code is judged on its first machine
**Machines**
- claude-code on every machine running it: sent
**Transitions**
- 2026-10-21 17:11 (new) → proposed (announced): novox/mesh-catalog#420's head announced
- 2026-10-21 17:17 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
- 2026-10-21 17:17 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-21 17:18 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
- 2026-10-21 17:19 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
- 2026-10-21 17:23 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says
The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Merge check at 0f159a16
Judged: head 0f159a166200 merged into trunk 7a7a4034852a; read beside it: mesh-controller 5ba2ffe8e608, mesh-controller-main 5ba2ffe8e608, mesh-host c231d1031fc7, mesh-tools e0fadc43d9dd
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: claude-code)
mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
Delivery plan — builds claude-code → ace, g14, novox, shanks; no bus step
tier 0: claude-code
ace: receives claude-code
g14: receives claude-code
novox: receives claude-code
shanks: receives claude-code
claude-code keeps data (ADR 0233): what it holds is backed up before it moves
grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before claude-code is judged on its first machine
read by no module's build: modules/claude-code/cmd/claude-code/session_start_test.go, modules/claude-code/cmd/claude-code/stop_test.go, modules/claude-code/cmd/claude-code/terminal_only_test.go, modules/claude-code/cmd/claude-code/waits_test.go
Run by the build seat on g14 as build-1792602692577830437 (builds --log build-1792602692577830437).
--- the gate: claude-code
note: claude-code uses the seat "operator-channel", which no module declares and the mesh does not define among the manifests given; registration judges it against the whole catalogue, and passing the declaring module's manifest too judges it here
claude-code: ok, 49 tool(s), keeps state servers, holdings, config, proposals, questions, memory, waits, reads claude-licence-manager.bindings, keeps agent-home (valuable); WARNING: settings ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339)
long-running resources without health: 0
files asking for a setting without saying whether it is trusted: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-21T17:09:49Z says they run
composing every machine as the mesh is
HELD: the vault did not answer for the core's credentials on f35; sealed by the controller until the vault holds it: mesh-provisioner-client — own/f35/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on idt; sealed by the controller until the vault holds it: mesh-provisioner-client — own/idt/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on ygcyr; sealed by the controller until the vault holds it: mesh-provisioner, mesh-provisioner-client, nats — own/ygcyr/mesh-provisioner/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/nats/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
the bus's user list leaves out 8 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, ygcyr.mesh-mail.send, ygcyr.only-office.converter, node.yvbpsm, yvbpsm.brother.device. Each is a user that cannot connect until one is issued
HELD: the vault did not answer for the core's credentials on yvbpsm; sealed by the controller until the vault holds it: mesh-provisioner-client — own/yvbpsm/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on f35; sealed by the controller until the vault holds it: mesh-provisioner-client — own/f35/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on idt; sealed by the controller until the vault holds it: mesh-provisioner-client — own/idt/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on ygcyr; sealed by the controller until the vault holds it: mesh-provisioner, mesh-provisioner-client, nats — own/ygcyr/mesh-provisioner/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/nats/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
the bus's user list leaves out 8 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, ygcyr.mesh-mail.send, ygcyr.only-office.converter, node.yvbpsm, yvbpsm.brother.device. Each is a user that cannot connect until one is issued
HELD: the vault did not answer for the core's credentials on yvbpsm; sealed by the controller until the vault holds it: mesh-provisioner-client — own/yvbpsm/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
composing every machine with the change
HELD: the vault did not answer for the core's credentials on f35; sealed by the controller until the vault holds it: mesh-provisioner-client — own/f35/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on idt; sealed by the controller until the vault holds it: mesh-provisioner-client — own/idt/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on ygcyr; sealed by the controller until the vault holds it: mesh-provisioner, mesh-provisioner-client, nats — own/ygcyr/mesh-provisioner/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/nats/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
the bus's user list leaves out 8 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, ygcyr.mesh-mail.send, ygcyr.only-office.converter, node.yvbpsm, yvbpsm.brother.device. Each is a user that cannot connect until one is issued
HELD: the vault did not answer for the core's credentials on yvbpsm; sealed by the controller until the vault holds it: mesh-provisioner-client — own/yvbpsm/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on f35; sealed by the controller until the vault holds it: mesh-provisioner-client — own/f35/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on idt; sealed by the controller until the vault holds it: mesh-provisioner-client — own/idt/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on ygcyr; sealed by the controller until the vault holds it: mesh-provisioner, mesh-provisioner-client, nats — own/ygcyr/mesh-provisioner/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/nats/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
the bus's user list leaves out 8 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, ygcyr.mesh-mail.send, ygcyr.only-office.converter, node.yvbpsm, yvbpsm.brother.device. Each is a user that cannot connect until one is issued
HELD: the vault did not answer for the core's credentials on yvbpsm; sealed by the controller until the vault holds it: mesh-provisioner-client — own/yvbpsm/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-21T17:09:49Z
modules the change moves:
- claude-code (changed)
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-config, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-provisioner, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): claude-code
notes:
- modules/claude-code/cmd/claude-code/session_start_test.go, modules/claude-code/cmd/claude-code/stop_test.go, modules/claude-code/cmd/claude-code/terminal_only_test.go and modules/claude-code/cmd/claude-code/waits_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.003s
words: 12 retired words and 70 daemons' lines, none in the 59597 strings of 917 files
ok git.novox.be/novox/mesh-catalog/checks/toolchains 0.009s
toolchains: go, nodejs and npm are declared only by their owners
ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.010s
agent-home: no archive and no file written into under the agent's home
ok git.novox.be/novox/mesh-catalog/checks/mailstore 0.010s
mail-store: claimed only by mesh-mail
ok git.novox.be/novox/mesh-catalog/checks/sshdir 0.014s
~/.ssh: named only by ssh-client
ok git.novox.be/novox/mesh-catalog/checks/owned 0.006s
owned paths: every directory and archive says how it treats unknown files; no module declares inside another's directory
ok git.novox.be/novox/mesh-catalog/checks/secrets 0.013s
secrets: no setting can replace a vault secret in a mergeable file (1 known, nested)
ok git.novox.be/novox/mesh-catalog/checks/predefined 0.239s
predefined.json: 2 key(s), none removed that a module names
ok git.novox.be/novox/mesh-catalog/checks/config 0.013s
config: no moved module says settings, takes-settings or ${setting:<key>}, or takes a whole layer (0 not yet moved)
ok git.novox.be/novox/mesh-catalog/checks/templates 0.010s
templates: no module keeps a file's text inline but the 0 listed
ok git.novox.be/novox/mesh-catalog/checks/paths 0.005s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 59, as kept
ok git.novox.be/novox/mesh-catalog/checks/deps 0.209s
testing modules/claude-code
ok claude-code/cmd/claude-code 5.570s
--- its merge-check-typescript.sh (typescript toolchain)
**Merge check** at `0f159a16`
Judged: head `0f159a166200` merged into trunk `7a7a4034852a`; read beside it: mesh-controller `5ba2ffe8e608`, mesh-controller-main `5ba2ffe8e608`, mesh-host `c231d1031fc7`, mesh-tools `e0fadc43d9dd`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: claude-code)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
**Delivery plan** — builds claude-code → ace, g14, novox, shanks; no bus step
- tier 0: claude-code
- ace: receives claude-code
- g14: receives claude-code
- novox: receives claude-code
- shanks: receives claude-code
- claude-code keeps data (ADR 0233): what it holds is backed up before it moves
- grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before claude-code is judged on its first machine
- read by no module's build: modules/claude-code/cmd/claude-code/session_start_test.go, modules/claude-code/cmd/claude-code/stop_test.go, modules/claude-code/cmd/claude-code/terminal_only_test.go, modules/claude-code/cmd/claude-code/waits_test.go
Run by the build seat on g14 as `build-1792602692577830437` (`builds --log build-1792602692577830437`).
```
--- the gate: claude-code
note: claude-code uses the seat "operator-channel", which no module declares and the mesh does not define among the manifests given; registration judges it against the whole catalogue, and passing the declaring module's manifest too judges it here
claude-code: ok, 49 tool(s), keeps state servers, holdings, config, proposals, questions, memory, waits, reads claude-licence-manager.bindings, keeps agent-home (valuable); WARNING: settings ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339)
long-running resources without health: 0
files asking for a setting without saying whether it is trusted: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-21T17:09:49Z says they run
composing every machine as the mesh is
HELD: the vault did not answer for the core's credentials on f35; sealed by the controller until the vault holds it: mesh-provisioner-client — own/f35/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on idt; sealed by the controller until the vault holds it: mesh-provisioner-client — own/idt/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on ygcyr; sealed by the controller until the vault holds it: mesh-provisioner, mesh-provisioner-client, nats — own/ygcyr/mesh-provisioner/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/nats/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
the bus's user list leaves out 8 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, ygcyr.mesh-mail.send, ygcyr.only-office.converter, node.yvbpsm, yvbpsm.brother.device. Each is a user that cannot connect until one is issued
HELD: the vault did not answer for the core's credentials on yvbpsm; sealed by the controller until the vault holds it: mesh-provisioner-client — own/yvbpsm/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on f35; sealed by the controller until the vault holds it: mesh-provisioner-client — own/f35/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on idt; sealed by the controller until the vault holds it: mesh-provisioner-client — own/idt/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on ygcyr; sealed by the controller until the vault holds it: mesh-provisioner, mesh-provisioner-client, nats — own/ygcyr/mesh-provisioner/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/nats/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
the bus's user list leaves out 8 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, ygcyr.mesh-mail.send, ygcyr.only-office.converter, node.yvbpsm, yvbpsm.brother.device. Each is a user that cannot connect until one is issued
HELD: the vault did not answer for the core's credentials on yvbpsm; sealed by the controller until the vault holds it: mesh-provisioner-client — own/yvbpsm/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
composing every machine with the change
HELD: the vault did not answer for the core's credentials on f35; sealed by the controller until the vault holds it: mesh-provisioner-client — own/f35/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on idt; sealed by the controller until the vault holds it: mesh-provisioner-client — own/idt/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on ygcyr; sealed by the controller until the vault holds it: mesh-provisioner, mesh-provisioner-client, nats — own/ygcyr/mesh-provisioner/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/nats/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
the bus's user list leaves out 8 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, ygcyr.mesh-mail.send, ygcyr.only-office.converter, node.yvbpsm, yvbpsm.brother.device. Each is a user that cannot connect until one is issued
HELD: the vault did not answer for the core's credentials on yvbpsm; sealed by the controller until the vault holds it: mesh-provisioner-client — own/yvbpsm/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on f35; sealed by the controller until the vault holds it: mesh-provisioner-client — own/f35/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on idt; sealed by the controller until the vault holds it: mesh-provisioner-client — own/idt/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
HELD: the vault did not answer for the core's credentials on ygcyr; sealed by the controller until the vault holds it: mesh-provisioner, mesh-provisioner-client, nats — own/ygcyr/mesh-provisioner/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus; own/ygcyr/nats/broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
the bus's user list leaves out 8 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, ygcyr.mesh-mail.send, ygcyr.only-office.converter, node.yvbpsm, yvbpsm.brother.device. Each is a user that cannot connect until one is issued
HELD: the vault did not answer for the core's credentials on yvbpsm; sealed by the controller until the vault holds it: mesh-provisioner-client — own/yvbpsm/mesh-provisioner-client/provisioner-broker: this control plane has no MESH_BUS_NATS, so it cannot reach the mesh's bus
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-21T17:09:49Z
modules the change moves:
- claude-code (changed)
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-config, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-provisioner, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): claude-code
notes:
- modules/claude-code/cmd/claude-code/session_start_test.go, modules/claude-code/cmd/claude-code/stop_test.go, modules/claude-code/cmd/claude-code/terminal_only_test.go and modules/claude-code/cmd/claude-code/waits_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.003s
words: 12 retired words and 70 daemons' lines, none in the 59597 strings of 917 files
ok git.novox.be/novox/mesh-catalog/checks/toolchains 0.009s
toolchains: go, nodejs and npm are declared only by their owners
ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.010s
agent-home: no archive and no file written into under the agent's home
ok git.novox.be/novox/mesh-catalog/checks/mailstore 0.010s
mail-store: claimed only by mesh-mail
ok git.novox.be/novox/mesh-catalog/checks/sshdir 0.014s
~/.ssh: named only by ssh-client
ok git.novox.be/novox/mesh-catalog/checks/owned 0.006s
owned paths: every directory and archive says how it treats unknown files; no module declares inside another's directory
ok git.novox.be/novox/mesh-catalog/checks/secrets 0.013s
secrets: no setting can replace a vault secret in a mergeable file (1 known, nested)
ok git.novox.be/novox/mesh-catalog/checks/predefined 0.239s
predefined.json: 2 key(s), none removed that a module names
ok git.novox.be/novox/mesh-catalog/checks/config 0.013s
config: no moved module says settings, takes-settings or ${setting:<key>}, or takes a whole layer (0 not yet moved)
ok git.novox.be/novox/mesh-catalog/checks/templates 0.010s
templates: no module keeps a file's text inline but the 0 listed
ok git.novox.be/novox/mesh-catalog/checks/paths 0.005s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 59, as kept
ok git.novox.be/novox/mesh-catalog/checks/deps 0.209s
testing modules/claude-code
ok claude-code/cmd/claude-code 5.570s
--- its merge-check-typescript.sh (typescript toolchain)
```
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Builds ADR 0489 as amended by ADR 0490 in modules/claude-code.
claude_code_wait(add, watched, done, said, list, adopt): waits kept in the module's new statewaitsunder the session's name and node (by, who@node), the name bound to the current session id; each session id's waits mirrored atomically to<agent home>/.claude/nox-mesh/waits/<session>.json(the union of every name bound to it) through the module's route into the agent's home, plus a node-wide index;adopttakes a name over at once after a /clear (Claude Code gives a new session id there) and the earlier id is told on its next call.claude_code_hook_registerstill refuses every hook.Tried with mesh-controller.try at
0f159a1: pass (go and typescript parts). Review: 3 rounds; the last remark is a follow-up of its own. The instruction sections that point sessions to the tool come after the delivery.Issues: issue 1629, issue 1743, issue 1744
Reviewed-by: reviewer@g14 at
0f159a1662Delivery
novox/mesh-catalog@0f159a166200— delivered since 2026-10-21T17:23:44ZDelivery plan — builds claude-code → ace, g14, novox, shanks; no bus step
Machines
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Merge check at
0f159a16Judged: head
0f159a166200merged into trunk7a7a4034852a; read beside it: mesh-controller5ba2ffe8e608, mesh-controller-main5ba2ffe8e608, mesh-hostc231d1031fc7, mesh-toolse0fadc43d9ddmesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: claude-code)mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)Delivery plan — builds claude-code → ace, g14, novox, shanks; no bus step
Run by the build seat on g14 as
build-1792602692577830437(builds --log build-1792602692577830437).