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

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 (192) 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/mcp-tool.ts +1 -1
  14. package/packages/agent-hooks/src/rules/shell/invocation.ts +2 -1
  15. package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +24 -2
  16. package/packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh +19 -45
  17. package/packages/agent-skills/assets/diagnosing-bugs/SKILL.md +9 -0
  18. package/packages/agent-skills/assets/diagnosing-bugs/scripts/hitl-loop.template.sh +1 -12
  19. package/packages/fleet/manifest.json +2 -1
  20. package/packages/fleet/src/repo-maintain.ts +5 -2
  21. package/packages/repo-controls/AGENTS.md +7 -4
  22. package/packages/repo-controls/README.md +346 -56
  23. package/packages/repo-controls/assets/GLOBAL-INVARIANTS.md +21 -0
  24. package/packages/repo-controls/assets/promote.ts +7 -85
  25. package/packages/repo-controls/assets/reconcile-guard.sh +1 -14
  26. package/packages/repo-controls/assets/scaffold/docs/AGENTS.md +25 -0
  27. package/packages/repo-controls/assets/scaffold/docs/CLAUDE.md +1 -0
  28. package/packages/repo-controls/assets/scaffold/docs/decisions/index.md +17 -0
  29. package/packages/repo-controls/assets/scaffold/docs/notes.md +20 -0
  30. package/packages/repo-controls/assets/scaffold/plans/AGENTS.md +28 -0
  31. package/packages/repo-controls/assets/scaffold/plans/CLAUDE.md +1 -0
  32. package/packages/repo-controls/assets/scaffold/plans/GAPS-AND-FINDINGS.md +12 -0
  33. package/packages/repo-controls/assets/scaffold/plans/LEDGER.md +8 -0
  34. package/packages/repo-controls/assets/scaffold/plans/features/.gitkeep +0 -0
  35. package/packages/repo-controls/assets/scaffold/root/AGENTS.md +22 -0
  36. package/packages/repo-controls/assets/scaffold/root/CLAUDE.md +1 -0
  37. package/packages/repo-controls/assets/scaffold/workflows/AGENTS.md +16 -0
  38. package/packages/repo-controls/assets/scaffold/workflows/CLAUDE.md +1 -0
  39. package/packages/repo-controls/assets/scaffold/workflows/pipelines/.gitkeep +0 -0
  40. package/packages/repo-controls/assets/scaffold/workflows/scripts/.gitkeep +0 -0
  41. package/packages/repo-controls/assets/version-bump.sh +7 -48
  42. package/packages/repo-controls/package/adl-compliance/AGENTS.md +25 -7
  43. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +6 -4
  44. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/check.py +1 -1
  45. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +1 -1
  46. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +7 -42
  47. 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
  48. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +10 -44
  49. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +1 -1
  50. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +1 -1
  51. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +1 -1
  52. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/check.py +1 -7
  53. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/test_check.py +12 -3
  54. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +15 -23
  55. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +0 -136
  56. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +18 -0
  57. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +2 -2
  58. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +0 -7
  59. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/check.py +2 -4
  60. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/stable-delivery.md +10 -0
  61. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/stable-recommendation.md +12 -0
  62. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/target-narration.md +10 -0
  63. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +6 -7
  64. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/ci_paths/test_check.py +3 -3
  65. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +10 -12
  66. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +1 -1
  67. 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
  68. 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
  69. 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
  70. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +1 -1
  71. 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
  72. 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
  73. 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
  74. 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
  75. 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
  76. 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
  77. 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
  78. 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
  79. 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
  80. 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
  81. 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
  82. 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
  83. 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
  84. 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
  85. 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
  86. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +1 -1
  87. 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
  88. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +1 -1
  89. 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
  90. 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
  91. 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
  92. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +2 -2
  93. 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
  94. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +1 -1
  95. 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
  96. 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
  97. 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
  98. 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
  99. 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
  100. 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
  101. 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
  102. 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
  103. 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
  104. 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
  105. 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
  106. 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
  107. 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
  108. 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
  109. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +46 -54
  110. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +16 -76
  111. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +35 -32
  112. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +46 -213
  113. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/README.md +1 -0
  114. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +76 -194
  115. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/check.py +18 -2
  116. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +49 -0
  117. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +1 -1
  118. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/guides/links.md +20 -0
  119. 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
  120. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/guides/links.md +20 -0
  121. 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
  122. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/test_check.py +4 -4
  123. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +3 -3
  124. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.notes/declared.txt +1 -0
  125. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +8 -1
  126. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +15 -135
  127. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +98 -50
  128. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +1 -1
  129. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +1 -1
  130. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +1 -1
  131. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +14 -10
  132. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/__init__.py +3 -10
  133. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/base.py +42 -0
  134. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/test_check.py +82 -49
  135. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/test_scope.py +71 -0
  136. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/__init__.py +7 -0
  137. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/check.py +15 -0
  138. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/test_check.py +32 -0
  139. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +1 -1
  140. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +56 -124
  141. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/Cargo.lock +7 -0
  142. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/Cargo.toml +4 -0
  143. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/bun.lock +1 -0
  144. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/package.json +1 -0
  145. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/rust-toolchain.toml +3 -0
  146. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/src/main.rs +1 -0
  147. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/Cargo.lock +7 -0
  148. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/Cargo.toml +4 -0
  149. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/bun.lock +0 -0
  150. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/package.json +1 -0
  151. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/rust-toolchain.toml +3 -0
  152. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/src/main.rs +1 -0
  153. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +8 -37
  154. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +95 -130
  155. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_root_lockfiles.py +101 -0
  156. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +25 -13
  157. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/__init__.py +7 -0
  158. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/check.py +124 -0
  159. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/fixtures/valid-register.yaml +4 -0
  160. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/scanner.py +217 -0
  161. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/test_check.py +391 -0
  162. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/__init__.py +7 -0
  163. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/check.py +16 -0
  164. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/test_check.py +32 -0
  165. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +12 -1
  166. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/ast_grep.py +2 -3
  167. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +3 -0
  168. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +1 -1
  169. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/declarations.py +24 -0
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/receipt.py +2 -2
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/scripts.py +120 -0
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -1
  173. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.toml +4 -4
  174. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +93 -99
  175. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +93 -99
  176. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +1 -1
  177. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +6 -47
  178. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +54 -8
  179. package/packages/repo-controls/scripts/hooks/pre-commit +12 -2
  180. package/packages/repo-controls/scripts/hooks/pre-push +27 -19
  181. package/packages/repo-controls/src/cli.ts +22 -3
  182. package/packages/repo-controls/src/compliance-config.ts +132 -65
  183. package/packages/repo-controls/src/config.ts +55 -46
  184. package/packages/repo-controls/src/constants.ts +9 -0
  185. package/packages/repo-controls/src/coverage-ratchet.ts +19 -4
  186. package/packages/repo-controls/src/doctor.ts +20 -30
  187. package/packages/repo-controls/src/findings-ratchet.ts +14 -0
  188. package/packages/repo-controls/src/gitignore-block.ts +3 -13
  189. package/packages/repo-controls/src/hook-log.ts +7 -10
  190. package/packages/repo-controls/src/hook.ts +127 -83
  191. package/packages/repo-controls/src/install-compliance.ts +112 -0
  192. package/packages/repo-controls/src/install.ts +133 -30
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * The `.adl/logs/` logger (docs/architecture/cli-design.md §5a, item 9). `agent-hooks`
3
- * "holds logs" (§12): this module lives here and writes `hooks.log` /
4
- * `hooks-deny.log` (`../hooks/log.ts`); `apps/cli/src/log.ts` re-exports it
5
- * unchanged so `adl run`'s `agents/tool-calls.log` writer keeps working. The module
6
- * is generic across all five documented files so a caller never has to
7
- * reinvent the format.
3
+ * "holds logs" (§12): this module lives here and writes
4
+ * `agent-hooks/hooks.log` / `agent-hooks/deny.log` (`../hooks/log.ts`);
5
+ * `apps/cli/src/log.ts` re-exports it so `adl run`'s `agents/tool-calls.log`
6
+ * writer and repo-controls' `git-hooks/` writer share it. One folder per
7
+ * owner under `.adl/logs/` (Bug 600); every stream rotates through here.
8
8
  *
9
9
  * - JSON Lines, one object per line. Fields are written in a fixed order;
10
10
  * an absent field is OMITTED, never null-filled.
@@ -14,24 +14,35 @@
14
14
  * must never fail the caller.
15
15
  * - `command` is redacted with `redact()` before it is written.
16
16
  * - Rotation: a file that would exceed `maxBytes` (10 MiB by default) is
17
- * rotated to `.1` … `.5`, dropping `.5`, before the new line is written.
17
+ * rotated to `.1` … `.5`, dropping `.5`, before the new line is written,
18
+ * under a per-file lock (README, "Hook logs").
18
19
  */
20
+ import { randomBytes } from "node:crypto";
19
21
  import { spawnSync } from "node:child_process";
20
22
  import {
21
23
  appendFileSync,
22
24
  chmodSync,
23
25
  existsSync,
26
+ linkSync,
24
27
  mkdirSync,
28
+ readdirSync,
29
+ readFileSync,
25
30
  renameSync,
31
+ rmSync,
26
32
  statSync,
27
- unlinkSync,
28
33
  writeFileSync,
29
34
  } from "node:fs";
30
35
  import { homedir } from "node:os";
31
36
  import { basename, dirname, join } from "node:path";
32
37
  import { ensureAdlGitignore } from "./adl-dir.ts";
33
38
 
34
- export type LogFileName = "hooks" | "hooks-deny" | "tool-rewrites" | "tool-calls" | "repo-controls";
39
+ /** Every stream as `<owner folder>/<file>` under `.adl/logs/` (README, "Hook logs": one folder per owner, Bug 600). */
40
+ export type LogFileName =
41
+ | "agent-hooks/hooks"
42
+ | "agent-hooks/deny"
43
+ | "git-hooks/activity"
44
+ | "git-hooks/deny"
45
+ | "agents/tool-calls";
35
46
 
36
47
  export interface LogEntryInput {
37
48
  /** Second column of every row: the host alone (`claude`, `codex`, `cursor`, `kimi`, `opencode`; `git` for git-hook rows). */
@@ -78,6 +89,8 @@ export interface WriteLogOptions {
78
89
  dir?: string;
79
90
  /** cwd used to resolve the git root when `dir` is not given. */
80
91
  cwd?: string;
92
+ /** How long to wait for the rotation lock before appending without rotating; defaults to 10 s. Tests inject a small value. */
93
+ lockTimeoutMs?: number;
81
94
  /** Rotation threshold in bytes; defaults to 10 MiB. Tests inject a small value. */
82
95
  maxBytes?: number;
83
96
  /** Clock override for deterministic `ts` values in tests. */
@@ -147,22 +160,154 @@ function ensureDirSecure(dir: string): void {
147
160
  ensureAdlGitignore(dir);
148
161
  }
149
162
 
163
+ function isMissing(error: unknown): boolean {
164
+ return (error as NodeJS.ErrnoException).code === "ENOENT";
165
+ }
166
+
150
167
  function ensureFileSecure(filePath: string): void {
151
- if (!existsSync(filePath)) {
152
- writeFileSync(filePath, "", { mode: 0o600 });
168
+ writeFileSync(filePath, "", { mode: 0o600, flag: "a" });
169
+ try {
170
+ tightenMode(filePath, 0o600);
171
+ } catch (error) {
172
+ if (!isMissing(error)) throw error;
153
173
  }
154
- tightenMode(filePath, 0o600);
155
174
  }
156
175
 
157
- function rotate(filePath: string): void {
158
- const dropped = `${filePath}.${MAX_BACKUPS}`;
159
- if (existsSync(dropped)) unlinkSync(dropped);
176
+ function renameIfPresent(src: string, dst: string): boolean {
177
+ try {
178
+ renameSync(src, dst);
179
+ return true;
180
+ } catch (error) {
181
+ if (isMissing(error)) return false;
182
+ throw error;
183
+ }
184
+ }
185
+
186
+ const INSERT_ATTEMPTS = 5;
187
+
188
+ function linkIfFree(src: string, dst: string): boolean {
189
+ try {
190
+ linkSync(src, dst);
191
+ return true;
192
+ } catch (error) {
193
+ const code = (error as NodeJS.ErrnoException).code;
194
+ if (code === "EEXIST" || code === "ENOENT") return false;
195
+ throw error;
196
+ }
197
+ }
198
+
199
+ function shiftBackups(filePath: string): void {
200
+ rmSync(`${filePath}.${MAX_BACKUPS}`, { force: true });
160
201
  for (let n = MAX_BACKUPS - 1; n >= 1; n--) {
161
- const src = `${filePath}.${n}`;
162
- const dst = `${filePath}.${n + 1}`;
163
- if (existsSync(src)) renameSync(src, dst);
202
+ if (linkIfFree(`${filePath}.${n}`, `${filePath}.${n + 1}`)) rmSync(`${filePath}.${n}`, { force: true });
203
+ }
204
+ }
205
+
206
+ /** Moves `src` into slot `.1` without ever overwriting a backup; exported for tests, `afterShift` is their race hook. */
207
+ export function insertBackup(filePath: string, src: string, afterShift?: () => void): void {
208
+ for (let attempt = 0; attempt < INSERT_ATTEMPTS; attempt++) {
209
+ shiftBackups(filePath);
210
+ afterShift?.();
211
+ if (linkIfFree(src, `${filePath}.1`)) {
212
+ rmSync(src, { force: true });
213
+ return;
214
+ }
215
+ if (!existsSync(src)) return;
216
+ }
217
+ throw new Error(`log rotation could not find a free backup slot for ${src}; file kept`);
218
+ }
219
+
220
+ function ownerAlive(pid: number): boolean {
221
+ try {
222
+ process.kill(pid, 0);
223
+ return true;
224
+ } catch (error) {
225
+ return (error as NodeJS.ErrnoException).code === "EPERM";
226
+ }
227
+ }
228
+
229
+ function orphanedAsides(filePath: string): string[] {
230
+ const prefix = `${basename(filePath)}.rotating-`;
231
+ const dir = dirname(filePath);
232
+ return readdirSync(dir)
233
+ .filter((name) => name.startsWith(prefix))
234
+ .map((name) => ({ path: join(dir, name), pid: Number(/^(\d+)-/.exec(name.slice(prefix.length))?.[1]) }))
235
+ .map((entry) => ({ ...entry, mtime: statSync(entry.path, { throwIfNoEntry: false })?.mtimeMs }))
236
+ .filter(({ pid, mtime }) => mtime !== undefined && (!Number.isInteger(pid) || !ownerAlive(pid) || Date.now() - mtime > LOCK_STALE_MS))
237
+ .sort((a, b) => (a.mtime as number) - (b.mtime as number))
238
+ .map(({ path }) => path);
239
+ }
240
+
241
+ function rotate(filePath: string): void {
242
+ for (const orphan of orphanedAsides(filePath)) insertBackup(filePath, orphan);
243
+ const aside = `${filePath}.rotating-${process.pid}-${randomBytes(4).toString("hex")}`;
244
+ if (!renameIfPresent(filePath, aside)) return;
245
+ insertBackup(filePath, aside);
246
+ }
247
+
248
+ /** A lock older than this was left by a crashed writer and is broken. */
249
+ const LOCK_STALE_MS = 5_000;
250
+ const LOCK_TIMEOUT_MS = 10_000;
251
+
252
+ function sleepSync(ms: number): void {
253
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
254
+ }
255
+
256
+ function readLockToken(lockPath: string): string | undefined {
257
+ try {
258
+ return readFileSync(lockPath, "utf8");
259
+ } catch (error) {
260
+ if (isMissing(error)) return undefined;
261
+ throw error;
262
+ }
263
+ }
264
+
265
+ export function breakStaleLock(lockPath: string, observed: string): void {
266
+ if (readLockToken(lockPath) === observed) rmSync(lockPath, { force: true });
267
+ }
268
+
269
+ /** Runs `body` holding `<filePath>.lock` (O_EXCL create); size check and rotation are one critical section (README, "Hook logs"). Exported for tests. */
270
+ export function withRotationLock(filePath: string, lockTimeoutMs: number, body: () => void): void {
271
+ const lockPath = `${filePath}.lock`;
272
+ const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
273
+ const deadline = Date.now() + lockTimeoutMs;
274
+ for (;;) {
275
+ try {
276
+ writeFileSync(lockPath, token, { flag: "wx", mode: 0o600 });
277
+ break;
278
+ } catch (error) {
279
+ if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
280
+ }
281
+ const held = statSync(lockPath, { throwIfNoEntry: false });
282
+ if (held !== undefined && Date.now() - held.mtimeMs > LOCK_STALE_MS) {
283
+ const observed = readLockToken(lockPath);
284
+ if (observed !== undefined) breakStaleLock(lockPath, observed);
285
+ }
286
+ if (Date.now() > deadline) throw new Error(`log rotation lock ${lockPath} is held; refusing to rotate without it`);
287
+ sleepSync(2);
288
+ }
289
+ try {
290
+ body();
291
+ } finally {
292
+ if (readLockToken(lockPath) === token) rmSync(lockPath, { force: true });
164
293
  }
165
- if (existsSync(filePath)) renameSync(filePath, `${filePath}.1`);
294
+ }
295
+
296
+ /** Rotates `filePath` to `.1` … `.5` when `incomingBytes` more would pass `maxBytes`, size re-read inside the lock; exported for raw-text streams (README, "Hook logs"). */
297
+ export function rotateIfNeeded(
298
+ filePath: string,
299
+ incomingBytes: number,
300
+ maxBytes: number = DEFAULT_MAX_BYTES,
301
+ lockTimeoutMs: number = LOCK_TIMEOUT_MS,
302
+ ): void {
303
+ const over = () => {
304
+ const size = statSync(filePath, { throwIfNoEntry: false })?.size ?? 0;
305
+ return size > 0 && size + incomingBytes > maxBytes;
306
+ };
307
+ if (!over()) return;
308
+ withRotationLock(filePath, lockTimeoutMs, () => {
309
+ if (over()) rotate(filePath);
310
+ });
166
311
  }
167
312
 
168
313
  const USERINFO_RE = /(https?:\/\/)[^/\s:@]+:[^/\s@]+@/gi;
@@ -223,22 +368,23 @@ function serialize(entry: LogEntryInput, ts: string): string {
223
368
  return `${JSON.stringify(record)}\n`;
224
369
  }
225
370
 
226
- /** Appends one JSON-Lines entry to `<dir>/<file>.log`, rotating first if needed. Never throws for a missing repo — callers resolve `dir` with `resolveLogDir`. */
371
+ /** Appends one JSON-Lines entry to `<dir>/<file>.log` (`file` is `<owner>/<name>`), rotating first if needed. Never throws for a missing repo — callers resolve `dir` with `resolveLogDir`. */
227
372
  export function writeLogEntry(file: LogFileName, entry: LogEntryInput, options: WriteLogOptions = {}): void {
228
373
  const dir = options.dir ?? resolveLogDir(options.cwd).dir;
229
374
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
230
375
  const ts = (options.now?.() ?? new Date()).toISOString();
231
376
  const line = serialize(entry, ts);
232
377
 
233
- ensureDirSecure(dir);
234
378
  const filePath = join(dir, `${file}.log`);
379
+ ensureDirSecure(dirname(filePath));
235
380
  ensureFileSecure(filePath);
236
381
 
237
- const currentSize = statSync(filePath).size;
238
- if (currentSize + Buffer.byteLength(line, "utf8") > maxBytes) {
239
- rotate(filePath);
240
- ensureFileSecure(filePath);
382
+ try {
383
+ rotateIfNeeded(filePath, Buffer.byteLength(line, "utf8"), maxBytes, options.lockTimeoutMs);
384
+ } catch (error) {
385
+ process.stderr.write(`adl log: rotation deferred, row kept: ${error instanceof Error ? error.message : String(error)}\n`);
241
386
  }
387
+ ensureFileSecure(filePath);
242
388
 
243
389
  appendFileSync(filePath, line, { mode: 0o600 });
244
390
  }
@@ -192,7 +192,7 @@ export async function run(argv: readonly string[], adlVersion?: string): Promise
192
192
  // `adl-config.toml` where `adl hooks deploy` has run — nowhere else. No
193
193
  // git root, or no `adl-config.toml` above `cwd`: a strict no-op (this
194
194
  // file's own header) — no rule evaluation, no identity check, no
195
- // `hooks.log` write. There is no global identity baseline: a repo where
195
+ // `agent-hooks/hooks.log` write. There is no global identity baseline: a repo where
196
196
  // `adl-config.toml` does not exist has no business running hooks at all.
197
197
  // The same no-op applies when `[hooks] enabled = false` (below).
198
198
  const managed = resolveManagedRepo(realRootIO, cwd)
@@ -55,7 +55,7 @@ export function toolNameEndingWithKnownSuffix(tool: string, knownSuffixes: Reado
55
55
  *
56
56
  * Prefix-less fallback (added 2026-09-28 with the one-agent-identity
57
57
  * standard): OpenCode's and Kimi's own MCP tool-call naming has never actually been
58
- * observed in production (`.adl/logs/hooks.log` shows only `mcp__`-prefixed
58
+ * observed in production (`.adl/logs/agent-hooks/hooks.log` shows only `mcp__`-prefixed
59
59
  * calls, from Claude/Codex/Cursor) — they MAY call an azdo tool with no
60
60
  * `mcp__` prefix at all, spelled `<server>_<tool>` / `<server>.<tool>` /
61
61
  * `<server>:<tool>`. When `tool` carries no `mcp__` prefix, this falls back
@@ -501,7 +501,8 @@ function classifyGitArgv(argv: readonly Token[], binary: string): readonly Invoc
501
501
 
502
502
  function walkGit(tokens: readonly Token[], index: number, parser: Parser): readonly Invocation[] {
503
503
  const binary = tokenValue(tokens[index] as Token)
504
- const argv = tokens.slice(index + 1)
504
+ // Redirections are the shell's, not git's: write targets are classified from the raw tokens elsewhere (Bug 626).
505
+ const argv = withoutRedirections(tokens.slice(index + 1))
505
506
  const scan = scanGitGlobals(argv, 0)
506
507
  if (scan.terminal) return [{ kind: 'read-only', name: 'git' }]
507
508
  if (scan.index >= argv.length) return uncertain('git command missing subcommand')
@@ -91,7 +91,7 @@ ready for the next research-page review.
91
91
 
92
92
  | Metric | Source | Use |
93
93
  | --- | --- | --- |
94
- | Total decisions per host | `hooks.log` / `hooks.log.1` / `hooks-deny.log`, deduped by `(ts, command, rule)` | Volume baseline |
94
+ | Total decisions per host | `agent-hooks/hooks.log*` / `agent-hooks/deny.log*` (plus the pre-Bug-600 top-level `hooks.log` / `hooks.log.1` / `hooks-deny.log`), deduped by `(ts, command, rule)` | Volume baseline |
95
95
  | Denials per host, broken down by `rule` field | same | Where the hook fires |
96
96
  | Search-tool denials (`tool.grep` / `tool.native-search` / `tool.find`) | same | The hook category this skill cares about |
97
97
  | `ast-grep` invocations (Bash command starting with `ast-grep`) | same | Whether the recommended tool is being used |
@@ -117,4 +117,26 @@ classifying them by tool). For the retry analysis, follow the
117
117
  — the hand-curated analysis this script would generate for today
118
118
  - `packages/agent-hooks/src/rules/impl/tool-grep.ts`,
119
119
  `tool-find.ts`, `tool-native-search.ts` — the deny messages the
120
- audit is measuring
120
+ audit is measuring
121
+
122
+ ## Script notes
123
+
124
+ Rationale moved out of `scripts/audit.sh` (REP-007: no multi-line comments).
125
+
126
+ - Usage: `scripts/audit.sh [--days N] [--out DIR]`; defaults `--days 7`, `--out docs/research`.
127
+ - Output: `<out>/research-YYYY-MM-DD-search-tool-usage-across-surfaces.md`, plus one new row appended
128
+ to `<out>/index.md`. Per-host, per-day tally of agent-hooks decisions, grouped by surface
129
+ (claude / codex / kimi / opencode), so the next weekly page in the series can compare against the
130
+ previous one. Reads only; never modifies the repo beyond those two files.
131
+ - Source data: `.adl/logs/agent-hooks/hooks.log[.1-.5]` and `deny.log[.1-.5]` (one folder per owner,
132
+ Bug 600, Tarun 2026-10-08); the pre-Bug-600 top-level `.adl/logs/hooks.log`, `hooks.log.1` and
133
+ `hooks-deny.log` (read-only history, never migrated, deduped against the new ones);
134
+ `.adl/logs/agents/tool-calls.log` (`adl run` lifecycle).
135
+ - Path discovery: the script lives in the agent-skills asset tree (5 levels deep in dev, 4 once
136
+ deployed to `.agents/skills/`), so it uses `git rev-parse --show-toplevel` to work in both layouts
137
+ and falls back to the dev layout if git is missing.
138
+ - Required tools: `jq`, `python3` and `rg`; each is checked up front and the script fails loud
139
+ (exit 1) naming the missing one. `rg` is a protected bootstrap tool on every managed machine.
140
+ - Cost: under 30 s on a typical 2-3 MB log set; no network.
141
+ - An existing page for today is left untouched (it may be hand-curated); `index.md` is still updated
142
+ so the catalog stays current. Existing index rows are never disturbed; one row is appended.
@@ -1,35 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # scripts/audit.sh — part of the agent-search-tool-audit skill
3
- # (packages/agent-skills/assets/agent-search-tool-audit/).
4
- #
5
- # Per-host, per-day tally of agent-hooks decisions in `.adl/logs/`,
6
- # grouped by surface (claude / codex / kimi / opencode). Writes one
7
- # dated markdown under `docs/research/` so the next weekly research
8
- # page in the "agent search-tool behaviour" series can compare against
9
- # the previous one. Reads only — never modifies the repo.
10
- #
11
- # Usage:
12
- # scripts/audit.sh [--days N] [--out DIR]
13
- #
14
- # Defaults: --days 7, --out docs/research
15
- #
16
- # Output:
17
- # <out>/research-YYYY-MM-DD-search-tool-usage-across-surfaces.md
18
- # <out>/index.md (one new row appended for the new page)
19
- #
20
- # Source data:
21
- # .adl/logs/hooks.log (current decision log)
22
- # .adl/logs/hooks.log.1 (rotated decision log)
23
- # .adl/logs/hooks-deny.log (dedicated deny stream)
24
- # .adl/logs/agents/tool-calls.log (adl run lifecycle)
25
- #
26
- # Path discovery: this script lives in the agent-skills asset tree
27
- # (5 levels deep in dev, 4 levels deep once deployed to
28
- # `.agents/skills/`). Use `git rev-parse --show-toplevel` so it
29
- # works in both layouts.
30
- #
31
- # Cost: <30 s on a typical 2-3 MB log set; no network, no new tools
32
- # beyond jq + python3 + the shell.
2
+ # Usage and rationale: SKILL.md, "Script notes", in this skill's folder.
33
3
 
34
4
  set -euo pipefail
35
5
 
@@ -40,7 +10,7 @@ while [[ $# -gt 0 ]]; do
40
10
  --days) DAYS="$2"; shift 2;;
41
11
  --out) OUT="$2"; shift 2;;
42
12
  -h|--help)
43
- sed -n '2,30p' "$0"
13
+ echo "usage: audit.sh [--days N] [--out DIR] (defaults: --days 7, --out docs/research)"
44
14
  exit 0
45
15
  ;;
46
16
  *)
@@ -52,10 +22,6 @@ done
52
22
 
53
23
  cd "$(dirname "$0")/../.."
54
24
 
55
- # `git rev-parse --show-toplevel` works in both dev (5 levels deep
56
- # under packages/agent-skills/assets/agent-search-tool-audit/scripts)
57
- # and deployed (4 levels deep under .agents/skills/). Fall back to
58
- # the dev layout if git is missing.
59
25
  if command -v git >/dev/null 2>&1; then
60
26
  if toplevel=$(git rev-parse --show-toplevel 2>/dev/null) && [[ -n "$toplevel" ]]; then
61
27
  cd "$toplevel"
@@ -63,9 +29,22 @@ if command -v git >/dev/null 2>&1; then
63
29
  fi
64
30
 
65
31
  command -v jq >/dev/null 2>&1 || { echo "jq is required and is not on PATH" >&2; exit 1; }
32
+ command -v rg >/dev/null 2>&1 || { echo "rg is required and is not on PATH" >&2; exit 1; }
66
33
  command -v python3 >/dev/null 2>&1 || { echo "python3 is required and is not on PATH" >&2; exit 1; }
67
34
 
68
35
  LOG_FILES=(
36
+ .adl/logs/agent-hooks/hooks.log
37
+ .adl/logs/agent-hooks/hooks.log.1
38
+ .adl/logs/agent-hooks/hooks.log.2
39
+ .adl/logs/agent-hooks/hooks.log.3
40
+ .adl/logs/agent-hooks/hooks.log.4
41
+ .adl/logs/agent-hooks/hooks.log.5
42
+ .adl/logs/agent-hooks/deny.log
43
+ .adl/logs/agent-hooks/deny.log.1
44
+ .adl/logs/agent-hooks/deny.log.2
45
+ .adl/logs/agent-hooks/deny.log.3
46
+ .adl/logs/agent-hooks/deny.log.4
47
+ .adl/logs/agent-hooks/deny.log.5
69
48
  .adl/logs/hooks.log
70
49
  .adl/logs/hooks.log.1
71
50
  .adl/logs/hooks-deny.log
@@ -80,15 +59,12 @@ TODAY="$(date -u +%Y-%m-%d)"
80
59
  RUN_TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
81
60
  OUT_FILE="${OUT}/research-${TODAY}-search-tool-usage-across-surfaces.md"
82
61
 
83
- # If today's page already exists, leave it alone — it might be a hand-curated
84
- # analysis from earlier in the day. Append / update `index.md` either way so
85
- # the catalog stays current.
86
62
  if [[ -f "$OUT_FILE" ]]; then
87
63
  echo "${OUT_FILE} already exists; leaving it untouched. Re-run after deleting it (or pass --out to a different dir)."
88
64
  # Still touch index.md so the catalog row stays current (idempotent).
89
65
  INDEX="${OUT}/index.md"
90
66
  ROW="| [Search-tool usage across surfaces — ${TODAY}](research-${TODAY}-search-tool-usage-across-surfaces.md) | Auto-generated weekly audit of per-host search-tool denials and recommended-tool invocations |"
91
- if [[ -f "$INDEX" ]] && ! grep -q "research-${TODAY}-search-tool-usage-across-surfaces.md" "$INDEX"; then
67
+ if [[ -f "$INDEX" ]] && ! rg -q "research-${TODAY}-search-tool-usage-across-surfaces.md" "$INDEX"; then
92
68
  python3 - "$INDEX" "$ROW" <<'PYEOF' >/dev/null
93
69
  import sys
94
70
  path, row = sys.argv[1], sys.argv[2]
@@ -177,13 +153,13 @@ lines.append("type: Research")
177
153
  lines.append("created: {0}".format(today))
178
154
  lines.append("status: stable")
179
155
  lines.append("last_update: {0}".format(run_ts))
180
- lines.append("sources: .adl/logs/hooks.log, .adl/logs/hooks.log.1, .adl/logs/hooks-deny.log")
156
+ lines.append("sources: .adl/logs/agent-hooks/hooks.log*, .adl/logs/agent-hooks/deny.log*, .adl/logs/hooks.log, .adl/logs/hooks.log.1, .adl/logs/hooks-deny.log")
181
157
  lines.append("related: ./index.md, ./research-2026-10-02-search-tool-usage-across-surfaces.md")
182
158
  lines.append("---")
183
159
  lines.append("")
184
160
  lines.append("# Agent interface search-tool usage across surfaces ({0})".format(today))
185
161
  lines.append("")
186
- lines.append("Auto-generated weekly audit. Window: trailing {0} UTC days ending {1}. Source: `.adl/logs/hooks*.log` and `.adl/logs/hooks-deny.log`. Re-run with `just research/weekly-search-audit`.".format(days, today))
162
+ lines.append("Auto-generated weekly audit. Window: trailing {0} UTC days ending {1}. Source: `.adl/logs/agent-hooks/` (plus the pre-Bug-600 top-level `hooks.log*` and `hooks-deny.log`). Re-run with `just research/weekly-search-audit`.".format(days, today))
187
163
  lines.append("")
188
164
  lines.append("## Headline per host")
189
165
  lines.append("")
@@ -238,8 +214,6 @@ lines.append("")
238
214
  sys.stdout.write("\n".join(lines))
239
215
  PYEOF
240
216
 
241
- # Append / update index.md with the new page row. We don't disturb
242
- # other rows; we add one to the end of the table.
243
217
  INDEX="${OUT}/index.md"
244
218
  ROW="| [Search-tool usage across surfaces — ${TODAY}](research-${TODAY}-search-tool-usage-across-surfaces.md) | Auto-generated weekly audit of per-host search-tool denials and recommended-tool invocations |"
245
219
 
@@ -247,7 +221,7 @@ if [[ ! -f "$INDEX" ]]; then
247
221
  echo "index.md not present at ${INDEX}; not updating" >&2
248
222
  exit 0
249
223
  fi
250
- if grep -q "research-${TODAY}-search-tool-usage-across-surfaces.md" "$INDEX"; then
224
+ if rg -q "research-${TODAY}-search-tool-usage-across-surfaces.md" "$INDEX"; then
251
225
  echo "index.md already has today's page; not appending"
252
226
  else
253
227
  # Insert before the closing fence (if any) or just append before EOF.
@@ -98,3 +98,12 @@ Required before declaring done:
98
98
  - [ ] The hypothesis that turned out correct is stated in the commit / PR message — so the next debugger learns
99
99
 
100
100
  **Then ask: what would have prevented this bug?** If the answer involves architectural change (no good test seam, tangled callers, hidden coupling) hand off to the `/improve-codebase-architecture` skill with the specifics. Make the recommendation **after** the fix is in, not before — you have more information now than when you started.
101
+
102
+ ## HITL loop script
103
+
104
+ `scripts/hitl-loop.template.sh` is the human-in-the-loop reproduction loop (usage notes moved out of the script so the deployed copy has no multi-line comment run).
105
+
106
+ - Copy the file, edit the steps in it, and run it. The agent runs the script; the user follows prompts in their terminal.
107
+ - Usage: `bash hitl-loop.template.sh`
108
+ - Two helpers: `step "<instruction>"` shows the instruction and waits for Enter; `capture VAR "<question>"` shows the question and reads the response into `VAR`.
109
+ - At the end, captured values are printed as `KEY=VALUE` for the agent to parse.
@@ -1,16 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Human-in-the-loop reproduction loop.
3
- # Copy this file, edit the steps below, and run it.
4
- # The agent runs the script; the user follows prompts in their terminal.
5
- #
6
- # Usage:
7
- # bash hitl-loop.template.sh
8
- #
9
- # Two helpers:
10
- # step "<instruction>" → show instruction, wait for Enter
11
- # capture VAR "<question>" → show question, read response into VAR
12
- #
13
- # At the end, captured values are printed as KEY=VALUE for the agent to parse.
2
+ # Human-in-the-loop reproduction loop; usage in SKILL.md "HITL loop script".
14
3
 
15
4
  set -euo pipefail
16
5
 
@@ -33,7 +33,7 @@
33
33
  "remove_worktrees": true
34
34
  },
35
35
  "brew_upgrade_exclude": [],
36
- "protected_tools": ["git", "jq"],
36
+ "protected_tools": ["git", "jq", "ripgrep"],
37
37
  "git_global_config": {
38
38
  "credential.helper": ["", "{git-credential-manager}"],
39
39
  "credential.https://dev.azure.com.useHttpPath": "true",
@@ -159,6 +159,7 @@
159
159
  "node": {"manager": "brew", "package": "node", "executable": "node"},
160
160
  "ripgrep": {"manager": "brew", "package": "ripgrep", "executable": "rg"},
161
161
  "search": {"manager": "shim", "package": "enact-docs-search", "executable": "/usr/local/bin/search"},
162
+ "shfmt": {"manager": "brew", "package": "shfmt", "executable": "shfmt"},
162
163
  "tailscale": {"manager": "apt", "package": "tailscale", "executable": "/usr/bin/tailscale"},
163
164
  "uv": {"manager": "brew", "package": "uv", "executable": "uv"},
164
165
  "yq": {"manager": "brew", "package": "yq", "executable": "yq"}
@@ -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,8 +44,8 @@ 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.
@@ -55,8 +55,11 @@ branch prefixes `feat|fix|docs|chore`, 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