@imdeadpool/guardex 7.0.16 → 7.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,8 @@ AGENT_NAME="${GUARDEX_AGENT_NAME:-agent}"
6
6
  BASE_BRANCH="${GUARDEX_BASE_BRANCH:-}"
7
7
  BASE_BRANCH_EXPLICIT=0
8
8
  CODEX_BIN="${GUARDEX_CODEX_BIN:-codex}"
9
+ NODE_BIN="${GUARDEX_NODE_BIN:-node}"
10
+ CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
9
11
  AUTO_FINISH_RAW="${GUARDEX_CODEX_AUTO_FINISH:-true}"
10
12
  AUTO_REVIEW_ON_CONFLICT_RAW="${GUARDEX_CODEX_AUTO_REVIEW_ON_CONFLICT:-true}"
11
13
  AUTO_CLEANUP_RAW="${GUARDEX_CODEX_AUTO_CLEANUP:-true}"
@@ -16,6 +18,23 @@ OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
16
18
  OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
17
19
  OPENSPEC_MASTERPLAN_LABEL_RAW="${GUARDEX_OPENSPEC_MASTERPLAN_LABEL-masterplan}"
18
20
 
21
+ run_guardex_cli() {
22
+ if [[ -n "$CLI_ENTRY" ]]; then
23
+ "$NODE_BIN" "$CLI_ENTRY" "$@"
24
+ return $?
25
+ fi
26
+ if command -v gx >/dev/null 2>&1; then
27
+ gx "$@"
28
+ return $?
29
+ fi
30
+ if command -v gitguardex >/dev/null 2>&1; then
31
+ gitguardex "$@"
32
+ return $?
33
+ fi
34
+ echo "[codex-agent] Guardex CLI entrypoint unavailable; rerun via gx." >&2
35
+ return 127
36
+ }
37
+
19
38
  normalize_bool() {
20
39
  local raw="${1:-}"
21
40
  local fallback="${2:-0}"
@@ -143,6 +162,7 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
143
162
  exit 1
144
163
  fi
145
164
  repo_root="$(git rev-parse --show-toplevel)"
165
+ active_session_state_script="${repo_root}/scripts/agent-session-state.js"
146
166
 
147
167
  guardex_env_helper="${repo_root}/scripts/guardex-env.sh"
148
168
  if [[ -f "$guardex_env_helper" ]]; then
@@ -373,11 +393,6 @@ start_sandbox_fallback() {
373
393
  printf '[agent-branch-start] Worktree: %s\n' "$worktree_path"
374
394
  }
375
395
 
376
- if [[ ! -x "${repo_root}/scripts/agent-branch-start.sh" ]]; then
377
- echo "[codex-agent] Missing scripts/agent-branch-start.sh. Run: gx setup" >&2
378
- exit 1
379
- fi
380
-
381
396
  start_args=("$TASK_NAME" "$AGENT_NAME")
382
397
  if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
383
398
  start_args+=("$BASE_BRANCH")
@@ -390,7 +405,7 @@ set +e
390
405
  start_output="$(
391
406
  GUARDEX_OPENSPEC_AUTO_INIT="$OPENSPEC_AUTO_INIT" \
392
407
  GUARDEX_OPENSPEC_MASTERPLAN_LABEL="$OPENSPEC_MASTERPLAN_LABEL_RAW" \
393
- bash "${repo_root}/scripts/agent-branch-start.sh" "${start_args[@]}" 2>&1
408
+ run_guardex_cli branch start "${start_args[@]}" 2>&1
394
409
  )"
395
410
  start_status=$?
396
411
  set -e
@@ -446,6 +461,40 @@ has_origin_remote() {
446
461
  git -C "$repo_root" remote get-url origin >/dev/null 2>&1
447
462
  }
448
463
 
464
+ run_active_session_state() {
465
+ local action="$1"
466
+ shift
467
+
468
+ if [[ ! -f "$active_session_state_script" ]]; then
469
+ return 0
470
+ fi
471
+ if ! command -v "$NODE_BIN" >/dev/null 2>&1; then
472
+ return 0
473
+ fi
474
+
475
+ "$NODE_BIN" "$active_session_state_script" "$action" "$@" >/dev/null 2>&1 || true
476
+ }
477
+
478
+ record_active_session_state() {
479
+ local wt="$1"
480
+ local branch="$2"
481
+
482
+ run_active_session_state \
483
+ start \
484
+ --repo "$repo_root" \
485
+ --branch "$branch" \
486
+ --task "$TASK_NAME" \
487
+ --agent "$AGENT_NAME" \
488
+ --worktree "$wt" \
489
+ --pid "$$" \
490
+ --cli "$CODEX_BIN"
491
+ }
492
+
493
+ clear_active_session_state() {
494
+ local branch="$1"
495
+ run_active_session_state stop --repo "$repo_root" --branch "$branch"
496
+ }
497
+
449
498
  origin_remote_supports_pr_finish() {
450
499
  local origin_url
451
500
  origin_url="$(git -C "$repo_root" remote get-url origin 2>/dev/null || true)"
@@ -474,6 +523,31 @@ resolve_worktree_base_branch() {
474
523
  printf 'dev'
475
524
  }
476
525
 
526
+ print_takeover_prompt() {
527
+ local wt="$1"
528
+ local branch="$2"
529
+ local base_branch change_slug change_artifact finish_cmd
530
+
531
+ base_branch="$(resolve_worktree_base_branch "$wt")"
532
+ if [[ -z "$base_branch" ]]; then
533
+ base_branch="dev"
534
+ fi
535
+
536
+ change_slug="$(resolve_openspec_change_slug "$branch")"
537
+ change_artifact="openspec/changes/${change_slug}/tasks.md"
538
+ if [[ ! -f "${wt}/${change_artifact}" ]]; then
539
+ change_artifact="openspec/changes/${change_slug}/notes.md"
540
+ fi
541
+ if [[ ! -f "${wt}/${change_artifact}" ]]; then
542
+ change_artifact="openspec/changes/${change_slug}/"
543
+ fi
544
+
545
+ finish_cmd="gx branch finish --branch \"${branch}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"
546
+
547
+ echo "[codex-agent] Takeover sandbox: ${wt}"
548
+ 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}\`."
549
+ }
550
+
477
551
  sync_worktree_with_base() {
478
552
  local wt="$1"
479
553
  if ! has_origin_remote; then
@@ -524,24 +598,12 @@ ensure_openspec_plan_workspace() {
524
598
  return 0
525
599
  fi
526
600
 
527
- hydrate_local_helper_in_worktree "$wt" "scripts/openspec/init-plan-workspace.sh"
528
-
529
- local openspec_script="${wt}/scripts/openspec/init-plan-workspace.sh"
530
- if [[ ! -f "$openspec_script" ]]; then
531
- echo "[codex-agent] Missing OpenSpec init script in sandbox: ${openspec_script}" >&2
532
- echo "[codex-agent] Run 'gx setup --target ${repo_root}' and retry." >&2
533
- return 1
534
- fi
535
- if [[ ! -x "$openspec_script" ]]; then
536
- chmod +x "$openspec_script" 2>/dev/null || true
537
- fi
538
-
539
601
  local plan_slug
540
602
  plan_slug="$(resolve_openspec_plan_slug "$branch")"
541
603
  local init_output=""
542
604
  if ! init_output="$(
543
605
  cd "$wt"
544
- bash "scripts/openspec/init-plan-workspace.sh" "$plan_slug" 2>&1
606
+ run_guardex_cli internal run-shell planInit "$plan_slug" 2>&1
545
607
  )"; then
546
608
  printf '%s\n' "$init_output" >&2
547
609
  echo "[codex-agent] OpenSpec workspace initialization failed for plan '${plan_slug}'." >&2
@@ -561,24 +623,12 @@ ensure_openspec_change_workspace() {
561
623
  return 0
562
624
  fi
563
625
 
564
- hydrate_local_helper_in_worktree "$wt" "scripts/openspec/init-change-workspace.sh"
565
-
566
- local openspec_script="${wt}/scripts/openspec/init-change-workspace.sh"
567
- if [[ ! -f "$openspec_script" ]]; then
568
- echo "[codex-agent] Missing OpenSpec change init script in sandbox: ${openspec_script}" >&2
569
- echo "[codex-agent] Run 'gx setup --target ${repo_root}' and retry." >&2
570
- return 1
571
- fi
572
- if [[ ! -x "$openspec_script" ]]; then
573
- chmod +x "$openspec_script" 2>/dev/null || true
574
- fi
575
-
576
626
  local change_slug capability_slug init_output=""
577
627
  change_slug="$(resolve_openspec_change_slug "$branch")"
578
628
  capability_slug="$(resolve_openspec_capability_slug)"
579
629
  if ! init_output="$(
580
630
  cd "$wt"
581
- bash "scripts/openspec/init-change-workspace.sh" "$change_slug" "$capability_slug" 2>&1
631
+ run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
582
632
  )"; then
583
633
  printf '%s\n' "$init_output" >&2
584
634
  echo "[codex-agent] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
@@ -607,11 +657,6 @@ worktree_has_changes() {
607
657
  claim_changed_files() {
608
658
  local wt="$1"
609
659
  local branch="$2"
610
- local lock_script="${repo_root}/scripts/agent-file-locks.py"
611
-
612
- if [[ ! -x "$lock_script" ]]; then
613
- return 0
614
- fi
615
660
 
616
661
  local changed_raw deleted_raw
617
662
  changed_raw="$({
@@ -622,7 +667,7 @@ claim_changed_files() {
622
667
 
623
668
  if [[ -n "$changed_raw" ]]; then
624
669
  mapfile -t changed_files < <(printf '%s\n' "$changed_raw")
625
- python3 "$lock_script" claim --branch "$branch" "${changed_files[@]}" >/dev/null 2>&1 || true
670
+ run_guardex_cli locks claim --branch "$branch" "${changed_files[@]}" >/dev/null 2>&1 || true
626
671
  fi
627
672
 
628
673
  deleted_raw="$({
@@ -632,7 +677,7 @@ claim_changed_files() {
632
677
 
633
678
  if [[ -n "$deleted_raw" ]]; then
634
679
  mapfile -t deleted_files < <(printf '%s\n' "$deleted_raw")
635
- python3 "$lock_script" allow-delete --branch "$branch" "${deleted_files[@]}" >/dev/null 2>&1 || true
680
+ run_guardex_cli locks allow-delete --branch "$branch" "${deleted_files[@]}" >/dev/null 2>&1 || true
636
681
  fi
637
682
  }
638
683
 
@@ -781,7 +826,7 @@ run_finish_flow() {
781
826
  return 2
782
827
  fi
783
828
 
784
- if finish_output="$(bash "${repo_root}/scripts/agent-branch-finish.sh" "${finish_args[@]}" 2>&1)"; then
829
+ if finish_output="$(run_guardex_cli branch finish "${finish_args[@]}" 2>&1)"; then
785
830
  printf '%s\n' "$finish_output"
786
831
  return 0
787
832
  fi
@@ -804,7 +849,7 @@ run_finish_flow() {
804
849
  fi
805
850
  )
806
851
 
807
- if finish_output="$(bash "${repo_root}/scripts/agent-branch-finish.sh" "${finish_args[@]}" 2>&1)"; then
852
+ if finish_output="$(run_guardex_cli branch finish "${finish_args[@]}" 2>&1)"; then
808
853
  printf '%s\n' "$finish_output"
809
854
  return 0
810
855
  fi
@@ -833,6 +878,19 @@ if ! ensure_openspec_plan_workspace "$worktree_path" "$worktree_branch"; then
833
878
  exit 1
834
879
  fi
835
880
 
881
+ active_session_recorded=0
882
+ cleanup_active_session_state_on_exit() {
883
+ set +e
884
+ if [[ "${active_session_recorded:-0}" -eq 1 && -n "${worktree_branch:-}" && "${worktree_branch:-}" != "HEAD" ]]; then
885
+ clear_active_session_state "$worktree_branch"
886
+ active_session_recorded=0
887
+ fi
888
+ }
889
+
890
+ record_active_session_state "$worktree_path" "$worktree_branch"
891
+ active_session_recorded=1
892
+ trap cleanup_active_session_state_on_exit EXIT INT TERM
893
+
836
894
  echo "[codex-agent] Launching ${CODEX_BIN} in sandbox: $worktree_path"
837
895
  cd "$worktree_path"
838
896
  set +e
@@ -841,6 +899,8 @@ codex_exit="$?"
841
899
  set -e
842
900
 
843
901
  cd "$repo_root"
902
+ cleanup_active_session_state_on_exit
903
+ trap - EXIT INT TERM
844
904
  final_exit="$codex_exit"
845
905
  auto_finish_completed=0
846
906
 
@@ -878,18 +938,16 @@ if [[ "$AUTO_FINISH" -eq 1 && -n "$worktree_branch" && "$worktree_branch" != "HE
878
938
  fi
879
939
  fi
880
940
 
881
- if [[ -x "${repo_root}/scripts/agent-worktree-prune.sh" ]]; then
882
- echo "[codex-agent] Session ended (exit=${codex_exit}). Running worktree cleanup..."
883
- prune_args=()
884
- if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
885
- prune_args+=(--base "$BASE_BRANCH")
886
- fi
887
- if [[ "$AUTO_CLEANUP" -eq 1 && "$auto_finish_completed" -eq 1 ]]; then
888
- prune_args+=(--only-dirty-worktrees --delete-branches --delete-remote-branches)
889
- fi
890
- if ! bash "${repo_root}/scripts/agent-worktree-prune.sh" "${prune_args[@]}"; then
891
- echo "[codex-agent] Warning: automatic worktree cleanup failed." >&2
892
- fi
941
+ echo "[codex-agent] Session ended (exit=${codex_exit}). Running worktree cleanup..."
942
+ prune_args=()
943
+ if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
944
+ prune_args+=(--base "$BASE_BRANCH")
945
+ fi
946
+ if [[ "$AUTO_CLEANUP" -eq 1 && "$auto_finish_completed" -eq 1 ]]; then
947
+ prune_args+=(--only-dirty-worktrees --delete-branches --delete-remote-branches)
948
+ fi
949
+ if ! run_guardex_cli worktree prune "${prune_args[@]}"; then
950
+ echo "[codex-agent] Warning: automatic worktree cleanup failed." >&2
893
951
  fi
894
952
 
895
953
  if [[ ! -d "$worktree_path" ]]; then
@@ -901,7 +959,8 @@ else
901
959
  if [[ "$auto_finish_completed" -eq 1 ]]; then
902
960
  echo "[codex-agent] Branch kept intentionally. Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
903
961
  else
904
- echo "[codex-agent] If finished, merge with: bash scripts/agent-branch-finish.sh --branch \"${worktree_branch}\" --base dev --via-pr --wait-for-merge"
962
+ print_takeover_prompt "$worktree_path" "$worktree_branch"
963
+ echo "[codex-agent] If finished, merge with: gx branch finish --branch \"${worktree_branch}\" --base dev --via-pr --wait-for-merge"
905
964
  echo "[codex-agent] Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
906
965
  fi
907
966
  fi
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ function parseOptions(argv) {
8
+ const options = {};
9
+ for (let index = 0; index < argv.length; index += 1) {
10
+ const token = argv[index];
11
+ if (!token.startsWith('--')) {
12
+ throw new Error(`Unexpected argument: ${token}`);
13
+ }
14
+ const key = token.slice(2);
15
+ const value = argv[index + 1];
16
+ if (!value || value.startsWith('--')) {
17
+ throw new Error(`Missing value for --${key}`);
18
+ }
19
+ options[key] = value;
20
+ index += 1;
21
+ }
22
+ return options;
23
+ }
24
+
25
+ function resolveExtensionSource(repoRoot) {
26
+ const candidates = [
27
+ path.join(repoRoot, 'vscode', 'guardex-active-agents'),
28
+ path.join(repoRoot, 'templates', 'vscode', 'guardex-active-agents'),
29
+ ];
30
+
31
+ for (const candidate of candidates) {
32
+ if (fs.existsSync(path.join(candidate, 'package.json'))) {
33
+ return candidate;
34
+ }
35
+ }
36
+
37
+ throw new Error('Could not find the Guardex VS Code companion sources.');
38
+ }
39
+
40
+ function removeIfExists(targetPath) {
41
+ if (fs.existsSync(targetPath)) {
42
+ fs.rmSync(targetPath, { recursive: true, force: true });
43
+ }
44
+ }
45
+
46
+ function main() {
47
+ const repoRoot = path.resolve(__dirname, '..');
48
+ const options = parseOptions(process.argv.slice(2));
49
+ const sourceDir = resolveExtensionSource(repoRoot);
50
+ const manifest = JSON.parse(fs.readFileSync(path.join(sourceDir, 'package.json'), 'utf8'));
51
+ const extensionId = `${manifest.publisher}.${manifest.name}`;
52
+ const extensionsDir = path.resolve(
53
+ options['extensions-dir'] ||
54
+ process.env.GUARDEX_VSCODE_EXTENSIONS_DIR ||
55
+ process.env.VSCODE_EXTENSIONS_DIR ||
56
+ path.join(os.homedir(), '.vscode', 'extensions'),
57
+ );
58
+
59
+ fs.mkdirSync(extensionsDir, { recursive: true });
60
+ const targetDir = path.join(extensionsDir, `${extensionId}-${manifest.version}`);
61
+
62
+ for (const entry of fs.readdirSync(extensionsDir, { withFileTypes: true })) {
63
+ if (!entry.isDirectory()) {
64
+ continue;
65
+ }
66
+ if (entry.name === path.basename(targetDir)) {
67
+ continue;
68
+ }
69
+ if (entry.name.startsWith(`${extensionId}-`)) {
70
+ removeIfExists(path.join(extensionsDir, entry.name));
71
+ }
72
+ }
73
+
74
+ removeIfExists(targetDir);
75
+ fs.cpSync(sourceDir, targetDir, {
76
+ recursive: true,
77
+ force: true,
78
+ preserveTimestamps: true,
79
+ });
80
+
81
+ process.stdout.write(
82
+ `[guardex-active-agents] Installed ${extensionId}@${manifest.version} to ${targetDir}\n` +
83
+ '[guardex-active-agents] Reload the VS Code window to activate the Source Control companion.\n',
84
+ );
85
+ }
86
+
87
+ try {
88
+ main();
89
+ } catch (error) {
90
+ process.stderr.write(`[guardex-active-agents] ${error.message}\n`);
91
+ process.exitCode = 1;
92
+ }
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- if [[ $# -lt 1 || $# -gt 2 ]]; then
5
- echo "Usage: $0 <change-slug> [capability-slug]"
6
- echo "Example: $0 add-dashboard-live-usage runtime-migration"
4
+ if [[ $# -lt 1 || $# -gt 3 ]]; then
5
+ echo "Usage: $0 <change-slug> [capability-slug] [agent-branch]"
6
+ echo "Example: $0 add-dashboard-live-usage runtime-migration agent/claude-odin/add-dashboard-live-usage-123456"
7
7
  exit 1
8
8
  fi
9
9
 
10
10
  CHANGE_SLUG="$1"
11
11
  CAPABILITY_SLUG="${2:-$CHANGE_SLUG}"
12
+ AGENT_BRANCH="${3:-agent/<your-name>/<branch-slug>}"
12
13
 
13
14
  if [[ "$CHANGE_SLUG" =~ [^a-z0-9-] ]]; then
14
15
  echo "Error: change slug must be kebab-case (lowercase letters, numbers, hyphens)."
@@ -20,11 +21,39 @@ if [[ "$CAPABILITY_SLUG" =~ [^a-z0-9-] ]]; then
20
21
  exit 1
21
22
  fi
22
23
 
24
+ resolve_base_branch() {
25
+ local branch="$1"
26
+ local base_branch=""
27
+
28
+ if [[ -n "$branch" ]] && [[ "$branch" != "agent/<your-name>/<branch-slug>" ]]; then
29
+ base_branch="$(git config --get "branch.${branch}.guardexBase" || true)"
30
+ fi
31
+ if [[ -z "$base_branch" ]]; then
32
+ base_branch="$(git config --get multiagent.baseBranch || true)"
33
+ fi
34
+ if [[ -z "$base_branch" ]]; then
35
+ base_branch="dev"
36
+ fi
37
+
38
+ printf '%s' "$base_branch"
39
+ }
40
+
23
41
  CHANGE_DIR="openspec/changes/${CHANGE_SLUG}"
24
42
  SPEC_DIR="${CHANGE_DIR}/specs/${CAPABILITY_SLUG}"
25
43
  TODAY="$(date -u +%Y-%m-%d)"
26
-
27
- mkdir -p "$SPEC_DIR"
44
+ BASE_BRANCH="$(resolve_base_branch "$AGENT_BRANCH")"
45
+
46
+ MINIMAL_RAW="${GUARDEX_OPENSPEC_MINIMAL:-0}"
47
+ case "$(printf '%s' "$MINIMAL_RAW" | tr '[:upper:]' '[:lower:]')" in
48
+ 1|true|yes|on) MINIMAL=1 ;;
49
+ *) MINIMAL=0 ;;
50
+ esac
51
+
52
+ if [[ "$MINIMAL" -eq 1 ]]; then
53
+ mkdir -p "$CHANGE_DIR"
54
+ else
55
+ mkdir -p "$SPEC_DIR"
56
+ fi
28
57
 
29
58
  if [[ ! -f "${CHANGE_DIR}/.openspec.yaml" ]]; then
30
59
  cat > "${CHANGE_DIR}/.openspec.yaml" <<YAMLEOF
@@ -33,6 +62,32 @@ created: ${TODAY}
33
62
  YAMLEOF
34
63
  fi
35
64
 
65
+ if [[ "$MINIMAL" -eq 1 ]]; then
66
+ if [[ ! -f "${CHANGE_DIR}/notes.md" ]]; then
67
+ cat > "${CHANGE_DIR}/notes.md" <<NOTESEOF
68
+ # ${CHANGE_SLUG} (minimal / T1)
69
+
70
+ Branch: \`${AGENT_BRANCH}\`
71
+
72
+ Describe the change in a sentence or two. Commit message is the spec of record.
73
+
74
+ ## Handoff
75
+
76
+ - Handoff: change=\`${CHANGE_SLUG}\`; branch=\`${AGENT_BRANCH}\`; scope=\`TODO\`; action=\`continue this sandbox or finish cleanup after a usage-limit/manual takeover\`.
77
+ - Copy prompt: Continue \`${CHANGE_SLUG}\` on branch \`${AGENT_BRANCH}\`. Work inside the existing sandbox, review \`openspec/changes/${CHANGE_SLUG}/notes.md\`, continue from the current state instead of creating a new sandbox, and when the work is done run \`gx branch finish --branch ${AGENT_BRANCH} --base ${BASE_BRANCH} --via-pr --wait-for-merge --cleanup\`.
78
+
79
+ ## Cleanup
80
+
81
+ - [ ] Run: \`gx branch finish --branch ${AGENT_BRANCH} --base ${BASE_BRANCH} --via-pr --wait-for-merge --cleanup\`
82
+ - [ ] Record PR URL + \`MERGED\` state in the completion handoff.
83
+ - [ ] Confirm sandbox worktree is gone (\`git worktree list\`, \`git branch -a\`).
84
+ NOTESEOF
85
+ fi
86
+ echo "[gitguardex] OpenSpec change workspace (minimal) ready: ${CHANGE_DIR}"
87
+ echo "[gitguardex] Notes-only scaffold: ${CHANGE_DIR}/notes.md"
88
+ exit 0
89
+ fi
90
+
36
91
  if [[ ! -f "${CHANGE_DIR}/proposal.md" ]]; then
37
92
  cat > "${CHANGE_DIR}/proposal.md" <<PROPOSALEOF
38
93
  ## Why
@@ -51,6 +106,19 @@ fi
51
106
 
52
107
  if [[ ! -f "${CHANGE_DIR}/tasks.md" ]]; then
53
108
  cat > "${CHANGE_DIR}/tasks.md" <<TASKSEOF
109
+ ## Definition of Done
110
+
111
+ This change is complete only when **all** of the following are true:
112
+
113
+ - Every checkbox below is checked.
114
+ - The agent branch reaches \`MERGED\` state on \`origin\` and the PR URL + state are recorded in the completion handoff.
115
+ - If any step blocks (test failure, conflict, ambiguous result), append a \`BLOCKED:\` line under section 4 explaining the blocker and **STOP**. Do not tick remaining cleanup boxes; do not silently skip the cleanup pipeline.
116
+
117
+ ## Handoff
118
+
119
+ - Handoff: change=\`${CHANGE_SLUG}\`; branch=\`${AGENT_BRANCH}\`; scope=\`TODO\`; action=\`continue this sandbox or finish cleanup after a usage-limit/manual takeover\`.
120
+ - Copy prompt: Continue \`${CHANGE_SLUG}\` on branch \`${AGENT_BRANCH}\`. Work inside the existing sandbox, review \`openspec/changes/${CHANGE_SLUG}/tasks.md\`, continue from the current state instead of creating a new sandbox, and when the work is done run \`gx branch finish --branch ${AGENT_BRANCH} --base ${BASE_BRANCH} --via-pr --wait-for-merge --cleanup\`.
121
+
54
122
  ## 1. Specification
55
123
 
56
124
  - [ ] 1.1 Finalize proposal scope and acceptance criteria for \`${CHANGE_SLUG}\`.
@@ -67,11 +135,11 @@ if [[ ! -f "${CHANGE_DIR}/tasks.md" ]]; then
67
135
  - [ ] 3.2 Run \`openspec validate ${CHANGE_SLUG} --type change --strict\`.
68
136
  - [ ] 3.3 Run \`openspec validate --specs\`.
69
137
 
70
- ## 4. Completion
138
+ ## 4. Cleanup (mandatory; run before claiming completion)
71
139
 
72
- - [ ] 4.1 Finish the agent branch via PR merge + cleanup (\`gx finish --via-pr --wait-for-merge --cleanup\` or \`bash scripts/agent-branch-finish.sh --branch <agent-branch> --base <base-branch> --via-pr --wait-for-merge --cleanup\`).
73
- - [ ] 4.2 Record PR URL + final \`MERGED\` state in the completion handoff.
74
- - [ ] 4.3 Confirm sandbox cleanup (\`git worktree list\`, \`git branch -a\`) or capture a \`BLOCKED:\` handoff if merge/cleanup is pending.
140
+ - [ ] 4.1 Run the cleanup pipeline: \`gx branch finish --branch ${AGENT_BRANCH} --base ${BASE_BRANCH} --via-pr --wait-for-merge --cleanup\`. This handles commit -> push -> PR create -> merge wait -> worktree prune in one invocation.
141
+ - [ ] 4.2 Record the PR URL and final merge state (\`MERGED\`) in the completion handoff.
142
+ - [ ] 4.3 Confirm the sandbox worktree is gone (\`git worktree list\` no longer shows the agent path; \`git branch -a\` shows no surviving local/remote refs for the branch).
75
143
  TASKSEOF
76
144
  fi
77
145