Commit Graph
18 Commits
Author SHA1 Message Date
jochen 02a8837348 Fix review round 1 of the wait list and its hooks (issues 1629, 1743, 1744)
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.
2026-10-21 18:23:42 +02:00
jochen 0fa427dad3 Add the plugin's Stop hook so a written-down wait is never left unwatched (issue 1743)
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.
2026-10-21 18:08:13 +02:00
jochen 37628f4bce Add the plugin's SessionStart hook so a session learns its own id (issue 1744)
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).
2026-10-21 18:07:36 +02:00
jochen 56c68be7fa Keep a session's wait list in the claude-code module (issue 1629)
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.
2026-10-21 17:59:10 +02:00
mesh-tools 0260ae92f6 Feature 57 (config and provisioning): release the catalogue, the reviewed release branch merged with main, on top of main 2026-10-16 09:42:45 +00:00
jschoubben 6aa30eae66 claude-code: carry each project's memory folder between nodes (issue 367, ADR 0343)
mesh/merge-gate head 6aa30eae66be merged into trunk a84d0094a0a3 — pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes …
mesh/repo-check head 6aa30eae66be merged into trunk a84d0094a0a3 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery superseded: a newer head of the same pull request
A session started on another node began without what the sessions elsewhere had
learned, because Claude Code keeps project memory on one node. The module's own
process now carries the memory folders through its kept memory state: a change
is written elsewhere only over the version it replaced, a file changed on two
nodes keeps both (newer first) with a warning, and every replaced or removed
version is kept. No tool writes memory.
2026-10-13 15:57:44 +02:00
jschoubben fcab25604e Review a long record within a bound sized to it, and let a proposal waiting on its author leave the knowledge base healthy (hq issue 508)
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 (…
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
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.
2026-10-11 20:32:33 +02:00
jochen f58840b8a6 mesh-issues: check every new issue for a duplicate; claude-code: one judgement with no tools (hq ADR 0278)
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…
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
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.
2026-10-10 17:11:54 +02:00
jochen 74e1be97ab claude-code: an agent proposes a shared instruction section, and every node takes it only on the operator's warrant (hq ADR 0274)
mesh/delivery superseded: a newer head of the same pull request
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…
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
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.
2026-10-10 03:40:48 +02:00
jochen b240085e0a Close the review's gaps round the agent account (hq ADR 0266)
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…
mesh/delivery delivered
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.
2026-10-09 23:48:15 +02:00
jochen b57e6e8144 claude-code: write the agent's home as its own account where it has one
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.
2026-10-09 10:14:18 +02:00
jochen 736743fedc claude-code: refuse registering what a session runs, so no agent can plant it in the operator's sessions
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: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
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.
2026-10-09 02:30:11 +02:00
jochen f72a435487 claude-code: read where it runs from the controller's JSON answer
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: its merge-check.sh passed
mesh/delivery delivered
nodesRunningMe looked for printed lines in what is a JSON list, so every register tool's "also on"
hint and every nodes: "all" named no machine.
2026-10-07 20:28:06 +02:00
jochen 24a3fac2ba claude-code: point the agent at the mesh's tools and guard its shell against ssh to the mesh (hq ADR 0245)
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.
2026-10-07 20:27:31 +02:00
jochen 1d8f1ceff8 claude-code: drop what was removed while away, render one at a time, refuse an empty settings set
Review of the nox-mesh plugin: a watch hands over what is there, not what
went, so the view is pruned to the state's keys before it starts; the
watches and the tools render one at a time, as the home's record is read
and written whole; the register answer names how an item is offered.
2026-10-05 14:25:10 +02:00
jochen dd1035a27d claude-code: register the agent's configuration at three scopes, served as the nox-mesh plugin
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.
2026-10-05 11:57:35 +02:00
jochen 4ef4983d0d A login moves its node; an API key is added from any node, sealed (novox/hq ADR 0209)
The manager binds the node a login was adopted from to that login's licence,
switching it if it was bound to another; serves public_key; adopt takes a
key sealed to it. claude-code gains claude_code_add_api_key: read a file on
this node, seal, hand to adopt, remove the file, optionally switch here.
2026-10-04 15:11:54 +02:00
jochen 306d01d74d claude-code in Go (operator: always Go)
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.
2026-10-04 12:27:36 +02:00