You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/plan critic + draft scratch escapes the workspace contract into the shared ~/data root — 67 stray entries; bare-root escape is now 3-for-3 across every bootstrap in agent-worktree-contract.sh #3926
/plan critic + draft scratch escapes the workspace contract into the shared ~/data root — 67 stray entries, and this is now 3-for-3 across every bootstrap in agent-worktree-contract.sh
Surfaced by /monitor-tick at 00:04Z while attributing a new bare-root directory on the shared volume.
This is the third family of the same escape, and the three together are the real finding: scripts/lib/agent-worktree-contract.sh defines exactly three workspace bootstraps, and all three
have bare-root escapes on disk right now:
Three independent skills, three independent bootstraps, all correctly written, all bypassed. That
pattern is much stronger evidence for a shared root cause than any one family was on its own —
see Suggested fix below.
Symptom
$HOME/data holds 67 stray bare-root plan-* entries (10 dirs + 57 files), 743,368 B =
0.709 MiB, accumulating since 2026-06-04. The newest, plan-3792/, was created at 2026-08-24T00:04:29Z — between two consecutive monitor ticks, so this is live, not residue.
Ten directories:
157,337 B plan-3279 2026-06-15 19:33:53
64,708 B plan-3811 2026-08-09 06:40:36
55,245 B plan-3801 2026-08-09 05:18:43
40,225 B plan-3768-scratch 2026-08-11 13:12:57
40,002 B plan-3219 2026-06-16 19:11:07
25,485 B plan-3752-scratch 2026-08-10 21:22:46
24,685 B plan-3835 2026-08-10 18:22:56
24,276 B plan-3444 2026-06-19 05:05:05
12,903 B plan-3792 2026-08-24 00:04:29 <- created during this tick
4,096 B plan-3434 2026-08-08 22:56:15
plan-3792/draft.md (8,807 B) is a round-1 plan draft for #3792 — it opens ## 📝 Plan Draft (Round 1) and its body is a concrete implementation plan for the overlay
broadcast-drop metric bridge, i.e. genuine /plan output, not a stray temp file.
There is also one adjacent bare-root dir with a re-prefixed name, replan-3365/ (2026-06-17),
which I mention for completeness but have not counted in the 67.
Why this is off-contract
.claude/skills/plan/SKILL.md:110-122 states the invariant and even spells out the enforcement:
plan_critic_bootstrap validates and exports WORKTREE_BASE, CARGO_TARGET_DIR, and CRITIC_WORKTREE. Pre-seeded values are accepted only if they resolve under the real ~/data
AND match the expected session/issue prefix ($HOME/data/$SESSION_ID/plan-$ISSUE/...).
and the helper cannot produce a bare root path. scripts/lib/agent-worktree-contract.sh:130-152:
plan_critic_bootstrap() {
local issue="$1"local critic_id="$2"local session_id="${CLAUDE_SESSION_ID:-${SESSION_ID:-$(date +%Y%m%d-%H%M%S)}}"
...
local expected_prefix="$real_home/data/$session_id/plan-$issue"
...
local candidate_base="${incoming_base:-$real_home/data/$session_id/plan-$issue}"if! WORKTREE_BASE="$(require_home_data_path "$candidate_base""WORKTREE_BASE")";then
...
if [[ -n"$incoming_base" ]];thenif! require_session_prefix "$WORKTREE_BASE""$expected_prefix""WORKTREE_BASE";then
session_id has a three-level fallback terminating in $(date +%Y%m%d-%H%M%S), so it is never
empty, and require_session_prefix (:109) re-validates any pre-seeded WORKTREE_BASE against expected_prefix. A path of the form ~/data/plan-3792 — one level, no session segment — is
unreachable through this function. Something is writing plan artifacts without going through the
bootstrap.
Note the SKILL.md section immediately following the bootstrap is titled "Forbidden scratch patterns
(issue #2843 — hard requirement)" and enumerates the repo-tree / <repo>-pr<N> / /tmp leaks. The
bare ~/data root is not in that enumeration — which is plausibly why all three families slip
through the same gap.
The contract-correct shape is achievable and is being achieved
23 session-scoped plan-* entries exist, so this is per-pass, not structural:
Same skill, same helper, correct prefix. The 20260619-001954/plan-3518-* set is especially useful:
it is the same artifact naming as the bare-root plan-3093-critic-{a,b,c}.md family, just placed
correctly — so the artifact writer clearly can take a session-scoped base when it is given one.
plus plan-3801-issue.json (11,278 B) and the replan-3365/ dir. Two different critic-file
naming conventions (-critic-a.md vs -critA.md) in the same bare-root pool is itself evidence of
more than one writer.
Impact — hygiene and contract integrity, not runway
I sized the pool before framing it, because the sibling families differ by five orders of magnitude:
This pool: 743,368 B = 0.709 MiB across 2.7 months ≈ 0.0000003 GiB/day. Invisible next to the
0.070 GiB watchdog scratch and the 0.054 GiB do-* workspaces that dominate the volume's churn.
Cross-run collision.~/data/plan-3792 is a fixed path, so two concurrent plan rounds on the
same issue share one directory. The -scratch suffixed variants (plan-3752-scratch, plan-3768-scratch) read like someone already worked around this.
Data-root attribution noise. This issue exists because a routine bare-root directory delta
had to be chased down before it could be explained.
Suggested fix — fix it once, at the shared layer
Because the escape now reproduces across all three bootstraps, per-skill patching is the wrong
shape. Two changes that would cover every family at once:
Extend assert_no_repo_tree_scratch (agent-worktree-contract.sh:329) with a bare-root
pattern. It is already the detection-only, no-deletion guard over an enumerated leak-pattern
list, and its docstring calls itself "a fixed enumeration of the OBSERVED leak patterns". The
bare ~/data root is now an observed leak pattern, three times over. A single check rejecting ^$HOME/data/(plan|review-pr|do)-[^/]*$ would have caught all 148 entries across the three
issues.
Find the artifact writers that compose paths inline rather than deriving them from the
bootstrap's exported CRITIC_WORTREE/WORKTREE_BASE. For /plan specifically, the draft /
critic / converged writes are the suspects: the bootstrap exports CRITIC_WORKTREE, but the
round artifacts land as siblings named plan-<issue>-<artifact>.md, which suggests they are
written by a path template that never sees $SESSION_ID.
Adding the bare-root pattern to #2843's forbidden-scratch enumeration in each SKILL.md would also
close the documentation gap that let three independent skills converge on the same mistake.
Existing entries are co-tenant data; the monitor has not deleted any of them and is not
proposing that they be swept without an owner's sign-off.
Non-urgent — no validator impact, no label. For context: the mainnet validator is down on the
unrelated #3910 outage (39h28m, build_sha0ac84d42, deploys held by the blocked_active
quarantine); nothing in this issue bears on that.
Related: #3921 (review-pr bare-root class, same shape), #3925 (do-* bare-root worktree class,
same shape, 84.3 GiB), #2843 (the isolation contract these all violate).
/plancritic + draft scratch escapes the workspace contract into the shared~/dataroot — 67 stray entries, and this is now 3-for-3 across every bootstrap inagent-worktree-contract.shSurfaced by
/monitor-tickat 00:04Z while attributing a new bare-root directory on the shared volume.This is the third family of the same escape, and the three together are the real finding:
scripts/lib/agent-worktree-contract.shdefines exactly three workspace bootstraps, and all threehave bare-root escapes on disk right now:
plan_critic_bootstrap:130~/data/$SESSION_ID/plan-$ISSUE/review_pr_bootstrap:191~/data/$SESSION_ID/review-pr-$ISSUE/do_bootstrap:258~/data/$SESSION_ID/do-$ISSUE/Three independent skills, three independent bootstraps, all correctly written, all bypassed. That
pattern is much stronger evidence for a shared root cause than any one family was on its own —
see Suggested fix below.
Symptom
$HOME/dataholds 67 stray bare-rootplan-*entries (10 dirs + 57 files), 743,368 B =0.709 MiB, accumulating since 2026-06-04. The newest,
plan-3792/, was created at2026-08-24T00:04:29Z — between two consecutive monitor ticks, so this is live, not residue.
Ten directories:
plan-3792/draft.md(8,807 B) is a round-1 plan draft for #3792 — it opens## 📝 Plan Draft (Round 1)and its body is a concrete implementation plan for the overlaybroadcast-drop metric bridge, i.e. genuine
/planoutput, not a stray temp file.There is also one adjacent bare-root dir with a
re-prefixed name,replan-3365/(2026-06-17),which I mention for completeness but have not counted in the 67.
Why this is off-contract
.claude/skills/plan/SKILL.md:110-122states the invariant and even spells out the enforcement:and the helper cannot produce a bare root path.
scripts/lib/agent-worktree-contract.sh:130-152:session_idhas a three-level fallback terminating in$(date +%Y%m%d-%H%M%S), so it is neverempty, and
require_session_prefix(:109) re-validates any pre-seededWORKTREE_BASEagainstexpected_prefix. A path of the form~/data/plan-3792— one level, no session segment — isunreachable through this function. Something is writing plan artifacts without going through the
bootstrap.
Note the SKILL.md section immediately following the bootstrap is titled "Forbidden scratch patterns
(issue #2843 — hard requirement)" and enumerates the repo-tree /
<repo>-pr<N>//tmpleaks. Thebare
~/dataroot is not in that enumeration — which is plausibly why all three families slipthrough the same gap.
The contract-correct shape is achievable and is being achieved
23 session-scoped
plan-*entries exist, so this is per-pass, not structural:Same skill, same helper, correct prefix. The
20260619-001954/plan-3518-*set is especially useful:it is the same artifact naming as the bare-root
plan-3093-critic-{a,b,c}.mdfamily, just placedcorrectly — so the artifact writer clearly can take a session-scoped base when it is given one.
Five name shapes, i.e. more than one bypass site
plus
plan-3801-issue.json(11,278 B) and thereplan-3365/dir. Two different critic-filenaming conventions (
-critic-a.mdvs-critA.md) in the same bare-root pool is itself evidence ofmore than one writer.
Impact — hygiene and contract integrity, not runway
I sized the pool before framing it, because the sibling families differ by five orders of magnitude:
0.070 GiB watchdog scratch and the 0.054 GiB
do-*workspaces that dominate the volume's churn.is 84.307 GiB of full worktrees and genuinely runway-relevant). The free arm is nowhere near
firing: 841.66 GiB free, 8.4× margin over the 100 G threshold.
What it does cost is the same three things as #3921:
prefix, so
require_session_prefix's poisoning protection never applies to it, and nothingexpires it — there is no session dir to go away.
~/data/plan-3792is a fixed path, so two concurrent plan rounds on thesame issue share one directory. The
-scratchsuffixed variants (plan-3752-scratch,plan-3768-scratch) read like someone already worked around this.had to be chased down before it could be explained.
Suggested fix — fix it once, at the shared layer
Because the escape now reproduces across all three bootstraps, per-skill patching is the wrong
shape. Two changes that would cover every family at once:
assert_no_repo_tree_scratch(agent-worktree-contract.sh:329) with a bare-rootpattern. It is already the detection-only, no-deletion guard over an enumerated leak-pattern
list, and its docstring calls itself "a fixed enumeration of the OBSERVED leak patterns". The
bare
~/dataroot is now an observed leak pattern, three times over. A single check rejecting^$HOME/data/(plan|review-pr|do)-[^/]*$would have caught all 148 entries across the threeissues.
bootstrap's exported
CRITIC_WORTREE/WORKTREE_BASE. For/planspecifically, the draft /critic / converged writes are the suspects: the bootstrap exports
CRITIC_WORKTREE, but theround artifacts land as siblings named
plan-<issue>-<artifact>.md, which suggests they arewritten by a path template that never sees
$SESSION_ID.Adding the bare-root pattern to
#2843's forbidden-scratch enumeration in each SKILL.md would alsoclose the documentation gap that let three independent skills converge on the same mistake.
Existing entries are co-tenant data; the monitor has not deleted any of them and is not
proposing that they be swept without an owner's sign-off.
Non-urgent — no validator impact, no label. For context: the mainnet validator is down on the
unrelated #3910 outage (39h28m,
build_sha0ac84d42, deploys held by theblocked_activequarantine); nothing in this issue bears on that.
Related: #3921 (
review-prbare-root class, same shape), #3925 (do-*bare-root worktree class,same shape, 84.3 GiB), #2843 (the isolation contract these all violate).
Filed by
/monitor-tick.