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

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 (405) hide show
  1. package/bin/adl +2 -2
  2. package/bin/git +16 -17
  3. package/bin/git-ado +25 -24
  4. package/bin/git-defaults +173 -0
  5. package/bin/health-check +8 -1
  6. package/bin/kimi-hooks +1 -1
  7. package/bin/lib/adl-package.sh +257 -0
  8. package/bin/lib/fleet-host.sh +8 -5
  9. package/bin/reconcile +482 -232
  10. package/bin/reconcile-host +30 -20
  11. package/bin/setup +20 -17
  12. package/bin/shell-path-policy +15 -5
  13. package/bin/sync-and-reconcile +62 -64
  14. package/bin/validate-manifest +33 -9
  15. package/package.json +2 -10
  16. package/packages/agent-hooks/README.md +14 -9
  17. package/packages/agent-hooks/assets/hooks.json +0 -20
  18. package/packages/agent-hooks/src/cli/args.ts +1 -1
  19. package/packages/agent-hooks/src/cli/doctor.ts +6 -58
  20. package/packages/agent-hooks/src/deploy/deploy.ts +12 -41
  21. package/packages/agent-hooks/src/deploy/doctor.ts +8 -11
  22. package/packages/agent-hooks/src/deploy/index.ts +14 -14
  23. package/packages/agent-hooks/src/deploy/json-merge.ts +7 -8
  24. package/packages/agent-hooks/src/deploy/legacy-lock.ts +2 -2
  25. package/packages/agent-hooks/src/deploy/list.ts +1 -1
  26. package/packages/agent-hooks/src/deploy/lock.ts +31 -12
  27. package/packages/agent-hooks/src/deploy/profile.ts +3 -4
  28. package/packages/agent-hooks/src/deploy/source.ts +1 -1
  29. package/packages/agent-hooks/src/gating/config.ts +10 -24
  30. package/packages/agent-hooks/src/gating/root.ts +3 -3
  31. package/packages/agent-hooks/src/hooks/briefing/handler.ts +2 -2
  32. package/packages/agent-hooks/src/hooks/briefing/index.ts +27 -287
  33. package/packages/agent-hooks/src/hooks/contract.ts +17 -17
  34. package/packages/agent-hooks/src/hooks/crg/background.ts +27 -0
  35. package/packages/agent-hooks/src/hooks/crg/index.ts +7 -6
  36. package/packages/agent-hooks/src/hooks/log.ts +8 -10
  37. package/packages/agent-hooks/src/hooks/user-prompt-submit/match.ts +1 -1
  38. package/packages/agent-hooks/src/hooks/user-prompt-submit/tokenizer.ts +1 -1
  39. package/packages/agent-hooks/src/hooks/user-prompt-submit/vector.ts +6 -5
  40. package/packages/agent-hooks/src/hosts/registry.ts +1 -1
  41. package/packages/agent-hooks/src/log/adl-dir.ts +44 -0
  42. package/packages/agent-hooks/src/log/writer.ts +16 -2
  43. package/packages/agent-hooks/src/main.ts +7 -41
  44. package/packages/agent-hooks/src/pre-tool-use/handler.ts +71 -82
  45. package/packages/agent-hooks/src/rules/engine.ts +13 -17
  46. package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +3 -3
  47. package/packages/agent-hooks/src/rules/impl/adl-git-credential-bypass.ts +12 -14
  48. package/packages/agent-hooks/src/rules/impl/adl-git-identity.ts +28 -28
  49. package/packages/agent-hooks/src/rules/impl/adl-git-push-policy.ts +8 -9
  50. package/packages/agent-hooks/src/rules/impl/adl-human-azdo-bypass.ts +56 -59
  51. package/packages/agent-hooks/src/rules/impl/adl-identity.ts +16 -23
  52. package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +19 -16
  53. package/packages/agent-hooks/src/rules/impl/adl-pr-complete-pending.ts +2 -2
  54. package/packages/agent-hooks/src/rules/impl/adl-pr-review-role.ts +9 -12
  55. package/packages/agent-hooks/src/rules/impl/adl-pr-role.ts +5 -7
  56. package/packages/agent-hooks/src/rules/impl/adl-run-required.ts +411 -0
  57. package/packages/agent-hooks/src/rules/impl/adl-token-minting-role.ts +4 -5
  58. package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +6 -7
  59. package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +77 -70
  60. package/packages/agent-hooks/src/rules/impl/deny-allowlist.ts +159 -0
  61. package/packages/agent-hooks/src/rules/impl/git-helpers.ts +21 -23
  62. package/packages/agent-hooks/src/rules/impl/git-worktree.ts +14 -28
  63. package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +3 -3
  64. package/packages/agent-hooks/src/rules/impl/messages.ts +1 -1
  65. package/packages/agent-hooks/src/rules/impl/pr-completion.ts +1 -1
  66. package/packages/agent-hooks/src/rules/impl/tool-azure-devops.ts +35 -46
  67. package/packages/agent-hooks/src/rules/impl/tool-find.ts +34 -7
  68. package/packages/agent-hooks/src/rules/impl/tool-grep.ts +34 -12
  69. package/packages/agent-hooks/src/rules/impl/tool-native-search.ts +52 -9
  70. package/packages/agent-hooks/src/rules/ports.ts +5 -7
  71. package/packages/agent-hooks/src/rules/registry.ts +4 -6
  72. package/packages/agent-hooks/src/rules/shell/git-identity.ts +86 -101
  73. package/packages/agent-hooks/src/rules/shell/git-internals.ts +1 -1
  74. package/packages/agent-hooks/src/rules/shell/invocation.ts +24 -118
  75. package/packages/agent-hooks/src/rules/shell/piped-script.ts +1 -1
  76. package/packages/agent-hooks/src/rules/shell/prefixes.ts +1 -1
  77. package/packages/agent-hooks/src/rules/shell/protected-carrier-writes.ts +11 -10
  78. package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +26 -26
  79. package/packages/agent-hooks/src/rules/shell/substitution.ts +1 -1
  80. package/packages/agent-hooks/src/rules/shell/xargs.ts +2 -2
  81. package/packages/agent-hooks/src/rules/spec.ts +2 -2
  82. package/packages/agent-settings/README.md +287 -0
  83. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +14 -16
  84. package/packages/agent-settings/assets/agents/architect.toml +2 -0
  85. package/packages/agent-settings/assets/agents/code-reviewer.toml +2 -0
  86. package/packages/agent-settings/assets/agents/executor.toml +2 -0
  87. package/packages/agent-settings/assets/agents/explore.toml +2 -0
  88. package/packages/agent-settings/assets/agents/planner.toml +2 -0
  89. package/packages/agent-settings/assets/mcp.json +0 -4
  90. package/packages/agent-settings/deploy-profile.json +139 -0
  91. package/packages/agent-settings/deploy-profile.schema.json +147 -0
  92. package/packages/agent-settings/managed/README.md +29 -30
  93. package/packages/agent-settings/managed/codex-requirements.toml +9 -9
  94. package/packages/agent-settings/package.json +20 -0
  95. package/packages/agent-settings/src/deploy/agent-render.ts +2 -2
  96. package/packages/agent-settings/src/deploy/agent-toml.ts +1 -1
  97. package/packages/agent-settings/src/deploy/agents-deploy.ts +17 -3
  98. package/packages/agent-settings/src/deploy/doctor.ts +3 -83
  99. package/packages/agent-settings/src/deploy/index.ts +6 -33
  100. package/packages/agent-settings/src/deploy/legacy-lock.ts +6 -6
  101. package/packages/agent-settings/src/deploy/list.ts +5 -19
  102. package/packages/agent-settings/src/deploy/lock.ts +8 -18
  103. package/packages/agent-settings/src/deploy/mcp-deploy.ts +10 -5
  104. package/packages/agent-settings/src/deploy/mcp-merge.ts +22 -3
  105. package/packages/agent-settings/src/deploy/mcp-source.ts +1 -1
  106. package/packages/agent-settings/src/deploy/profile.ts +11 -35
  107. package/packages/agent-settings/src/deploy/retire-lean-ctx.ts +184 -0
  108. package/packages/agent-settings/src/deploy/settings.ts +17 -22
  109. package/packages/agent-settings/src/global-surface-sweep.ts +0 -2
  110. package/packages/agent-settings/src/kimi-hooks.ts +339 -192
  111. package/packages/agent-settings/src/opencode-hooks.ts +1 -1
  112. package/packages/agent-settings/tsconfig.json +23 -0
  113. package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +120 -0
  114. package/packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh +276 -0
  115. package/packages/agent-skills/assets/debug-issue/SKILL.md +8 -7
  116. package/packages/agent-skills/assets/explore-codebase/SKILL.md +13 -13
  117. package/packages/agent-skills/assets/refactor-safely/SKILL.md +12 -11
  118. package/packages/agent-skills/assets/review-changes/SKILL.md +7 -7
  119. package/packages/agent-skills/assets/testing/references/e2e.md +37 -0
  120. package/packages/agent-skills/assets/testing/references/integration.md +37 -0
  121. package/packages/agent-skills/assets/testing/references/unit.md +32 -0
  122. package/packages/agent-skills/dist/deploy.d.ts +2 -0
  123. package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
  124. package/packages/agent-skills/dist/deploy.js +27 -1
  125. package/packages/agent-skills/dist/deploy.js.map +1 -1
  126. package/packages/agent-skills/dist/doctor.d.ts +0 -2
  127. package/packages/agent-skills/dist/doctor.d.ts.map +1 -1
  128. package/packages/agent-skills/dist/doctor.js +1 -1
  129. package/packages/agent-skills/dist/doctor.js.map +1 -1
  130. package/packages/agent-skills/dist/fs-utils.d.ts +0 -1
  131. package/packages/agent-skills/dist/fs-utils.d.ts.map +1 -1
  132. package/packages/agent-skills/dist/fs-utils.js +1 -1
  133. package/packages/agent-skills/dist/fs-utils.js.map +1 -1
  134. package/packages/agent-skills/src/deploy.ts +29 -1
  135. package/packages/agent-skills/src/doctor.ts +1 -1
  136. package/packages/agent-skills/src/fs-utils.ts +1 -1
  137. package/packages/azdo/README.md +10 -9
  138. package/packages/azdo/package.json +1 -3
  139. package/packages/azdo/src/azdo-pl.ts +2 -54
  140. package/packages/azdo/src/azdo-pr-review.ts +4 -4
  141. package/packages/azdo/src/azdo-repo-doctor.ts +101 -52
  142. package/packages/azdo/src/azdo-wit.ts +41 -2
  143. package/packages/azdo/src/lib/agent-identity.ts +12 -14
  144. package/packages/azdo/src/lib/agent-token.ts +2 -2
  145. package/packages/azdo/src/lib/client.ts +0 -5
  146. package/packages/azdo/src/lib/graph-identity.ts +4 -4
  147. package/packages/azdo/src/workflow-rules-matrix.csv +85 -0
  148. package/packages/azdo/src/workflow-rules.ts +128 -0
  149. package/packages/fleet/manifest.json +35 -11
  150. package/packages/fleet/manifest.schema.json +62 -10
  151. package/packages/fleet/src/default-agent-file.ts +3 -3
  152. package/packages/fleet/src/install-adl-package.ts +73 -15
  153. package/packages/fleet/src/install-embedding-model.ts +13 -13
  154. package/packages/fleet/src/managed-settings.ts +7 -7
  155. package/packages/fleet/src/repo-maintain.ts +91 -55
  156. package/packages/fleet/src/scratch-reap.ts +5 -5
  157. package/packages/fleet/src/vscode-settings.ts +5 -5
  158. package/packages/repo-controls/README.md +141 -43
  159. package/packages/repo-controls/assets/promote.ts +4 -4
  160. package/packages/repo-controls/assets/reconcile-guard.sh +10 -46
  161. package/packages/repo-controls/assets/version-bump.sh +31 -84
  162. package/packages/repo-controls/bunfig.toml +6 -7
  163. package/packages/repo-controls/package/adl-compliance/AGENTS.md +5 -6
  164. package/packages/repo-controls/package/adl-compliance/conftest.py +3 -1
  165. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +4 -16
  166. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +9 -2
  167. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +1 -0
  168. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/test_check.py +11 -0
  169. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/__init__.py +7 -0
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/check.py +266 -0
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.drafts/c.md +1 -0
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.drafts/d.md +1 -0
  173. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.hidden.md +1 -0
  174. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/a.md +1 -0
  175. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/b.md +1 -0
  176. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/data.csv +1 -0
  177. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/diagram.png +1 -0
  178. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/mock.png +1 -0
  179. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/sub/nested.md +1 -0
  180. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs.md +1 -0
  181. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/grants.yaml +4 -0
  182. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p1.md +1 -0
  183. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p2.md +1 -0
  184. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p3.md +1 -0
  185. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p4.md +1 -0
  186. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p5.md +1 -0
  187. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/build.md +1 -0
  188. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/overview.md +1 -0
  189. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/install.md +1 -0
  190. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/start.md +1 -0
  191. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/grants.yaml +4 -0
  192. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-001-tenancy.md +1 -0
  193. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-002-carrier.md +1 -0
  194. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-003-cli.md +1 -0
  195. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/archive/adr-000-old.md +1 -0
  196. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/archive/adr-000-older.md +1 -0
  197. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/a.md +1 -0
  198. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/b.md +1 -0
  199. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/c.md +1 -0
  200. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/grants.yaml +4 -0
  201. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-01.md +1 -0
  202. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-02.md +1 -0
  203. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-03.md +1 -0
  204. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-04.md +1 -0
  205. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-05.md +1 -0
  206. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-06.md +1 -0
  207. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-07.md +1 -0
  208. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-08.md +1 -0
  209. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-09.md +1 -0
  210. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-10.md +1 -0
  211. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-11.md +1 -0
  212. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-12.md +1 -0
  213. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-13.md +1 -0
  214. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-14.md +1 -0
  215. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-15.md +1 -0
  216. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-16.md +1 -0
  217. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/grants.yaml +4 -0
  218. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/a.md +1 -0
  219. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/b.md +1 -0
  220. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/c.md +1 -0
  221. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/grants.yaml +4 -0
  222. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/a.md +1 -0
  223. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/b.md +1 -0
  224. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/c.md +1 -0
  225. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/grants.yaml +4 -0
  226. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/a.md +1 -0
  227. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/b.md +1 -0
  228. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/c.md +1 -0
  229. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/d.md +1 -0
  230. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/e.md +1 -0
  231. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/grants.yaml +4 -0
  232. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/a.md +1 -0
  233. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/b.md +1 -0
  234. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/c.md +1 -0
  235. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/short/only.md +1 -0
  236. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/grants.yaml +4 -0
  237. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/a.md +1 -0
  238. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/b.md +1 -0
  239. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/c.md +1 -0
  240. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/d.md +1 -0
  241. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/a.md +1 -0
  242. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/b.md +1 -0
  243. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/grants.yaml +6 -0
  244. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/a.md +1 -0
  245. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/b.md +1 -0
  246. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/c.md +1 -0
  247. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/grants.yaml +3 -0
  248. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/test_check.py +345 -0
  249. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +18 -5
  250. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +35 -0
  251. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/__init__.py +7 -0
  252. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/check.py +91 -0
  253. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/AGENTS.md +3 -0
  254. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/CLAUDE.md +3 -0
  255. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/-leading-hyphen.md +3 -0
  256. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/AGENTS.md +3 -0
  257. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/double--hyphen.md +3 -0
  258. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/getting started.md +3 -0
  259. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/nested/page-here.md +3 -0
  260. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/trailing-hyphen-.md +3 -0
  261. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +4 -0
  262. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/user_profile.md +3 -0
  263. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/UI-CONTRACT.png +2 -0
  264. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/diagram.png +1 -0
  265. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/index.md +4 -0
  266. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/notes.md +3 -0
  267. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/reference.html +1 -0
  268. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/2026-10-experiment.md +3 -0
  269. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +6 -0
  270. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs.md +5 -0
  271. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +194 -0
  272. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +14 -2
  273. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +5 -5
  274. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-shipped-register-legacy/docs/compliance/index.md +10 -0
  275. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-shipped-register-type/docs/compliance/index.md +10 -0
  276. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +58 -6
  277. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/test_check.py +0 -1
  278. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/check.py +64 -17
  279. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +60 -9
  280. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +17 -0
  281. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +13 -0
  282. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs/architecture/demo.md +0 -1
  283. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs-okf-profiles.yaml +0 -1
  284. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/no-type.md +9 -0
  285. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-experiment.md +10 -0
  286. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-research.md +10 -0
  287. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs-okf-profiles.yaml +5 -0
  288. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs/architecture/bad-status.md +0 -1
  289. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs-okf-profiles.yaml +0 -1
  290. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/experiment-ok.md +15 -0
  291. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +15 -0
  292. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +16 -0
  293. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +11 -0
  294. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +11 -0
  295. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-last-update.md +11 -0
  296. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-sources.md +10 -0
  297. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +11 -0
  298. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +11 -0
  299. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +16 -0
  300. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +14 -0
  301. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/architecture/created-date.md +21 -0
  302. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/architecture/research-shaped.md +19 -0
  303. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/operations/runbook.md +26 -0
  304. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/test_check.py +228 -2
  305. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +5 -0
  306. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +11 -2
  307. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/__init__.py +5 -3
  308. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +160 -44
  309. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +0 -2
  310. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/a.ts +1 -0
  311. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/b.ts +1 -0
  312. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/grants.yaml +3 -0
  313. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +0 -2
  314. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  315. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +5 -0
  316. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/a.ts +1 -0
  317. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/b.ts +1 -0
  318. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/c.ts +1 -0
  319. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +4 -0
  320. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  321. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +5 -0
  322. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +0 -2
  323. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  324. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/grants.yaml +3 -0
  325. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/register.py +127 -0
  326. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +265 -29
  327. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +75 -2
  328. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +194 -0
  329. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/check.py +56 -10
  330. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/test_check.py +256 -0
  331. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +40 -12
  332. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +108 -0
  333. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +1 -1
  334. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +2 -3
  335. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +23 -6
  336. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +76 -0
  337. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +58 -17
  338. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +161 -0
  339. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +35 -3
  340. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +108 -1
  341. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +281 -61
  342. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/absolute-path.yaml +3 -0
  343. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/backslash-path.yaml +3 -0
  344. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/baseline-is-a-list.yaml +2 -0
  345. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/boolean-size.yaml +3 -0
  346. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/dot-segment.yaml +3 -0
  347. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/duplicate-path.yaml +4 -0
  348. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-file.yaml +0 -0
  349. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-segment.yaml +3 -0
  350. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/float-size.yaml +3 -0
  351. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/negative-size.yaml +3 -0
  352. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-over-limit-baseline.yaml +1 -0
  353. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-schema-version.yaml +1 -0
  354. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/non-string-path.yaml +3 -0
  355. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/not-yaml.yaml +2 -0
  356. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/null-size.yaml +3 -0
  357. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/parent-segment.yaml +3 -0
  358. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/string-size.yaml +3 -0
  359. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/top-level-is-a-list.yaml +2 -0
  360. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/trailing-slash.yaml +3 -0
  361. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/unknown-key.yaml +3 -0
  362. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/wrong-schema-version.yaml +2 -0
  363. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/zero-size.yaml +3 -0
  364. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/valid.yaml +4 -0
  365. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +758 -45
  366. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/check.py +4 -3
  367. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/test_check.py +2 -2
  368. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +490 -0
  369. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +368 -55
  370. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +4 -2
  371. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +523 -0
  372. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +1179 -0
  373. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +122 -3
  374. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +33 -1
  375. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +0 -11
  376. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/context.py +16 -1
  377. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -2
  378. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/docs_shape.py +12 -1
  379. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +133 -147
  380. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +133 -147
  381. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +1 -1
  382. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +52 -10
  383. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +23 -4
  384. package/packages/repo-controls/package.json +2 -9
  385. package/packages/repo-controls/scripts/hooks/post-merge +13 -0
  386. package/packages/repo-controls/scripts/hooks/pre-push +3 -3
  387. package/packages/repo-controls/src/azdo-push.ts +64 -36
  388. package/packages/repo-controls/src/azure-devops-governance.ts +7 -9
  389. package/packages/repo-controls/src/azure-devops-remote.ts +1 -1
  390. package/packages/repo-controls/src/cli.ts +29 -8
  391. package/packages/repo-controls/src/compliance-config.ts +410 -2
  392. package/packages/repo-controls/src/config.ts +133 -60
  393. package/packages/repo-controls/src/constants.ts +13 -27
  394. package/packages/repo-controls/src/coverage-ratchet.ts +4 -4
  395. package/packages/repo-controls/src/doctor.ts +46 -19
  396. package/packages/repo-controls/src/gitignore-block.ts +5 -29
  397. package/packages/repo-controls/src/hook-log.ts +1 -2
  398. package/packages/repo-controls/src/hook.ts +201 -45
  399. package/packages/repo-controls/src/install.ts +14 -8
  400. package/packages/repo-controls/src/integrity.ts +3 -3
  401. package/packages/repo-controls/src/lfs.ts +111 -0
  402. package/packages/repo-controls/src/promote.ts +8 -8
  403. package/packages/repo-controls/src/runtime.ts +9 -1
  404. package/packages/repo-controls/src/workflows-drift.ts +4 -5
  405. package/packages/repo-controls/src/worktree-guard.ts +3 -3
package/bin/reconcile CHANGED
@@ -110,9 +110,7 @@ failed_tools=()
110
110
  # block at the very end, just before "global-tools: reconciliation complete"
111
111
  # (Tarun, 2026-09-28). Collected from every step that backs something up
112
112
  # before overwriting/removing a file: kimi-hooks (parsed from its own
113
- # captured "backed up previous config to <path>" note), lean-ctx-observe-strip
114
- # (2026-09-29, STATUS.md 12p item 13 final scope -- same note shape, parsed
115
- # by collect_lean_ctx_observe_strip_backups), the global surface sweep
113
+ # captured "backed up previous config to <path>" note), the global surface sweep
116
114
  # (parsed from ITS OWN already-printed "global-surface-sweep: backups
117
115
  # this run:" block -- never recomputed here, per Tarun's instruction not to
118
116
  # duplicate that package's backup logic), and write_one_repo_script below
@@ -157,39 +155,6 @@ collect_kimi_hooks_backups() {
157
155
  done <<<"$text"
158
156
  }
159
157
 
160
- # Same shape as collect_kimi_hooks_backups, for bin/lean-ctx-observe-strip's
161
- # own "lean-ctx-observe-strip: backed up previous config to <path>" note --
162
- # unlike kimi-hooks (always exactly one config file), this step can strip
163
- # multiple JSON/shell-rc targets in one run, so the loop below (like
164
- # collect_sweep_backups) records every match, not just the first.
165
- #
166
- # A skill-directory backup gets a SECOND, distinct line shape: "backed up
167
- # previous skill directory <original> to <backup>" (final review finding
168
- # 1: its backup path is a SIBLING of the scanned skills/ directory, e.g.
169
- # ".codex/skills.pre-lean-ctx-observe-strip-<ts>/lean-ctx" -- it no longer
170
- # ends in ".pre-<label>-<timestamp>", so strip_backup_suffix's regex can't
171
- # recover the original path from it; that note states BOTH paths directly
172
- # instead, and is matched here as its own case rather than forced through
173
- # the single-path regex above).
174
- collect_lean_ctx_observe_strip_backups() {
175
- local text="$1" line backup original
176
- while IFS= read -r line; do
177
- if [[ "$line" =~ ^lean-ctx-observe-strip:\ backed\ up\ previous\ config\ to\ (.+)$ ]]; then
178
- backup="${BASH_REMATCH[1]}"
179
- record_backup "$(strip_backup_suffix "$backup")" "$backup"
180
- elif [[ "$line" =~ ^lean-ctx-observe-strip:\ backed\ up\ previous\ skill\ directory\ (.+)\ to\ (.+)$ ]]; then
181
- original="${BASH_REMATCH[1]}"
182
- backup="${BASH_REMATCH[2]}"
183
- record_backup "$original" "$backup"
184
- elif [[ "$line" =~ ^lean-ctx-observe-strip:\ backed\ up\ previous\ hook\ script\ (.+)\ to\ (.+)$ ]]; then
185
- # ~/.claude/hooks/lean-ctx-* scripts: same two-path shape as a skill
186
- # directory (sibling "hooks.pre-lean-ctx-observe-strip-<ts>/" backup).
187
- original="${BASH_REMATCH[1]}"
188
- backup="${BASH_REMATCH[2]}"
189
- record_backup "$original" "$backup"
190
- fi
191
- done <<<"$text"
192
- }
193
158
 
194
159
  # Folds the global-surface-sweep's OWN already-computed and already-printed
195
160
  # backup lines into this run's combined summary, without recomputing
@@ -223,7 +188,9 @@ repo_root="$(cd "$script_dir/.." && pwd)"
223
188
  manifest="${manifest:-$repo_root/packages/fleet/manifest.json}"
224
189
  # shellcheck source=lib/bun-compile-build.sh
225
190
  source "$script_dir/lib/bun-compile-build.sh"
226
- "$script_dir/validate-manifest" "$manifest" >/dev/null
191
+ # shellcheck source=lib/adl-package.sh
192
+ source "$script_dir/lib/adl-package.sh"
193
+ "$BASH" "$script_dir/validate-manifest" "$manifest" >/dev/null
227
194
  if [[ "${RECONCILE_SOURCE_ONLY:-0}" != "1" ]]; then
228
195
  if [[ "$source_mode" == npm && -e "$repo_root/.git" ]]; then
229
196
  echo "ERROR npm source mode requires an installed package root; use --source checkout for a Git checkout" >&2
@@ -331,9 +298,24 @@ run_brew() {
331
298
  return 0
332
299
  fi
333
300
  [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
301
+ if [[ -n "${brew_noninteractive_dir:-}" ]]; then
302
+ # The upgrade step: no sudo Homebrew spawns may prompt (see run_brew_upgrade_all).
303
+ (cd /tmp && sudo -u "$brew_owner" -H env -u SUDO_ASKPASS NONINTERACTIVE=1 HOMEBREW_NO_SUDO=1 HOMEBREW_NO_ENV_HINTS=1 PATH="$brew_noninteractive_dir:${brew_bin%/*}:/usr/bin:/bin" "$brew_bin" "$@" </dev/null)
304
+ return
305
+ fi
334
306
  (cd /tmp && sudo -u "$brew_owner" -H env NONINTERACTIVE=1 PATH="${brew_bin%/*}:/usr/bin:/bin" "$brew_bin" "$@" </dev/null)
335
307
  }
336
308
 
309
+ # A directory holding a `sudo` that can never prompt: it execs /usr/bin/sudo -n. Put first on the
310
+ # PATH of the brew upgrade step, for anything that calls sudo by name.
311
+ make_brew_noninteractive_shim() {
312
+ local dir
313
+ dir="$(mktemp -d "${TMPDIR:-/tmp}/adl-brew-sudo-shim.XXXXXX")" || return 1
314
+ printf '#!/bin/sh\nexec /usr/bin/sudo -n "$@"\n' >"$dir/sudo"
315
+ chmod 0755 "$dir" "$dir/sudo"
316
+ printf '%s\n' "$dir"
317
+ }
318
+
337
319
  # Portable command timeout: neither GNU coreutils' `timeout` nor its `gtimeout`
338
320
  # alias is installed by default on a fresh mac (this manifest installs
339
321
  # neither), so a real `command -v timeout` gate would silently do nothing on
@@ -383,52 +365,126 @@ run_with_timeout() {
383
365
  BREW_UPGRADE_ALL_TIMEOUT_SECONDS="${RECONCILE_BREW_UPGRADE_TIMEOUT_SECONDS:-1200}"
384
366
  SURFACE_UPDATE_TIMEOUT_SECONDS="${RECONCILE_SURFACE_UPDATE_TIMEOUT_SECONDS:-300}"
385
367
 
386
- # Upgrades every outdated Homebrew formula AND cask in one call -- never a
387
- # per-package list, so a formula outdated for reasons unrelated to this
388
- # manifest (installed once by hand, or a transitive dependency of a manifest
389
- # formula) still converges instead of accumulating outdated versions forever
390
- # (item 1, 2026-09-28: `brew update` alone, the only brew-wide step that ran
391
- # before this, never upgrades anything). Gated by the manifest's own
392
- # brew_upgrade_all switch at the call site below, not inside this function --
393
- # this function always does the real (or plan-mode preview) work when called.
368
+ # brew's output for a cask whose upgrade needed sudo but was not allowed to ask: Homebrew refusing
369
+ # ("sudo is disabled by HOMEBREW_NO_SUDO."), or sudo -n failing fast. It never blocks.
370
+ brew_output_needs_password() {
371
+ grep -Eiq 'sudo is disabled by HOMEBREW_NO_SUDO|sudo: .*(password|terminal|tty|askpass)|^Password:' <<<"$1"
372
+ }
373
+
374
+ # Upgrades the outdated Homebrew formulae and casks, from ONE `brew outdated --json=v2` (the
375
+ # formulae and casks, with installed and available versions). A formula outdated for reasons
376
+ # unrelated to this manifest (installed once by hand, or a transitive dependency of a manifest
377
+ # formula) still converges instead of accumulating outdated versions forever (item 1,
378
+ # 2026-09-28: `brew update` alone, the only brew-wide step that ran before this, never upgrades
379
+ # anything). Gated by the manifest's own brew_upgrade_all switch at the call site below, not
380
+ # inside this function -- this function always does the real (or plan-mode preview) work when
381
+ # called.
382
+ #
383
+ # Two rules from the live tests (Tarun, 2026-10-01):
384
+ # - config.brew_upgrade_exclude names packages that are never upgraded here (git: every Homebrew
385
+ # git upgrade installs an ad-hoc-signed binary at a new Cellar path and re-prompts the
386
+ # osxkeychain ACL). Not a `brew pin`, not a version pin: an outdated excluded package is one
387
+ # info line, and the rest is upgraded BY NAME.
388
+ # - a reconcile NEVER prompts, terminal or not (m5, 2026-10-01: an interactive `sudo adl setup`
389
+ # got a nested, echoing `Password:` from the visual-studio-code cask's sudo inside brew inside
390
+ # our sudo -u). Every brew call here runs with stdin /dev/null, SUDO_ASKPASS unset,
391
+ # HOMEBREW_NO_SUDO=1 (brew refuses before spawning sudo) and a PATH-shimmed `sudo -n`. Casks are
392
+ # upgraded one at a time; one that needs a password is reported as a WARN with the command to
393
+ # run by hand and is not a failed tool. A failing formula, or a cask that fails for any other
394
+ # reason, still fails loud.
394
395
  run_brew_upgrade_all() {
396
+ if [[ "$mode" != apply ]]; then
397
+ run_brew_upgrade_all_body
398
+ return
399
+ fi
400
+ local status=0
401
+ brew_noninteractive_dir="$(make_brew_noninteractive_shim)" || { echo "ERROR could not create the brew sudo shim" >&2; return 1; }
402
+ run_brew_upgrade_all_body || status=$?
403
+ command rm -rf -- "$brew_noninteractive_dir"
404
+ brew_noninteractive_dir=""
405
+ return "$status"
406
+ }
407
+
408
+ run_brew_upgrade_all_body() {
395
409
  if [[ "$mode" != apply ]]; then
396
410
  echo "would run: brew upgrade (all outdated formulae and casks)"
411
+ local planned_excludes
412
+ planned_excludes="$(jq -r '(.config.brew_upgrade_exclude // []) | join(", ")' "$manifest")"
413
+ if [[ -n "$planned_excludes" ]]; then
414
+ echo "brew upgrade excludes (config.brew_upgrade_exclude): $planned_excludes"
415
+ fi
397
416
  return 0
398
417
  fi
399
418
  [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
400
- local outdated_formulae outdated_casks formulae_count casks_count
401
- outdated_formulae="$(brew_query outdated --formula --quiet)" || {
402
- echo "ERROR brew outdated --formula check failed" >&2
419
+ local outdated_json excluded_names line kind name installed available
420
+ local -a formulae=() casks=()
421
+ local skipped=0
422
+ outdated_json="$(brew_query outdated --json=v2)" || {
423
+ echo "ERROR brew outdated check failed" >&2
403
424
  return 1
404
425
  }
405
- outdated_casks="$(brew_query outdated --cask --quiet)" || {
406
- echo "ERROR brew outdated --cask check failed" >&2
426
+ jq -e '(.formulae | type == "array") and (.casks | type == "array")' >/dev/null 2>&1 <<<"$outdated_json" || {
427
+ echo "ERROR brew outdated --json=v2 did not print formulae and casks" >&2
407
428
  return 1
408
429
  }
409
- formulae_count=0
410
- [[ -z "$outdated_formulae" ]] || formulae_count="$(wc -l <<<"$outdated_formulae" | tr -d ' ')"
411
- casks_count=0
412
- [[ -z "$outdated_casks" ]] || casks_count="$(wc -l <<<"$outdated_casks" | tr -d ' ')"
430
+ excluded_names="$(jq -r '(.config.brew_upgrade_exclude // [])[]' "$manifest")"
431
+ while IFS=$'\t' read -r kind name installed available; do
432
+ [[ -n "$name" ]] || continue
433
+ if [[ -n "$excluded_names" ]] && grep -Fxq -- "$name" <<<"$excluded_names"; then
434
+ echo "global-tools: brew upgrade skips excluded: $name ($installed → $available)"
435
+ skipped=$((skipped + 1))
436
+ continue
437
+ fi
438
+ if [[ "$kind" == formula ]]; then formulae+=("$name"); else casks+=("$name"); fi
439
+ done < <(jq -r '
440
+ def versions: if type == "array" then join(", ") else tostring end;
441
+ (.formulae[]? | ["formula", .name, (.installed_versions | versions), .current_version]),
442
+ (.casks[]? | ["cask", .name, (.installed_versions | versions), .current_version]) | @tsv
443
+ ' <<<"$outdated_json")
444
+ local formulae_count="${#formulae[@]}" casks_count="${#casks[@]}"
413
445
  if [[ "$formulae_count" -eq 0 && "$casks_count" -eq 0 ]]; then
414
- echo "brew upgrade: nothing outdated"
446
+ if [[ "$skipped" -gt 0 ]]; then
447
+ echo "brew upgrade: nothing left to upgrade after $skipped excluded"
448
+ else
449
+ echo "brew upgrade: nothing outdated"
450
+ fi
415
451
  return 0
416
452
  fi
417
- local upgrade_status=0
418
453
  # Routed through run_brew itself (same owner/cwd/stdin wiring as every
419
454
  # other brew mutation in this file), bounded by run_with_timeout so a
420
455
  # network-stalled `brew upgrade` cannot hang the rest of this run forever.
421
456
  # brew's output streams live -- never captured and printed at the end,
422
457
  # which left a long upgrade silent for minutes and looking stuck.
423
458
  echo "brew upgrade: $formulae_count formulae, $casks_count casks outdated; upgrading (timeout ${BREW_UPGRADE_ALL_TIMEOUT_SECONDS}s)"
424
- run_with_timeout "$BREW_UPGRADE_ALL_TIMEOUT_SECONDS" run_brew upgrade 2>&1 || upgrade_status=$?
425
- if [[ "$upgrade_status" -eq 124 ]]; then
426
- echo "ERROR brew upgrade timed out after ${BREW_UPGRADE_ALL_TIMEOUT_SECONDS}s" >&2
427
- return 1
428
- elif [[ "$upgrade_status" -ne 0 ]]; then
429
- echo "ERROR brew upgrade failed (exit $upgrade_status)" >&2
430
- return 1
459
+ local upgrade_status=0
460
+ local failed=0 cask cask_output
461
+ if [[ "$formulae_count" -gt 0 ]]; then
462
+ run_with_timeout "$BREW_UPGRADE_ALL_TIMEOUT_SECONDS" run_brew upgrade --formula "${formulae[@]}" 2>&1 || upgrade_status=$?
463
+ if [[ "$upgrade_status" -eq 124 ]]; then
464
+ echo "ERROR brew upgrade timed out after ${BREW_UPGRADE_ALL_TIMEOUT_SECONDS}s" >&2
465
+ return 1
466
+ elif [[ "$upgrade_status" -ne 0 ]]; then
467
+ echo "ERROR brew upgrade failed (exit $upgrade_status)" >&2
468
+ failed=1
469
+ fi
431
470
  fi
471
+ for cask in ${casks[@]+"${casks[@]}"}; do
472
+ upgrade_status=0
473
+ cask_output="$(run_with_timeout "$BREW_UPGRADE_ALL_TIMEOUT_SECONDS" run_brew upgrade --cask "$cask" 2>&1)" || upgrade_status=$?
474
+ [[ -z "$cask_output" ]] || printf '%s\n' "$cask_output"
475
+ if [[ "$upgrade_status" -eq 124 ]]; then
476
+ echo "ERROR brew upgrade timed out after ${BREW_UPGRADE_ALL_TIMEOUT_SECONDS}s ($cask)" >&2
477
+ return 1
478
+ elif [[ "$upgrade_status" -ne 0 ]]; then
479
+ if brew_output_needs_password "$cask_output"; then
480
+ echo "WARN brew: $cask needs an interactive password to upgrade; run: brew upgrade --cask $cask" >&2
481
+ else
482
+ echo "ERROR brew upgrade failed (exit $upgrade_status): $cask" >&2
483
+ failed=1
484
+ fi
485
+ fi
486
+ done
487
+ [[ "$failed" -eq 0 ]] || return 1
432
488
  echo "brew upgrade: $formulae_count formulae, $casks_count casks were outdated before upgrade"
433
489
  return 0
434
490
  }
@@ -582,6 +638,22 @@ npm_package_owns_executable() {
582
638
  }
583
639
  }
584
640
 
641
+ # The published package is installed with bun (Tarun, 2026-10-01). Package mode
642
+ # proves the administrator's `adl` IS this package (bin/lib/adl-package.sh, the
643
+ # check bin/setup runs too): a different adl earlier on the administrator's PATH
644
+ # -- a legacy compiled /usr/local/bin/adl from before the package existed -- would
645
+ # keep running instead of the package this job updates, so it fails loud here with
646
+ # the one-line fix instead of being silently tolerated.
647
+ adl_package_owns_executable() {
648
+ local os package_real
649
+ case "$platform" in
650
+ mac) os=Darwin ;;
651
+ *) os=Linux ;;
652
+ esac
653
+ package_real="$(cd -P -- "$repo_root" && pwd -P)" || return 1
654
+ adl_check_adl_not_shadowed "$administrator" "$os" "$administrator_home" "$package_real" "global-tools"
655
+ }
656
+
585
657
  run_pip() {
586
658
  if [[ "$mode" != apply ]]; then
587
659
  printf 'would run pip as %s: python3 -m pip' "$administrator"
@@ -752,7 +824,7 @@ run_embedding_model() {
752
824
  local package="$1"
753
825
  local spec_json="$2"
754
826
  if [[ "$mode" != apply ]]; then
755
- printf 'would install embedding model: %s as %s -> ~/.cache/adl-embedding/model\n' "$package" "$administrator"
827
+ printf 'would install embedding model: %s as %s -> ~/.cache/enact-embedding/model\n' "$package" "$administrator"
756
828
  return 0
757
829
  fi
758
830
  [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
@@ -763,9 +835,8 @@ run_embedding_model() {
763
835
 
764
836
  # Installs $src as $dest via a same-directory temp-file + rename, never a
765
837
  # direct in-place write -- so a concurrent reader (including the next agent
766
- # shell's `command -v $dest`, per the lean-ctx incident this fixed: an
767
- # in-place npm postinstall rewrite left the shim executing but printing
768
- # "binary not found" for ~44s on every scheduled reconcile) can never observe
838
+ # shell's `command -v $dest`, which could otherwise observe an executable
839
+ # partway through an in-place rewrite) can never observe
769
840
  # $dest existing but only partially written. `mv` within the same directory
770
841
  # is a single rename(2) on both Darwin and Linux -- atomic by construction,
771
842
  # not by scheduling luck.
@@ -796,8 +867,8 @@ install_surface_policy_file() {
796
867
 
797
868
  # A resolved release version against the first line of a release-archive-
798
869
  # latest tool's own `--version` output. Different tools format that line
799
- # differently -- lean-ctx: "lean-ctx 3.10.5 (official, ...)" (version padded
800
- # by spaces on both sides); agent-vault: "agent-vault version 0.39.3" (flush
870
+ # differently -- some include parenthetical metadata; agent-vault prints
871
+ # "agent-vault version 0.39.3" (flush
801
872
  # against the end of the line, no trailing space); a plain "vX.Y.Z" line
802
873
  # with no separating space before the version at all -- so the LEFT boundary
803
874
  # is a string start or a real space, with an OPTIONAL literal "v" directly
@@ -927,7 +998,7 @@ resolve_surface_mode() {
927
998
  # root") is a real environment fault, not a flaky install, and still
928
999
  # aborts immediately -- `exit` is unconditional and is never caught here.
929
1000
  install_one_tool() {
930
- local tool_id="$1" manager package executable previous_executable migrating_from_bun=0 rc=0 npm_spec npm_allow_scripts npm_target_version npm_installed_ver brew_outdated_output brew_installed_version
1001
+ local tool_id="$1" manager package executable previous_executable migrating_from_bun=0 rc=0 npm_spec npm_allow_scripts npm_target_version npm_installed_ver brew_outdated_output brew_installed_version trust_kind
931
1002
  manager="$(jq -r --arg id "$tool_id" '.[$id].manager' "$resolved_file")"
932
1003
  package="$(jq -r --arg id "$tool_id" '.[$id].package' "$resolved_file")"
933
1004
  executable="$(jq -r --arg id "$tool_id" '.[$id].executable' "$resolved_file")"
@@ -941,19 +1012,76 @@ install_one_tool() {
941
1012
  fi
942
1013
  if [[ "$tool_id" == adl && "$source_mode" == npm ]]; then
943
1014
  if [[ "$mode" != apply ]]; then
944
- echo "would use installed npm package payload: $repo_root"
1015
+ echo "would use installed package payload: $repo_root"
945
1016
  return 0
946
1017
  fi
947
1018
  if is_in_place_bun_to_npm_migration "$tool_id"; then
948
1019
  prepare_in_place_bun_to_npm_migration "$tool_id" || return 1
949
1020
  fi
950
- npm_package_owns_executable "$package" "$executable" || return 1
951
- echo "using installed npm package payload: $repo_root"
1021
+ adl_package_owns_executable || return 1
1022
+ echo "using installed package payload: $repo_root"
952
1023
  return 0
953
1024
  fi
954
1025
  echo "ensure latest stable: $tool_id ($manager:$package)"
955
1026
  case "$manager" in
956
1027
  brew)
1028
+ # Optional declared tap (manifest .tap, owner/repo): fully-qualified
1029
+ # package names (e.g. stablyai/orca/orca) refuse to resolve until their
1030
+ # tap exists, and Homebrew will not auto-tap a non-core tap for a
1031
+ # noninteractive caller — so reconcile taps it first, idempotently.
1032
+ # Tarun 2026-10-02: orca is brew-managed from Stably's own tap; that
1033
+ # cask is auto_updates, so the per-tool path installs-if-missing and
1034
+ # the in-app updater owns versioning — no version is ever pinned here.
1035
+ tap="$(jq -r --arg id "$tool_id" '.[$id].tap // ""' "$resolved_file")"
1036
+ if [[ "$rc" -eq 0 && -n "$tap" ]]; then
1037
+ [[ "$tap" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || {
1038
+ echo "ERROR refusing unsafe brew tap name for $tool_id: $tap" >&2
1039
+ rc=1
1040
+ }
1041
+ fi
1042
+ if [[ "$rc" -eq 0 && -n "$tap" && "$mode" == apply ]]; then
1043
+ brew_query tap | grep -Fqx "$tap" || run_brew tap "$tap" || {
1044
+ echo "ERROR $tool_id: brew tap $tap failed" >&2
1045
+ rc=1
1046
+ }
1047
+ fi
1048
+ # Homebrew >=6 tap trust (brew 7.0.7 on this fleet; the 2026-10 orca
1049
+ # install failure was exactly this refusal): a command that must load an
1050
+ # untrusted non-core tap FAILS until trust is granted, noninteractively
1051
+ # or not. Grant ITEM-level trust for the declared package only --
1052
+ # brew's own automation guidance is `brew trust --formula|--cask
1053
+ # owner/repo/item`, never the whole tap. Gated on the same declared
1054
+ # .tap as the tap step above: that manifest field is the reviewed trust
1055
+ # boundary, and a bare core package (curl, ripgrep) needs no grant.
1056
+ if [[ "$rc" -eq 0 && -n "$tap" && "$mode" == apply ]]; then
1057
+ # The kind is resolved, never guessed: a fully-qualified name is
1058
+ # loadable exactly as one of formula/cask once its tap exists.
1059
+ if brew_query info --formula "$package" >/dev/null 2>&1; then
1060
+ trust_kind=formula
1061
+ elif brew_query info --cask "$package" >/dev/null 2>&1; then
1062
+ trust_kind=cask
1063
+ else
1064
+ echo "ERROR $tool_id: cannot determine whether $package is a formula or a cask — refusing to guess its trust kind" >&2
1065
+ rc=1
1066
+ fi
1067
+ # `brew trust` (list) prints each granted item on its own line,
1068
+ # indented under a "Trusted formulae:"/"Trusted casks:" header
1069
+ # (verified on brew 7.0.7) -- trim, then exact-line match so a
1070
+ # whole-tap grant ("Trusted taps: owner/repo") can never satisfy the
1071
+ # item check.
1072
+ if [[ "$rc" -eq 0 ]]; then
1073
+ brew_query trust | awk '{$1=$1};1' | grep -Fqx "$package" || run_brew trust "--$trust_kind" "$package" || {
1074
+ echo "ERROR $tool_id: brew trust --$trust_kind $package failed" >&2
1075
+ rc=1
1076
+ }
1077
+ fi
1078
+ elif [[ "$rc" -eq 0 && -n "$tap" ]]; then
1079
+ # Plan mode: brew_query is apply-only (see its own header comment),
1080
+ # so the kind is not resolved for a preview -- name the grant anyway.
1081
+ echo "would trust Homebrew item: $package (formula|cask)"
1082
+ fi
1083
+ if [[ "$rc" -ne 0 ]]; then
1084
+ :
957
1085
  # Plan mode's message is intentionally unconditional (see brew_query's
958
1086
  # own comment for why: a real up-to-date check is slow enough on a
959
1087
  # loaded box to defeat the point of a quick preview). Apply mode pays
@@ -961,7 +1089,7 @@ install_one_tool() {
961
1089
  # run `brew upgrade` on an already-current formula -- which brew itself
962
1090
  # accepts silently (prints "Warning: <pkg> <ver> already installed" and
963
1091
  # exits 0), the exact per-run noise this replaces.
964
- if [[ "$mode" != apply ]]; then
1092
+ elif [[ "$mode" != apply ]]; then
965
1093
  echo "would install or upgrade Homebrew formula: $package"
966
1094
  elif brew_query list --formula "$package" >/dev/null 2>&1; then
967
1095
  if brew_outdated_output="$(brew_query outdated --formula --quiet "$package")"; then
@@ -1054,10 +1182,8 @@ install_one_tool() {
1054
1182
  latest_tag="$(resolve_latest_release_tag "$repo")" || { rc=1; latest_tag=""; }
1055
1183
  if [[ "$rc" -eq 0 ]]; then
1056
1184
  latest_version="${latest_tag#v}"
1057
- # A release whose asset filename bakes in the version (agent-vault:
1058
- # agent-vault_<ver>_darwin_arm64.tar.gz) declares "{version}" in
1059
- # .asset/.checksums_asset; lean-ctx's own asset names carry no
1060
- # version at all, so this is a no-op substring replace for it.
1185
+ # A release whose asset filename bakes in the version declares
1186
+ # "{version}" in .asset/.checksums_asset; otherwise this is a no-op.
1061
1187
  resolved_asset="${asset//\{version\}/$latest_version}"
1062
1188
  resolved_checksums_asset="${checksums_asset//\{version\}/$latest_version}"
1063
1189
  if [[ -x "$executable" ]] &&
@@ -1129,10 +1255,17 @@ prepare_in_place_bun_to_npm_migration() {
1129
1255
  if [[ -L "$migration_path" ]]; then
1130
1256
  package="$(jq -r --arg id "$tool_id" '.[$id].package' "$resolved_file")"
1131
1257
  executable="$(jq -r --arg id "$tool_id" '.[$id].executable' "$resolved_file")"
1132
- npm_package_owns_executable "$package" "$executable" || {
1133
- echo "ERROR refusing to remove an executable symlink not owned by npm: $previous_executable" >&2
1134
- return 1
1135
- }
1258
+ if [[ "$tool_id" == adl && "$source_mode" == npm ]]; then
1259
+ adl_package_owns_executable || {
1260
+ echo "ERROR refusing to remove an executable symlink not owned by the installed package: $previous_executable" >&2
1261
+ return 1
1262
+ }
1263
+ else
1264
+ npm_package_owns_executable "$package" "$executable" || {
1265
+ echo "ERROR refusing to remove an executable symlink not owned by npm: $previous_executable" >&2
1266
+ return 1
1267
+ }
1268
+ fi
1136
1269
  hash_file="$(bun_source_hash_file "$tool_id")"
1137
1270
  rm -f -- "$hash_file" || return 1
1138
1271
  echo "retained npm-owned executable and removed retired Bun marker: $previous_executable ($hash_file)"
@@ -1214,7 +1347,7 @@ build_checkout_adl() {
1214
1347
  return "$rc"
1215
1348
  }
1216
1349
 
1217
- # embedding-model installs the adl-embedding static (Model2Vec) artifact
1350
+ # embedding-model installs the enact-embedding static (Model2Vec) artifact
1218
1351
  # that adl hook's (the folded-in hook engine's) skill-match hook loads
1219
1352
  # from the administrator's cache.
1220
1353
  # It runs as the declared administrator (root on Linux) so the hook and
@@ -1284,7 +1417,7 @@ managed_repo_script_marker='# Managed by adl global-tools.'
1284
1417
  # real, previously-self-installed files as "unmanaged", so apply mode refused
1285
1418
  # to update them ("refusing to replace unmanaged or symlinked managed script
1286
1419
  # target") instead of replacing them with the renamed source -- seen live on
1287
- # M3's `fleet-cutover.sh --apply` for /usr/local/bin/git and
1420
+ # M3's `packages/fleet/scripts/fleet-cutover.sh --apply` for /usr/local/bin/git and
1288
1421
  # /usr/local/bin/git-ado. A target carrying EITHER line is this writer's own
1289
1422
  # prior output and must still be treated as managed; a target carrying
1290
1423
  # neither (or a symlink) must keep being refused.
@@ -1325,8 +1458,10 @@ write_one_repo_script() {
1325
1458
  return 1
1326
1459
  }
1327
1460
  source_path="$repo_root/$source"
1328
- [[ -f "$source_path" && ! -L "$source_path" && -x "$source_path" ]] || {
1329
- echo "ERROR managed script source must be an executable regular non-symlink file: $source_path" >&2
1461
+ # -f, not -x: bun leaves every shipped file but the `bin` targets 0644, and the
1462
+ # copy below is installed 0755.
1463
+ [[ -f "$source_path" && ! -L "$source_path" ]] || {
1464
+ echo "ERROR managed script source must be a regular non-symlink file: $source_path" >&2
1330
1465
  return 1
1331
1466
  }
1332
1467
  if [[ "$mode" != apply ]]; then
@@ -1681,6 +1816,27 @@ executable_claimed_by_current_manifest() {
1681
1816
  # into its own function, like install_one_tool, so
1682
1817
  # packages/fleet/tests/test-manifest.sh can exercise it directly through the
1683
1818
  # RECONCILE_SOURCE_ONLY seam instead of only through a real apply run.
1819
+ remove_release_archive_executable() {
1820
+ local executable="$1" label="$2" target="$executable"
1821
+ [[ "$executable" =~ ^/usr/local/bin/[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || {
1822
+ echo "ERROR refusing unsafe release archive removal target: $executable" >&2
1823
+ return 1
1824
+ }
1825
+ if [[ "${RECONCILE_SOURCE_ONLY:-0}" == 1 && -n "${RECONCILE_TEST_COMMAND_ROOT:-}" ]]; then
1826
+ target="${RECONCILE_TEST_COMMAND_ROOT%/}/${executable##*/}"
1827
+ fi
1828
+ if [[ "$mode" == apply ]]; then
1829
+ rm -f -- "$target" || return 1
1830
+ [[ ! -e "$target" && ! -L "$target" ]] || {
1831
+ echo "ERROR release archive executable remains after removal: $target" >&2
1832
+ return 1
1833
+ }
1834
+ echo "removed $label release executable: $executable"
1835
+ else
1836
+ echo "would remove $label release executable: $executable"
1837
+ fi
1838
+ }
1839
+
1684
1840
  remove_one_manifest_entry() {
1685
1841
  local tool_id="$1" manager package executable removed_repo_dir_target
1686
1842
  if jq -e --arg id "$tool_id" '.config.protected_tools | index($id)' "$manifest" >/dev/null; then
@@ -1735,26 +1891,10 @@ remove_one_manifest_entry() {
1735
1891
  # still cleanly uninstall it the next time it reconciles; previous
1736
1892
  # state is read straight from disk, never re-validated.
1737
1893
  release-archive)
1738
- [[ "$executable" == /usr/local/bin/* ]] || {
1739
- echo "ERROR refusing unsafe release archive removal target: $executable" >&2
1740
- exit 1
1741
- }
1742
- if [[ "$mode" == apply ]]; then
1743
- rm -f -- "$executable"
1744
- else
1745
- echo "would remove pinned release executable: $executable"
1746
- fi
1894
+ remove_release_archive_executable "$executable" pinned
1747
1895
  ;;
1748
1896
  release-archive-latest)
1749
- [[ "$executable" == /usr/local/bin/* ]] || {
1750
- echo "ERROR refusing unsafe release archive removal target: $executable" >&2
1751
- exit 1
1752
- }
1753
- if [[ "$mode" == apply ]]; then
1754
- rm -f -- "$executable"
1755
- else
1756
- echo "would remove latest release executable: $executable"
1757
- fi
1897
+ remove_release_archive_executable "$executable" latest
1758
1898
  ;;
1759
1899
  shim)
1760
1900
  [[ "$executable" == /usr/local/bin/* ]] || {
@@ -1808,29 +1948,13 @@ remove_one_manifest_entry() {
1808
1948
  (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
1809
1949
  bun "$repo_root/packages/fleet/src/install-embedding-model.ts" uninstall </dev/null)
1810
1950
  else
1811
- echo "would remove embedding model cache: ~/.cache/adl-embedding/model (as $administrator)"
1951
+ echo "would remove embedding model cache: ~/.cache/enact-embedding/model (as $administrator)"
1812
1952
  fi
1813
1953
  ;;
1814
1954
  *) echo "ERROR unsupported manager in previous manifest: $manager" >&2; exit 1 ;;
1815
1955
  esac
1816
1956
  }
1817
1957
 
1818
- # config.git_global_config: global git config for the administrator (the
1819
- # account whose HOME the per-user deployers below also write to). Apply runs git as
1820
- # that user (sudo -u -H) so ~/.gitconfig stays user-owned; plan/audit only
1821
- # read, as whoever invoked reconcile. Motivating entries: Azure DevOps SSH
1822
- # has no Git LFS support, and git-lfs's default pure-SSH `git-lfs-transfer`
1823
- # probe hangs forever against ssh.dev.azure.com, stalling every `git push` in
1824
- # the LFS pre-push hook.
1825
- run_git_config_global() {
1826
- if [[ "$mode" == apply ]]; then
1827
- [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
1828
- (cd /tmp && sudo -u "$git_config_user" -H git config --global "$@" </dev/null)
1829
- else
1830
- git config --global "$@" </dev/null
1831
- fi
1832
- }
1833
-
1834
1958
  # One-release migration for Linux state written by the retired two-user
1835
1959
  # contract. The previous applied manifest is the authority for the former
1836
1960
  # account and home, so no legacy identity remains configured after a successful
@@ -1875,49 +1999,18 @@ cleanup_legacy_linux_git_config() {
1875
1999
  done
1876
2000
  }
1877
2001
 
1878
- # Prints the current global value of $1, or nothing when unset. git config
1879
- # --get exits 1 for a missing key; any other failure is real and propagates.
1880
- git_global_config_value() {
1881
- local key="$1" value rc=0
1882
- value="$(run_git_config_global --get "$key")" || rc=$?
1883
- if [[ "$rc" -eq 1 ]]; then
1884
- return 0
1885
- fi
1886
- [[ "$rc" -eq 0 ]] || { echo "ERROR git config --global --get $key failed (exit $rc)" >&2; return "$rc"; }
1887
- printf '%s\n' "$value"
1888
- }
1889
-
2002
+ # config.git_global_config + <platform>.common.git_global_config + the device's
2003
+ # git_user: the administrator's global git config, identical on every host
2004
+ # (Tarun, 2026-10-01). All logic lives in bin/git-defaults; undeclared keys are
2005
+ # never touched. Motivating entries: Azure DevOps SSH has no Git LFS support
2006
+ # (git-lfs's pure-SSH probe hangs against ssh.dev.azure.com), and GCM is the
2007
+ # credential helper on every host.
1890
2008
  reconcile_git_global_config() {
1891
- local key value current
1892
- while IFS=$'\t' read -r key value; do
1893
- [[ -n "$key" ]] || continue
1894
- current="$(git_global_config_value "$key")" || return 1
1895
- if [[ "$current" == "$value" ]]; then
1896
- echo "global git config already set: $key=$value"
1897
- elif [[ "$mode" != apply ]]; then
1898
- echo "would set global git config: $key=$value"
1899
- else
1900
- run_git_config_global "$key" "$value" || return 1
1901
- echo "set global git config: $key=$value"
1902
- fi
1903
- done < <(jq -r '.config.git_global_config | to_entries[] | [.key, .value] | @tsv' "$manifest")
1904
-
1905
- [[ "$have_previous" -eq 1 ]] || return 0
1906
- while IFS= read -r key; do
1907
- [[ -n "$key" ]] || continue
1908
- current="$(git_global_config_value "$key")" || return 1
1909
- [[ -n "$current" ]] || continue
1910
- if [[ "$mode" != apply ]]; then
1911
- echo "would unset global git config (explicit manifest deletion): $key"
1912
- else
1913
- run_git_config_global --unset-all "$key" || return 1
1914
- echo "explicit manifest deletion: unset global git config $key"
1915
- fi
1916
- done < <(jq -r --slurpfile current "$manifest" '
1917
- (.config.git_global_config // {}) | keys[] as $key
1918
- | select(($current[0].config.git_global_config | has($key)) | not)
1919
- | $key
1920
- ' "$applied_manifest")
2009
+ if [[ "$mode" == apply ]]; then
2010
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
2011
+ fi
2012
+ "$BASH" "$repo_root/bin/git-defaults" --manifest "$manifest" --platform "$platform" \
2013
+ --device "$device" --mode "$mode" --administrator "$git_config_user"
1921
2014
  }
1922
2015
 
1923
2016
  # default-agent-file.ts and managed-settings.ts (Claude/Codex) MUST run
@@ -1946,9 +2039,11 @@ require_bun_for_identity_files() {
1946
2039
  # The PATH searched for a surface command intentionally extends past the
1947
2040
  # usual brew/system directories into the per-user install locations these
1948
2041
  # specific CLIs' own installers use (claude's and cursor's native installers,
1949
- # kimi's and opencode's curl-script installers) -- none of those are on the
1950
- # plain system PATH this file uses elsewhere for brew/npm/pip.
1951
- surface_cli_search_path="$administrator_home/.local/bin:$administrator_home/.kimi-code/bin:$administrator_home/.opencode/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin"
2042
+ # kimi's curl-script installer) -- none of those are on the plain system PATH
2043
+ # this file uses elsewhere for brew/npm/pip. opencode is the exception (Tarun,
2044
+ # 2026-10-01): it comes from the npm registry through bun, so bun's global bin
2045
+ # directory is searched instead of the retired native ~/.opencode/bin.
2046
+ surface_cli_search_path="$administrator_home/.local/bin:$administrator_home/.kimi-code/bin:$administrator_home/.bun/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin"
1952
2047
 
1953
2048
  # Resolves a surface to the real executable that will be run: an explicit
1954
2049
  # manifest `executable` (a fixed, known path -- e.g. hostinger's report-only
@@ -2017,6 +2112,173 @@ surface_update_error_reason() {
2017
2112
  fi
2018
2113
  }
2019
2114
 
2115
+ # bun-managed surface (Tarun, 2026-10-01, "we are not on GitHub"): opencode is installed and updated
2116
+ # from the npm registry package `opencode-ai` with `bun add -g opencode-ai@latest`, run as the
2117
+ # administrator -- never `opencode upgrade`, which asks the unauthenticated GitHub REST API for its
2118
+ # latest release and 403s under the fleet's shared-IP rate limit. Named so tests can stub the one
2119
+ # boundary that touches the machine; adl_bun_add_global (bin/lib/adl-package.sh) is the same call
2120
+ # the adl package install uses (the administrator's own ~/.bun, bun found through Homebrew).
2121
+ run_surface_bun_add() {
2122
+ local spec="$1" os
2123
+ case "$platform" in
2124
+ mac) os=Darwin ;;
2125
+ linux) os=Linux ;;
2126
+ *) echo "ERROR unsupported platform for a bun surface: $platform" >&2; return 1 ;;
2127
+ esac
2128
+ adl_bun_add_global "$administrator" "$os" "$administrator_home" "$spec" </dev/null
2129
+ }
2130
+
2131
+ # Finds every OTHER copy of a bun-managed surface command: each directory on the surface search
2132
+ # path plus the native installer's ~/.opencode/bin. Prints one TAB-separated record per distinct
2133
+ # copy, `kind<TAB>path<TAB>detail`, where kind is
2134
+ # native <home>/.opencode/bin/<command> (a regular executable the native installer wrote;
2135
+ # path is the bin DIRECTORY that is moved aside)
2136
+ # brew a symlink into <this host's Homebrew prefix>/Cellar/<command>/<version>/bin/<command>
2137
+ # npm a symlink into <prefix>/lib/node_modules/<package>/bin/<command>.exe whose
2138
+ # package.json names <package> (detail is the prefix)
2139
+ # unrecognised anything else (detail is where it really points)
2140
+ # A copy is retired only when it is positively one of the first three; the bun install itself is
2141
+ # skipped. Several PATH entries that reach one real file (/bin -> usr/bin) count once.
2142
+ classify_surface_copies() {
2143
+ local command_name="$1" package="$2" bun_exe="$3"
2144
+ local dirs=() dir path real bun_real seen=" " native_bin="$administrator_home/.opencode/bin" prefix pkg_dir brew_prefix
2145
+ brew_prefix="$(cd -P -- "${brew_bin%/bin/brew}" 2>/dev/null && pwd -P)" || brew_prefix="${brew_bin%/bin/brew}"
2146
+ IFS=: read -r -a dirs <<<"$surface_cli_search_path"
2147
+ dirs+=("$native_bin")
2148
+ bun_real="$(adl_realpath "$bun_exe" 2>/dev/null)" || bun_real="$bun_exe"
2149
+ for dir in "${dirs[@]}"; do
2150
+ [[ -n "$dir" ]] || continue
2151
+ path="$dir/$command_name"
2152
+ [[ -e "$path" || -L "$path" ]] || continue
2153
+ real="$(adl_realpath "$path" 2>/dev/null)" || real="$path"
2154
+ [[ "$real" != "$bun_real" ]] || continue
2155
+ case "$seen" in *" $real "*) continue ;; esac
2156
+ seen="$seen$real "
2157
+ if [[ "$path" == "$native_bin/$command_name" && -f "$path" && ! -L "$path" && -x "$path" ]]; then
2158
+ printf 'native\t%s\t%s\n' "$native_bin" "$path"
2159
+ elif [[ "$real" =~ ^(.*)/Cellar/${command_name}/[^/]+/bin/${command_name}$ && "${BASH_REMATCH[1]}" == "$brew_prefix" ]]; then
2160
+ printf 'brew\t%s\t%s\n' "$path" "${BASH_REMATCH[1]}"
2161
+ elif [[ "$real" =~ ^(.*)/lib/node_modules/${package}/bin/${command_name}\.exe$ ]]; then
2162
+ prefix="${BASH_REMATCH[1]}"
2163
+ pkg_dir="$prefix/lib/node_modules/$package"
2164
+ if [[ -f "$pkg_dir/package.json" ]] && jq -e --arg name "$package" '.name == $name' "$pkg_dir/package.json" >/dev/null 2>&1; then
2165
+ printf 'npm\t%s\t%s\n' "$path" "$prefix"
2166
+ else
2167
+ printf 'unrecognised\t%s\t%s\n' "$path" "$real"
2168
+ fi
2169
+ else
2170
+ printf 'unrecognised\t%s\t%s\n' "$path" "$real"
2171
+ fi
2172
+ done
2173
+ }
2174
+
2175
+ # Retires every other copy of a bun-managed surface so the host has exactly one, idempotently:
2176
+ # the native installer's ~/.opencode/bin is moved ASIDE under <state dir>/retired-opencode/<UTC
2177
+ # stamp>/native-bin (never deleted; ~/.opencode's own plugin files and ~/.config/opencode are not
2178
+ # touched), a Homebrew formula goes through `brew uninstall` as the Homebrew owner (run_brew, never
2179
+ # sudo brew), and an npm global goes through `npm uninstall -g --prefix <its real prefix>`. Anything
2180
+ # not positively identified is an ERROR and NOTHING is removed. Plan mode lists what apply would do.
2181
+ retire_other_surface_copies() {
2182
+ local name="$1" command_name="$2" package="$3" bun_exe="$4"
2183
+ local kinds=() paths=() details=() kind path detail i unrecognised=0 aside=""
2184
+ while IFS=$'\t' read -r kind path detail; do
2185
+ [[ -n "$kind" ]] || continue
2186
+ kinds+=("$kind"); paths+=("$path"); details+=("$detail")
2187
+ done < <(classify_surface_copies "$command_name" "$package" "$bun_exe")
2188
+ [[ "${#kinds[@]}" -gt 0 ]] || return 0
2189
+ for ((i = 0; i < ${#kinds[@]}; i++)); do
2190
+ [[ "${kinds[i]}" == unrecognised ]] || continue
2191
+ unrecognised=1
2192
+ if [[ "$mode" != apply ]]; then
2193
+ echo "global-tools: would FAIL: unrecognised $command_name at ${paths[i]} (-> ${details[i]}); not a copy reconcile knows how to retire"
2194
+ else
2195
+ echo "ERROR surface $name: unrecognised $command_name at ${paths[i]} (-> ${details[i]}); not a copy reconcile knows how to retire, so nothing was removed" >&2
2196
+ fi
2197
+ done
2198
+ if [[ "$unrecognised" -eq 1 ]]; then
2199
+ [[ "$mode" != apply ]] || return 1
2200
+ return 0
2201
+ fi
2202
+ if [[ "$mode" != apply ]]; then
2203
+ for ((i = 0; i < ${#kinds[@]}; i++)); do
2204
+ case "${kinds[i]}" in
2205
+ native) echo "global-tools: would retire $command_name copy: move ${paths[i]} aside (native installer)" ;;
2206
+ brew) echo "global-tools: would retire $command_name copy: brew uninstall $command_name (Homebrew formula, ${paths[i]})" ;;
2207
+ npm) echo "global-tools: would retire $command_name copy: npm uninstall -g --prefix ${details[i]} $package (${paths[i]})" ;;
2208
+ esac
2209
+ done
2210
+ return 0
2211
+ fi
2212
+ for ((i = 0; i < ${#kinds[@]}; i++)); do
2213
+ case "${kinds[i]}" in
2214
+ native)
2215
+ [[ -n "$aside" ]] || aside="$state_dir/retired-$command_name/$(date -u +%Y%m%dT%H%M%SZ)"
2216
+ mkdir -p -- "$aside" || return 1
2217
+ mv -- "${paths[i]}" "$aside/native-bin" || { echo "ERROR surface $name: could not move ${paths[i]} aside to $aside" >&2; return 1; }
2218
+ echo "global-tools: retiring $command_name copy: moved ${paths[i]} aside to $aside/native-bin"
2219
+ ;;
2220
+ brew)
2221
+ run_brew uninstall "$command_name" || { echo "ERROR surface $name: brew uninstall $command_name failed" >&2; return 1; }
2222
+ [[ ! -e "${paths[i]}" && ! -L "${paths[i]}" ]] || { echo "ERROR surface $name: ${paths[i]} is still present after brew uninstall $command_name" >&2; return 1; }
2223
+ echo "global-tools: retiring $command_name copy: brew uninstalled $command_name (${paths[i]})"
2224
+ ;;
2225
+ npm)
2226
+ run_npm uninstall -g --prefix "${details[i]}" "$package" || { echo "ERROR surface $name: npm uninstall -g $package failed" >&2; return 1; }
2227
+ [[ ! -e "${paths[i]}" && ! -L "${paths[i]}" ]] || { echo "ERROR surface $name: ${paths[i]} is still present after npm uninstall -g $package" >&2; return 1; }
2228
+ echo "global-tools: retiring $command_name copy: npm uninstalled $package from ${details[i]} (${paths[i]})"
2229
+ ;;
2230
+ esac
2231
+ done
2232
+ }
2233
+
2234
+ # Installs or updates a bun-managed surface (see run_surface_bun_add): one `bun add -g
2235
+ # <package>@latest` covers both "absent" and "present", then the other copies are retired and the
2236
+ # command the administrator's PATH resolves is checked to be bun's. Fails loud at every step; a
2237
+ # version is never pinned.
2238
+ reconcile_bun_surface_cli() {
2239
+ local name="$1" command_name="$2" package="$3"
2240
+ local bun_exe="$administrator_home/.bun/bin/$command_name" spec="$package@latest"
2241
+ local old_version="" new_version update_output update_status=0 resolved
2242
+ if [[ "$mode" != apply ]]; then
2243
+ echo "would install or update surface from the npm registry: $name (bun add -g $spec as $administrator)"
2244
+ retire_other_surface_copies "$name" "$command_name" "$package" "$bun_exe"
2245
+ return 0
2246
+ fi
2247
+ [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
2248
+ if [[ -x "$bun_exe" ]]; then old_version="$(surface_cli_version "$bun_exe")"; fi
2249
+ update_output="$(run_with_timeout "$SURFACE_UPDATE_TIMEOUT_SECONDS" run_surface_bun_add "$spec" 2>&1)" || update_status=$?
2250
+ printf '%s\n' "$update_output"
2251
+ if [[ "$update_status" -eq 124 ]]; then
2252
+ echo "ERROR surface update timed out: $name" >&2
2253
+ return 1
2254
+ elif [[ "$update_status" -ne 0 ]]; then
2255
+ echo "ERROR surface update failed: $name (exit $update_status)" >&2
2256
+ return 1
2257
+ fi
2258
+ # opencode-ai's bin is a stub that exits 1 until its postinstall puts the platform binary in
2259
+ # place, and bun skips an untrusted package's postinstall: a bun add that "succeeded" but left
2260
+ # a command that cannot report its version is not an install.
2261
+ new_version="$(surface_cli_version "$bun_exe")"
2262
+ if [[ -z "$new_version" ]]; then
2263
+ echo "ERROR surface $name: bun add -g $spec succeeded but '$bun_exe --version' printed nothing; the package's postinstall did not run (check 'bun pm default-trusted' lists $package, or add it to the global trustedDependencies)" >&2
2264
+ return 1
2265
+ fi
2266
+ retire_other_surface_copies "$name" "$command_name" "$package" "$bun_exe" || return 1
2267
+ resolved="$(resolve_surface_executable "$command_name" "")"
2268
+ if [[ "$resolved" != "$bun_exe" ]]; then
2269
+ echo "ERROR surface $name: $command_name on ${administrator}'s PATH is ${resolved:-missing}, not the bun-installed $bun_exe" >&2
2270
+ return 1
2271
+ fi
2272
+ if [[ -z "$old_version" ]]; then
2273
+ echo "surface $name: installed $new_version ($package from the npm registry)"
2274
+ elif [[ "$old_version" != "$new_version" ]]; then
2275
+ echo "surface $name: $old_version -> $new_version"
2276
+ else
2277
+ echo "surface $name: already latest ($new_version)"
2278
+ fi
2279
+ return 0
2280
+ }
2281
+
2020
2282
  # Reconciles one declared surface_clis entry. report_only (hostinger's loose
2021
2283
  # /root/codex, unknown install method) is version-reported only -- run_surface_update
2022
2284
  # is never called for it, by construction (this branch returns before
@@ -2028,12 +2290,30 @@ reconcile_one_surface_cli() {
2028
2290
  local name="$1" command_name="$2" executable_override="$3" report_only="$4" manager="$5"
2029
2291
  shift 5
2030
2292
  local update_args=("$@")
2293
+ if [[ "$manager" == bun ]]; then
2294
+ # For a bun surface the one trailing argument is the npm registry package, not an update argv.
2295
+ reconcile_bun_surface_cli "$name" "$command_name" "${update_args[0]}"
2296
+ return
2297
+ fi
2031
2298
  if [[ "$manager" == brew ]]; then
2032
2299
  if [[ "$mode" != apply ]]; then
2300
+ if [[ "$name" == */* ]]; then
2301
+ echo "would trust Homebrew item: $name (cask)"
2302
+ fi
2033
2303
  echo "would ensure Homebrew cask: $name (brew install --cask $name; brew upgrade --cask $name)"
2034
2304
  return 0
2035
2305
  fi
2036
2306
  [[ "$(id -u)" -eq 0 ]] || { echo "ERROR apply mode must run as root" >&2; exit 1; }
2307
+ # A fully-qualified cask name comes from a non-core tap, which Homebrew
2308
+ # >=6 refuses to load until the ITEM is trusted (same tap-trust grant as
2309
+ # the per-tool brew path in install_one_tool; the kind is certain here).
2310
+ # Bare names are core casks, trusted by default -- no grant.
2311
+ if [[ "$name" == */* ]]; then
2312
+ brew_query trust | awk '{$1=$1};1' | grep -Fqx "$name" || run_brew trust --cask "$name" || {
2313
+ echo "ERROR surface $name: brew trust --cask $name failed" >&2
2314
+ return 1
2315
+ }
2316
+ fi
2037
2317
  local installed_casks query_status=0 cask_action=() update_output update_status=0
2038
2318
  installed_casks="$(brew_query list --cask --versions 2>&1)" || query_status=$?
2039
2319
  if [[ "$query_status" -ne 0 ]]; then
@@ -2181,10 +2461,15 @@ reconcile_surface_clis() {
2181
2461
  report_only="$(jq -r --argjson i "$i" --arg platform "$platform" --arg device "$device" \
2182
2462
  '.[$platform].devices[$device].surface_clis[$i].report_only // false' "$manifest")"
2183
2463
  update_args=()
2184
- while IFS= read -r arg; do
2185
- [[ -n "$arg" ]] && update_args+=("$arg")
2186
- done < <(jq -r --argjson i "$i" --arg platform "$platform" --arg device "$device" \
2187
- '.[$platform].devices[$device].surface_clis[$i].update_args // [] | .[]' "$manifest")
2464
+ if [[ "$manager" == bun ]]; then
2465
+ update_args+=("$(jq -r --argjson i "$i" --arg platform "$platform" --arg device "$device" \
2466
+ '.[$platform].devices[$device].surface_clis[$i].package // empty' "$manifest")")
2467
+ else
2468
+ while IFS= read -r arg; do
2469
+ [[ -n "$arg" ]] && update_args+=("$arg")
2470
+ done < <(jq -r --argjson i "$i" --arg platform "$platform" --arg device "$device" \
2471
+ '.[$platform].devices[$device].surface_clis[$i].update_args // [] | .[]' "$manifest")
2472
+ fi
2188
2473
  if ! reconcile_one_surface_cli "$name" "$command_name" "$executable_override" "$report_only" "$manager" ${update_args[@]+"${update_args[@]}"}; then
2189
2474
  echo "ERROR global-tools: TOOL INSTALL FAILED: surface-$name" >&2
2190
2475
  failed_tools+=("surface-$name")
@@ -2483,7 +2768,7 @@ if [[ "$(jq -r '.config.scratch_reap.enabled // false' "$manifest")" == true ]];
2483
2768
  # REFUSE to remove worktrees. That is the safe direction, so it must not
2484
2769
  # abort the fleet's update -- but it must not vanish either, or the
2485
2770
  # published-HEAD gate rots with nothing ever reporting it.
2486
- "$script_dir/scratch-reap" \
2771
+ "$BASH" "$script_dir/scratch-reap" \
2487
2772
  "${scratch_reap_args[@]}" \
2488
2773
  --mode "$scratch_reap_mode" \
2489
2774
  --idle-days "$scratch_reap_idle_days" \
@@ -2512,15 +2797,10 @@ if [[ "$have_previous" -eq 1 ]]; then
2512
2797
  [[ -n "$tool_id" ]] || continue
2513
2798
  remove_one_manifest_entry "$tool_id"
2514
2799
  # Selects an id when it was deleted outright (absent from the current
2515
- # manifest) OR retained but migrated to a different manager/package (e.g.
2516
- # lean-ctx moving from npm:lean-ctx-bin to release-archive-latest:lean-ctx)
2800
+ # manifest) OR retained but migrated to a different manager/package
2517
2801
  # -- a plain "id no longer present" check would silently leave the OLD
2518
2802
  # manager's install in place forever whenever an id survives a manager
2519
- # change, which for lean-ctx meant the stale npm package (and the
2520
- # /opt/homebrew/bin/lean-ctx shim it owns) would never be uninstalled, and
2521
- # that shim's earlier position on PATH than /usr/local/bin would keep
2522
- # `command -v lean-ctx` resolving to it instead of the new release binary
2523
- # no matter how many times reconcile ran.
2803
+ # change, even though the current declaration points to a different install.
2524
2804
  done < <(jq -r --slurpfile current "$resolved_file" '
2525
2805
  keys[] as $id
2526
2806
  | ($current[0][$id]) as $cur
@@ -2547,6 +2827,9 @@ project_adl_package_payload() {
2547
2827
  echo "would build agent-skills payload from checkout"
2548
2828
  fi
2549
2829
  echo "would project Adl package payload from $repo_root to /usr/local/libexec/adl and /usr/local/bin/git{,-ado}"
2830
+ if [[ "$source_mode" == npm ]]; then
2831
+ echo "would link /usr/local/bin/adl to $repo_root/bin/adl"
2832
+ fi
2550
2833
  return 0
2551
2834
  fi
2552
2835
  bun_bin="$(command -v bun || true)"
@@ -2561,6 +2844,12 @@ project_adl_package_payload() {
2561
2844
  if [[ -n "${RECONCILE_TEST_SYSTEM_ROOT:-}" ]]; then
2562
2845
  system_root_args+=(--system-root "$RECONCILE_TEST_SYSTEM_ROOT")
2563
2846
  fi
2847
+ # Package mode also links /usr/local/bin/adl to this package's own bin/adl:
2848
+ # bun links adl into the owner's ~/.bun/bin only, yet bin/git-ado, the MCP
2849
+ # server command and the managed surface hooks run a bare `adl`.
2850
+ if [[ "$source_mode" == npm ]]; then
2851
+ system_root_args+=(--link-adl)
2852
+ fi
2564
2853
  "$bun_bin" "$repo_root/packages/fleet/src/install-adl-package.ts" install \
2565
2854
  --package-root "$repo_root" ${system_root_args[@]+"${system_root_args[@]}"}
2566
2855
  }
@@ -2572,7 +2861,7 @@ fi
2572
2861
 
2573
2862
  # Same continue-on-failure contract as the tool passes above: runs after git
2574
2863
  # itself is installed/upgraded, and a failure is recorded, not fatal.
2575
- if ! "$repo_root/bin/shell-path-policy" --home "$administrator_home" --platform "$platform" --mode "$mode" --owner "$administrator"; then
2864
+ if ! "$BASH" "$repo_root/bin/shell-path-policy" --home "$administrator_home" --platform "$platform" --mode "$mode" --owner "$administrator"; then
2576
2865
  echo "ERROR global-tools: TOOL INSTALL FAILED: shell-path-policy" >&2
2577
2866
  failed_tools+=("shell-path-policy")
2578
2867
  fi
@@ -2596,7 +2885,7 @@ if ! retire_git_maintain; then
2596
2885
  fi
2597
2886
 
2598
2887
  if [[ "$mode" == apply ]]; then
2599
- "$script_dir/health-check" "$device"
2888
+ "$BASH" "$script_dir/health-check" "$device"
2600
2889
  fi
2601
2890
 
2602
2891
  if [[ "$(jq -r '.config.global_surface_sweep.enabled' "$manifest")" == true ]]; then
@@ -2609,7 +2898,7 @@ if [[ "$(jq -r '.config.global_surface_sweep.enabled' "$manifest")" == true ]];
2609
2898
  # recomputes the sweep's own backupPath() logic. stderr is left
2610
2899
  # unredirected so a real failure still streams live as before.
2611
2900
  sweep_status=0
2612
- sweep_output="$("$script_dir/global-surface-sweep" \
2901
+ sweep_output="$("$BASH" "$script_dir/global-surface-sweep" \
2613
2902
  --home "$administrator_home" \
2614
2903
  --mode "$surface_mode" \
2615
2904
  --state-dir "$state_dir")" || sweep_status=$?
@@ -2647,7 +2936,7 @@ if [[ "$kimi_hooks_enabled" == true ]]; then
2647
2936
  echo "global-tools: kimi-hooks skipped: kimi not installed"
2648
2937
  elif [[ "$mode" != apply ]]; then
2649
2938
  echo "global-tools: kimi hooks mode=plan home=$administrator_home"
2650
- "$script_dir/kimi-hooks" --home "$administrator_home" --mode plan
2939
+ "$BASH" "$script_dir/kimi-hooks" --home "$administrator_home" --mode plan
2651
2940
  else
2652
2941
  # Ordering: adl (bun-compile pass, above) must already resolve as
2653
2942
  # a bare command before we write TOML entries that reference it -- Kimi
@@ -2657,12 +2946,12 @@ if [[ "$kimi_hooks_enabled" == true ]]; then
2657
2946
  # docs/specs/two-branch-standard-2026-09-27.md) -- hard cut, no compat
2658
2947
  # shim: there is no standalone hook executable to resolve any more.
2659
2948
  adl_executable="$(jq -r '."adl".executable' "$resolved_file")"
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
2949
+ if ! adl_resolved="$(sudo -u "$administrator" -H env PATH="/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:$administrator_home/.bun/bin:/usr/bin:/bin" /bin/sh -c "command -v '$adl_executable'" 2>/dev/null)"; then
2661
2950
  echo "ERROR global-tools: could not resolve adl from the administrator PATH" >&2
2662
2951
  exit 1
2663
2952
  fi
2664
2953
  [[ -n "$adl_resolved" && -x "$adl_resolved" ]] || {
2665
- echo "ERROR global-tools: kimi-hooks requires adl on PATH ($adl_executable); bun-compile installation must succeed first" >&2
2954
+ echo "ERROR global-tools: kimi-hooks requires adl on PATH ($adl_executable); the adl package installation must succeed first" >&2
2666
2955
  exit 1
2667
2956
  }
2668
2957
  echo "global-tools: kimi hooks mode=apply home=$administrator_home (as $administrator)"
@@ -2679,7 +2968,7 @@ if [[ "$kimi_hooks_enabled" == true ]]; then
2679
2968
  # streams live exactly as before.
2680
2969
  kimi_hooks_status=0
2681
2970
  kimi_hooks_output="$(cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
2682
- "$script_dir/kimi-hooks" --home "$administrator_home" --mode apply)" || kimi_hooks_status=$?
2971
+ "$BASH" "$script_dir/kimi-hooks" --home "$administrator_home" --mode apply)" || kimi_hooks_status=$?
2683
2972
  printf '%s\n' "$kimi_hooks_output"
2684
2973
  collect_kimi_hooks_backups "$kimi_hooks_output"
2685
2974
  [[ "$kimi_hooks_status" -eq 0 ]] || exit "$kimi_hooks_status"
@@ -2692,50 +2981,11 @@ fi
2692
2981
  # run the same hook contract twice. The exact old path remains Enact-owned.
2693
2982
  if [[ "$mode" != apply ]]; then
2694
2983
  echo "global-tools: retired OpenCode carrier mode=plan home=$administrator_home"
2695
- "$script_dir/opencode-hooks" --home "$administrator_home" --mode plan
2984
+ "$BASH" "$script_dir/opencode-hooks" --home "$administrator_home" --mode plan
2696
2985
  else
2697
2986
  echo "global-tools: retired OpenCode carrier mode=remove home=$administrator_home (as $administrator)"
2698
2987
  (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
2699
- "$script_dir/opencode-hooks" --home "$administrator_home" --mode remove)
2700
- fi
2701
-
2702
- # STATUS.md 12p item 13, final scope (2026-09-29): lean-ctx hooks are "moved
2703
- # to repo level" and must be REMOVED FROM GLOBAL -- the per-repo carrier
2704
- # (`adl hook lean-ctx pre-tool-use|post-tool-use --host=<x>`, written by
2705
- # `adl hooks deploy`) already provides them with the repo's own config, so
2706
- # any raw, unwrapped `lean-ctx hook <subcommand>` entry left in a per-user
2707
- # config (observe, rewrite, redirect, read-dedup, any other) is retracted
2708
- # global wiring, never the per-repo carrier or Orca's own hooks -- see
2709
- # bin/lib/lean-ctx-observe-hooks.sh's header for the full decision trail and
2710
- # exactly which per-user files can carry one. Unconditional, like the
2711
- # retired OpenCode carrier cleanup just above: not a manifest-gated optional
2712
- # feature, a standing cleanup that runs every reconciliation so a
2713
- # hand-edited or Orca-reinstalled per-user config never lets this creep back
2714
- # in unnoticed.
2715
- if [[ "$mode" != apply ]]; then
2716
- echo "global-tools: lean-ctx observe/raw-hook strip mode=plan home=$administrator_home"
2717
- # Plan runs either unprivileged (the administrator's own shell) or under
2718
- # sudo (fleet-cutover.sh's plan). As root it must drop to the administrator
2719
- # exactly like apply: lean-ctx-observe-strip refuses root against a
2720
- # non-root home (m5 cutover plan, 2026-09-29).
2721
- if [[ "$EUID" -eq 0 && "$administrator" != root ]]; then
2722
- (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
2723
- "$script_dir/lean-ctx-observe-strip" --home "$administrator_home" --mode plan)
2724
- else
2725
- "$script_dir/lean-ctx-observe-strip" --home "$administrator_home" --mode plan
2726
- fi
2727
- else
2728
- echo "global-tools: lean-ctx observe/raw-hook strip mode=apply home=$administrator_home (as $administrator)"
2729
- # stdout captured (then re-printed) so its own "backed up previous config
2730
- # to ..." note(s) can be folded into this run's combined backups list,
2731
- # exactly like kimi-hooks above; stderr left unredirected so a real
2732
- # failure (a file that fails to parse) still streams live.
2733
- lean_ctx_observe_strip_status=0
2734
- lean_ctx_observe_strip_output="$(cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
2735
- "$script_dir/lean-ctx-observe-strip" --home "$administrator_home" --mode apply)" || lean_ctx_observe_strip_status=$?
2736
- printf '%s\n' "$lean_ctx_observe_strip_output"
2737
- collect_lean_ctx_observe_strip_backups "$lean_ctx_observe_strip_output"
2738
- [[ "$lean_ctx_observe_strip_status" -eq 0 ]] || exit "$lean_ctx_observe_strip_status"
2988
+ "$BASH" "$script_dir/opencode-hooks" --home "$administrator_home" --mode remove)
2739
2989
  fi
2740
2990
 
2741
2991
  # surface-policy re-asserts each host's permission MODE from the single source
@@ -2758,11 +3008,11 @@ fi
2758
3008
  if [[ "$surface_policy_enabled" == true ]]; then
2759
3009
  if [[ "$mode" != apply ]]; then
2760
3010
  echo "global-tools: surface policy mode=plan home=$administrator_home"
2761
- "$script_dir/surface-policy" --home "$administrator_home" --mode plan
3011
+ "$BASH" "$script_dir/surface-policy" --home "$administrator_home" --mode plan
2762
3012
  else
2763
3013
  echo "global-tools: surface policy mode=apply home=$administrator_home (as $administrator)"
2764
3014
  (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
2765
- "$script_dir/surface-policy" --home "$administrator_home" --mode apply)
3015
+ "$BASH" "$script_dir/surface-policy" --home "$administrator_home" --mode apply)
2766
3016
  fi
2767
3017
  fi
2768
3018
 
@@ -2774,24 +3024,24 @@ fi
2774
3024
  if [[ "$vscode_settings_enabled" == true ]]; then
2775
3025
  if [[ "$mode" != apply ]]; then
2776
3026
  echo "global-tools: VS Code settings mode=plan home=$administrator_home"
2777
- "$script_dir/vscode-settings" --home "$administrator_home" --platform "$platform" --mode plan
3027
+ "$BASH" "$script_dir/vscode-settings" --home "$administrator_home" --platform "$platform" --mode plan
2778
3028
  else
2779
3029
  echo "global-tools: VS Code settings mode=apply home=$administrator_home (as $administrator)"
2780
3030
  (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
2781
- "$script_dir/vscode-settings" --home "$administrator_home" --platform "$platform" --mode apply)
3031
+ "$BASH" "$script_dir/vscode-settings" --home "$administrator_home" --platform "$platform" --mode apply)
2782
3032
  fi
2783
3033
  fi
2784
3034
 
2785
3035
  if [[ "$global_agents_enabled" == true ]]; then
2786
3036
  if [[ "$mode" != apply ]]; then
2787
3037
  echo "global-tools: global agents mode=status home=$administrator_home"
2788
- "$script_dir/global-agents" --home "$administrator_home" --mode status
3038
+ "$BASH" "$script_dir/global-agents" --home "$administrator_home" --mode status
2789
3039
  else
2790
3040
  echo "global-tools: global agents mode=deploy home=$administrator_home (as $administrator)"
2791
3041
  # The exact target files are authoritative global artifacts. Drift is
2792
3042
  # replaced from GLOBAL-AGENTS.md with no backup or leave-in-place branch.
2793
3043
  (cd /tmp && sudo -u "$administrator" -H env PATH="${brew_bin%/*}:/usr/bin:/bin" \
2794
- "$script_dir/global-agents" --home "$administrator_home" --mode deploy)
3044
+ "$BASH" "$script_dir/global-agents" --home "$administrator_home" --mode deploy)
2795
3045
  fi
2796
3046
  fi
2797
3047