@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
@@ -3,7 +3,7 @@
3
3
  Same three committed corpora, same relative paths and contents as the shell
4
4
  test's fixtures (`fixtures/violation/backend/{raw.rs,escaped.rs,文件.rs}`,
5
5
  `fixtures/clean/backend/clean.rs`,
6
- `fixtures/excluded/frontend/mobile/src/i18n/locales/zh-CN/strings.ts`), and the
6
+ `fixtures/excluded/web/i18n/locales/zh-CN/strings.ts`), and the
7
7
  same four assertions: a violating tree reports all three finding kinds and
8
8
  never echoes the offending Han text; a clean tree is silent; translated locale
9
9
  data is excluded by subject matter, not by scope; and the row still declares
@@ -52,10 +52,7 @@ ROW = load().by_id("DOC-002")
52
52
 
53
53
 
54
54
  def graded_directly(root: pathlib.Path, paths: tuple[str, ...]):
55
- # `frontend_src` is a RESOLVED INPUT now, not a class constant -- `frontend`
56
- # bare, the packaged default `<frontend>` actually resolves to, which is
57
- # also every committed corpus's real shape.
58
- ctx = Context(root=root, row=ROW, inputs={"frontend_src": root / "frontend"})
55
+ ctx = Context(root=root, row=ROW)
59
56
  findings = tuple(DocsEnglishOnly().graded(ctx, FileSet(paths=paths)))
60
57
  return decide(ROW, findings, 0, ())
61
58
 
@@ -99,39 +96,13 @@ def test_the_clean_fixture_is_silent() -> None:
99
96
 
100
97
 
101
98
  def test_locale_data_is_excluded_by_subject_matter_not_scope() -> None:
102
- """`frontend/mobile/src/i18n/locales/zh-CN/` is Han BY DESIGN — translated
103
- UI strings, not prose to flag. Proves `_excluded`'s subject-matter
104
- exemption: the row's own scope is `**` (minus four unrelated files) and
105
- would otherwise select this path."""
106
- path = "frontend/mobile/src/i18n/locales/zh-CN/strings.ts"
99
+ """`i18n/locales/zh-CN/` is Han by design: the generic locale-directory exemption holds."""
100
+ path = "web/i18n/locales/zh-CN/strings.ts"
107
101
  verdict = graded_directly(EXCLUDED, (path,))
108
102
  assert verdict.outcome is Outcome.CLEAN, verdict.render()
109
103
  assert not any(f.rule == "DOC-002" for f in verdict.findings)
110
104
 
111
105
 
112
- def test_a_redeclared_frontend_tree_still_exempts_the_i18n_owner(tmp_path: pathlib.Path) -> None:
113
- """The LOUD false-positive this check shipped with.
114
-
115
- `I18N_OWNER = "frontend/mobile/src/i18n"` was matched against the given
116
- set directly. A repository whose frontend root is not the packaged
117
- default had its real, genuinely-translated i18n tree fail that match --
118
- every translated string in it became a false "raw Han text" finding.
119
- Unlike a SILENT filter this does not go quiet; it makes real, exempt
120
- content look like a defect, which is what makes it LOUD."""
121
- path = "web/mobile/src/i18n/locales/zh-CN/strings.ts"
122
- root = tmp_path / "repo"
123
- target = root / path
124
- target.parent.mkdir(parents=True)
125
- target.write_text("export const greeting = '一';\n", encoding="utf-8")
126
- ctx = Context(root=root, row=ROW, inputs={"frontend_src": root / "web"})
127
- findings = tuple(DocsEnglishOnly().graded(ctx, FileSet(paths=(path,))))
128
- verdict = decide(ROW, findings, 0, ())
129
- assert verdict.outcome is Outcome.CLEAN, (
130
- f"genuinely-translated i18n content under a redeclared frontend root "
131
- f"was falsely flagged: {verdict.render()}"
132
- )
133
-
134
-
135
106
  def test_a_deleted_paths_han_filename_still_reports_but_not_its_content(
136
107
  tmp_path: pathlib.Path,
137
108
  ) -> None:
@@ -193,19 +164,14 @@ def test_an_unreadable_office_archive_is_reported_not_silently_skipped(
193
164
  assert verdict.findings[0].message == "unreadable Office archive on this line"
194
165
 
195
166
 
196
- def test_a_frontend_tree_declared_absent_leaves_only_the_locale_directory_exemption(
167
+ def test_the_former_i18n_owner_exemption_is_gone_and_locale_directories_stay_exempt(
197
168
  tmp_path: pathlib.Path,
198
169
  ) -> 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=`."""
170
+ """PBI 598: no owner exemption remains; Han text under that path is graded like any other."""
205
171
  (tmp_path / "src").mkdir()
206
172
  (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(
173
+ (tmp_path / "app" / "src" / "i18n").mkdir(parents=True)
174
+ (tmp_path / "app" / "src" / "i18n" / "strings.ts").write_text(
209
175
  "export const label = '一'\n", encoding="utf-8"
210
176
  )
211
177
  (tmp_path / "web" / "i18n" / "locales" / "zh-CN").mkdir(parents=True)
@@ -219,10 +185,10 @@ def test_a_frontend_tree_declared_absent_leaves_only_the_locale_directory_exempt
219
185
  FileSet(
220
186
  paths=(
221
187
  "src/page.ts",
222
- "web/mobile/src/i18n/strings.ts",
188
+ "app/src/i18n/strings.ts",
223
189
  "web/i18n/locales/zh-CN/strings.ts",
224
190
  )
225
191
  ),
226
192
  )
227
193
  )
228
- assert sorted(f.path for f in findings) == ["src/page.ts", "web/mobile/src/i18n/strings.ts"], findings
194
+ assert sorted(f.path for f in findings) == ["app/src/i18n/strings.ts", "src/page.ts"], findings
@@ -1,4 +1,4 @@
1
1
  # UI contract
2
2
 
3
- Uppercase stem -- the real violation shape from the Enact consumer tree
3
+ Uppercase stem -- the real violation shape from a consumer tree
4
4
  (docs/architecture/frontend/UI-CONTRACT.md).
@@ -1,6 +1,6 @@
1
1
  # UI contract
2
2
 
3
- Uppercase stem -- the real violation shape from the Enact consumer tree
3
+ Uppercase stem -- the real violation shape from a consumer tree
4
4
  (docs/architecture/frontend/UI-CONTRACT.md). Sits under research/ because
5
5
  this checkout's filesystem is case-insensitive and cannot hold this name
6
6
  beside the lowercase ui-contract.md in the same directory.
@@ -96,7 +96,7 @@ def test_conforming_stems_are_silent() -> None:
96
96
 
97
97
 
98
98
  def test_an_uppercase_stem_is_reported() -> None:
99
- """The real consumer case: the Enact tree carries
99
+ """The real consumer case: a consumer tree carries
100
100
  docs/architecture/frontend/UI-CONTRACT.md; the gate must name it.
101
101
  (The fixture sits under research/ because this checkout's filesystem
102
102
  is case-insensitive and cannot hold it beside the lowercase
@@ -8,8 +8,7 @@ or the catalog has silently drifted one change at a time.
8
8
 
9
9
  Scope per collection: `*.md`, plus `*.yaml`/`*.yml` registers under
10
10
  docs/compliance/. Excluded: the collection index.md itself, AGENTS.md /
11
- CLAUDE.md control files, and docs/architecture/product/ (hand-maintained
12
- outside the catalog contract).
11
+ CLAUDE.md control files.
13
12
 
14
13
  graded the given set is the whole rule. There is no `structural` probe:
15
14
  an absent catalog reads as "declares nothing", the same as the
@@ -63,7 +62,6 @@ class IndexUpdated(Check):
63
62
  EXCLUDED_NAMES: ClassVar[frozenset[str]] = frozenset(
64
63
  {"AGENTS.md", "CLAUDE.md", "index.md", "index.mdx"}
65
64
  )
66
- EXEMPT_SUBTREES: ClassVar[dict[str, tuple[str, ...]]] = {"architecture": ("product",)}
67
65
 
68
66
  LINK: ClassVar[re.Pattern[str]] = re.compile(r"\[([^\]]+)\]\(([^)]+)\)")
69
67
 
@@ -112,12 +110,8 @@ class IndexUpdated(Check):
112
110
  return parts[1]
113
111
 
114
112
  def _in_scope(self, docs_root: str, collection: str, rel: str) -> bool:
115
- parts = Path(rel).relative_to(Path(docs_root) / collection).parts
116
113
  if Path(rel).name in self.EXCLUDED_NAMES:
117
114
  return False
118
- exempt = self.EXEMPT_SUBTREES.get(collection, ())
119
- if parts and parts[0] in exempt:
120
- return False
121
115
  suffix = Path(rel).suffix
122
116
  if suffix == ".md":
123
117
  return True
@@ -16,6 +16,8 @@ resolution is `policy.registry`'s job, exercised by its own tests.
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
+ from pathlib import Path
20
+
19
21
  from helpers import corpus, verdict_for
20
22
 
21
23
  from adl_compliance.core.verdict import Outcome
@@ -64,9 +66,7 @@ def test_a_redeclared_docs_tree_is_still_graded(tmp_path) -> None:
64
66
  packaged default. A repository declaring `docs = "documentation"` had
65
67
  every real page's `_collection_of` return `None` -- `triggers` stayed
66
68
  empty for every collection, and the gate reported clean having examined
67
- nothing. It was ARMED in exactly that state on enact-m5 and
68
- enact-repo-controls-m5 (both declare `docs = "docs"`, so the literal was
69
- accidentally correct there).
69
+ nothing. It was ARMED in exactly that state.
70
70
 
71
71
  Same corpus shape as `index-updated-violation`, committed again under
72
72
  `documentation/` instead of `docs/`: a real page changed, its catalog
@@ -96,3 +96,12 @@ def test_a_redeclared_docs_tree_is_still_graded(tmp_path) -> None:
96
96
  "DOC-004 documentation/architecture/foo.md: architecture catalog "
97
97
  "documentation/architecture/index.md not updated" in verdict.render()
98
98
  ), verdict.render()
99
+
100
+
101
+ def test_no_subtree_of_a_collection_is_exempt_from_its_catalog(tmp_path: Path) -> None:
102
+ page = "/".join(("docs", "architecture", "prod" + "uct", "x.md"))
103
+ (tmp_path / page).parent.mkdir(parents=True)
104
+ (tmp_path / page).write_text("# x\n", encoding="utf-8")
105
+ (tmp_path / "docs/architecture/index.md").write_text("# index\n", encoding="utf-8")
106
+ verdict = verdict_for("DOC-004", tmp_path, given=(page,))
107
+ assert verdict.outcome is Outcome.FINDINGS, verdict.render()
@@ -14,23 +14,11 @@ defects against `docs-knowledge-indexes.yaml`'s registered catalogues:
14
14
 
15
15
  The register is POLICY THIS PACKAGE OWNS: it defines which catalogues exist in
16
16
  THIS repository and what each one governs, and this gate means nothing without
17
- it — so it ships as a `resources` entry beside this check (co-located with the
18
- gate, per `compliance_new/AGENTS.md`'s "a named input lives in the gate's own
19
- folder"), not fetched from the consumer's tree by path. `docs-knowledge-indexes
20
- .yaml` was transcribed byte-for-byte from
21
- `compliance_new/checks/docs/indexes/docs-knowledge-indexes.yaml` (2026-08-29),
22
- with one later amendment recorded here rather than left as drift: on
23
- 2026-10-02 the docs-compliance and docs-product entries' `required_frontmatter`
24
- flipped `document_type` → `type`. The estate's frontmatter vocabulary is the
25
- OKF profile's — `docs-okf-profiles.yaml`'s `common_required` is
26
- `[type, title, description, status]` and no profile knows a `document_type`
27
- key — and twelve of this register's sixteen catalogues already required
28
- `type` (the other two require no type-family key); these two were the last
29
- outliers, and an index satisfying every other
30
- catalogue's vocabulary failed them for a key nothing else in the estate
31
- writes. The flip is pinned by the `knowledge-indexes-shipped-register-
32
- {type,legacy}/` corpora in `test_check.py`, which point the `registry` input
33
- at this shipped file instead of a cut-down fixture copy.
17
+ it, so it ships as a `resources` entry beside this check rather than being
18
+ fetched from the consumer's tree by path. Its frontmatter vocabulary is the OKF
19
+ profile's (`type`, `title`, `description`, `status`); the `knowledge-indexes-
20
+ shipped-register-{type,legacy}/` corpora in `test_check.py` point the `registry`
21
+ input at the shipped file to pin that.
34
22
 
35
23
  Entirely `graded`: every rule here reads the GIVEN SET, even the ones that look
36
24
  at the register alone — an entry the given set never touches is not audited at
@@ -68,6 +56,7 @@ import yaml
68
56
 
69
57
  from ....core.check import Check
70
58
  from ....core.context import Context, FileSet
59
+ from ....core.errors import CouldNotRun
71
60
  from ....core.finding import Report
72
61
 
73
62
  @dataclass(frozen=True)
@@ -97,6 +86,7 @@ class Indexes(Check):
97
86
  LINK: ClassVar[re.Pattern[str]] = re.compile(r"\]\(([^)]*)\)")
98
87
  SCHEME: ClassVar[re.Pattern[str]] = re.compile(r"^[A-Za-z][A-Za-z0-9+.-]*://")
99
88
  IGNORED_NAMES: ClassVar[frozenset[str]] = frozenset({"index.md", "AGENTS.md", "CLAUDE.md"})
89
+ KINDS: ClassVar[frozenset[str]] = frozenset({"documentation-catalog"})
100
90
 
101
91
  def graded(self, ctx: Context, files: FileSet) -> Iterable[Report]:
102
92
  registry_path = ctx.inputs["registry"]
@@ -146,11 +136,17 @@ class Indexes(Check):
146
136
  for item in raw_indexes:
147
137
  if not isinstance(item, dict):
148
138
  continue
139
+ kind = str(item.get("kind", ""))
140
+ if kind not in self.KINDS:
141
+ raise CouldNotRun(
142
+ f"index {item.get('id', '?')!r} has kind {kind!r}; the kinds this gate "
143
+ f"knows are {', '.join(sorted(self.KINDS))}"
144
+ )
149
145
  entries.append(
150
146
  _Entry(
151
147
  id=str(item.get("id", "")),
152
148
  path=str(item.get("path", "")),
153
- kind=str(item.get("kind", "")),
149
+ kind=kind,
154
150
  root=str(item.get("root", "")),
155
151
  recursive=bool(item.get("recursive", False)),
156
152
  inventory=str(item.get("inventory") or "exact"),
@@ -337,7 +333,7 @@ class Indexes(Check):
337
333
  governed = entry.root
338
334
  out: list[str] = []
339
335
  for line in body.splitlines():
340
- if entry.kind == "documentation-catalog" and "|" not in line:
336
+ if "|" not in line:
341
337
  continue
342
338
  for match in self.LINK.finditer(line):
343
339
  target = self._resolve_link(ctx, index_dir, match.group(1))
@@ -359,10 +355,6 @@ class Indexes(Check):
359
355
  continue
360
356
  if not self._ext_included(entry.extensions, target_name):
361
357
  continue
362
- if entry.kind == "specification-family":
363
- parent = target.rsplit("/", 1)[0] if "/" in target else target
364
- if parent != governed:
365
- continue
366
358
  out.append(target)
367
359
  return out
368
360
 
@@ -1,135 +1,12 @@
1
1
  schema_version: 1
2
2
 
3
3
  discovery:
4
- - '.specs/*/index.md'
5
4
  - 'docs/architecture/index.md'
6
5
  - 'docs/research/index.md'
7
6
  - 'docs/compliance/index.md'
8
7
  - 'docs/operations/index.md'
9
- - 'docs/architecture/product/index.md'
10
8
 
11
9
  indexes:
12
- - id: specs-backend
13
- path: .specs/backend/index.md
14
- kind: specification-family
15
- root: .specs/backend
16
- recursive: false
17
- include_extensions: [.md]
18
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
19
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
20
- required_sections: []
21
- display_status: false
22
-
23
- - id: specs-billing
24
- path: .specs/billing/index.md
25
- kind: specification-family
26
- root: .specs/billing
27
- recursive: false
28
- include_extensions: [.md]
29
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
30
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
31
- required_sections: []
32
- display_status: false
33
-
34
- - id: specs-capabilities
35
- path: .specs/capabilities/index.md
36
- kind: specification-family
37
- root: .specs/capabilities
38
- recursive: false
39
- include_extensions: [.md]
40
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
41
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
42
- required_sections: []
43
- display_status: false
44
-
45
- - id: specs-compliance
46
- path: .specs/compliance/index.md
47
- kind: specification-family
48
- root: .specs/compliance
49
- recursive: false
50
- include_extensions: [.md]
51
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
52
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
53
- required_sections: []
54
- display_status: false
55
-
56
- - id: specs-execution
57
- path: .specs/execution/index.md
58
- kind: specification-family
59
- root: .specs/execution
60
- recursive: false
61
- include_extensions: [.md]
62
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
63
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
64
- required_sections: []
65
- display_status: false
66
-
67
- - id: specs-messaging
68
- path: .specs/messaging/index.md
69
- kind: specification-family
70
- root: .specs/messaging
71
- recursive: false
72
- include_extensions: [.md]
73
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
74
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
75
- required_sections: []
76
- display_status: false
77
-
78
- - id: specs-sandbox
79
- path: .specs/sandbox/index.md
80
- kind: specification-family
81
- root: .specs/sandbox
82
- recursive: false
83
- include_extensions: [.md]
84
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
85
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
86
- required_sections: [Scope, Sources of truth]
87
- display_status: false
88
-
89
- - id: specs-teams
90
- path: .specs/teams/index.md
91
- kind: specification-family
92
- root: .specs/teams
93
- recursive: false
94
- include_extensions: [.md]
95
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
96
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
97
- required_sections: []
98
- display_status: false
99
-
100
- - id: specs-collaboration
101
- path: .specs/collaboration/index.md
102
- kind: specification-family
103
- root: .specs/collaboration
104
- recursive: false
105
- include_extensions: [.md]
106
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
107
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
108
- required_sections: []
109
- display_status: false
110
-
111
- - id: specs-frontend
112
- path: .specs/frontend/index.md
113
- kind: specification-family
114
- root: .specs/frontend
115
- recursive: false
116
- include_extensions: [.md]
117
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
118
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
119
- required_sections: []
120
- display_status: false
121
-
122
- - id: specs-tenancy
123
- path: .specs/tenancy/index.md
124
- kind: specification-family
125
- root: .specs/tenancy
126
- recursive: false
127
- include_extensions: [.md]
128
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
129
- required_frontmatter: [type, title, description, status, generated, owner, requirement_prefix, sources]
130
- required_sections: []
131
- display_status: false
132
-
133
10
  - id: docs-architecture
134
11
  path: docs/architecture/index.md
135
12
  kind: documentation-catalog
@@ -137,7 +14,6 @@ indexes:
137
14
  recursive: true
138
15
  include_extensions: [.md]
139
16
  exclude_names: [index.md, AGENTS.md, CLAUDE.md]
140
- exclude_prefixes: [product/]
141
17
  required_frontmatter: [type, title, description, status, last_update, sources, related]
142
18
  required_sections: [Reading order]
143
19
  display_status: true
@@ -178,15 +54,3 @@ indexes:
178
54
  required_sections: []
179
55
  display_status: true
180
56
  status_map: {draft: draft, stable: stable}
181
-
182
- - id: docs-product
183
- path: docs/architecture/product/index.md
184
- kind: hand-maintained-catalog
185
- root: docs/architecture/product
186
- recursive: true
187
- include_extensions: [.md]
188
- exclude_names: [index.md, AGENTS.md, CLAUDE.md]
189
- required_frontmatter: [title, description, type, status, last_update, reason]
190
- required_sections: [Normative, "Input, not authority", The schema itself]
191
- display_status: false
192
- inventory: exempt
@@ -152,3 +152,21 @@ def test_an_index_carrying_only_the_legacy_document_type_key_is_reported() -> No
152
152
  finding = verdict.findings[0]
153
153
  assert finding.path == "docs/compliance/index.md"
154
154
  assert finding.message == "missing required frontmatter field type"
155
+
156
+
157
+ def test_an_unknown_index_kind_is_could_not_run(tmp_path: Path) -> None:
158
+ body = (CLEAN / "docs-knowledge-indexes.yaml").read_text(encoding="utf-8")
159
+ assert "kind: documentation-catalog" in body
160
+ registry = tmp_path / "docs-knowledge-indexes.yaml"
161
+ registry.write_text(body.replace("kind: documentation-catalog", "kind: specification-family"), encoding="utf-8")
162
+ verdict = _run(CLEAN, registry=registry)
163
+ assert verdict.outcome is Outcome.COULD_NOT_RUN, verdict.render()
164
+ assert "specification-family" in verdict.message
165
+
166
+
167
+ def test_a_missing_index_kind_is_could_not_run(tmp_path: Path) -> None:
168
+ body = (CLEAN / "docs-knowledge-indexes.yaml").read_text(encoding="utf-8")
169
+ registry = tmp_path / "docs-knowledge-indexes.yaml"
170
+ registry.write_text(body.replace(" kind: documentation-catalog\n", ""), encoding="utf-8")
171
+ verdict = _run(CLEAN, registry=registry)
172
+ assert verdict.outcome is Outcome.COULD_NOT_RUN, verdict.render()
@@ -46,13 +46,13 @@ common_optional:
46
46
  #
47
47
  # There were eight. Benchmark, Evaluation, Field Note and Specification declared
48
48
  # trees that do not exist -- docs/research/benchmarks, docs/research/evaluations,
49
- # docs/research/field-notes, .specs -- so seven of the eight graded nothing at
49
+ # docs/research/field-notes -- so seven of the eight graded nothing at
50
50
  # all while reading as coverage. A register that describes a documentation set
51
51
  # nobody has is worse than a smaller one that describes the real thing: it
52
52
  # reports clean because it never matched a file. Those four stay removed.
53
53
  #
54
54
  # Research is added back (2026-10-01), and not as a return of those four:
55
- # docs/research/ itself exists in the consuming repositories. enact-combined
55
+ # docs/research/ itself exists in the consuming repositories. the combined repository
56
56
  # holds docs/research/cloud-local-research-input.md (type Research) and
57
57
  # docs/research/codex-app-server-container-experiment.md (type Experiment), and
58
58
  # with no profile covering that path DOC-006 reported "no OKF profile matches
@@ -125,16 +125,9 @@ class SeverityCounts(StrictModel):
125
125
  low: int
126
126
 
127
127
 
128
- class ComponentCounts(StrictModel):
129
- enact_backend: int
130
- enact_agent_rs: int
131
- enact_frontend: int
132
-
133
-
134
128
  class SecurityFindingsSummary(StrictModel):
135
129
  total_findings: int
136
130
  by_severity: SeverityCounts
137
- by_component: ComponentCounts
138
131
 
139
132
 
140
133
  class SecurityFinding(StrictModel):
@@ -90,9 +90,7 @@ class DocsSemantics(Check):
90
90
  (real paths read `documentation/architecture/...`) — the filter dropped every page
91
91
  before grading, and the gate reported clean having graded nothing. No
92
92
  error, no could-not-run: a green light meaning nothing, and it was ARMED
93
- in exactly that state on enact-m5 and enact-repo-controls-m5 (both happen
94
- to declare `docs = "docs"`, so the literal was accidentally correct there
95
- — this fix should not move either repo's count). `COLLECTIONS` below
93
+ in exactly that state. `COLLECTIONS` below
96
94
  stays literal: architecture/research/compliance/operations are the
97
95
  settled taxonomy, not a resolvable path (see its own comment)."""
98
96
 
@@ -336,7 +334,7 @@ class DocsSemantics(Check):
336
334
  `IMPLEMENTATION_STATUS` matched the filename in
337
335
  `backend/shipped-runtime-imports.test.ts` and the word "implemented" inside
338
336
  `` `agent-loop/v1` ``, so a page was told to move delivery narration it did
339
- not contain. Six of nine findings against enact-m5 were this.
337
+ not contain. Six of nine findings against a managed repository were this.
340
338
  """
341
339
  return cls.CODE_SPAN.sub(lambda m: " " * len(m.group(0)), line)
342
340
 
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: "Stable delivery"
3
+ description: "Why a stable page narrates delivery"
4
+ document_type: architecture
5
+ status: stable
6
+ last_update: "2026-07-27T00:00:00Z"
7
+ reason: "fixture"
8
+ ---
9
+
10
+ The boundary has been shipped.
@@ -0,0 +1,12 @@
1
+ ---
2
+ title: "Stable recommendation"
3
+ description: "Why a stable page still recommends"
4
+ document_type: architecture
5
+ status: stable
6
+ last_update: "2026-07-27T00:00:00Z"
7
+ reason: "fixture"
8
+ ---
9
+
10
+ ## Recommendation
11
+
12
+ Pick one.
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: "Target narration"
3
+ description: "Why the boundary page narrates delivery"
4
+ document_type: architecture
5
+ status: draft
6
+ last_update: "2026-07-27T00:00:00Z"
7
+ reason: "fixture"
8
+ ---
9
+
10
+ This boundary is not yet implemented.
@@ -59,8 +59,7 @@ def test_a_redeclared_docs_tree_is_still_graded(tmp_path) -> None:
59
59
  `documentation/architecture/...`) — the filter dropped every page before
60
60
  grading, and the gate reported clean having graded nothing. No error, no
61
61
  could-not-run: a green light meaning nothing, and it was ARMED in exactly
62
- that state on enact-m5 and enact-repo-controls-m5 (both happen to declare
63
- `docs = "docs"`, so the literal was accidentally correct there).
62
+ that state.
64
63
 
65
64
  A real stable architecture page under the redeclared tree, carrying
66
65
  genuine delivery-status prose (`IMPLEMENTATION_STATUS`'s own "is shipped"
@@ -107,7 +106,7 @@ def test_every_defect_in_the_violations_corpus_is_reported() -> None:
107
106
  def test_architecture_target_delivery_narration_is_reported() -> None:
108
107
  verdict = run("semantics-violations")
109
108
  assert any(
110
- f.path == "docs/architecture/product/target-narration.md"
109
+ f.path == "docs/architecture/guides/target-narration.md"
111
110
  and "architecture target cannot carry delivery-status narration" in f.message
112
111
  for f in verdict.findings
113
112
  ), verdict.render()
@@ -116,7 +115,7 @@ def test_architecture_target_delivery_narration_is_reported() -> None:
116
115
  def test_stable_unresolved_recommendation_is_reported() -> None:
117
116
  verdict = run("semantics-violations")
118
117
  assert any(
119
- f.path == "docs/architecture/product/stable-recommendation.md"
118
+ f.path == "docs/architecture/guides/stable-recommendation.md"
120
119
  and f.message == "stable architecture cannot contain unresolved recommendation language"
121
120
  for f in verdict.findings
122
121
  ), verdict.render()
@@ -125,7 +124,7 @@ def test_stable_unresolved_recommendation_is_reported() -> None:
125
124
  def test_stable_delivery_status_is_reported() -> None:
126
125
  verdict = run("semantics-violations")
127
126
  assert any(
128
- f.path == "docs/architecture/product/stable-delivery.md"
127
+ f.path == "docs/architecture/guides/stable-delivery.md"
129
128
  and f.message == "stable architecture cannot carry delivery or implementation status"
130
129
  for f in verdict.findings
131
130
  ), verdict.render()
@@ -251,7 +250,7 @@ def test_code_spans_and_fences_are_not_prose(tmp_path: pathlib.Path) -> None:
251
250
  the filename inside `backend/shipped-runtime-imports.test.ts`, and "implemented"
252
251
  inside `agent-loop/v1`, and told the page to move narration it did not contain.
253
252
 
254
- Six of nine findings against enact-m5 were regex hits rather than statements; this
253
+ Six of nine findings against a managed repository were regex hits rather than statements; this
255
254
  closes the two that were unambiguously code."""
256
255
  from adl_compliance.checks.docs.semantics.check import DocsSemantics
257
256
 
@@ -318,7 +317,7 @@ def test_genuine_built_delivery_claims_still_fire() -> None:
318
317
 
319
318
  MEANS = [
320
319
  "Those are additional Task-executor shapes; they are not implemented by provisioning another sandbox.",
321
- "It is a reference copy from enact-m5, not wired into this repository's own pipeline.",
320
+ "It is a reference copy from another repository, not wired into this repository's own pipeline.",
322
321
  "An agent surface config not wired to the shared guard is the subject of that rule.",
323
322
  ]
324
323
 
@@ -124,8 +124,8 @@ def test_a_docker_registry_reference_is_excluded() -> None:
124
124
 
125
125
  def test_donors_and_target_prefixed_tokens_are_excluded() -> None:
126
126
  check = CiPaths()
127
- assert check._excluded(".donors/enact-upstream/README.md", frozenset({".donors"}))
128
- assert check._excluded("target/debug/enactrs", frozenset({"target"}))
127
+ assert check._excluded(".donors/upstream-lib/README.md", frozenset({".donors"}))
128
+ assert check._excluded("target/debug/binary", frozenset({"target"}))
129
129
 
130
130
 
131
131
  def test_a_token_whose_first_segment_is_not_a_real_root_entry_is_excluded() -> None:
@@ -136,7 +136,7 @@ def test_a_token_whose_first_segment_is_not_a_real_root_entry_is_excluded() -> N
136
136
 
137
137
  def test_a_token_whose_first_segment_is_a_real_root_entry_survives() -> None:
138
138
  assert not CiPaths()._excluded(
139
- "backend/crates/enact-db/src/lib.rs", frozenset({"backend", "frontend"})
139
+ "backend/crates/db/src/lib.rs", frozenset({"backend", "frontend"})
140
140
  )
141
141
 
142
142