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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/bin/adl +2 -2
  2. package/bin/git +16 -17
  3. package/bin/git-ado +25 -24
  4. package/bin/git-defaults +173 -0
  5. package/bin/health-check +8 -1
  6. package/bin/kimi-hooks +1 -1
  7. package/bin/lib/adl-package.sh +257 -0
  8. package/bin/lib/fleet-host.sh +8 -5
  9. package/bin/reconcile +482 -232
  10. package/bin/reconcile-host +30 -20
  11. package/bin/setup +20 -17
  12. package/bin/shell-path-policy +15 -5
  13. package/bin/sync-and-reconcile +62 -64
  14. package/bin/validate-manifest +33 -9
  15. package/package.json +2 -10
  16. package/packages/agent-hooks/README.md +14 -9
  17. package/packages/agent-hooks/assets/hooks.json +0 -20
  18. package/packages/agent-hooks/src/cli/args.ts +1 -1
  19. package/packages/agent-hooks/src/cli/doctor.ts +6 -58
  20. package/packages/agent-hooks/src/deploy/deploy.ts +12 -41
  21. package/packages/agent-hooks/src/deploy/doctor.ts +8 -11
  22. package/packages/agent-hooks/src/deploy/index.ts +14 -14
  23. package/packages/agent-hooks/src/deploy/json-merge.ts +7 -8
  24. package/packages/agent-hooks/src/deploy/legacy-lock.ts +2 -2
  25. package/packages/agent-hooks/src/deploy/list.ts +1 -1
  26. package/packages/agent-hooks/src/deploy/lock.ts +31 -12
  27. package/packages/agent-hooks/src/deploy/profile.ts +3 -4
  28. package/packages/agent-hooks/src/deploy/source.ts +1 -1
  29. package/packages/agent-hooks/src/gating/config.ts +10 -24
  30. package/packages/agent-hooks/src/gating/root.ts +3 -3
  31. package/packages/agent-hooks/src/hooks/briefing/handler.ts +2 -2
  32. package/packages/agent-hooks/src/hooks/briefing/index.ts +27 -287
  33. package/packages/agent-hooks/src/hooks/contract.ts +17 -17
  34. package/packages/agent-hooks/src/hooks/crg/background.ts +27 -0
  35. package/packages/agent-hooks/src/hooks/crg/index.ts +7 -6
  36. package/packages/agent-hooks/src/hooks/log.ts +8 -10
  37. package/packages/agent-hooks/src/hooks/user-prompt-submit/match.ts +1 -1
  38. package/packages/agent-hooks/src/hooks/user-prompt-submit/tokenizer.ts +1 -1
  39. package/packages/agent-hooks/src/hooks/user-prompt-submit/vector.ts +6 -5
  40. package/packages/agent-hooks/src/hosts/registry.ts +1 -1
  41. package/packages/agent-hooks/src/log/adl-dir.ts +44 -0
  42. package/packages/agent-hooks/src/log/writer.ts +16 -2
  43. package/packages/agent-hooks/src/main.ts +7 -41
  44. package/packages/agent-hooks/src/pre-tool-use/handler.ts +71 -82
  45. package/packages/agent-hooks/src/rules/engine.ts +13 -17
  46. package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +3 -3
  47. package/packages/agent-hooks/src/rules/impl/adl-git-credential-bypass.ts +12 -14
  48. package/packages/agent-hooks/src/rules/impl/adl-git-identity.ts +28 -28
  49. package/packages/agent-hooks/src/rules/impl/adl-git-push-policy.ts +8 -9
  50. package/packages/agent-hooks/src/rules/impl/adl-human-azdo-bypass.ts +56 -59
  51. package/packages/agent-hooks/src/rules/impl/adl-identity.ts +16 -23
  52. package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +19 -16
  53. package/packages/agent-hooks/src/rules/impl/adl-pr-complete-pending.ts +2 -2
  54. package/packages/agent-hooks/src/rules/impl/adl-pr-review-role.ts +9 -12
  55. package/packages/agent-hooks/src/rules/impl/adl-pr-role.ts +5 -7
  56. package/packages/agent-hooks/src/rules/impl/adl-run-required.ts +411 -0
  57. package/packages/agent-hooks/src/rules/impl/adl-token-minting-role.ts +4 -5
  58. package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +6 -7
  59. package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +77 -70
  60. package/packages/agent-hooks/src/rules/impl/deny-allowlist.ts +159 -0
  61. package/packages/agent-hooks/src/rules/impl/git-helpers.ts +21 -23
  62. package/packages/agent-hooks/src/rules/impl/git-worktree.ts +14 -28
  63. package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +3 -3
  64. package/packages/agent-hooks/src/rules/impl/messages.ts +1 -1
  65. package/packages/agent-hooks/src/rules/impl/pr-completion.ts +1 -1
  66. package/packages/agent-hooks/src/rules/impl/tool-azure-devops.ts +35 -46
  67. package/packages/agent-hooks/src/rules/impl/tool-find.ts +34 -7
  68. package/packages/agent-hooks/src/rules/impl/tool-grep.ts +34 -12
  69. package/packages/agent-hooks/src/rules/impl/tool-native-search.ts +52 -9
  70. package/packages/agent-hooks/src/rules/ports.ts +5 -7
  71. package/packages/agent-hooks/src/rules/registry.ts +4 -6
  72. package/packages/agent-hooks/src/rules/shell/git-identity.ts +86 -101
  73. package/packages/agent-hooks/src/rules/shell/git-internals.ts +1 -1
  74. package/packages/agent-hooks/src/rules/shell/invocation.ts +24 -118
  75. package/packages/agent-hooks/src/rules/shell/piped-script.ts +1 -1
  76. package/packages/agent-hooks/src/rules/shell/prefixes.ts +1 -1
  77. package/packages/agent-hooks/src/rules/shell/protected-carrier-writes.ts +11 -10
  78. package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +26 -26
  79. package/packages/agent-hooks/src/rules/shell/substitution.ts +1 -1
  80. package/packages/agent-hooks/src/rules/shell/xargs.ts +2 -2
  81. package/packages/agent-hooks/src/rules/spec.ts +2 -2
  82. package/packages/agent-settings/README.md +287 -0
  83. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +14 -16
  84. package/packages/agent-settings/assets/agents/architect.toml +2 -0
  85. package/packages/agent-settings/assets/agents/code-reviewer.toml +2 -0
  86. package/packages/agent-settings/assets/agents/executor.toml +2 -0
  87. package/packages/agent-settings/assets/agents/explore.toml +2 -0
  88. package/packages/agent-settings/assets/agents/planner.toml +2 -0
  89. package/packages/agent-settings/assets/mcp.json +0 -4
  90. package/packages/agent-settings/deploy-profile.json +139 -0
  91. package/packages/agent-settings/deploy-profile.schema.json +147 -0
  92. package/packages/agent-settings/managed/README.md +29 -30
  93. package/packages/agent-settings/managed/codex-requirements.toml +9 -9
  94. package/packages/agent-settings/package.json +20 -0
  95. package/packages/agent-settings/src/deploy/agent-render.ts +2 -2
  96. package/packages/agent-settings/src/deploy/agent-toml.ts +1 -1
  97. package/packages/agent-settings/src/deploy/agents-deploy.ts +17 -3
  98. package/packages/agent-settings/src/deploy/doctor.ts +3 -83
  99. package/packages/agent-settings/src/deploy/index.ts +6 -33
  100. package/packages/agent-settings/src/deploy/legacy-lock.ts +6 -6
  101. package/packages/agent-settings/src/deploy/list.ts +5 -19
  102. package/packages/agent-settings/src/deploy/lock.ts +8 -18
  103. package/packages/agent-settings/src/deploy/mcp-deploy.ts +10 -5
  104. package/packages/agent-settings/src/deploy/mcp-merge.ts +22 -3
  105. package/packages/agent-settings/src/deploy/mcp-source.ts +1 -1
  106. package/packages/agent-settings/src/deploy/profile.ts +11 -35
  107. package/packages/agent-settings/src/deploy/retire-lean-ctx.ts +184 -0
  108. package/packages/agent-settings/src/deploy/settings.ts +17 -22
  109. package/packages/agent-settings/src/global-surface-sweep.ts +0 -2
  110. package/packages/agent-settings/src/kimi-hooks.ts +339 -192
  111. package/packages/agent-settings/src/opencode-hooks.ts +1 -1
  112. package/packages/agent-settings/tsconfig.json +23 -0
  113. package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +120 -0
  114. package/packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh +276 -0
  115. package/packages/agent-skills/assets/debug-issue/SKILL.md +8 -7
  116. package/packages/agent-skills/assets/explore-codebase/SKILL.md +13 -13
  117. package/packages/agent-skills/assets/refactor-safely/SKILL.md +12 -11
  118. package/packages/agent-skills/assets/review-changes/SKILL.md +7 -7
  119. package/packages/agent-skills/assets/testing/references/e2e.md +37 -0
  120. package/packages/agent-skills/assets/testing/references/integration.md +37 -0
  121. package/packages/agent-skills/assets/testing/references/unit.md +32 -0
  122. package/packages/agent-skills/dist/deploy.d.ts +2 -0
  123. package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
  124. package/packages/agent-skills/dist/deploy.js +27 -1
  125. package/packages/agent-skills/dist/deploy.js.map +1 -1
  126. package/packages/agent-skills/dist/doctor.d.ts +0 -2
  127. package/packages/agent-skills/dist/doctor.d.ts.map +1 -1
  128. package/packages/agent-skills/dist/doctor.js +1 -1
  129. package/packages/agent-skills/dist/doctor.js.map +1 -1
  130. package/packages/agent-skills/dist/fs-utils.d.ts +0 -1
  131. package/packages/agent-skills/dist/fs-utils.d.ts.map +1 -1
  132. package/packages/agent-skills/dist/fs-utils.js +1 -1
  133. package/packages/agent-skills/dist/fs-utils.js.map +1 -1
  134. package/packages/agent-skills/src/deploy.ts +29 -1
  135. package/packages/agent-skills/src/doctor.ts +1 -1
  136. package/packages/agent-skills/src/fs-utils.ts +1 -1
  137. package/packages/azdo/README.md +10 -9
  138. package/packages/azdo/package.json +1 -3
  139. package/packages/azdo/src/azdo-pl.ts +2 -54
  140. package/packages/azdo/src/azdo-pr-review.ts +4 -4
  141. package/packages/azdo/src/azdo-repo-doctor.ts +101 -52
  142. package/packages/azdo/src/azdo-wit.ts +41 -2
  143. package/packages/azdo/src/lib/agent-identity.ts +12 -14
  144. package/packages/azdo/src/lib/agent-token.ts +2 -2
  145. package/packages/azdo/src/lib/client.ts +0 -5
  146. package/packages/azdo/src/lib/graph-identity.ts +4 -4
  147. package/packages/azdo/src/workflow-rules-matrix.csv +85 -0
  148. package/packages/azdo/src/workflow-rules.ts +128 -0
  149. package/packages/fleet/manifest.json +35 -11
  150. package/packages/fleet/manifest.schema.json +62 -10
  151. package/packages/fleet/src/default-agent-file.ts +3 -3
  152. package/packages/fleet/src/install-adl-package.ts +73 -15
  153. package/packages/fleet/src/install-embedding-model.ts +13 -13
  154. package/packages/fleet/src/managed-settings.ts +7 -7
  155. package/packages/fleet/src/repo-maintain.ts +91 -55
  156. package/packages/fleet/src/scratch-reap.ts +5 -5
  157. package/packages/fleet/src/vscode-settings.ts +5 -5
  158. package/packages/repo-controls/README.md +141 -43
  159. package/packages/repo-controls/assets/promote.ts +4 -4
  160. package/packages/repo-controls/assets/reconcile-guard.sh +10 -46
  161. package/packages/repo-controls/assets/version-bump.sh +31 -84
  162. package/packages/repo-controls/bunfig.toml +6 -7
  163. package/packages/repo-controls/package/adl-compliance/AGENTS.md +5 -6
  164. package/packages/repo-controls/package/adl-compliance/conftest.py +3 -1
  165. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +4 -16
  166. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +9 -2
  167. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +1 -0
  168. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/test_check.py +11 -0
  169. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/__init__.py +7 -0
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/check.py +266 -0
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.drafts/c.md +1 -0
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.drafts/d.md +1 -0
  173. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.hidden.md +1 -0
  174. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/a.md +1 -0
  175. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/b.md +1 -0
  176. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/data.csv +1 -0
  177. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/diagram.png +1 -0
  178. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/mock.png +1 -0
  179. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/sub/nested.md +1 -0
  180. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs.md +1 -0
  181. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/grants.yaml +4 -0
  182. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p1.md +1 -0
  183. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p2.md +1 -0
  184. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p3.md +1 -0
  185. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p4.md +1 -0
  186. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p5.md +1 -0
  187. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/build.md +1 -0
  188. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/overview.md +1 -0
  189. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/install.md +1 -0
  190. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/start.md +1 -0
  191. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/grants.yaml +4 -0
  192. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-001-tenancy.md +1 -0
  193. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-002-carrier.md +1 -0
  194. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-003-cli.md +1 -0
  195. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/archive/adr-000-old.md +1 -0
  196. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/archive/adr-000-older.md +1 -0
  197. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/a.md +1 -0
  198. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/b.md +1 -0
  199. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/c.md +1 -0
  200. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/grants.yaml +4 -0
  201. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-01.md +1 -0
  202. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-02.md +1 -0
  203. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-03.md +1 -0
  204. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-04.md +1 -0
  205. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-05.md +1 -0
  206. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-06.md +1 -0
  207. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-07.md +1 -0
  208. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-08.md +1 -0
  209. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-09.md +1 -0
  210. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-10.md +1 -0
  211. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-11.md +1 -0
  212. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-12.md +1 -0
  213. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-13.md +1 -0
  214. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-14.md +1 -0
  215. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-15.md +1 -0
  216. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-16.md +1 -0
  217. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/grants.yaml +4 -0
  218. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/a.md +1 -0
  219. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/b.md +1 -0
  220. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/c.md +1 -0
  221. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/grants.yaml +4 -0
  222. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/a.md +1 -0
  223. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/b.md +1 -0
  224. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/c.md +1 -0
  225. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/grants.yaml +4 -0
  226. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/a.md +1 -0
  227. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/b.md +1 -0
  228. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/c.md +1 -0
  229. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/d.md +1 -0
  230. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/e.md +1 -0
  231. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/grants.yaml +4 -0
  232. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/a.md +1 -0
  233. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/b.md +1 -0
  234. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/c.md +1 -0
  235. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/short/only.md +1 -0
  236. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/grants.yaml +4 -0
  237. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/a.md +1 -0
  238. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/b.md +1 -0
  239. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/c.md +1 -0
  240. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/d.md +1 -0
  241. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/a.md +1 -0
  242. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/b.md +1 -0
  243. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/grants.yaml +6 -0
  244. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/a.md +1 -0
  245. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/b.md +1 -0
  246. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/c.md +1 -0
  247. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/grants.yaml +3 -0
  248. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/test_check.py +345 -0
  249. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +18 -5
  250. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +35 -0
  251. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/__init__.py +7 -0
  252. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/check.py +91 -0
  253. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/AGENTS.md +3 -0
  254. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/CLAUDE.md +3 -0
  255. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/-leading-hyphen.md +3 -0
  256. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/AGENTS.md +3 -0
  257. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/double--hyphen.md +3 -0
  258. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/getting started.md +3 -0
  259. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/nested/page-here.md +3 -0
  260. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/trailing-hyphen-.md +3 -0
  261. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +4 -0
  262. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/user_profile.md +3 -0
  263. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/UI-CONTRACT.png +2 -0
  264. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/diagram.png +1 -0
  265. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/index.md +4 -0
  266. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/notes.md +3 -0
  267. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/reference.html +1 -0
  268. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/2026-10-experiment.md +3 -0
  269. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +6 -0
  270. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs.md +5 -0
  271. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +194 -0
  272. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +14 -2
  273. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +5 -5
  274. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-shipped-register-legacy/docs/compliance/index.md +10 -0
  275. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-shipped-register-type/docs/compliance/index.md +10 -0
  276. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +58 -6
  277. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/test_check.py +0 -1
  278. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/check.py +64 -17
  279. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +60 -9
  280. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +17 -0
  281. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +13 -0
  282. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs/architecture/demo.md +0 -1
  283. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs-okf-profiles.yaml +0 -1
  284. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/no-type.md +9 -0
  285. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-experiment.md +10 -0
  286. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-research.md +10 -0
  287. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs-okf-profiles.yaml +5 -0
  288. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs/architecture/bad-status.md +0 -1
  289. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs-okf-profiles.yaml +0 -1
  290. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/experiment-ok.md +15 -0
  291. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +15 -0
  292. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +16 -0
  293. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +11 -0
  294. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +11 -0
  295. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-last-update.md +11 -0
  296. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-sources.md +10 -0
  297. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +11 -0
  298. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +11 -0
  299. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +16 -0
  300. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +14 -0
  301. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/architecture/created-date.md +21 -0
  302. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/architecture/research-shaped.md +19 -0
  303. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/operations/runbook.md +26 -0
  304. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/test_check.py +228 -2
  305. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +5 -0
  306. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +11 -2
  307. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/__init__.py +5 -3
  308. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +160 -44
  309. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +0 -2
  310. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/a.ts +1 -0
  311. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/b.ts +1 -0
  312. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/grants.yaml +3 -0
  313. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +0 -2
  314. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  315. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +5 -0
  316. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/a.ts +1 -0
  317. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/b.ts +1 -0
  318. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/c.ts +1 -0
  319. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +4 -0
  320. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  321. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +5 -0
  322. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +0 -2
  323. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  324. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/grants.yaml +3 -0
  325. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/register.py +127 -0
  326. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +265 -29
  327. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +75 -2
  328. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +194 -0
  329. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/check.py +56 -10
  330. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/test_check.py +256 -0
  331. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +40 -12
  332. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +108 -0
  333. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +1 -1
  334. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +2 -3
  335. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +23 -6
  336. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +76 -0
  337. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +58 -17
  338. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +161 -0
  339. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +35 -3
  340. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +108 -1
  341. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +281 -61
  342. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/absolute-path.yaml +3 -0
  343. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/backslash-path.yaml +3 -0
  344. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/baseline-is-a-list.yaml +2 -0
  345. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/boolean-size.yaml +3 -0
  346. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/dot-segment.yaml +3 -0
  347. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/duplicate-path.yaml +4 -0
  348. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-file.yaml +0 -0
  349. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-segment.yaml +3 -0
  350. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/float-size.yaml +3 -0
  351. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/negative-size.yaml +3 -0
  352. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-over-limit-baseline.yaml +1 -0
  353. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-schema-version.yaml +1 -0
  354. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/non-string-path.yaml +3 -0
  355. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/not-yaml.yaml +2 -0
  356. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/null-size.yaml +3 -0
  357. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/parent-segment.yaml +3 -0
  358. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/string-size.yaml +3 -0
  359. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/top-level-is-a-list.yaml +2 -0
  360. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/trailing-slash.yaml +3 -0
  361. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/unknown-key.yaml +3 -0
  362. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/wrong-schema-version.yaml +2 -0
  363. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/zero-size.yaml +3 -0
  364. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/valid.yaml +4 -0
  365. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +758 -45
  366. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/check.py +4 -3
  367. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/test_check.py +2 -2
  368. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +490 -0
  369. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +368 -55
  370. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +4 -2
  371. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +523 -0
  372. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +1179 -0
  373. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +122 -3
  374. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +33 -1
  375. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +0 -11
  376. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/context.py +16 -1
  377. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -2
  378. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/docs_shape.py +12 -1
  379. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +133 -147
  380. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +133 -147
  381. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +1 -1
  382. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +52 -10
  383. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +23 -4
  384. package/packages/repo-controls/package.json +2 -9
  385. package/packages/repo-controls/scripts/hooks/post-merge +13 -0
  386. package/packages/repo-controls/scripts/hooks/pre-push +3 -3
  387. package/packages/repo-controls/src/azdo-push.ts +64 -36
  388. package/packages/repo-controls/src/azure-devops-governance.ts +7 -9
  389. package/packages/repo-controls/src/azure-devops-remote.ts +1 -1
  390. package/packages/repo-controls/src/cli.ts +29 -8
  391. package/packages/repo-controls/src/compliance-config.ts +410 -2
  392. package/packages/repo-controls/src/config.ts +133 -60
  393. package/packages/repo-controls/src/constants.ts +13 -27
  394. package/packages/repo-controls/src/coverage-ratchet.ts +4 -4
  395. package/packages/repo-controls/src/doctor.ts +46 -19
  396. package/packages/repo-controls/src/gitignore-block.ts +5 -29
  397. package/packages/repo-controls/src/hook-log.ts +1 -2
  398. package/packages/repo-controls/src/hook.ts +201 -45
  399. package/packages/repo-controls/src/install.ts +14 -8
  400. package/packages/repo-controls/src/integrity.ts +3 -3
  401. package/packages/repo-controls/src/lfs.ts +111 -0
  402. package/packages/repo-controls/src/promote.ts +8 -8
  403. package/packages/repo-controls/src/runtime.ts +9 -1
  404. package/packages/repo-controls/src/workflows-drift.ts +4 -5
  405. package/packages/repo-controls/src/worktree-guard.ts +3 -3
@@ -0,0 +1,4 @@
1
+ schema_version: 2
2
+ over_children_baseline:
3
+ archive/src: 4
4
+ single_file_baseline: {}
@@ -0,0 +1,6 @@
1
+ schema_version: 2
2
+ docs_pages:
3
+ baseline:
4
+ docs/shrunk: 3
5
+ docs/gone: 2
6
+ docs/held: 4
@@ -0,0 +1,345 @@
1
+ """DOC-012 — the page-count cap for every directory directly under docs/.
2
+
3
+ Ten committed corpora, the runner-driven shape LAY-004's test uses:
4
+ `docs-size-clean` (every directory within the engine-default cap of 15),
5
+ `docs-size-default-cap` (sixteen pages in one directory, no declared cap —
6
+ DIR-004 names the default; the old-style register beside it proves a register
7
+ without a `docs_pages` section is no exemption), `docs-size-over-cap` (DIR-004
8
+ with an in-cap control beside it), `docs-size-exempt` (a baseline entry
9
+ exempts its directory up to the recorded number), `docs-size-grew` (DIR-005,
10
+ a directory past its recorded exemption), `docs-size-stale` (DIR-006's two
11
+ kept cases, shrunk-under-cap and directory-gone, with a still-valid baseline
12
+ entry beside them), `docs-size-no-section` (an old-style register holds no
13
+ exemptions, so the cap is enforced bare over its five-page directory),
14
+ `docs-size-decisions` (the flat ADR log is exempt by rule at any depth, with
15
+ an over-cap directory beside it proving the corpus is not silent by
16
+ accident), `docs-size-assets` (non-`.md` assets, dot-prefixed pages, and
17
+ paths outside docs/ are not counted, at the runner level and — reading the
18
+ check directly, the way DOC-011's test does — at the check level), and
19
+ `docs-size-unusable` (a register that exists but is not valid YAML —
20
+ could-not-run, the ruling's kept exception).
21
+
22
+ V2 (owner ruling 2026-10-02, replacing v1 the same day): the cap lives in
23
+ `[controls.compliance.limits].docs_pages_per_directory` (engine default 15),
24
+ NOT in the register. The register is exemptions ONLY — a `docs_pages.baseline`
25
+ entry exempts its directory up to the recorded number; no entry means no
26
+ exemption, so an ABSENT register or a register without a `docs_pages` section
27
+ grades STRICTER (the widening input's normal contract — v1's "ratchets sit
28
+ ON TOP" absence-is-clean exception belonged to LAY-004, whose configuration
29
+ IS the register; DOC-012's configuration is the declared limit). Two tests
30
+ below are the v1 tests reworked for the flip, and say so. At zero exemptions
31
+ the register file has no meaning and is deleted — adl-azdo's own was.
32
+
33
+ Each fixture carries its own `grants.yaml` at its own root, passed as the
34
+ `register` input override — the same mechanism LAY-004's test uses. The cap
35
+ travels as the `docs-pages-per-directory` declaration, the same channel
36
+ LAY-014's tests use for `scripts-direct-files`. `docs_root` needs no
37
+ override: the corpora are laid out in the fixture tree shape and
38
+ `conftest.py` declares it once for the suite.
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ import pathlib
44
+
45
+ from adl_compliance import runner
46
+ from adl_compliance.checks.docs.directory_size import DocsDirectorySize
47
+ from adl_compliance.core.context import Context, FileSet
48
+ from adl_compliance.core.finding import Finding
49
+ from adl_compliance.core.verdict import Outcome
50
+ from adl_compliance.policy.registry import load
51
+
52
+ FIXTURES = pathlib.Path(__file__).parent / "fixtures"
53
+ ROW = load().by_id("DOC-012")
54
+
55
+
56
+ def corpus_files(root: pathlib.Path) -> tuple[str, ...]:
57
+ return tuple(
58
+ sorted(
59
+ str(path.relative_to(root))
60
+ for path in root.rglob("*")
61
+ if path.is_file() and path.name != "grants.yaml"
62
+ )
63
+ )
64
+
65
+
66
+ def run(case: str, *, register: str = "grants.yaml", cap: str | None = None) -> object:
67
+ directory = FIXTURES / case
68
+ declarations = {} if cap is None else {DocsDirectorySize.PAGES_DECLARATION: cap}
69
+ return runner.run(
70
+ ROW, directory, corpus_files(directory),
71
+ inputs={ROW.id: {"register": directory / register}},
72
+ declarations=declarations,
73
+ )
74
+
75
+
76
+ def findings_for(
77
+ root: pathlib.Path, given: tuple[str, ...], *, cap: str | None = None
78
+ ) -> tuple[Finding, ...]:
79
+ """The check read directly, bypassing row-scope narrowing — the honest way
80
+ to prove the CHECK's own filters (prefix, `.md`, dot-components,
81
+ liveness), which the runner's scope intersection could otherwise be
82
+ mistaken for."""
83
+ declarations = {} if cap is None else {DocsDirectorySize.PAGES_DECLARATION: cap}
84
+ ctx = Context(
85
+ root=root, row=ROW,
86
+ inputs={"register": root / "grants.yaml", "docs_root": root / "docs"},
87
+ declarations=declarations,
88
+ )
89
+ return tuple(DocsDirectorySize().run(ctx, FileSet(paths=given)))
90
+
91
+
92
+ # ------------------------------------------------------------------ the cap
93
+
94
+
95
+ def test_a_tree_within_the_default_cap_passes() -> None:
96
+ """No declaration, cap 15, every directory well under it."""
97
+ verdict = run("docs-size-clean")
98
+ assert verdict.outcome is Outcome.CLEAN, verdict.render()
99
+ assert not verdict.findings
100
+ assert not verdict.advisories
101
+
102
+
103
+ def test_a_directory_over_the_default_cap_is_dir_004() -> None:
104
+ verdict = run("docs-size-default-cap")
105
+ assert verdict.outcome is Outcome.FINDINGS
106
+ findings = [f for f in verdict.findings if f.rule == "DIR-004"]
107
+ assert len(findings) == 1, tuple(f.render() for f in verdict.findings)
108
+ finding = findings[0]
109
+ assert finding.path == "docs/guide"
110
+ assert finding.message == "16 pages (max 15), no ratchet entry"
111
+ assert finding.fix == (
112
+ "add a docs_pages baseline entry for docs/guide to the ratchet "
113
+ "register, or split the directory"
114
+ )
115
+
116
+
117
+ def test_a_declared_cap_wins_over_the_default() -> None:
118
+ """The same three-page corpus is clean under a declared cap of 3 and
119
+ reported under a declared cap of 2 — the declaration, not the default,
120
+ is the limit in force (no down-only ratchet, same as LAY-014)."""
121
+ assert run("docs-size-over-cap", cap="3").outcome is Outcome.CLEAN
122
+ assert run("docs-size-over-cap", cap="2").outcome is Outcome.FINDINGS
123
+
124
+
125
+ def test_a_malformed_cap_declaration_is_could_not_run() -> None:
126
+ """Mirrors LAY-014's `direct_file_limit`: a value that is not a base-10
127
+ positive integer is could-not-run naming
128
+ `[controls.compliance.limits].docs_pages_per_directory`, never a quiet
129
+ fall back to the default."""
130
+ verdict = run("docs-size-over-cap", cap="banana")
131
+ assert verdict.outcome is Outcome.COULD_NOT_RUN
132
+ assert "docs_pages_per_directory" in verdict.message
133
+ verdict = run("docs-size-over-cap", cap="0")
134
+ assert verdict.outcome is Outcome.COULD_NOT_RUN
135
+ assert "docs_pages_per_directory" in verdict.message
136
+
137
+
138
+ # -------------------------------------------- the register is exemptions only
139
+
140
+
141
+ def test_an_absent_register_enforces_the_cap_bare() -> None:
142
+ """REWORKED from v1's `test_an_absent_register_means_nothing_is_ratcheted`,
143
+ which asserted the opposite. Owner ruling 2026-10-02 (v2): the register
144
+ holds EXEMPTIONS, not configuration — if it is not there, no exemptions
145
+ are allowed, so the cap is enforced bare and the over-cap directory
146
+ reports. (v1's "ratchets sit ON TOP" absence-is-clean reading belonged to
147
+ LAY-004, whose configuration IS the register; DOC-012's configuration is
148
+ the declared limit, so absence here grades STRICTER — the widening
149
+ input's normal contract.)"""
150
+ verdict = run("docs-size-over-cap", register="does-not-exist.yaml", cap="2")
151
+ assert verdict.outcome is Outcome.FINDINGS
152
+ findings = [f for f in verdict.findings if f.rule == "DIR-004"]
153
+ assert len(findings) == 1, tuple(f.render() for f in verdict.findings)
154
+ assert findings[0].path == "docs/guide"
155
+
156
+
157
+ def test_a_register_without_a_docs_pages_section_enforces_the_cap_bare() -> None:
158
+ """REWORKED from v1's `test_a_register_without_a_docs_pages_section_passes_clean`,
159
+ which asserted the opposite under the v1 ruling. Same v2 reasoning as the
160
+ absent-register case, one step milder: the register EXISTS and parses —
161
+ it just names no exemptions, so the five-page directory reports against a
162
+ declared cap of 3."""
163
+ verdict = run("docs-size-no-section", cap="3")
164
+ assert verdict.outcome is Outcome.FINDINGS
165
+ findings = [f for f in verdict.findings if f.rule == "DIR-004"]
166
+ assert len(findings) == 1, tuple(f.render() for f in verdict.findings)
167
+ assert findings[0].path == "docs/guide"
168
+ assert findings[0].message == "5 pages (max 3), no ratchet entry"
169
+
170
+
171
+ def test_a_baseline_entry_exempts_its_directory_up_to_the_recorded_number() -> None:
172
+ """The exemption channel itself: three pages against a declared cap of 2
173
+ are CLEAN because the register records `docs/guide: 3` — the entry
174
+ forgives the directory up to the count it was recorded at (growing past
175
+ it is DIR-005, reported separately)."""
176
+ verdict = run("docs-size-exempt", cap="2")
177
+ assert verdict.outcome is Outcome.CLEAN, verdict.render()
178
+ assert not verdict.findings
179
+
180
+
181
+ # ------------------------------------------------------------------ DIR-004
182
+
183
+
184
+ def test_an_over_cap_directory_with_no_entry_is_dir_004() -> None:
185
+ verdict = run("docs-size-over-cap", cap="2")
186
+ assert verdict.outcome is Outcome.FINDINGS
187
+ findings = [f for f in verdict.findings if f.rule == "DIR-004"]
188
+ assert len(findings) == 1, tuple(f.render() for f in verdict.findings)
189
+ finding = findings[0]
190
+ assert finding.path == "docs/guide"
191
+ assert finding.message == "3 pages (max 2), no ratchet entry"
192
+ assert finding.fix == (
193
+ "add a docs_pages baseline entry for docs/guide to the ratchet "
194
+ "register, or split the directory"
195
+ )
196
+ assert "docs/short" not in {f.path for f in verdict.findings}
197
+
198
+
199
+ def test_the_register_is_not_a_grant_catalogue() -> None:
200
+ """`structural_grant = True` forces the allowance to 0 regardless of the
201
+ row's `grants:` field — the same arithmetic LAY-004 pins: the register is
202
+ spent BEFORE counting, never subtracted after."""
203
+ verdict = run("docs-size-over-cap", cap="2")
204
+ assert verdict.granted == 0
205
+ assert verdict.outcome is Outcome.FINDINGS
206
+
207
+
208
+ # ------------------------------------------------------------------ DIR-005
209
+
210
+
211
+ def test_a_directory_that_grew_past_its_baseline_is_dir_005() -> None:
212
+ verdict = run("docs-size-grew", cap="2")
213
+ assert verdict.outcome is Outcome.FINDINGS
214
+ findings = [f for f in verdict.findings if f.rule == "DIR-005"]
215
+ assert len(findings) == 1, tuple(f.render() for f in verdict.findings)
216
+ finding = findings[0]
217
+ assert finding.path == "docs/guide"
218
+ assert finding.message == "3 pages (baseline 2)"
219
+ assert finding.fix == (
220
+ "reduce docs/guide back to at most 2 pages, or raise its "
221
+ "docs_pages baseline entry"
222
+ )
223
+ # The entry exists, so DIR-004 must NOT fire alongside DIR-005.
224
+ assert not [f for f in verdict.findings if f.rule == "DIR-004"]
225
+
226
+
227
+ # ------------------------------------------------------------------ DIR-006
228
+
229
+
230
+ def test_a_baseline_entry_shrunk_under_the_cap_is_dir_006() -> None:
231
+ verdict = run("docs-size-stale", cap="3")
232
+ stale = [f for f in verdict.findings if f.rule == "DIR-006"]
233
+ assert len(stale) == 2, tuple(f.render() for f in verdict.findings)
234
+ by_path = {f.path: f for f in stale}
235
+ shrunk = by_path["docs/shrunk"]
236
+ assert shrunk.message == "ratchet entry is no longer required"
237
+ assert shrunk.fix == (
238
+ "delete docs/shrunk's stale docs_pages baseline entry from the "
239
+ "ratchet register"
240
+ )
241
+ # The directory-gone case: the entry names a directory the tree no
242
+ # longer holds. Grading ABSENCE, exactly like LAY-004's DIR-003.
243
+ assert "docs/gone" in by_path
244
+
245
+
246
+ def test_a_still_valid_baseline_entry_is_not_reported() -> None:
247
+ """`docs/held` holds 4 pages over the declared cap of 3 with a recorded
248
+ baseline of 4 — over the cap but AT its recorded number, so neither
249
+ DIR-004 (the entry exists) nor DIR-005 (it did not grow) fires, and
250
+ DIR-006 must not either (4 is still over the cap, so the entry is
251
+ load-bearing)."""
252
+ verdict = run("docs-size-stale", cap="3")
253
+ assert "docs/held" not in {f.path for f in verdict.findings}
254
+
255
+
256
+ # ------------------------------------------------------- unusable registers
257
+
258
+
259
+ def test_a_register_that_exists_but_is_unusable_is_could_not_run() -> None:
260
+ """The kept exception through both ruling shapes: a register that exists
261
+ but cannot be parsed may be mid-edit; grading it as "no exemptions" would
262
+ silently drop every baseline it names. Could-not-run either way."""
263
+ verdict = run("docs-size-unusable", cap="2")
264
+ assert verdict.outcome is Outcome.COULD_NOT_RUN
265
+ assert "unusable" in verdict.message
266
+
267
+
268
+ def test_a_register_that_exists_but_is_not_a_file_is_could_not_run() -> None:
269
+ directory = FIXTURES / "docs-size-over-cap"
270
+ verdict = runner.run(
271
+ ROW, directory, corpus_files(directory),
272
+ inputs={ROW.id: {"register": directory}},
273
+ declarations={DocsDirectorySize.PAGES_DECLARATION: "2"},
274
+ )
275
+ assert verdict.outcome is Outcome.COULD_NOT_RUN
276
+ assert "exists but is not a file" in verdict.message
277
+
278
+
279
+ # --------------------------------------------------------- the rule's filters
280
+
281
+
282
+ def test_decisions_is_exempt_at_any_depth() -> None:
283
+ """The flat ADR log is exempt by rule (owner ruling 2026-10-02, flat by
284
+ design per the docs convention), at the log root and below it. `guide` is
285
+ over the same cap and MUST be reported — without it the corpus would be
286
+ silent by accident."""
287
+ verdict = run("docs-size-decisions", cap="2")
288
+ assert verdict.outcome is Outcome.FINDINGS
289
+ assert {f.path for f in verdict.findings} == {"docs/guide"}
290
+ assert all(f.rule == "DIR-004" for f in verdict.findings)
291
+
292
+
293
+ def test_assets_dotfiles_and_outside_paths_are_not_counted() -> None:
294
+ """Through the runner: `guide` holds 2 visible pages beside 3 non-`.md`
295
+ assets; counting assets would report it (5 over the declared cap of 3).
296
+ `plans/` and the root `docs.md` never reach the check — the row's scope
297
+ is the runner's business — but they are in the given set, so a check that
298
+ counted them would say so here."""
299
+ verdict = run("docs-size-assets", cap="3")
300
+ assert verdict.outcome is Outcome.CLEAN, verdict.render()
301
+ assert not verdict.findings
302
+
303
+
304
+ def test_the_check_itself_skips_assets_dotfiles_and_outside_paths() -> None:
305
+ """Read directly, with the dot-prefixed files the runner may or may not
306
+ hand over: the counted set is `a.md`, `b.md`, `sub/nested.md` — the
307
+ dot-prefixed pages, the assets, the `plans/` tree and the root `docs.md`
308
+ lookalike are all invisible to the rule. Counting any of them would push
309
+ `guide` over the declared cap of 3 and report it."""
310
+ directory = FIXTURES / "docs-size-assets"
311
+ findings = findings_for(directory, corpus_files(directory), cap="3")
312
+ assert findings == (), tuple(f.render() for f in findings)
313
+
314
+
315
+ def test_a_deleted_file_is_not_counted() -> None:
316
+ """The `is_file` liveness check, LAY-004's discipline: a set may name a
317
+ file this commit deleted. Handed the two live paths and one ghost under a
318
+ declared cap of 2, the count stays at the cap — no finding."""
319
+ directory = FIXTURES / "docs-size-over-cap"
320
+ given = (
321
+ "docs/guide/a.md",
322
+ "docs/guide/b.md",
323
+ "docs/guide/ghost.md", # in the set, deleted from disk
324
+ "docs/short/only.md",
325
+ )
326
+ findings = findings_for(directory, given, cap="2")
327
+ assert findings == (), tuple(f.render() for f in findings)
328
+
329
+
330
+ def test_an_empty_set_grades_nothing() -> None:
331
+ findings = findings_for(FIXTURES / "docs-size-over-cap", (), cap="2")
332
+ assert findings == ()
333
+
334
+
335
+ # --------------------------------------------------------------- the wiring
336
+
337
+
338
+ def test_the_register_is_not_packaged_data() -> None:
339
+ """The regression guard, same shape as LAY-004's: re-adding a `resources`
340
+ entry here would ship one repository's exemption register to every other
341
+ repository (the K-42 defect)."""
342
+ assert "register" in DocsDirectorySize.inputs
343
+ assert "register" not in DocsDirectorySize.resources
344
+ assert DocsDirectorySize.inputs["register"].startswith("<")
345
+ assert "docs_root" in DocsDirectorySize.inputs
@@ -190,15 +190,28 @@ class DocsEnglishOnly(Check):
190
190
 
191
191
  # ------------------------------------------------------ subject-matter scope
192
192
 
193
- def _i18n_owner(self, ctx: Context) -> str:
194
- return f"{ctx.relative(ctx.inputs['frontend_src'])}/mobile/src/i18n"
193
+ def _i18n_owner(self, ctx: Context) -> str | None:
194
+ # Decision for a tree declared absent (`frontend = ""`, which drops
195
+ # `frontend_src` from the resolved inputs): this input only contributes
196
+ # the i18n OWNER exemption, an extra path that excuses translated strings
197
+ # beneath it. A repository with no frontend tree has no such directory, so
198
+ # there is no exemption to grant -- Han text anywhere is graded by the rule
199
+ # as written, and the generic locale-directory exemption below still
200
+ # applies. This read `ctx.inputs['frontend_src']` unguarded -- a `KeyError`
201
+ # traceback (exit 1), not a verdict, on any file.
202
+ frontend_src = ctx.inputs.get("frontend_src")
203
+ if frontend_src is None:
204
+ return None
205
+ return f"{ctx.relative(frontend_src)}/mobile/src/i18n"
195
206
 
196
- def _excluded(self, relative: str, i18n_owner: str) -> bool:
207
+ def _excluded(self, relative: str, i18n_owner: str | None) -> bool:
197
208
  # 1. Hidden paths, by decision. Every component is tested.
198
209
  if any(part.startswith(".") for part in relative.split("/")):
199
210
  return True
200
- # 2. The one named i18n owner exemption.
201
- if relative == i18n_owner or relative.startswith(i18n_owner + "/"):
211
+ # 2. The one named i18n owner exemption (absent when the frontend tree is).
212
+ if i18n_owner is not None and (
213
+ relative == i18n_owner or relative.startswith(i18n_owner + "/")
214
+ ):
202
215
  return True
203
216
  # 3. Any locale-named data directory, anywhere.
204
217
  if self.LOCALE_DIR.search(relative):
@@ -191,3 +191,38 @@ def test_an_unreadable_office_archive_is_reported_not_silently_skipped(
191
191
  verdict = graded_directly(tmp_path, ("broken.docx",))
192
192
  assert verdict.outcome is Outcome.FINDINGS, verdict.render()
193
193
  assert verdict.findings[0].message == "unreadable Office archive on this line"
194
+
195
+
196
+ def test_a_frontend_tree_declared_absent_leaves_only_the_locale_directory_exemption(
197
+ tmp_path: pathlib.Path,
198
+ ) -> None:
199
+ """`frontend = ""` drops `frontend_src` from the resolved inputs. It only
200
+ contributes the i18n OWNER exemption (an extra path), so absent that
201
+ exemption does not exist -- but the generic locale-directory exemption
202
+ still does, and Han text elsewhere is still reported. This was a
203
+ `KeyError: 'frontend_src'` traceback (exit 1) on any file, measured against
204
+ enact-combined with `--tree rust= --tree frontend=`."""
205
+ (tmp_path / "src").mkdir()
206
+ (tmp_path / "src" / "page.ts").write_text("const label = '一'\n", encoding="utf-8")
207
+ (tmp_path / "web" / "mobile" / "src" / "i18n").mkdir(parents=True)
208
+ (tmp_path / "web" / "mobile" / "src" / "i18n" / "strings.ts").write_text(
209
+ "export const label = '一'\n", encoding="utf-8"
210
+ )
211
+ (tmp_path / "web" / "i18n" / "locales" / "zh-CN").mkdir(parents=True)
212
+ (tmp_path / "web" / "i18n" / "locales" / "zh-CN" / "strings.ts").write_text(
213
+ "export const label = '一'\n", encoding="utf-8"
214
+ )
215
+ ctx = Context(root=tmp_path, row=ROW)
216
+ findings = tuple(
217
+ DocsEnglishOnly().graded(
218
+ ctx,
219
+ FileSet(
220
+ paths=(
221
+ "src/page.ts",
222
+ "web/mobile/src/i18n/strings.ts",
223
+ "web/i18n/locales/zh-CN/strings.ts",
224
+ )
225
+ ),
226
+ )
227
+ )
228
+ assert sorted(f.path for f in findings) == ["src/page.ts", "web/mobile/src/i18n/strings.ts"], findings
@@ -0,0 +1,7 @@
1
+ """DOC-011 — `docs.filename`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .check import DocsFilename
6
+
7
+ __all__ = ["DocsFilename"]
@@ -0,0 +1,91 @@
1
+ """DOC-011 -- every Markdown page under `docs/` has a lower-kebab stem.
2
+
3
+ The rule this gate grades, stated in full:
4
+
5
+ For every `.md` file in scope, the filename stem must full-match
6
+ `^[a-z0-9]+(-[a-z0-9]+)*$` -- lower-kebab: runs of `[a-z0-9]` joined by
7
+ single hyphens, nothing else. Exact-name exemptions: `AGENTS.md` and
8
+ `CLAUDE.md`, the standing tree-guide names the other docs gates exclude
9
+ from grading too (DOC-006's scope carries them; `layout.agents-layout`
10
+ governs them). Non-`.md` files are NOT graded: docs trees legitimately
11
+ hold `.png`/`.html`/`.csv`/`.yaml` assets, and the rule is about page
12
+ names, not asset names -- a `UI-CONTRACT.png` beside a fixed
13
+ `UI-CONTRACT.md` is not a finding.
14
+
15
+ The row's scope mirrors DOC-006 (`docs.okf-profile`) exactly -- `<docs>/**`
16
+ with the same `proposals/`, `compliance/`, guide, and `index.md` excludes --
17
+ so both docs/ content gates walk the same tree (see the registry row's
18
+ comment for the carried-over rationale).
19
+
20
+ SCOPE OF THE RULE, DECIDED 2026-10-02 (orchestrator correction): an earlier
21
+ shape of this gate enforced ADR naming (`adr-<NNN>-<slug>.md`) over
22
+ `<docs>/decisions/**`; that reading invented a rule the brief did not carry.
23
+ This gate is the kebab-stem rule above; ADR naming may become its own gate
24
+ later and is deliberately not graded here -- every well-formed ADR name
25
+ (`adr-001-organization-workspace-tenancy.md`) is kebab-conforming anyway,
26
+ so the two rules only diverge on legacy prose names, which this gate reports
27
+ by the kebab rule wherever they sit under docs/.
28
+
29
+ There is no `structural` half, the same call DOC-010 (`docs.closed-set`)
30
+ makes: every rule here is answered from the GIVEN SET, and a repository
31
+ whose docs/ tree holds no `.md` page is permitted, not failing.
32
+
33
+ The check-level filters, in order: the path must sit under the resolved
34
+ `docs_root` (a lookalike `docs.md` beside the tree is outside it), the file
35
+ must be `.md`, and the basename must not be one of the two exempt guide
36
+ names. The row's scope excludes (`proposals/`, `compliance/`, guides,
37
+ `index.md`) are the runner's narrowing; the guide-name exemption is repeated
38
+ here because it is a property of the RULE (a name may not be lower-kebab and
39
+ still be correct), not of one scope.
40
+
41
+ Like DOC-010, `docs_root` is read straight from `ctx.inputs` -- a missing
42
+ docs tree is a misconfiguration this gate has no lenient mode for, and
43
+ adl-azdo declares `docs = "docs"`.
44
+ """
45
+
46
+ from __future__ import annotations
47
+
48
+ import re
49
+ from collections.abc import Iterable
50
+ from typing import ClassVar
51
+
52
+ from ....core.check import Check
53
+ from ....core.context import Context, FileSet
54
+ from ....core.finding import Report
55
+
56
+
57
+ class DocsFilename(Check):
58
+ id: ClassVar[str] = "DOC-011"
59
+ rule: ClassVar[str] = "DOC-011"
60
+
61
+ inputs: ClassVar[dict[str, str]] = {"docs_root": "<docs>"}
62
+ """The same named input DOC-001 and DOC-010 declare for the same reason
63
+ (K-42: a hardcoded `"docs"` literal silently drops every page when a
64
+ repository names its documentation tree something else)."""
65
+
66
+ KEBAB: ClassVar[re.Pattern[str]] = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
67
+ """The stem grammar, anchored by `fullmatch`: one or more runs of
68
+ `[a-z0-9]`, single-hyphen joined. Uppercase, underscores, spaces,
69
+ leading/trailing/double hyphens all fail the full match."""
70
+
71
+ GUIDE_NAMES: ClassVar[frozenset[str]] = frozenset({"AGENTS.md", "CLAUDE.md"})
72
+ """The standing tree-guide names, exempt by exact basename at any depth
73
+ -- the rule-level pair to the scope excludes the row carries."""
74
+
75
+ def graded(self, ctx: Context, files: FileSet) -> Iterable[Report]:
76
+ docs_root = ctx.relative(ctx.inputs["docs_root"])
77
+ prefix = f"{docs_root}/"
78
+ for path in files:
79
+ if not path.startswith(prefix):
80
+ continue
81
+ name = path.rsplit("/", 1)[-1]
82
+ if not name.endswith(".md") or name in self.GUIDE_NAMES:
83
+ continue
84
+ stem = name[: -len(".md")]
85
+ if not self.KEBAB.fullmatch(stem):
86
+ yield self.report(
87
+ path,
88
+ f"{stem} is not lower-kebab ([a-z0-9]+(-[a-z0-9]+)*)",
89
+ f"rename {path} so the stem is lower-kebab -- words of "
90
+ "[a-z0-9] joined by single hyphens",
91
+ )
@@ -0,0 +1,3 @@
1
+ # AGENTS fixture
2
+
3
+ Standing tree guide name; the gate exempts it by exact name.
@@ -0,0 +1,3 @@
1
+ # CLAUDE fixture
2
+
3
+ Standing tree guide name; the gate exempts it by exact name.
@@ -0,0 +1,3 @@
1
+ # Leading hyphen
2
+
3
+ Stem begins with a hyphen; the first run must be [a-z0-9]+.
@@ -0,0 +1,3 @@
1
+ # Architecture guide
2
+
3
+ Nested tree guide; the gate exempts the name at any depth.
@@ -0,0 +1,3 @@
1
+ # Double hyphen
2
+
3
+ A double hyphen leaves an empty segment between the dashes.
@@ -0,0 +1,3 @@
1
+ # Trailing hyphen
2
+
3
+ Stem ends with a hyphen; the last run must be [a-z0-9]+.
@@ -0,0 +1,4 @@
1
+ # UI contract
2
+
3
+ Uppercase stem -- the real violation shape from the Enact consumer tree
4
+ (docs/architecture/frontend/UI-CONTRACT.md).
@@ -0,0 +1,2 @@
1
+ PNG asset with a violating STEM -- still not graded, because the rule
2
+ grades Markdown pages only and this file is not .md.
@@ -0,0 +1 @@
1
+ PNG asset placeholder; non-Markdown files are not graded.
@@ -0,0 +1,4 @@
1
+ # Index
2
+
3
+ Standing catalog name; excluded from the row scope, and its stem is
4
+ conforming anyway.