Skip to content

Commit 3ecb012

Browse files
xuyushun441-sysos-zhuangclaude
authored
chore(agents): enforce worktree-first discipline with a PreToolUse guard (#1839)
Multiple agents work this repo in parallel. The shared `main` checkout has its HEAD switched and the tree reset under an agent mid-task, silently clobbering uncommitted edits (a full session's work was reverted twice). AGENTS.md already recommended one-worktree-per-task, but it was a soft "prefer" buried below the Prime Directives and nothing enforced it. - Add `.claude/hooks/guard-main-checkout.sh` (PreToolUse, wired in `.claude/settings.json`): blocks Edit/Write/NotebookEdit whenever HEAD is on `main`, with an actionable message; `OS_ALLOW_MAIN_EDITS=1` overrides for a deliberate non-task fix. - Promote worktree-first to AGENTS.md Prime Directive #11 and harden the Multi-agent working discipline section ("prefer" -> mandatory; main is not a supported fallback). Tooling/docs only — no package version impact. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent e1df97c commit 3ecb012

4 files changed

Lines changed: 79 additions & 4 deletions

File tree

.changeset/agent-worktree-guard.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
---
3+
4+
chore(agents): enforce worktree-first discipline for parallel agents
5+
6+
Adds a PreToolUse guard (`.claude/hooks/guard-main-checkout.sh`) that blocks
7+
`Edit`/`Write`/`NotebookEdit` while the session's checkout is on the shared
8+
`main` branch, and promotes worktree-first to AGENTS.md Prime Directive #11.
9+
Tooling/docs only — no package version impact (empty changeset to satisfy the
10+
changeset check). Override a deliberate non-task main edit with
11+
`OS_ALLOW_MAIN_EDITS=1`.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env bash
2+
# guard-main-checkout.sh — PreToolUse guard enforcing AGENTS.md Prime Directive #11
3+
# (worktree-first). Blocks Edit / Write / NotebookEdit while the session's checkout
4+
# is on the shared `main` branch.
5+
#
6+
# Why: this repo is worked on by multiple agents in parallel. The shared `main`
7+
# checkout has its HEAD switched and the tree reset *under you* by other agents,
8+
# silently clobbering uncommitted edits (observed: a full session's work reverted
9+
# twice). Dedicated per-task worktrees are physically isolated, so edits there are
10+
# safe. This guard turns the documented discipline into a hard stop for the one
11+
# place it actually fails — editing on `main`.
12+
#
13+
# Deliberate exception (a human quick-fix that will still land via PR, never task
14+
# work committed straight to main): export OS_ALLOW_MAIN_EDITS=1 for the session.
15+
16+
set -uo pipefail
17+
18+
# Escape hatch.
19+
if [ "${OS_ALLOW_MAIN_EDITS:-}" = "1" ]; then
20+
exit 0
21+
fi
22+
23+
dir="${CLAUDE_PROJECT_DIR:-$PWD}"
24+
25+
# Not a git repo (or git unavailable) → nothing to guard, allow.
26+
branch="$(git -C "$dir" rev-parse --abbrev-ref HEAD 2>/dev/null)" || exit 0
27+
28+
if [ "$branch" = "main" ]; then
29+
root="$(git -C "$dir" rev-parse --show-toplevel 2>/dev/null || printf '%s' "$dir")"
30+
cat >&2 <<EOF
31+
⛔ Blocked: editing files while on the shared 'main' checkout (branch=main, root=$root).
32+
33+
This repo is worked on by multiple agents in parallel — the shared 'main' tree gets
34+
its HEAD switched and reset under you, silently clobbering uncommitted edits.
35+
36+
Per AGENTS.md Prime Directive #11 (worktree-first), create a dedicated worktree and
37+
re-run your edits from there:
38+
39+
git worktree add ../framework-<task> -b <branch> main
40+
cd ../framework-<task> && pnpm install
41+
42+
Deliberate exception (not task work): re-run with OS_ALLOW_MAIN_EDITS=1.
43+
EOF
44+
exit 2
45+
fi
46+
47+
exit 0

.claude/settings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,18 @@
2020
"Bash(agent-browser snapshot *)",
2121
"Bash(agent-browser skills *)"
2222
]
23+
},
24+
"hooks": {
25+
"PreToolUse": [
26+
{
27+
"matcher": "Edit|Write|NotebookEdit",
28+
"hooks": [
29+
{
30+
"type": "command",
31+
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-main-checkout.sh\""
32+
}
33+
]
34+
}
35+
]
2336
}
2437
}

AGENTS.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,21 @@ Other scripts: `objectui:bump` (pull only), `objectui:build`, `objectui:clean`.
5858
8. **North Star alignment.** Read `content/docs/concepts/north-star.mdx` before structural changes. If a change doesn't advance §7 Built, shrink Drift, or unlock Missing — it probably shouldn't ship.
5959
9. **`OS_` env-var prefix.** All ObjectStack-owned env vars MUST start with `OS_`. When renaming a legacy var, use `readEnvWithDeprecation('OS_NEW', 'LEGACY')` from `@objectstack/types` (keeps legacy working one release). Third-party exceptions kept as-is: `NODE_ENV`, `HOME`, `OPENAI_API_KEY`, `TURSO_*`, OAuth `*_CLIENT_ID/SECRET`, `RESEND_API_KEY`, `POSTMARK_TOKEN`, `AI_GATEWAY_*`, `SMTP_*`. See #1382.
6060
10. **File issues for out-of-scope findings — don't silently expand scope or leave them buried.** When you hit a bug, gap, or unenforced capability that's unrelated to the current task, or too large to fix in scope, open a GitHub issue (`gh issue create`) with a clear repro/decision and link it from your PR. Corollary: **never advertise or demo a capability the runtime doesn't actually deliver** (declared ≠ enforced) — fix it, trim it, or file an issue, but don't fake coverage. Example: the spec declares 9 validation-rule types but the write-path validator enforces only 3 (`state_machine`/`script`/`cross_field`); the other 6 are tracked in #1475 rather than demoed in the showcase.
61+
11. **Worktree-first — never edit on the shared `main` checkout.** This repo is edited by **multiple agents at once**; the shared `main` tree has its HEAD switched and reset *under you*, silently clobbering uncommitted work. Before your **first file edit**, you MUST be in a dedicated worktree on a feature branch: `git worktree add ../framework-<task> -b <branch> main && cd ../framework-<task> && pnpm install`. A PreToolUse hook (`.claude/hooks/guard-main-checkout.sh`) **enforces** this — it blocks `Edit`/`Write`/`NotebookEdit` whenever HEAD is on `main` (override for a deliberate non-task fix with `OS_ALLOW_MAIN_EDITS=1`). Full playbook below.
6162

6263
---
6364

6465
## Multi-agent working discipline
6566

66-
This repo is worked on by **multiple agents in parallel**. Prefer **one git
67+
This repo is worked on by **multiple agents in parallel**. **Use one git
6768
worktree per agent/task** (`git worktree add ../framework-<task> -b <branch>`;
6869
run `pnpm install` in the new tree) so file systems are physically isolated —
69-
that avoids most of the contention below. When agents must share one working
70-
tree, branches get switched and shared files change *under you* mid-task — this
71-
is expected, not a bug. Operate defensively:
70+
this is mandatory, not a preference (Prime Directive #11), and a PreToolUse hook
71+
blocks edits made while on the shared `main` branch. Working in the shared `main`
72+
checkout is *not* a supported fallback: branches get switched and shared files —
73+
including ones you just wrote — get reset *under you* mid-task (a full session's
74+
work was silently reverted twice before this rule was enforced). Even inside your
75+
own worktree, operate defensively:
7276

7377
1. **Only touch the files your task needs.** Don't "fix" unrelated diffs,
7478
reverts, or other agents' in-flight edits, and don't try to manage the whole

0 commit comments

Comments
 (0)