@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
@@ -1,12 +1,14 @@
1
- import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
1
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, existsSync } from 'node:fs'
2
2
  import { tmpdir } from 'node:os'
3
3
  import { join } from 'node:path'
4
- import { loadComplianceConfig } from './compliance-config.ts'
4
+ import { LOCKFILE_KEYS, loadComplianceConfig } from './compliance-config.ts'
5
5
  import { configEnv, type FindingsConfig, loadConfig, parseConfig, type StaticCheck } from './config.ts'
6
6
  import { BUNDLE_ROOT, COMPLIANCE_ROOT, HOOK_NAMES, type HookName } from './constants.ts'
7
7
  import { docFreshnessProblems, inScope } from './doc-freshness.ts'
8
8
  import { azureDevOpsPromotionGovernanceChecks, type Check, doctor, type DoctorReport } from './doctor.ts'
9
9
  import { commitRequiredProblems, integrityProblems } from './integrity.ts'
10
+ import { lfsChain } from './lfs.ts'
11
+ import { logHookEvent } from './hook-log.ts'
10
12
  import { type CoverageSummary, coverageRegressions, readSummary } from './coverage-ratchet.ts'
11
13
  import { type Floor, findingsProblems, parseCounterOutput, parseFloor } from './findings-ratchet.ts'
12
14
  import { bashProblem, capture, childEnv, type Env, resolveRepo, which } from './runtime.ts'
@@ -45,7 +47,16 @@ export interface RunContext {
45
47
  }
46
48
 
47
49
  /** Runs one bundled script with the repository's config exported; returns its exit status. */
48
- function runBundled(runtime: 'bash' | 'bun', script: string, args: string[], repo: string, workdir: string, context: RunContext): number {
50
+ function runBundled(
51
+ runtime: 'bash' | 'bun',
52
+ script: string,
53
+ args: string[],
54
+ repo: string,
55
+ workdir: string,
56
+ context: RunContext,
57
+ /** What to write to the script's stdin instead of {@link RunContext.stdin}: pre-push's already-captured ref lines. */
58
+ input?: string,
59
+ ): number {
49
60
  const problem = bashProblem(context.env)
50
61
  if (problem) throw new Error(problem)
51
62
  const env = { ...context.env, ...configEnv(repo, loadConfig(repo)) }
@@ -54,7 +65,7 @@ function runBundled(runtime: 'bash' | 'bun', script: string, args: string[], rep
54
65
  const proc = Bun.spawnSync([executable, join(BUNDLE_ROOT, script), ...args], {
55
66
  cwd: workdir,
56
67
  env: childEnv(env),
57
- stdin: context.stdin ?? 'inherit',
68
+ stdin: input === undefined ? (context.stdin ?? 'inherit') : Buffer.from(input),
58
69
  stdout: 'inherit',
59
70
  stderr: 'inherit',
60
71
  })
@@ -83,6 +94,49 @@ function runUv(args: string[], repo: string, context: RunContext): number {
83
94
  ...Object.entries(config.trees).flatMap(([name, path]) => ['--tree', `${name}=${path}`]),
84
95
  ...Object.entries(config.inputs).flatMap(([name, path]) => ['--input', `${name}=${path}`]),
85
96
  ...Object.entries(config.declarations).flatMap(([name, value]) => ['--declare', `${name}=${value}`]),
97
+ // Extra root names this repository allows, as comma lists. An empty list is
98
+ // skipped, never sent: the engine rejects an empty `--declare` value. Names are
99
+ // validated comma-free in `parseRoot`, so the join is unambiguous.
100
+ ...(config.root?.files.length ? ['--declare', `root-files=${config.root.files.join(',')}`] : []),
101
+ ...(config.root?.dirs.length ? ['--declare', `root-dirs=${config.root.dirs.join(',')}`] : []),
102
+ // Engine limits this repository overrides. An unset key sends nothing, so the
103
+ // engine's own default stays in force; `parseLimits` already guaranteed a
104
+ // positive integer, which is what the engine will accept.
105
+ ...(config.limits?.scripts_direct_files !== undefined
106
+ ? ['--declare', `scripts-direct-files=${config.limits.scripts_direct_files}`]
107
+ : []),
108
+ ...(config.limits?.open_gaps !== undefined ? ['--declare', `open-gaps=${config.limits.open_gaps}`] : []),
109
+ ...(config.limits?.open_plans !== undefined ? ['--declare', `open-plans=${config.limits.open_plans}`] : []),
110
+ ...(config.limits?.docs_pages_per_directory !== undefined
111
+ ? ['--declare', `docs-pages-per-directory=${config.limits.docs_pages_per_directory}`]
112
+ : []),
113
+ // LAY-004's per-tree ceilings, as ONE comma list of `<path>:<n>` entries —
114
+ // the engine splits the list and refuses anything but a repo-relative
115
+ // path and a base-10 positive integer. Entries are sorted so the argv is
116
+ // deterministic whatever the TOML table's order. An empty or undeclared
117
+ // table sends nothing: no configuration, nothing to enforce.
118
+ ...(() => {
119
+ const ceilings = config.directory_ratchet?.max_children ?? {}
120
+ const entries = Object.entries(ceilings)
121
+ if (entries.length === 0) return []
122
+ const joined = entries
123
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
124
+ .map(([path, n]) => `${path}:${n}`)
125
+ .join(',')
126
+ return ['--declare', `directory-ceilings=${joined}`]
127
+ })(),
128
+ // Extra Markdown roots, as one comma list. An empty list is skipped, never sent
129
+ // (the engine rejects an empty `--declare` value); `parseMarkdown` already
130
+ // guaranteed each root is comma-free, so the join is unambiguous.
131
+ ...(config.markdown?.roots.length ? ['--declare', `markdown-roots=${config.markdown.roots.join(',')}`] : []),
132
+ // REP-002's declared manifest and lockfile paths, one comma list per key, in
133
+ // `LOCKFILE_KEYS` order (`bun-manifests`, `bun-lockfiles`, `cargo-manifests`,
134
+ // `cargo-lockfiles`). An empty list is skipped, never sent; `parseLockfiles` already
135
+ // guaranteed each entry is comma-free and that a half is set as a pair.
136
+ ...LOCKFILE_KEYS.flatMap((key) => {
137
+ const entries = config.lockfiles?.[key] ?? []
138
+ return entries.length > 0 ? ['--declare', `${key.replace('_', '-')}=${entries.join(',')}`] : []
139
+ }),
86
140
  ]
87
141
  const proc = Bun.spawnSync(
88
142
  [executable, 'run', '--project', COMPLIANCE_ROOT, 'adl-compliance', '--root', repo, ...injected, ...args],
@@ -286,6 +340,45 @@ export function stagedCoverageStep(
286
340
  return run.code
287
341
  }
288
342
 
343
+ /**
344
+ * The coverage MEASUREMENT at push: the repository's declared push-phase
345
+ * command, not ours.
346
+ *
347
+ * The same division as `stagedCoverageStep`, one phase later: the repository
348
+ * says HOW to measure (`[controls.coverage].produce_push`), this package
349
+ * decides WHEN (pre-push, before the ratchet grades what the measurement
350
+ * wrote) and grades the exit status. The estate moved the measurement here
351
+ * because it serialises minutes of work; commits stay fast, and the push
352
+ * measures then grades in one gate.
353
+ *
354
+ * `produce_push` is a separate declaration from `produce` rather than a reuse:
355
+ * a commit-phase producer may stage a refreshed summary, while a push-phase
356
+ * command runs against an immutable HEAD — a check-mode run that never mutates
357
+ * the index is the shape.
358
+ *
359
+ * Absence is deliberately NOT a skip, same rule as at commit: with the gate
360
+ * armed and no command declared, this is could-not-run — a step that measures
361
+ * nothing is not a pass.
362
+ */
363
+ export function coverageProduceStep(
364
+ producePush: string,
365
+ runCommand: (command: string) => { code: number; stdout: string; stderr: string },
366
+ err: (line: string) => void,
367
+ ): number {
368
+ const declared = producePush.trim()
369
+ if (declared === '') {
370
+ err('pre-push: FAIL — pre_push.coverage_produce is on but [controls.coverage].produce_push declares no command.')
371
+ err(' A step that measures nothing is not a pass.')
372
+ return 1
373
+ }
374
+ const run = runCommand(declared)
375
+ if (run.code !== 0) {
376
+ err(`pre-push: FAIL — coverage.produce_push exited ${run.code}: ${declared}`)
377
+ if (run.stderr.trim() !== '') err(` ${run.stderr.trim().split('\n').slice(-3).join('\n ')}`)
378
+ }
379
+ return run.code
380
+ }
381
+
289
382
  /**
290
383
  * The coverage ratchet at push: did anything get worse between the baseline and
291
384
  * what is being pushed.
@@ -365,6 +458,10 @@ export function declaredChecksFor(
365
458
  // here, on purpose — see tests/declared-hook-scripts.test.ts.
366
459
  case 'post-checkout':
367
460
  return []
461
+ // `post-merge` exists only to chain Git LFS (scripts/hooks/lib/lfs.sh); like
462
+ // post-checkout it has no repository-declared seam.
463
+ case 'post-merge':
464
+ return []
368
465
  }
369
466
  }
370
467
 
@@ -524,7 +621,7 @@ export function doctorGate(readDoctor: () => DoctorReport, err: (line: string) =
524
621
  * `loadConfig`) can throw on a malformed config; that is reported the same
525
622
  * way as an unarmed check rather than crashing the hook process.
526
623
  */
527
- export function azureDevOpsPromotionGovernanceGate(readChecks: () => Check[], err: (line: string) => void): number {
624
+ function azureDevOpsPromotionGovernanceGate(readChecks: () => Check[], err: (line: string) => void): number {
528
625
  let checks: Check[]
529
626
  try {
530
627
  checks = readChecks()
@@ -557,7 +654,7 @@ export function runHook(name: HookName, args: string[], context: RunContext): nu
557
654
  const top = capture(['git', 'rev-parse', '--show-toplevel'], context.cwd, context.env)
558
655
  if (top.code !== 0) throw new Error(`not inside a git work tree: ${context.cwd}`)
559
656
  const repo = top.stdout.trim()
560
- // The worktree-count backstop (STATUS.md item 2, adl-m3-items). Git has no
657
+ // The worktree-count backstop (docs/operations/worktree-guard.md). Git has no
561
658
  // pre-worktree hook, so this cannot stop `git worktree add` from creating
562
659
  // the checkout — it runs after, in the NEW worktree, and actively removes it
563
660
  // when the repository is over the limit. See ./worktree-guard.ts for why
@@ -597,34 +694,41 @@ export function runHook(name: HookName, args: string[], context: RunContext): nu
597
694
  // The armed compliance gates, before the bundled script. Until this existed
598
695
  // `[controls.compliance].enabled` armed nothing that ever ran: no bundled hook
599
696
  // mentions compliance and `runHook` never reached `runUv`, so a gate was
600
- // "enabled" and dormant. Off by default -- a repository opts in with
601
- // `pre_commit.compliance = true`, the same way it opts into every other step.
697
+ // "enabled" and dormant. `compliance` defaults ON, but the step is a no-op
698
+ // when `[controls.compliance].enabled` is empty -- the same "wrapper on,
699
+ // work gated by configured state" shape as stagedCoverage/coverageRatchet/
700
+ // findingsRatchet above and the docFreshness floor's empty-files case.
602
701
  if (name === 'pre-commit' && stagedComplianceEnabled(repo, context.env)) {
603
- const code = withStagedIndexSnapshot(repo, context.env, (snapshot) => {
604
- // Activation is itself read from the candidate commit. An unstaged config
605
- // edit cannot turn a staged gate off (or on), and a commit introducing the
606
- // gate proves it is armed before it lands.
607
- return complianceStep(() =>
608
- runUv(['--files', snapshot.files, '--exhaustive'], snapshot.root, context),
609
- )
610
- })
611
- if (code !== 0) return code
702
+ if (loadComplianceConfig(repo).enabled.length > 0) {
703
+ const code = withStagedIndexSnapshot(repo, context.env, (snapshot) => {
704
+ // Activation is itself read from the candidate commit. An unstaged config
705
+ // edit cannot turn a staged gate off (or on), and a commit introducing the
706
+ // gate proves it is armed before it lands.
707
+ return complianceStep(() =>
708
+ runUv(['--files', snapshot.files, '--exhaustive'], snapshot.root, context),
709
+ )
710
+ })
711
+ if (code !== 0) return code
712
+ }
612
713
  }
613
714
  // Staged coverage, through the repository's declared command. Was a hardcoded
614
715
  // shell-out inside the bundled pre-commit into this package's copy of the
615
716
  // consumer's coverage code; see `stagedCoverageStep`.
717
+ // Safety gate: `stagedCoverage` defaults ON; a repository that has not declared
718
+ // `[controls.coverage].produce` has nothing to run, so the step is a no-op.
719
+ // Same shape as `compliance` below: the wrapper is on, the work is gated by
720
+ // whether the repository opted into the configuration it needs.
616
721
  if (name === 'pre-commit' && loadConfig(repo).preCommit.stagedCoverage) {
617
722
  const config = loadConfig(repo)
618
- const code = stagedCoverageStep(
619
- config.coverage,
620
- (command) => capture(['bash', '-c', command], repo, context.env),
621
- // No fallback. The bundled coverage mechanism is gone, so a repository
622
- // that arms staged coverage without declaring `produce` is could-not-run,
623
- // which is the honest answer rather than a step that measures nothing.
624
- null,
625
- context.err,
626
- )
627
- if (code !== 0) return code
723
+ if (config.coverage.produce.trim() !== '') {
724
+ const code = stagedCoverageStep(
725
+ config.coverage,
726
+ (command) => capture(['bash', '-c', command], repo, context.env),
727
+ null,
728
+ context.err,
729
+ )
730
+ if (code !== 0) return code
731
+ }
628
732
  }
629
733
  // The Azure DevOps promotion-governance checks, moved here from pre-commit
630
734
  // (2026-09-28, Tarun): network access and a minted token are already
@@ -639,49 +743,101 @@ export function runHook(name: HookName, args: string[], context: RunContext): nu
639
743
  )
640
744
  if (code !== 0) return code
641
745
  }
746
+ // The coverage MEASUREMENT at push, before the ratchet below grades what it
747
+ // wrote. Opt-in: `coverageProduce` defaults OFF (new gates opt in), unlike
748
+ // the ratchet's ON. Armed with no declared command, the step itself fails
749
+ // loud — a step that measures nothing is not a pass; off is a no-op.
750
+ if (name === 'pre-push' && loadConfig(repo).prePush.coverageProduce) {
751
+ const code = coverageProduceStep(
752
+ loadConfig(repo).coverage.producePush,
753
+ (command) => capture(['bash', '-c', command], repo, context.env),
754
+ context.err,
755
+ )
756
+ if (code !== 0) return code
757
+ }
642
758
  // The coverage ratchet at push. Was `command.ts check-push` inside the bundled
643
759
  // pre-push, running the package's copy of the consumer's coverage code.
760
+ // Safety gate: `coverageRatchet` defaults ON; the ratchet has no baseline
761
+ // unless the repository has staged a coverage summary file at the path
762
+ // `[controls.coverage].summary` declares. With no file on either side of the
763
+ // index, the step is a no-op — same shape as compliance below.
644
764
  if (name === 'pre-push' && loadConfig(repo).prePush.coverageRatchet) {
645
765
  const config = loadConfig(repo)
646
766
  const rel = config.coverage.summary
647
- const at = (ref: string) => {
648
- const shown = capture(['git', 'show', `${ref}:${rel}`], repo, context.env)
649
- return shown.code === 0 ? shown.stdout : null
767
+ if (rel !== '' && (existsSync(join(repo, rel)) || capture(['git', 'cat-file', '-e', `HEAD:${rel}`], repo, context.env).code === 0)) {
768
+ const at = (ref: string) => {
769
+ const shown = capture(['git', 'show', `${ref}:${rel}`], repo, context.env)
770
+ return shown.code === 0 ? shown.stdout : null
771
+ }
772
+ const code = coverageRatchetStep(
773
+ rel,
774
+ () => at('HEAD~1'),
775
+ () => at('HEAD') ?? readFileSync(join(repo, rel), 'utf8'),
776
+ context.err,
777
+ )
778
+ if (code !== 0) return code
650
779
  }
651
- const code = coverageRatchetStep(
652
- rel,
653
- () => at('HEAD~1'),
654
- () => at('HEAD') ?? readFileSync(join(repo, rel), 'utf8'),
655
- context.err,
656
- )
657
- if (code !== 0) return code
658
780
  }
659
781
  // The findings ratchet, before the bundled script, same shape as compliance
660
- // above. `[controls.findings]` was parsed, defaulted, exported into every
661
- // child process, documented and doctored -- and read by nothing. Opt-in via
662
- // `pre_push.findings_ratchet`, off by default.
782
+ // above. Safety gate: `findingsRatchet` defaults ON; the gate has nothing
783
+ // to grade unless `[controls.findings].floor` exists. With no floor file,
784
+ // the step is a no-op — same shape as compliance and coverageRatchet.
663
785
  if (name === 'pre-push' && loadConfig(repo).prePush.findingsRatchet) {
664
- const code = runFindingsRatchet(repo, context)
665
- if (code !== 0) return code
786
+ const floorPath = loadConfig(repo).findings.floor
787
+ if (floorPath !== '' && existsSync(join(repo, floorPath))) {
788
+ const code = runFindingsRatchet(repo, context)
789
+ if (code !== 0) return code
790
+ }
666
791
  }
792
+ // git writes pre-push's ref lines on stdin exactly once. The bundled script
793
+ // reads them for the branch rules and `git lfs pre-push` needs them too, so
794
+ // they are read here, once, and fed to both. Every other hook has no stdin
795
+ // contract and passes none.
796
+ const pushUpdates = name === 'pre-push' && (context.stdin ?? 'inherit') === 'inherit' ? readFileSync(0, 'utf8') : ''
667
797
  // The package's standard bash: the shared policy every repository gets.
668
- const standard = runBundled('bash', `hooks/${name}`, args, repo, context.cwd, context)
798
+ const standard = runBundled(
799
+ 'bash',
800
+ `hooks/${name}`,
801
+ args,
802
+ repo,
803
+ context.cwd,
804
+ context,
805
+ name === 'pre-push' ? pushUpdates : undefined,
806
+ )
669
807
  if (standard !== 0) return standard
670
808
 
671
809
  // pre-push owns its declared static checks inside the bundled script: it
672
810
  // exports immutable HEAD, runs them there, and caches the pass by tree OID.
673
811
  // Running them again here would grade the dirty checkout a second time.
674
- if (name === 'pre-push') return 0
812
+ // Git LFS comes last, after every adl check passed: a refused push uploads
813
+ // nothing. See ./lfs.ts for why this is here and not in the bash.
814
+ if (name === 'pre-push') return lfsStep(name, args, pushUpdates, repo, context)
675
815
 
676
816
  // Then the repository's own script, on top (owner: "standard bash + script
677
817
  // calling on top"). Declared per hook, run from the repo root, never named by
678
818
  // this package. Absent means nothing runs -- there is no bundled default for
679
819
  // a repository's own policy.
680
- return preCommitChecks(
820
+ const declared = preCommitChecks(
681
821
  declaredChecksFor(name, loadConfig(repo)),
682
822
  (command) => capture(['bash', '-c', command], repo, context.env),
683
823
  context.err,
684
824
  )
825
+ const lfs = lfsStep(name, args, '', repo, context)
826
+ return declared !== 0 ? declared : lfs
827
+ }
828
+
829
+ /** `lfsChain` plus the log row the bundled script cannot write: it has already logged PASS by the time git-lfs runs. */
830
+ function lfsStep(name: HookName, args: string[], input: string, repo: string, context: RunContext): number {
831
+ const failures: string[] = []
832
+ const code = lfsChain(name, args, input, repo, {
833
+ env: context.env,
834
+ err: (line) => {
835
+ failures.push(line)
836
+ context.err(line)
837
+ },
838
+ })
839
+ if (code !== 0) logHookEvent(name, `FAIL exit=${code}`, '', failures[0] ?? `git lfs ${name} failed`, repo)
840
+ return code
685
841
  }
686
842
 
687
843
  // `_tool` is unread on purpose. `compliance` is the only tool left, and it is a
@@ -48,7 +48,7 @@ interface GitignoreRecord {
48
48
  created: boolean
49
49
  }
50
50
 
51
- export type TableConfigRecord = { created: boolean; sha256: string } | null
51
+ type TableConfigRecord = { created: boolean; sha256: string } | null
52
52
 
53
53
  export interface Lock {
54
54
  lockVersion: 1
@@ -79,9 +79,9 @@ export function vendoredPromotePath(): string {
79
79
 
80
80
  /**
81
81
  * The stub execs `adl repo controls hook <name>`, not a standalone
82
- * `adl-repo-controls` binary (adl-m3-finish item 1, 2026-09-27): this
82
+ * `adl-repo-controls` binary (2026-09-27): this
83
83
  * package is folded into the `adl` binary's `repo controls` namespace
84
- * (docs/architecture/cli-design.md §6, STATUS.md item 10) — hard cut, no compat shim, the
84
+ * (docs/architecture/cli-design.md §6) — hard cut, no compat shim, the
85
85
  * same decision `adl hook` already made for agent-hooks. `adl` is what a
86
86
  * repository's hooks now require on PATH, and `adl` is what the fleet
87
87
  * actually installs (bin/reconcile's `bun-compile` manager); see
@@ -335,15 +335,21 @@ function ownedTable(text: string, tableName: string): Table | null {
335
335
  }
336
336
  }
337
337
 
338
- /** Reject malformed text that declares itself to be this package's table. */
338
+ /**
339
+ * Reject malformed text that declares itself to be this package's table, and the two
340
+ * array tables install owns outright: `[[controls]]` and `[[controls.compliance]]`.
341
+ * Array tables NESTED under an owned table (`[[controls.pre_push.static_checks]]`,
342
+ * what `adl migrate` writes) are ordinary user data: the same value as the inline
343
+ * form, kept byte-for-byte.
344
+ */
339
345
  function assertNoMalformedOwnedHeader(text: string): void {
340
346
  for (const raw of text.split('\n')) {
341
347
  const line = raw.trim()
342
- const ownedArrayTable = /^\[\[\s*(controls(?:\.[A-Za-z0-9_-]+)*)\s*\]\]\s*(?:#.*)?$/.exec(line)
343
- if (ownedArrayTable !== null) {
344
- throw new Error(`${CONFIG_REL} declares unsupported owned array table: [[${ownedArrayTable[1]}]]`)
348
+ const arrayTable = /^\[\[\s*(controls(?:\.[A-Za-z0-9_-]+)*)\s*\]\]\s*(?:#.*)?$/.exec(line)
349
+ if (arrayTable !== null && (arrayTable[1] === 'controls' || arrayTable[1] === 'controls.compliance')) {
350
+ throw new Error(`${CONFIG_REL} declares unsupported owned array table: [[${arrayTable[1]}]]`)
345
351
  }
346
- if (/^\[\[\s*controls(?:\.|\]|\s)/.test(line)) {
352
+ if (arrayTable === null && /^\[\[\s*controls(?:\.|\]|\s)/.test(line)) {
347
353
  throw new Error(`${CONFIG_REL} [controls] is invalid TOML: malformed table header`)
348
354
  }
349
355
  if (line === '[controls' || /^\[controls(?=\.|\])/.test(line) && !/^\[controls(?:\.[A-Za-z0-9_-]+)*\]$/.test(line)) {
@@ -7,7 +7,7 @@
7
7
  * adl hook is responsible for refusing that escape hatch to agents.
8
8
  */
9
9
 
10
- export const WIRING: readonly string[] = ['adl-config.toml']
10
+ const WIRING: readonly string[] = ['adl-config.toml']
11
11
 
12
12
  // Tarun, 2026-09-30: `.workflows/` and every agent surface root are protected
13
13
  // like `workflows/` — a dirty path under any of them blocks a normal commit and
@@ -50,7 +50,7 @@ function patternToRegExp(pattern: string): RegExp {
50
50
  }
51
51
 
52
52
  /** Match the repository-declared portion of the protected set. */
53
- export function protectedMatchers(watched: readonly string[]): ((path: string) => boolean)[] {
53
+ function protectedMatchers(watched: readonly string[]): ((path: string) => boolean)[] {
54
54
  const wiring = WIRING.map((name) => (path: string) => path === name)
55
55
  const declared = watched.map((pattern) => {
56
56
  const re = patternToRegExp(pattern)
@@ -62,7 +62,7 @@ export function protectedMatchers(watched: readonly string[]): ((path: string) =
62
62
  const isUnderFixedRoot = (path: string): boolean => FIXED_ROOTS.some((root) => path === root || path.startsWith(`${root}/`))
63
63
 
64
64
  /** Centrally managed repository files that must travel with the next commit. */
65
- export const isTopLevelDotPath = (path: string): boolean => path.startsWith('.')
65
+ const isTopLevelDotPath = (path: string): boolean => path.startsWith('.')
66
66
 
67
67
  /**
68
68
  * The single source of truth used for final-index and dirty-worktree checks.
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Git LFS chaining for the projected hooks.
3
+ *
4
+ * WHY THIS EXISTS (live, 2026-10-01, enact-combined). `adl repo controls install`
5
+ * sets `core.hooksPath=.workflows/hooks`, so git never runs the
6
+ * `.git/hooks/{pre-push,post-checkout,post-commit,post-merge}` that
7
+ * `git lfs install` writes. adl's own hooks did not call git-lfs, so a push of a
8
+ * commit carrying an LFS pointer succeeded WITHOUT uploading the object and left
9
+ * a broken pointer on the remote. Each of those four hooks now chains to
10
+ * `git lfs <hook> "$@"` from `runHook`.
11
+ *
12
+ * Rules, in order:
13
+ * 1. A repository that does not use LFS never calls git lfs.
14
+ * 2. A repository that uses LFS with no `git-lfs` on PATH FAILS LOUD. There is
15
+ * no silent skip: a skipped pre-push is exactly the broken-pointer bug.
16
+ * 3. A failing `git lfs <hook>` is the hook's failure (for pre-push: the push
17
+ * fails).
18
+ *
19
+ * WHY THIS IS TYPESCRIPT, NOT THE BUNDLED BASH. bin/git-ado authenticates an
20
+ * agent's `git push` by exporting `GIT_CONFIG_COUNT` and a
21
+ * `GIT_CONFIG_KEY_n=http.https://<host>/.extraHeader` /
22
+ * `GIT_CONFIG_VALUE_n=Authorization: Bearer <token>` pair to the real git; git
23
+ * passes its environment to the hook. `runBundled` hands the bash scripts a
24
+ * `childEnv` with every `GIT_CONFIG_*` removed, on purpose, so adl's own checks
25
+ * never hold the token. git-lfs is the one child that must, and it must hold
26
+ * exactly the pairs the parent `git push` had -- the same mechanism, never a
27
+ * second mint, never the human's credential helper (tests/lfs-credential-
28
+ * boundary.test.ts proves git-lfs reads that header and does not fall back).
29
+ * `runHook` is the only place that still has the unstripped `context.env`, so
30
+ * the chaining lives here and only this one spawn is given it.
31
+ */
32
+ import { existsSync, readFileSync } from 'node:fs'
33
+ import { join } from 'node:path'
34
+ import type { HookName } from './constants.ts'
35
+ import { capture, type Env, which } from './runtime.ts'
36
+
37
+ /** The hooks `git lfs install` writes -- and so the ones a controlled repository must chain. */
38
+ export const LFS_HOOKS: readonly HookName[] = ['pre-push', 'post-checkout', 'post-commit', 'post-merge']
39
+
40
+ const FILTER_LFS = /filter=lfs(\s|$)/m
41
+
42
+ /**
43
+ * Whether the repository uses Git LFS: a `filter=lfs` attribute in any
44
+ * `.gitattributes` (tracked, or untracked and not ignored) or in
45
+ * `.git/info/attributes`, a `.lfsconfig`, or `lfs.*` in the repository's own git
46
+ * config. The git calls run through `capture`, whose env carries no
47
+ * `GIT_CONFIG_*`.
48
+ */
49
+ export function repoUsesLfs(repo: string, env: Env): boolean {
50
+ if (existsSync(join(repo, '.lfsconfig'))) return true
51
+ const grep = capture(['git', 'grep', '-q', '--untracked', '-E', 'filter=lfs([[:space:]]|$)', '--', ':(glob)**/.gitattributes'], repo, env)
52
+ if (grep.code === 0) return true
53
+ const info = capture(['git', 'rev-parse', '--git-path', 'info/attributes'], repo, env)
54
+ if (info.code === 0) {
55
+ const path = join(repo, info.stdout.trim())
56
+ if (existsSync(path) && FILTER_LFS.test(readFileSync(path, 'utf8'))) return true
57
+ }
58
+ return capture(['git', 'config', '--local', '--name-only', '--get-regexp', '^lfs\\.'], repo, env).code === 0
59
+ }
60
+
61
+ export interface LfsChainContext {
62
+ /** The environment git handed the hook, UNSTRIPPED: it is what authenticates git-lfs. */
63
+ env: Env
64
+ err: (line: string) => void
65
+ }
66
+
67
+ /**
68
+ * Chains one hook to `git lfs <hook> <args...>`, feeding `input` on stdin
69
+ * (pre-push's ref lines; `''` for the others). Returns the hook's exit status.
70
+ *
71
+ * `run` is injected so the decision (skip / fail loud / propagate the status) is
72
+ * unit-testable without spawning git-lfs; production passes {@link spawnGitLfs}.
73
+ */
74
+ export function lfsChain(
75
+ hook: HookName,
76
+ args: string[],
77
+ input: string,
78
+ repo: string,
79
+ context: LfsChainContext,
80
+ run: (hook: HookName, args: string[], input: string, repo: string, env: Env) => number = spawnGitLfs,
81
+ ): number {
82
+ if (!LFS_HOOKS.includes(hook)) return 0
83
+ if (!repoUsesLfs(repo, context.env)) return 0
84
+ if (!which('git-lfs', context.env)) {
85
+ context.err(`${hook}: FAIL — this repository uses Git LFS but 'git-lfs' is not on PATH.`)
86
+ context.err(' Skipping it would leave LFS pointers without their objects; refusing instead.')
87
+ context.err(' Install git-lfs (macOS: brew install git-lfs; Debian/Ubuntu: apt install git-lfs), then retry.')
88
+ context.err(' If this repository no longer uses LFS, remove the filter=lfs lines from .gitattributes (and any .lfsconfig).')
89
+ return 1
90
+ }
91
+ const code = run(hook, args, input, repo, context.env)
92
+ if (code !== 0) context.err(`${hook}: FAIL — git lfs ${hook} exited ${code}.`)
93
+ return code
94
+ }
95
+
96
+ /**
97
+ * `git lfs <hook> <args...>` with the caller's whole environment, GIT_CONFIG_*
98
+ * included. This is deliberately NOT `childEnv`: see the header.
99
+ */
100
+ function spawnGitLfs(hook: HookName, args: string[], input: string, repo: string, env: Env): number {
101
+ const git = which('git', env)
102
+ if (!git) throw new Error('git not found on PATH')
103
+ const proc = Bun.spawnSync([git, 'lfs', hook, ...args], {
104
+ cwd: repo,
105
+ env: { ...process.env, ...env },
106
+ stdin: Buffer.from(input),
107
+ stdout: 'inherit',
108
+ stderr: 'inherit',
109
+ })
110
+ return proc.exitCode ?? 1
111
+ }
@@ -6,9 +6,9 @@
6
6
  * repositories. Do not add a second implementation here.
7
7
  */
8
8
  export {
9
- BUILD_POLICY_TYPE,
9
+
10
10
  MERGE_STRATEGIES,
11
- MINIMUM_REVIEWER_POLICY_TYPE,
11
+
12
12
  ensurePromotionPullRequest,
13
13
  main,
14
14
  promotionPolicyReceipts,
@@ -16,10 +16,10 @@ export {
16
16
  type FetchImpl,
17
17
  type MergeStrategy,
18
18
  type PolicyConfiguration,
19
- type PolicyConfigurationList,
20
- type PolicyScope,
21
- type PromotionPolicyReceipts,
22
- type PromotionPolicyRequirement,
23
- type PromotionRequest,
24
- type PromotionResult,
19
+
20
+
21
+
22
+
23
+
24
+
25
25
  } from '../assets/promote.ts'
@@ -76,9 +76,17 @@ export function gitOrThrow(repo: string, args: string[]): string {
76
76
  return result.stdout
77
77
  }
78
78
 
79
- /** The work tree root for `path`; `path` must be that root. */
79
+ /** The work tree root for `path`; `path` must be that root, or empty to default to cwd's git root. */
80
80
  export function resolveRepo(path: string): string {
81
81
  const absolute = resolve(path)
82
+ // An empty `path` defaults to cwd's git work tree root — `--repo` is no
83
+ // longer a typing tax when the caller is already inside the repository.
84
+ // All `--repo`-taking commands inherit this; the explicit flag still wins.
85
+ if (absolute === '' || (path === '' && !existsSync(absolute))) {
86
+ const cwdTop = capture(['git', 'rev-parse', '--show-toplevel'], process.cwd())
87
+ if (cwdTop.code !== 0) throw new Error(`not a git repository: ${process.cwd()} (cwd)`)
88
+ return cwdTop.stdout.trim()
89
+ }
82
90
  if (!existsSync(absolute)) throw new Error(`repository path does not exist: ${absolute}`)
83
91
  const result = capture(['git', 'rev-parse', '--show-toplevel'], absolute)
84
92
  if (result.code !== 0) throw new Error(`not a git repository: ${absolute}`)
@@ -5,7 +5,7 @@ import { HOOK_NAMES, HOOKS_PATH, INSTALL_HINT, LOCK_REL, MANAGED_ASSETS, VERSION
5
5
  import { checkGitignoreBlock } from './gitignore-block.ts'
6
6
  import { readLock, stubText, type Lock } from './install.ts'
7
7
 
8
- export interface WorkflowsDriftFinding {
8
+ interface WorkflowsDriftFinding {
9
9
  /** Same ids `doctor()` uses for the same surface (`promote`, `hook:pre-commit`, `version`, ...). */
10
10
  id: string
11
11
  message: string
@@ -36,13 +36,12 @@ export interface WorkflowsDriftReport {
36
36
  * Written for exactly the drift this package's own dogfood install had on
37
37
  * 2026-09-27: adl-agent-control's root `.workflows/repo-controls.lock.json`
38
38
  * claimed version 0.3.2 with only `promote.ts` and the four hook stubs
39
- * recorded, while `version-bump.sh` and
39
+ * recorded, while `version-bump.sh` and the since-retired
40
40
  * `scripts/enable-integration-auto-complete.mjs` existed on disk with no
41
41
  * lock entry at all, and the `adl-config.toml` the lock's own `config`
42
42
  * record claimed to have created did not exist. `checkWorkflowsDrift` would
43
- * report `version-bump` and `integration-auto-complete` findings for the
44
- * missing lock entries and a `version` finding for the lock/package
45
- * mismatch — `doctor()` already does, but nothing outside a full `doctor`
43
+ * report a finding for each missing lock entry and a `version` finding for
44
+ * the lock/package mismatch — `doctor()` already does, but nothing outside a full `doctor`
46
45
  * run could ask for just this until now.
47
46
  */
48
47
  export function checkWorkflowsDrift(repoRoot: string): WorkflowsDriftReport {
@@ -1,7 +1,7 @@
1
1
  /**
2
- * The `post-checkout` worktree-count backstop (STATUS.md item 2, adl-m3-items,
3
- * 2026-09-27 — "add a hook to detect existing worktrees and deny worktree
4
- * creation — maximum worktrees allowed is 3 including the main folder").
2
+ * The `post-checkout` worktree-count backstop (Tarun, 2026-09-27: "add a hook
3
+ * to detect existing worktrees and deny worktree creation — maximum worktrees
4
+ * allowed is 3 including the main folder"; see docs/operations/worktree-guard.md).
5
5
  *
6
6
  * Git has no pre-worktree hook: nothing runs BEFORE `git worktree add` creates
7
7
  * the new checkout. What git DOES run is `post-checkout`, and — per git's own