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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ OMX runtime state typically lives under `.omx/`:
- OMX completion policy: when a task is done, the agent must commit the task changes, push the agent branch, and create/update a PR for those changes (via `codex-agent` or `agent-branch-finish`).
- Auto-finish now waits for required checks/merge and then cleans merged sandbox branch/worktree by default.
- Use `--no-cleanup` only when you explicitly need to keep a merged sandbox for audit/debug follow-up.
- If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "<agent-branch>" --via-pr --wait-for-merge` and keep the branch open until checks/review pass.
- If merge/rebase conflicts block auto-finish, run a conflict-resolution review pass in that sandbox branch, then rerun `agent-branch-finish.sh --via-pr` until merged.
- If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "<agent-branch>" --base dev --via-pr --wait-for-merge` and keep the branch open until checks/review pass.
- If merge/rebase conflicts block auto-finish, run a conflict-resolution review pass in that sandbox branch, then rerun `agent-branch-finish.sh --base dev --via-pr --wait-for-merge` until merged.
- Completion is not valid until these are true: commit exists on the agent branch, branch is pushed to `origin`, and PR/merge status is produced by `agent-branch-finish.sh` or `codex-agent`.
- 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; otherwise create a fresh one from the current local base snapshot with `scripts/agent-branch-start.sh`.
- Never implement directly on the local/base branch checkout; keep it unchanged and perform all edits in the agent sub-branch/worktree.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref
npm test

# 4) Finish (commit/push/PR/merge flow)
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)"
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --base dev --via-pr --wait-for-merge

# 5) Optional cleanup after merge
gx cleanup --branch "$(git rev-parse --abbrev-ref HEAD)"
```

If you use `scripts/codex-agent.sh`, the finish flow is auto-run after the Codex session exits.
It auto-commits sandbox changes, retries once after syncing if the branch moved behind base during the run, then pushes/opens PR merge flow against the current base branch.
It auto-commits sandbox changes, retries once after syncing if the branch moved behind base during the run, then pushes/opens PR merge flow against `dev`.

If you run Codex in multiple existing agent worktrees directly (for example from VS Code Source Control), finalize all completed branches with:

Expand Down
19 changes: 4 additions & 15 deletions bin/multiagent-safety.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,10 @@ const AI_SETUP_PROMPT = `Use this exact checklist to setup GuardeX (Guardian T-R
bash scripts/codex-agent.sh "task" "agent-name"
bash scripts/agent-branch-start.sh "task" "agent-name"
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)"
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --base dev --via-pr --wait-for-merge
- For every new user message/task, repeat the same cycle:
start isolated agent branch/worktree -> claim file locks -> implement/verify ->
finish via PR/merge cleanup with scripts/agent-branch-finish.sh.
finish via PR/merge cleanup into dev with scripts/agent-branch-finish.sh.
- Finished branches stay available by default for audit/follow-up.
Remove them explicitly when done:
gx cleanup --branch "$(git rev-parse --abbrev-ref HEAD)"
Expand Down Expand Up @@ -288,7 +288,7 @@ gx review --interval 30
bash scripts/codex-agent.sh "task" "agent-name"
bash scripts/agent-branch-start.sh "task" "agent-name"
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)"
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --base dev --via-pr --wait-for-merge
gx finish --all
gx cleanup --branch "$(git rev-parse --abbrev-ref HEAD)"
bash scripts/openspec/init-plan-workspace.sh "<plan-slug>"
Expand Down Expand Up @@ -2792,27 +2792,16 @@ function branchExists(repoRoot, branch) {
return result.status === 0;
}

function resolveFinishBaseBranch(repoRoot, sourceBranch, explicitBase) {
function resolveFinishBaseBranch(repoRoot, _sourceBranch, explicitBase) {
if (explicitBase) {
return explicitBase;
}

const branchSpecific = readGitConfig(repoRoot, `branch.${sourceBranch}.musafetyBase`);
if (branchSpecific) {
return branchSpecific;
}

const configured = readGitConfig(repoRoot, GIT_BASE_BRANCH_KEY);
if (configured) {
return configured;
}

const current = gitRun(repoRoot, ['rev-parse', '--abbrev-ref', 'HEAD'], { allowFailure: true });
const currentBranch = String(current.stdout || '').trim();
if (current.status === 0 && currentBranch && currentBranch !== 'HEAD' && !currentBranch.startsWith('agent/')) {
return currentBranch;
}

return DEFAULT_BASE_BRANCH;
}

Expand Down
22 changes: 0 additions & 22 deletions scripts/agent-branch-finish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -162,28 +162,6 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 0 ]]; then
fi
fi

if [[ -z "$BASE_BRANCH" ]]; then
branch_stored_base="$(git -C "$repo_root" config --get "branch.${SOURCE_BRANCH}.musafetyBase" || true)"
if [[ -n "$branch_stored_base" ]]; then
BASE_BRANCH="$branch_stored_base"
fi
fi

if [[ -z "$BASE_BRANCH" ]]; then
source_upstream="$(git -C "$repo_root" for-each-ref --format='%(upstream:short)' "refs/heads/${SOURCE_BRANCH}" | head -n 1)"
source_upstream="${source_upstream:-}"
if [[ "$source_upstream" == */* ]]; then
BASE_BRANCH="${source_upstream#*/}"
fi
fi

if [[ -z "$BASE_BRANCH" ]]; then
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -n "$current_branch" && "$current_branch" != "HEAD" && "$current_branch" != "$SOURCE_BRANCH" ]]; then
BASE_BRANCH="$current_branch"
fi
fi

if [[ -z "$BASE_BRANCH" ]]; then
BASE_BRANCH="dev"
fi
Expand Down
2 changes: 1 addition & 1 deletion scripts/agent-branch-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,4 @@ echo "[agent-branch-start] Next steps:"
echo " cd \"${worktree_path}\""
echo " python3 scripts/agent-file-locks.py claim --branch \"${branch_name}\" <file...>"
echo " # implement + commit"
echo " bash scripts/agent-branch-finish.sh --branch \"${branch_name}\""
echo " bash scripts/agent-branch-finish.sh --branch \"${branch_name}\" --base dev --via-pr --wait-for-merge"
45 changes: 18 additions & 27 deletions scripts/codex-agent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,6 @@ resolve_start_base_branch() {
return 0
fi

local current_branch
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]]; then
printf '%s' "$current_branch"
return 0
fi

printf 'dev'
}

Expand Down Expand Up @@ -338,30 +331,20 @@ has_origin_remote() {
}

resolve_worktree_base_branch() {
local wt="$1"
local _wt="$1"
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -n "$BASE_BRANCH" ]]; then
printf '%s' "$BASE_BRANCH"
return 0
fi

local branch
branch="$(git -C "$wt" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -z "$branch" || "$branch" == "HEAD" ]]; then
return 0
fi

local stored_base
stored_base="$(git -C "$repo_root" config --get "branch.${branch}.musafetyBase" || true)"
if [[ -n "$stored_base" ]]; then
printf '%s' "$stored_base"
return 0
fi

local configured_base
configured_base="$(git -C "$repo_root" config --get multiagent.baseBranch || true)"
if [[ -n "$configured_base" ]]; then
printf '%s' "$configured_base"
return 0
fi

printf 'dev'
}

sync_worktree_with_base() {
Expand Down Expand Up @@ -598,12 +581,18 @@ looks_like_conflict_failure() {
run_finish_flow() {
local wt="$1"
local branch="$2"
local finish_base_branch=""
local finish_output=""
local -a finish_args

finish_args=(--branch "$branch")
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
finish_args+=(--base "$BASE_BRANCH")
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -n "$BASE_BRANCH" ]]; then
finish_base_branch="$BASE_BRANCH"
else
finish_base_branch="$(resolve_worktree_base_branch "$wt")"
fi
if [[ -n "$finish_base_branch" ]]; then
finish_args+=(--base "$finish_base_branch")
fi
if [[ "$AUTO_CLEANUP" -eq 1 ]]; then
finish_args+=(--cleanup)
Expand All @@ -613,9 +602,11 @@ run_finish_flow() {
fi

if has_origin_remote; then
if command -v gh >/dev/null 2>&1 || command -v "${MUSAFETY_GH_BIN:-gh}" >/dev/null 2>&1; then
finish_args+=(--via-pr)
if ! command -v "${MUSAFETY_GH_BIN:-gh}" >/dev/null 2>&1 && ! command -v gh >/dev/null 2>&1; then
echo "[codex-agent] Auto-finish requires GitHub CLI for PR flow; command not found: ${MUSAFETY_GH_BIN:-gh}" >&2
return 2
fi
finish_args+=(--via-pr)
else
echo "[codex-agent] No origin remote detected; skipping auto-finish merge/PR pipeline." >&2
return 2
Expand All @@ -631,7 +622,7 @@ run_finish_flow() {
if [[ "$AUTO_REVIEW_ON_CONFLICT" -eq 1 ]] && looks_like_conflict_failure "$finish_output"; then
echo "[codex-agent] Auto-finish hit conflicts. Launching Codex conflict-review pass in sandbox..." >&2
local review_prompt
review_prompt="Resolve git conflicts for branch ${branch} against ${BASE_BRANCH:-base branch}, then commit the resolution in this sandbox worktree and exit."
review_prompt="Resolve git conflicts for branch ${branch} against ${finish_base_branch:-dev}, then commit the resolution in this sandbox worktree and exit."

(
cd "$wt"
Expand Down Expand Up @@ -735,7 +726,7 @@ else
if [[ "$auto_finish_completed" -eq 1 ]]; then
echo "[codex-agent] Branch kept intentionally. Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
else
echo "[codex-agent] If finished, merge with: bash scripts/agent-branch-finish.sh --branch \"${worktree_branch}\" --via-pr"
echo "[codex-agent] If finished, merge with: bash scripts/agent-branch-finish.sh --branch \"${worktree_branch}\" --base dev --via-pr --wait-for-merge"
echo "[codex-agent] Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
fi
fi
Expand Down
4 changes: 2 additions & 2 deletions templates/AGENTS.multiagent-safety.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
- OMX completion policy: when a task is done, the agent must commit the task changes, push the agent branch, and create/update a PR for those changes (via `codex-agent` or `agent-branch-finish`).
- Auto-finish now waits for required checks/merge and then cleans merged sandbox branch/worktree by default.
- Use `--no-cleanup` only when you explicitly need to keep a merged sandbox for audit/debug follow-up.
- If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "<agent-branch>" --via-pr --wait-for-merge` and keep the branch open until checks/review pass.
- If merge/rebase conflicts block auto-finish, run a conflict-resolution review pass in that sandbox branch, then rerun `agent-branch-finish.sh --via-pr` until merged.
- If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "<agent-branch>" --base dev --via-pr --wait-for-merge` and keep the branch open until checks/review pass.
- If merge/rebase conflicts block auto-finish, run a conflict-resolution review pass in that sandbox branch, then rerun `agent-branch-finish.sh --base dev --via-pr --wait-for-merge` until merged.
- Completion is not valid until these are true: commit exists on the agent branch, branch is pushed to `origin`, and PR/merge status is produced by `agent-branch-finish.sh` or `codex-agent`.
- 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; otherwise create a fresh one from the current local base snapshot with `scripts/agent-branch-start.sh`.
- Never implement directly on the local/base branch checkout; keep it unchanged and perform all edits in the agent sub-branch/worktree.
Expand Down
22 changes: 0 additions & 22 deletions templates/scripts/agent-branch-finish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -162,28 +162,6 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 0 ]]; then
fi
fi

if [[ -z "$BASE_BRANCH" ]]; then
branch_stored_base="$(git -C "$repo_root" config --get "branch.${SOURCE_BRANCH}.musafetyBase" || true)"
if [[ -n "$branch_stored_base" ]]; then
BASE_BRANCH="$branch_stored_base"
fi
fi

if [[ -z "$BASE_BRANCH" ]]; then
source_upstream="$(git -C "$repo_root" for-each-ref --format='%(upstream:short)' "refs/heads/${SOURCE_BRANCH}" | head -n 1)"
source_upstream="${source_upstream:-}"
if [[ "$source_upstream" == */* ]]; then
BASE_BRANCH="${source_upstream#*/}"
fi
fi

if [[ -z "$BASE_BRANCH" ]]; then
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -n "$current_branch" && "$current_branch" != "HEAD" && "$current_branch" != "$SOURCE_BRANCH" ]]; then
BASE_BRANCH="$current_branch"
fi
fi

if [[ -z "$BASE_BRANCH" ]]; then
BASE_BRANCH="dev"
fi
Expand Down
2 changes: 1 addition & 1 deletion templates/scripts/agent-branch-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,4 @@ echo "[agent-branch-start] Next steps:"
echo " cd \"${worktree_path}\""
echo " python3 scripts/agent-file-locks.py claim --branch \"${branch_name}\" <file...>"
echo " # implement + commit"
echo " bash scripts/agent-branch-finish.sh --branch \"${branch_name}\""
echo " bash scripts/agent-branch-finish.sh --branch \"${branch_name}\" --base dev --via-pr --wait-for-merge"
45 changes: 18 additions & 27 deletions templates/scripts/codex-agent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,6 @@ resolve_start_base_branch() {
return 0
fi

local current_branch
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]]; then
printf '%s' "$current_branch"
return 0
fi

printf 'dev'
}

Expand Down Expand Up @@ -338,30 +331,20 @@ has_origin_remote() {
}

resolve_worktree_base_branch() {
local wt="$1"
local _wt="$1"
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -n "$BASE_BRANCH" ]]; then
printf '%s' "$BASE_BRANCH"
return 0
fi

local branch
branch="$(git -C "$wt" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -z "$branch" || "$branch" == "HEAD" ]]; then
return 0
fi

local stored_base
stored_base="$(git -C "$repo_root" config --get "branch.${branch}.musafetyBase" || true)"
if [[ -n "$stored_base" ]]; then
printf '%s' "$stored_base"
return 0
fi

local configured_base
configured_base="$(git -C "$repo_root" config --get multiagent.baseBranch || true)"
if [[ -n "$configured_base" ]]; then
printf '%s' "$configured_base"
return 0
fi

printf 'dev'
}

sync_worktree_with_base() {
Expand Down Expand Up @@ -598,12 +581,18 @@ looks_like_conflict_failure() {
run_finish_flow() {
local wt="$1"
local branch="$2"
local finish_base_branch=""
local finish_output=""
local -a finish_args

finish_args=(--branch "$branch")
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
finish_args+=(--base "$BASE_BRANCH")
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -n "$BASE_BRANCH" ]]; then
finish_base_branch="$BASE_BRANCH"
else
finish_base_branch="$(resolve_worktree_base_branch "$wt")"
fi
if [[ -n "$finish_base_branch" ]]; then
finish_args+=(--base "$finish_base_branch")
fi
if [[ "$AUTO_CLEANUP" -eq 1 ]]; then
finish_args+=(--cleanup)
Expand All @@ -613,9 +602,11 @@ run_finish_flow() {
fi

if has_origin_remote; then
if command -v gh >/dev/null 2>&1 || command -v "${MUSAFETY_GH_BIN:-gh}" >/dev/null 2>&1; then
finish_args+=(--via-pr)
if ! command -v "${MUSAFETY_GH_BIN:-gh}" >/dev/null 2>&1 && ! command -v gh >/dev/null 2>&1; then
echo "[codex-agent] Auto-finish requires GitHub CLI for PR flow; command not found: ${MUSAFETY_GH_BIN:-gh}" >&2
return 2
fi
finish_args+=(--via-pr)
else
echo "[codex-agent] No origin remote detected; skipping auto-finish merge/PR pipeline." >&2
return 2
Expand All @@ -631,7 +622,7 @@ run_finish_flow() {
if [[ "$AUTO_REVIEW_ON_CONFLICT" -eq 1 ]] && looks_like_conflict_failure "$finish_output"; then
echo "[codex-agent] Auto-finish hit conflicts. Launching Codex conflict-review pass in sandbox..." >&2
local review_prompt
review_prompt="Resolve git conflicts for branch ${branch} against ${BASE_BRANCH:-base branch}, then commit the resolution in this sandbox worktree and exit."
review_prompt="Resolve git conflicts for branch ${branch} against ${finish_base_branch:-dev}, then commit the resolution in this sandbox worktree and exit."

(
cd "$wt"
Expand Down Expand Up @@ -735,7 +726,7 @@ else
if [[ "$auto_finish_completed" -eq 1 ]]; then
echo "[codex-agent] Branch kept intentionally. Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
else
echo "[codex-agent] If finished, merge with: bash scripts/agent-branch-finish.sh --branch \"${worktree_branch}\" --via-pr"
echo "[codex-agent] If finished, merge with: bash scripts/agent-branch-finish.sh --branch \"${worktree_branch}\" --base dev --via-pr --wait-for-merge"
echo "[codex-agent] Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
fi
fi
Expand Down