@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.30 → 0.1.33

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 (199) hide show
  1. package/bin/adl +2 -2
  2. package/bin/health-check +2 -2
  3. package/bin/reconcile +41 -21
  4. package/bin/setup +2 -1
  5. package/package.json +1 -1
  6. package/packages/agent-hooks/README.md +77 -8
  7. package/packages/agent-hooks/assets/opencode-plugin.template.ts +2 -6
  8. package/packages/agent-hooks/src/deploy/opencode-template.ts +5 -3
  9. package/packages/agent-hooks/src/hooks/crg/background.ts +7 -4
  10. package/packages/agent-hooks/src/hooks/log.ts +12 -12
  11. package/packages/agent-hooks/src/log/writer.ts +170 -24
  12. package/packages/agent-hooks/src/main.ts +1 -1
  13. package/packages/agent-hooks/src/rules/impl/adl-git-push-policy.ts +5 -4
  14. package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +1 -1
  15. package/packages/agent-hooks/src/rules/shell/invocation.ts +2 -1
  16. package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +19 -5
  17. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +1 -1
  18. package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +24 -2
  19. package/packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh +19 -45
  20. package/packages/agent-skills/assets/diagnosing-bugs/SKILL.md +9 -0
  21. package/packages/agent-skills/assets/diagnosing-bugs/scripts/hitl-loop.template.sh +1 -12
  22. package/packages/azdo/src/lib/commit-types.ts +8 -0
  23. package/packages/azdo/src/lib/validate-pr-metadata.ts +4 -1
  24. package/packages/fleet/manifest.json +2 -1
  25. package/packages/fleet/src/repo-maintain.ts +5 -2
  26. package/packages/repo-controls/AGENTS.md +8 -5
  27. package/packages/repo-controls/README.md +348 -58
  28. package/packages/repo-controls/assets/GLOBAL-INVARIANTS.md +21 -0
  29. package/packages/repo-controls/assets/promote.ts +7 -85
  30. package/packages/repo-controls/assets/reconcile-guard.sh +1 -14
  31. package/packages/repo-controls/assets/scaffold/docs/AGENTS.md +25 -0
  32. package/packages/repo-controls/assets/scaffold/docs/CLAUDE.md +1 -0
  33. package/packages/repo-controls/assets/scaffold/docs/decisions/index.md +17 -0
  34. package/packages/repo-controls/assets/scaffold/docs/notes.md +20 -0
  35. package/packages/repo-controls/assets/scaffold/plans/AGENTS.md +28 -0
  36. package/packages/repo-controls/assets/scaffold/plans/CLAUDE.md +1 -0
  37. package/packages/repo-controls/assets/scaffold/plans/GAPS-AND-FINDINGS.md +12 -0
  38. package/packages/repo-controls/assets/scaffold/plans/LEDGER.md +8 -0
  39. package/packages/repo-controls/assets/scaffold/plans/features/.gitkeep +0 -0
  40. package/packages/repo-controls/assets/scaffold/root/AGENTS.md +22 -0
  41. package/packages/repo-controls/assets/scaffold/root/CLAUDE.md +1 -0
  42. package/packages/repo-controls/assets/scaffold/workflows/AGENTS.md +16 -0
  43. package/packages/repo-controls/assets/scaffold/workflows/CLAUDE.md +1 -0
  44. package/packages/repo-controls/assets/scaffold/workflows/pipelines/.gitkeep +0 -0
  45. package/packages/repo-controls/assets/scaffold/workflows/scripts/.gitkeep +0 -0
  46. package/packages/repo-controls/assets/version-bump.sh +7 -48
  47. package/packages/repo-controls/package/adl-compliance/AGENTS.md +25 -7
  48. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +6 -4
  49. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/check.py +1 -1
  50. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +1 -1
  51. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +7 -42
  52. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/excluded/web/i18n/locales/zh-CN/strings.ts +1 -0
  53. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +10 -44
  54. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +1 -1
  55. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +1 -1
  56. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +1 -1
  57. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/check.py +1 -7
  58. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/test_check.py +12 -3
  59. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +15 -23
  60. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +0 -136
  61. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +18 -0
  62. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +2 -2
  63. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +0 -7
  64. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/check.py +2 -4
  65. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/stable-delivery.md +10 -0
  66. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/stable-recommendation.md +12 -0
  67. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/target-narration.md +10 -0
  68. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +6 -7
  69. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/ci_paths/test_check.py +3 -3
  70. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +10 -12
  71. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +1 -1
  72. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/web/src/renderer/solo/item.ts +1 -0
  73. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/web/src/a.ts +1 -0
  74. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/web/src/b.ts +1 -0
  75. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +1 -1
  76. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/fresh/fresh.ts +1 -0
  77. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub1/only.ts +1 -0
  78. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub10/only.ts +1 -0
  79. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub11/only.ts +1 -0
  80. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub2/only.ts +1 -0
  81. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub3/only.ts +1 -0
  82. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub4/only.ts +1 -0
  83. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub5/only.ts +1 -0
  84. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub6/only.ts +1 -0
  85. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub7/only.ts +1 -0
  86. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub8/only.ts +1 -0
  87. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub9/only.ts +1 -0
  88. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/services/i18n/locales/de/common.json +1 -0
  89. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/solo/item.ts +1 -0
  90. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/vendored-crate/Cargo.toml +2 -0
  91. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +1 -1
  92. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/web/src/renderer/solo/item.ts +1 -0
  93. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +1 -1
  94. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/web/src/a.ts +1 -0
  95. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/web/src/b.ts +1 -0
  96. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/web/src/c.ts +1 -0
  97. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +2 -2
  98. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/web/src/renderer/solo/item.ts +1 -0
  99. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +1 -1
  100. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/fresh/fresh.ts +1 -0
  101. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub1/only.ts +1 -0
  102. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub10/only.ts +1 -0
  103. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub11/only.ts +1 -0
  104. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub2/only.ts +1 -0
  105. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub3/only.ts +1 -0
  106. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub4/only.ts +1 -0
  107. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub5/only.ts +1 -0
  108. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub6/only.ts +1 -0
  109. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub7/only.ts +1 -0
  110. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub8/only.ts +1 -0
  111. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub9/only.ts +1 -0
  112. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/solo/item.ts +1 -0
  113. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/web/src/renderer/solo/item.ts +1 -0
  114. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +46 -54
  115. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +16 -76
  116. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +35 -32
  117. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +46 -213
  118. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/README.md +1 -0
  119. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +76 -194
  120. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/check.py +18 -2
  121. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +49 -0
  122. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +1 -1
  123. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/guides/links.md +20 -0
  124. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/guides/route-target.md +8 -0
  125. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/guides/links.md +20 -0
  126. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/guides/route-target.md +8 -0
  127. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/test_check.py +4 -4
  128. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +3 -3
  129. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.notes/declared.txt +1 -0
  130. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +8 -1
  131. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +15 -135
  132. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +98 -50
  133. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +1 -1
  134. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +1 -1
  135. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +1 -1
  136. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +14 -10
  137. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/__init__.py +3 -10
  138. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/base.py +42 -0
  139. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/test_check.py +82 -49
  140. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/test_scope.py +71 -0
  141. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/__init__.py +7 -0
  142. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/check.py +15 -0
  143. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/test_check.py +32 -0
  144. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +1 -1
  145. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +56 -124
  146. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/Cargo.lock +7 -0
  147. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/Cargo.toml +4 -0
  148. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/bun.lock +1 -0
  149. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/package.json +1 -0
  150. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/rust-toolchain.toml +3 -0
  151. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/src/main.rs +1 -0
  152. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/Cargo.lock +7 -0
  153. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/Cargo.toml +4 -0
  154. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/bun.lock +0 -0
  155. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/package.json +1 -0
  156. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/rust-toolchain.toml +3 -0
  157. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/src/main.rs +1 -0
  158. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +8 -37
  159. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +95 -130
  160. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_root_lockfiles.py +101 -0
  161. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +25 -13
  162. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/__init__.py +7 -0
  163. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/check.py +124 -0
  164. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/fixtures/valid-register.yaml +4 -0
  165. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/scanner.py +217 -0
  166. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/test_check.py +391 -0
  167. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/__init__.py +7 -0
  168. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/check.py +16 -0
  169. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/test_check.py +32 -0
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +12 -1
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/ast_grep.py +2 -3
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +3 -0
  173. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +1 -1
  174. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/declarations.py +24 -0
  175. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/receipt.py +2 -2
  176. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/scripts.py +120 -0
  177. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -1
  178. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.toml +4 -4
  179. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +93 -99
  180. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +93 -99
  181. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +1 -1
  182. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +6 -47
  183. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +54 -8
  184. package/packages/repo-controls/scripts/hooks/commit-msg +1 -1
  185. package/packages/repo-controls/scripts/hooks/pre-commit +12 -2
  186. package/packages/repo-controls/scripts/hooks/pre-push +27 -19
  187. package/packages/repo-controls/src/cli.ts +22 -3
  188. package/packages/repo-controls/src/compliance-config.ts +132 -65
  189. package/packages/repo-controls/src/config.ts +62 -52
  190. package/packages/repo-controls/src/constants.ts +9 -0
  191. package/packages/repo-controls/src/coverage-ratchet.ts +19 -4
  192. package/packages/repo-controls/src/doctor.ts +20 -30
  193. package/packages/repo-controls/src/findings-ratchet.ts +14 -0
  194. package/packages/repo-controls/src/gitignore-block.ts +3 -13
  195. package/packages/repo-controls/src/hook-log.ts +7 -10
  196. package/packages/repo-controls/src/hook.ts +127 -83
  197. package/packages/repo-controls/src/install-compliance.ts +112 -0
  198. package/packages/repo-controls/src/install.ts +133 -30
  199. package/packages/repo-controls/src/integrity.ts +5 -18
@@ -328,8 +328,8 @@ export function maintainRepositories(options: MaintainOptions): MaintainResult {
328
328
  if (maxAgeDays !== undefined) pruneStale(repo, name, current, maxAgeDays)
329
329
 
330
330
  // 5. the per-repo enrolment.
331
- const argv = [...options.install.leadingArgs, 'install', '--repo', repo]
332
- const shown = `adl install --repo ${repo}`
331
+ const argv = [...options.install.leadingArgs, 'install', '--repo', repo, '--rewire']
332
+ const shown = `adl install --repo ${repo} --rewire`
333
333
  if (dry) {
334
334
  info(`${name}: [dry] would run ${shown}`)
335
335
  return
@@ -341,6 +341,9 @@ export function maintainRepositories(options: MaintainOptions): MaintainResult {
341
341
  fail(`${name}: ${shown} exited ${result.status}: ${(result.stderr ?? '').trim()}`, repo)
342
342
  } else {
343
343
  info(`${name}: ${shown}: ok`)
344
+ for (const line of `${result.stdout}${result.stderr}`.split('\n')) {
345
+ if (/WARNING|FAIL|COULD-NOT-RUN/.test(line)) warn(`${name}: ${line.trim()}`)
346
+ }
344
347
  }
345
348
  }
346
349
 
@@ -44,19 +44,22 @@ connection to `enact-vps`.
44
44
  **This repo's own layout versus what it writes.** Package-owned hook sources
45
45
  live under `scripts/hooks/`; commit `20ec790` removed the former
46
46
  `scripts/verbatim/` mirror and its manifest when mechanism moved into the
47
- package. The `.workflows/hooks/` stubs, the `.adl/logs/{repo-controls,
48
- hooks-deny}.log` JSON-Lines streams (written via `adl-repo-controls log`,
47
+ package. The `.workflows/hooks/` stubs, the `.adl/logs/git-hooks/{activity,
48
+ deny}.log` JSON-Lines streams (written via `adl-repo-controls log`,
49
49
  `src/hook-log.ts` — `.enact/logs/` is a 2026-09-27 hard cut, no longer
50
50
  written) and the `core.hooksPath` setting are what `install --repo` wires in
51
51
  **target** repos.
52
52
 
53
53
  **Rule values are defaults, not universals.** The bundled defaults are enact-m5's:
54
- branch prefixes `feat|fix|docs|chore`, protected `main` and `integration`,
54
+ branch prefixes fixed to the commit types minus `release` (`packages/azdo/src/lib/commit-types.ts`), protected `main` and `integration`,
55
55
  lowercase kebab-case descriptions, lowercase commit subject with an 80-character
56
56
  limit and a fixed type list, plus the coverage ratchet and the behind-origin push
57
57
  check. Per-repo values are config-driven, in `adl-config.toml`'s `[controls]`
58
- table. Installing seeds `branches` and `pre_push.remote` only and arms no gate;
59
- every other control is absent and therefore off until someone adds it.
58
+ table. Installing seeds `branches` and `pre_push.remote` for `[controls]` and arms no
59
+ push or commit gate there; every other `[controls]` key is absent and therefore off
60
+ until someone adds it. `[controls.compliance]` is the opposite: install seeds the standard
61
+ trees and arms the strict gate set (see
62
+ `docs/architecture/compliance-controls.md#strict-activation-and-switching-a-gate-off`).
60
63
 
61
64
  **`doctor --repo` must detect a dangling `core.hooksPath`.** If the directory it
62
65
  points at is missing, git silently runs no hooks and does not error. That is the
@@ -33,12 +33,10 @@ local .git/config: core.hooksPath = .workflows/hooks
33
33
  They originated in enact-m5; commit `20ec790` removed the former byte-copy
34
34
  tree and manifest when mechanism moved into this package. Their current
35
35
  behaviour is exercised by `tests/hooks-e2e.test.ts` and focused unit tests.
36
- - **Logs** (gitignored): `.adl/logs/repo-controls.log` gets a JSON-Lines row
37
- for every hook invocation; `.adl/logs/hooks-deny.log` gets only `FAIL`
38
- rows — shared with agent-hooks' own PreToolUse denies, so a human `git
39
- commit` denial and an agent's blocked shell command land in one timeline.
40
- Written via `adl repo controls log` (`src/hook-log.ts`), the same shared
41
- `writeLogEntry` format every `.adl/logs/*.log` file uses
36
+ - **Logs** (gitignored): `.adl/logs/git-hooks/activity.log` gets a JSON-Lines row
37
+ for every hook invocation; `.adl/logs/git-hooks/deny.log` gets only `FAIL`
38
+ rows. Written via `adl repo controls log` (`src/hook-log.ts`), the same shared
39
+ `writeLogEntry` format every `.adl/logs/<owner>/*.log` file uses
42
40
  (docs/architecture/cli-design.md §5a). `.enact/logs/` is a hard cut: nothing writes
43
41
  there any more; pre-existing evidence is left in place, never migrated.
44
42
 
@@ -186,9 +184,9 @@ or a `Task`/`Agent` launch with `isolation: "worktree"`) — see
186
184
  `docs/operations/agent-vault-environment.md` for the host-side broker
187
185
  contract underneath `adl-agent-id`/`agent-vault` itself.
188
186
  - **uv** — only if `[controls.compliance].enabled` is non-empty; see below.
189
- - **ast-grep** — optional; only 3 of the compliance registry's 93 checks use it,
190
- and the Python runner degrades those specific rows gracefully (not a hard failure)
191
- when it is absent. `doctor` warns, never fails, on a missing `ast-grep`.
187
+ - **ast-grep** and **shfmt** — only REP-007 (`no_multiline_comments`) requires
188
+ them (`requires` in `package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/check.py`).
189
+ `doctor` warns, never fails, on a missing `ast-grep`.
192
190
 
193
191
  ### Why bun (and not node)
194
192
 
@@ -251,8 +249,9 @@ reading those mtimes) — a path whose content is already byte- and
251
249
  mode-identical is left alone untouched, and anything else (missing,
252
250
  altered, or retired) is still deleted or repaired. It then appends its
253
251
  `[controls]` table only when that table is missing. It appends the single
254
- shared root ignore `/.enact/logs/` and `/.coverage-cache/` to `.gitignore`,
255
- writes the lock, and sets `core.hooksPath=.workflows/hooks` with
252
+ one marker-bounded block to `.gitignore` (`/.adl/`, the agent scratch folder,
253
+ `/.coverage-cache/` and the retired log roots kept as quarantine; the list is
254
+ `src/gitignore-block.ts`), writes the lock, and sets `core.hooksPath=.workflows/hooks` with
256
255
  `git config --local`.
257
256
  - Re-running is a true no-op: the rebuilt root is byte-identical AND no
258
257
  managed file's mtime changes when nothing needed correcting.
@@ -276,15 +275,14 @@ merge into `integration`) runs.
276
275
 
277
276
  **doctor** is read-only and exits 1 on any failure: bash >= 4, bun, binary on
278
277
  PATH, the bundled compliance package present at its packaged path, repository
279
- root, `[controls]` config validity, (warning) `coverage/coverage-summary.json`
280
- exists whenever `pre_push.coverage_ratchet` is enabled (`coverage_ratchet`
281
- defaults ON; the gate is a no-op until the file lands, so this is informational
282
- rather than blocking), (warning) `pre_push.coverage_produce` enabled with no
283
- `[controls.coverage].produce_push` declared (the push gate fails loud there, so
284
- doctor names it first), `[controls.compliance]` config validity, `uv` on PATH
278
+ root, `[controls]` config validity, (fail) `coverage/coverage-summary.json`
279
+ exists whenever `pre_push.coverage_ratchet` is enabled, (fail)
280
+ `pre_push.coverage_produce` enabled with no `[controls.coverage].produce_push`
281
+ declared, (fail) the findings floor and counters whenever `pre_push.findings_ratchet`
282
+ is enabled (see "No silent skips" below), `[controls.compliance]` config validity, `uv` on PATH
285
283
  (only checked once `[controls.compliance].enabled` is non-empty; otherwise
286
284
  reported ok unconditionally, same on-by-default posture as the coverage-summary
287
- check), (warning only, same conditioning) `ast-grep` on PATH, lock, each stub
285
+ check), (fail, same conditioning) `ast-grep` on PATH, lock, each stub
288
286
  (present, unedited, executable), `core.hooksPath`, `.gitignore` lines, and
289
287
  (warning) lock version skew. When `[controls.release]` is declared, it also
290
288
  derives the configured `origin` remote, loads the validated canonical Agent
@@ -350,12 +348,24 @@ comments) is opaque and is never inspected, reordered, or dropped.
350
348
  running `install` may effectively create that marker even if `adl hook` itself
351
349
  is not installed there. `adl-config.toml` itself is never deleted by uninstall.
352
350
 
353
- ### Install wires the hooks. It arms nothing.
354
-
355
- `install` seeds exactly three attributes — `version`, `branches` and
356
- `pre_push.remote` — and **every gate is off**. A first install changes what no
357
- `git push` and no `git commit` does until somebody opts in. `branches` and
358
- `pre_push.remote` are seeded rather than omitted because they are not gates but the
351
+ ### Install wires the hooks, then arms the strict compliance set
352
+
353
+ `install` seeds the `[controls]` vocabulary (`version`, `branches`,
354
+ `pre_push.remote`) and arms **no push or commit gate** of its own: the
355
+ `[controls]` gates below are absent until somebody adds them. Compliance is the
356
+ opposite. `install` seeds `[controls.compliance]` strict: the standard trees, the
357
+ strict `enabled` set, the folder skeleton, the root entry files and printed
358
+ guidance (see [Compliance seeding](#compliance-seeding) below). The decisions
359
+ and the gate list live in
360
+ [`docs/architecture/compliance-controls.md`](../../docs/architecture/compliance-controls.md#strict-activation-and-switching-a-gate-off).
361
+ `install --rewire` (what fleet reconcile runs on every enrolled repo) seeds nothing,
362
+ scaffolds nothing and does not grade: it only re-deploys the hook stubs, `.workflows`
363
+ and the lock, and leaves adl-config.toml byte-identical. Without `uv`, the `adl install`
364
+ wrapper prints a one-line warning that grading is skipped, finishes deploying and exits 0;
365
+ pre-commit still fails loudly without `uv`. See
366
+ [`docs/architecture/compliance-controls.md`](../../docs/architecture/compliance-controls.md#install-and-re-wire).
367
+
368
+ `branches` and `pre_push.remote` are seeded rather than omitted because they are not gates but the
359
369
  vocabulary gates are phrased in, and because `pre-push` reads their env vars with
360
370
  `${VAR:?}`, which aborts on an empty value: "no branch policy" cannot be spelled as
361
371
  an empty list.
@@ -367,24 +377,23 @@ disarm or restyle one you set. Granularity is the top-level key inside `[control
367
377
  an inline table is one attribute, because editing inside one means rewriting bytes
368
378
  you own. Nothing is parsed and re-serialized; text is spliced into the table's span.
369
379
 
370
- `[controls]` is strictly validated: unknown keys and wrong types are errors. An
371
- **absent** key takes its documented default, never an error. Most gates default
372
- ON; the matching `[controls.<gate>]` keys are what configure them. A gate whose
373
- wrapper is ON but whose configuration is missing is a no-op at hook time and a
374
- *warning* at `doctor` time — informational, never blocking.
380
+ `[controls]` is strictly validated: unknown keys, wrong types and missing gate keys are
381
+ errors. A gate key has no default: an absent one fails config load naming the key, and a
382
+ gate is switched off by an explicit `false`. See "No silent skips" below.
375
383
 
376
- | Key | Seeded by install | Default when absent | enact-m5 | Effect |
384
+ | Key | Seeded by install | When absent | enact-m5 | Effect |
377
385
  |---|---|---|---|---|
378
386
  | `branches.protected` | yes | *required* | `["main", "integration"]` | pre-push refuses while on these branches |
379
- | `branches.prefixes` | yes | *required* | `["feat", "fix", "docs", "chore"]` | pre-push / azdo-push branch pattern `<prefix>/<kebab>` |
387
+ | `branches.prefixes` | removed | a config error naming the key | n/a | not configurable: branch prefixes are the commit types minus `release` (`packages/azdo/src/lib/commit-types.ts`); pre-push / azdo-push enforce `<prefix>/<kebab>`; delete the key |
380
388
  | `pre_push.remote` | yes | *required* | `"origin"` | remote azdo-push pushes to and reads status from |
381
- | `pre_commit.staged_coverage` | no | `true` | `true` | enact-m5-only staged coverage measurement (no-op until `[controls.coverage].produce` is declared) |
382
- | `pre_commit.doc_freshness` | no | `true` | opt in | a staged `docs/` or `plans/` `.md` carries a `last_update` stamped within 5h (no-op when no docs are staged) |
383
- | `pre_commit.compliance` | no | `true` | `true` | staged adl-compliance gates (no-op until `[controls.compliance].enabled` is non-empty) |
384
- | `pre_push.coverage_ratchet` | no | `true` | `true` | per outgoing commit, `coverage/coverage-summary.json` must exist and not regress (no-op until the file lands) |
385
- | `pre_push.coverage_produce` | no | `false` | opt in | run the coverage MEASUREMENT at push via `[controls.coverage].produce_push` (fails the push when armed with no command declared) |
386
- | `pre_push.findings_ratchet` | no | `true` | opt in | per outgoing commit, every declared static-analysis counter's findings must not exceed the committed floor (no-op until `[controls.findings].counters` is declared and the floor file lands) |
389
+ | `pre_commit.staged_coverage` | yes (`true`) | config error | `true` | enact-m5-only staged coverage measurement (fails when `[controls.coverage].produce` is not declared) |
390
+ | `pre_commit.doc_freshness` | yes (`true`) | config error | opt in | a staged `docs/` or `plans/` `.md` carries a `last_update` stamped within 5h (nothing to grade when no docs are staged) |
391
+ | `pre_commit.compliance` | yes (`true`) | config error | `true` | staged adl-compliance gates (fails when `[controls.compliance].enabled`, read from the staged config, is empty) |
392
+ | `pre_push.coverage_ratchet` | yes (`true`) | config error | `true` | per outgoing commit, `coverage/coverage-summary.json` must exist and not regress (fails when missing) |
393
+ | `pre_push.coverage_produce` | yes (`false`) | config error | opt in | run the coverage MEASUREMENT at push via `[controls.coverage].produce_push` (fails the push when armed with no command declared) |
394
+ | `pre_push.findings_ratchet` | yes (`true`) | config error | opt in | per outgoing commit, every declared static-analysis counter's findings must not exceed the committed floor (fails when the floor file or counters are missing) |
387
395
  | `pre_push.static_checks` | no | `[]` | tsc, knip, svelte-check | `{ label, run }` commands run on an export of HEAD, pass cached per tree OID |
396
+ | `release` | yes (`false`) | promotion governance fails at pre-push | `[controls.release]` table | `release = false` opts out of promotion governance; a `[controls.release]` table declares it |
388
397
  | `release.source` | no | `""` | `"integration"` | source branch for the managed promotion PR; no branch pair is guessed |
389
398
  | `release.target` | no | `""` | `"main"` | target branch for the managed promotion PR |
390
399
  | `release.merge_strategy` | no | `"squash"` | `"no-fast-forward"` | one of `squash`, `rebase`, `rebase-merge`, or `no-fast-forward` — sent to Azure DevOps as the matching wire value (`squash`, `rebase`, `rebaseMerge`, `noFastForward`) |
@@ -508,12 +517,123 @@ runs no build; the approval is the only gate). Pre-commit calls the same read-on
508
517
  doctor report and never repairs or stages the managed surface: run
509
518
  `adl repo controls install --repo .` explicitly to repair it.
510
519
 
511
- `[controls.compliance]` follows the same rule: install seeds `version` and
512
- `enabled = []` only. `registry` and `[controls.compliance.trees]` have working
513
- defaults and are not written into every repository.
520
+ #### Compliance seeding
521
+
522
+ `[controls.compliance]` follows the same per-attribute rule. Install seeds:
523
+
524
+ - `[controls.compliance.trees]` with the four adl standard trees `docs`, `plans`,
525
+ `workflows` and `compliance` (default paths `docs/`, `plans/`, `workflows/`,
526
+ `compliance/`); `""` declares "this repository has no such tree". Never a
527
+ deleted tree such as `rust`, `frontend`, `typescript`, `infra` or `dev`. What each tree
528
+ is: [`#standard-trees`](../../docs/architecture/compliance-controls.md#standard-trees),
529
+ and `compliance` as the register folder:
530
+ [`#register-folder-and-ratchets`](../../docs/architecture/compliance-controls.md#register-folder-and-ratchets).
531
+ - `enabled` with **every gate in the engine registry**, one `domain.name` address per
532
+ line, `STRICT_ENABLED_GATES` in `src/compliance-config.ts` (Tarun, 2026-10-08). That
533
+ includes `repository.no-vcs-in-scripts` (GIT-001), `repository.license-declared`,
534
+ `repository.lockfile-integrity`, `docs.adr-lifecycle` and
535
+ `repository.no-multiline-comments`. `tests/strict-gates-registry.test.ts` fails when
536
+ the registry gains a gate the seed lacks, so the two cannot drift; the gates and why
537
+ each is on: [`#gate-catalogue`](../../docs/architecture/compliance-controls.md#gate-catalogue).
538
+ Armed from the first commit with NO ratchet baseline for existing violations, so an
539
+ existing repository reports its violations as guidance. Measured on a fresh install
540
+ (see "Fresh-install result" below), the one gate this package's own install leaves
541
+ failing is `repository.license-declared` (REP-001: no `license` declared). An existing `enabled` key, even `[]`, is never rewritten, and
542
+ switching a gate off is the repository's own decision, made by removing its one line
543
+ from `enabled`
544
+ ([`#strict-activation-and-switching-a-gate-off`](../../docs/architecture/compliance-controls.md#strict-activation-and-switching-a-gate-off)).
545
+ `registry` has a working default and is not written.
546
+ - `[controls] integrity = { watched = ["<compliance tree>/**"] }`, the declared
547
+ compliance register folder, so the protection is visible in the file (see
548
+ [Managed dot paths and protected controls](#managed-dot-paths-and-protected-controls)).
549
+ Not written when the tree is declared `""`; an existing `integrity` is never rewritten.
550
+
551
+ An existing `[controls.compliance]` with no `trees` table gets it appended; one
552
+ that already has a `trees` table (any keys) is left byte-identical.
553
+
554
+ Install also scaffolds the repository entry files `AGENTS.md` and `CLAUDE.md`
555
+ (`@AGENTS.md`) at the root when absent (never overwritten) — `SCAFFOLD_ROOT_FILES` in
556
+ `src/constants.ts` is the list. `INVARIANTS.md` is deployed from
557
+ `assets/GLOBAL-INVARIANTS.md`, the invariants that apply to ALL repositories, only when
558
+ the target has none (never overwritten); the engine's root gate still requires
559
+ `INVARIANTS.md`. See Scaffolding below
560
+ for which folders it creates.
561
+
562
+ #### Grading
563
+
564
+ `install` run with `grade: { env }` (what `adl repo controls install` passes) grades
565
+ the repository once its own wiring is written and returns `compliance`:
566
+ `{ ok, enabled, failing[], guidance[] }`. Each failing gate carries its `id`,
567
+ `address`, `status` (`FAIL`, or `ERROR` = could not run, never a pass), summary and
568
+ findings, each with the engine's `fix`. `guidance` is the printable form:
569
+ "This repository now enforces N compliance gates; K failing. Fix the findings below
570
+ before committing." then one block per gate, then the line "To switch a gate off, remove it
571
+ from [controls.compliance] enabled in adl-config.toml — that is this repository's decision,
572
+ and adl-config.toml is a protected file landed by the owner." The engine runs through
573
+ `runTool` (the way `compliance run` and the hooks run it, trees and declarations included)
574
+ with its own `--log-dir`; the `failures.log` there is the structured channel `install`
575
+ parses. `RunContext.quietEngine` discards the engine's stdout rows, because Install prints
576
+ its own guidance and the engine's row listing would otherwise land in the caller's stdout
577
+ and corrupt `install --json`. `ok` is `true` only when every enabled gate passed. Install
578
+ still completes its wiring when gates fail (exit 0, `ok: false`); a missing `uv` is a loud
579
+ error naming `uv` and the real command to re-run (`adl repo controls compliance run --repo .`),
580
+ raised after the wiring is written, and an empty `enabled` reports "nothing is enforced". A
581
+ `--dry-run` never grades.
582
+
583
+ `adl install` (the all-in-one command) is not one repo-controls call: it runs repo-controls
584
+ install with `--no-grade`, then hooks, skills, settings and doctor, and only then grades
585
+ through the `grade` command (`adl repo controls grade --repo <path>`, which prints the
586
+ verdict as JSON and exits 0 whatever the verdict). Grading inside the repo-controls step
587
+ would run before the hooks, skills and settings deploy, so it would under-report what the
588
+ finished install contains. `adl install` prints one report at the end, its top-level `ok`
589
+ means the wiring succeeded (exit 0 when gates fail), and `compliance.ok` stays separate and
590
+ truthful. A `uv`-less machine errors out of the grade step, after everything is wired.
591
+
592
+ #### Scaffolding
593
+
594
+ Install bootstraps the minimal skeleton of `docs`, `plans` and `workflows` from the
595
+ package's `assets/scaffold/<tree>/` (Bug 570, Tarun 2026-10-08):
596
+ `docs/{AGENTS.md,CLAUDE.md,notes.md,decisions/index.md}`,
597
+ `plans/{AGENTS.md,CLAUDE.md,GAPS-AND-FINDINGS.md,LEDGER.md,features/.gitkeep}`, and
598
+ `workflows/{AGENTS.md,CLAUDE.md,pipelines/.gitkeep,scripts/.gitkeep}`. Rules:
599
+
600
+ - **An existing tree folder is ignored entirely** (Tarun, 2026-10-08). If `docs/`,
601
+ `plans/` or `workflows/` (or wherever the tree is declared) already exists at the
602
+ target, empty or not, install adds NOTHING inside it: no missing file, no
603
+ `.gitkeep`. Only a missing folder gets the whole skeleton, so
604
+ `plans/features/.gitkeep` appears only when `plans/` itself is created fresh. Other
605
+ trees are still scaffolded independently. A non-directory in the way of the tree path
606
+ (or a parent) fails the install loudly.
607
+ - The root entry files (`AGENTS.md`, `CLAUDE.md`, `INVARIANTS.md`) are created one by one
608
+ only when absent, never overwritten.
609
+ - `docs/decisions/index.md` makes `docs/decisions/` exist, which `docs.adr-lifecycle`
610
+ (DOC-001) requires; the gate only grades `adr-*.md` files, so the index passes and the
611
+ gate runs on a fresh repository instead of reporting could-not-run.
612
+ - **A tree pointed elsewhere is scaffolded there, never duplicated.** The paths
613
+ come from the effective `[controls.compliance.trees]` after the config step: with
614
+ `docs = "documentation"` the skeleton lands in `documentation/` and no `docs/`
615
+ is created; a tree declared `""`, or not declared, is not scaffolded at all. A
616
+ path that is not repository-relative is refused.
617
+ - **The skeleton is user-owned**: unlike `.workflows/` it is not recorded in the
618
+ lock, uninstall never removes it, and an edited scaffold file is never drift.
619
+ - `compliance` is seeded as a tree but its folder is **not** scaffolded here: it is the
620
+ register folder (ratchets, exemptions) and appears when the first register is
621
+ written. An absent register means "no grants" to the gates that read it.
622
+ - `--dry-run` lists every scaffold file as `create` and writes nothing.
623
+
624
+ #### Fresh-install result
625
+
626
+ The scaffold gates (DOC-001, DOC-002, DOC-004 to DOC-012, LAY-002, LAY-004, LAY-009,
627
+ LAY-011, LAY-014, LAY-016, LFS-001) pass on a fresh install
628
+ (`tests/install-scaffold.test.ts`). Of the 29 enabled gates, the package-level result of a
629
+ fresh install is exactly one failing gate, REP-001 `repository.license-declared`
630
+ (`tests/install-strict.test.ts`). `adl install` also deploys skills, hooks and plugins into
631
+ the repository, and any of their files with consecutive comment lines fail REP-007
632
+ `repository.no-multiline-comments`; that is a defect of the deployed assets, not an expected
633
+ result.
514
634
 
515
635
  Fixed conventions (enact-m5, not configurable): commit subject
516
- `^(feat|fix|docs|refactor|perf|chore|release)(\([a-z0-9][a-z0-9-]*\))?: .+`, at most 80
636
+ `^(<commit type>)(\([a-z0-9][a-z0-9-]*\))?: .+` (types `feat|fix|docs|refactor|perf|chore|release`, defined once in `packages/azdo/src/lib/commit-types.ts` and handed to the hook as `ADL_REPO_CONTROLS_COMMIT_TYPES`; `release` is the pipeline's only), at most 80
517
637
  characters, lowercase description without trailing period; `Merge `, `Revert "`
518
638
  and `X.Y.Z` subjects exempt; no staged `.scratchpad` paths or new references;
519
639
  `git diff --cached --check`; git environment scrubbed before push checks.
@@ -529,14 +649,18 @@ The pre-commit gate has two deliberately different outcomes:
529
649
 
530
650
  - A changed or untracked **top-level** dot path is **commit-required**.
531
651
  When it is omitted from the index, the hook names it and tells the author to
532
- stage it. Once staged, it can be committed normally. This covers centrally
533
- managed paths such as `.agents/**`, `.claude/**`, `.codex/**`, `.cursor/**`,
534
- `.mcp.json`, and future top-level dot paths. Nested paths such as
652
+ stage it. Once staged, it can be committed normally. This covers top-level dot
653
+ paths that are not protected, such as `.mcp.json`, `.workflows/` and the agent surface roots
654
+ (`.agents/`, `.claude/`, `.codex/`, `.cursor/`, `.kimi-code/`, `.opencode/`): those hold a
655
+ repository's own hooks and skills, so they are not protected as a whole (Bug 644). Nested paths such as
535
656
  `tools/.claude/**` are ordinary repository content. The designated ephemeral
536
657
  scratch storage is never committable.
537
658
  - A **protected control** requires human approval even when staged: every path
538
- under `compliance/` or `workflows/`, `adl-config.toml`, and the repository's
539
- explicit `[controls.integrity].watched` patterns. The same controls cannot be
659
+ under `compliance/` or `workflows/`, `adl-config.toml`, the folder declared as `[controls.compliance.trees].compliance`
660
+ (in addition to the fixed `compliance/` root, so a relocated register folder stays
661
+ protected: `integrityWatched` in `src/hook.ts`), and the repository's explicit
662
+ `[controls.integrity].watched` patterns (`PROTECTED_ROOTS` and `WIRING` in `src/integrity.ts`). A repository protects any dot path it chooses through `watched`. The full definition and the
663
+ owner override: [`#protected-paths-and-owner-override`](../../docs/architecture/compliance-controls.md#protected-paths-and-owner-override). The same controls cannot be
540
664
  left dirty while committing other work.
541
665
 
542
666
  Git cannot make a pre-commit hook run for `git commit --no-verify`. That is the
@@ -581,6 +705,8 @@ human makes in the same commit as the fix, the same discipline
581
705
 
582
706
  ## Configuration — `adl-config.toml` `[controls.compliance]`
583
707
 
708
+ **Root declarations.** `[controls.compliance.root]` `dirs` are bare names directly under the repository root. `files` may also be relative paths with `/` (for example `.donors/MANIFEST.md`); install refuses an absolute path, a `.`, `..` or empty segment, a glob character, `,` and `=`. The list travels as one comma-separated `--declare root-files=a,b` value, so `,` and `=` would corrupt it, and `dirs` keep the stricter rule because a directory name is matched as a single entry.
709
+
584
710
  `install` also appends a `[controls.compliance]` table to the same shared
585
711
  `adl-config.toml`, independently of `[controls]` (each table is its own
586
712
  append-once/user-owned-afterwards span; installing, editing, or uninstalling one
@@ -604,24 +730,22 @@ non-empty string.
604
730
  | Key | Default | Effect |
605
731
  |---|---|---|
606
732
  | `version` | `1` | config schema version |
607
- | `enabled` | `[]` | gate ids (`RST-001`) or `domain.name` addresses (`rust.acp-boundary`) to grade; empty = installed, nothing runs |
733
+ | `enabled` | the strict standard set (see above) | gate ids (ids such as `DOC-001`) or `domain.name` addresses (`layout.references`) to grade; empty = installed, nothing runs |
608
734
  | `registry` | `"default"` | forward-compatible; today both values point at the same packaged registry YAML |
609
- | `[controls.compliance.trees]` | `{}` (no entries) | per-key path overrides; a key absent here keeps the packaged (source-shaped) default for that key |
735
+ | `[controls.compliance.trees]` | the four standard trees (seeded) | per-key path overrides; the bundled engine ships NO tree defaults (`policy/config.toml` `[trees]` is empty), so a tree a gate reads that the repository has not declared is could-not-run naming what to add. See [`#standard-trees`](../../docs/architecture/compliance-controls.md#standard-trees) |
610
736
  | `[controls.compliance.limits]` | absent (engine defaults) | per-repository engine limits, strictly validated. `scripts_direct_files` (positive integer) replaces LAY-014's default of 15 direct files per scripts directory and reaches the engine as `--declare scripts-direct-files=N`; a value the engine cannot read as a positive integer is could-not-run, never a fall back to 15. The depth limit is not configurable. `open_gaps` and `open_plans` (each a positive integer) replace LAY-016's default cap of 10 open gaps and 10 open plans, independently, and reach the engine as `--declare open-gaps=N` / `--declare open-plans=N`; the same could-not-run rule applies to each, and a finding names the effective cap as `N open gaps, cap is M`. The engine default stays 10: a repository declares today's measured count and ratchets it down |
611
737
  | `[controls.compliance.markdown]` | absent (no extra Markdown roots) | extra Markdown roots for LAY-006 (`layout.markdown-trees`), strictly validated. `roots` is an array of repository-relative directories whose Markdown is site or application content rather than documentation (an Astro site's `src/content/**/*.md`); LAY-006 admits `.md` and `.mdx` beneath each, in addition to the docs trees, so a declared root only ever widens the allowlist and absent is the stricter grade. Reaches the engine as one `--declare markdown-roots=a,b`. An entry that is empty, absolute, contains `,` or `=`, or has a `.`, `..` or empty path segment is rejected when the config loads (a trailing `/` is dropped); the engine refuses the same values as could-not-run (exit 2) |
612
- | `[controls.compliance.lockfiles]` | absent (REP-002 keeps its tree-derived scope) | exactly which manifest and lockfile paths REP-002 (`repository.lockfile-integrity`) scans and grades, strictly validated. Four arrays: `bun_manifests` / `bun_lockfiles` and `cargo_manifests` / `cargo_lockfiles`. A manifest entry is a repository-relative path or glob ending in `package.json` (`Cargo.toml`), such as `package.json` or `apps/*/package.json`; a lockfile entry is a literal path (a missing one is reported by name). An ecosystem is declared as a pair, and a declared pair replaces that ecosystem's default (`<typescript>/package.json` + `<typescript>/bun.lock`, the `<rust>` pair) in both what selects the gate and what it grades; an undeclared ecosystem is unchanged. Reaches the engine as `--declare bun-manifests=a,b`, `bun-lockfiles=`, `cargo-manifests=`, `cargo-lockfiles=` (a list is omitted when empty); a declared manifest that matches no file is could-not-run naming it. An entry that is empty, absolute, duplicated, contains `,` `=` `\` `<` `>` `{` `}`, starts with `!`, has a `.`, `..` or empty path segment, ends in the wrong file name, or (for a lockfile) is a glob, an ecosystem with only one of its two lists, and a `REP-002:bun_lock` / `cargo_lock` / `cargo_manifest` / `workspace` input beside the pair it supersedes are all rejected when the config loads; the engine refuses the same values as could-not-run (exit 2) |
738
+ | `[controls.compliance.placement]` | absent (LAY-008 uses its built-in asset directories) | extra asset directories for LAY-008 (`layout.placement`), strictly validated. `asset_dirs` is an array of repository-relative directories; reaches the engine as one `--declare asset-dirs=a,b` (omitted when empty). An entry that is empty, absolute, contains `,` or `=`, or has a `.`, `..` or empty path segment is rejected when the config loads (a trailing `/` is dropped); an unknown key is an error |
739
+ | `[controls.compliance.lockfiles]` | absent (REP-002 grades only the repository-root pairs) | exactly which manifest and lockfile paths REP-002 (`repository.lockfile-integrity`) scans and grades, strictly validated. Four arrays: `bun_manifests` / `bun_lockfiles` and `cargo_manifests` / `cargo_lockfiles`. A manifest entry is a repository-relative path or glob ending in `package.json` (`Cargo.toml`), such as `package.json` or `apps/*/package.json`; a lockfile entry is a literal path (a missing one is reported by name). REP-002 always grades the repository-root pairs itself (`package.json` -> `bun.lock`, `Cargo.toml` -> `Cargo.lock`); every other lockfile is graded only when declared here, because the engine does not guess where a repository keeps its workspaces. An ecosystem is declared as a pair. Reaches the engine as `--declare bun-manifests=a,b`, `bun-lockfiles=`, `cargo-manifests=`, `cargo-lockfiles=` (a list is omitted when empty); a declared manifest that matches no file is could-not-run naming it. An entry that is empty, absolute, duplicated, contains `,` `=` `\` `<` `>` `{` `}`, starts with `!`, has a `.`, `..` or empty path segment, ends in the wrong file name, or (for a lockfile) is a glob, an ecosystem with only one of its two lists, and a `REP-002:bun_lock` / `cargo_lock` / `cargo_manifest` / `workspace` input beside the pair it supersedes are all rejected when the config loads; the engine refuses the same values as could-not-run (exit 2) |
613
740
 
614
741
  ### Registry curation
615
742
 
616
- The full compliance registry (145 rows) always ships and loads — `enabled` is
617
- what decides which of those rows is ever graded, not what's present. The
618
- packaged `[controls.compliance.trees]` defaults are shaped after the *original* source
619
- repository the checks were authored against, not any arbitrary target repo. A
620
- repository enabling any row needs its own `[controls.compliance.trees]` overrides for
621
- whatever that row's scope actually needs in that repository's layout — this
622
- package does **not** ship a pre-vetted "safe for any repo" subset; enabling a
623
- row without pointing its trees at the right paths will simply grade the wrong
624
- (or nonexistent) files.
743
+ The bundled registry (29 rows, counted from `gate-registry.yaml` and
744
+ `len(CHECKS)`) always ships and loads — `enabled` is what decides which row is
745
+ ever graded, not what is present. The engine ships no tree defaults: a repository
746
+ declares its own in `[controls.compliance.trees]`, and a gate whose tree is
747
+ undeclared is could-not-run, never a pass. The catalogue of gates is
748
+ [`#gate-catalogue`](../../docs/architecture/compliance-controls.md#gate-catalogue).
625
749
 
626
750
  ## Development
627
751
 
@@ -634,3 +758,169 @@ bun test --coverage # 100% lines + functions gate (bunfig.toml)
634
758
  Tests run real git in temporary repositories under `.scratchpad/test-tmp/`
635
759
  (gitignored), exercising the installed stubs end to end. CI runs them through
636
760
  the repository's root pipelines under `workflows/pipelines/`.
761
+
762
+ ## Managed workflow scripts
763
+
764
+ Rationale moved out of the scripts that `install` deploys into `.workflows/`, so the
765
+ deployed copies stay free of multi-line comments (REP-007).
766
+
767
+ ### `promote.ts`
768
+
769
+ - Managed by adl-repo-controls; do not edit. Regenerate with `adl-repo-controls install --repo .`.
770
+ - Vendored into `.workflows/` byte-for-byte so CI depends on nothing: no adl-repo-controls on PATH,
771
+ no MCP client, no package install. A pipeline agent is a cold environment, and a promotion that
772
+ needs a global npm binary fails there for reasons that have nothing to do with the promotion.
773
+ - Byte-identical in every repository, deliberately. Refs come from the environment, never baked in;
774
+ otherwise each consumer's copy would hash differently and one published hash could not verify any
775
+ of them.
776
+ - No reviewer is named on the promotion PR, deliberately (Tarun, 2026-09-27). Naming one, typically
777
+ the person who pushed to `integration`, is a self-approval risk and contradicts "agent-reviewer is
778
+ optional, never required". The gate is the target branch's own minimum-reviewer policy (verified
779
+ earlier in the script), which excludes the author's own vote.
780
+
781
+ ### `reconcile-guard.sh`
782
+
783
+ - Stage 1 of the CI pipeline for pull requests into `integration` (stage 2 runs the tests). It forces
784
+ the PR author, usually an agent, to reconcile with integration before anything merges.
785
+ - The only check: the PR source branch contains integration's tip. The version bump is not a PR
786
+ concern any more; the integration-merge pipeline bumps after the merge (Tarun, 2026-10-01).
787
+ - Read-only: it only fetches and reads. It never pushes, and the only refs it writes are the two
788
+ remote-tracking refs it fetches.
789
+ - CI never carries an agent identity: the script unsets `ADL_AGENT_ID_AGENT` and never inherits one
790
+ from the caller.
791
+
792
+ Further `promote.ts` rationale (moved from its doc comments):
793
+
794
+ - Standard, not repo-local: every repository in this estate is on Azure DevOps, and promoting a
795
+ branch to its trunk is find-or-reuse a PR, then set auto-complete. Ported from enact-m5's
796
+ `workflows/scripts/release/promote-main.ts`, where the mechanism was already injectable (fetch,
797
+ collection, project, repository and token were all parameters); only three module-scoped literals
798
+ made it that repository's: `SOURCE_REF = 'refs/heads/integration'`, `TARGET_REF =
799
+ 'refs/heads/main'`, and `title = 'release: promote integration to main'`.
800
+ - The refs are declared as `[controls.release].source` / `.target`. The title is DERIVED from them
801
+ rather than being a third key: a title that can disagree with the refs it describes is a bug
802
+ waiting for someone to change one and forget the other, and the derived form satisfies this
803
+ package's own commit-msg grammar for any pair.
804
+ - The target branch's gate is Tarun's approval alone (Tarun, 2026-09-30): the promotion PR gets NO
805
+ build validation and CD runs after the merge into the target. The pre-flight proves the
806
+ minimum-reviewer policy (>=1 approver, resetOnSourcePush=true) and does not require a build
807
+ policy. `build` is still reported so a caller can show one that happens to be present, but
808
+ nothing demands it.
809
+ - There is no default pair. Defaulting to integration -> main would put one repository's branch
810
+ names back into the package by the back door.
811
+ - Merge strategy: Azure's `GitPullRequestCompletionOptions.mergeStrategy` is sent by name. The REST
812
+ wire values are the camelCase strings; sending the numeric `GitPullRequestMergeStrategy` enum
813
+ ordinal is silently accepted by the type system but is not what the API expects (PR 1477,
814
+ 2026-09-27, armed auto-complete with `rebase` although "no-fast-forward" was intended). `squash`
815
+ is the standard, matching the MCP server's PR tool, which admits `enum: ['Squash']` only. A
816
+ repository wanting something else declares it, so the deviation is visible.
817
+ - Policy contract: one contract is used by both promotion and doctor. A configuration only counts
818
+ when it is enabled, blocking, and scoped exactly to the target branch; a project-wide or
819
+ path-filtered lookalike cannot guard a promotion.
820
+ - No named-reviewer policy is checked (Tarun, 2026-09-27): the two-branch standard's `main` gate is
821
+ "exactly one human reviewer, author's vote excluded" via the minimum-reviewer policy, never a
822
+ specific named individual. A required-reviewer-by-identity policy on `main` was PR 1477's second
823
+ live bug: it named the person who pushed to `integration` as a mandatory approver, which both
824
+ self-approves (that person is usually the change's author) and contradicts "agent-reviewer is
825
+ optional, never required".
826
+ - Standalone entry point: reads the refs from the ENVIRONMENT, not from config. The file is vendored
827
+ into `.workflows/` byte-for-byte, so its content must be identical in every repository or a single
828
+ published hash cannot verify any of them; the pipeline supplies repository-specific values through
829
+ environment variables.
830
+
831
+ ### `version-bump.sh`
832
+
833
+ - Stage 1 of the promote pipeline (push to `integration`): the integration-merge build owns package
834
+ version allocation (Tarun, 2026-10-01). A PR's build-validation policy ignores `[skip ci]`, so a
835
+ bump pushed from PR CI re-queued itself; a push to a branch honours it. After a merge lands on
836
+ `integration` the script sets the manifest to that commit's version + patch 1:
837
+ `version-bump.sh --file package.json --integration --push`.
838
+ - The Azure Pipelines build checks out the detached triggering commit with persisted credentials, so
839
+ `git push` authenticates as the project build service (needs Contribute and "Bypass policies when
840
+ pushing" on `integration`). The bump commit is created on that checkout, carries `[skip ci]` so the
841
+ push does not trigger this pipeline again, and is pushed as a plain fast-forward, never forced. If
842
+ integration moved since the checkout the push is rejected and the script fails loud; the newer
843
+ merge's own run bumps. No npm query, no PR.
844
+ - CI never carries an agent identity: the push authenticates as the build service through the
845
+ checkout's persisted `System.AccessToken`. The script unsets `ADL_AGENT_ID_AGENT` and never
846
+ inherits one.
847
+ - `[controls.release].pre_bump` (`adl-config.toml`, repo root): ordered, repository-relative scripts a
848
+ multi-language repo runs before the bump commit, for example a Go version constant that nothing in
849
+ `package.json` can reach. No file, or no `pre_bump` key: no scripts, and the resulting commit is
850
+ byte-identical to the behaviour before the feature existed
851
+ (`tests/version-bump-integration-pre-bump.test.ts`).
852
+ - Declared scripts are all validated before any runs, so a bad declaration fails clearly instead of
853
+ leaving the repo half-way through a partial run.
854
+ - Every variable whose name ends `_TOKEN`, `_PAT`, `_SECRET` or `_PASSWORD` (any case), plus
855
+ `SYSTEM_ACCESSTOKEN` and the Azure DevOps credentials by exact name, is stripped from each script's
856
+ environment: a pre_bump script is repo-authored, unreviewed by this package, and has no business
857
+ seeing the pipeline's push credentials. The match is plain bash (no search tool, no `${var^^}`)
858
+ because Azure Pipelines agents do not guarantee ripgrep and macOS ships bash 3.2.
859
+ - A pre_bump script that edits `.git` directly (hooks, config, refs) never shows in `git status`, so
860
+ it is checked separately and the bump refuses. There is no equivalent check for a script writing
861
+ to an absolute path outside the repository: a plain shell script has no sandbox, so only `.git`
862
+ tampering is refused, honestly. Every add is scoped to `$repo_root`, so nothing outside it is staged.
863
+ - `[skip ci]` (Azure DevOps: a push whose commits contain it does not trigger CI pipelines) keeps the
864
+ push from re-running the promote pipeline that made it.
865
+
866
+ ## Log folders
867
+
868
+ Bug 600 (Tarun, 2026-10-08): `.adl/logs/` has one folder per owner and no file
869
+ is written by two owners. The git hooks write `git-hooks/activity.log` (every
870
+ invocation) and `git-hooks/deny.log` (FAIL rows only); they no longer share a
871
+ deny file with agent-hooks (`agent-hooks/deny.log`). The old top-level
872
+ `repo-controls.log` and `hooks-deny.log` are left where they are, never
873
+ migrated. A git-hook row has `host: git` and is written by the same writer as
874
+ agent-hook rows (`packages/agent-hooks/src/log/writer.ts`).
875
+
876
+ `.enact/logs/` is pre-hard-cut evidence only: nothing writes there, and the
877
+ managed `.gitignore` block keeps ignoring it alongside the two legacy hook-log
878
+ roots. Hook and session logs live under `.adl/logs/<owner>/` (`agent-hooks/`,
879
+ `git-hooks/`, `agents/`), covered by the block's `/.adl/` line.
880
+
881
+ A log write that fails never changes a git hook's exit code; `hook-log.ts`
882
+ prints one `adl repo controls: could not write log row: ...` line to stderr.
883
+
884
+ ## No silent skips
885
+
886
+ Tarun, 2026-10-09: an armed gate whose input is missing fails loud; switching a gate off is an
887
+ explicit `false` in `adl-config.toml`, an owner decision.
888
+
889
+ - Install seeds every gate key under `[controls]` (`pre_commit.*`, `pre_push.coverage_ratchet`,
890
+ `coverage_produce`, `findings_ratchet`) and `release = false`. A consumer config missing a gate key
891
+ fails to load, naming the key. `release` is the exception: an absent `[controls.release]` loads, and
892
+ fails the pre-push promotion check instead, telling the owner to declare source/target or set
893
+ `release = false`.
894
+ - Coverage ratchet: with the gate on, a summary missing from both HEAD and the work tree fails, and so does
895
+ a missing HEAD~1 baseline unless the summary is added in HEAD (a loud first-baseline note). A file with
896
+ no baseline entry is reported as "new, now baselined"; a file dropped from the summary while it still
897
+ exists on disk fails.
898
+ - Findings ratchet: a missing floor file fails, and so does a floor entry for a counter not declared in
899
+ `[controls.findings.counters]`.
900
+ - Staged coverage with an empty `[controls.coverage].produce` fails at commit.
901
+ - Compliance: the flag and `enabled` are both read from the staged `adl-config.toml`; flag true with
902
+ `enabled` empty fails.
903
+ - `doctor` reports these prerequisites as `fail` (not `warn`) while the governing gate is on, and the
904
+ pre-commit doctor gate prints them.
905
+ - `ADL_SKIP_STAGED_COVERAGE` was a dead export (nothing read it) and is removed.
906
+
907
+ ## No silent skips
908
+
909
+ Tarun, 2026-10-09: an armed gate with a missing input fails loudly; it never
910
+ degrades to a pass.
911
+
912
+ - The pre-push static-check pass marker is keyed by the HEAD tree OID plus a
913
+ hash of the declared `static_checks` (label and command) and the
914
+ `export_links` list, so editing a check invalidates a cached pass. No marker
915
+ is written when zero static checks ran.
916
+ - A declared `export_links` entry whose target is missing in the checkout fails
917
+ the push instead of being skipped.
918
+ - A static check with an empty label or an empty command fails the push. The
919
+ entry is split on its first tab by hand because `read` with a tab `IFS`
920
+ collapses a leading tab and would shift the command into the label.
921
+ - In `workflows/scripts/coverage`, a full or `--merge-only` run with no
922
+ committed baseline at `HEAD:coverage/coverage-summary.json` fails unless
923
+ `--first-baseline` is passed; `--only` legs still never grade. The ratchet
924
+ reports a measured file with no baseline entry as "new file, baselined", the
925
+ sanitize step says when HEAD has no summary, and auto-publish no longer
926
+ falls back to the working summary or the candidate as its own baseline.
@@ -0,0 +1,21 @@
1
+ # Invariants
2
+
3
+ The things this repository must never regress. These apply to every repository
4
+ adl manages; add this repository's own below, one bullet each, stating the rule
5
+ and how it is verified.
6
+
7
+ - The compliance gates enabled in `adl-config.toml` pass before every commit
8
+ (verified by `adl repo controls` pre-commit and CI). A gate is switched off by
9
+ removing it from `[controls.compliance] enabled`, which is the owner's decision.
10
+ - Protected paths (`adl-config.toml`, `.workflows/`, the agent surface folders,
11
+ `workflows/` and the declared compliance register folder) are landed only by the
12
+ owner. An agent leaves them uncommitted and asks (verified by the pre-commit
13
+ integrity gate).
14
+ - Ratchets only shrink. A register names the specific files granted, and a grant
15
+ is removed once its file is clean, never added to (verified by the gate that reads
16
+ the register).
17
+ - No multi-line comments in code. The explanation lives in `docs/`, and the code
18
+ keeps at most a one-line pointer (verified by `repository.no-multiline-comments`).
19
+ - The standard trees `docs`, `plans`, `workflows` and `compliance` are declared in
20
+ `[controls.compliance.trees]`, and layout rules follow those declarations
21
+ (verified by the layout and docs gates).