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
37 changes: 28 additions & 9 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [[ -z "$repo_root" ]]; then
exit 0
fi
NODE_BIN="${GUARDEX_NODE_BIN:-node}"
CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
guardex_env_helper="${repo_root}/scripts/guardex-env.sh"
if [[ -f "$guardex_env_helper" ]]; then
# shellcheck source=/dev/null
Expand All @@ -22,6 +24,23 @@ if declare -F guardex_repo_is_enabled >/dev/null 2>&1 && ! guardex_repo_is_enabl
exit 0
fi

run_guardex_cli() {
if [[ -n "$CLI_ENTRY" ]]; then
"$NODE_BIN" "$CLI_ENTRY" "$@"
return $?
fi
if command -v gx >/dev/null 2>&1; then
gx "$@"
return $?
fi
if command -v gitguardex >/dev/null 2>&1; then
gitguardex "$@"
return $?
fi
echo "[agent-branch-guard] Guardex CLI entrypoint unavailable; rerun via gx." >&2
return 127
}

if [[ "${ALLOW_COMMIT_ON_PROTECTED_BRANCH:-0}" == "1" ]]; then
exit 0
fi
Expand Down Expand Up @@ -118,9 +137,9 @@ if [[ "$should_require_codex_agent_branch" == "1" && "${GUARDEX_ALLOW_CODEX_ON_N
[guardex-preedit-guard] Codex edit/commit detected on a protected branch.
GuardeX requires Codex work to run from an isolated agent/* branch.
Start the sub-branch/worktree with:
bash scripts/codex-agent.sh "<task-or-plan>" "<agent-name>"
gx branch start "<task-or-plan>" "<agent-name>"
Or manually:
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
gx branch start "<task-or-plan>" "<agent-name>"
Then commit from the created agent/* branch.

Temporary bypass (not recommended):
Expand All @@ -132,7 +151,7 @@ MSG
cat >&2 <<'MSG'
[codex-branch-guard] Codex agent commit blocked on non-agent branch.
Use isolated branch/worktree first:
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
gx branch start "<task-or-plan>" "<agent-name>"
Then commit from the created agent/* branch.

Temporary bypass (not recommended):
Expand Down Expand Up @@ -163,9 +182,9 @@ if [[ "$is_protected_branch" == "1" ]]; then
cat >&2 <<'MSG'
[agent-branch-guard] Direct commits on protected branches are blocked.
Use an agent branch first:
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
gx branch start "<task-or-plan>" "<agent-name>"
After finishing work:
bash scripts/agent-branch-finish.sh
gx branch finish

Temporary bypass (not recommended):
ALLOW_COMMIT_ON_PROTECTED_BRANCH=1 git commit ...
Expand All @@ -177,7 +196,7 @@ if [[ "$is_agent_session" == "1" && "$branch" != agent/* ]]; then
cat >&2 <<'MSG'
[agent-branch-guard] Agent commits must run on dedicated agent/* branches.
Start an agent branch first:
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
gx branch start "<task-or-plan>" "<agent-name>"
Then commit on that branch.

Temporary bypass (not recommended):
Expand All @@ -191,15 +210,15 @@ if [[ "$branch" == agent/* ]]; then
while IFS= read -r staged_file; do
[[ -z "$staged_file" ]] && continue
[[ "$staged_file" == ".omx/state/agent-file-locks.json" ]] && continue
python3 scripts/agent-file-locks.py claim --branch "$branch" "$staged_file" >/dev/null 2>&1 || true
run_guardex_cli locks claim --branch "$branch" "$staged_file" >/dev/null 2>&1 || true
done < <(git diff --cached --name-only --diff-filter=ACMRDTUXB)
fi

if ! python3 scripts/agent-file-locks.py validate --branch "$branch" --staged; then
if ! run_guardex_cli locks validate --branch "$branch" --staged; then
cat >&2 <<'MSG'
[agent-branch-guard] Agent branch commits require file ownership locks.
Claim files first:
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
gx locks claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
MSG
exit 1
fi
Expand Down
32 changes: 16 additions & 16 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ When Guardex is enabled, Claude Code sessions use the same agent-worktree + Open

### Tiering (token-aware scaffolding)

`agent-branch-start.sh` and `agent-branch-finish.sh` accept `--tier {T0|T1|T2|T3}` to size the OpenSpec scaffolding to the change's blast radius. Default is `T3` (full scaffolding; current behavior). The tier is recorded in the bootstrap manifest so `finish` picks it up automatically.
`gx branch start` and `gx branch finish` accept `--tier {T0|T1|T2|T3}` to size the OpenSpec scaffolding to the change's blast radius. Default is `T3` (full scaffolding; current behavior). The tier is recorded in the bootstrap manifest so `finish` picks it up automatically.

| Tier | Use for | Scaffolding on `start` | Gates on `finish` |
|------|---------|------------------------|--------------------|
Expand All @@ -67,24 +67,24 @@ Examples:

```bash
# T0 (typo / trivial): fastest path, no OpenSpec artifacts
bash scripts/agent-branch-start.sh --tier T0 "fix-typo-in-readme" "claude-name"
gx branch start --tier T0 "fix-typo-in-readme" "claude-name"

# T1 (small fix): notes-only scaffold, commit message is the spec of record
bash scripts/agent-branch-start.sh --tier T1 "tighten-retry-backoff" "claude-name"
gx branch start --tier T1 "tighten-retry-backoff" "claude-name"

# T2 (default for real behavior changes): full change spec, no plan workspace
bash scripts/agent-branch-start.sh --tier T2 "add-oauth-endpoint" "claude-name"
gx branch start --tier T2 "add-oauth-endpoint" "claude-name"

# T3 (current default if --tier is omitted): plan workspace + full OpenSpec
bash scripts/agent-branch-start.sh "refactor-payment-pipeline" "claude-name"
gx branch start "refactor-payment-pipeline" "claude-name"
```

`finish` reads the tier from the manifest automatically; passing `--tier` on finish is only needed to override (e.g., upgrading to a fuller gate).

1. Start a sandbox worktree:

```bash
bash scripts/agent-branch-start.sh [--tier T0|T1|T2|T3] "<task>" "claude-<name>"
gx branch start [--tier T0|T1|T2|T3] "<task>" "claude-<name>"
```

Creates `agent/claude-<name>/<slug>` under `.omc/agent-worktrees/`, scaffolds the OpenSpec change + plan workspaces (sized by tier), and records the bootstrap manifest. Codex sessions keep using `.omx/agent-worktrees/`. Missing `codex-auth` silently falls back to an empty snapshot slug (expected for Claude sessions).
Expand All @@ -93,7 +93,7 @@ bash scripts/agent-branch-start.sh "refactor-payment-pipeline" "claude-name"

```bash
cd .omc/agent-worktrees/agent__claude-<name>__<slug>
python3 scripts/agent-file-locks.py claim --branch "agent/claude-<name>/<slug>" <file...>
gx locks claim --branch "agent/claude-<name>/<slug>" <file...>
# implement + commit inside this worktree
```

Expand All @@ -102,7 +102,7 @@ bash scripts/agent-branch-start.sh "refactor-payment-pipeline" "claude-name"
3. Finish via PR + cleanup:

```bash
bash scripts/agent-branch-finish.sh \
gx branch finish \
--branch "agent/claude-<name>/<slug>" \
--base dev --via-pr --wait-for-merge --cleanup
```
Expand All @@ -113,11 +113,11 @@ Notes:

- Slash commands `/opsx:*` in `.claude/commands/opsx/` drive the OpenSpec artifact flow.
- `.claude/settings.json` already wires the `skill_activation` / `skill_guard` hooks, so project-conventions enforcement runs automatically on edits.
- `skill_guard` blocks most Bash commands while the shell is on `dev`; run the start/claim/finish commands from within the worktree, or prefix the invocation with `ALLOW_BASH_ON_NON_AGENT_BRANCH=1` when calling from the primary checkout.
- `skill_guard` blocks most Bash commands while the shell is on `dev`; run the `gx branch ...`, `gx locks ...`, and `gx branch finish ...` commands from within the worktree, or prefix the invocation with `ALLOW_BASH_ON_NON_AGENT_BRANCH=1` when calling from the primary checkout.

### Stalled agent worktree recovery

`codex-agent.sh` auto-finishes a branch only when the codex CLI exits cleanly inside it. If the agent is killed, crashes, runs out of budget, or is started directly via `agent-branch-start.sh` (no `codex-agent.sh` wrapper), the worktree is left dirty with no commits and no PR — a "stalled" worktree.
The Guardex Codex launcher auto-finishes a branch only when the codex CLI exits cleanly inside it. If the agent is killed, crashes, runs out of budget, or is started directly via `gx branch start` without the launcher, the worktree is left dirty with no commits and no PR — a "stalled" worktree.

`scripts/agent-stalled-report.sh` is a quiet wrapper around `scripts/agent-autofinish-watch.sh --once --dry-run` that surfaces stalled worktrees. It is wired as a `SessionStart` hook in `.claude/settings.json`, so each Claude Code session begins with a one-line summary per stalled branch (and is silent when nothing is stalled).

Expand All @@ -144,7 +144,7 @@ Apply these repo-specific supplements in addition to that canonical contract:
- `agent-branch-start` and `agent-branch-finish` must fast-forward local `dev` from `origin/dev` before branch creation/merge.

2. Ownership and lock discipline
- Claim owned files before edits: `python3 scripts/agent-file-locks.py claim --branch "<agent-branch>" <file...>`.
- Claim owned files before edits: `gx locks claim --branch "<agent-branch>" <file...>`.
- If `main.rs` is in scope, claim lock first: `python3 scripts/main_rs_lock.py claim --owner "<agent-name>" --branch "<agent-branch>"`.
- Non-integrator branches must not edit `main.rs` unless explicit emergency override is approved.
- Pre-commit blocks `agent/*` commits with unclaimed files or missing valid `main.rs` lock.
Expand Down Expand Up @@ -181,7 +181,7 @@ When Guardex is enabled, this repo uses **OpenSpec as the primary workflow and S
3. Keep artifacts editable throughout implementation (proposal/spec/design/tasks are living docs, not rigid phase gates).
4. Implement from `tasks.md`; keep code and specs in sync (update `spec.md` as behavior changes).
5. Keep `tasks.md` checkpoint status updated continuously during execution; mark items as soon as they complete (do not batch-update at the end).
6. Default `tasks.md` scaffolds and manual task edits must include a final completion/cleanup section that ends with PR merge + sandbox cleanup (`gx finish --via-pr --wait-for-merge --cleanup` or `scripts/agent-branch-finish.sh ... --cleanup`) and captures PR URL + final `MERGED` handoff evidence.
6. Default `tasks.md` scaffolds and manual task edits must include a final completion/cleanup section that ends with PR merge + sandbox cleanup (`gx branch finish ... --cleanup` or `gx finish --all`) and captures PR URL + final `MERGED` handoff evidence.
7. Validate specs locally: `openspec validate --specs`.
8. Verify before archiving (`/opsx:verify <change>` when applicable); never archive unverified changes.

Expand Down Expand Up @@ -261,22 +261,22 @@ scripts/openspec/init-plan-workspace.sh <plan-slug>
`GUARDEX_ON=0` disables Guardex for that repo.
`GUARDEX_ON=1` explicitly enables Guardex for that repo again.

**Isolation.** Every task runs on a dedicated `agent/*` branch + worktree. Start with `scripts/agent-branch-start.sh "<task>" "<agent-name>"`. Treat the base branch (`main`/`dev`) as read-only while an agent branch is active. Never `git checkout <branch>` on a primary working tree (including nested repos); use `git worktree add` instead. The `.githooks/post-checkout` hook auto-reverts primary-branch switches during agent sessions - bypass only with `GUARDEX_ALLOW_PRIMARY_BRANCH_SWITCH=1`.
**Isolation.** Every task runs on a dedicated `agent/*` branch + worktree. Start with `gx branch start "<task>" "<agent-name>"`. Treat the base branch (`main`/`dev`) as read-only while an agent branch is active. Never `git checkout <branch>` on a primary working tree (including nested repos); use `git worktree add` instead. The `.githooks/post-checkout` hook auto-reverts primary-branch switches during agent sessions - bypass only with `GUARDEX_ALLOW_PRIMARY_BRANCH_SWITCH=1`.
For every new task, including follow-up work in the same chat/session, if an assigned agent sub-branch/worktree is already open, continue in that sub-branch instead of creating a fresh lane unless the user explicitly redirects scope.
Never implement directly on the local/base branch checkout; keep it unchanged and perform all edits in the agent sub-branch/worktree.

**Ownership.** Before editing, claim files: `scripts/agent-file-locks.py claim --branch "<agent-branch>" <file...>`. Before deleting, confirm the path is in your claim. Don't edit outside your scope unless reassigned.
**Ownership.** Before editing, claim files: `gx locks claim --branch "<agent-branch>" <file...>`. Before deleting, confirm the path is in your claim. Don't edit outside your scope unless reassigned.

**Handoff gate.** Post a one-line handoff note (plan/change, owned scope, intended action) before editing. Re-read the latest handoffs before replacing others' code.

**Completion.** Finish with `scripts/agent-branch-finish.sh --branch "<agent-branch>" --via-pr --wait-for-merge --cleanup` (or `gx finish --all`). Task is only complete when: commit pushed, PR URL recorded, state = `MERGED`, sandbox worktree pruned. If anything blocks, append a `BLOCKED:` note and stop - don't half-finish.
**Completion.** Finish with `gx branch finish --branch "<agent-branch>" --via-pr --wait-for-merge --cleanup` (or `gx finish --all`). Task is only complete when: commit pushed, PR URL recorded, state = `MERGED`, sandbox worktree pruned. If anything blocks, append a `BLOCKED:` note and stop - don't half-finish.
OMX completion policy: when a task is done, the agent must commit the task changes, push the agent branch, and create/update a PR before considering the branch complete.

**Parallel safety.** Assume other agents edit nearby. Never revert unrelated changes. Report conflicts in the handoff.

**Reporting.** Every completion handoff includes: files changed, behavior touched, verification commands + results, risks/follow-ups.

**OpenSpec (when change-driven).** Keep `openspec/changes/<slug>/tasks.md` checkboxes current during work, not batched at the end. Task scaffolds and manual task edits must include an explicit final completion/cleanup section that ends with PR merge + sandbox cleanup (`gx finish --via-pr --wait-for-merge --cleanup` or `scripts/agent-branch-finish.sh ... --cleanup`) and records PR URL + final `MERGED` evidence. Verify specs with `openspec validate --specs` before archive. Don't archive unverified.
**OpenSpec (when change-driven).** Keep `openspec/changes/<slug>/tasks.md` checkboxes current during work, not batched at the end. Task scaffolds and manual task edits must include an explicit final completion/cleanup section that ends with PR merge + sandbox cleanup (`gx branch finish ... --cleanup` or `gx finish --all`) and records PR URL + final `MERGED` evidence. Verify specs with `openspec validate --specs` before archive. Don't archive unverified.

**Version bumps.** If a change bumps a published version, the same PR updates release notes/changelog.
<!-- multiagent-safety:END -->
Loading