Agents have been doing their work in the operator's checkout at /var/home/keith/source/home-network-k3s-impl and clobbering each other. Nothing in the tree
told them not to. Worktrees appeared in CLAUDE.md only as §3's gate workarounds — i.e. as an
awkward edge case to survive — and decisions/README.md described worktree isolation as
something agents already do rather than as a rule they must follow. The rule itself was written
down nowhere.
What this changes
Documentation only. No code, no manifests, no pipeline logic.
CLAUDE.md: new ## 0. Work in your OWN workspace. Never in the shared checkout. Placed before §1 so that every §1/§2b/§3/§7 reference elsewhere in the repo and in the
handoff log keeps its meaning. Three commands, an explicit do-not list for the shared
checkout, the measurements, "put it outside the repo root", cleanup, what to do if you are
already standing in the shared checkout with edits, and what a PM must put in a brief it
writes for another agent.
CLAUDE.md §3 reframed: a worktree gate run is now the normal path, not an edge case. The
breakage list grew from two to three — spec-immutability's skip is avoidable, not merely
tolerated, if the ENGINE shim also mounts the sibling spec repo read-only with -e SPEC_REPO=/home-network-k3s. And the rc-3 paragraph now names both expected skips: gitea-settings needs a CI-only token, so a local run cannot return 0 at all.
CLAUDE.md §4: one branch, one workspace, one PR.
README.md: a fourth non-negotiable rule, and "Working on this repo" now opens with how
to get out of the checkout before describing the container that runs in it.
decisions/README.md: cites §0 as the requirement instead of describing the practice.
Measured, in the shared checkout, 2026-10-05
Every number in the new §0 table was read off the thing itself (CLAUDE.md §2b), not recalled:
Claim
Value
checkout: moving entries in its reflog
107
branch it was sitting on
feat/pm-agent-issue-to-pr — not main
other runs' uncommitted trees left in it
2 (infra/agent/, platform/pm-agent/), mtimes 5 min apart
its local main vs origin/main
4386d14 vs 22c94ef — 2 behind
registered worktrees / already merged
42 branches, 31 merged
gate scripts carrying a .claude/ exclusion
9 — an in-tree worktree made find discovery count the same manifest up to 18 times
One claim of mine was wrong and is corrected in the doc. Earlier briefs recorded git worktree remove as blocked by the agent sandbox. Provoked rather than assumed: created a
throwaway detached worktree and removed it — rc 0. §0 now tells agents to try it, and the rework/<pr-number> hatch is scoped to someone else's stale worktree holding a branch, which
is a real and separate problem (31 of them).
Gate
rc 3 — UNDETERMINED. I am not calling this green. Pinned toolbox, from a worktree, DEV_KUBECONFIG=/dev/null + a two-mount ENGINE shim:
stage spec-immutability: pass <- genuinely RAN; the second mount is what makes it run
stage decisions-log: pass
stage lint: pass
stage l0: pass
stage render-golden: pass
stage l1: pass
stage approval-policy: pass <- but N/A: event='none', it cannot see a PR locally
stage gitea-settings: SKIPPED <- SETTINGS_API_TOKEN exists only in CI
stage conformance: SKIPPED <- CLUSTER_AVAILABLE!=true; no cluster-needing req touched
MERGE GATE: UNDETERMINED — stages that did not run: gitea-settings
gitea-settings is the only non-cluster skip and it cannot be made to run from a workstation,
so rc 3 is the ceiling for any local run, not a defect in this branch. Read the CI gate, not this.
This PR needs a human, and it is not a formality
CLAUDE.md is in infra/gitea/approval-policy.json's sensitive_paths ("the delivery contract
agents follow"), so approval-policy will be RED in CI until either a non-author posts an
approving review, or an administrator posts
/approve-override <reason>
as a PR review of type "Comment" — not in the plain comment box, which is invisible to CI on
today's token scopes (measured on PR #38).
I have not posted that override and will not.gitea-admin holds owner here and could
clear it, but CLAUDE.md §1 is explicit that an agent doing so on its own PR is exactly the
self-approval the stage exists to prevent.
Not done, deliberately
§0 is prose and nothing enforces it. There is no gate stage that detects work done in the
shared checkout, no check that a workspace is outside the repo root, no check that a branch was
cut from origin/main. Every other rule agents kept breaking (DECISIONS-LOG.md appends, the
three handoff headings) eventually became a gate stage, and that is the honest follow-up here. I
did not write one: the ask was documentation, and an enforcement stage lands in ci/**, itself
a sensitive path, and deserves its own PR.
Also left alone: the 31 stale merged worktrees and the 35 nested under .claude/worktrees/
(other sessions' workspaces — operator action), the two untracked trees in the shared checkout
(another run's in-flight work), and docs/plans/ssd-etcd-execution-runbook.md:18, which still
names an in-repo worktree path but is marked COMPLETED/historical and describes the
decommissioned dev cluster, so that path is a record rather than an instruction.
## The problem
Agents have been doing their work in the operator's checkout at
`/var/home/keith/source/home-network-k3s-impl` and clobbering each other. Nothing in the tree
told them not to. Worktrees appeared in `CLAUDE.md` only as §3's *gate workarounds* — i.e. as an
awkward edge case to survive — and `decisions/README.md` described worktree isolation as
something agents already do rather than as a rule they must follow. The rule itself was written
down nowhere.
## What this changes
Documentation only. No code, no manifests, no pipeline logic.
- **`CLAUDE.md`: new `## 0. Work in your OWN workspace. Never in the shared checkout.`** Placed
*before* §1 so that every `§1`/`§2b`/`§3`/`§7` reference elsewhere in the repo and in the
handoff log keeps its meaning. Three commands, an explicit do-not list for the shared
checkout, the measurements, "put it outside the repo root", cleanup, what to do if you are
already standing in the shared checkout with edits, and what a PM must put in a brief it
writes for another agent.
- **`CLAUDE.md` §3** reframed: a worktree gate run is now the normal path, not an edge case. The
breakage list grew from two to three — `spec-immutability`'s skip is *avoidable*, not merely
tolerated, if the `ENGINE` shim also mounts the sibling spec repo read-only with
`-e SPEC_REPO=/home-network-k3s`. And the rc-3 paragraph now names **both** expected skips:
`gitea-settings` needs a CI-only token, so a local run cannot return 0 at all.
- **`CLAUDE.md` §4**: one branch, one workspace, one PR.
- **`README.md`**: a fourth non-negotiable rule, and "Working on this repo" now opens with how
to get *out* of the checkout before describing the container that runs in it.
- **`decisions/README.md`**: cites §0 as the requirement instead of describing the practice.
## Measured, in the shared checkout, 2026-10-05
Every number in the new §0 table was read off the thing itself (CLAUDE.md §2b), not recalled:
| Claim | Value |
|---|---|
| `checkout: moving` entries in its reflog | **107** |
| branch it was sitting on | `feat/pm-agent-issue-to-pr` — not `main` |
| other runs' uncommitted trees left in it | **2** (`infra/agent/`, `platform/pm-agent/`), mtimes 5 min apart |
| its local `main` vs `origin/main` | `4386d14` vs `22c94ef` — **2 behind** |
| registered worktrees / already merged | **42** branches, **31** merged |
| gate scripts carrying a `.claude/` exclusion | **9** — an in-tree worktree made `find` discovery count the same manifest up to **18 times** |
**One claim of mine was wrong and is corrected in the doc.** Earlier briefs recorded
`git worktree remove` as blocked by the agent sandbox. Provoked rather than assumed: created a
throwaway detached worktree and removed it — **rc 0**. §0 now tells agents to try it, and the
`rework/<pr-number>` hatch is scoped to *someone else's* stale worktree holding a branch, which
is a real and separate problem (31 of them).
## Gate
**rc 3 — UNDETERMINED. I am not calling this green.** Pinned toolbox, from a worktree,
`DEV_KUBECONFIG=/dev/null` + a two-mount `ENGINE` shim:
```
stage spec-immutability: pass <- genuinely RAN; the second mount is what makes it run
stage decisions-log: pass
stage lint: pass
stage l0: pass
stage render-golden: pass
stage l1: pass
stage approval-policy: pass <- but N/A: event='none', it cannot see a PR locally
stage gitea-settings: SKIPPED <- SETTINGS_API_TOKEN exists only in CI
stage conformance: SKIPPED <- CLUSTER_AVAILABLE!=true; no cluster-needing req touched
MERGE GATE: UNDETERMINED — stages that did not run: gitea-settings
```
`gitea-settings` is the only non-cluster skip and it cannot be made to run from a workstation,
so rc 3 is the ceiling for any local run, not a defect in this branch. Read the CI gate, not this.
## This PR needs a human, and it is not a formality
`CLAUDE.md` is in `infra/gitea/approval-policy.json`'s `sensitive_paths` ("the delivery contract
agents follow"), so **`approval-policy` will be RED in CI** until either a non-author posts an
approving review, or an administrator posts
/approve-override <reason>
**as a PR review of type "Comment"** — not in the plain comment box, which is invisible to CI on
today's token scopes (measured on PR #38).
**I have not posted that override and will not.** `gitea-admin` holds `owner` here and could
clear it, but CLAUDE.md §1 is explicit that an agent doing so on its own PR is exactly the
self-approval the stage exists to prevent.
## Not done, deliberately
**§0 is prose and nothing enforces it.** There is no gate stage that detects work done in the
shared checkout, no check that a workspace is outside the repo root, no check that a branch was
cut from `origin/main`. Every other rule agents kept breaking (`DECISIONS-LOG.md` appends, the
three handoff headings) eventually became a gate stage, and that is the honest follow-up here. I
did not write one: the ask was documentation, and an enforcement stage lands in `ci/**`, itself
a sensitive path, and deserves its own PR.
Also left alone: the 31 stale merged worktrees and the 35 nested under `.claude/worktrees/`
(other sessions' workspaces — operator action), the two untracked trees in the shared checkout
(another run's in-flight work), and `docs/plans/ssd-etcd-execution-runbook.md:18`, which still
names an in-repo worktree path but is marked COMPLETED/historical and describes the
decommissioned dev cluster, so that path is a record rather than an instruction.
Handoff entry: `decisions/2026-10-05-agents-work-in-their-own-workspace.md`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Agents have been working in the operator's checkout and clobbering each other.
Nothing in the tree told them not to: worktrees appeared in CLAUDE.md only as
§3's gate workarounds, and decisions/README.md described worktree isolation as
something agents already do rather than as a rule.
New CLAUDE.md §0 states the rule, with the commands and an explicit do-not list
for the shared checkout. Placed before §1 so every existing §-reference in the
repo keeps its meaning.
Measured in the shared checkout on 2026-10-05, each row a distinct failure mode:
107 `checkout: moving` reflog entries; sitting on feat/pm-agent-issue-to-pr, not
main; two other runs' untracked trees left in it; local main two commits behind
origin/main; 42 registered worktrees of which 31 are on already-merged branches;
9 gate scripts carrying a `.claude/` exclusion because an in-tree worktree made
find-based discovery count the same manifest up to 18 times.
Corrects a claim carried in earlier briefs: `git worktree remove` is NOT blocked
by the agent sandbox. Provoked on a throwaway worktree — rc 0.
§3 reframed as the normal path and grown from two breakages to three:
spec-immutability's skip is avoidable, not merely tolerated, with the spec repo
mounted read-only in the same ENGINE shim plus -e SPEC_REPO=/home-network-k3s.
CLAUDE.md is a sensitive path, so this PR needs a non-author review or an
administrator /approve-override posted as a PR review. Not posted by me: §1
forbids an agent clearing that on its own PR.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The problem
Agents have been doing their work in the operator's checkout at
/var/home/keith/source/home-network-k3s-impland clobbering each other. Nothing in the treetold them not to. Worktrees appeared in
CLAUDE.mdonly as §3's gate workarounds — i.e. as anawkward edge case to survive — and
decisions/README.mddescribed worktree isolation assomething agents already do rather than as a rule they must follow. The rule itself was written
down nowhere.
What this changes
Documentation only. No code, no manifests, no pipeline logic.
CLAUDE.md: new## 0. Work in your OWN workspace. Never in the shared checkout.Placedbefore §1 so that every
§1/§2b/§3/§7reference elsewhere in the repo and in thehandoff log keeps its meaning. Three commands, an explicit do-not list for the shared
checkout, the measurements, "put it outside the repo root", cleanup, what to do if you are
already standing in the shared checkout with edits, and what a PM must put in a brief it
writes for another agent.
CLAUDE.md§3 reframed: a worktree gate run is now the normal path, not an edge case. Thebreakage list grew from two to three —
spec-immutability's skip is avoidable, not merelytolerated, if the
ENGINEshim also mounts the sibling spec repo read-only with-e SPEC_REPO=/home-network-k3s. And the rc-3 paragraph now names both expected skips:gitea-settingsneeds a CI-only token, so a local run cannot return 0 at all.CLAUDE.md§4: one branch, one workspace, one PR.README.md: a fourth non-negotiable rule, and "Working on this repo" now opens with howto get out of the checkout before describing the container that runs in it.
decisions/README.md: cites §0 as the requirement instead of describing the practice.Measured, in the shared checkout, 2026-10-05
Every number in the new §0 table was read off the thing itself (CLAUDE.md §2b), not recalled:
checkout: movingentries in its reflogfeat/pm-agent-issue-to-pr— notmaininfra/agent/,platform/pm-agent/), mtimes 5 min apartmainvsorigin/main4386d14vs22c94ef— 2 behind.claude/exclusionfinddiscovery count the same manifest up to 18 timesOne claim of mine was wrong and is corrected in the doc. Earlier briefs recorded
git worktree removeas blocked by the agent sandbox. Provoked rather than assumed: created athrowaway detached worktree and removed it — rc 0. §0 now tells agents to try it, and the
rework/<pr-number>hatch is scoped to someone else's stale worktree holding a branch, whichis a real and separate problem (31 of them).
Gate
rc 3 — UNDETERMINED. I am not calling this green. Pinned toolbox, from a worktree,
DEV_KUBECONFIG=/dev/null+ a two-mountENGINEshim:gitea-settingsis the only non-cluster skip and it cannot be made to run from a workstation,so rc 3 is the ceiling for any local run, not a defect in this branch. Read the CI gate, not this.
This PR needs a human, and it is not a formality
CLAUDE.mdis ininfra/gitea/approval-policy.json'ssensitive_paths("the delivery contractagents follow"), so
approval-policywill be RED in CI until either a non-author posts anapproving review, or an administrator posts
as a PR review of type "Comment" — not in the plain comment box, which is invisible to CI on
today's token scopes (measured on PR #38).
I have not posted that override and will not.
gitea-adminholdsownerhere and couldclear it, but CLAUDE.md §1 is explicit that an agent doing so on its own PR is exactly the
self-approval the stage exists to prevent.
Not done, deliberately
§0 is prose and nothing enforces it. There is no gate stage that detects work done in the
shared checkout, no check that a workspace is outside the repo root, no check that a branch was
cut from
origin/main. Every other rule agents kept breaking (DECISIONS-LOG.mdappends, thethree handoff headings) eventually became a gate stage, and that is the honest follow-up here. I
did not write one: the ask was documentation, and an enforcement stage lands in
ci/**, itselfa sensitive path, and deserves its own PR.
Also left alone: the 31 stale merged worktrees and the 35 nested under
.claude/worktrees/(other sessions' workspaces — operator action), the two untracked trees in the shared checkout
(another run's in-flight work), and
docs/plans/ssd-etcd-execution-runbook.md:18, which stillnames an in-repo worktree path but is marked COMPLETED/historical and describes the
decommissioned dev cluster, so that path is a record rather than an instruction.
Handoff entry:
decisions/2026-10-05-agents-work-in-their-own-workspace.md.🤖 Generated with Claude Code
b988da8b77to4d6f669c604d6f669c60toa795cb8b9aa795cb8b9ato2cb891c05d