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

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 (410) 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 +12 -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 +131 -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 +33 -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/scripts/probe-ado-rules.ts +311 -0
  140. package/packages/azdo/src/azdo-pl.ts +2 -54
  141. package/packages/azdo/src/azdo-pr-review.ts +4 -4
  142. package/packages/azdo/src/azdo-repo-doctor.ts +101 -52
  143. package/packages/azdo/src/azdo-wit.ts +44 -2
  144. package/packages/azdo/src/lib/agent-identity.ts +12 -14
  145. package/packages/azdo/src/lib/agent-token.ts +2 -2
  146. package/packages/azdo/src/lib/client.ts +0 -5
  147. package/packages/azdo/src/lib/graph-identity.ts +4 -4
  148. package/packages/azdo/src/workflow-ladder.csv +241 -0
  149. package/packages/azdo/src/workflow-rules-matrix.csv +85 -0
  150. package/packages/azdo/src/workflow-rules.ts +194 -0
  151. package/packages/fleet/manifest.json +35 -11
  152. package/packages/fleet/manifest.schema.json +62 -10
  153. package/packages/fleet/src/default-agent-file.ts +3 -3
  154. package/packages/fleet/src/install-adl-package.ts +73 -15
  155. package/packages/fleet/src/install-embedding-model.ts +13 -13
  156. package/packages/fleet/src/managed-settings.ts +7 -7
  157. package/packages/fleet/src/repo-maintain.ts +91 -55
  158. package/packages/fleet/src/scratch-reap.ts +5 -5
  159. package/packages/fleet/src/vscode-settings.ts +5 -5
  160. package/packages/repo-controls/README.md +141 -43
  161. package/packages/repo-controls/assets/promote.ts +4 -4
  162. package/packages/repo-controls/assets/reconcile-guard.sh +10 -46
  163. package/packages/repo-controls/assets/version-bump.sh +31 -84
  164. package/packages/repo-controls/bunfig.toml +6 -7
  165. package/packages/repo-controls/package/adl-compliance/AGENTS.md +5 -6
  166. package/packages/repo-controls/package/adl-compliance/conftest.py +3 -1
  167. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +4 -16
  168. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +9 -2
  169. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +1 -0
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/test_check.py +11 -0
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/__init__.py +7 -0
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/check.py +266 -0
  173. 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
  174. 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
  175. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.hidden.md +1 -0
  176. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/a.md +1 -0
  177. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/b.md +1 -0
  178. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/data.csv +1 -0
  179. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/diagram.png +1 -0
  180. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/mock.png +1 -0
  181. 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
  182. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs.md +1 -0
  183. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/grants.yaml +4 -0
  184. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p1.md +1 -0
  185. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p2.md +1 -0
  186. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p3.md +1 -0
  187. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p4.md +1 -0
  188. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p5.md +1 -0
  189. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/build.md +1 -0
  190. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/overview.md +1 -0
  191. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/install.md +1 -0
  192. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/start.md +1 -0
  193. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/grants.yaml +4 -0
  194. 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
  195. 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
  196. 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
  197. 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
  198. 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
  199. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/a.md +1 -0
  200. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/b.md +1 -0
  201. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/c.md +1 -0
  202. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/grants.yaml +4 -0
  203. 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
  204. 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
  205. 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
  206. 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
  207. 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
  208. 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
  209. 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
  210. 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
  211. 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
  212. 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
  213. 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
  214. 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
  215. 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
  216. 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
  217. 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
  218. 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
  219. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/grants.yaml +4 -0
  220. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/a.md +1 -0
  221. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/b.md +1 -0
  222. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/c.md +1 -0
  223. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/grants.yaml +4 -0
  224. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/a.md +1 -0
  225. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/b.md +1 -0
  226. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/c.md +1 -0
  227. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/grants.yaml +4 -0
  228. 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
  229. 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
  230. 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
  231. 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
  232. 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
  233. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/grants.yaml +4 -0
  234. 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
  235. 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
  236. 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
  237. 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
  238. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/grants.yaml +4 -0
  239. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/a.md +1 -0
  240. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/b.md +1 -0
  241. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/c.md +1 -0
  242. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/d.md +1 -0
  243. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/a.md +1 -0
  244. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/b.md +1 -0
  245. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/grants.yaml +6 -0
  246. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/a.md +1 -0
  247. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/b.md +1 -0
  248. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/c.md +1 -0
  249. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/grants.yaml +3 -0
  250. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/test_check.py +345 -0
  251. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +18 -5
  252. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +35 -0
  253. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/__init__.py +7 -0
  254. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/check.py +91 -0
  255. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/AGENTS.md +3 -0
  256. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/CLAUDE.md +3 -0
  257. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/-leading-hyphen.md +3 -0
  258. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/AGENTS.md +3 -0
  259. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/double--hyphen.md +3 -0
  260. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/getting started.md +3 -0
  261. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/nested/page-here.md +3 -0
  262. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/trailing-hyphen-.md +3 -0
  263. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +4 -0
  264. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/user_profile.md +3 -0
  265. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/UI-CONTRACT.png +2 -0
  266. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/diagram.png +1 -0
  267. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/index.md +4 -0
  268. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/notes.md +3 -0
  269. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/reference.html +1 -0
  270. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/2026-10-experiment.md +3 -0
  271. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +6 -0
  272. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs.md +5 -0
  273. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +194 -0
  274. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +14 -2
  275. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +5 -5
  276. 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
  277. 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
  278. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +58 -6
  279. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/test_check.py +0 -1
  280. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/check.py +64 -17
  281. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +60 -9
  282. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +17 -0
  283. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +13 -0
  284. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs/architecture/demo.md +0 -1
  285. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs-okf-profiles.yaml +0 -1
  286. 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
  287. 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
  288. 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
  289. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs-okf-profiles.yaml +5 -0
  290. 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
  291. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs-okf-profiles.yaml +0 -1
  292. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/experiment-ok.md +15 -0
  293. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +15 -0
  294. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +16 -0
  295. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +11 -0
  296. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +11 -0
  297. 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
  298. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-sources.md +10 -0
  299. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +11 -0
  300. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +11 -0
  301. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +16 -0
  302. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +14 -0
  303. 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
  304. 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
  305. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/operations/runbook.md +26 -0
  306. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/test_check.py +228 -2
  307. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +5 -0
  308. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +11 -2
  309. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/__init__.py +5 -3
  310. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +160 -44
  311. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +0 -2
  312. 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
  313. 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
  314. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/grants.yaml +3 -0
  315. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +0 -2
  316. 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
  317. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +5 -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/a.ts +1 -0
  319. 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
  320. 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
  321. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +4 -0
  322. 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
  323. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +5 -0
  324. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +0 -2
  325. 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
  326. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/grants.yaml +3 -0
  327. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/register.py +127 -0
  328. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +265 -29
  329. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +75 -2
  330. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +194 -0
  331. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/check.py +56 -10
  332. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/test_check.py +258 -2
  333. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +40 -12
  334. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +108 -0
  335. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plan_metadata.py +9 -4
  336. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/check.py +1 -1
  337. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/items/az351-restore-entrypoint.md +1 -1
  338. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +3 -3
  339. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +2 -3
  340. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +23 -6
  341. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +76 -0
  342. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +58 -17
  343. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +161 -0
  344. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +35 -3
  345. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +108 -1
  346. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +281 -61
  347. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/absolute-path.yaml +3 -0
  348. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/backslash-path.yaml +3 -0
  349. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/baseline-is-a-list.yaml +2 -0
  350. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/boolean-size.yaml +3 -0
  351. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/dot-segment.yaml +3 -0
  352. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/duplicate-path.yaml +4 -0
  353. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-file.yaml +0 -0
  354. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-segment.yaml +3 -0
  355. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/float-size.yaml +3 -0
  356. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/negative-size.yaml +3 -0
  357. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-over-limit-baseline.yaml +1 -0
  358. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-schema-version.yaml +1 -0
  359. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/non-string-path.yaml +3 -0
  360. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/not-yaml.yaml +2 -0
  361. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/null-size.yaml +3 -0
  362. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/parent-segment.yaml +3 -0
  363. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/string-size.yaml +3 -0
  364. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/top-level-is-a-list.yaml +2 -0
  365. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/trailing-slash.yaml +3 -0
  366. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/unknown-key.yaml +3 -0
  367. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/wrong-schema-version.yaml +2 -0
  368. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/zero-size.yaml +3 -0
  369. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/valid.yaml +4 -0
  370. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +758 -45
  371. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/check.py +4 -3
  372. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/test_check.py +2 -2
  373. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +490 -0
  374. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +368 -55
  375. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +4 -2
  376. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +523 -0
  377. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +1179 -0
  378. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +122 -3
  379. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +33 -1
  380. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +0 -11
  381. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/context.py +16 -1
  382. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -2
  383. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/docs_shape.py +12 -1
  384. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +133 -147
  385. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +133 -147
  386. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +1 -1
  387. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +52 -10
  388. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +23 -4
  389. package/packages/repo-controls/package.json +2 -9
  390. package/packages/repo-controls/scripts/hooks/post-merge +13 -0
  391. package/packages/repo-controls/scripts/hooks/pre-push +3 -3
  392. package/packages/repo-controls/src/azdo-push.ts +64 -36
  393. package/packages/repo-controls/src/azure-devops-governance.ts +7 -9
  394. package/packages/repo-controls/src/azure-devops-remote.ts +1 -1
  395. package/packages/repo-controls/src/cli.ts +29 -8
  396. package/packages/repo-controls/src/compliance-config.ts +410 -2
  397. package/packages/repo-controls/src/config.ts +133 -60
  398. package/packages/repo-controls/src/constants.ts +13 -27
  399. package/packages/repo-controls/src/coverage-ratchet.ts +4 -4
  400. package/packages/repo-controls/src/doctor.ts +46 -19
  401. package/packages/repo-controls/src/gitignore-block.ts +5 -29
  402. package/packages/repo-controls/src/hook-log.ts +1 -2
  403. package/packages/repo-controls/src/hook.ts +201 -45
  404. package/packages/repo-controls/src/install.ts +14 -8
  405. package/packages/repo-controls/src/integrity.ts +3 -3
  406. package/packages/repo-controls/src/lfs.ts +111 -0
  407. package/packages/repo-controls/src/promote.ts +8 -8
  408. package/packages/repo-controls/src/runtime.ts +9 -1
  409. package/packages/repo-controls/src/workflows-drift.ts +4 -5
  410. package/packages/repo-controls/src/worktree-guard.ts +3 -3
package/bin/adl CHANGED
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/bin/adl
3
- size: 82949600 bytes
4
- sha256: b0711f880f163524abb11d00eb61e134b582627c11fe70bb96ea2d91133b3fbb
3
+ size: 82912736 bytes
4
+ sha256: 1a2ae55423977502b4d00c4c8ba08eac8d3c2d4d9388c934b0ea1f7ff80efe72
package/bin/git CHANGED
@@ -117,7 +117,7 @@ done
117
117
  (( command_index >= 0 )) || exec "$real_git" ${args[@]+"${args[@]}"}
118
118
  command_name="${args[command_index]}"
119
119
 
120
- # L1 worktree guard (STATUS.md item 2, docs/operations/worktree-guard.md): refuse
120
+ # L1 worktree guard (docs/operations/worktree-guard.md): refuse
121
121
  # `git worktree add` when the repository already has 3 checkouts, including
122
122
  # the main folder. Runs BEFORE the network-command routing below because it
123
123
  # applies to every command line, not only Azure DevOps ones, and to humans
@@ -567,22 +567,21 @@ case "$command_name" in
567
567
  done
568
568
  ;;
569
569
  lfs)
570
- # GAP (second independent review, 2026-09-29), fix REVISED (third
571
- # independent review, 2026-09-29, item 3): `git lfs push|pull|fetch`
572
- # against an Azure DevOps remote used to reach the real `git-lfs`
573
- # binary (a SEPARATE executable git dispatches to via PATH, never
574
- # through THIS controller's own real_git exec) with no identity at all.
575
- # The FIRST fix refused this outright here in bin/git -- but that also
576
- # refused it in Tarun's OWN marked human terminal, a regression: his
577
- # real credential helper has always handled `git lfs` fine, and the
578
- # human path never involved git-ado's unverified header mechanism in
579
- # the first place. Routing through git-ado instead lets IT apply the
580
- # SAME policy it applies to every other verb: human (TTY+marker) passes
581
- # through unchanged (unaffected by any of this); the agent identity
582
- # path refuses with a clear message (git-lfs's own HTTP credential
583
- # resolution has not been verified to honor git-ado's
584
- # `http.<url>.extraHeader` mechanism); no identity and no human marker
585
- # refuses, exactly as for every other verb.
570
+ # GAP (second independent review, 2026-09-29): `git lfs push|pull|fetch`
571
+ # against an Azure DevOps remote reaches the real `git-lfs` binary (a
572
+ # SEPARATE executable git dispatches to via PATH, never through THIS
573
+ # controller's own real_git exec) with no identity at all, so it is routed
574
+ # through git-ado like every other network verb. git-ado applies the one
575
+ # policy: human (TTY+marker) passes through unchanged; an agent identity
576
+ # authenticates with the same `http.<url>.extraHeader` GIT_CONFIG_*
577
+ # mechanism as `git push` (proved to be honored by git-lfs, with no fallback
578
+ # to the human credential helper, in packages/repo-controls/tests/
579
+ # lfs-credential-boundary.test.ts -- it was refused for agents until that
580
+ # proof existed); no identity and no human marker is refused, as for every
581
+ # other verb. Other `git lfs` subcommands -- notably `pre-push`,
582
+ # `post-checkout`, `post-commit` and `post-merge`, which the projected hooks
583
+ # run inside a `git push` that already carries the header -- never reach
584
+ # the network through here and are not routed.
586
585
  lfs_sub=''
587
586
  lfs_sub_index=-1
588
587
  for ((index = command_index + 1; index < ${#args[@]}; index += 1)); do
package/bin/git-ado CHANGED
@@ -15,16 +15,17 @@
15
15
  # three Entra service principals: agent-tarun, agent-vivek, agent-reviewer.
16
16
  # Agents never see a secret.
17
17
  #
18
- # HUMAN PATH (STATUS.md 12p decision 1: fail closed, the human opts in).
18
+ # HUMAN PATH (fail closed: the human opts in; Tarun, 2026-09-29).
19
19
  # An unset/empty ADL_AGENT_ID_AGENT is passed through to the human's own
20
20
  # credential helper (Git Credential Manager, etc.) ONLY when BOTH stdin is
21
21
  # a real terminal AND ADL_HUMAN_AZDO is the exact string "1" -- Tarun
22
- # exports it from his own `.zshrc` (decision 1c: "its not only git, its
23
- # azdo" -- the marker governs every human Azure DevOps path, not only this
24
- # script). Otherwise this script refuses outright, by name, before
22
+ # exports it from his own `.zshrc` ("its not only git, its azdo": the
23
+ # marker governs every human Azure DevOps path, not only this script;
24
+ # `adl run` strips it from a child's environment and the adl hook denies
25
+ # any agent command that sets it). Otherwise this script refuses outright, by name, before
25
26
  # touching git at all: the earlier "unset means human" rule relied
26
27
  # entirely on the adl hook denying an agent's Azure DevOps network git
27
- # command that omits ADL_AGENT_ID_AGENT (H2 rule 1) -- a host the hook
28
+ # command that omits ADL_AGENT_ID_AGENT -- a host the hook
28
29
  # cannot rewrite on, or a chained/opaque shell shape the hook's walker
29
30
  # misses, reached THIS script with no agent identity and no TTY, and used
30
31
  # to be treated as human regardless. `adl id whoami`
@@ -311,10 +312,10 @@ ado_host="$(printf '%s' "$remote_url" | tr '[:upper:]' '[:lower:]')"
311
312
  ado_host="${ado_host#https://}"
312
313
  ado_host="${ado_host%%/*}"
313
314
 
314
- # HUMAN PASS-THROUGH (STATUS.md 12p decision 1: fail closed, the human opts
315
- # in). Unset or empty ADL_AGENT_ID_AGENT passes through to the real git
315
+ # HUMAN PASS-THROUGH (fail closed: the human opts in; Tarun, 2026-09-29).
316
+ # Unset or empty ADL_AGENT_ID_AGENT passes through to the real git
316
317
  # UNCHANGED only when this is also a marked human terminal: stdin is a real
317
- # tty AND ADL_HUMAN_AZDO is the exact string "1" (decision 1b/1c). Anything
318
+ # tty AND ADL_HUMAN_AZDO is the exact string "1". Anything
318
319
  # else with no agent identity -- no marker, no tty, or both -- is refused
319
320
  # outright, by name, before touching git at all. This is the ONE place that
320
321
  # check is made in bash; `isMarkedHumanAzdoCaller` in
@@ -333,22 +334,22 @@ fi
333
334
  # AGENT PATH.
334
335
  require_agent_identity "$ADL_AGENT_ID_AGENT" || exit 1
335
336
 
336
- # THIRD independent review (2026-09-29), item 3: `git lfs push|pull|fetch`
337
- # reaches here too now that `is_network_command_name` includes `lfs` (fixing
338
- # the live bug above) -- but git-lfs is a SEPARATE binary whose HTTP
339
- # credential resolution has not been verified to honor the
340
- # `http.<url>.extraHeader` mechanism the agent path below relies on (unlike
341
- # git itself, which this script's own tests prove reads it). Minting a
342
- # token and injecting a header git-lfs might silently ignore -- falling
343
- # back to ITS OWN Basic-auth `git credential fill` flow, bypassing the
344
- # identity model entirely -- is worse than refusing outright. The human
345
- # path above is UNAFFECTED: it execs the real git unchanged regardless of
346
- # `$git_command`, exactly like every other verb, so Tarun's own marked
347
- # terminal keeps using `git lfs` via his real credential helper as before.
348
- if [[ "$git_command" == "lfs" ]]; then
349
- printf 'git-ado: git lfs against Azure DevOps remote %s is refused for the agent identity path (fail closed): git-lfs is a separate binary whose HTTP credential resolution has not been verified to honor this header mechanism. Run the underlying git fetch/push through git-ado directly, or ask Tarun to confirm LFS support before removing this refusal.\n' "$remote_url" >&2
350
- exit 1
351
- fi
337
+ # `git lfs push|pull|fetch` reaches here too (`is_network_command_name`
338
+ # includes `lfs`) and takes the SAME agent path as every other verb. It used to
339
+ # be refused (independent review, 2026-09-29, item 3) because git-lfs is a
340
+ # SEPARATE binary whose HTTP credential resolution had never been verified to
341
+ # honor the `http.<url>.extraHeader` mechanism below; if it ignored the header
342
+ # it would fall back to `git credential fill`, i.e. Git Credential Manager,
343
+ # i.e. the HUMAN. That doubt is settled by a test, not an assumption:
344
+ # packages/repo-controls/tests/lfs-credential-boundary.test.ts runs real
345
+ # git-lfs (3.8.0) against a local LFS endpoint that requires the Bearer token
346
+ # and a credential helper that records every call, with exactly this
347
+ # GIT_CONFIG_COUNT/KEY_n/VALUE_n mechanism: every request carried the header
348
+ # and the helper was never called (and, as the control, was called when the
349
+ # header was absent). The same proof covers the pre-push hook's
350
+ # `git lfs pre-push` (packages/repo-controls/src/lfs.ts), which inherits this
351
+ # environment from the `git push` this script runs. Had it failed, this would
352
+ # still be a refusal; it must become one again if that test ever goes red.
352
353
 
353
354
  # adl-agent-id mints the session and runs `agent-vault run -- <child>`. The
354
355
  # child asks `adl id token` for this agent's Entra token over the proxy
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env bash
2
+ # Managed by adl global-tools.
3
+ #
4
+ # Every host's human git setup is identical and kept that way by the 12-hourly
5
+ # reconcile (Tarun, 2026-10-01): no manual `git config` on any machine. This
6
+ # script sets the administrator's GLOBAL git config from the manifest:
7
+ #
8
+ # config.git_global_config shared by every host
9
+ # <platform>.common.git_global_config added for that platform only
10
+ # <platform>.devices.<device>.git_user user.name / user.email
11
+ #
12
+ # A value is a string (single-valued key) or an ordered array of strings
13
+ # (multi-valued key: the empty-reset-then-helper pattern of credential.helper).
14
+ # A key is rewritten only when its current values differ from the declared
15
+ # ordered list: single-valued via `git config --global KEY VALUE`, multi-valued
16
+ # via `--unset-all` then `--add` for each value in order. Keys the manifest does
17
+ # not declare (including any GitHub or gist credential helper a human set) are
18
+ # never read, changed or removed.
19
+ #
20
+ # Git Credential Manager is installed by the human (macOS: `brew install --cask
21
+ # git-credential-manager`; Linux: the .deb or tarball) -- reconcile never
22
+ # installs it. The one placeholder a value may carry, {git-credential-manager},
23
+ # is resolved at run time as the administrator (`command -v` on the explicit
24
+ # PATH below, which covers /usr/local/bin, /usr/local/share/gcm-core and the
25
+ # Homebrew/linuxbrew bins). When it is not installed, every key that needs it is
26
+ # skipped and left as it is, with one loud WARN; the next reconcile after the
27
+ # install sets it. Nothing is ever written that points at a missing executable.
28
+ #
29
+ # Headless Linux has no keychain, so the manifest's Linux common config sets
30
+ # GCM's credentialStore=cache (an in-memory credential cache daemon) with a
31
+ # one-week timeout instead of the macOS keychain.
32
+ #
33
+ # Git runs as the administrator (`sudo -u ADMIN -H env PATH=... git ...`) so
34
+ # ~/.gitconfig stays user-owned. Apply is invoked by bin/reconcile as root. Plan
35
+ # mode only reads, through sudo when run as root and as the caller otherwise.
36
+ set -euo pipefail
37
+
38
+ usage() {
39
+ echo 'usage: git-defaults --manifest FILE --platform mac|linux --device NAME --mode plan|apply --administrator USER [--path-dirs DIR:DIR...]' >&2
40
+ exit 2
41
+ }
42
+
43
+ manifest=''
44
+ platform=''
45
+ device=''
46
+ mode=''
47
+ administrator=''
48
+ # /usr/local/share/gcm-core comes first so a macOS cask install resolves to the
49
+ # real GCM path rather than its /usr/local/bin symlink. --path-dirs exists for
50
+ # tests; a real run never passes it.
51
+ path_dirs='/usr/local/share/gcm-core:/usr/local/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/bin:/bin:/usr/sbin:/sbin'
52
+ while [[ $# -gt 0 ]]; do
53
+ case "$1" in
54
+ --manifest) manifest="${2:-}"; shift 2 ;;
55
+ --platform) platform="${2:-}"; shift 2 ;;
56
+ --device) device="${2:-}"; shift 2 ;;
57
+ --mode) mode="${2:-}"; shift 2 ;;
58
+ --administrator) administrator="${2:-}"; shift 2 ;;
59
+ --path-dirs) path_dirs="${2:-}"; shift 2 ;;
60
+ *) usage ;;
61
+ esac
62
+ done
63
+ [[ -n "$manifest" && -f "$manifest" ]] || usage
64
+ [[ "$platform" == mac || "$platform" == linux ]] || usage
65
+ [[ -n "$device" && -n "$administrator" && -n "$path_dirs" ]] || usage
66
+ [[ "$mode" == plan || "$mode" == apply ]] || usage
67
+
68
+ as_administrator() {
69
+ if [[ "$mode" == apply || "$(id -u)" -eq 0 ]]; then
70
+ (cd /tmp && sudo -u "$administrator" -H env PATH="$path_dirs" "$@" </dev/null)
71
+ else
72
+ (cd /tmp && env PATH="$path_dirs" "$@" </dev/null)
73
+ fi
74
+ }
75
+
76
+ scratch="$(mktemp -d "${TMPDIR:-/tmp}/git-defaults.XXXXXX")"
77
+ trap 'rm -rf -- "$scratch"' EXIT
78
+
79
+ # Current values of $1 into the global `current` array, in file order. Exit 1
80
+ # from git means the key is unset; any other failure is real and propagates.
81
+ read_current() {
82
+ local key="$1" rc=0 value
83
+ current=()
84
+ as_administrator git config --global -z --get-all "$key" >"$scratch/values" || rc=$?
85
+ if [[ "$rc" -eq 1 ]]; then
86
+ return 0
87
+ fi
88
+ [[ "$rc" -eq 0 ]] || { echo "ERROR git-config: git config --global --get-all $key failed (exit $rc)" >&2; return "$rc"; }
89
+ while IFS= read -r -d '' value; do
90
+ current+=("$value")
91
+ done <"$scratch/values"
92
+ }
93
+
94
+ gcm_path=''
95
+ gcm_resolved=0
96
+ resolve_gcm() {
97
+ [[ "$gcm_resolved" -eq 0 ]] || return 0
98
+ gcm_resolved=1
99
+ local found rc=0
100
+ found="$(as_administrator sh -c 'command -v git-credential-manager')" || rc=$?
101
+ if [[ "$rc" -eq 0 && "$found" == /* ]]; then
102
+ gcm_path="$found"
103
+ return 0
104
+ fi
105
+ echo "WARN git-config: git-credential-manager not installed on $device; credential helper not changed (install it, then the next reconcile sets it)" >&2
106
+ }
107
+
108
+ declared="$(jq -r --arg platform "$platform" --arg device "$device" '
109
+ . as $m
110
+ | ($m[$platform].devices[$device].git_user // error("git-config: device \($device) has no git_user in the manifest")) as $user
111
+ | ($m.config.git_global_config + ($m[$platform].common.git_global_config // {})
112
+ + {"user.name": $user.name, "user.email": $user.email})
113
+ | to_entries[]
114
+ | [.key, (.value | if type == "array" then . else [.] end | tojson)]
115
+ | @tsv
116
+ ' "$manifest")"
117
+
118
+ changed=0
119
+ while IFS=$'\t' read -r key values_json; do
120
+ [[ -n "$key" ]] || continue
121
+ wanted=()
122
+ needs_gcm=0
123
+ while IFS= read -r value; do
124
+ [[ "$value" != *'{git-credential-manager}'* ]] || needs_gcm=1
125
+ wanted+=("$value")
126
+ done < <(jq -r '.[]' <<<"$values_json")
127
+ if [[ "$needs_gcm" -eq 1 ]]; then
128
+ resolve_gcm
129
+ [[ -n "$gcm_path" ]] || continue
130
+ for index in "${!wanted[@]}"; do
131
+ wanted[index]="${wanted[index]//'{git-credential-manager}'/"$gcm_path"}"
132
+ done
133
+ fi
134
+
135
+ read_current "$key"
136
+ same=0
137
+ if [[ "${#current[@]}" -eq "${#wanted[@]}" ]]; then
138
+ same=1
139
+ for index in "${!wanted[@]}"; do
140
+ [[ "${current[index]}" == "${wanted[index]}" ]] || same=0
141
+ done
142
+ fi
143
+ if [[ "$same" -eq 1 ]]; then
144
+ echo "git-config: already set $key"
145
+ continue
146
+ fi
147
+
148
+ if [[ "${#wanted[@]}" -eq 1 ]]; then
149
+ shown="${wanted[0]}"
150
+ else
151
+ shown="$(jq -nc '$ARGS.positional' --args -- "${wanted[@]}")"
152
+ fi
153
+ if [[ "$mode" != apply ]]; then
154
+ echo "git-config: would set $key=$shown"
155
+ continue
156
+ fi
157
+ if [[ "${#wanted[@]}" -eq 1 && "${#current[@]}" -le 1 ]]; then
158
+ as_administrator git config --global "$key" "${wanted[0]}"
159
+ else
160
+ if [[ "${#current[@]}" -gt 0 ]]; then
161
+ as_administrator git config --global --unset-all "$key"
162
+ fi
163
+ for value in "${wanted[@]}"; do
164
+ as_administrator git config --global --add "$key" "$value"
165
+ done
166
+ fi
167
+ echo "git-config: set $key=$shown"
168
+ changed=$((changed + 1))
169
+ done <<<"$declared"
170
+
171
+ if [[ "$mode" == apply ]]; then
172
+ echo "git-config: $changed key(s) changed on $device"
173
+ fi
package/bin/health-check CHANGED
@@ -21,7 +21,14 @@ case "$device" in
21
21
  grep -q 'state = running'
22
22
  ;;
23
23
  hostinger-vps-1)
24
- systemctl is-active --quiet docker tailscaled enact-agent-vault
24
+ # One unit per call: `systemctl is-active` given several units exits 0 when at
25
+ # least one is active, so a combined call cannot see a single unit being down.
26
+ for unit_name in docker tailscaled; do
27
+ systemctl is-active --quiet "$unit_name" || {
28
+ echo "required service is not active: $unit_name" >&2
29
+ exit 1
30
+ }
31
+ done
25
32
  for container_name in nordvpn-exit-gluetun-1 nordvpn-exit-tailscale-1 traefik-traefik-1; do
26
33
  [[ "$(docker inspect -f '{{.State.Running}}' "$container_name" 2>/dev/null)" == true ]] || {
27
34
  echo "required container is not running: $container_name" >&2
package/bin/kimi-hooks CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- export PATH="/usr/local/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/bin:/bin:/usr/sbin:/sbin"
4
+ export PATH="/usr/local/bin:${HOME:-}/.bun/bin:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin:/usr/bin:/bin:/usr/sbin:/sbin"
5
5
 
6
6
  script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
7
  repo_root="$(cd "$script_dir/.." && pwd)"
@@ -0,0 +1,257 @@
1
+ # Where the published adl package lives on a host, and how to prove it is the
2
+ # `adl` the owner gets. Sourced (never executed) by bin/sync-and-reconcile,
3
+ # bin/setup, bin/reconcile-host and bin/reconcile, so the scheduled job, the
4
+ # onboarding command and the reconcile itself all ask the same question the
5
+ # same way.
6
+ #
7
+ # Hosts install with bun (Tarun, 2026-10-01): `bun install -g
8
+ # @amsterdamdatalabs/adl-agent-control`, run as the device's `administrator`.
9
+ # Measured with bun 1.4.2 (packages: workflows/scripts/release/tests/
10
+ # bun-global-install.test.ts): bun HOISTS the platform package as a SIBLING of
11
+ # the wrapper, unlike npm, which nests it inside the wrapper:
12
+ #
13
+ # $HOME/.bun/install/global/node_modules/@amsterdamdatalabs/
14
+ # adl-agent-control/ the wrapper (bin/adl.js)
15
+ # adl-agent-control-<os>-<arch>/ the platform package this job runs from
16
+ # $HOME/.bun/bin/adl -> the wrapper's bin/adl.js
17
+ #
18
+ # bun marks ONLY `bin`-field targets executable; every other shipped file stays
19
+ # 0644, so nothing here ever executes a shipped script directly (always
20
+ # `bash <script>`) or checks it with -x.
21
+ #
22
+ # The owner's bun is found through Homebrew (macOS /opt/homebrew, Linux
23
+ # /home/linuxbrew/.linuxbrew), exactly as the owner's other global tools are.
24
+
25
+ adl_wrapper_package='@amsterdamdatalabs/adl-agent-control'
26
+
27
+ adl_brew_bin_dir() {
28
+ case "$1" in
29
+ Darwin) printf '%s\n' /opt/homebrew/bin ;;
30
+ Linux) printf '%s\n' /home/linuxbrew/.linuxbrew/bin ;;
31
+ *) echo "ERROR unsupported operating system for the adl package: $1" >&2; return 1 ;;
32
+ esac
33
+ }
34
+
35
+ # PATH for the bun calls the job makes as the owner (install, nothing else).
36
+ adl_bun_path() {
37
+ local brew
38
+ brew="$(adl_brew_bin_dir "$1")" || return 1
39
+ printf '%s:/usr/bin:/bin\n' "$brew"
40
+ }
41
+
42
+ # PATH the owner gets when it runs `adl` (the repo step, and the shadow check).
43
+ # /usr/local/bin stays FIRST: git must resolve to the managed front controller,
44
+ # which routes Azure DevOps traffic through the agent identity; Homebrew's git
45
+ # would bypass it. The owner's bun bin directory comes next, then Homebrew.
46
+ adl_owner_path() {
47
+ local os="$1" home="$2" brew
48
+ brew="$(adl_brew_bin_dir "$os")" || return 1
49
+ printf '/usr/local/bin:%s/.bun/bin:%s:/usr/bin:/bin\n' "${home%/}" "$brew"
50
+ }
51
+
52
+ # The owner's home directory, asked of the owner (never guessed from the name).
53
+ adl_owner_home() {
54
+ local owner="$1" home
55
+ home="$(cd /tmp && sudo -u "$owner" -H /bin/sh -c 'printf "%s" "$HOME"')" || return 1
56
+ [[ "$home" == /* ]] || { echo "ERROR cannot resolve the home directory of $owner" >&2; return 1; }
57
+ printf '%s\n' "${home%/}"
58
+ }
59
+
60
+ # bun's global package directory for an owner home. `bun pm bin -g` errors on a
61
+ # host that has never run `bun add -g`, so the path is derived, and pinned in
62
+ # the install call below with an explicit BUN_INSTALL.
63
+ adl_global_node_modules() {
64
+ printf '%s/.bun/install/global/node_modules\n' "${1%/}"
65
+ }
66
+
67
+ adl_platform_package_dir() {
68
+ local global_node_modules="$1" os="$2" arch="$3" platform
69
+ case "$os:$arch" in
70
+ Darwin:arm64) platform=darwin-arm64 ;;
71
+ Linux:x86_64) platform=linux-x64 ;;
72
+ *) echo "ERROR unsupported platform for the adl package: $os $arch" >&2; return 1 ;;
73
+ esac
74
+ # bun hoists the platform package next to the wrapper; it is not nested.
75
+ printf '%s/%s-%s\n' "${global_node_modules%/}" "$adl_wrapper_package" "$platform"
76
+ }
77
+
78
+ # Real path of a file, following every symlink (macOS has no `readlink -f` on
79
+ # older releases, and the owner's command is always a symlink).
80
+ adl_realpath() {
81
+ local path="$1" link dir
82
+ while [[ -L "$path" ]]; do
83
+ link="$(readlink "$path")" || return 1
84
+ case "$link" in
85
+ /*) path="$link" ;;
86
+ *) path="$(dirname -- "$path")/$link" ;;
87
+ esac
88
+ done
89
+ dir="$(cd -P -- "$(dirname -- "$path")" 2>/dev/null && pwd -P)" || return 1
90
+ printf '%s/%s\n' "$dir" "$(basename -- "$path")"
91
+ }
92
+
93
+ # The registry's `latest` for the package, asked of the owner. `bun pm view`
94
+ # needs a package.json in its working directory (measured with bun 1.4.2), and the
95
+ # owner's bun global directory always has one once the package is installed.
96
+ # Prints the version, or an error and a non-zero status: a failed lookup is never
97
+ # guessed around.
98
+ adl_registry_latest() {
99
+ local owner="$1" os="$2" home="$3" bun_path answer
100
+ bun_path="$(adl_bun_path "$os")" || return 1
101
+ answer="$(sudo -u "$owner" -H env PATH="$bun_path" BUN_INSTALL="${home%/}/.bun" /bin/sh -c 'cd "$1" && bun pm view "$2" version' sh "${home%/}/.bun/install/global" "$adl_wrapper_package")" || return 1
102
+ answer="$(printf '%s' "$answer" | tr -d '[:space:]')"
103
+ [[ "$answer" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] || {
104
+ echo "ERROR the registry answered '$answer' for $adl_wrapper_package, which is not a release version" >&2
105
+ return 1
106
+ }
107
+ printf '%s\n' "$answer"
108
+ }
109
+
110
+ # Compares two release versions (x.y.z with an optional -prerelease): prints -1, 0
111
+ # or 1 for A older than, equal to, or newer than B. Semver ordering: components
112
+ # numerically (0.1.9 < 0.1.10), a pre-release is older than its release, and
113
+ # pre-release identifiers compare numerically when both are numbers, numbers
114
+ # before words, else by text. A value that is not a version is an error.
115
+ adl_semver_compare() {
116
+ local a="$1" b="$2" re='^([0-9]+)\.([0-9]+)\.([0-9]+)(-([0-9A-Za-z.-]+))?$' i x y pre_a pre_b
117
+ local -a core_a core_b ids_a ids_b
118
+ [[ "$a" =~ $re ]] || { echo "ERROR not a release version: '$a'" >&2; return 1; }
119
+ core_a=("${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}")
120
+ pre_a="${BASH_REMATCH[5]:-}"
121
+ [[ "$b" =~ $re ]] || { echo "ERROR not a release version: '$b'" >&2; return 1; }
122
+ core_b=("${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}")
123
+ pre_b="${BASH_REMATCH[5]:-}"
124
+ for i in 0 1 2; do
125
+ if ((10#${core_a[i]} > 10#${core_b[i]})); then printf '1\n'; return 0; fi
126
+ if ((10#${core_a[i]} < 10#${core_b[i]})); then printf -- '-1\n'; return 0; fi
127
+ done
128
+ if [[ -z "$pre_a" && -z "$pre_b" ]]; then printf '0\n'; return 0; fi
129
+ if [[ -z "$pre_a" ]]; then printf '1\n'; return 0; fi
130
+ if [[ -z "$pre_b" ]]; then printf -- '-1\n'; return 0; fi
131
+ IFS=. read -r -a ids_a <<<"$pre_a"
132
+ IFS=. read -r -a ids_b <<<"$pre_b"
133
+ for ((i = 0; i < ${#ids_a[@]} || i < ${#ids_b[@]}; i++)); do
134
+ if ((i >= ${#ids_a[@]})); then printf -- '-1\n'; return 0; fi
135
+ if ((i >= ${#ids_b[@]})); then printf '1\n'; return 0; fi
136
+ x="${ids_a[i]}"
137
+ y="${ids_b[i]}"
138
+ if [[ "$x" =~ ^[0-9]+$ && "$y" =~ ^[0-9]+$ ]]; then
139
+ if ((10#$x > 10#$y)); then printf '1\n'; return 0; fi
140
+ if ((10#$x < 10#$y)); then printf -- '-1\n'; return 0; fi
141
+ elif [[ "$x" =~ ^[0-9]+$ ]]; then
142
+ printf -- '-1\n'; return 0
143
+ elif [[ "$y" =~ ^[0-9]+$ ]]; then
144
+ printf '1\n'; return 0
145
+ elif [[ "$x" < "$y" ]]; then
146
+ printf -- '-1\n'; return 0
147
+ elif [[ "$x" > "$y" ]]; then
148
+ printf '1\n'; return 0
149
+ fi
150
+ done
151
+ printf '0\n'
152
+ }
153
+
154
+ # Runs `bun add --global SPEC` as the owner, pinned to the owner's ~/.bun.
155
+ adl_bun_add_global() {
156
+ local owner="$1" os="$2" home="$3" spec="$4" bun_path
157
+ bun_path="$(adl_bun_path "$os")" || return 1
158
+ # cd /tmp: the caller's working directory may be unreadable to the owner (root's home), and sudo then fails.
159
+ (cd /tmp && sudo -u "$owner" -H env PATH="$bun_path" BUN_INSTALL="${home%/}/.bun" bun add --global "$spec")
160
+ }
161
+
162
+ # The `adl` the owner gets must be THIS package: a different one earlier on the
163
+ # owner's PATH (a legacy compiled /usr/local/bin/adl from before the package
164
+ # existed is the real case) would keep running instead of the package the job
165
+ # keeps up to date. Accepts the package's own bin/adl, or the wrapper's
166
+ # bin/adl.js that sits beside it (the link `bun install -g` puts on PATH).
167
+ # adl_check_adl_not_shadowed OWNER OS OWNER_HOME PACKAGE_REAL_DIR CONTEXT
168
+ adl_check_adl_not_shadowed() {
169
+ local owner="$1" os="$2" home="$3" package_real="$4" context="$5" path resolved real wrapper_real
170
+ path="$(adl_owner_path "$os" "$home")" || return 1
171
+ resolved="$(cd /tmp && sudo -u "$owner" -H env PATH="$path" /bin/sh -c 'command -v adl' 2>/dev/null)" || resolved=""
172
+ if [[ -z "$resolved" ]]; then
173
+ {
174
+ echo "ERROR $context: $owner has no 'adl' on PATH ($path)."
175
+ echo "ERROR $context: expected ${home%/}/.bun/bin/adl from: bun install -g $adl_wrapper_package (run as $owner)"
176
+ } >&2
177
+ return 1
178
+ fi
179
+ real="$(adl_realpath "$resolved")" || real="$resolved"
180
+ wrapper_real="$(adl_realpath "$(dirname -- "$package_real")/$(basename -- "$adl_wrapper_package")/bin/adl.js" 2>/dev/null)" || wrapper_real=""
181
+ if [[ "$real" == "$package_real/bin/adl" || ( -n "$wrapper_real" && "$real" == "$wrapper_real" ) ]]; then
182
+ return 0
183
+ fi
184
+ {
185
+ echo "ERROR $context: $owner's 'adl' is $resolved (-> $real), not this package's ($package_real/bin/adl)."
186
+ echo "ERROR $context: a different adl shadows the package, so the job would keep updating one adl while $owner runs another."
187
+ echo "ERROR $context: move it aside (never delete it), then run this again:"
188
+ echo "ERROR $context: sudo mv $resolved $resolved.legacy-$(date +%Y%m%d)"
189
+ } >&2
190
+ return 1
191
+ }
192
+
193
+ # ---------------------------------------------------------------------------------------------
194
+ # The two reconcile locks (/var/run/adl-reconcile.lock, /var/run/adl-sync.lock) are mkdir locks.
195
+ # A run that is killed or hangs used to leave the directory behind, and every later `adl setup` /
196
+ # `adl reconcile` then refused "another reconcile is running" forever (m5, 2026-10-01). The holder
197
+ # now records its pid and start time in the lock directory:
198
+ # - pid running with the same start time -> refused, naming the pid;
199
+ # - pid gone, or running with a different start time (the pid was reused) -> stale;
200
+ # - no pid file (the old format, or a holder killed between mkdir and the write) -> stale only
201
+ # when older than the longest a run may take, 3 hours (the systemd unit's TimeoutStartSec).
202
+ # A stale lock is renamed aside and only then re-created with mkdir, so two takers cannot both win.
203
+ # Prints WARN on stderr when it takes over; returns 1 (after an ERROR naming the holder) when the
204
+ # lock is held. The caller removes the directory on exit.
205
+ # adl_lock_acquire LOCK_DIR "what is running"
206
+ adl_lock_max_age_seconds=10800
207
+
208
+ adl_lock_start_time() {
209
+ ps -p "$1" -o lstart= 2>/dev/null | sed -e 's/^ *//' -e 's/ *$//'
210
+ }
211
+
212
+ adl_lock_mtime() {
213
+ stat -c %Y "$1" 2>/dev/null || stat -f %m "$1" 2>/dev/null
214
+ }
215
+
216
+ adl_lock_write_holder() {
217
+ printf '%s\n' "$$" >"$1/pid"
218
+ printf '%s\n' "$(adl_lock_start_time "$$")" >"$1/started"
219
+ }
220
+
221
+ adl_lock_acquire() {
222
+ local lock="$1" running="$2" pid="" recorded_start="" actual_start reason="" now mtime aside
223
+ if mkdir "$lock" 2>/dev/null; then
224
+ adl_lock_write_holder "$lock"
225
+ return 0
226
+ fi
227
+ [[ -d "$lock" ]] && pid="$(tr -d '[:space:]' 2>/dev/null <"$lock/pid" || true)"
228
+ if [[ "$pid" =~ ^[0-9]+$ ]]; then
229
+ if kill -0 "$pid" 2>/dev/null; then
230
+ recorded_start="$(tr -d '\n' <"$lock/started" 2>/dev/null | sed -e 's/^ *//' -e 's/ *$//' || true)"
231
+ actual_start="$(adl_lock_start_time "$pid")"
232
+ if [[ -z "$recorded_start" || "$recorded_start" == "$actual_start" ]]; then
233
+ echo "ERROR $running (lock: $lock, pid $pid)" >&2
234
+ return 1
235
+ fi
236
+ fi
237
+ reason="pid $pid not running"
238
+ else
239
+ now="$(date +%s)"
240
+ mtime="$(adl_lock_mtime "$lock")" || mtime="$now"
241
+ if (( now - mtime < adl_lock_max_age_seconds )); then
242
+ echo "ERROR $running (lock: $lock, no pid recorded; it is treated as stale after 3h)" >&2
243
+ return 1
244
+ fi
245
+ reason="no pid recorded, older than 3h"
246
+ fi
247
+ echo "WARN adl reconcile: removing stale lock $lock ($reason)" >&2
248
+ aside="$lock.stale.$$"
249
+ mv -- "$lock" "$aside" 2>/dev/null || true
250
+ command rm -rf -- "$aside"
251
+ if mkdir "$lock" 2>/dev/null; then
252
+ adl_lock_write_holder "$lock"
253
+ return 0
254
+ fi
255
+ echo "ERROR $running (lock: $lock, taken by another run while removing the stale one)" >&2
256
+ return 1
257
+ }
@@ -10,8 +10,9 @@
10
10
  #
11
11
  # fleet_host_resolve COMMAND MANIFEST HOST
12
12
  # On success sets fleet_host_platform (mac|linux), fleet_host_owner,
13
- # fleet_host_minute and fleet_host_repo_location (empty when the device
14
- # declares none). On failure prints one `ERROR COMMAND: ...` line to stderr
13
+ # fleet_host_minute, fleet_host_repo_location (empty when the device
14
+ # declares none) and fleet_host_branch_max_age_days (the manifest's
15
+ # config.repo_maintain.branch_max_age_days; empty when the manifest has none). On failure prints one `ERROR COMMAND: ...` line to stderr
15
16
  # and returns 2 (unknown host) or 1 (unreadable manifest, bad entry, or the
16
17
  # host belongs to the other operating system).
17
18
 
@@ -22,13 +23,15 @@ fleet_host_resolve() {
22
23
  fleet_host_owner=""
23
24
  fleet_host_minute=""
24
25
  fleet_host_repo_location=""
26
+ fleet_host_branch_max_age_days=""
25
27
 
26
28
  [[ -f "$manifest" ]] || { echo "ERROR $command: fleet manifest missing: $manifest" >&2; return 1; }
27
29
 
28
30
  row="$(jq -r --arg host "$host" '
29
- [ ("mac", "linux") as $platform
31
+ (.config // {}) as $config
32
+ | [ ("mac", "linux") as $platform
30
33
  | (.[$platform].devices // {}) | to_entries[] | select(.key == $host)
31
- | [$platform, (.value.administrator // ""), ((.value.schedule_minute // "") | tostring), (.value.repo_location // "")]
34
+ | [$platform, (.value.administrator // ""), ((.value.schedule_minute // "") | tostring), (.value.repo_location // ""), ((($config.repo_maintain.branch_max_age_days) // "") | tostring)]
32
35
  | join("\u001f") ] | first // empty
33
36
  ' "$manifest")" || { echo "ERROR $command: cannot read fleet manifest: $manifest" >&2; return 1; }
34
37
 
@@ -43,7 +46,7 @@ fleet_host_resolve() {
43
46
 
44
47
  # A non-whitespace delimiter (unit separator): `read` collapses runs of a
45
48
  # whitespace IFS such as tab, which would shift a later field into an empty one.
46
- IFS=$'\x1f' read -r fleet_host_platform fleet_host_owner fleet_host_minute fleet_host_repo_location <<<"$row"
49
+ IFS=$'\x1f' read -r fleet_host_platform fleet_host_owner fleet_host_minute fleet_host_repo_location fleet_host_branch_max_age_days <<<"$row"
47
50
  [[ -n "$fleet_host_owner" ]] || { echo "ERROR $command: host '$host' has no administrator in $manifest" >&2; return 1; }
48
51
  [[ "$fleet_host_minute" =~ ^[0-9]+$ ]] || { echo "ERROR $command: host '$host' has no schedule_minute in $manifest" >&2; return 1; }
49
52