@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.9 → 0.1.12

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.
Files changed (41) hide show
  1. package/bin/adl +2 -2
  2. package/bin/git +1 -1
  3. package/bin/git-ado +2 -2
  4. package/bin/lib/bun-compile-build.sh +1 -1
  5. package/bin/lib/fleet-host.sh +59 -0
  6. package/bin/lib/lean-ctx-observe-hooks.sh +9 -9
  7. package/bin/reconcile +222 -249
  8. package/bin/reconcile-host +111 -0
  9. package/bin/setup +216 -0
  10. package/bin/sync-and-reconcile +52 -39
  11. package/bin/validate-manifest +15 -44
  12. package/package.json +1 -3
  13. package/packages/agent-hooks/package.json +1 -2
  14. package/packages/agent-hooks/src/hooks/crg/background.ts +179 -0
  15. package/packages/agent-hooks/src/hooks/crg/handler.ts +2 -1
  16. package/packages/agent-hooks/src/hooks/crg/index.ts +36 -5
  17. package/packages/agent-hooks/src/rules/impl/fs-trash.ts +2 -2
  18. package/packages/agent-hooks/src/rules/impl/git-paths.ts +5 -6
  19. package/packages/agent-hooks/src/rules/impl/tool-grep.ts +8 -8
  20. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +4 -2
  21. package/packages/agent-settings/src/global-surface-sweep.ts +5 -15
  22. package/packages/agent-skills/package.json +1 -1
  23. package/packages/azdo/package.json +1 -2
  24. package/packages/azdo/src/azdo-repo-doctor.ts +70 -75
  25. package/packages/fleet/manifest.json +5 -24
  26. package/packages/fleet/manifest.schema.json +8 -58
  27. package/packages/fleet/src/install-embedding-model.ts +1 -1
  28. package/packages/fleet/src/repo-maintain.ts +403 -0
  29. package/packages/fleet/src/scratch-reap.ts +1 -1
  30. package/packages/repo-controls/README.md +46 -8
  31. package/packages/repo-controls/assets/promote.ts +12 -7
  32. package/packages/repo-controls/assets/reconcile-guard.sh +113 -0
  33. package/packages/repo-controls/assets/version-bump.sh +91 -51
  34. package/packages/repo-controls/package.json +0 -1
  35. package/packages/repo-controls/src/azure-devops-governance.ts +17 -16
  36. package/packages/repo-controls/src/constants.ts +8 -0
  37. package/packages/repo-controls/src/hook.ts +3 -2
  38. package/packages/repo-controls/src/integrity.ts +15 -1
  39. package/packages/fleet/jobs/git-maintain/src/main.ts +0 -848
  40. package/packages/fleet/systemd/adl-azure-agent-update.service +0 -13
  41. package/packages/fleet/systemd/adl-azure-agent-update.timer +0 -10
package/bin/reconcile CHANGED
@@ -44,7 +44,7 @@ if [[ -z "${RECONCILE_REEXEC_BASH:-}" ]]; then
44
44
  export RECONCILE_REEXEC_BASH=1
45
45
  exec "$reconcile_newer_bash" "$0" "$@"
46
46
  fi
47
- echo "bin/reconcile requires bash >= 4.4 (this run is bash ${BASH_VERSION:-unknown} -- empty-array expansions are unsafe under set -u before 4.4). No newer bash was found at: $reconcile_bash_candidates. Install one (e.g. 'brew install bash') and re-run, or put a newer bash earlier on PATH -- refusing to start rather than risk an 'unbound variable' abort mid-run after state has already changed." >&2
47
+ echo "ERROR bin/reconcile requires bash >= 4.4 (this run is bash ${BASH_VERSION:-unknown} -- empty-array expansions are unsafe under set -u before 4.4). No newer bash was found at: $reconcile_bash_candidates. Install one (e.g. 'brew install bash') and re-run, or put a newer bash earlier on PATH -- refusing to start rather than risk an 'unbound variable' abort mid-run after state has already changed." >&2
48
48
  exit 1
49
49
  fi
50
50
  fi
@@ -54,7 +54,7 @@ set -euo pipefail
54
54
  export PATH="/usr/local/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/bin:/bin:/usr/sbin:/sbin"
55
55
 
56
56
  usage() {
57
- echo "usage: reconcile --device NAME [--source npm|checkout] [--mode audit|plan|apply] [--manifest PATH] [--state-dir PATH] [--platform mac|linux]" >&2
57
+ echo "ERROR usage: reconcile --device NAME [--source npm|checkout] [--mode audit|plan|apply] [--manifest PATH] [--state-dir PATH] [--platform mac|linux]" >&2
58
58
  exit 2
59
59
  }
60
60
 
@@ -68,8 +68,9 @@ source_mode="npm"
68
68
  # remove_one_repo_dir themselves, so packages/fleet/tests/test-manifest.sh
69
69
  # can point it at a disposable test_root directory (RECONCILE_SOURCE_ONLY=1)
70
70
  # and exercise the real sync/removal mechanics without root or a real
71
- # /usr/local. Matches packages/fleet/src/launch-agents.ts's
72
- # DEFAULT_LIBEXEC_DIR in production.
71
+ # /usr/local. The production value is the libexec dir the apps/cli
72
+ # commands (repo-controls, skills, hooks, settings) resolve the projected
73
+ # payload from.
73
74
  repo_dir_libexec_root="/usr/local/libexec/adl"
74
75
  # Test-only escape hatch for default-agent-file.ts / managed-settings.ts's own
75
76
  # `--root` (both already accept it; see their headers). Unset in every real
@@ -225,11 +226,11 @@ source "$script_dir/lib/bun-compile-build.sh"
225
226
  "$script_dir/validate-manifest" "$manifest" >/dev/null
226
227
  if [[ "${RECONCILE_SOURCE_ONLY:-0}" != "1" ]]; then
227
228
  if [[ "$source_mode" == npm && -e "$repo_root/.git" ]]; then
228
- echo "npm source mode requires an installed package root; use --source checkout for a Git checkout" >&2
229
+ echo "ERROR npm source mode requires an installed package root; use --source checkout for a Git checkout" >&2
229
230
  exit 2
230
231
  fi
231
232
  if [[ "$source_mode" == checkout && ! -e "$repo_root/.git" ]]; then
232
- echo "checkout source mode requires a Git checkout" >&2
233
+ echo "ERROR checkout source mode requires a Git checkout" >&2
233
234
  exit 2
234
235
  fi
235
236
  fi
@@ -237,7 +238,7 @@ fi
237
238
  case "$(uname -s)" in
238
239
  Darwin) platform=mac; sha256_cmd=(shasum -a 256) ;;
239
240
  Linux) platform=linux; sha256_cmd=(sha256sum) ;;
240
- *) echo "unsupported operating system: $(uname -s)" >&2; exit 1 ;;
241
+ *) echo "ERROR unsupported operating system: $(uname -s)" >&2; exit 1 ;;
241
242
  esac
242
243
  if [[ -n "$platform_override" ]]; then
243
244
  # Applying as a foreign platform would install the wrong platform's tools, so
@@ -248,7 +249,7 @@ if [[ -n "$platform_override" ]]; then
248
249
  # agents of the mixed CI pool it dies with "device m3 is not declared under
249
250
  # linux" (build 4226).
250
251
  if [[ "$mode" == apply && "${RECONCILE_SOURCE_ONLY:-0}" != "1" ]]; then
251
- echo "--platform cannot override the host in apply mode" >&2
252
+ echo "ERROR --platform cannot override the host in apply mode" >&2
252
253
  exit 2
253
254
  fi
254
255
  [[ "$platform_override" == mac || "$platform_override" == linux ]] || usage
@@ -256,7 +257,7 @@ if [[ -n "$platform_override" ]]; then
256
257
  fi
257
258
 
258
259
  jq -e --arg platform "$platform" --arg device "$device" '.[$platform].devices[$device]' "$manifest" >/dev/null || {
259
- echo "device $device is not declared under $platform" >&2
260
+ echo "ERROR device $device is not declared under $platform" >&2
260
261
  exit 1
261
262
  }
262
263
 
@@ -292,7 +293,7 @@ path_owner() {
292
293
  elif owner="$(stat -c '%U' "$path" 2>/dev/null)"; then
293
294
  printf '%s\n' "$owner"
294
295
  else
295
- echo "cannot determine owner of Homebrew prefix: $path" >&2
296
+ echo "ERROR cannot determine owner of Homebrew prefix: $path" >&2
296
297
  return 1
297
298
  fi
298
299
  }
@@ -305,7 +306,7 @@ resolve_brew_owner() {
305
306
  fi
306
307
  if [[ ! -d "$prefix" ]]; then
307
308
  if [[ "$target_mode" == apply ]]; then
308
- echo "Homebrew prefix missing: $prefix" >&2
309
+ echo "ERROR Homebrew prefix missing: $prefix" >&2
309
310
  return 1
310
311
  fi
311
312
  printf '%s\n' '<unresolved-linuxbrew-owner>'
@@ -313,7 +314,7 @@ resolve_brew_owner() {
313
314
  fi
314
315
  owner="$(path_owner "$prefix")" || return 1
315
316
  if [[ "$owner" == root ]]; then
316
- echo "Homebrew prefix must be owned by a non-root account: $prefix" >&2
317
+ echo "ERROR Homebrew prefix must be owned by a non-root account: $prefix" >&2
317
318
  return 1
318
319
  fi
319
320
  printf '%s\n' "$owner"
@@ -329,7 +330,7 @@ run_brew() {
329
330
  printf '\n'
330
331
  return 0
331
332
  fi
332
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
333
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
333
334
  (cd /tmp && sudo -u "$brew_owner" -H env NONINTERACTIVE=1 PATH="${brew_bin%/*}:/usr/bin:/bin" "$brew_bin" "$@" </dev/null)
334
335
  }
335
336
 
@@ -395,14 +396,14 @@ run_brew_upgrade_all() {
395
396
  echo "would run: brew upgrade (all outdated formulae and casks)"
396
397
  return 0
397
398
  fi
398
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
399
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
399
400
  local outdated_formulae outdated_casks formulae_count casks_count
400
401
  outdated_formulae="$(brew_query outdated --formula --quiet)" || {
401
- echo "brew outdated --formula check failed" >&2
402
+ echo "ERROR brew outdated --formula check failed" >&2
402
403
  return 1
403
404
  }
404
405
  outdated_casks="$(brew_query outdated --cask --quiet)" || {
405
- echo "brew outdated --cask check failed" >&2
406
+ echo "ERROR brew outdated --cask check failed" >&2
406
407
  return 1
407
408
  }
408
409
  formulae_count=0
@@ -422,10 +423,10 @@ run_brew_upgrade_all() {
422
423
  echo "brew upgrade: $formulae_count formulae, $casks_count casks outdated; upgrading (timeout ${BREW_UPGRADE_ALL_TIMEOUT_SECONDS}s)"
423
424
  run_with_timeout "$BREW_UPGRADE_ALL_TIMEOUT_SECONDS" run_brew upgrade 2>&1 || upgrade_status=$?
424
425
  if [[ "$upgrade_status" -eq 124 ]]; then
425
- echo "brew upgrade timed out after ${BREW_UPGRADE_ALL_TIMEOUT_SECONDS}s" >&2
426
+ echo "ERROR brew upgrade timed out after ${BREW_UPGRADE_ALL_TIMEOUT_SECONDS}s" >&2
426
427
  return 1
427
428
  elif [[ "$upgrade_status" -ne 0 ]]; then
428
- echo "brew upgrade failed (exit $upgrade_status)" >&2
429
+ echo "ERROR brew upgrade failed (exit $upgrade_status)" >&2
429
430
  return 1
430
431
  fi
431
432
  echo "brew upgrade: $formulae_count formulae, $casks_count casks were outdated before upgrade"
@@ -439,7 +440,7 @@ run_apt() {
439
440
  printf '\n'
440
441
  return 0
441
442
  fi
442
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
443
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
443
444
  env DEBIAN_FRONTEND=noninteractive apt-get "$@" </dev/null
444
445
  }
445
446
 
@@ -454,7 +455,7 @@ run_npm() {
454
455
  printf '\n'
455
456
  return 0
456
457
  fi
457
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
458
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
458
459
  if [[ "$platform" == linux ]]; then
459
460
  (cd /tmp && sudo -u "$administrator" -H env npm_config_prefix=/usr/local PATH="${brew_bin%/*}:/usr/bin:/bin" npm "$@" </dev/null)
460
461
  else
@@ -538,12 +539,12 @@ canonical_parent_path() {
538
539
  npm_package_owns_executable() {
539
540
  local package="$1" executable="$2" npm_root package_manifest bin_relative actual_path actual_link expected_target actual_target
540
541
  npm_root="$(npm_global_root)" || {
541
- echo "could not resolve npm global root for $package" >&2
542
+ echo "ERROR could not resolve npm global root for $package" >&2
542
543
  return 1
543
544
  }
544
545
  package_manifest="$npm_root/$package/package.json"
545
546
  [[ -f "$package_manifest" ]] || {
546
- echo "npm package metadata missing: $package_manifest" >&2
547
+ echo "ERROR npm package metadata missing: $package_manifest" >&2
547
548
  return 1
548
549
  }
549
550
  bin_relative="$(jq -r --arg executable "$executable" '
@@ -553,20 +554,20 @@ npm_package_owns_executable() {
553
554
  end
554
555
  ' "$package_manifest")"
555
556
  [[ -n "$bin_relative" && "$bin_relative" != /* && "$bin_relative" != .. && "$bin_relative" != ../* && "$bin_relative" != */../* && "$bin_relative" != */.. ]] || {
556
- echo "npm package $package does not declare a safe bin target for $executable" >&2
557
+ echo "ERROR npm package $package does not declare a safe bin target for $executable" >&2
557
558
  return 1
558
559
  }
559
560
  actual_path="$(npm_global_executable_path "$executable")" || {
560
- echo "npm executable missing from global PATH: $executable" >&2
561
+ echo "ERROR npm executable missing from global PATH: $executable" >&2
561
562
  return 1
562
563
  }
563
564
  [[ -L "$actual_path" ]] || {
564
- echo "npm executable is not an npm-managed symlink: $actual_path" >&2
565
+ echo "ERROR npm executable is not an npm-managed symlink: $actual_path" >&2
565
566
  return 1
566
567
  }
567
568
  expected_target="$(canonical_parent_path "$npm_root/$package/$bin_relative")" || return 1
568
569
  [[ -f "$expected_target" && -x "$expected_target" ]] || {
569
- echo "npm package $package bin target is not an executable file: $expected_target" >&2
570
+ echo "ERROR npm package $package bin target is not an executable file: $expected_target" >&2
570
571
  return 1
571
572
  }
572
573
  actual_link="$(readlink "$actual_path")" || return 1
@@ -576,7 +577,7 @@ npm_package_owns_executable() {
576
577
  actual_target="$(canonical_parent_path "${actual_path%/*}/$actual_link")" || return 1
577
578
  fi
578
579
  [[ "$actual_target" == "$expected_target" ]] || {
579
- echo "npm executable $executable is not owned by $package" >&2
580
+ echo "ERROR npm executable $executable is not owned by $package" >&2
580
581
  return 1
581
582
  }
582
583
  }
@@ -588,21 +589,21 @@ run_pip() {
588
589
  printf '\n'
589
590
  return 0
590
591
  fi
591
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
592
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
592
593
  (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" python3 -m pip "$@" </dev/null)
593
594
  }
594
595
 
595
596
  pip_user_executable() {
596
597
  local executable="$1"
597
598
  [[ "$executable" != */* ]] || {
598
- echo "pip executable must be a command name, not a path: $executable" >&2
599
+ echo "ERROR pip executable must be a command name, not a path: $executable" >&2
599
600
  exit 1
600
601
  }
601
602
  local user_base user_home
602
603
  user_home="$(cd /tmp && sudo -u "$administrator" -H /bin/sh -c 'printf "%s\n" "$HOME"')"
603
604
  user_base="$(cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" python3 -m site --user-base)"
604
605
  [[ "$user_base" == "$user_home" || "$user_base" == "$user_home"/* ]] || {
605
- echo "pip user base is outside the declared administrator home: $user_base" >&2
606
+ echo "ERROR pip user base is outside the declared administrator home: $user_base" >&2
606
607
  exit 1
607
608
  }
608
609
  printf '%s/bin/%s\n' "${user_base%/}" "$executable"
@@ -615,11 +616,11 @@ ensure_global_pip_executable() {
615
616
  echo "would expose pip executable globally: $target"
616
617
  return 0
617
618
  fi
618
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
619
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
619
620
  local source existing_source user_home
620
621
  source="$(pip_user_executable "$executable")"
621
622
  user_home="$(cd /tmp && sudo -u "$administrator" -H /bin/sh -c 'printf "%s\n" "$HOME"')"
622
- [[ -x "$source" ]] || { echo "pip executable missing after install: $source" >&2; exit 1; }
623
+ [[ -x "$source" ]] || { echo "ERROR pip executable missing after install: $source" >&2; exit 1; }
623
624
  if [[ -L "$target" ]]; then
624
625
  existing_source="$(readlink "$target")"
625
626
  if [[ "$existing_source" == "$source" ]]; then
@@ -628,10 +629,10 @@ ensure_global_pip_executable() {
628
629
  fi
629
630
  case "$existing_source" in
630
631
  "$user_home"/*/bin/"$executable") ;;
631
- *) echo "refusing to replace unrelated symlink: $target -> $existing_source" >&2; exit 1 ;;
632
+ *) echo "ERROR refusing to replace unrelated symlink: $target -> $existing_source" >&2; exit 1 ;;
632
633
  esac
633
634
  elif [[ -e "$target" ]]; then
634
- echo "refusing to replace unrelated global executable: $target" >&2
635
+ echo "ERROR refusing to replace unrelated global executable: $target" >&2
635
636
  exit 1
636
637
  fi
637
638
  ln -sfn "$source" "$target"
@@ -645,17 +646,17 @@ remove_global_pip_executable() {
645
646
  echo "would remove global pip executable: $target"
646
647
  return 0
647
648
  fi
648
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
649
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
649
650
  if [[ ! -e "$target" && ! -L "$target" ]]; then
650
651
  return 0
651
652
  fi
652
- [[ -L "$target" ]] || { echo "refusing to remove unrelated global executable: $target" >&2; exit 1; }
653
+ [[ -L "$target" ]] || { echo "ERROR refusing to remove unrelated global executable: $target" >&2; exit 1; }
653
654
  local source user_home
654
655
  source="$(readlink "$target")"
655
656
  user_home="$(cd /tmp && sudo -u "$administrator" -H /bin/sh -c 'printf "%s\n" "$HOME"')"
656
657
  case "$source" in
657
658
  "$user_home"/*/bin/"$executable") rm -f -- "$target" ;;
658
- *) echo "refusing to remove unrelated symlink: $target -> $source" >&2; exit 1 ;;
659
+ *) echo "ERROR refusing to remove unrelated symlink: $target -> $source" >&2; exit 1 ;;
659
660
  esac
660
661
  }
661
662
 
@@ -705,12 +706,12 @@ bun_source_hash() {
705
706
  local source_dir="$1" hash
706
707
  if repo_root_is_git; then
707
708
  hash="$(repo_git rev-parse --verify --quiet "HEAD:$source_dir")" || {
708
- echo "bun-compile source is not tracked at HEAD: $source_dir" >&2
709
+ echo "ERROR bun-compile source is not tracked at HEAD: $source_dir" >&2
709
710
  return 1
710
711
  }
711
712
  else
712
713
  [[ -d "$repo_root/$source_dir" ]] || {
713
- echo "bun-compile source missing from installed package: $repo_root/$source_dir" >&2
714
+ echo "ERROR bun-compile source missing from installed package: $repo_root/$source_dir" >&2
714
715
  return 1
715
716
  }
716
717
  hash="$(content_tree_hash "$repo_root/$source_dir")"
@@ -732,17 +733,17 @@ run_bun_compile() {
732
733
  printf 'would build and install bun binary: %s -> %s (source %s)\n' "$package" "$target" "$source_hash"
733
734
  return 0
734
735
  fi
735
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
736
- [[ "$target" == /usr/local/bin/* ]] || { echo "bun-compile target must be under /usr/local/bin: $target" >&2; exit 1; }
736
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
737
+ [[ "$target" == /usr/local/bin/* ]] || { echo "ERROR bun-compile target must be under /usr/local/bin: $target" >&2; exit 1; }
737
738
  [[ "$source_dir" != /* && "$source_dir" != *..* ]] || {
738
- echo "unsafe bun-compile source path: $source_dir" >&2
739
+ echo "ERROR unsafe bun-compile source path: $source_dir" >&2
739
740
  exit 1
740
741
  }
741
742
  local project_dir="$repo_root/$source_dir"
742
- [[ -d "$project_dir" ]] || { echo "bun-compile source missing: $project_dir" >&2; exit 1; }
743
+ [[ -d "$project_dir" ]] || { echo "ERROR bun-compile source missing: $project_dir" >&2; exit 1; }
743
744
  local bun_bin
744
745
  bun_bin="$(command -v bun || true)"
745
- [[ -n "$bun_bin" ]] || { echo "bun-compile requires bun on PATH; ensure bun installs before this tool" >&2; exit 1; }
746
+ [[ -n "$bun_bin" ]] || { echo "ERROR bun-compile requires bun on PATH; ensure bun installs before this tool" >&2; exit 1; }
746
747
  (cd "$project_dir" && "$bun_bin" install --frozen-lockfile)
747
748
  bun_compile_build "$bun_bin" "$project_dir/$entry" "$target"
748
749
  }
@@ -754,8 +755,8 @@ run_embedding_model() {
754
755
  printf 'would install embedding model: %s as %s -> ~/.cache/adl-embedding/model\n' "$package" "$administrator"
755
756
  return 0
756
757
  fi
757
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
758
- command -v bun >/dev/null 2>&1 || { echo "embedding-model requires bun; ensure bun installs before this tool" >&2; exit 1; }
758
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
759
+ command -v bun >/dev/null 2>&1 || { echo "ERROR embedding-model requires bun; ensure bun installs before this tool" >&2; exit 1; }
759
760
  (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
760
761
  bun "$repo_root/packages/fleet/src/install-embedding-model.ts" install --spec "$spec_json" </dev/null)
761
762
  }
@@ -782,9 +783,9 @@ install_surface_policy_file() {
782
783
  echo "would install required surface policy: $source -> $target"
783
784
  return 0
784
785
  fi
785
- [[ -f "$source" ]] || { echo "surface policy source missing: $source" >&2; return 1; }
786
+ [[ -f "$source" ]] || { echo "ERROR surface policy source missing: $source" >&2; return 1; }
786
787
  jq -e '(.deny.paths | arrays) and (.deny.nativeTools | arrays)' "$source" >/dev/null || {
787
- echo "surface policy source is invalid: $source" >&2
788
+ echo "ERROR surface policy source is invalid: $source" >&2
788
789
  return 1
789
790
  }
790
791
  mkdir -p /etc/adl
@@ -823,7 +824,7 @@ resolve_latest_release_tag() {
823
824
  local repo="$1"
824
825
  local location
825
826
  location="$(curl --proto '=https' --proto-redir '=https' -fsS -o /dev/null -w '%{redirect_url}' "https://github.com/$repo/releases/latest")"
826
- [[ -n "$location" ]] || { echo "could not resolve latest release tag for $repo" >&2; exit 1; }
827
+ [[ -n "$location" ]] || { echo "ERROR could not resolve latest release tag for $repo" >&2; exit 1; }
827
828
  basename "$location"
828
829
  }
829
830
 
@@ -854,10 +855,10 @@ run_release_archive_latest() {
854
855
  local checksums_asset="$5"
855
856
  local archive_executable="$6"
856
857
  local target="$7"
857
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
858
- [[ "$target" == /usr/local/bin/* ]] || { echo "release archive target must be under /usr/local/bin: $target" >&2; exit 1; }
858
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
859
+ [[ "$target" == /usr/local/bin/* ]] || { echo "ERROR release archive target must be under /usr/local/bin: $target" >&2; exit 1; }
859
860
  [[ "$archive_executable" != /* && "$archive_executable" != *..* ]] || {
860
- echo "unsafe archive executable path: $archive_executable" >&2
861
+ echo "ERROR unsafe archive executable path: $archive_executable" >&2
861
862
  exit 1
862
863
  }
863
864
 
@@ -872,7 +873,7 @@ run_release_archive_latest() {
872
873
  curl --proto '=https' --proto-redir '=https' -fsSL --retry 3 \
873
874
  -o "$checksums_file" "https://github.com/$repo/releases/download/$tag/$checksums_asset"
874
875
  expected_sha256="$(awk -v want="$asset" '$2 == want { print $1; found = 1 } END { exit !found }' "$checksums_file")" || {
875
- echo "no published checksum for $asset in $repo's $checksums_asset -- refusing to install unverified bytes" >&2
876
+ echo "ERROR no published checksum for $asset in $repo's $checksums_asset -- refusing to install unverified bytes" >&2
876
877
  exit 1
877
878
  }
878
879
  curl --proto '=https' --proto-redir '=https' -fsSL --retry 3 \
@@ -880,15 +881,15 @@ run_release_archive_latest() {
880
881
  case "$(uname -s)" in
881
882
  Darwin) actual_sha256="$(shasum -a 256 "$archive_file" | awk '{print $1}')" ;;
882
883
  Linux) actual_sha256="$(sha256sum "$archive_file" | awk '{print $1}')" ;;
883
- *) echo "unsupported operating system for release archive" >&2; exit 1 ;;
884
+ *) echo "ERROR unsupported operating system for release archive" >&2; exit 1 ;;
884
885
  esac
885
886
  [[ "$actual_sha256" == "$expected_sha256" ]] || {
886
- echo "checksum mismatch for $package (latest): expected $expected_sha256 from published $checksums_asset, got $actual_sha256" >&2
887
+ echo "ERROR checksum mismatch for $package (latest): expected $expected_sha256 from published $checksums_asset, got $actual_sha256" >&2
887
888
  exit 1
888
889
  }
889
890
  tar -xzf "$archive_file" -C "$extract_dir" "$archive_executable"
890
891
  [[ -f "$extract_dir/$archive_executable" ]] || {
891
- echo "archive executable missing: $archive_executable" >&2
892
+ echo "ERROR archive executable missing: $archive_executable" >&2
892
893
  exit 1
893
894
  }
894
895
  atomic_install_executable "$extract_dir/$archive_executable" "$target"
@@ -934,7 +935,7 @@ install_one_tool() {
934
935
  echo "ensure Adl from explicit checkout source: $repo_root"
935
936
  build_checkout_adl || rc=1
936
937
  if [[ "$mode" == apply && "$rc" -eq 0 ]]; then
937
- command -v "$executable" >/dev/null 2>&1 || { echo "checkout Adl executable missing from PATH: $executable" >&2; rc=1; }
938
+ command -v "$executable" >/dev/null 2>&1 || { echo "ERROR checkout Adl executable missing from PATH: $executable" >&2; rc=1; }
938
939
  fi
939
940
  return "$rc"
940
941
  fi
@@ -971,7 +972,7 @@ install_one_tool() {
971
972
  run_brew upgrade "$package" || rc=1
972
973
  fi
973
974
  else
974
- echo "$tool_id: brew outdated check failed for $package" >&2
975
+ echo "ERROR $tool_id: brew outdated check failed for $package" >&2
975
976
  rc=1
976
977
  fi
977
978
  else
@@ -1012,12 +1013,12 @@ install_one_tool() {
1012
1013
  prepare_in_place_bun_to_npm_migration "$tool_id" || rc=1
1013
1014
  fi
1014
1015
  if [[ "$rc" -eq 0 ]] && ! npm_target_version="$(npm_latest_version "$package")"; then
1015
- echo "$tool_id: npm view could not resolve the latest version of $package" >&2
1016
+ echo "ERROR $tool_id: npm view could not resolve the latest version of $package" >&2
1016
1017
  rc=1
1017
1018
  fi
1018
1019
  if [[ "$rc" -eq 0 ]]; then
1019
1020
  if ! npm_installed_ver="$(npm_installed_version "$package")"; then
1020
- echo "$tool_id: npm ls -g could not inspect the global install of $package" >&2
1021
+ echo "ERROR $tool_id: npm ls -g could not inspect the global install of $package" >&2
1021
1022
  rc=1
1022
1023
  elif [[ -n "$npm_installed_ver" && "$npm_installed_ver" == "$npm_target_version" ]]; then
1023
1024
  echo "already latest: $tool_id (npm:$package@$npm_installed_ver)"
@@ -1068,15 +1069,15 @@ install_one_tool() {
1068
1069
  fi
1069
1070
  fi
1070
1071
  ;;
1071
- *) echo "unsupported manager: $manager" >&2; rc=1 ;;
1072
+ *) echo "ERROR unsupported manager: $manager" >&2; rc=1 ;;
1072
1073
  esac
1073
1074
  if [[ "$mode" == apply && "$rc" -eq 0 ]]; then
1074
1075
  if [[ "$executable" == /* ]]; then
1075
- [[ -x "$executable" ]] || { echo "$tool_id executable missing: $executable" >&2; rc=1; }
1076
+ [[ -x "$executable" ]] || { echo "ERROR $tool_id executable missing: $executable" >&2; rc=1; }
1076
1077
  else
1077
1078
  local executable_path
1078
1079
  executable_path="$(sudo -u "$administrator" -H env PATH="/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin" /bin/sh -c "command -v '$executable'" 2>/dev/null || true)"
1079
- [[ -n "$executable_path" ]] || { echo "$tool_id executable missing from global PATH: $executable" >&2; rc=1; }
1080
+ [[ -n "$executable_path" ]] || { echo "ERROR $tool_id executable missing from global PATH: $executable" >&2; rc=1; }
1080
1081
  fi
1081
1082
  fi
1082
1083
  return "$rc"
@@ -1102,11 +1103,11 @@ is_in_place_bun_to_npm_migration() {
1102
1103
  remove_retired_bun_compilation() {
1103
1104
  local tool_id="$1" executable="$2" hash_file
1104
1105
  [[ "$tool_id" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || {
1105
- echo "unsafe retired Bun compilation tool id: $tool_id" >&2
1106
+ echo "ERROR unsafe retired Bun compilation tool id: $tool_id" >&2
1106
1107
  return 1
1107
1108
  }
1108
1109
  [[ "$executable" =~ ^/usr/local/bin/[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || {
1109
- echo "refusing unsafe retired Bun binary removal target: $executable" >&2
1110
+ echo "ERROR refusing unsafe retired Bun binary removal target: $executable" >&2
1110
1111
  return 1
1111
1112
  }
1112
1113
  hash_file="$(bun_source_hash_file "$tool_id")"
@@ -1129,7 +1130,7 @@ prepare_in_place_bun_to_npm_migration() {
1129
1130
  package="$(jq -r --arg id "$tool_id" '.[$id].package' "$resolved_file")"
1130
1131
  executable="$(jq -r --arg id "$tool_id" '.[$id].executable' "$resolved_file")"
1131
1132
  npm_package_owns_executable "$package" "$executable" || {
1132
- echo "refusing to remove an executable symlink not owned by npm: $previous_executable" >&2
1133
+ echo "ERROR refusing to remove an executable symlink not owned by npm: $previous_executable" >&2
1133
1134
  return 1
1134
1135
  }
1135
1136
  hash_file="$(bun_source_hash_file "$tool_id")"
@@ -1144,7 +1145,7 @@ failed_in_place_npm_migration() {
1144
1145
  local tool_id
1145
1146
  for tool_id in ${failed_tools[@]+"${failed_tools[@]}"}; do
1146
1147
  if is_in_place_bun_to_npm_migration "$tool_id"; then
1147
- echo "global-tools: npm migration failed for $tool_id; retired Bun command was removed and reconciliation must stop" >&2
1148
+ echo "ERROR global-tools: npm migration failed for $tool_id; retired Bun command was removed and reconciliation must stop" >&2
1148
1149
  return 0
1149
1150
  fi
1150
1151
  done
@@ -1181,7 +1182,7 @@ build_one_bun_tool() {
1181
1182
  fi
1182
1183
  run_bun_compile "$package" "$source_hash" "$source_dir" "$entry" "$executable" || rc=1
1183
1184
  if [[ "$mode" == apply && "$rc" -eq 0 ]]; then
1184
- [[ -x "$executable" ]] || { echo "$tool_id executable missing: $executable" >&2; rc=1; }
1185
+ [[ -x "$executable" ]] || { echo "ERROR $tool_id executable missing: $executable" >&2; rc=1; }
1185
1186
  fi
1186
1187
  if [[ "$mode" == apply && "$rc" -eq 0 ]]; then
1187
1188
  mkdir -p "${hash_file%/*}"
@@ -1202,7 +1203,7 @@ build_checkout_adl() {
1202
1203
  fi
1203
1204
  run_bun_compile adl "$source_hash" "$source_dir" "$entry" "$target" || rc=1
1204
1205
  if [[ "$mode" == apply && "$rc" -eq 0 ]]; then
1205
- [[ -x "$target" ]] || { echo "checkout Adl executable missing: $target" >&2; rc=1; }
1206
+ [[ -x "$target" ]] || { echo "ERROR checkout Adl executable missing: $target" >&2; rc=1; }
1206
1207
  if [[ "$rc" -eq 0 ]]; then
1207
1208
  mkdir -p "${hash_file%/*}"
1208
1209
  printf '%s\n' "$source_hash" >"$hash_file.new"
@@ -1243,16 +1244,16 @@ write_one_shim() {
1243
1244
  executable="$(jq -r --arg id "$tool_id" '.[$id].executable' "$resolved_file")"
1244
1245
  echo "ensure shim: $tool_id (shim:$package -> $executable)"
1245
1246
  [[ "$executable" == /usr/local/bin/* ]] || {
1246
- echo "refusing unsafe shim target: $executable" >&2
1247
+ echo "ERROR refusing unsafe shim target: $executable" >&2
1247
1248
  return 1
1248
1249
  }
1249
1250
  if [[ "$mode" != apply ]]; then
1250
1251
  echo "would write shim: $executable -> $package"
1251
1252
  return 0
1252
1253
  fi
1253
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
1254
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
1254
1255
  target_path="$(sudo -u "$administrator" -H env PATH="/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin" /bin/sh -c "command -v '$package'" 2>/dev/null || true)"
1255
- [[ -n "$target_path" ]] || { echo "shim target not yet on PATH: $package" >&2; return 1; }
1256
+ [[ -n "$target_path" ]] || { echo "ERROR shim target not yet on PATH: $package" >&2; return 1; }
1256
1257
  content="#!/bin/sh
1257
1258
  # Generated by global-tools -- do not edit.
1258
1259
  exec '$target_path' \"\$@\"
@@ -1270,7 +1271,7 @@ exec '$target_path' \"\$@\"
1270
1271
  alias_name="$(basename "$executable")"
1271
1272
  resolved_alias="$(sudo -u "$administrator" -H env PATH="/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin" /bin/sh -c "command -v '$alias_name'" 2>/dev/null || true)"
1272
1273
  [[ "$resolved_alias" == "$executable" ]] || {
1273
- echo "$tool_id shim written but not resolving from global PATH as '$alias_name' (got: ${resolved_alias:-nothing})" >&2
1274
+ echo "ERROR $tool_id shim written but not resolving from global PATH as '$alias_name' (got: ${resolved_alias:-nothing})" >&2
1274
1275
  rc=1
1275
1276
  }
1276
1277
  return "$rc"
@@ -1316,16 +1317,16 @@ write_one_repo_script() {
1316
1317
  source="$(jq -r --arg id "$tool_id" '.[$id].source' "$resolved_file")"
1317
1318
  echo "ensure managed repo script: $tool_id (repo-script:$package -> $executable)"
1318
1319
  [[ "$executable" =~ ^/usr/local/bin/[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || {
1319
- echo "unsafe managed script target: $executable" >&2
1320
+ echo "ERROR unsafe managed script target: $executable" >&2
1320
1321
  return 1
1321
1322
  }
1322
1323
  [[ "$source" != /* && "$source" != *..* ]] || {
1323
- echo "unsafe managed script source: $source" >&2
1324
+ echo "ERROR unsafe managed script source: $source" >&2
1324
1325
  return 1
1325
1326
  }
1326
1327
  source_path="$repo_root/$source"
1327
1328
  [[ -f "$source_path" && ! -L "$source_path" && -x "$source_path" ]] || {
1328
- echo "managed script source must be an executable regular non-symlink file: $source_path" >&2
1329
+ echo "ERROR managed script source must be an executable regular non-symlink file: $source_path" >&2
1329
1330
  return 1
1330
1331
  }
1331
1332
  if [[ "$mode" != apply ]]; then
@@ -1339,10 +1340,10 @@ write_one_repo_script() {
1339
1340
  fi
1340
1341
  return 0
1341
1342
  fi
1342
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
1343
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
1343
1344
  if [[ -e "$executable" || -L "$executable" ]]; then
1344
1345
  is_managed_repo_script "$executable" || {
1345
- echo "refusing to replace unmanaged or symlinked managed script target: $executable" >&2
1346
+ echo "ERROR refusing to replace unmanaged or symlinked managed script target: $executable" >&2
1346
1347
  return 1
1347
1348
  }
1348
1349
  fi
@@ -1361,7 +1362,7 @@ write_one_repo_script() {
1361
1362
  echo "backed up previous managed repo script to $repo_script_backup"
1362
1363
  record_backup "$executable" "$repo_script_backup"
1363
1364
  else
1364
- echo "could not back up managed repo script before replacing it: $executable" >&2
1365
+ echo "ERROR could not back up managed repo script before replacing it: $executable" >&2
1365
1366
  return 1
1366
1367
  fi
1367
1368
  fi
@@ -1371,7 +1372,7 @@ write_one_repo_script() {
1371
1372
  alias_name="$(basename "$executable")"
1372
1373
  resolved_alias="$(sudo -u "$administrator" -H env PATH="/usr/local/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/bin:/bin" /bin/sh -c 'command -v "$1"' sh "$alias_name" 2>/dev/null || true)"
1373
1374
  [[ "$resolved_alias" == "$executable" ]] || {
1374
- echo "$tool_id managed script installed but not resolving from global PATH as '$alias_name' (got: ${resolved_alias:-nothing})" >&2
1375
+ echo "ERROR $tool_id managed script installed but not resolving from global PATH as '$alias_name' (got: ${resolved_alias:-nothing})" >&2
1375
1376
  rc=1
1376
1377
  }
1377
1378
  return "$rc"
@@ -1380,14 +1381,14 @@ write_one_repo_script() {
1380
1381
  remove_one_repo_script() {
1381
1382
  local executable="$1"
1382
1383
  [[ "$executable" =~ ^/usr/local/bin/[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || {
1383
- echo "refusing unsafe managed script removal target: $executable" >&2
1384
+ echo "ERROR refusing unsafe managed script removal target: $executable" >&2
1384
1385
  return 1
1385
1386
  }
1386
1387
  if [[ ! -e "$executable" && ! -L "$executable" ]]; then
1387
1388
  return 0
1388
1389
  fi
1389
1390
  is_managed_repo_script "$executable" || {
1390
- echo "refusing to remove unmanaged or symlinked managed script target: $executable" >&2
1391
+ echo "ERROR refusing to remove unmanaged or symlinked managed script target: $executable" >&2
1391
1392
  return 1
1392
1393
  }
1393
1394
  rm -f -- "$executable"
@@ -1400,7 +1401,7 @@ repo_dir_source_hash() {
1400
1401
  local source_dir="$1" hash
1401
1402
  if repo_root_is_git; then
1402
1403
  hash="$(repo_git rev-parse --verify --quiet "HEAD:$source_dir")" || {
1403
- echo "repo-dir source is not tracked at HEAD: $source_dir" >&2
1404
+ echo "ERROR repo-dir source is not tracked at HEAD: $source_dir" >&2
1404
1405
  return 1
1405
1406
  }
1406
1407
  else
@@ -1452,7 +1453,7 @@ managed_repo_dir_marker='.adl-managed-repo-dir'
1452
1453
  # have to provide.
1453
1454
  run_repo_dir_sync() {
1454
1455
  local tool_id="$1" source="$2" build_dir="$3" npm_install="$4" bun_bin
1455
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
1456
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
1456
1457
  if repo_root_is_git; then
1457
1458
  repo_git archive "HEAD:$source" | tar -x -C "$build_dir"
1458
1459
  else
@@ -1463,8 +1464,8 @@ run_repo_dir_sync() {
1463
1464
  fi
1464
1465
  if [[ "$npm_install" == true ]]; then
1465
1466
  bun_bin="$(command -v bun || true)"
1466
- [[ -n "$bun_bin" ]] || { echo "repo-dir $tool_id requires bun on PATH for npmInstall; ensure bun installs before this tool" >&2; return 1; }
1467
- (cd "$build_dir" && "$bun_bin" install) || { echo "repo-dir $tool_id: bun install failed" >&2; return 1; }
1467
+ [[ -n "$bun_bin" ]] || { echo "ERROR repo-dir $tool_id requires bun on PATH for npmInstall; ensure bun installs before this tool" >&2; return 1; }
1468
+ (cd "$build_dir" && "$bun_bin" install) || { echo "ERROR repo-dir $tool_id: bun install failed" >&2; return 1; }
1468
1469
  # agent-skills (the only npmInstall:true source today) ships no `dist/`
1469
1470
  # in git -- every one of its script entry points imports from
1470
1471
  # `../../dist/index.js` (confirmed empirically, all four: run-sync,
@@ -1474,7 +1475,7 @@ run_repo_dir_sync() {
1474
1475
  # the package's own declared `build` script, exactly as a developer
1475
1476
  # would, whenever the synced package.json has one.
1476
1477
  if [[ "$(jq -r '.scripts.build // empty' "$build_dir/package.json" 2>/dev/null)" != "" ]]; then
1477
- (cd "$build_dir" && "$bun_bin" run build) || { echo "repo-dir $tool_id: bun run build failed" >&2; return 1; }
1478
+ (cd "$build_dir" && "$bun_bin" run build) || { echo "ERROR repo-dir $tool_id: bun run build failed" >&2; return 1; }
1478
1479
  fi
1479
1480
  fi
1480
1481
  }
@@ -1535,16 +1536,16 @@ write_one_repo_dir() {
1535
1536
  npm_install="$(jq -r --arg id "$tool_id" '.[$id].npmInstall // false' "$resolved_file")"
1536
1537
  echo "ensure repo dir: $tool_id (repo-dir:$package -> $target)"
1537
1538
  [[ "$target" == "$repo_dir_libexec_root"/* ]] || {
1538
- echo "unsafe repo-dir target: $target" >&2
1539
+ echo "ERROR unsafe repo-dir target: $target" >&2
1539
1540
  return 1
1540
1541
  }
1541
1542
  [[ "$source" != /* && "$source" != *..* ]] || {
1542
- echo "unsafe repo-dir source: $source" >&2
1543
+ echo "ERROR unsafe repo-dir source: $source" >&2
1543
1544
  return 1
1544
1545
  }
1545
1546
  source_path="$repo_root/$source"
1546
1547
  [[ -d "$source_path" ]] || {
1547
- echo "repo-dir source missing: $source_path" >&2
1548
+ echo "ERROR repo-dir source missing: $source_path" >&2
1548
1549
  return 1
1549
1550
  }
1550
1551
  source_hash="$(repo_dir_source_hash "$source")" || return 1
@@ -1588,7 +1589,7 @@ write_one_repo_dir() {
1588
1589
  fi
1589
1590
  fi
1590
1591
  [[ -e "$target/$executable" ]] || {
1591
- echo "$tool_id repo dir installed but entry missing: $target/$executable" >&2
1592
+ echo "ERROR $tool_id repo dir installed but entry missing: $target/$executable" >&2
1592
1593
  rc=1
1593
1594
  }
1594
1595
  return "$rc"
@@ -1597,12 +1598,12 @@ write_one_repo_dir() {
1597
1598
  remove_one_repo_dir() {
1598
1599
  local target="$1"
1599
1600
  [[ "$target" == "$repo_dir_libexec_root"/* ]] || {
1600
- echo "refusing unsafe repo-dir removal target: $target" >&2
1601
+ echo "ERROR refusing unsafe repo-dir removal target: $target" >&2
1601
1602
  return 1
1602
1603
  }
1603
1604
  [[ -e "$target" ]] || return 0
1604
1605
  [[ -f "$target/$managed_repo_dir_marker" ]] || {
1605
- echo "refusing to remove unmanaged repo dir: $target" >&2
1606
+ echo "ERROR refusing to remove unmanaged repo dir: $target" >&2
1606
1607
  return 1
1607
1608
  }
1608
1609
  rm -rf -- "$target"
@@ -1683,7 +1684,7 @@ executable_claimed_by_current_manifest() {
1683
1684
  remove_one_manifest_entry() {
1684
1685
  local tool_id="$1" manager package executable removed_repo_dir_target
1685
1686
  if jq -e --arg id "$tool_id" '.config.protected_tools | index($id)' "$manifest" >/dev/null; then
1686
- echo "refusing to remove protected bootstrap tool: $tool_id" >&2
1687
+ echo "ERROR refusing to remove protected bootstrap tool: $tool_id" >&2
1687
1688
  exit 1
1688
1689
  fi
1689
1690
  manager="$(jq -r --arg id "$tool_id" '.[$id].manager' "$previous_resolved_file")"
@@ -1735,7 +1736,7 @@ remove_one_manifest_entry() {
1735
1736
  # state is read straight from disk, never re-validated.
1736
1737
  release-archive)
1737
1738
  [[ "$executable" == /usr/local/bin/* ]] || {
1738
- echo "refusing unsafe release archive removal target: $executable" >&2
1739
+ echo "ERROR refusing unsafe release archive removal target: $executable" >&2
1739
1740
  exit 1
1740
1741
  }
1741
1742
  if [[ "$mode" == apply ]]; then
@@ -1746,7 +1747,7 @@ remove_one_manifest_entry() {
1746
1747
  ;;
1747
1748
  release-archive-latest)
1748
1749
  [[ "$executable" == /usr/local/bin/* ]] || {
1749
- echo "refusing unsafe release archive removal target: $executable" >&2
1750
+ echo "ERROR refusing unsafe release archive removal target: $executable" >&2
1750
1751
  exit 1
1751
1752
  }
1752
1753
  if [[ "$mode" == apply ]]; then
@@ -1757,7 +1758,7 @@ remove_one_manifest_entry() {
1757
1758
  ;;
1758
1759
  shim)
1759
1760
  [[ "$executable" == /usr/local/bin/* ]] || {
1760
- echo "refusing unsafe shim removal target: $executable" >&2
1761
+ echo "ERROR refusing unsafe shim removal target: $executable" >&2
1761
1762
  exit 1
1762
1763
  }
1763
1764
  if [[ "$mode" == apply ]]; then
@@ -1771,7 +1772,7 @@ remove_one_manifest_entry() {
1771
1772
  remove_one_repo_script "$executable"
1772
1773
  else
1773
1774
  [[ "$executable" =~ ^/usr/local/bin/[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || {
1774
- echo "refusing unsafe managed script removal target: $executable" >&2
1775
+ echo "ERROR refusing unsafe managed script removal target: $executable" >&2
1775
1776
  exit 1
1776
1777
  }
1777
1778
  echo "would remove managed repo script: $executable"
@@ -1783,7 +1784,7 @@ remove_one_manifest_entry() {
1783
1784
  remove_one_repo_dir "$removed_repo_dir_target"
1784
1785
  else
1785
1786
  [[ "$removed_repo_dir_target" == "$repo_dir_libexec_root"/* ]] || {
1786
- echo "refusing unsafe repo-dir removal target: $removed_repo_dir_target" >&2
1787
+ echo "ERROR refusing unsafe repo-dir removal target: $removed_repo_dir_target" >&2
1787
1788
  exit 1
1788
1789
  }
1789
1790
  echo "would remove repo dir: $removed_repo_dir_target"
@@ -1791,7 +1792,7 @@ remove_one_manifest_entry() {
1791
1792
  ;;
1792
1793
  bun-compile)
1793
1794
  [[ "$executable" == /usr/local/bin/* ]] || {
1794
- echo "refusing unsafe bun binary removal target: $executable" >&2
1795
+ echo "ERROR refusing unsafe bun binary removal target: $executable" >&2
1795
1796
  exit 1
1796
1797
  }
1797
1798
  if [[ "$mode" == apply ]]; then
@@ -1802,15 +1803,15 @@ remove_one_manifest_entry() {
1802
1803
  ;;
1803
1804
  embedding-model)
1804
1805
  if [[ "$mode" == apply ]]; then
1805
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
1806
- command -v bun >/dev/null 2>&1 || { echo "embedding-model requires bun; ensure bun installs before this tool" >&2; exit 1; }
1806
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
1807
+ command -v bun >/dev/null 2>&1 || { echo "ERROR embedding-model requires bun; ensure bun installs before this tool" >&2; exit 1; }
1807
1808
  (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
1808
1809
  bun "$repo_root/packages/fleet/src/install-embedding-model.ts" uninstall </dev/null)
1809
1810
  else
1810
1811
  echo "would remove embedding model cache: ~/.cache/adl-embedding/model (as $administrator)"
1811
1812
  fi
1812
1813
  ;;
1813
- *) echo "unsupported manager in previous manifest: $manager" >&2; exit 1 ;;
1814
+ *) echo "ERROR unsupported manager in previous manifest: $manager" >&2; exit 1 ;;
1814
1815
  esac
1815
1816
  }
1816
1817
 
@@ -1823,7 +1824,7 @@ remove_one_manifest_entry() {
1823
1824
  # the LFS pre-push hook.
1824
1825
  run_git_config_global() {
1825
1826
  if [[ "$mode" == apply ]]; then
1826
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
1827
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
1827
1828
  (cd /tmp && sudo -u "$git_config_user" -H git config --global "$@" </dev/null)
1828
1829
  else
1829
1830
  git config --global "$@" </dev/null
@@ -1848,7 +1849,7 @@ cleanup_legacy_linux_git_config() {
1848
1849
  legacy_home="$(jq -r --arg device "$device" '.linux.devices[$device].brew_user_home // empty' "$applied_manifest")"
1849
1850
  [[ -n "$legacy_user" && -n "$legacy_home" ]] || return 0
1850
1851
  [[ "$legacy_home" == /* && "$legacy_home" != / ]] || {
1851
- echo "invalid legacy per-user home in applied manifest: $legacy_home" >&2
1852
+ echo "ERROR invalid legacy per-user home in applied manifest: $legacy_home" >&2
1852
1853
  return 1
1853
1854
  }
1854
1855
  config_file="$legacy_home/.gitconfig"
@@ -1862,7 +1863,7 @@ cleanup_legacy_linux_git_config() {
1862
1863
  continue
1863
1864
  fi
1864
1865
  [[ "$rc" -eq 0 ]] || {
1865
- echo "failed to inspect legacy global git config: $key in $config_file (exit $rc)" >&2
1866
+ echo "ERROR failed to inspect legacy global git config: $key in $config_file (exit $rc)" >&2
1866
1867
  return "$rc"
1867
1868
  }
1868
1869
  if [[ "$mode" != apply ]]; then
@@ -1882,7 +1883,7 @@ git_global_config_value() {
1882
1883
  if [[ "$rc" -eq 1 ]]; then
1883
1884
  return 0
1884
1885
  fi
1885
- [[ "$rc" -eq 0 ]] || { echo "git config --global --get $key failed (exit $rc)" >&2; return "$rc"; }
1886
+ [[ "$rc" -eq 0 ]] || { echo "ERROR git config --global --get $key failed (exit $rc)" >&2; return "$rc"; }
1886
1887
  printf '%s\n' "$value"
1887
1888
  }
1888
1889
 
@@ -1931,7 +1932,7 @@ reconcile_git_global_config() {
1931
1932
  # "bun: command not found" abort with no explanation.
1932
1933
  require_bun_for_identity_files() {
1933
1934
  command -v bun >/dev/null 2>&1 || {
1934
- echo "global-tools: bun is required before default-agent-file/managed-settings; install bun first, then re-run reconcile" >&2
1935
+ echo "ERROR global-tools: bun is required before default-agent-file/managed-settings; install bun first, then re-run reconcile" >&2
1935
1936
  return 1
1936
1937
  }
1937
1938
  }
@@ -2032,12 +2033,12 @@ reconcile_one_surface_cli() {
2032
2033
  echo "would ensure Homebrew cask: $name (brew install --cask $name; brew upgrade --cask $name)"
2033
2034
  return 0
2034
2035
  fi
2035
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
2036
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
2036
2037
  local installed_casks query_status=0 cask_action=() update_output update_status=0
2037
2038
  installed_casks="$(brew_query list --cask --versions 2>&1)" || query_status=$?
2038
2039
  if [[ "$query_status" -ne 0 ]]; then
2039
- printf '%s\n' "$installed_casks" >&2
2040
- echo "brew list --cask --versions failed for surface $name (exit $query_status)" >&2
2040
+ printf 'ERROR brew list --cask --versions output: %s\n' "$installed_casks" >&2
2041
+ echo "ERROR brew list --cask --versions failed for surface $name (exit $query_status)" >&2
2041
2042
  return 1
2042
2043
  fi
2043
2044
  if awk -v cask="$name" '$1 == cask { found = 1 } END { exit !found }' <<<"$installed_casks"; then
@@ -2048,10 +2049,10 @@ reconcile_one_surface_cli() {
2048
2049
  update_output="$(run_with_timeout "$SURFACE_UPDATE_TIMEOUT_SECONDS" run_brew ${cask_action[@]+"${cask_action[@]}"} 2>&1)" || update_status=$?
2049
2050
  printf '%s\n' "$update_output"
2050
2051
  if [[ "$update_status" -eq 124 ]]; then
2051
- echo "surface update timed out: $name" >&2
2052
+ echo "ERROR surface update timed out: $name" >&2
2052
2053
  return 1
2053
2054
  elif [[ "$update_status" -ne 0 ]]; then
2054
- echo "surface update failed: $name (exit $update_status)" >&2
2055
+ echo "ERROR surface update failed: $name (exit $update_status)" >&2
2055
2056
  return 1
2056
2057
  fi
2057
2058
  # Same broadened check as the native/npm path below (independent
@@ -2068,9 +2069,9 @@ reconcile_one_surface_cli() {
2068
2069
  # coordinator pass, 2026-09-30): the same `cask_action[0]` that already
2069
2070
  # picks the real brew argv above also names which one failed.
2070
2071
  if [[ "${cask_action[0]}" == install ]]; then
2071
- echo "surface $name: cask install failed ($update_error_line)" >&2
2072
+ echo "ERROR surface $name: cask install failed ($update_error_line)" >&2
2072
2073
  else
2073
- echo "surface $name: cask upgrade failed ($update_error_line)" >&2
2074
+ echo "ERROR surface $name: cask upgrade failed ($update_error_line)" >&2
2074
2075
  fi
2075
2076
  return 1
2076
2077
  fi
@@ -2089,7 +2090,7 @@ reconcile_one_surface_cli() {
2089
2090
  fi
2090
2091
  return 0
2091
2092
  fi
2092
- [[ "$(id -u)" -eq 0 ]] || { echo "apply mode must run as root" >&2; exit 1; }
2093
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
2093
2094
  local resolved
2094
2095
  resolved="$(resolve_surface_executable "$command_name" "$executable_override")"
2095
2096
  if [[ -z "$resolved" ]]; then
@@ -2107,10 +2108,10 @@ reconcile_one_surface_cli() {
2107
2108
  update_output="$(run_with_timeout "$SURFACE_UPDATE_TIMEOUT_SECONDS" run_surface_update "$resolved" ${update_args[@]+"${update_args[@]}"} 2>&1)" || update_status=$?
2108
2109
  printf '%s\n' "$update_output"
2109
2110
  if [[ "$update_status" -eq 124 ]]; then
2110
- echo "surface update timed out: $name" >&2
2111
+ echo "ERROR surface update timed out: $name" >&2
2111
2112
  return 1
2112
2113
  elif [[ "$update_status" -ne 0 ]]; then
2113
- echo "surface update failed: $name (exit $update_status)" >&2
2114
+ echo "ERROR surface update failed: $name (exit $update_status)" >&2
2114
2115
  return 1
2115
2116
  fi
2116
2117
  # A surface's update command can exit 0 while its own output says the
@@ -2139,7 +2140,7 @@ reconcile_one_surface_cli() {
2139
2140
  new_version="$(surface_cli_version "$resolved")"
2140
2141
  update_error_line="$(surface_update_error_reason "$update_output")"
2141
2142
  if [[ -z "$new_version" ]]; then
2142
- echo "surface $name: update ran but version unreadable (${update_error_line:-"--version produced no output"}); was ${old_version:-unknown}" >&2
2143
+ echo "WARN surface $name: update ran but version unreadable (${update_error_line:-"--version produced no output"}); was ${old_version:-unknown}" >&2
2143
2144
  return 1
2144
2145
  fi
2145
2146
  if [[ "$old_version" != "$new_version" ]]; then
@@ -2151,7 +2152,7 @@ reconcile_one_surface_cli() {
2151
2152
  return 0
2152
2153
  fi
2153
2154
  if [[ -n "$update_error_line" ]]; then
2154
- echo "surface $name: update check failed ($update_error_line); kept ${old_version:-unknown}" >&2
2155
+ echo "WARN surface $name: update check failed ($update_error_line); kept ${old_version:-unknown}" >&2
2155
2156
  return 1
2156
2157
  fi
2157
2158
  echo "surface $name: already latest ($new_version)"
@@ -2159,7 +2160,7 @@ reconcile_one_surface_cli() {
2159
2160
  }
2160
2161
 
2161
2162
  # Loops over this device's declared surface_clis (absent or empty -> silently
2162
- # does nothing, same convention as launch_agents/scratch_repos). A failure is
2163
+ # does nothing, same convention as scratch_repos). A failure is
2163
2164
  # collected into failed_tools by id "surface-<name>", exactly like every other
2164
2165
  # apply-mode step's continue-on-failure contract in this file.
2165
2166
  reconcile_surface_clis() {
@@ -2185,12 +2186,78 @@ reconcile_surface_clis() {
2185
2186
  done < <(jq -r --argjson i "$i" --arg platform "$platform" --arg device "$device" \
2186
2187
  '.[$platform].devices[$device].surface_clis[$i].update_args // [] | .[]' "$manifest")
2187
2188
  if ! reconcile_one_surface_cli "$name" "$command_name" "$executable_override" "$report_only" "$manager" ${update_args[@]+"${update_args[@]}"}; then
2188
- echo "global-tools: TOOL INSTALL FAILED: surface-$name" >&2
2189
+ echo "ERROR global-tools: TOOL INSTALL FAILED: surface-$name" >&2
2189
2190
  failed_tools+=("surface-$name")
2190
2191
  fi
2191
2192
  done
2192
2193
  }
2193
2194
 
2195
+ # retire_git_maintain (2026-09-30, Tarun: "one binary controlling everything"):
2196
+ # the git-maintain user LaunchAgent (com.adl.git-maintain) and its compiled
2197
+ # binary are retired; repository maintenance is `adl reconcile`'s own step
2198
+ # (bin/reconcile-host -> `adl maintain`). M3 and M5 already have the agent
2199
+ # installed and loaded, so this cleans it up actively rather than leaving a
2200
+ # job that scans ~/Documents on its own timer:
2201
+ # - boot out gui/<administrator uid>/com.adl.git-maintain if loaded, and fail
2202
+ # loud if it is still loaded afterwards (nothing is moved in that case);
2203
+ # - move the plist, the compiled binary and its source-hash marker ASIDE
2204
+ # (never deleted) under <state dir>/retired-git-maintain/<UTC stamp>/,
2205
+ # logging each one.
2206
+ # Already gone means no change and no output; plan/audit only list what they
2207
+ # would do; Linux never had it. Returns non-zero (after an ERROR line) on a
2208
+ # failure; the caller records it like any other failed step.
2209
+ retire_git_maintain() {
2210
+ [[ "$platform" == mac ]] || return 0
2211
+ local label=com.adl.git-maintain uid loaded=0 path aside
2212
+ local leftovers=()
2213
+ local agent_plist="$administrator_home/Library/LaunchAgents/$label.plist"
2214
+ local binary="$repo_dir_libexec_root/git-maintain"
2215
+ local hash_marker="$state_dir/launch-agents/git-maintain.source-hash"
2216
+
2217
+ if uid="$(id -u "$administrator" 2>/dev/null)"; then
2218
+ if launchctl print "gui/$uid/$label" >/dev/null 2>&1; then loaded=1; fi
2219
+ elif [[ "$mode" == apply ]]; then
2220
+ echo "ERROR global-tools: cannot resolve the uid of $administrator to retire git-maintain" >&2
2221
+ return 1
2222
+ else
2223
+ # plan/audit on a machine without that account (a test, a preview): the
2224
+ # leftover files can still be listed, but there is nothing to ask launchd.
2225
+ uid="<uid of $administrator>"
2226
+ fi
2227
+ for path in "$agent_plist" "$binary" "$hash_marker"; do
2228
+ if [[ -e "$path" || -L "$path" ]]; then leftovers+=("$path"); fi
2229
+ done
2230
+ if [[ "$loaded" -eq 0 && "${#leftovers[@]}" -eq 0 ]]; then return 0; fi
2231
+
2232
+ if [[ "$mode" != apply ]]; then
2233
+ if [[ "$loaded" -eq 1 ]]; then echo "global-tools: would retire git-maintain: boot out gui/$uid/$label"; fi
2234
+ for path in ${leftovers[@]+"${leftovers[@]}"}; do
2235
+ echo "global-tools: would retire git-maintain: move $path aside"
2236
+ done
2237
+ return 0
2238
+ fi
2239
+
2240
+ if [[ "$loaded" -eq 1 ]]; then
2241
+ if ! launchctl bootout "gui/$uid/$label"; then
2242
+ echo "ERROR global-tools: could not boot out gui/$uid/$label; git-maintain is not retired" >&2
2243
+ return 1
2244
+ fi
2245
+ if launchctl print "gui/$uid/$label" >/dev/null 2>&1; then
2246
+ echo "ERROR global-tools: gui/$uid/$label is still loaded after bootout; git-maintain is not retired" >&2
2247
+ return 1
2248
+ fi
2249
+ echo "global-tools: retiring git-maintain: booted out gui/$uid/$label"
2250
+ fi
2251
+ if [[ "${#leftovers[@]}" -gt 0 ]]; then
2252
+ aside="$state_dir/retired-git-maintain/$(date -u +%Y%m%dT%H%M%SZ)"
2253
+ mkdir -p -- "$aside"
2254
+ for path in "${leftovers[@]}"; do
2255
+ mv -- "$path" "$aside/" || { echo "ERROR global-tools: could not move $path aside to $aside" >&2; return 1; }
2256
+ echo "global-tools: retiring git-maintain: moved $path aside to $aside"
2257
+ done
2258
+ fi
2259
+ }
2260
+
2194
2261
  # Test-only escape hatch: source this script with RECONCILE_SOURCE_ONLY=1 to
2195
2262
  # load every function definition above (and the arg-parsing / manifest
2196
2263
  # validation / platform-and-tool-resolution state already computed by this
@@ -2215,7 +2282,7 @@ scratch_reap_failed=0
2215
2282
  available_kib="$(df -Pk / | awk 'NR == 2 {print $4}')"
2216
2283
  required_kib="$((minimum_free_gib * 1024 * 1024))"
2217
2284
  if [[ "$available_kib" -lt "$required_kib" ]]; then
2218
- echo "global-tools: only $((available_kib / 1024 / 1024)) GiB free; $minimum_free_gib GiB required" >&2
2285
+ echo "ERROR global-tools: only $((available_kib / 1024 / 1024)) GiB free; $minimum_free_gib GiB required" >&2
2219
2286
  [[ "$mode" != apply ]] || exit 1
2220
2287
  fi
2221
2288
 
@@ -2224,7 +2291,7 @@ if [[ "$mode" == apply ]]; then
2224
2291
  fi
2225
2292
 
2226
2293
  if ! install_surface_policy_file; then
2227
- echo "global-tools: TOOL INSTALL FAILED: surface-policy-file" >&2
2294
+ echo "ERROR global-tools: TOOL INSTALL FAILED: surface-policy-file" >&2
2228
2295
  failed_tools+=("surface-policy-file")
2229
2296
  fi
2230
2297
 
@@ -2235,7 +2302,7 @@ require_bun_for_identity_files || exit 1
2235
2302
  # below that could read it (kimi-hooks resolves adl the same way; this
2236
2303
  # is the identity every consumer -- `adl run`, the managed-settings render
2237
2304
  # below, and host automation's git-ado call -- reads instead of its own
2238
- # copy). Runs for real in plan mode too (like launch-agents.ts): default-agent-file.ts's
2305
+ # copy). Runs for real in plan mode too (like the other plan-safe steps): default-agent-file.ts's
2239
2306
  # own --mode plan never writes, so this is safe to run unconditionally.
2240
2307
  default_agent="$(jq -r --arg platform "$platform" --arg device "$device" '.[$platform].devices[$device].default_agent' "$manifest")"
2241
2308
  if [[ "$mode" != apply ]]; then
@@ -2244,7 +2311,7 @@ if [[ "$mode" != apply ]]; then
2244
2311
  else
2245
2312
  echo "global-tools: default-agent file mode=apply device=$device -> $default_agent"
2246
2313
  if ! bun "$repo_root/packages/fleet/src/default-agent-file.ts" --agent "$default_agent" --platform "$platform" --mode apply ${reconcile_test_system_root_args[@]+"${reconcile_test_system_root_args[@]}"}; then
2247
- echo "global-tools: TOOL INSTALL FAILED: default-agent-file" >&2
2314
+ echo "ERROR global-tools: TOOL INSTALL FAILED: default-agent-file" >&2
2248
2315
  failed_tools+=("default-agent-file")
2249
2316
  fi
2250
2317
  fi
@@ -2261,7 +2328,7 @@ if [[ "$mode" != apply ]]; then
2261
2328
  else
2262
2329
  echo "global-tools: managed settings mode=apply device=$device -> $default_agent"
2263
2330
  if ! bun "$repo_root/packages/fleet/src/managed-settings.ts" --agent "$default_agent" --platform "$platform" --mode apply ${reconcile_test_system_root_args[@]+"${reconcile_test_system_root_args[@]}"}; then
2264
- echo "global-tools: TOOL INSTALL FAILED: managed-settings" >&2
2331
+ echo "ERROR global-tools: TOOL INSTALL FAILED: managed-settings" >&2
2265
2332
  failed_tools+=("managed-settings")
2266
2333
  fi
2267
2334
  fi
@@ -2282,7 +2349,7 @@ if jq -e 'to_entries | any(.value.manager == "brew")' "$resolved_file" >/dev/nul
2282
2349
  brew_upgrade_all="$(jq -r --arg platform "$platform" --arg device "$device" '.[$platform].devices[$device].brew_upgrade_all // false' "$manifest")"
2283
2350
  if [[ "$brew_upgrade_all" == true ]]; then
2284
2351
  if ! run_brew_upgrade_all; then
2285
- echo "global-tools: TOOL INSTALL FAILED: brew-upgrade-all" >&2
2352
+ echo "ERROR global-tools: TOOL INSTALL FAILED: brew-upgrade-all" >&2
2286
2353
  failed_tools+=("brew-upgrade-all")
2287
2354
  fi
2288
2355
  fi
@@ -2295,7 +2362,7 @@ fi
2295
2362
 
2296
2363
  while IFS= read -r tool_id; do
2297
2364
  if ! install_one_tool "$tool_id"; then
2298
- echo "global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2365
+ echo "ERROR global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2299
2366
  failed_tools+=("$tool_id")
2300
2367
  fi
2301
2368
  done < <(jq -r 'to_entries[] | select(.value.manager != "shim" and .value.manager != "repo-script" and .value.manager != "repo-dir" and .value.manager != "bun-compile" and .value.manager != "embedding-model") | .key' "$resolved_file")
@@ -2310,28 +2377,28 @@ fi
2310
2377
 
2311
2378
  while IFS= read -r tool_id; do
2312
2379
  if ! build_one_bun_tool "$tool_id"; then
2313
- echo "global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2380
+ echo "ERROR global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2314
2381
  failed_tools+=("$tool_id")
2315
2382
  fi
2316
2383
  done < <(jq -r 'to_entries[] | select(.value.manager == "bun-compile") | .key' "$resolved_file")
2317
2384
 
2318
2385
  while IFS= read -r tool_id; do
2319
2386
  if ! install_one_embedding_model_tool "$tool_id"; then
2320
- echo "global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2387
+ echo "ERROR global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2321
2388
  failed_tools+=("$tool_id")
2322
2389
  fi
2323
2390
  done < <(jq -r 'to_entries[] | select(.value.manager == "embedding-model") | .key' "$resolved_file")
2324
2391
 
2325
2392
  while IFS= read -r tool_id; do
2326
2393
  if ! write_one_shim "$tool_id"; then
2327
- echo "global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2394
+ echo "ERROR global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2328
2395
  failed_tools+=("$tool_id")
2329
2396
  fi
2330
2397
  done < <(jq -r 'to_entries[] | select(.value.manager == "shim") | .key' "$resolved_file")
2331
2398
 
2332
2399
  while IFS= read -r tool_id; do
2333
2400
  if ! write_one_repo_script "$tool_id"; then
2334
- echo "global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2401
+ echo "ERROR global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2335
2402
  failed_tools+=("$tool_id")
2336
2403
  fi
2337
2404
  done < <(jq -r 'to_entries[] | select(.value.manager == "repo-script") | .key' "$resolved_file")
@@ -2426,7 +2493,7 @@ fi
2426
2493
 
2427
2494
  while IFS= read -r tool_id; do
2428
2495
  if ! write_one_repo_dir "$tool_id"; then
2429
- echo "global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2496
+ echo "ERROR global-tools: TOOL INSTALL FAILED: $tool_id" >&2
2430
2497
  failed_tools+=("$tool_id")
2431
2498
  fi
2432
2499
  done < <(jq -r 'to_entries[] | select(.value.manager == "repo-dir") | .key' "$resolved_file")
@@ -2466,12 +2533,12 @@ project_adl_package_payload() {
2466
2533
  local adl_package bun_bin
2467
2534
  adl_package="$(jq -r '.adl.package // empty' "$resolved_file")"
2468
2535
  [[ "$adl_package" == @amsterdamdatalabs/adl-agent-control ]] || {
2469
- echo "Adl package projection requires the published wrapper entry" >&2
2536
+ echo "ERROR Adl package projection requires the published wrapper entry" >&2
2470
2537
  return 1
2471
2538
  }
2472
2539
  for failed_tool in ${failed_tools[@]+"${failed_tools[@]}"}; do
2473
2540
  if [[ "$failed_tool" == adl ]]; then
2474
- echo "skipping Adl payload projection because Adl source verification failed" >&2
2541
+ echo "WARN skipping Adl payload projection because Adl source verification failed" >&2
2475
2542
  return 1
2476
2543
  fi
2477
2544
  done
@@ -2483,10 +2550,10 @@ project_adl_package_payload() {
2483
2550
  return 0
2484
2551
  fi
2485
2552
  bun_bin="$(command -v bun || true)"
2486
- [[ -n "$bun_bin" ]] || { echo "Adl package projection requires bun on PATH" >&2; return 1; }
2553
+ [[ -n "$bun_bin" ]] || { echo "ERROR Adl package projection requires bun on PATH" >&2; return 1; }
2487
2554
  if [[ "$source_mode" == checkout ]]; then
2488
2555
  (cd "$repo_root/packages/agent-skills" && "$bun_bin" run build) || {
2489
- echo "could not build agent-skills payload from checkout" >&2
2556
+ echo "ERROR could not build agent-skills payload from checkout" >&2
2490
2557
  return 1
2491
2558
  }
2492
2559
  fi
@@ -2499,50 +2566,33 @@ project_adl_package_payload() {
2499
2566
  }
2500
2567
 
2501
2568
  if ! project_adl_package_payload; then
2502
- echo "global-tools: TOOL INSTALL FAILED: adl-package-projection" >&2
2569
+ echo "ERROR global-tools: TOOL INSTALL FAILED: adl-package-projection" >&2
2503
2570
  failed_tools+=("adl-package-projection")
2504
2571
  fi
2505
2572
 
2506
2573
  # Same continue-on-failure contract as the tool passes above: runs after git
2507
2574
  # itself is installed/upgraded, and a failure is recorded, not fatal.
2508
2575
  if ! "$repo_root/bin/shell-path-policy" --home "$administrator_home" --platform "$platform" --mode "$mode" --owner "$administrator"; then
2509
- echo "global-tools: TOOL INSTALL FAILED: shell-path-policy" >&2
2576
+ echo "ERROR global-tools: TOOL INSTALL FAILED: shell-path-policy" >&2
2510
2577
  failed_tools+=("shell-path-policy")
2511
2578
  fi
2512
2579
 
2513
2580
  if ! reconcile_git_global_config; then
2514
- echo "global-tools: TOOL INSTALL FAILED: git-global-config" >&2
2581
+ echo "ERROR global-tools: TOOL INSTALL FAILED: git-global-config" >&2
2515
2582
  failed_tools+=("git-global-config")
2516
2583
  fi
2517
2584
  cleanup_legacy_linux_git_config || {
2518
- echo "global-tools: legacy Linux git config cleanup failed" >&2
2585
+ echo "ERROR global-tools: legacy Linux git config cleanup failed" >&2
2519
2586
  exit 1
2520
2587
  }
2521
2588
 
2522
- # launch_agents (mac only; validate-manifest rejects them on linux): user
2523
- # LaunchAgents adl owns -- see packages/fleet/src/launch-agents.ts. Runs
2524
- # before the global surface sweep, whose LaunchAgent protection is derived
2525
- # from these same manifest declarations. Apply runs as root (it installs the
2526
- # compiles each job into its own signed binary under
2527
- # /usr/local/libexec/adl, writes the administrator's plist owned by
2528
- # that user, and bootstraps gui/<uid>); plan only prints. Same
2529
- # continue-on-failure contract as the tool passes above.
2530
- if [[ "$platform" == mac ]] && jq -e --arg device "$device" \
2531
- '((.mac.common.launch_agents // {}) + (.mac.devices[$device].launch_agents // {})) | length > 0' "$manifest" >/dev/null; then
2532
- launch_agents_args=(--manifest "$manifest" --platform "$platform" --device "$device" --home "$administrator_home" --state-dir "$state_dir")
2533
- if [[ "$have_previous" -eq 1 ]]; then
2534
- launch_agents_args+=(--previous-manifest "$applied_manifest")
2535
- fi
2536
- if [[ "$mode" == apply ]]; then
2537
- echo "global-tools: launch agents mode=apply home=$administrator_home (for $administrator)"
2538
- if ! bun "$repo_root/packages/fleet/src/launch-agents.ts" --mode apply "${launch_agents_args[@]}"; then
2539
- echo "global-tools: TOOL INSTALL FAILED: launch-agents" >&2
2540
- failed_tools+=("launch-agents")
2541
- fi
2542
- else
2543
- echo "global-tools: launch agents mode=plan home=$administrator_home"
2544
- bun "$repo_root/packages/fleet/src/launch-agents.ts" --mode plan "${launch_agents_args[@]}"
2545
- fi
2589
+ # git-maintain is retired: clean it up where it is still installed (mac), ahead
2590
+ # of the global surface sweep, which would otherwise see its leftover
2591
+ # LaunchAgent as an approval-gated candidate. Same continue-on-failure
2592
+ # contract as the tool passes above.
2593
+ if ! retire_git_maintain; then
2594
+ echo "ERROR global-tools: TOOL INSTALL FAILED: git-maintain-retire" >&2
2595
+ failed_tools+=("git-maintain-retire")
2546
2596
  fi
2547
2597
 
2548
2598
  if [[ "$mode" == apply ]]; then
@@ -2608,11 +2658,11 @@ if [[ "$kimi_hooks_enabled" == true ]]; then
2608
2658
  # shim: there is no standalone hook executable to resolve any more.
2609
2659
  adl_executable="$(jq -r '."adl".executable' "$resolved_file")"
2610
2660
  if ! adl_resolved="$(sudo -u "$administrator" -H env PATH="/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin" /bin/sh -c "command -v '$adl_executable'" 2>/dev/null)"; then
2611
- echo "global-tools: could not resolve adl from the administrator PATH" >&2
2661
+ echo "ERROR global-tools: could not resolve adl from the administrator PATH" >&2
2612
2662
  exit 1
2613
2663
  fi
2614
2664
  [[ -n "$adl_resolved" && -x "$adl_resolved" ]] || {
2615
- echo "global-tools: kimi-hooks requires adl on PATH ($adl_executable); bun-compile installation must succeed first" >&2
2665
+ echo "ERROR global-tools: kimi-hooks requires adl on PATH ($adl_executable); bun-compile installation must succeed first" >&2
2616
2666
  exit 1
2617
2667
  }
2618
2668
  echo "global-tools: kimi hooks mode=apply home=$administrator_home (as $administrator)"
@@ -2764,14 +2814,14 @@ fi
2764
2814
  if [[ "$mode" == apply ]]; then
2765
2815
  if repo_root_is_git; then
2766
2816
  applied_commit="$(sudo -u "$administrator" -H git -C "$repo_root" rev-parse HEAD)" || {
2767
- echo "could not resolve checkout commit for applied manifest identity" >&2
2817
+ echo "ERROR could not resolve checkout commit for applied manifest identity" >&2
2768
2818
  exit 1
2769
2819
  }
2770
2820
  else
2771
2821
  # az374: the installed npm package's own version is the identity of what
2772
2822
  # was applied -- there is no commit to ask for.
2773
2823
  applied_npm_version="$(jq -er '.version | select(type == "string" and length > 0)' "$repo_root/package.json")" || {
2774
- echo "installed Adl package has no valid version: $repo_root/package.json" >&2
2824
+ echo "ERROR installed Adl package has no valid version: $repo_root/package.json" >&2
2775
2825
  exit 1
2776
2826
  }
2777
2827
  applied_commit="npm:$applied_npm_version"
@@ -2785,83 +2835,6 @@ else
2785
2835
  echo "global-tools: $mode complete; no state changed"
2786
2836
  fi
2787
2837
 
2788
- # git_maintain_repos (2026-09-30, arm-only decision the same day): an
2789
- # explicit, closed list of repositories this device's own checkout lives
2790
- # at OUTSIDE any recursively-scanned home directory -- the Linux VPSes'
2791
- # root-owned /opt/adl-azdo is the confirmed case for both strato-vps-1 and
2792
- # hostinger-vps-1 (STATUS.md's 2026-09-29 first-deployment record;
2793
- # hostinger's own fleet-cutover --apply landed 2026-09-29T23:43Z, confirmed
2794
- # the same day). git-maintain's own scheduling (packages/fleet/src/
2795
- # launch-agents.ts: a dedicated compiled binary, ad-hoc signed, run by a
2796
- # launchd LaunchAgent under TCC) is macOS-only by construction, so it never
2797
- # reaches these checkouts on its own. This runs the job directly, on
2798
- # reconcile's own schedule (every platform this script runs on already has
2799
- # one), passing each declared path as `--repo` argv -- never a recursive
2800
- # scan of a shared parent like `/opt` (which also holds unrelated,
2801
- # explicitly off-limits directories), and never a new environment
2802
- # variable: the list itself comes only from the manifest, and
2803
- # GIT_MAINTAIN_DRY below is this job's own pre-existing dry/real contract,
2804
- # driven by reconcile's own --mode exactly like every other step in this
2805
- # file, not a knob an operator sets by hand. Absent or empty (every mac
2806
- # device today) is a silent no-op, the same convention
2807
- # launch_agents/scratch_repos/surface_clis already use.
2808
- #
2809
- # ARM-ONLY, always, on this path (independent review, 2026-09-30): the full
2810
- # job on this SAME checkout `bin/reconcile` runs from turned out to be new,
2811
- # unapproved, root-run behaviour once actually reasoned through, not merely
2812
- # a self-checkout read/write ordering hazard -- see the `--arm-only`
2813
- # decision recorded in jobs/git-maintain/src/main.ts's own header comment
2814
- # for the full list (a silent fast-forward-driven auto-deploy of
2815
- # `origin/integration`/main with no approval gate, colliding with
2816
- # fleet-cutover.sh re-reading fleet-retire-list.json from this same
2817
- # checkout after reconcile runs, `git branch -D` as root, and a fetch that
2818
- # still runs even in dry/plan/audit mode and exits 0 on its own failure).
2819
- # `--arm-only` narrows this call to ONLY `armHooks`: no fetch, no
2820
- # fast-forward, no branch prune, no bundle-refresh installer, no network,
2821
- # and no Azure DevOps identity resolution. The only remaining write is
2822
- # `.git/config`'s `core.hooksPath`, real only in apply mode
2823
- # (GIT_MAINTAIN_DRY=1 below makes it a `[dry] would arm ...` log line and
2824
- # nothing else, in both plan AND audit mode). That is never a tracked file
2825
- # this script (or anything downstream of it) reads, so arm-only has no
2826
- # self-checkout read/write ordering hazard left to avoid -- this step stays
2827
- # in this late position anyway, unchanged from the full-job version: it is
2828
- # still the simplest place for it, and there is no reason to special-case
2829
- # arm-only's position just because its own hazard is gone.
2830
- git_maintain_repos=()
2831
- while IFS= read -r repo; do
2832
- [[ -n "$repo" ]] && git_maintain_repos+=("$repo")
2833
- done < <(jq -r --arg platform "$platform" --arg device "$device" \
2834
- '.[$platform].devices[$device].git_maintain_repos // [] | .[]' "$manifest")
2835
- if [[ "${#git_maintain_repos[@]}" -gt 0 ]]; then
2836
- git_maintain_repo_args=(--arm-only)
2837
- for repo in "${git_maintain_repos[@]}"; do
2838
- git_maintain_repo_args+=(--repo "$repo")
2839
- done
2840
- # Unlike default-agent-file.ts/managed-settings.ts's plan branches earlier
2841
- # in this file (which never fail, by construction, so they run
2842
- # unguarded), this job's dry pass still validates each declared path is a
2843
- # real repo -- a genuine failure mode even in a non-apply mode, e.g. a
2844
- # declared checkout that has not been cloned yet. So every non-apply mode
2845
- # is guarded exactly like apply mode here, deliberately deviating from
2846
- # those sibling steps: report it, collect it, never let it abort the rest
2847
- # of this script under this file's own `set -e`. `$mode` (plan OR audit)
2848
- # is reported verbatim -- audit is a real, distinct reconcile mode, never
2849
- # relabeled "plan".
2850
- if [[ "$mode" != apply ]]; then
2851
- echo "global-tools: git-maintain (declared repos, arm-only) mode=$mode device=$device -> ${git_maintain_repos[*]}"
2852
- if ! GIT_MAINTAIN_DRY=1 bun "$repo_root/packages/fleet/jobs/git-maintain/src/main.ts" "${git_maintain_repo_args[@]}"; then
2853
- echo "global-tools: TOOL INSTALL FAILED: git-maintain-repos" >&2
2854
- failed_tools+=("git-maintain-repos")
2855
- fi
2856
- else
2857
- echo "global-tools: git-maintain (declared repos, arm-only) mode=apply device=$device -> ${git_maintain_repos[*]}"
2858
- if ! bun "$repo_root/packages/fleet/jobs/git-maintain/src/main.ts" "${git_maintain_repo_args[@]}"; then
2859
- echo "global-tools: TOOL INSTALL FAILED: git-maintain-repos" >&2
2860
- failed_tools+=("git-maintain-repos")
2861
- fi
2862
- fi
2863
- fi
2864
-
2865
2838
  # A recorded, continued-past tool failure (install_one_tool /
2866
2839
  # build_one_bun_tool / install_one_embedding_model_tool / write_one_shim)
2867
2840
  # still lets every post-pass above run -- sweep, kimi-hooks, global-agents,
@@ -2869,11 +2842,11 @@ fi
2869
2842
  # this point. The non-zero exit here is the only signal a caller (cron,
2870
2843
  # systemd, CI) needs that something needs attention.
2871
2844
  if [[ "${#failed_tools[@]}" -gt 0 ]]; then
2872
- echo "global-tools: FAILED to install/build ${#failed_tools[@]} tool(s): ${failed_tools[*]}" >&2
2845
+ echo "ERROR global-tools: FAILED to install/build ${#failed_tools[@]} tool(s): ${failed_tools[*]}" >&2
2873
2846
  exit 1
2874
2847
  fi
2875
2848
 
2876
2849
  if [[ "$scratch_reap_failed" -eq 1 ]]; then
2877
- echo "global-tools: scratch reap could not verify remotes; worktree removal was refused" >&2
2850
+ echo "ERROR global-tools: scratch reap could not verify remotes; worktree removal was refused" >&2
2878
2851
  exit 1
2879
2852
  fi