Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/agent-worktree-guard.md
Original file line number Diff line number Diff line change
@@ -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`.
47 changes: 47 additions & 0 deletions .claude/hooks/guard-main-checkout.sh
Original file line number Diff line number Diff line change
@@ -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 <<EOF
⛔ Blocked: editing files while on the shared 'main' checkout (branch=main, root=$root).

This repo is worked on by multiple agents in parallel — the shared 'main' tree gets
its HEAD switched and reset under you, silently clobbering uncommitted edits.

Per AGENTS.md Prime Directive #11 (worktree-first), create a dedicated worktree and
re-run your edits from there:

git worktree add ../framework-<task> -b <branch> main
cd ../framework-<task> && pnpm install

Deliberate exception (not task work): re-run with OS_ALLOW_MAIN_EDITS=1.
EOF
exit 2
fi

exit 0
13 changes: 13 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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\""
}
]
}
]
}
}
12 changes: 8 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-<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.

---

## 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-<task> -b <branch>`;
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
Expand Down