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
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Improve Guard startup output design

## Problem

Guardex startup and takeover hints were hard to scan: machine-readable status,
human next steps, and finish/cleanup guidance were mixed into long lines.

## Scope

- Keep existing parser-stable `[agent-branch-start] Created branch` and
`[agent-branch-start] Worktree` lines.
- Replace the human next-step block with aligned branch/worktree/next fields.
- Replace the long Codex takeover sentence with a scannable resume block.
- Include `--cleanup` in finish guidance.

## Verification

- `bash -n scripts/agent-branch-start.sh scripts/codex-agent.sh templates/scripts/agent-branch-start.sh templates/scripts/codex-agent.sh`
- `node --test test/branch.test.js test/sandbox.test.js test/metadata.test.js`
32 changes: 22 additions & 10 deletions scripts/agent-branch-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,27 @@ print_reused_agent_worktree() {
echo "[agent-branch-start] OpenSpec tier: ${OPENSPEC_TIER}"
echo "[agent-branch-start] OpenSpec change: existing worktree"
echo "[agent-branch-start] OpenSpec plan: existing worktree"
echo "[agent-branch-start] Next steps:"
echo " cd \"${worktree_path}\""
echo " gx locks claim --branch \"${branch_name}\" <file...>"
echo " # continue work in this existing sandbox"
echo " gx branch finish --branch \"${branch_name}\" --via-pr --wait-for-merge"
print_agent_next_steps "$branch_name" "$worktree_path" "continue work in this existing sandbox" "$BASE_BRANCH"
}

print_agent_next_steps() {
local branch_name="$1"
local worktree_path="$2"
local work_step="$3"
local base_branch="${4:-main}"

if [[ -z "$base_branch" ]]; then
base_branch="main"
fi

echo "[agent-branch-start] Ready:"
echo " branch: ${branch_name}"
echo " worktree: ${worktree_path}"
echo " next:"
echo " cd \"${worktree_path}\""
echo " gx locks claim --branch \"${branch_name}\" <file...>"
echo " # ${work_step}"
echo " gx branch finish --branch \"${branch_name}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"
}

has_local_changes() {
Expand Down Expand Up @@ -842,8 +858,4 @@ if [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
else
echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
fi
echo "[agent-branch-start] Next steps:"
echo " cd \"${worktree_path}\""
echo " gx locks claim --branch \"${branch_name}\" <file...>"
echo " # implement + commit"
echo " gx branch finish --branch \"${branch_name}\" --base ${BASE_BRANCH} --via-pr --wait-for-merge"
print_agent_next_steps "$branch_name" "$worktree_path" "implement + commit" "$BASE_BRANCH"
17 changes: 13 additions & 4 deletions scripts/codex-agent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -735,9 +735,14 @@ print_takeover_prompt() {

finish_cmd="gx branch finish --branch \"${branch}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"

echo "[codex-agent] Takeover sandbox: ${wt}"
echo "[codex-agent] Takeover routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
echo "[codex-agent] Takeover prompt: Continue \`${change_slug}\` on branch \`${branch}\`. Work inside \`${wt}\`, review \`${change_artifact}\`, continue from the current state instead of creating a new sandbox, and when the work is done run \`${finish_cmd}\`."
echo "[codex-agent] Resume this sandbox:"
echo " change: ${change_slug}"
echo " branch: ${branch}"
echo " worktree: ${wt}"
echo " spec: ${change_artifact}"
echo " routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
echo " rule: continue current state; do not create a new sandbox"
echo " finish: ${finish_cmd}"
}

sync_worktree_with_base() {
Expand Down Expand Up @@ -1161,7 +1166,11 @@ else
echo "[codex-agent] Branch kept intentionally. Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
else
print_takeover_prompt "$worktree_path" "$worktree_branch"
echo "[codex-agent] If finished, merge with: gx branch finish --branch \"${worktree_branch}\" --base dev --via-pr --wait-for-merge"
finish_base_branch="$(resolve_worktree_base_branch "$worktree_path")"
if [[ -z "$finish_base_branch" ]]; then
finish_base_branch="dev"
fi
echo "[codex-agent] If finished, merge with: gx branch finish --branch \"${worktree_branch}\" --base ${finish_base_branch} --via-pr --wait-for-merge --cleanup"
echo "[codex-agent] Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
fi
fi
Expand Down
32 changes: 22 additions & 10 deletions templates/scripts/agent-branch-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,27 @@ print_reused_agent_worktree() {
echo "[agent-branch-start] OpenSpec tier: ${OPENSPEC_TIER}"
echo "[agent-branch-start] OpenSpec change: existing worktree"
echo "[agent-branch-start] OpenSpec plan: existing worktree"
echo "[agent-branch-start] Next steps:"
echo " cd \"${worktree_path}\""
echo " gx locks claim --branch \"${branch_name}\" <file...>"
echo " # continue work in this existing sandbox"
echo " gx branch finish --branch \"${branch_name}\" --via-pr --wait-for-merge"
print_agent_next_steps "$branch_name" "$worktree_path" "continue work in this existing sandbox" "$BASE_BRANCH"
}

print_agent_next_steps() {
local branch_name="$1"
local worktree_path="$2"
local work_step="$3"
local base_branch="${4:-main}"

if [[ -z "$base_branch" ]]; then
base_branch="main"
fi

echo "[agent-branch-start] Ready:"
echo " branch: ${branch_name}"
echo " worktree: ${worktree_path}"
echo " next:"
echo " cd \"${worktree_path}\""
echo " gx locks claim --branch \"${branch_name}\" <file...>"
echo " # ${work_step}"
echo " gx branch finish --branch \"${branch_name}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"
}

has_local_changes() {
Expand Down Expand Up @@ -842,8 +858,4 @@ if [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
else
echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
fi
echo "[agent-branch-start] Next steps:"
echo " cd \"${worktree_path}\""
echo " gx locks claim --branch \"${branch_name}\" <file...>"
echo " # implement + commit"
echo " gx branch finish --branch \"${branch_name}\" --base ${BASE_BRANCH} --via-pr --wait-for-merge"
print_agent_next_steps "$branch_name" "$worktree_path" "implement + commit" "$BASE_BRANCH"
17 changes: 13 additions & 4 deletions templates/scripts/codex-agent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -735,9 +735,14 @@ print_takeover_prompt() {

finish_cmd="gx branch finish --branch \"${branch}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"

echo "[codex-agent] Takeover sandbox: ${wt}"
echo "[codex-agent] Takeover routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
echo "[codex-agent] Takeover prompt: Continue \`${change_slug}\` on branch \`${branch}\`. Work inside \`${wt}\`, review \`${change_artifact}\`, continue from the current state instead of creating a new sandbox, and when the work is done run \`${finish_cmd}\`."
echo "[codex-agent] Resume this sandbox:"
echo " change: ${change_slug}"
echo " branch: ${branch}"
echo " worktree: ${wt}"
echo " spec: ${change_artifact}"
echo " routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
echo " rule: continue current state; do not create a new sandbox"
echo " finish: ${finish_cmd}"
}

sync_worktree_with_base() {
Expand Down Expand Up @@ -1161,7 +1166,11 @@ else
echo "[codex-agent] Branch kept intentionally. Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
else
print_takeover_prompt "$worktree_path" "$worktree_branch"
echo "[codex-agent] If finished, merge with: gx branch finish --branch \"${worktree_branch}\" --base dev --via-pr --wait-for-merge"
finish_base_branch="$(resolve_worktree_base_branch "$worktree_path")"
if [[ -z "$finish_base_branch" ]]; then
finish_base_branch="dev"
fi
echo "[codex-agent] If finished, merge with: gx branch finish --branch \"${worktree_branch}\" --base ${finish_base_branch} --via-pr --wait-for-merge --cleanup"
echo "[codex-agent] Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
fi
fi
Expand Down
7 changes: 7 additions & 0 deletions test/branch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,13 @@ test('agent-branch-start honors T1 notes-only OpenSpec scaffolding', () => {
assert.equal(result.status, 0, result.stderr || result.stdout);
assert.match(result.stdout, /\[agent-branch-start\] OpenSpec tier: T1/);
assert.match(result.stdout, /\[agent-branch-start\] OpenSpec plan: skipped by tier T1/);
assert.match(result.stdout, /\[agent-branch-start\] Ready:/);
assert.match(result.stdout, / branch: agent\/codex\/simple-tighten-copy-/);
assert.match(result.stdout, / next:\n cd "/);
assert.match(
result.stdout,
/gx branch finish --branch "agent\/codex\/simple-tighten-copy-[^"]+" --base dev --via-pr --wait-for-merge --cleanup/,
);

const createdWorktree = extractCreatedWorktree(result.stdout);
const changeSlug = extractOpenSpecChangeSlug(result.stdout);
Expand Down
8 changes: 5 additions & 3 deletions test/sandbox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -580,12 +580,14 @@ test('codex-agent prints a takeover prompt when the sandbox is kept after an inc
const launchedBranch = extractCreatedBranch(launch.stdout);
const changeSlug = launchedBranch.replace(/\//g, '-');
assert.match(combinedOutput, /\[codex-agent\] Sandbox worktree kept:/);
assert.match(combinedOutput, new RegExp(`\\[codex-agent\\] Takeover sandbox: ${escapeRegexLiteral(fs.readFileSync(cwdMarker, 'utf8').trim())}`));
assert.match(combinedOutput, /\[codex-agent\] Resume this sandbox:/);
assert.match(combinedOutput, new RegExp(` worktree: ${escapeRegexLiteral(fs.readFileSync(cwdMarker, 'utf8').trim())}`));
assert.match(
combinedOutput,
new RegExp(`\\[codex-agent\\] Takeover prompt: Continue \`${escapeRegexLiteral(changeSlug)}\` on branch \`${escapeRegexLiteral(launchedBranch)}\``),
new RegExp(` change: ${escapeRegexLiteral(changeSlug)}`),
);
assert.match(combinedOutput, /continue from the current state instead of creating a new sandbox/);
assert.match(combinedOutput, new RegExp(` branch: ${escapeRegexLiteral(launchedBranch)}`));
assert.match(combinedOutput, /rule: continue current state; do not create a new sandbox/);
assert.match(
combinedOutput,
new RegExp(`openspec/changes/${escapeRegexLiteral(changeSlug)}/tasks\\.md`),
Expand Down
Loading