mesh/merge-gate pass: builds claude-code, mesh-knowledge → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (…
A judgement over one bus call's 30 seconds is collected by its ticket from
claude-code, so an eleven-clause record can be reviewed; a proposal out of
review tries is told to the operator, not raised as the module's fault, so
it no longer holds the module's own rollouts.
mesh/merge-gate pass: builds claude-code, mesh-issues → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 o…
The operator asked for duplicate issues to be consolidated as they are opened.
The tracker consumes its own opened event and asks the agent on its node, through
claude-code's new claude_code_judge, whether an older issue not resolved asks the
same question; it closes only the newer issue of a certain duplicate, keeping its
report on the older one, and fails loudly when it cannot judge. groom runs the
same judgement over the backlog as a dry run, applied on confirm.
mesh/merge-gate fail: builds claude-code → ace, g14, novox, shanks; no bus step; a manifest the change touches fails the module check: claude-code uses the…
An @ after any whitespace or punctuation, invisible and control characters, and top headings are refused;
every node judges a proposal record as the module would have made it before applying an approval, never
applies one older than its section's last change, never takes an unreadable record for an empty one, and
keys a node section by its nodes. The controller records the warrant from the router's word alone.
mesh/merge-gate fail: builds claude-code → ace, g14, novox, shanks; no bus step; a manifest the change touches fails the module check: claude-code uses the…
Shared instructions were the terminal's alone since hq issue 340, because the tool runner cannot tell an
agent from the operator. The operator channel now proves who answered, so an agent may propose a section;
each node proves the router's warrant against the exact text itself and keeps what it approved in its own
file, so nothing written to the bus becomes active.
mesh/delivery covered: a later merge that contains it was delivered: novox/mesh-catalog@e4edab3fd0eb (merged as c41c32cb into main, walk plan-17916298010…
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
Typing claude on the control node should start the session as the agent's own account, not as the
operator. The controller fills no machine fact into shell code, so the account agents run as is
written to /etc/claude-code/agent-account and the zsh code adds alias claude=claude-agent only when
that file names an account other than the shell's own. Where agents run as the operator's account,
nothing is added. claude-agent keeps running the real claude: exec sees no alias.
mesh/merge-gate pass: builds claude-code, claude-licence-manager, restic, sudo → ace, g14, novox, shanks; no bus step; every machine composes with the chan…
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
The managed settings apply to every account's claude, so a key-helper script under the
agent's home would run as the operator, and through sudo as root: the command is now
carried whole in the root-owned managed settings and reads the key only as the agent.
The helper reads only a regular file, bounded, without blocking, and a request it does
not answer fails in time instead of holding every later one; a directory entry it
answers that is not a name is refused. The guard's record is matched on its binary in
the query, a half-named agent account is not taken as the operator's, the kept API key
stays until the agent's home holds one, restic refuses to restore beside a directory
others may write, and the agent-home check cleans a path before judging it.
mesh/merge-gate pass: builds claude-code, claude-licence-manager, restic, sudo → ace, g14, novox, shanks; no bus step; every machine composes with the chan…
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
The records send the operator to "the controller's terminal", and on the
control-node nothing answered (hq issue 343). mesh-cli is a module of its
own, assigned to every node and built from its own directory: its program
is unpacked into /usr/local/bin by an archive resource (one file, no link,
no copy), and its zsh contribution makes nox its alias. It holds no
credential: it asks the node-engine on its machine over the SDK's go/cli
socket, and prints the controller's answer with its exit.
The agent module's refusals and README, and the route adapter's README, now
name `mesh-cli settings set …` on the control-node instead of a
mesh-controller command nobody could type; a settings file is passed inline,
since a path would be read where the controller runs.
The control node is to run its agents under an account that cannot become root
(hq ADR 0266). Declare it as a user the node-engine judges root: never, put the
agent's directory in its home, and place claude-agent so the operator starts a
session there through their own sudo. Where no agent account is named, both
name the operator's account, as before.
On the control node agents are to run as an account that cannot become root
(hq ADR 0266). The runtime keeps running as the operator, so the token, the
identity and the home-scope items go to the agent's home through a helper
started as that account; the licence and the account file follow the move,
what was placed in the operator's home goes once the agent holds the licence,
and the guard refuses every tool of a session running as anyone else,
recording to the journal. An API key is taken only from the agent's home and
only when it is one, here and in the licence manager, so the tool cannot carry
off the runtime's credential. Unchanged where no agent account is named.
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh, merge-check-typescript.sh): main's version judged it; the change's judges the pull r…
Removing a rule from the operator's own home changes what their sessions
obey as surely as placing one: an agent could remove the rule that
constrains it (hq issue 340).
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh, merge-check-typescript.sh): main's version judged it; the change's judges the pull r…
On the nodes where the agent runs as the operator, the home scope wrote the
operator's own ~/.claude, whose rules and skills load in every session, and
any node's tool could be addressed from another (hq issue 340). Take it
only for the serving node, where its home is an agent account's own; never
place a bus item in the operator's home. Ignore the built binary.
Instructions, output styles, skills, commands and subagents for every node
or some reach every session as the mesh's own word, and the tools cannot
tell an agent from the operator; the operator decided they are set at the
terminal until a surface of their own updates them (hq issue 340). They
are settings keys, each text a list of lines; the home scope stays free.
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
A byte-order mark, a flow mapping or a merged key hid a key the line scan
looked for, and @path read any file into every session the item reached
(hq issue 340 review).
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
The tool runner is not told who called, so a hook, a managed setting, a
permission rule or a tool server registered through these tools could come
from an agent confined on another node and run as the operator there
(hq issue 340). They are set at the controller's terminal now; a skill,
subagent or command that runs something by itself is refused too, kept
hooks and settings are never rendered, and claude_code_registrations
lists what is registered and by which node.
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
The agent kept running ssh <machine> journalctl while the journal verb existed. The managed
instructions now carry an "instead of" table generated from what each verb says it replaces, and
the plugin carries a PreToolUse guard that refuses ssh to a mesh machine and local work-arounds for a
mesh name, naming the tool or saying one must be created. The operator's override is read from the
session's start environment and recorded.
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
A removal kept its copy but nothing could put it back without a shell on
the machine. claude_code_home_restore puts a kept copy back when nothing is
at its path and the copy matches the digests recorded at removal;
claude_code_home_removed lists what is kept.
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
Hand-written items in an account's ~/.claude could only be removed over a
shell on the machine. claude_code_home_show reads one in full (memory,
~/.claude/CLAUDE.md, included); claude_code_home_remove removes one on the
person's word, with a required reason, refusing what the mesh placed and
symbolic links, keeping a dated copy in the module's state and logging it.
Refuse paths with empty, dot or parent segments and a file that is also a
directory; take an item only when its key says what it is; write the view
under its lock; expand nodes all and check node names; merge the plugin's
entries into the operator's own; render every file past one that fails;
in the home, never take over the person's file, never write through a
symbolic link, keep a deleted file deleted, keep the kind's directory; and
refuse settings that would deny the console or the marketplace.
Skills, subagents, commands and hooks had no machine-wide place, so they were
copied into homes by hand and drifted. Each is now registered once through the
module's tools, kept in its config state, and written per node: the plugin in
the managed directory, settings and instructions in the managed files, or the
account's own directory, touching only what the module placed. hq ADR 0216.
managed-settings.json carried only the mesh's fixed keys, so permissions and
auto-mode rules could only be set by hand per machine, outside the mesh.
A managed_settings setting is laid under the mesh's keys, which still win.
The module is one Go binary the runtime launches: the renderer (its
instruction file held byte for byte to the TypeScript one it replaces),
the credentials and identity files, the licence flow of ADR 0206 and the
MCP servers in state. Keeps the TypeScript module's key files, so a node
moving to it keeps its key. The npm package, its tests and its build go.
Both binaries were run together under the real runtime on a test bus with
postgres and a stub vendor: a login was adopted by one exchange, the node
bound and handed an access token, its file left with no refresh token, and
no token in either state.
Awaited at import, a bucket not yet on the bus — or a grant the bus had not
reloaded — answered after the runtime's 10s handshake, and the module was left
unserved on its first assignment. Also cites module state as ADR 0201, as hq
main numbers it (folds #256).
One key per registration in the module's servers bucket — all.<server> or
<node>.<server> — watched by every node, so a node assigned after a
registration takes it at start, which the mcp.registered event could not do.
Also narrows apply()'s refusal by hand: the builder compiles without strict,
where the discriminated union does not narrow and the build failed.
Events carry what happened and no secret; tokens travel on requests (design 32 §10). The manager's
licence.rotated/switched events make the module ask anthropic-licence-manager.current; at start it
asks once to catch up. A refresh token appearing in the credentials file is a login: it is pushed to
the manager's adopt at once, sealed to the manager's key — the one time a refresh token travels. A
switch replaces the old licence's grant whole, removes the API key and its helper, and rewrites
oauthAccount in ~/.claude.json. New tools register and unregister MCP servers on this node, or with
nodes: all / a list via an mcp.registered event every node consumes; called for one node, the
answer names the other nodes running claude-code. 26 tests.
So the controller's ownership check refuses a second module owning either. ~/.claude is the
operator's at 0700 (it was 0755 on the workstations); of what is inside, the module owns only what it
writes, and the host keeps a directory that is not empty when the module goes (hq ADR 0182).
The module owns /etc/claude-code: managed-mcp.json lists the console as `mesh` over HTTP on
loopback plus the servers in its mcp_servers setting (exclusive, by the operator's choice — the
https rule of managedMcpServers refuses a loopback console); managed-settings.json carries the
attribution convention, keeps claude.ai connectors, and adds the key-helper only for an API-key
licence; CLAUDE.md says how a session here works. Rendered whenever the runtime collects the
tools, written only on change, through the operator account's sudo. Under the home, only the
credentials file, only on a hand-over. Nothing declared under a home or /etc; the console's
port comes from node-tools' mcp-endpoint (mesh-tools #34).