From e7cb79c969b2b552985b9f0754526dde022c82eb Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Sun, 14 Jun 2026 11:35:30 +0500 Subject: [PATCH] chore(agents): enforce worktree-first discipline with a PreToolUse guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: Claude Opus 4.8 (1M context) --- .changeset/agent-worktree-guard.md | 11 +++++++ .claude/hooks/guard-main-checkout.sh | 47 ++++++++++++++++++++++++++++ .claude/settings.json | 13 ++++++++ AGENTS.md | 12 ++++--- 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 .changeset/agent-worktree-guard.md create mode 100755 .claude/hooks/guard-main-checkout.sh diff --git a/.changeset/agent-worktree-guard.md b/.changeset/agent-worktree-guard.md new file mode 100644 index 0000000000..59f5f202e2 --- /dev/null +++ b/.changeset/agent-worktree-guard.md @@ -0,0 +1,11 @@ +--- +--- + +chore(agents): enforce worktree-first discipline for parallel agents + +Adds a PreToolUse guard (`.claude/hooks/guard-main-checkout.sh`) that blocks +`Edit`/`Write`/`NotebookEdit` while the session's checkout is on the shared +`main` branch, and promotes worktree-first to AGENTS.md Prime Directive #11. +Tooling/docs only — no package version impact (empty changeset to satisfy the +changeset check). Override a deliberate non-task main edit with +`OS_ALLOW_MAIN_EDITS=1`. diff --git a/.claude/hooks/guard-main-checkout.sh b/.claude/hooks/guard-main-checkout.sh new file mode 100755 index 0000000000..09f9a191d8 --- /dev/null +++ b/.claude/hooks/guard-main-checkout.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# guard-main-checkout.sh — PreToolUse guard enforcing AGENTS.md Prime Directive #11 +# (worktree-first). Blocks Edit / Write / NotebookEdit while the session's checkout +# is on the shared `main` branch. +# +# Why: this repo is worked on by multiple agents in parallel. The shared `main` +# checkout has its HEAD switched and the tree reset *under you* by other agents, +# silently clobbering uncommitted edits (observed: a full session's work reverted +# twice). Dedicated per-task worktrees are physically isolated, so edits there are +# safe. This guard turns the documented discipline into a hard stop for the one +# place it actually fails — editing on `main`. +# +# Deliberate exception (a human quick-fix that will still land via PR, never task +# work committed straight to main): export OS_ALLOW_MAIN_EDITS=1 for the session. + +set -uo pipefail + +# Escape hatch. +if [ "${OS_ALLOW_MAIN_EDITS:-}" = "1" ]; then + exit 0 +fi + +dir="${CLAUDE_PROJECT_DIR:-$PWD}" + +# Not a git repo (or git unavailable) → nothing to guard, allow. +branch="$(git -C "$dir" rev-parse --abbrev-ref HEAD 2>/dev/null)" || exit 0 + +if [ "$branch" = "main" ]; then + root="$(git -C "$dir" rev-parse --show-toplevel 2>/dev/null || printf '%s' "$dir")" + cat >&2 < -b main + cd ../framework- && pnpm install + +Deliberate exception (not task work): re-run with OS_ALLOW_MAIN_EDITS=1. +EOF + exit 2 +fi + +exit 0 diff --git a/.claude/settings.json b/.claude/settings.json index bcd3c3f0b0..8e7ce810b3 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -20,5 +20,18 @@ "Bash(agent-browser snapshot *)", "Bash(agent-browser skills *)" ] + }, + "hooks": { + "PreToolUse": [ + { + "matcher": "Edit|Write|NotebookEdit", + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-main-checkout.sh\"" + } + ] + } + ] } } diff --git a/AGENTS.md b/AGENTS.md index 3a373c12df..a7f2decd38 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,17 +58,21 @@ Other scripts: `objectui:bump` (pull only), `objectui:build`, `objectui:clean`. 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. 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. 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. +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- -b main && cd ../framework- && 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. --- ## Multi-agent working discipline -This repo is worked on by **multiple agents in parallel**. Prefer **one git +This repo is worked on by **multiple agents in parallel**. **Use one git worktree per agent/task** (`git worktree add ../framework- -b `; run `pnpm install` in the new tree) so file systems are physically isolated — -that avoids most of the contention below. When agents must share one working -tree, branches get switched and shared files change *under you* mid-task — this -is expected, not a bug. Operate defensively: +this is mandatory, not a preference (Prime Directive #11), and a PreToolUse hook +blocks edits made while on the shared `main` branch. Working in the shared `main` +checkout is *not* a supported fallback: branches get switched and shared files — +including ones you just wrote — get reset *under you* mid-task (a full session's +work was silently reverted twice before this rule was enforced). Even inside your +own worktree, operate defensively: 1. **Only touch the files your task needs.** Don't "fix" unrelated diffs, reverts, or other agents' in-flight edits, and don't try to manage the whole