From 825b8a7526397d7825b6fc6574fe18aa9d939510 Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Wed, 15 Apr 2026 12:59:32 +0200 Subject: [PATCH] Align branch-finish/codex-agent workflow messaging with current merge expectations Capture the current local musafety script and docs updates in an isolated branch so they can be merged through the protected main workflow. Constraint: User requested merging the currently edited multiagent-safety files Confidence: medium Scope-risk: moderate Reversibility: clean Directive: Keep parent recodee from tracking nested multiagent-safety file content to avoid duplicate Source Control diffs Tested: npm test (2 passed, 1 failed: test/fuzzing.test.js) Not-tested: Additional CI environments --- AGENTS.md | 4 +-- README.md | 4 +-- bin/multiagent-safety.js | 19 +++------- scripts/agent-branch-finish.sh | 22 ------------ scripts/agent-branch-start.sh | 2 +- scripts/codex-agent.sh | 45 ++++++++++-------------- templates/AGENTS.multiagent-safety.md | 4 +-- templates/scripts/agent-branch-finish.sh | 22 ------------ templates/scripts/agent-branch-start.sh | 2 +- templates/scripts/codex-agent.sh | 45 ++++++++++-------------- 10 files changed, 48 insertions(+), 121 deletions(-) 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