@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.10 → 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 (413) hide show
  1. package/bin/adl +2 -2
  2. package/bin/git +17 -18
  3. package/bin/git-ado +27 -26
  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/bun-compile-build.sh +1 -1
  9. package/bin/lib/fleet-host.sh +62 -0
  10. package/bin/lib/lean-ctx-observe-hooks.sh +9 -9
  11. package/bin/reconcile +693 -470
  12. package/bin/reconcile-host +121 -0
  13. package/bin/setup +219 -0
  14. package/bin/shell-path-policy +15 -5
  15. package/bin/sync-and-reconcile +79 -68
  16. package/bin/validate-manifest +46 -51
  17. package/package.json +2 -12
  18. package/packages/agent-hooks/README.md +14 -9
  19. package/packages/agent-hooks/assets/hooks.json +0 -20
  20. package/packages/agent-hooks/package.json +1 -2
  21. package/packages/agent-hooks/src/cli/args.ts +1 -1
  22. package/packages/agent-hooks/src/cli/doctor.ts +6 -58
  23. package/packages/agent-hooks/src/deploy/deploy.ts +12 -41
  24. package/packages/agent-hooks/src/deploy/doctor.ts +8 -11
  25. package/packages/agent-hooks/src/deploy/index.ts +14 -14
  26. package/packages/agent-hooks/src/deploy/json-merge.ts +7 -8
  27. package/packages/agent-hooks/src/deploy/legacy-lock.ts +2 -2
  28. package/packages/agent-hooks/src/deploy/list.ts +1 -1
  29. package/packages/agent-hooks/src/deploy/lock.ts +31 -12
  30. package/packages/agent-hooks/src/deploy/profile.ts +3 -4
  31. package/packages/agent-hooks/src/deploy/source.ts +1 -1
  32. package/packages/agent-hooks/src/gating/config.ts +10 -24
  33. package/packages/agent-hooks/src/gating/root.ts +3 -3
  34. package/packages/agent-hooks/src/hooks/briefing/handler.ts +2 -2
  35. package/packages/agent-hooks/src/hooks/briefing/index.ts +27 -287
  36. package/packages/agent-hooks/src/hooks/contract.ts +17 -17
  37. package/packages/agent-hooks/src/hooks/crg/background.ts +27 -0
  38. package/packages/agent-hooks/src/hooks/crg/index.ts +7 -6
  39. package/packages/agent-hooks/src/hooks/log.ts +8 -10
  40. package/packages/agent-hooks/src/hooks/user-prompt-submit/match.ts +1 -1
  41. package/packages/agent-hooks/src/hooks/user-prompt-submit/tokenizer.ts +1 -1
  42. package/packages/agent-hooks/src/hooks/user-prompt-submit/vector.ts +6 -5
  43. package/packages/agent-hooks/src/hosts/registry.ts +1 -1
  44. package/packages/agent-hooks/src/log/adl-dir.ts +44 -0
  45. package/packages/agent-hooks/src/log/writer.ts +16 -2
  46. package/packages/agent-hooks/src/main.ts +7 -41
  47. package/packages/agent-hooks/src/pre-tool-use/handler.ts +71 -82
  48. package/packages/agent-hooks/src/rules/engine.ts +13 -17
  49. package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +3 -3
  50. package/packages/agent-hooks/src/rules/impl/adl-git-credential-bypass.ts +12 -14
  51. package/packages/agent-hooks/src/rules/impl/adl-git-identity.ts +28 -28
  52. package/packages/agent-hooks/src/rules/impl/adl-git-push-policy.ts +8 -9
  53. package/packages/agent-hooks/src/rules/impl/adl-human-azdo-bypass.ts +56 -59
  54. package/packages/agent-hooks/src/rules/impl/adl-identity.ts +16 -23
  55. package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +19 -16
  56. package/packages/agent-hooks/src/rules/impl/adl-pr-complete-pending.ts +2 -2
  57. package/packages/agent-hooks/src/rules/impl/adl-pr-review-role.ts +9 -12
  58. package/packages/agent-hooks/src/rules/impl/adl-pr-role.ts +5 -7
  59. package/packages/agent-hooks/src/rules/impl/adl-run-required.ts +411 -0
  60. package/packages/agent-hooks/src/rules/impl/adl-token-minting-role.ts +4 -5
  61. package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +6 -7
  62. package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +77 -70
  63. package/packages/agent-hooks/src/rules/impl/deny-allowlist.ts +159 -0
  64. package/packages/agent-hooks/src/rules/impl/fs-trash.ts +2 -2
  65. package/packages/agent-hooks/src/rules/impl/git-helpers.ts +21 -23
  66. package/packages/agent-hooks/src/rules/impl/git-worktree.ts +14 -28
  67. package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +3 -3
  68. package/packages/agent-hooks/src/rules/impl/messages.ts +1 -1
  69. package/packages/agent-hooks/src/rules/impl/pr-completion.ts +1 -1
  70. package/packages/agent-hooks/src/rules/impl/tool-azure-devops.ts +35 -46
  71. package/packages/agent-hooks/src/rules/impl/tool-find.ts +34 -7
  72. package/packages/agent-hooks/src/rules/impl/tool-grep.ts +34 -12
  73. package/packages/agent-hooks/src/rules/impl/tool-native-search.ts +52 -9
  74. package/packages/agent-hooks/src/rules/ports.ts +5 -7
  75. package/packages/agent-hooks/src/rules/registry.ts +4 -6
  76. package/packages/agent-hooks/src/rules/shell/git-identity.ts +86 -101
  77. package/packages/agent-hooks/src/rules/shell/git-internals.ts +1 -1
  78. package/packages/agent-hooks/src/rules/shell/invocation.ts +24 -118
  79. package/packages/agent-hooks/src/rules/shell/piped-script.ts +1 -1
  80. package/packages/agent-hooks/src/rules/shell/prefixes.ts +1 -1
  81. package/packages/agent-hooks/src/rules/shell/protected-carrier-writes.ts +11 -10
  82. package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +26 -26
  83. package/packages/agent-hooks/src/rules/shell/substitution.ts +1 -1
  84. package/packages/agent-hooks/src/rules/shell/xargs.ts +2 -2
  85. package/packages/agent-hooks/src/rules/spec.ts +2 -2
  86. package/packages/agent-settings/README.md +287 -0
  87. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +18 -18
  88. package/packages/agent-settings/assets/agents/architect.toml +2 -0
  89. package/packages/agent-settings/assets/agents/code-reviewer.toml +2 -0
  90. package/packages/agent-settings/assets/agents/executor.toml +2 -0
  91. package/packages/agent-settings/assets/agents/explore.toml +2 -0
  92. package/packages/agent-settings/assets/agents/planner.toml +2 -0
  93. package/packages/agent-settings/assets/mcp.json +0 -4
  94. package/packages/agent-settings/deploy-profile.json +139 -0
  95. package/packages/agent-settings/deploy-profile.schema.json +147 -0
  96. package/packages/agent-settings/managed/README.md +29 -30
  97. package/packages/agent-settings/managed/codex-requirements.toml +9 -9
  98. package/packages/agent-settings/package.json +20 -0
  99. package/packages/agent-settings/src/deploy/agent-render.ts +2 -2
  100. package/packages/agent-settings/src/deploy/agent-toml.ts +1 -1
  101. package/packages/agent-settings/src/deploy/agents-deploy.ts +17 -3
  102. package/packages/agent-settings/src/deploy/doctor.ts +3 -83
  103. package/packages/agent-settings/src/deploy/index.ts +6 -33
  104. package/packages/agent-settings/src/deploy/legacy-lock.ts +6 -6
  105. package/packages/agent-settings/src/deploy/list.ts +5 -19
  106. package/packages/agent-settings/src/deploy/lock.ts +8 -18
  107. package/packages/agent-settings/src/deploy/mcp-deploy.ts +10 -5
  108. package/packages/agent-settings/src/deploy/mcp-merge.ts +22 -3
  109. package/packages/agent-settings/src/deploy/mcp-source.ts +1 -1
  110. package/packages/agent-settings/src/deploy/profile.ts +11 -35
  111. package/packages/agent-settings/src/deploy/retire-lean-ctx.ts +184 -0
  112. package/packages/agent-settings/src/deploy/settings.ts +17 -22
  113. package/packages/agent-settings/src/global-surface-sweep.ts +5 -17
  114. package/packages/agent-settings/src/kimi-hooks.ts +339 -192
  115. package/packages/agent-settings/src/opencode-hooks.ts +1 -1
  116. package/packages/agent-settings/tsconfig.json +23 -0
  117. package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +120 -0
  118. package/packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh +276 -0
  119. package/packages/agent-skills/assets/debug-issue/SKILL.md +8 -7
  120. package/packages/agent-skills/assets/explore-codebase/SKILL.md +13 -13
  121. package/packages/agent-skills/assets/refactor-safely/SKILL.md +12 -11
  122. package/packages/agent-skills/assets/review-changes/SKILL.md +7 -7
  123. package/packages/agent-skills/assets/testing/references/e2e.md +37 -0
  124. package/packages/agent-skills/assets/testing/references/integration.md +37 -0
  125. package/packages/agent-skills/assets/testing/references/unit.md +32 -0
  126. package/packages/agent-skills/dist/deploy.d.ts +2 -0
  127. package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
  128. package/packages/agent-skills/dist/deploy.js +27 -1
  129. package/packages/agent-skills/dist/deploy.js.map +1 -1
  130. package/packages/agent-skills/dist/doctor.d.ts +0 -2
  131. package/packages/agent-skills/dist/doctor.d.ts.map +1 -1
  132. package/packages/agent-skills/dist/doctor.js +1 -1
  133. package/packages/agent-skills/dist/doctor.js.map +1 -1
  134. package/packages/agent-skills/dist/fs-utils.d.ts +0 -1
  135. package/packages/agent-skills/dist/fs-utils.d.ts.map +1 -1
  136. package/packages/agent-skills/dist/fs-utils.js +1 -1
  137. package/packages/agent-skills/dist/fs-utils.js.map +1 -1
  138. package/packages/agent-skills/package.json +1 -1
  139. package/packages/agent-skills/src/deploy.ts +29 -1
  140. package/packages/agent-skills/src/doctor.ts +1 -1
  141. package/packages/agent-skills/src/fs-utils.ts +1 -1
  142. package/packages/azdo/README.md +10 -9
  143. package/packages/azdo/package.json +2 -5
  144. package/packages/azdo/src/azdo-pl.ts +2 -54
  145. package/packages/azdo/src/azdo-pr-review.ts +4 -4
  146. package/packages/azdo/src/azdo-repo-doctor.ts +101 -52
  147. package/packages/azdo/src/azdo-wit.ts +41 -2
  148. package/packages/azdo/src/lib/agent-identity.ts +12 -14
  149. package/packages/azdo/src/lib/agent-token.ts +2 -2
  150. package/packages/azdo/src/lib/client.ts +0 -5
  151. package/packages/azdo/src/lib/graph-identity.ts +4 -4
  152. package/packages/azdo/src/workflow-rules-matrix.csv +85 -0
  153. package/packages/azdo/src/workflow-rules.ts +128 -0
  154. package/packages/fleet/manifest.json +38 -33
  155. package/packages/fleet/manifest.schema.json +67 -65
  156. package/packages/fleet/src/default-agent-file.ts +3 -3
  157. package/packages/fleet/src/install-adl-package.ts +73 -15
  158. package/packages/fleet/src/install-embedding-model.ts +14 -14
  159. package/packages/fleet/src/managed-settings.ts +7 -7
  160. package/packages/fleet/src/repo-maintain.ts +439 -0
  161. package/packages/fleet/src/scratch-reap.ts +6 -6
  162. package/packages/fleet/src/vscode-settings.ts +5 -5
  163. package/packages/repo-controls/README.md +141 -43
  164. package/packages/repo-controls/assets/promote.ts +4 -4
  165. package/packages/repo-controls/assets/reconcile-guard.sh +10 -46
  166. package/packages/repo-controls/assets/version-bump.sh +31 -84
  167. package/packages/repo-controls/bunfig.toml +6 -7
  168. package/packages/repo-controls/package/adl-compliance/AGENTS.md +5 -6
  169. package/packages/repo-controls/package/adl-compliance/conftest.py +3 -1
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +4 -16
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +9 -2
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +1 -0
  173. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/test_check.py +11 -0
  174. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/__init__.py +7 -0
  175. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/check.py +266 -0
  176. 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
  177. 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
  178. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.hidden.md +1 -0
  179. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/a.md +1 -0
  180. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/b.md +1 -0
  181. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/data.csv +1 -0
  182. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/diagram.png +1 -0
  183. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/mock.png +1 -0
  184. 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
  185. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs.md +1 -0
  186. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/grants.yaml +4 -0
  187. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p1.md +1 -0
  188. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p2.md +1 -0
  189. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p3.md +1 -0
  190. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p4.md +1 -0
  191. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p5.md +1 -0
  192. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/build.md +1 -0
  193. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/overview.md +1 -0
  194. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/install.md +1 -0
  195. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/start.md +1 -0
  196. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/grants.yaml +4 -0
  197. 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
  198. 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
  199. 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
  200. 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
  201. 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
  202. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/a.md +1 -0
  203. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/b.md +1 -0
  204. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/c.md +1 -0
  205. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/grants.yaml +4 -0
  206. 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
  207. 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
  208. 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
  209. 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
  210. 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
  211. 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
  212. 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
  213. 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
  214. 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
  215. 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
  216. 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
  217. 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
  218. 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
  219. 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
  220. 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
  221. 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
  222. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/grants.yaml +4 -0
  223. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/a.md +1 -0
  224. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/b.md +1 -0
  225. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/c.md +1 -0
  226. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/grants.yaml +4 -0
  227. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/a.md +1 -0
  228. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/b.md +1 -0
  229. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/c.md +1 -0
  230. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/grants.yaml +4 -0
  231. 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
  232. 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
  233. 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
  234. 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
  235. 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
  236. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/grants.yaml +4 -0
  237. 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
  238. 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
  239. 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
  240. 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
  241. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/grants.yaml +4 -0
  242. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/a.md +1 -0
  243. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/b.md +1 -0
  244. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/c.md +1 -0
  245. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/d.md +1 -0
  246. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/a.md +1 -0
  247. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/b.md +1 -0
  248. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/grants.yaml +6 -0
  249. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/a.md +1 -0
  250. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/b.md +1 -0
  251. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/c.md +1 -0
  252. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/grants.yaml +3 -0
  253. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/test_check.py +345 -0
  254. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +18 -5
  255. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +35 -0
  256. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/__init__.py +7 -0
  257. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/check.py +91 -0
  258. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/AGENTS.md +3 -0
  259. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/CLAUDE.md +3 -0
  260. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/-leading-hyphen.md +3 -0
  261. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/AGENTS.md +3 -0
  262. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/double--hyphen.md +3 -0
  263. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/getting started.md +3 -0
  264. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/nested/page-here.md +3 -0
  265. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/trailing-hyphen-.md +3 -0
  266. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +4 -0
  267. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/user_profile.md +3 -0
  268. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/UI-CONTRACT.png +2 -0
  269. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/diagram.png +1 -0
  270. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/index.md +4 -0
  271. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/notes.md +3 -0
  272. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/reference.html +1 -0
  273. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/2026-10-experiment.md +3 -0
  274. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +6 -0
  275. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs.md +5 -0
  276. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +194 -0
  277. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +14 -2
  278. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +5 -5
  279. 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
  280. 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
  281. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +58 -6
  282. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/test_check.py +0 -1
  283. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/check.py +64 -17
  284. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +60 -9
  285. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +17 -0
  286. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +13 -0
  287. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs/architecture/demo.md +0 -1
  288. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs-okf-profiles.yaml +0 -1
  289. 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
  290. 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
  291. 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
  292. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs-okf-profiles.yaml +5 -0
  293. 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
  294. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs-okf-profiles.yaml +0 -1
  295. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/experiment-ok.md +15 -0
  296. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +15 -0
  297. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +16 -0
  298. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +11 -0
  299. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +11 -0
  300. 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
  301. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-sources.md +10 -0
  302. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +11 -0
  303. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +11 -0
  304. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +16 -0
  305. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +14 -0
  306. 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
  307. 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
  308. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/operations/runbook.md +26 -0
  309. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/test_check.py +228 -2
  310. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +5 -0
  311. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +11 -2
  312. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/__init__.py +5 -3
  313. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +160 -44
  314. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +0 -2
  315. 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
  316. 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
  317. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/grants.yaml +3 -0
  318. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +0 -2
  319. 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
  320. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +5 -0
  321. 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
  322. 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
  323. 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
  324. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +4 -0
  325. 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
  326. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +5 -0
  327. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +0 -2
  328. 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
  329. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/grants.yaml +3 -0
  330. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/register.py +127 -0
  331. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +265 -29
  332. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +75 -2
  333. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +194 -0
  334. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/check.py +56 -10
  335. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/test_check.py +256 -0
  336. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +40 -12
  337. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +108 -0
  338. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +1 -1
  339. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +2 -3
  340. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +23 -6
  341. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +76 -0
  342. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +58 -17
  343. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +161 -0
  344. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +35 -3
  345. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +108 -1
  346. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +281 -61
  347. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/absolute-path.yaml +3 -0
  348. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/backslash-path.yaml +3 -0
  349. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/baseline-is-a-list.yaml +2 -0
  350. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/boolean-size.yaml +3 -0
  351. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/dot-segment.yaml +3 -0
  352. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/duplicate-path.yaml +4 -0
  353. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-file.yaml +0 -0
  354. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-segment.yaml +3 -0
  355. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/float-size.yaml +3 -0
  356. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/negative-size.yaml +3 -0
  357. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-over-limit-baseline.yaml +1 -0
  358. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-schema-version.yaml +1 -0
  359. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/non-string-path.yaml +3 -0
  360. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/not-yaml.yaml +2 -0
  361. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/null-size.yaml +3 -0
  362. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/parent-segment.yaml +3 -0
  363. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/string-size.yaml +3 -0
  364. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/top-level-is-a-list.yaml +2 -0
  365. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/trailing-slash.yaml +3 -0
  366. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/unknown-key.yaml +3 -0
  367. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/wrong-schema-version.yaml +2 -0
  368. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/zero-size.yaml +3 -0
  369. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/valid.yaml +4 -0
  370. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +758 -45
  371. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/check.py +4 -3
  372. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/test_check.py +2 -2
  373. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +490 -0
  374. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +368 -55
  375. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +4 -2
  376. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +523 -0
  377. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +1179 -0
  378. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +122 -3
  379. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +33 -1
  380. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +0 -11
  381. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/context.py +16 -1
  382. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -2
  383. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/docs_shape.py +12 -1
  384. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +133 -147
  385. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +133 -147
  386. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +1 -1
  387. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +52 -10
  388. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +23 -4
  389. package/packages/repo-controls/package.json +2 -10
  390. package/packages/repo-controls/scripts/hooks/post-merge +13 -0
  391. package/packages/repo-controls/scripts/hooks/pre-push +3 -3
  392. package/packages/repo-controls/src/azdo-push.ts +64 -36
  393. package/packages/repo-controls/src/azure-devops-governance.ts +7 -9
  394. package/packages/repo-controls/src/azure-devops-remote.ts +1 -1
  395. package/packages/repo-controls/src/cli.ts +29 -8
  396. package/packages/repo-controls/src/compliance-config.ts +410 -2
  397. package/packages/repo-controls/src/config.ts +133 -60
  398. package/packages/repo-controls/src/constants.ts +13 -27
  399. package/packages/repo-controls/src/coverage-ratchet.ts +4 -4
  400. package/packages/repo-controls/src/doctor.ts +46 -19
  401. package/packages/repo-controls/src/gitignore-block.ts +5 -29
  402. package/packages/repo-controls/src/hook-log.ts +1 -2
  403. package/packages/repo-controls/src/hook.ts +201 -45
  404. package/packages/repo-controls/src/install.ts +14 -8
  405. package/packages/repo-controls/src/integrity.ts +3 -3
  406. package/packages/repo-controls/src/lfs.ts +111 -0
  407. package/packages/repo-controls/src/promote.ts +8 -8
  408. package/packages/repo-controls/src/runtime.ts +9 -1
  409. package/packages/repo-controls/src/workflows-drift.ts +4 -5
  410. package/packages/repo-controls/src/worktree-guard.ts +3 -3
  411. package/packages/fleet/jobs/git-maintain/src/main.ts +0 -848
  412. package/packages/fleet/systemd/adl-azure-agent-update.service +0 -13
  413. package/packages/fleet/systemd/adl-azure-agent-update.timer +0 -10
@@ -15,9 +15,10 @@ actually RUNNING `test.sh` (with a `compliance` symlink pointed at
15
15
  internally) confirms it cannot execute even once: `env: init: No such file or
16
16
  directory`, from `fixture_() { env ... "$@"; }` being called as `fixture_
17
17
  init -q`, i.e. `env -C "$FIXTURE" init -q` -- the missing word is the command
18
- name itself. This is a harder case than `checks/coverage/config_committed`'s:
19
- that gate's shell tool could still run and its broken behaviour was directly
20
- observed and ported; here nothing runs even once, so there is no executed
18
+ name itself. This is a harder case than `checks/coverage/config_committed`'s
19
+ (deleted 2026-10-01 with the `coverage` domain): that gate's shell tool could
20
+ still run and its broken behaviour was directly observed and ported; here
21
+ nothing runs even once, so there is no executed
21
22
  behaviour to carry forward, only the intent the surrounding text still states
22
23
  unambiguously.
23
24
 
@@ -11,8 +11,8 @@ for why the fixture is named `.gitignore` (not `.ignore`, which is what
11
11
  REP-005 is not yet listed in `checks/__init__.py` (a later step wires this
12
12
  whole wave in one shot), so dispatch-by-id would report `COULD_NOT_RUN` for
13
13
  every case regardless of this check's own behaviour -- the same reason
14
- `coverage/config_committed` and `layout/plans_layout`'s own test suites give
15
- for the identical choice.
14
+ `coverage/config_committed` (deleted 2026-10-01 with the `coverage` domain) and
15
+ `layout/plans_layout`'s own test suites give for the identical choice.
16
16
 
17
17
  Every scenario below hands `.gitignore` itself in the given set alongside the
18
18
  candidate paths, because this check reads `.gitignore` content from the GIVEN
@@ -0,0 +1,490 @@
1
+ """What a `Cargo.lock` must contain for the manifests beside it, judged without cargo.
2
+
3
+ REP-002's `LCK-002` used to be cargo's exit code alone: `cargo metadata --locked
4
+ --offline --no-deps`. With `--no-deps` cargo never resolves, so it never reads the
5
+ lockfile, and a lock that was `version = 4` alone, was not TOML, or said
6
+ `version = 99` passed (measured through the real check, 2026-10-01). This module
7
+ is the content half: deterministic Python (`tomllib`) that reads only the lock,
8
+ the workspace manifest, the member manifests and the path-dependency manifests the
9
+ manifest text names. It spawns nothing and never touches the registry cache or the
10
+ network, so a verdict cannot differ between a machine that has the crate sources
11
+ cached and one that does not. (Dropping `--no-deps` would make it differ: cargo
12
+ then loads every dependency's manifest, which fails offline when the sources are
13
+ not cached.)
14
+
15
+ What is judged, each finding naming the lock path and the exact thing missing:
16
+
17
+ * the lock is UTF-8 and TOML;
18
+ * its top-level `version` is 3 or 4, or absent (the legacy V1/V2 encodings).
19
+ cargo 0.99.0, `src/cargo/core/resolver/encode.rs`, `EncodableResolve::into_resolve`:
20
+ `Some(4)` and `Some(3)` are read, `Some(5)` only on nightly with
21
+ `-Znext-lockfile-bump`, any other number bails ("lock file version `n` was found,
22
+ but this version of Cargo does not understand this lock file"), and no `version`
23
+ key means V1, "so this could either be the V1 or V2 encoding". An explicit
24
+ `version = 1` or `2` is therefore NOT supported, whatever the encodings' names;
25
+ * `[[package]]` is a non-empty array of tables, each with a string `name` and
26
+ `version`;
27
+ * the lock is closed over itself: every `name`, `name version` or `name version
28
+ (source)` a package lists in `dependencies` is a package the lock holds (name, and
29
+ version when the entry gives one). A package dropped from the lock while its
30
+ dependents still list it is a lock cargo refuses to parse, and no manifest shows it
31
+ when the package is not a direct dependency. This is judged from the lock alone,
32
+ before any manifest is read, as are the first three points;
33
+ * every workspace member appears in the lock as a path package (no `source`) with
34
+ its name AND version. Members are the root `[package]`, every `[workspace].members`
35
+ entry (expanded as cargo expands it: the `glob` crate's patterns, hidden
36
+ directories included) minus `[workspace].exclude`, and every path dependency that
37
+ lives inside the workspace root (cargo: "All path dependencies residing in the
38
+ workspace directory automatically become members") unless excluded or a workspace
39
+ root of its own;
40
+ * every direct dependency of every member appears under its REAL package name, and in
41
+ the member's OWN lock entry's `dependencies` (a name the lock holds for some other
42
+ package does not satisfy it: `tokio` is in nearly every lock already, and a manifest
43
+ that gained it would otherwise pass while `cargo build --locked` refuses the lock):
44
+ `[dependencies]`, `[dev-dependencies]`, `[build-dependencies]` (and the underscore
45
+ spellings cargo accepts) and each `[target.<cfg>.<those>]`, optional ones included
46
+ because cargo locks every feature. A package that dev-depends on itself (to enable
47
+ features) is not asked to list itself. `package = "x"` is the real name when present,
48
+ else the key; `workspace = true` reads the `[workspace.dependencies]` entry (whose
49
+ `path` is relative to the workspace root); a path dependency's real name is the
50
+ `[package].name` of the manifest it points at, and it must be a path package.
51
+
52
+ What cannot be judged from the manifest text raises `Undecidable` naming it, and the
53
+ gate turns that into could-not-run. It is never skipped: a manifest this module
54
+ cannot parse, a root with neither `[package]` nor `[workspace]`, a `members` entry
55
+ matching no directory or a directory with no `Cargo.toml`, a member with no
56
+ `[package].name`, an `exclude` entry using glob syntax (cargo's treatment of it is not
57
+ verified, and a wrong guess invents a finding or hides a member), a dependency
58
+ specification that is neither a string nor a table, `workspace = true` with no
59
+ `[workspace.dependencies]` entry, and a path dependency whose manifest cannot be read.
60
+
61
+ **The limits, stated plainly.**
62
+
63
+ * Version-REQUIREMENT drift is not detected: a manifest that raises
64
+ `serde = "1.0.200"` past a locked `1.0.150` still passes here. That is
65
+ `cargo metadata` without `--no-deps`, which needs the registry, and it stays the
66
+ authority for it. This module covers presence, structure and membership.
67
+ * A path dependency that is NOT a workspace member (any path dependency of a manifest
68
+ with no `[workspace]`, or one outside the workspace root) is judged by its own
69
+ presence in the lock only; its own dependencies are not read, so a dependency added
70
+ to such a crate (enact-combined's `plugins/enact-keychain` is one) is not detected.
71
+ Reading them would need that crate's own workspace for its `workspace = true`
72
+ entries and cargo's feature rules for its optional ones (an optional dependency of a
73
+ non-member is locked only when a feature activates it), and a wrong guess is a false
74
+ positive on a good lock.
75
+ """
76
+
77
+ from __future__ import annotations
78
+
79
+ import glob
80
+ import os
81
+ import tomllib
82
+ from collections.abc import Callable
83
+ from pathlib import Path
84
+ from typing import ClassVar, NamedTuple
85
+
86
+ Table = dict[str, object]
87
+
88
+
89
+ class Undecidable(Exception):
90
+ """The lock cannot be judged from the manifests' text. Could-not-run, never a pass."""
91
+
92
+
93
+ class Package(NamedTuple):
94
+ """One `[[package]]` of the lock."""
95
+
96
+ name: str
97
+ version: str
98
+ path: bool
99
+ """No `source`: a path package (a workspace member or a path dependency)."""
100
+ needs: tuple[str, ...]
101
+ """Its `dependencies = [...]`, verbatim: `name`, `name version` or `name version (source)`."""
102
+
103
+
104
+ class Dependency(NamedTuple):
105
+ """A direct dependency, resolved to the package the lock must list."""
106
+
107
+ key: str
108
+ """The name it is declared under (differs from `name` when renamed)."""
109
+ name: str
110
+ """The REAL package name."""
111
+ section: str
112
+ """`[dependencies]`, `[target.'cfg(unix)'.dev-dependencies]`, ... as written."""
113
+ target: Path | None
114
+ """The manifest a path dependency points at; `None` for a registry or git one."""
115
+
116
+
117
+ class Member(NamedTuple):
118
+ manifest: Path
119
+ name: str
120
+ version: str
121
+ dependencies: tuple[Dependency, ...]
122
+
123
+
124
+ class CargoLock:
125
+ """One lockfile against the manifest of the workspace it belongs to."""
126
+
127
+ VERSIONS: ClassVar[tuple[int, ...]] = (3, 4)
128
+ SECTIONS: ClassVar[tuple[str, ...]] = (
129
+ "dependencies",
130
+ "dev-dependencies",
131
+ "dev_dependencies",
132
+ "build-dependencies",
133
+ "build_dependencies",
134
+ )
135
+ GLOB_SYNTAX: ClassVar[str] = "*?["
136
+
137
+ def __init__(self, lock: Path, manifest: Path, relative: Callable[[Path], str]) -> None:
138
+ self.lock = lock
139
+ self.manifest = manifest
140
+ self.shown = relative(lock)
141
+ self.relative = relative
142
+ self.root = manifest.parent
143
+ self.root_document: Table = {}
144
+
145
+ # ------------------------------------------------------------------ the verdict
146
+
147
+ def problems(self) -> list[str]:
148
+ """Every way the lock fails its manifests, in a stable order; empty when it holds."""
149
+ try:
150
+ text = self.lock.read_text(encoding="utf-8")
151
+ except UnicodeDecodeError:
152
+ return [f"{self.shown} is not valid UTF-8 text"]
153
+ try:
154
+ document: Table = tomllib.loads(text)
155
+ except tomllib.TOMLDecodeError as exc:
156
+ return [f"{self.shown} is not valid TOML: {exc}"]
157
+
158
+ packages, found = self._packages(document)
159
+ found = [*self._version(document), *found]
160
+ if found:
161
+ # The structure is wrong, so there is nothing sound to compare the manifests with.
162
+ return found
163
+ # What the lock says about itself is conclusive without any manifest.
164
+ return self._closure(packages) or self._membership(packages)
165
+
166
+ # ------------------------------------------------------------------ the lock itself
167
+
168
+ def _version(self, document: Table) -> list[str]:
169
+ if "version" not in document:
170
+ return [] # the legacy V1/V2 encodings carry no version key; cargo reads them
171
+ value = document["version"]
172
+ if isinstance(value, bool) or not isinstance(value, int) or value not in self.VERSIONS:
173
+ return [
174
+ f"{self.shown} version {value!r} is not supported "
175
+ "(cargo reads lock versions 3 and 4)"
176
+ ]
177
+ return []
178
+
179
+ def _packages(self, document: Table) -> tuple[list[Package], list[str]]:
180
+ raw = document.get("package")
181
+ if raw is None or raw == []:
182
+ return [], [f"{self.shown} lists no packages ([[package]] is absent or empty)"]
183
+ if not isinstance(raw, list):
184
+ return [], [
185
+ f"{self.shown} has a `package` key that is not an array of [[package]] tables"
186
+ ]
187
+ packages: list[Package] = []
188
+ found: list[str] = []
189
+ for number, entry in enumerate(raw, start=1):
190
+ where = f"{self.shown} has a [[package]] entry (number {number})"
191
+ if not isinstance(entry, dict):
192
+ found.append(f"{where} that is not a table")
193
+ continue
194
+ name, version = entry.get("name"), entry.get("version")
195
+ needs = entry.get("dependencies", [])
196
+ if not isinstance(name, str):
197
+ found.append(f"{where} without a string name")
198
+ elif not isinstance(version, str):
199
+ found.append(f"{where} without a string version")
200
+ elif not isinstance(needs, list) or not all(isinstance(n, str) for n in needs):
201
+ found.append(f"{where} whose `dependencies` is not a list of strings")
202
+ else:
203
+ packages.append(Package(name, version, "source" not in entry, tuple(needs)))
204
+ return packages, found
205
+
206
+ def _closure(self, packages: list[Package]) -> list[str]:
207
+ """A package some other package lists as a dependency must itself be in the lock.
208
+
209
+ Internal to the lock, so it needs no manifest. A lock with a package removed (a
210
+ merge that dropped a block, a hand edit) is one cargo refuses to parse, and the
211
+ manifests never show it when the missing package is not a direct dependency.
212
+ """
213
+ held = {(p.name, p.version) for p in packages}
214
+ names = {p.name for p in packages}
215
+ found: list[str] = []
216
+ listed: set[str] = set()
217
+ for package in packages:
218
+ for need in package.needs:
219
+ name, _, rest = need.partition(" ")
220
+ version = rest.partition(" ")[0]
221
+ if need in listed or ((name, version) in held if version else name in names):
222
+ continue
223
+ listed.add(need)
224
+ found.append(
225
+ f"{self.shown} lists dependency '{need}' of package "
226
+ f"'{package.name}' {package.version} but has no such package"
227
+ )
228
+ return found
229
+
230
+ # ------------------------------------------------------------- lock against manifests
231
+
232
+ def _membership(self, packages: list[Package]) -> list[str]:
233
+ members = self._members()
234
+ found: list[str] = []
235
+ missing_members: set[str] = set()
236
+ entries: dict[Path, Package] = {} # a member's manifest -> the lock entry that is it
237
+
238
+ for member in members:
239
+ locked = [p for p in packages if p.name == member.name and p.path]
240
+ at = f"workspace member {self.relative(member.manifest)}"
241
+ if not locked:
242
+ missing_members.add(member.name)
243
+ found.append(f"{self.shown} lists no path package '{member.name}' ({at})")
244
+ elif all(p.version != member.version for p in locked):
245
+ versions = ", ".join(sorted({p.version for p in locked}))
246
+ found.append(
247
+ f"{self.shown} lists '{member.name}' at version {versions} but {at} "
248
+ f"declares {member.version}"
249
+ )
250
+ else:
251
+ entries[member.manifest] = next(p for p in locked if p.version == member.version)
252
+
253
+ # A package nothing in the lock holds is one finding however many members need it. One
254
+ # the lock holds, but that this member's own entry does not list, is a finding per member:
255
+ # `tokio` is in nearly every lock already, so presence anywhere would pass the commonest
256
+ # stale lock (a manifest that gained a dependency the lock has for someone else).
257
+ absent: set[tuple[str, bool]] = set()
258
+ unlisted: set[tuple[Path, str]] = set()
259
+ for member in members:
260
+ entry = entries.get(member.manifest)
261
+ listed = {need.partition(" ")[0] for need in entry.needs} if entry else None
262
+ for dependency in member.dependencies:
263
+ name, path = dependency.name, dependency.target is not None
264
+ if name == member.name or (path and name in missing_members):
265
+ # A package may dev-depend on itself to enable features; a missing member
266
+ # was already reported as the member it is.
267
+ continue
268
+ if not any(p.name == name and (p.path or not path) for p in packages):
269
+ if (name, path) not in absent:
270
+ absent.add((name, path))
271
+ found.append(
272
+ f"{self.shown} lists no {'path ' if path else ''}package '{name}' "
273
+ f"(dependency '{dependency.key}' in {dependency.section} of "
274
+ f"{self.relative(member.manifest)})"
275
+ )
276
+ elif (
277
+ listed is not None
278
+ and name not in listed
279
+ and (member.manifest, name) not in unlisted
280
+ ):
281
+ unlisted.add((member.manifest, name))
282
+ found.append(
283
+ f"{self.shown} package '{member.name}' {member.version} does not list "
284
+ f"'{name}' as a dependency ({dependency.section} of "
285
+ f"{self.relative(member.manifest)} declares dependency '{dependency.key}')"
286
+ )
287
+ return found
288
+
289
+ # ------------------------------------------------------------------------- members
290
+
291
+ def _members(self) -> list[Member]:
292
+ """The workspace's members, root first, then declared, then implicit path members."""
293
+ self.root_document = self._load(self.manifest)
294
+ workspace = self.root_document.get("workspace")
295
+ if workspace is not None and not isinstance(workspace, dict):
296
+ raise Undecidable(
297
+ f"{self.relative(self.manifest)} has a [workspace] that is not a table"
298
+ )
299
+ if "package" not in self.root_document and workspace is None:
300
+ raise Undecidable(
301
+ f"{self.relative(self.manifest)} has neither a [package] nor a [workspace] table"
302
+ )
303
+
304
+ queue: list[Path] = []
305
+ if "package" in self.root_document:
306
+ queue.append(self.manifest)
307
+ if workspace is not None:
308
+ queue.extend(self._declared(workspace))
309
+
310
+ members: dict[Path, Member] = {}
311
+ while queue:
312
+ manifest = queue.pop(0)
313
+ if manifest in members:
314
+ continue
315
+ document = self.root_document if manifest == self.manifest else self._load(manifest)
316
+ member = self._member(manifest, document)
317
+ members[manifest] = member
318
+ if workspace is None:
319
+ continue
320
+ for dependency in member.dependencies:
321
+ if dependency.target is not None and self._implicit(dependency.target, workspace):
322
+ queue.append(dependency.target)
323
+ return list(members.values())
324
+
325
+ def _declared(self, workspace: Table) -> list[Path]:
326
+ """`members` globs expanded on disk, minus `exclude`, as manifests."""
327
+ shown = self.relative(self.manifest)
328
+ members = self._strings(workspace, "members", shown)
329
+ for entry in self._strings(workspace, "exclude", shown):
330
+ if any(char in entry for char in self.GLOB_SYNTAX):
331
+ raise Undecidable(
332
+ f"{shown} [workspace].exclude entry {entry!r} uses glob syntax, which this "
333
+ "check does not interpret"
334
+ )
335
+ manifests: list[Path] = []
336
+ for entry in members:
337
+ matched = sorted(
338
+ glob.glob(entry, root_dir=self.root, recursive=True, include_hidden=True)
339
+ )
340
+ directories = [self._normal(self.root / m) for m in matched if (self.root / m).is_dir()]
341
+ if not directories:
342
+ raise Undecidable(
343
+ f"{shown} [workspace].members entry {entry!r} matches no directory"
344
+ )
345
+ for directory in directories:
346
+ if self._excluded(directory, workspace):
347
+ continue
348
+ manifest = directory / "Cargo.toml"
349
+ if not manifest.is_file():
350
+ raise Undecidable(
351
+ f"workspace member directory {self.relative(directory)} (members entry "
352
+ f"{entry!r} of {shown}) has no Cargo.toml"
353
+ )
354
+ manifests.append(manifest)
355
+ return manifests
356
+
357
+ def _implicit(self, manifest: Path, workspace: Table) -> bool:
358
+ """A path dependency inside the workspace root is a member, unless it opts out."""
359
+ directory = manifest.parent
360
+ if not directory.is_relative_to(self.root) or self._excluded(directory, workspace):
361
+ return False
362
+ return "workspace" not in self._load(manifest)
363
+
364
+ def _excluded(self, directory: Path, workspace: Table) -> bool:
365
+ exclude = self._strings(workspace, "exclude", self.relative(self.manifest))
366
+ return any(directory.is_relative_to(self._normal(self.root / entry)) for entry in exclude)
367
+
368
+ def _member(self, manifest: Path, document: Table) -> Member:
369
+ shown = self.relative(manifest)
370
+ package = document.get("package")
371
+ if not isinstance(package, dict):
372
+ raise Undecidable(f"{shown} is a workspace member but has no [package] table")
373
+ name = package.get("name")
374
+ if not isinstance(name, str):
375
+ raise Undecidable(f"{shown} [package].name is not a string")
376
+ return Member(
377
+ manifest,
378
+ name,
379
+ self._version_of(shown, package),
380
+ self._dependencies(manifest, document),
381
+ )
382
+
383
+ def _version_of(self, shown: str, package: Table) -> str:
384
+ raw = package.get("version", "0.0.0") # cargo's default for a package with no version
385
+ if isinstance(raw, str):
386
+ return raw
387
+ if isinstance(raw, dict) and raw.get("workspace") is True:
388
+ workspace = self.root_document.get("workspace")
389
+ inherited = workspace.get("package") if isinstance(workspace, dict) else None
390
+ version = inherited.get("version") if isinstance(inherited, dict) else None
391
+ if isinstance(version, str):
392
+ return version
393
+ raise Undecidable(
394
+ f"{shown} [package].version is `version.workspace = true` but "
395
+ f"[workspace.package].version of {self.relative(self.manifest)} is not a string"
396
+ )
397
+ raise Undecidable(
398
+ f"{shown} [package].version is neither a string nor `version.workspace = true`"
399
+ )
400
+
401
+ # ---------------------------------------------------------------------- dependencies
402
+
403
+ def _dependencies(self, manifest: Path, document: Table) -> tuple[Dependency, ...]:
404
+ shown = self.relative(manifest)
405
+ sections: list[tuple[str, object]] = [
406
+ (f"[{key}]", document[key]) for key in self.SECTIONS if key in document
407
+ ]
408
+ target = document.get("target")
409
+ if target is not None:
410
+ if not isinstance(target, dict):
411
+ raise Undecidable(f"{shown} [target] is not a table")
412
+ for cfg, body in target.items():
413
+ if not isinstance(body, dict):
414
+ raise Undecidable(f"{shown} [target.{cfg!r}] is not a table")
415
+ label = cfg if all(c.isalnum() or c in "_-" for c in cfg) else f"'{cfg}'"
416
+ sections.extend(
417
+ (f"[target.{label}.{key}]", body[key]) for key in self.SECTIONS if key in body
418
+ )
419
+ found: list[Dependency] = []
420
+ for section, table in sections:
421
+ if not isinstance(table, dict):
422
+ raise Undecidable(f"{shown} {section} is not a table")
423
+ for key, spec in table.items():
424
+ found.append(self._dependency(manifest, section, key, spec))
425
+ return tuple(found)
426
+
427
+ def _dependency(self, manifest: Path, section: str, key: str, spec: object) -> Dependency:
428
+ where = f"dependency {key!r} in {section} of {self.relative(manifest)}"
429
+ base = manifest.parent
430
+ if isinstance(spec, dict) and "workspace" in spec:
431
+ if spec["workspace"] is not True:
432
+ given = str(spec["workspace"]).lower()
433
+ raise Undecidable(f"{where} has `workspace = {given}`, not `workspace = true`")
434
+ workspace = self.root_document.get("workspace")
435
+ entries = workspace.get("dependencies") if isinstance(workspace, dict) else None
436
+ if not isinstance(entries, dict) or key not in entries:
437
+ raise Undecidable(
438
+ f"{where} is `workspace = true` but [workspace.dependencies] of "
439
+ f"{self.relative(self.manifest)} has no entry {key!r}"
440
+ )
441
+ spec, base = entries[key], self.root # a workspace path is relative to the root
442
+ if isinstance(spec, str):
443
+ return Dependency(key, key, section, None)
444
+ if not isinstance(spec, dict):
445
+ raise Undecidable(f"{where} is neither a version string nor a table")
446
+
447
+ package, path = spec.get("package"), spec.get("path")
448
+ if package is not None and not isinstance(package, str):
449
+ raise Undecidable(f"{where} has a `package` that is not a string")
450
+ if path is not None and not isinstance(path, str):
451
+ raise Undecidable(f"{where} has a `path` that is not a string")
452
+ if path is None:
453
+ return Dependency(key, package or key, section, None)
454
+ target = self._normal(base / path / "Cargo.toml")
455
+ name = package or self._name_at(target, where)
456
+ return Dependency(key, name, section, target)
457
+
458
+ def _name_at(self, manifest: Path, where: str) -> str:
459
+ """A path dependency's real name: the `[package].name` of the manifest it points at."""
460
+ if not manifest.is_file():
461
+ raise Undecidable(f"{where} points at {self.relative(manifest)}, which is not a file")
462
+ package = self._load(manifest).get("package")
463
+ name = package.get("name") if isinstance(package, dict) else None
464
+ if not isinstance(name, str):
465
+ raise Undecidable(
466
+ f"{where} points at {self.relative(manifest)}, which has no [package].name"
467
+ )
468
+ return name
469
+
470
+ # ------------------------------------------------------------------------- plumbing
471
+
472
+ def _load(self, manifest: Path) -> Table:
473
+ shown = self.relative(manifest)
474
+ try:
475
+ return tomllib.loads(manifest.read_text(encoding="utf-8"))
476
+ except tomllib.TOMLDecodeError as exc:
477
+ raise Undecidable(f"{shown} is not valid TOML for this check: {exc}") from exc
478
+ except (OSError, UnicodeDecodeError) as exc:
479
+ raise Undecidable(f"{shown} could not be read: {exc}") from exc
480
+
481
+ @staticmethod
482
+ def _strings(table: Table, key: str, shown: str) -> list[str]:
483
+ value = table.get(key, [])
484
+ if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
485
+ raise Undecidable(f"{shown} [workspace].{key} is not a list of strings")
486
+ return value
487
+
488
+ @staticmethod
489
+ def _normal(path: Path) -> Path:
490
+ return Path(os.path.normpath(path))