Three fixes, all in .claude/, all derived from the posture matrix measured on #337. Grouped because they are one unit of work — "a session can report, and can find the verbs" — and because each separately would cost its own pin ceremony.
1. The channel out is classifier-gated, and boot.sh clobbers the file that could fix it
infra#577 shipped a credential-free, connector-free report route. P4 still could not reach it — the auto-mode classifier blocked curl before it ran, twice, deterministically. In P4's words:
the routine picked curl specifically to avoid needing an MCP tool: the gate is in front of Bash egress, so that choice does not avoid it.
So "a checkout-less session has a channel out" is conditional, and no change to the door can make it otherwise — the gate is upstream of the network.
The org already has the mechanism. .claude/settings.json carries:
"permissions": { "allow": ["Bash(bash .claude/org-repair.sh)"] }
That is exactly how org-repair.sh is pre-approved past the classifier. The same shape, narrowed to the door endpoint, makes the report channel reliably reachable.
But boot.sh:161 writes $CFG/settings.json with cat > — it clobbers, and writes only a hooks block. So today it would erase any allowlist (and any user setting) on every bare boot. Two consequences:
- the allowlist cannot be installed until the write merges instead of overwriting
- this is already a live bug independent of this issue: a session's own
~/.claude/settings.json is destroyed at boot
Scope: make the write merge-preserving, and add a narrow allow entry for the pathbase-door report endpoint. Narrow is load-bearing — Bash(curl:*) is a blanket egress grant and a different security posture; it must not ride in on this.
2. read_session returns the PARENT's transcript inside a subagent
Measured by P5. read_session with no arguments returned the parent session's 453 turns and none of the subagent's own. Its words:
a subagent that trusts read_session will silently read its parent's history believing it is its own.
verb-server.mjs resolves through $CLAUDE_CODE_SESSION_ID, which in a subagent is the parent's. It returns plausible, wrong data with no error — the worst failure class in the set, because nothing signals it.
Scope: name whose transcript came back, and give the caller a way to ask for its own versus the parent's. Silent is the defect; either answer is fine once it is stated.
3. The verb names in our own docs do not resolve
Measured by P3, and independently by P4 and P5:
ToolSearch select:read_session,read_chat returned 'No matching deferred tools found' — the real names are prefixed mcp__bounded-verbs__, so even the exact-name select missed on the bare names.
claude/context.md and #326's framing both name the verbs bare. A session that has been told the name still cannot find the tool. That is precisely the trap #325/#326 exist to prevent, reintroduced by the documentation rather than the mechanism.
Scope: use the prefixed names wherever a session is told what to look for. One-line class of change; the highest value-per-byte fix in this issue.
What this does NOT fix
Cost, stated up front
Fixes 1 and 2 touch files inside the pinned bootstrap payload, so this needs the pin ceremony and a payload publish before it takes effect for any new session. Fix 3 is repo content and takes effect on merge.
Measured on #337 (P2–P6). Related: #325, #326, #339 (whose causal claim these runs refute), #342, .github-private#514, .github-private#848.
Three fixes, all in
.claude/, all derived from the posture matrix measured on #337. Grouped because they are one unit of work — "a session can report, and can find the verbs" — and because each separately would cost its own pin ceremony.1. The channel out is classifier-gated, and
boot.shclobbers the file that could fix itinfra#577shipped a credential-free, connector-free report route. P4 still could not reach it — the auto-mode classifier blockedcurlbefore it ran, twice, deterministically. In P4's words:So "a checkout-less session has a channel out" is conditional, and no change to the door can make it otherwise — the gate is upstream of the network.
The org already has the mechanism.
.claude/settings.jsoncarries:That is exactly how
org-repair.shis pre-approved past the classifier. The same shape, narrowed to the door endpoint, makes the report channel reliably reachable.But
boot.sh:161writes$CFG/settings.jsonwithcat >— it clobbers, and writes only ahooksblock. So today it would erase any allowlist (and any user setting) on every bare boot. Two consequences:~/.claude/settings.jsonis destroyed at bootScope: make the write merge-preserving, and add a narrow allow entry for the pathbase-door report endpoint. Narrow is load-bearing —
Bash(curl:*)is a blanket egress grant and a different security posture; it must not ride in on this.2.
read_sessionreturns the PARENT's transcript inside a subagentMeasured by P5.
read_sessionwith no arguments returned the parent session's 453 turns and none of the subagent's own. Its words:verb-server.mjsresolves through$CLAUDE_CODE_SESSION_ID, which in a subagent is the parent's. It returns plausible, wrong data with no error — the worst failure class in the set, because nothing signals it.Scope: name whose transcript came back, and give the caller a way to ask for its own versus the parent's. Silent is the defect; either answer is fine once it is stated.
3. The verb names in our own docs do not resolve
Measured by P3, and independently by P4 and P5:
claude/context.mdand #326's framing both name the verbs bare. A session that has been told the name still cannot find the tool. That is precisely the trap #325/#326 exist to prevent, reintroduced by the documentation rather than the mechanism.Scope: use the prefixed names wherever a session is told what to look for. One-line class of change; the highest value-per-byte fix in this issue.
What this does NOT fix
Blocked by classifierand names no route (The first wall a session hits is the auto-mode classifier, and it teaches nothing #342). An allowlist removes one instance; it does not make denials self-documenting, and that may be a vendor ask rather than ours.exit 0is upstream of all of this.Cost, stated up front
Fixes 1 and 2 touch files inside the pinned bootstrap payload, so this needs the pin ceremony and a payload publish before it takes effect for any new session. Fix 3 is repo content and takes effect on merge.
Measured on #337 (P2–P6). Related: #325, #326, #339 (whose causal claim these runs refute), #342,
.github-private#514,.github-private#848.