Skip to content
Closed
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
38 changes: 27 additions & 11 deletions scripts/agent-branch-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,32 @@ resolve_protected_branches() {
printf '%s' "$raw"
}

resolve_default_base_branch() {
local root="$1"
local configured_base preferred_base current_branch

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

preferred_base="${MUSAFETY_BASE_BRANCH_DEFAULT:-dev}"
if git -C "$root" show-ref --verify --quiet "refs/remotes/origin/${preferred_base}" \
|| git -C "$root" show-ref --verify --quiet "refs/heads/${preferred_base}"; then
printf '%s' "$preferred_base"
return 0
fi

current_branch="$(git -C "$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 '%s' "$preferred_base"
}

is_protected_branch_name() {
local branch="$1"
local protected_raw="$2"
Expand Down Expand Up @@ -195,17 +221,7 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then
fi

if [[ "$BASE_BRANCH_EXPLICIT" -eq 0 ]]; then
configured_base="$(git -C "$repo_root" config --get multiagent.baseBranch || true)"
if [[ -n "$configured_base" ]]; then
BASE_BRANCH="$configured_base"
else
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]]; then
BASE_BRANCH="$current_branch"
else
BASE_BRANCH="dev"
fi
fi
BASE_BRANCH="$(resolve_default_base_branch "$repo_root")"
fi

if git show-ref --verify --quiet "refs/remotes/origin/${BASE_BRANCH}"; then
Expand Down
12 changes: 11 additions & 1 deletion scripts/review-bot-watch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Continuously monitor GitHub pull requests targeting a base branch and dispatch
one Codex-agent task per newly opened/updated PR.

Options:
--base <branch> Base branch to watch (default: current branch)
--base <branch> Base branch to watch (default: multiagent.baseBranch or dev)
--interval <seconds> Poll interval (default: 30)
--agent <name> Agent name for codex-agent (default: guardex-review-bot)
--task-prefix <prefix> Task prefix for codex-agent branches (default: review-merge)
Expand Down Expand Up @@ -117,6 +117,16 @@ fi
repo_root="$(git rev-parse --show-toplevel)"

if [[ -z "$BASE_BRANCH" ]]; then
BASE_BRANCH="$(git -C "$repo_root" config --get multiagent.baseBranch 2>/dev/null || true)"
fi
if [[ -z "$BASE_BRANCH" ]]; then
preferred_base="${MUSAFETY_BASE_BRANCH_DEFAULT:-dev}"
if git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${preferred_base}" \
|| git -C "$repo_root" show-ref --verify --quiet "refs/heads/${preferred_base}"; then
BASE_BRANCH="$preferred_base"
fi
fi
if [[ -z "$BASE_BRANCH" || "$BASE_BRANCH" == "HEAD" ]]; then
BASE_BRANCH="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
fi
if [[ -z "$BASE_BRANCH" || "$BASE_BRANCH" == "HEAD" ]]; then
Expand Down
38 changes: 27 additions & 11 deletions templates/scripts/agent-branch-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,32 @@ resolve_protected_branches() {
printf '%s' "$raw"
}

resolve_default_base_branch() {
local root="$1"
local configured_base preferred_base current_branch

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

preferred_base="${MUSAFETY_BASE_BRANCH_DEFAULT:-dev}"
if git -C "$root" show-ref --verify --quiet "refs/remotes/origin/${preferred_base}" \
|| git -C "$root" show-ref --verify --quiet "refs/heads/${preferred_base}"; then
printf '%s' "$preferred_base"
return 0
fi

current_branch="$(git -C "$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 '%s' "$preferred_base"
}

is_protected_branch_name() {
local branch="$1"
local protected_raw="$2"
Expand Down Expand Up @@ -195,17 +221,7 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then
fi

if [[ "$BASE_BRANCH_EXPLICIT" -eq 0 ]]; then
configured_base="$(git -C "$repo_root" config --get multiagent.baseBranch || true)"
if [[ -n "$configured_base" ]]; then
BASE_BRANCH="$configured_base"
else
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]]; then
BASE_BRANCH="$current_branch"
else
BASE_BRANCH="dev"
fi
fi
BASE_BRANCH="$(resolve_default_base_branch "$repo_root")"
fi

if git show-ref --verify --quiet "refs/remotes/origin/${BASE_BRANCH}"; then
Expand Down
12 changes: 11 additions & 1 deletion templates/scripts/review-bot-watch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Continuously monitor GitHub pull requests targeting a base branch and dispatch
one Codex-agent task per newly opened/updated PR.

Options:
--base <branch> Base branch to watch (default: current branch)
--base <branch> Base branch to watch (default: multiagent.baseBranch or dev)
--interval <seconds> Poll interval (default: 30)
--agent <name> Agent name for codex-agent (default: guardex-review-bot)
--task-prefix <prefix> Task prefix for codex-agent branches (default: review-merge)
Expand Down Expand Up @@ -117,6 +117,16 @@ fi
repo_root="$(git rev-parse --show-toplevel)"

if [[ -z "$BASE_BRANCH" ]]; then
BASE_BRANCH="$(git -C "$repo_root" config --get multiagent.baseBranch 2>/dev/null || true)"
fi
if [[ -z "$BASE_BRANCH" ]]; then
preferred_base="${MUSAFETY_BASE_BRANCH_DEFAULT:-dev}"
if git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${preferred_base}" \
|| git -C "$repo_root" show-ref --verify --quiet "refs/heads/${preferred_base}"; then
BASE_BRANCH="$preferred_base"
fi
fi
if [[ -z "$BASE_BRANCH" || "$BASE_BRANCH" == "HEAD" ]]; then
BASE_BRANCH="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
fi
if [[ -z "$BASE_BRANCH" || "$BASE_BRANCH" == "HEAD" ]]; then
Expand Down
84 changes: 74 additions & 10 deletions test/install.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ test('review-bot-watch script prints help after setup', () => {
const helpResult = runCmd('bash', ['scripts/review-bot-watch.sh', '--help'], repoDir);
assert.equal(helpResult.status, 0, helpResult.stderr || helpResult.stdout);
assert.match(helpResult.stdout, /Continuously monitor GitHub pull requests targeting a base branch/);
assert.match(helpResult.stdout, /default: multiagent\.baseBranch or dev/);
});

test('review-bot-watch uses explicit codex-agent flags for argument parsing compatibility', () => {
Expand Down Expand Up @@ -635,12 +636,14 @@ test('setup agent-branch-start supports explicit snapshot override without codex
assert.match(result.stdout, /Created branch: agent\/bot\/prod-snapshot-one-ship-fix(?:-\d+)?/);
});

test('setup agent-branch-start defaults base to current branch and stores per-branch base metadata', () => {
const repoDir = initRepoOnBranch('main');
test('setup agent-branch-start defaults base to configured\/dev and keeps current checkout unchanged', () => {
const repoDir = initRepo();
seedCommit(repoDir);
attachOriginRemoteForBranch(repoDir, 'main');
attachOriginRemote(repoDir);
let result = runCmd('git', ['checkout', '-b', 'main'], repoDir);
assert.equal(result.status, 0, result.stderr || result.stdout);

let result = runNode(['setup', '--target', repoDir, '--no-global-install'], repoDir);
result = runNode(['setup', '--target', repoDir, '--no-global-install'], repoDir);
assert.equal(result.status, 0, result.stderr || result.stdout);

result = runCmd('git', ['add', '.'], repoDir);
Expand All @@ -649,21 +652,26 @@ test('setup agent-branch-start defaults base to current branch and stores per-br
ALLOW_COMMIT_ON_PROTECTED_BRANCH: '1',
});
assert.equal(result.status, 0, result.stderr || result.stdout);
result = runCmd('git', ['push', 'origin', 'main'], repoDir);
assert.equal(result.status, 0, result.stderr || result.stdout);

result = runCmd('bash', ['scripts/agent-branch-start.sh', 'auto-base', 'bot'], repoDir);
assert.equal(result.status, 0, result.stderr || result.stdout);
const agentBranch = extractCreatedBranch(result.stdout);
const agentWorktree = extractCreatedWorktree(result.stdout);

const upstream = runCmd('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'], agentWorktree);
const upstream = runCmd(
'git',
['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'],
agentWorktree,
);
assert.equal(upstream.status, 0, upstream.stderr || upstream.stdout);
assert.equal(upstream.stdout.trim(), 'origin/main');
assert.equal(upstream.stdout.trim(), 'origin/dev');

const storedBase = runCmd('git', ['config', '--get', `branch.${agentBranch}.musafetyBase`], repoDir);
assert.equal(storedBase.status, 0, storedBase.stderr || storedBase.stdout);
assert.equal(storedBase.stdout.trim(), 'main');
assert.equal(storedBase.stdout.trim(), 'dev');

const currentBranch = runCmd('git', ['branch', '--show-current'], repoDir);
assert.equal(currentBranch.status, 0, currentBranch.stderr || currentBranch.stdout);
assert.equal(currentBranch.stdout.trim(), 'main');
});

test('agent-branch-start moves protected-branch local changes into the new agent worktree', () => {
Expand Down Expand Up @@ -1104,6 +1112,62 @@ test('codex-agent launches codex inside a fresh sandbox worktree and keeps branc
assert.equal(branchResult.status, 0, 'agent branch should remain after default codex-agent run');
});

test('codex-agent defaults to configured/dev base and keeps current checkout branch unchanged', () => {
const repoDir = initRepo();
seedCommit(repoDir);
attachOriginRemote(repoDir);
let result = runCmd('git', ['checkout', '-b', 'main'], repoDir);
assert.equal(result.status, 0, result.stderr || result.stdout);

result = runNode(['setup', '--target', repoDir, '--no-global-install'], repoDir);
assert.equal(result.status, 0, result.stderr || result.stdout);
result = runCmd('git', ['add', '.'], repoDir);
assert.equal(result.status, 0, result.stderr || result.stdout);
result = runCmd('git', ['commit', '-m', 'apply gx setup'], repoDir, {
ALLOW_COMMIT_ON_PROTECTED_BRANCH: '1',
});
assert.equal(result.status, 0, result.stderr || result.stdout);

const fakeBin = fs.mkdtempSync(path.join(os.tmpdir(), 'musafety-fake-codex-default-base-'));
const fakeCodexPath = path.join(fakeBin, 'codex');
fs.writeFileSync(
fakeCodexPath,
`#!/usr/bin/env bash\n` +
`pwd > "${'${MUSAFETY_TEST_CODEX_CWD}'}"\n` +
`echo "$@" > "${'${MUSAFETY_TEST_CODEX_ARGS}'}"\n`,
'utf8',
);
fs.chmodSync(fakeCodexPath, 0o755);

const cwdMarker = path.join(repoDir, '.codex-agent-default-base-cwd');
const argsMarker = path.join(repoDir, '.codex-agent-default-base-args');
const launch = runCmd(
'bash',
['scripts/codex-agent.sh', 'launch-task', 'planner', '--model', 'gpt-5.4-mini'],
repoDir,
{
PATH: `${fakeBin}:${process.env.PATH}`,
MUSAFETY_TEST_CODEX_CWD: cwdMarker,
MUSAFETY_TEST_CODEX_ARGS: argsMarker,
},
);
assert.equal(launch.status, 0, launch.stderr || launch.stdout);
const launchedBranch = extractCreatedBranch(launch.stdout);
const launchedCwd = fs.readFileSync(cwdMarker, 'utf8').trim();

const upstream = runCmd('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'], launchedCwd);
assert.equal(upstream.status, 0, upstream.stderr || upstream.stdout);
assert.equal(upstream.stdout.trim(), 'origin/dev');

const storedBase = runCmd('git', ['config', '--get', `branch.${launchedBranch}.musafetyBase`], repoDir);
assert.equal(storedBase.status, 0, storedBase.stderr || storedBase.stdout);
assert.equal(storedBase.stdout.trim(), 'dev');

const currentBranch = runCmd('git', ['branch', '--show-current'], repoDir);
assert.equal(currentBranch.status, 0, currentBranch.stderr || currentBranch.stdout);
assert.equal(currentBranch.stdout.trim(), 'main');
});

test('codex-agent supports --codex-bin override before positional arguments', () => {
const repoDir = initRepo();
seedCommit(repoDir);
Expand Down
Loading