diff --git a/AGENTS.md b/AGENTS.md index 14eccb2..1d41226 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 "" --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 "" --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. diff --git a/README.md b/README.md index 5ee37b7..644dcbd 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/bin/multiagent-safety.js b/bin/multiagent-safety.js index 45a84b2..fe23c28 100755 --- a/bin/multiagent-safety.js +++ b/bin/multiagent-safety.js @@ -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)" - 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)" @@ -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)" -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 "" @@ -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; } diff --git a/scripts/agent-branch-finish.sh b/scripts/agent-branch-finish.sh index 13a6438..9be4944 100755 --- a/scripts/agent-branch-finish.sh +++ b/scripts/agent-branch-finish.sh @@ -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 diff --git a/scripts/agent-branch-start.sh b/scripts/agent-branch-start.sh index ad41605..ffd8db8 100755 --- a/scripts/agent-branch-start.sh +++ b/scripts/agent-branch-start.sh @@ -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}\" " 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" diff --git a/scripts/codex-agent.sh b/scripts/codex-agent.sh index ba923a7..a4f734d 100755 --- a/scripts/codex-agent.sh +++ b/scripts/codex-agent.sh @@ -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' } @@ -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() { @@ -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) @@ -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 @@ -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" @@ -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 diff --git a/templates/AGENTS.multiagent-safety.md b/templates/AGENTS.multiagent-safety.md index 0bbf17d..6c4f187 100644 --- a/templates/AGENTS.multiagent-safety.md +++ b/templates/AGENTS.multiagent-safety.md @@ -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 "" --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 "" --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. diff --git a/templates/scripts/agent-branch-finish.sh b/templates/scripts/agent-branch-finish.sh index 13a6438..9be4944 100755 --- a/templates/scripts/agent-branch-finish.sh +++ b/templates/scripts/agent-branch-finish.sh @@ -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 diff --git a/templates/scripts/agent-branch-start.sh b/templates/scripts/agent-branch-start.sh index ad41605..ffd8db8 100755 --- a/templates/scripts/agent-branch-start.sh +++ b/templates/scripts/agent-branch-start.sh @@ -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}\" " 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" diff --git a/templates/scripts/codex-agent.sh b/templates/scripts/codex-agent.sh index ba923a7..a4f734d 100755 --- a/templates/scripts/codex-agent.sh +++ b/templates/scripts/codex-agent.sh @@ -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' } @@ -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() { @@ -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) @@ -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 @@ -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" @@ -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