Skip to content

fix(hooks): the Claude Code and Codex hooks stay inside their session's namespace - #698

Merged
atlas-from-plumb merged 9 commits into
mainfrom
atlas/hooks-namespace
Oct 16, 2026
Merged

atlas-from-plumb merged 9 commits into
mainfrom
atlas/hooks-namespace

Conversation

@atlas-from-plumb

Copy link
Copy Markdown
Collaborator

What

plumb hooks run-claude / run-codex inherit the session's PLUMB_NAMESPACE but never sent it, so the daemon matched a hook's conversation in any namespace: a default-namespace hook could read a namespaced session's unread count, resolve its hint root, or hand over its /clear marker (follow-up from PLAN-520 slice C).

  • The Stop-hook mailbox probe, the context-hint request (Claude Code and Codex) and the /clear notice now carry the token (hooks_namespace.go).
  • The daemon resolves it (connRegistry.namespaceForToken) and answers only from that namespace's connections: the default namespace for no token, nobody for a released/expired token, and nobody for a tokened request when no resolver is wired — never a fallback to the default namespace.
  • /clear markers are keyed by namespace; a conversation id carrying : cannot forge another namespace's key.
  • Old daemon / new hook: the extra JSON field is ignored, and the /clear line's second word only makes an old daemon store a marker nobody takes (advisory).

Verification

  • internal/cli/hooks_namespace_test.go: token read; each handler scoped and refusing; probe and hint lookups filtered by namespace; the hook senders carry the token (fake control socket); marker keys separate namespaces; id forgery; resolver against the real registry (live / none / released).
  • Mutation: 15 mutants — all KILLED except the daemon.go wiring line, which now fails closed.

🤖 Generated with Claude Code

atlas-from-plumb and others added 9 commits October 16, 2026 10:14
…'s namespace

The hooks inherit the session's PLUMB_NAMESPACE but never sent it, so the
daemon matched a hook's conversation in any namespace: a default-namespace
hook could read a namespaced session's unread count, hint root or /clear
handover. The mailbox probe, the context-hint request and the /clear notice
now carry the token; the daemon resolves it (connRegistry.namespaceForToken)
and answers only from that namespace's connections — the default namespace for
no token, nobody for a released or expired one. /clear markers are keyed by
namespace, and an id carrying ':' cannot forge another namespace's key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is refused, not answered from the default namespace

Pins namespaceForToken against the real registry (live, none, released).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the handover, hint and wiring paths

Review of #698: hookPeerCount counted sessions of every namespace, which both
extended a watcher for nobody and revealed another namespace's sessions. It now
counts only the caller's namespace. Hint state (window, seeds) is keyed by
namespace; a malformed token skips the /clear notice rather than sending it as
the default namespace; the daemon's registry-backed control handlers are built
by registryCtrlHandlers so their wiring is testable. New tests pin the
namespaced /clear handover, both hint resolve calls and the wiring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dow or seeds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wance

The context-hint allowance (per-turn and per-agent bytes) is now keyed by the
namespace-qualified id (namespacedID, renamed from clearMarkerKey); the default
namespace's rows are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@atlas-from-plumb
atlas-from-plumb merged commit 27c764f into main Oct 16, 2026
9 checks passed
@atlas-from-plumb
atlas-from-plumb deleted the atlas/hooks-namespace branch October 16, 2026 00:27
atlas-from-plumb added a commit that referenced this pull request Oct 16, 2026
Hooks stay inside their session's namespace (#698); context-hint uptake credits
a hint's use on a connection its session alone declared (#699); the shared file
watcher leaves linked git worktrees out (#700); an [lsp] restart can no longer
land on a superseded config (#701).


Plumb-Session: forest-ridge

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants