@amsterdamdatalabs/adl-agent-control-darwin-arm64 0.1.4

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 (741) hide show
  1. package/bin/adl +4 -0
  2. package/bin/cursor-settings +14 -0
  3. package/bin/default-agent-file +14 -0
  4. package/bin/git +670 -0
  5. package/bin/git-ado +425 -0
  6. package/bin/global-agents +14 -0
  7. package/bin/global-surface-sweep +14 -0
  8. package/bin/health-check +38 -0
  9. package/bin/kimi-hooks +14 -0
  10. package/bin/lean-ctx-observe-strip +361 -0
  11. package/bin/lib/bun-compile-build.sh +34 -0
  12. package/bin/lib/lean-ctx-observe-hooks.sh +634 -0
  13. package/bin/managed-settings +14 -0
  14. package/bin/opencode-hooks +14 -0
  15. package/bin/reconcile +2648 -0
  16. package/bin/scratch-reap +14 -0
  17. package/bin/shell-path-policy +64 -0
  18. package/bin/surface-policy +14 -0
  19. package/bin/sync-and-reconcile +215 -0
  20. package/bin/update-azure-agent-pool +29 -0
  21. package/bin/validate-manifest +171 -0
  22. package/bin/vscode-settings +14 -0
  23. package/package.json +44 -0
  24. package/packages/agent-hooks/README.md +411 -0
  25. package/packages/agent-hooks/assets/hooks.json +97 -0
  26. package/packages/agent-hooks/assets/opencode-plugin.template.ts +224 -0
  27. package/packages/agent-hooks/bunfig.toml +27 -0
  28. package/packages/agent-hooks/deploy-profile.json +53 -0
  29. package/packages/agent-hooks/deploy-profile.schema.json +120 -0
  30. package/packages/agent-hooks/package.json +23 -0
  31. package/packages/agent-hooks/src/cli/args.ts +99 -0
  32. package/packages/agent-hooks/src/cli/doctor.ts +241 -0
  33. package/packages/agent-hooks/src/cli/rules.ts +133 -0
  34. package/packages/agent-hooks/src/deploy/deploy.ts +181 -0
  35. package/packages/agent-hooks/src/deploy/doctor.ts +158 -0
  36. package/packages/agent-hooks/src/deploy/index.ts +17 -0
  37. package/packages/agent-hooks/src/deploy/json-merge.ts +115 -0
  38. package/packages/agent-hooks/src/deploy/legacy-lock.ts +213 -0
  39. package/packages/agent-hooks/src/deploy/list.ts +117 -0
  40. package/packages/agent-hooks/src/deploy/lock.ts +65 -0
  41. package/packages/agent-hooks/src/deploy/opencode-template.ts +74 -0
  42. package/packages/agent-hooks/src/deploy/profile.ts +117 -0
  43. package/packages/agent-hooks/src/deploy/render.ts +162 -0
  44. package/packages/agent-hooks/src/deploy/source.ts +84 -0
  45. package/packages/agent-hooks/src/gating/config.ts +514 -0
  46. package/packages/agent-hooks/src/gating/root.ts +117 -0
  47. package/packages/agent-hooks/src/hooks/briefing/handler.ts +22 -0
  48. package/packages/agent-hooks/src/hooks/briefing/index.ts +320 -0
  49. package/packages/agent-hooks/src/hooks/contract.ts +112 -0
  50. package/packages/agent-hooks/src/hooks/crg/handler.ts +22 -0
  51. package/packages/agent-hooks/src/hooks/crg/index.ts +209 -0
  52. package/packages/agent-hooks/src/hooks/lean-ctx/handler.ts +22 -0
  53. package/packages/agent-hooks/src/hooks/lean-ctx/index.ts +173 -0
  54. package/packages/agent-hooks/src/hooks/lean-ctx/mapping.ts +121 -0
  55. package/packages/agent-hooks/src/hooks/lean-ctx/scope-rewrite.ts +207 -0
  56. package/packages/agent-hooks/src/hooks/log.ts +233 -0
  57. package/packages/agent-hooks/src/hooks/user-prompt-submit/config.ts +58 -0
  58. package/packages/agent-hooks/src/hooks/user-prompt-submit/constants.ts +149 -0
  59. package/packages/agent-hooks/src/hooks/user-prompt-submit/handler.ts +72 -0
  60. package/packages/agent-hooks/src/hooks/user-prompt-submit/index.ts +203 -0
  61. package/packages/agent-hooks/src/hooks/user-prompt-submit/match.ts +212 -0
  62. package/packages/agent-hooks/src/hooks/user-prompt-submit/skill-roots.ts +73 -0
  63. package/packages/agent-hooks/src/hooks/user-prompt-submit/skills.ts +98 -0
  64. package/packages/agent-hooks/src/hooks/user-prompt-submit/tokenizer.ts +115 -0
  65. package/packages/agent-hooks/src/hooks/user-prompt-submit/vector.ts +174 -0
  66. package/packages/agent-hooks/src/hosts/impl/claude.ts +47 -0
  67. package/packages/agent-hooks/src/hosts/impl/codex.ts +47 -0
  68. package/packages/agent-hooks/src/hosts/impl/cursor.ts +40 -0
  69. package/packages/agent-hooks/src/hosts/impl/kimi.ts +47 -0
  70. package/packages/agent-hooks/src/hosts/impl/opencode.ts +54 -0
  71. package/packages/agent-hooks/src/hosts/registry.ts +29 -0
  72. package/packages/agent-hooks/src/hosts/spec.ts +113 -0
  73. package/packages/agent-hooks/src/lean-ctx/env.ts +106 -0
  74. package/packages/agent-hooks/src/lean-ctx/mcp.ts +140 -0
  75. package/packages/agent-hooks/src/lib/spawn-with-timeout.ts +120 -0
  76. package/packages/agent-hooks/src/log/writer.ts +230 -0
  77. package/packages/agent-hooks/src/main.ts +269 -0
  78. package/packages/agent-hooks/src/pre-tool-use/handler.ts +549 -0
  79. package/packages/agent-hooks/src/rules/engine.ts +293 -0
  80. package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +51 -0
  81. package/packages/agent-hooks/src/rules/impl/adl-git-credential-bypass.ts +320 -0
  82. package/packages/agent-hooks/src/rules/impl/adl-git-identity.ts +249 -0
  83. package/packages/agent-hooks/src/rules/impl/adl-git-push-policy.ts +258 -0
  84. package/packages/agent-hooks/src/rules/impl/adl-human-azdo-bypass.ts +731 -0
  85. package/packages/agent-hooks/src/rules/impl/adl-identity.ts +128 -0
  86. package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +179 -0
  87. package/packages/agent-hooks/src/rules/impl/adl-pr-complete-pending.ts +65 -0
  88. package/packages/agent-hooks/src/rules/impl/adl-pr-review-role.ts +156 -0
  89. package/packages/agent-hooks/src/rules/impl/adl-pr-role.ts +121 -0
  90. package/packages/agent-hooks/src/rules/impl/adl-session-required.ts +403 -0
  91. package/packages/agent-hooks/src/rules/impl/adl-token-minting-role.ts +92 -0
  92. package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +74 -0
  93. package/packages/agent-hooks/src/rules/impl/advisory-coverage.ts +43 -0
  94. package/packages/agent-hooks/src/rules/impl/advisory-finder.ts +62 -0
  95. package/packages/agent-hooks/src/rules/impl/agent-secrets-stay-unread.ts +173 -0
  96. package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +508 -0
  97. package/packages/agent-hooks/src/rules/impl/fs-canonical-checkout.ts +86 -0
  98. package/packages/agent-hooks/src/rules/impl/fs-git-internals.ts +75 -0
  99. package/packages/agent-hooks/src/rules/impl/fs-mv.ts +66 -0
  100. package/packages/agent-hooks/src/rules/impl/fs-rm.ts +83 -0
  101. package/packages/agent-hooks/src/rules/impl/fs-trash.ts +140 -0
  102. package/packages/agent-hooks/src/rules/impl/git-core.ts +101 -0
  103. package/packages/agent-hooks/src/rules/impl/git-helpers.ts +408 -0
  104. package/packages/agent-hooks/src/rules/impl/git-history.ts +77 -0
  105. package/packages/agent-hooks/src/rules/impl/git-paths.ts +139 -0
  106. package/packages/agent-hooks/src/rules/impl/git-plumbing.ts +167 -0
  107. package/packages/agent-hooks/src/rules/impl/git-protected-tree.ts +132 -0
  108. package/packages/agent-hooks/src/rules/impl/git-transitions.ts +292 -0
  109. package/packages/agent-hooks/src/rules/impl/git-verify.ts +95 -0
  110. package/packages/agent-hooks/src/rules/impl/git-worktree.ts +265 -0
  111. package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +76 -0
  112. package/packages/agent-hooks/src/rules/impl/messages.ts +128 -0
  113. package/packages/agent-hooks/src/rules/impl/parse-uncertain.ts +143 -0
  114. package/packages/agent-hooks/src/rules/impl/pr-completion.ts +35 -0
  115. package/packages/agent-hooks/src/rules/impl/shell-remote-script-exec.ts +63 -0
  116. package/packages/agent-hooks/src/rules/impl/tool-azure-devops.ts +283 -0
  117. package/packages/agent-hooks/src/rules/impl/tool-bun-test.ts +81 -0
  118. package/packages/agent-hooks/src/rules/impl/tool-cargo.ts +66 -0
  119. package/packages/agent-hooks/src/rules/impl/tool-chmod-hook.ts +42 -0
  120. package/packages/agent-hooks/src/rules/impl/tool-find.ts +82 -0
  121. package/packages/agent-hooks/src/rules/impl/tool-frontend.ts +83 -0
  122. package/packages/agent-hooks/src/rules/impl/tool-grep.ts +93 -0
  123. package/packages/agent-hooks/src/rules/impl/tool-lean-ctx-global.ts +594 -0
  124. package/packages/agent-hooks/src/rules/impl/tool-native-search.ts +77 -0
  125. package/packages/agent-hooks/src/rules/ports.ts +178 -0
  126. package/packages/agent-hooks/src/rules/registry.ts +233 -0
  127. package/packages/agent-hooks/src/rules/shell/git-identity.ts +1415 -0
  128. package/packages/agent-hooks/src/rules/shell/git-internals.ts +186 -0
  129. package/packages/agent-hooks/src/rules/shell/invocation.ts +1083 -0
  130. package/packages/agent-hooks/src/rules/shell/piped-script.ts +201 -0
  131. package/packages/agent-hooks/src/rules/shell/prefixes.ts +214 -0
  132. package/packages/agent-hooks/src/rules/shell/protected-carrier-writes.ts +108 -0
  133. package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +149 -0
  134. package/packages/agent-hooks/src/rules/shell/split.ts +179 -0
  135. package/packages/agent-hooks/src/rules/shell/substitution.ts +394 -0
  136. package/packages/agent-hooks/src/rules/shell/token.ts +180 -0
  137. package/packages/agent-hooks/src/rules/shell/xargs.ts +81 -0
  138. package/packages/agent-hooks/src/rules/spec.ts +152 -0
  139. package/packages/agent-hooks/src/surface-policy.ts +37 -0
  140. package/packages/agent-hooks/tsconfig.json +23 -0
  141. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +107 -0
  142. package/packages/agent-settings/assets/agents/architect.toml +30 -0
  143. package/packages/agent-settings/assets/agents/code-reviewer.toml +27 -0
  144. package/packages/agent-settings/assets/agents/critic.toml +35 -0
  145. package/packages/agent-settings/assets/agents/executor.toml +28 -0
  146. package/packages/agent-settings/assets/agents/explore.toml +25 -0
  147. package/packages/agent-settings/assets/agents/planner.toml +29 -0
  148. package/packages/agent-settings/assets/lean-ctx/config.toml +39 -0
  149. package/packages/agent-settings/assets/lean-ctx/gitignore +6 -0
  150. package/packages/agent-settings/assets/lean-ctx/layout.toml +5 -0
  151. package/packages/agent-settings/assets/mcp.json +20 -0
  152. package/packages/agent-settings/managed/README.md +331 -0
  153. package/packages/agent-settings/managed/claude-managed-settings.json +150 -0
  154. package/packages/agent-settings/managed/codex-requirements.toml +105 -0
  155. package/packages/agent-settings/protected.json +470 -0
  156. package/packages/agent-settings/src/deploy/agent-render.ts +94 -0
  157. package/packages/agent-settings/src/deploy/agent-toml.ts +76 -0
  158. package/packages/agent-settings/src/deploy/agents-deploy.ts +129 -0
  159. package/packages/agent-settings/src/deploy/doctor.ts +201 -0
  160. package/packages/agent-settings/src/deploy/index.ts +33 -0
  161. package/packages/agent-settings/src/deploy/json-pretty.ts +66 -0
  162. package/packages/agent-settings/src/deploy/lean-ctx-deploy.ts +138 -0
  163. package/packages/agent-settings/src/deploy/legacy-lock.ts +286 -0
  164. package/packages/agent-settings/src/deploy/list.ts +135 -0
  165. package/packages/agent-settings/src/deploy/lock.ts +112 -0
  166. package/packages/agent-settings/src/deploy/mcp-deploy.ts +175 -0
  167. package/packages/agent-settings/src/deploy/mcp-inspect.ts +56 -0
  168. package/packages/agent-settings/src/deploy/mcp-merge.ts +164 -0
  169. package/packages/agent-settings/src/deploy/mcp-render.ts +80 -0
  170. package/packages/agent-settings/src/deploy/mcp-source.ts +43 -0
  171. package/packages/agent-settings/src/deploy/profile.ts +296 -0
  172. package/packages/agent-settings/src/deploy/settings.ts +65 -0
  173. package/packages/agent-settings/src/global-agents.ts +230 -0
  174. package/packages/agent-settings/src/global-surface-sweep.ts +2179 -0
  175. package/packages/agent-settings/src/kimi-hooks.ts +455 -0
  176. package/packages/agent-settings/src/opencode-hooks.ts +76 -0
  177. package/packages/agent-settings/src/surface-policy.ts +441 -0
  178. package/packages/agent-settings/surface-policy.json +20 -0
  179. package/packages/agent-skills/assets/code-review/SKILL.md +100 -0
  180. package/packages/agent-skills/assets/codebase-design/SKILL.md +98 -0
  181. package/packages/agent-skills/assets/codebase-design/references/DEEPENING.md +37 -0
  182. package/packages/agent-skills/assets/codebase-design/references/DESIGN-IT-TWICE.md +44 -0
  183. package/packages/agent-skills/assets/codebase-design/references/TESTABILITY.md +29 -0
  184. package/packages/agent-skills/assets/debug-issue/SKILL.md +40 -0
  185. package/packages/agent-skills/assets/diagnosing-bugs/SKILL.md +100 -0
  186. package/packages/agent-skills/assets/diagnosing-bugs/references/FEEDBACK-LOOP-TACTICS.md +34 -0
  187. package/packages/agent-skills/assets/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  188. package/packages/agent-skills/assets/domain-modeling/SKILL.md +86 -0
  189. package/packages/agent-skills/assets/domain-modeling/references/ADR-FORMAT.md +47 -0
  190. package/packages/agent-skills/assets/domain-modeling/references/CONTEXT-FORMAT.md +60 -0
  191. package/packages/agent-skills/assets/explore-codebase/SKILL.md +41 -0
  192. package/packages/agent-skills/assets/handoff/SKILL.md +28 -0
  193. package/packages/agent-skills/assets/implement/SKILL.md +28 -0
  194. package/packages/agent-skills/assets/refactor-safely/SKILL.md +44 -0
  195. package/packages/agent-skills/assets/review-changes/SKILL.md +45 -0
  196. package/packages/agent-skills/assets/tdd/SKILL.md +49 -0
  197. package/packages/agent-skills/assets/tdd/references/mocking.md +59 -0
  198. package/packages/agent-skills/assets/tdd/references/tests.md +77 -0
  199. package/packages/agent-skills/assets/testing/SKILL.md +54 -0
  200. package/packages/agent-skills/assets/to-feature/SKILL.md +91 -0
  201. package/packages/agent-skills/assets/to-feature/references/FEATURE-SPEC-TEMPLATE.md +88 -0
  202. package/packages/agent-skills/assets/to-workitems/SKILL.md +90 -0
  203. package/packages/agent-skills/assets/to-workitems/references/AZURE-DEVOPS-HIERARCHY.md +118 -0
  204. package/packages/agent-skills/assets/to-workitems/references/TICKET-TEMPLATES.md +70 -0
  205. package/packages/agent-skills/assets/wayfinder/SKILL.md +100 -0
  206. package/packages/agent-skills/assets/wayfinder/references/TEMPLATES.md +35 -0
  207. package/packages/agent-skills/assets/wayfinder/references/TICKET-TYPES.md +8 -0
  208. package/packages/agent-skills/deploy-profile.json +9 -0
  209. package/packages/agent-skills/deploy-profile.schema.json +45 -0
  210. package/packages/agent-skills/dist/deploy.d.ts +27 -0
  211. package/packages/agent-skills/dist/deploy.d.ts.map +1 -0
  212. package/packages/agent-skills/dist/deploy.js +186 -0
  213. package/packages/agent-skills/dist/deploy.js.map +1 -0
  214. package/packages/agent-skills/dist/doctor.d.ts +24 -0
  215. package/packages/agent-skills/dist/doctor.d.ts.map +1 -0
  216. package/packages/agent-skills/dist/doctor.js +141 -0
  217. package/packages/agent-skills/dist/doctor.js.map +1 -0
  218. package/packages/agent-skills/dist/fs-utils.d.ts +8 -0
  219. package/packages/agent-skills/dist/fs-utils.d.ts.map +1 -0
  220. package/packages/agent-skills/dist/fs-utils.js +40 -0
  221. package/packages/agent-skills/dist/fs-utils.js.map +1 -0
  222. package/packages/agent-skills/dist/index.d.ts +8 -0
  223. package/packages/agent-skills/dist/index.d.ts.map +1 -0
  224. package/packages/agent-skills/dist/index.js +8 -0
  225. package/packages/agent-skills/dist/index.js.map +1 -0
  226. package/packages/agent-skills/dist/legacy-lock.d.ts +44 -0
  227. package/packages/agent-skills/dist/legacy-lock.d.ts.map +1 -0
  228. package/packages/agent-skills/dist/legacy-lock.js +121 -0
  229. package/packages/agent-skills/dist/legacy-lock.js.map +1 -0
  230. package/packages/agent-skills/dist/list.d.ts +23 -0
  231. package/packages/agent-skills/dist/list.d.ts.map +1 -0
  232. package/packages/agent-skills/dist/list.js +75 -0
  233. package/packages/agent-skills/dist/list.js.map +1 -0
  234. package/packages/agent-skills/dist/manifest.d.ts +15 -0
  235. package/packages/agent-skills/dist/manifest.d.ts.map +1 -0
  236. package/packages/agent-skills/dist/manifest.js +57 -0
  237. package/packages/agent-skills/dist/manifest.js.map +1 -0
  238. package/packages/agent-skills/dist/profile.d.ts +41 -0
  239. package/packages/agent-skills/dist/profile.d.ts.map +1 -0
  240. package/packages/agent-skills/dist/profile.js +86 -0
  241. package/packages/agent-skills/dist/profile.js.map +1 -0
  242. package/packages/agent-skills/dist/skill-config.d.ts +24 -0
  243. package/packages/agent-skills/dist/skill-config.d.ts.map +1 -0
  244. package/packages/agent-skills/dist/skill-config.js +68 -0
  245. package/packages/agent-skills/dist/skill-config.js.map +1 -0
  246. package/packages/agent-skills/dist/skill-meta.d.ts +17 -0
  247. package/packages/agent-skills/dist/skill-meta.d.ts.map +1 -0
  248. package/packages/agent-skills/dist/skill-meta.js +67 -0
  249. package/packages/agent-skills/dist/skill-meta.js.map +1 -0
  250. package/packages/agent-skills/package.json +31 -0
  251. package/packages/agent-skills/src/deploy.ts +222 -0
  252. package/packages/agent-skills/src/doctor.ts +178 -0
  253. package/packages/agent-skills/src/fs-utils.ts +40 -0
  254. package/packages/agent-skills/src/index.ts +29 -0
  255. package/packages/agent-skills/src/legacy-lock.ts +152 -0
  256. package/packages/agent-skills/src/list.ts +94 -0
  257. package/packages/agent-skills/src/manifest.ts +66 -0
  258. package/packages/agent-skills/src/profile.ts +128 -0
  259. package/packages/agent-skills/src/skill-config.ts +75 -0
  260. package/packages/agent-skills/src/skill-meta.ts +78 -0
  261. package/packages/agent-skills/tsconfig.build.json +13 -0
  262. package/packages/agent-skills/tsconfig.json +23 -0
  263. package/packages/azdo/README.md +111 -0
  264. package/packages/azdo/package.json +21 -0
  265. package/packages/azdo/src/azdo-pl.ts +275 -0
  266. package/packages/azdo/src/azdo-pr-review.ts +160 -0
  267. package/packages/azdo/src/azdo-pr.ts +289 -0
  268. package/packages/azdo/src/azdo-repo-doctor.ts +839 -0
  269. package/packages/azdo/src/azdo-wit.ts +145 -0
  270. package/packages/azdo/src/cli.ts +251 -0
  271. package/packages/azdo/src/lib/agent-identity.ts +198 -0
  272. package/packages/azdo/src/lib/agent-token.ts +167 -0
  273. package/packages/azdo/src/lib/client.ts +259 -0
  274. package/packages/azdo/src/lib/graph-identity.ts +153 -0
  275. package/packages/azdo/src/lib/validate-pr-metadata.ts +57 -0
  276. package/packages/azdo/src/mcp-server.ts +209 -0
  277. package/packages/azdo/src/tool-dispatch.ts +204 -0
  278. package/packages/azdo/tsconfig.json +13 -0
  279. package/packages/fleet/jobs/git-maintain/src/main.ts +588 -0
  280. package/packages/fleet/manifest.json +207 -0
  281. package/packages/fleet/manifest.schema.json +283 -0
  282. package/packages/fleet/src/default-agent-file.ts +144 -0
  283. package/packages/fleet/src/install-adl-package.ts +178 -0
  284. package/packages/fleet/src/install-embedding-model.ts +412 -0
  285. package/packages/fleet/src/launch-agents.ts +649 -0
  286. package/packages/fleet/src/managed-settings.ts +278 -0
  287. package/packages/fleet/src/scratch-reap.ts +601 -0
  288. package/packages/fleet/src/vscode-settings.ts +705 -0
  289. package/packages/fleet/systemd/adl-azure-agent-update.service +13 -0
  290. package/packages/fleet/systemd/adl-azure-agent-update.timer +10 -0
  291. package/packages/repo-controls/AGENTS.md +96 -0
  292. package/packages/repo-controls/CLAUDE.md +1 -0
  293. package/packages/repo-controls/LICENSE +20 -0
  294. package/packages/repo-controls/README.md +493 -0
  295. package/packages/repo-controls/assets/enable-integration-auto-complete.mjs +79 -0
  296. package/packages/repo-controls/assets/promote.ts +371 -0
  297. package/packages/repo-controls/assets/version-bump.sh +189 -0
  298. package/packages/repo-controls/azure-pipelines.yml +274 -0
  299. package/packages/repo-controls/bin/adl-repo-controls.ts +10 -0
  300. package/packages/repo-controls/bunfig.toml +58 -0
  301. package/packages/repo-controls/package/adl-compliance/AGENTS.md +163 -0
  302. package/packages/repo-controls/package/adl-compliance/CLAUDE.md +1 -0
  303. package/packages/repo-controls/package/adl-compliance/conftest.py +78 -0
  304. package/packages/repo-controls/package/adl-compliance/pyproject.toml +31 -0
  305. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/__init__.py +50 -0
  306. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +98 -0
  307. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/__init__.py +1 -0
  308. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/__init__.py +12 -0
  309. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/check.py +210 -0
  310. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-bad-status/docs/architecture/overview.md +8 -0
  311. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-bad-status/docs/decisions/adr-901-proposed.md +10 -0
  312. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-broken-backlink/docs/decisions/adr-902-dangling.md +11 -0
  313. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-clean/docs/architecture/overview.md +8 -0
  314. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-clean/docs/decisions/adr-900-example.md +10 -0
  315. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-frontmatter-mismatch/.plans/b_committed/26-08-08-lifecycle-fixture.md +3 -0
  316. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-frontmatter-mismatch/.specs/demo/lifecycle.md +7 -0
  317. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-frontmatter-mismatch/docs/decisions/adr-903-frontmatter-mismatch.md +11 -0
  318. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-inactive-plan/.plans/c_done/26-08-08-completed-fixture.md +3 -0
  319. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-inactive-plan/.specs/demo/lifecycle.md +7 -0
  320. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/fixtures/adr-lifecycle-inactive-plan/docs/decisions/adr-904-completed-plan.md +11 -0
  321. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +127 -0
  322. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/__init__.py +7 -0
  323. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/check.py +91 -0
  324. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/AGENTS.md +1 -0
  325. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/CLAUDE.md +1 -0
  326. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/architecture/declared.txt +1 -0
  327. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/compliance/declared.txt +1 -0
  328. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/decisions/declared.txt +1 -0
  329. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/index.md +1 -0
  330. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/operations/declared.txt +1 -0
  331. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/research/declared.txt +1 -0
  332. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/stray-root-file.md +1 -0
  333. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/totally-made-up/nonsense.md +1 -0
  334. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/test_check.py +106 -0
  335. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/__init__.py +7 -0
  336. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +291 -0
  337. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/clean/backend/clean.rs +1 -0
  338. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/excluded/frontend/mobile/src/i18n/locales/zh-CN/strings.ts +1 -0
  339. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/violation/backend/escaped.rs +2 -0
  340. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/violation/backend/raw.rs +1 -0
  341. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/violation/backend//346/226/207/344/273/266.rs +1 -0
  342. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +193 -0
  343. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/__init__.py +12 -0
  344. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/check.py +140 -0
  345. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/fixtures/index-updated-clean/docs/architecture/foo.md +7 -0
  346. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/fixtures/index-updated-clean/docs/architecture/index.md +10 -0
  347. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/fixtures/index-updated-violation/docs/architecture/foo.md +7 -0
  348. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/fixtures/index-updated-violation/docs/architecture/index.md +9 -0
  349. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/test_check.py +98 -0
  350. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/__init__.py +12 -0
  351. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +378 -0
  352. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +192 -0
  353. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-clean/docs/index.md +14 -0
  354. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-clean/docs/one.md +7 -0
  355. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-clean/docs/two.md +7 -0
  356. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-clean/docs-knowledge-indexes.yaml +20 -0
  357. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-duplicate/docs/index.md +15 -0
  358. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-duplicate/docs/one.md +7 -0
  359. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-duplicate/docs/two.md +7 -0
  360. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-duplicate/docs-knowledge-indexes.yaml +16 -0
  361. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-missing-entry/docs/index.md +13 -0
  362. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-missing-entry/docs/one.md +7 -0
  363. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-missing-entry/docs/two.md +7 -0
  364. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-missing-entry/docs-knowledge-indexes.yaml +16 -0
  365. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +102 -0
  366. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/__init__.py +12 -0
  367. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/check.py +387 -0
  368. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/fixtures/ledger-bad-date/plans/LEDGER.md +5 -0
  369. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/fixtures/ledger-clean/plans/LEDGER.md +6 -0
  370. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/fixtures/ledger-empty-table/plans/LEDGER.md +4 -0
  371. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/fixtures/ledger-long-event/plans/LEDGER.md +5 -0
  372. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/fixtures/ledger-violation/plans/LEDGER.md +5 -0
  373. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/test_check.py +427 -0
  374. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/markdown_ast/__init__.py +11 -0
  375. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/markdown_ast/check.py +84 -0
  376. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/markdown_ast/fixtures/markdown-structure-bad-fence/docs/bad-fence.md +2 -0
  377. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/markdown_ast/fixtures/markdown-structure-bad-fence/docs/valid.md +5 -0
  378. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/markdown_ast/fixtures/markdown-structure-bad-frontmatter/docs/bad-front-matter.md +2 -0
  379. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/markdown_ast/fixtures/markdown-structure-bad-frontmatter/docs/valid.md +5 -0
  380. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/markdown_ast/fixtures/markdown-structure-clean/docs/valid.md +7 -0
  381. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/markdown_ast/test_check.py +126 -0
  382. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/__init__.py +12 -0
  383. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/check.py +210 -0
  384. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +119 -0
  385. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs/architecture/demo.md +17 -0
  386. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs-okf-profiles.yaml +8 -0
  387. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs/architecture/bad-status.md +17 -0
  388. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs-okf-profiles.yaml +8 -0
  389. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/test_check.py +49 -0
  390. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +159 -0
  391. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/__init__.py +12 -0
  392. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/check.py +578 -0
  393. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-clean/docs/architecture/backend/crate-overview.md +8 -0
  394. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-clean/docs/architecture/team/team-overview.md +8 -0
  395. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-clean/docs/compliance/controls/control-matrix.md +12 -0
  396. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-clean/docs/compliance/controls/control-matrix.yaml +10 -0
  397. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-clean/docs/research/benchmarks/note.md +24 -0
  398. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/backend/crate-overview.md +8 -0
  399. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-delivery.md +10 -0
  400. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-recommendation.md +12 -0
  401. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/target-narration.md +10 -0
  402. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/team/team-overview.md +8 -0
  403. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/compliance/controls/control-matrix.md +8 -0
  404. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/compliance/controls/control-matrix.yaml +17 -0
  405. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/research/benchmarks/bad-research.md +8 -0
  406. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +461 -0
  407. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/__init__.py +1 -0
  408. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/__init__.py +12 -0
  409. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/check.py +124 -0
  410. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/.workflows/AGENTS.md +1 -0
  411. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/.workflows/CLAUDE.md +1 -0
  412. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/AGENTS.md +3 -0
  413. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/CLAUDE.md +2 -0
  414. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/INVARIANTS.md +3 -0
  415. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/bad-pointer/AGENTS.md +3 -0
  416. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/bad-pointer/CLAUDE.md +1 -0
  417. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/docs/AGENTS.md +1 -0
  418. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/docs/CLAUDE.md +1 -0
  419. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/missing-pair/AGENTS.md +3 -0
  420. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/owned/AGENTS.md +1 -0
  421. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/fixtures/owned/CLAUDE.md +2 -0
  422. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/agents_layout/test_check.py +116 -0
  423. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/ci_paths/__init__.py +13 -0
  424. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/ci_paths/check.py +380 -0
  425. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/ci_paths/fixtures/redeclared-workflows-tree/automation/pipelines/pr-validation.yaml +1 -0
  426. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/ci_paths/test_check.py +274 -0
  427. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/__init__.py +12 -0
  428. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +227 -0
  429. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  430. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +6 -0
  431. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/fresh/fresh.ts +1 -0
  432. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub1/only.ts +1 -0
  433. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub10/only.ts +1 -0
  434. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub11/only.ts +1 -0
  435. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub2/only.ts +1 -0
  436. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub3/only.ts +1 -0
  437. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub4/only.ts +1 -0
  438. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub5/only.ts +1 -0
  439. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub6/only.ts +1 -0
  440. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub7/only.ts +1 -0
  441. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub8/only.ts +1 -0
  442. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub9/only.ts +1 -0
  443. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/services/i18n/locales/de/common.json +1 -0
  444. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  445. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/vendored-crate/Cargo.toml +2 -0
  446. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +6 -0
  447. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/fresh/fresh.ts +1 -0
  448. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub1/only.ts +1 -0
  449. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub10/only.ts +1 -0
  450. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub11/only.ts +1 -0
  451. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub2/only.ts +1 -0
  452. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub3/only.ts +1 -0
  453. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub4/only.ts +1 -0
  454. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub5/only.ts +1 -0
  455. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub6/only.ts +1 -0
  456. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub7/only.ts +1 -0
  457. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub8/only.ts +1 -0
  458. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub9/only.ts +1 -0
  459. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/solo/item.ts +1 -0
  460. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +6 -0
  461. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +138 -0
  462. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/__init__.py +12 -0
  463. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +240 -0
  464. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/fixtures/markdown-trees-clean/.plans/a_new/plan.md +7 -0
  465. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/fixtures/markdown-trees-clean/backend/AGENTS.md +4 -0
  466. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/fixtures/markdown-trees-clean/docs/page.md +7 -0
  467. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/fixtures/markdown-trees-stray/backend/notes.md +4 -0
  468. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/fixtures/markdown-trees-stray/docs/page.md +8 -0
  469. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +220 -0
  470. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/__init__.py +5 -0
  471. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/check.py +225 -0
  472. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/fixtures/open-work-cap-clean/plans/GAPS-AND-FINDINGS.md +5 -0
  473. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/fixtures/open-work-cap-clean/plans/features/az601-clean/spec.md +10 -0
  474. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/test_check.py +330 -0
  475. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/__init__.py +12 -0
  476. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +240 -0
  477. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/agent/crates/enact-providers/src/snapshots/README.md +1 -0
  478. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-app/assets/builtin-assistants/avatars/assistant.jpg +1 -0
  479. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-assets/assets/logos/tools/coding/tool.png +1 -0
  480. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-session/src/backend/codex_init_prompt.md +1 -0
  481. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/mobile/assets/images/icon.png +1 -0
  482. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/packages/desktop/src/renderer/assets/logos/brand/app.png +1 -0
  483. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/packages/desktop/src/renderer/assets/themes/default-theme.png +1 -0
  484. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/public/pwa/icon-192.png +1 -0
  485. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/resources/product.png +1 -0
  486. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-stray/apps/legacy/file.ts +1 -0
  487. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-stray/backend/crates/enact-session/src/backend/README.md +1 -0
  488. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-stray/frontend/unapproved/screenshot.png +1 -0
  489. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +230 -0
  490. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plan_metadata.py +53 -0
  491. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/__init__.py +7 -0
  492. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/check.py +438 -0
  493. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/AGENTS.md +1 -0
  494. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/items/az351-restore-entrypoint.md +9 -0
  495. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/spec.md +12 -0
  496. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/templates/feature.md +1 -0
  497. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-violation/plans/features/az347-test-coverage/items/az351-restore-entrypoint.md +9 -0
  498. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-violation/plans/features/az347-test-coverage/spec.md +12 -0
  499. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +461 -0
  500. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/__init__.py +11 -0
  501. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/check.py +62 -0
  502. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/NotAMigration.sql +1 -0
  503. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/0001_long_prefix.sql +1 -0
  504. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/001_init.sql +1 -0
  505. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/002_add_index.sql +1 -0
  506. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/003-dashed-slug.sql +1 -0
  507. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/004_MixedCase.sql +1 -0
  508. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/005_trailing space.sql +1 -0
  509. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/01_short_prefix.sql +1 -0
  510. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/not-a-migration.sql +1 -0
  511. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/test_check.py +116 -0
  512. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/__init__.py +11 -0
  513. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +188 -0
  514. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/.plans/c_done/26-07-27-history.md +3 -0
  515. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/README.md +8 -0
  516. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/links.md +20 -0
  517. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/route-target.md +8 -0
  518. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/.plans/c_done/26-07-27-history.md +3 -0
  519. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/README.md +8 -0
  520. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/links.md +20 -0
  521. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/route-target.md +8 -0
  522. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/test_check.py +166 -0
  523. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/__init__.py +11 -0
  524. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +135 -0
  525. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.agents/declared.txt +1 -0
  526. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.changeset/declared.txt +1 -0
  527. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.claude/declared.txt +1 -0
  528. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.code-review-graph/declared.txt +1 -0
  529. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.code-review-graph_old/declared.txt +1 -0
  530. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.codex/declared.txt +1 -0
  531. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.cursor/declared.txt +1 -0
  532. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.cursorrules +1 -0
  533. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.dockerignore +1 -0
  534. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.docs/declared.txt +1 -0
  535. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.donors/declared.txt +1 -0
  536. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.gitattributes +1 -0
  537. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.kimi-code/declared.txt +1 -0
  538. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.lfsconfig +1 -0
  539. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.mcp.json +1 -0
  540. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.opencode/declared.txt +1 -0
  541. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.workflows/declared.txt +1 -0
  542. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/AGENTS.md +1 -0
  543. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/CHANGELOG.md +1 -0
  544. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/CLAUDE-INVARIANTS.md +1 -0
  545. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/CLAUDE.md +1 -0
  546. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/INDEPENDENT-VERIFICATION.md +1 -0
  547. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/INVARIANTS.md +1 -0
  548. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/LICENSE +1 -0
  549. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/NOTICE +1 -0
  550. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/README.md +1 -0
  551. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/RELEASE.md +1 -0
  552. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/VERSION +1 -0
  553. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/backend/declared.txt +1 -0
  554. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/compliance/declared.txt +1 -0
  555. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/dev/declared.txt +1 -0
  556. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/docker/declared.txt +1 -0
  557. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/docs/declared.txt +1 -0
  558. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/frontend/declared.txt +1 -0
  559. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/infra/declared.txt +1 -0
  560. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/justfile +1 -0
  561. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/opencode.jsonc +1 -0
  562. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/rule-tests/undeclared.txt +1 -0
  563. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/rules/undeclared.txt +1 -0
  564. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/unapproved-root/undeclared.txt +1 -0
  565. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/undeclared-root-file.txt +1 -0
  566. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +122 -0
  567. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/__init__.py +12 -0
  568. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +290 -0
  569. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/.changeset/some-change.md +3 -0
  570. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/.plans/AGENTS.md +3 -0
  571. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/.specs/AGENTS.md +3 -0
  572. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/.workflows/AGENTS.md +3 -0
  573. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/AGENTS.md +3 -0
  574. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/CHANGELOG.md +3 -0
  575. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/CLAUDE.md +3 -0
  576. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/INDEPENDENT-VERIFICATION.md +3 -0
  577. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/INVARIANTS.md +3 -0
  578. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/README.md +3 -0
  579. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/RELEASE.md +3 -0
  580. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/STRAY.md +3 -0
  581. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/docs/AGENTS.md +3 -0
  582. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/docs/architecture/note.md +3 -0
  583. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/frontend/README.md +3 -0
  584. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-missing-guide/.workflows/AGENTS.md +1 -0
  585. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-missing-guide/AGENTS.md +1 -0
  586. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-missing-guide/README.md +1 -0
  587. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-missing-guide/docs/overview.md +1 -0
  588. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-missing-guide/plans/AGENTS.md +1 -0
  589. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +329 -0
  590. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/__init__.py +12 -0
  591. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +127 -0
  592. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/a/b/c/d/at-limit.sh +3 -0
  593. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/a/b/c/d/e/too-deep.sh +3 -0
  594. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-01.sh +3 -0
  595. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-02.sh +3 -0
  596. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-03.sh +3 -0
  597. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-04.sh +3 -0
  598. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-05.sh +3 -0
  599. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-06.sh +3 -0
  600. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-07.sh +3 -0
  601. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-08.sh +3 -0
  602. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-09.sh +3 -0
  603. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-10.sh +3 -0
  604. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-11.sh +3 -0
  605. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-12.sh +3 -0
  606. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-13.sh +3 -0
  607. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-14.sh +3 -0
  608. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-15.sh +3 -0
  609. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/fixtures/scripts-layout/.workflows/scripts/direct-16.sh +3 -0
  610. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +163 -0
  611. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/__init__.py +1 -0
  612. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/__init__.py +11 -0
  613. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +169 -0
  614. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/.gitattributes +1 -0
  615. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/large file.bin +3 -0
  616. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/.gitattributes +1 -0
  617. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/large file.bin +3 -0
  618. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/small.txt +1 -0
  619. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/missing-filter/large file.bin +0 -0
  620. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/.gitattributes +1 -0
  621. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/payload.dat +0 -0
  622. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +133 -0
  623. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/__init__.py +1 -0
  624. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/__init__.py +12 -0
  625. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/check.py +129 -0
  626. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/disjoint/gate-registry.yaml +13 -0
  627. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/empty/gate-registry.yaml +7 -0
  628. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/overlapping/gate-registry.yaml +13 -0
  629. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/test_check.py +109 -0
  630. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/__init__.py +1 -0
  631. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/__init__.py +11 -0
  632. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/check.py +149 -0
  633. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/clean/docs/readme.md +4 -0
  634. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/relevant/VERSION +1 -0
  635. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/test_check.py +151 -0
  636. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/__init__.py +11 -0
  637. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/check.py +202 -0
  638. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/changesets/package.json +5 -0
  639. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/package.json +5 -0
  640. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/CHANGELOG.md +5 -0
  641. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/VERSION +1 -0
  642. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/changesets/package.json +5 -0
  643. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/package.json +5 -0
  644. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/CHANGELOG.md +5 -0
  645. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/VERSION +1 -0
  646. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/test_check.py +162 -0
  647. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/__init__.py +1 -0
  648. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/__init__.py +12 -0
  649. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/check.py +103 -0
  650. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/compliance_py/checks/git-bad.sh +2 -0
  651. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/compliance_py/checks/git-note.sh +3 -0
  652. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/compliance_py/checks/good.sh +2 -0
  653. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/compliance_py/checks/grep-bad.sh +2 -0
  654. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/compliance_py/checks/grep-note.sh +3 -0
  655. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/compliance_py/checks/grep-trailing.sh +2 -0
  656. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/compliance_py/checks/notes.md +1 -0
  657. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/elsewhere/loose-git.sh +6 -0
  658. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/fixtures/no-banned-commands/elsewhere/loose-grep.sh +4 -0
  659. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/test_check.py +117 -0
  660. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/__init__.py +13 -0
  661. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/check.py +265 -0
  662. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/fixtures/ignored-paths/keep.log +1 -0
  663. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/fixtures/ignored-paths/src/main.rs +1 -0
  664. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/test_check.py +180 -0
  665. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/license_declared/__init__.py +12 -0
  666. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/license_declared/check.py +84 -0
  667. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/license_declared/fixtures/license-declared-clean/LICENSE +10 -0
  668. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/license_declared/fixtures/license-declared-mismatch/LICENSE +3 -0
  669. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/license_declared/fixtures/license-declared-missing/README.md +1 -0
  670. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/license_declared/test_check.py +66 -0
  671. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/__init__.py +11 -0
  672. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +167 -0
  673. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.lock +7 -0
  674. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.toml +4 -0
  675. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/rust-toolchain.toml +3 -0
  676. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/src/main.rs +1 -0
  677. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/frontend/bun.lock +1 -0
  678. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.lock +7 -0
  679. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.toml +4 -0
  680. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/rust-toolchain.toml +3 -0
  681. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/src/main.rs +1 -0
  682. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/frontend/bun.lock +0 -0
  683. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +109 -0
  684. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +483 -0
  685. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/__init__.py +1 -0
  686. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/ast_grep.py +199 -0
  687. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/batch.py +52 -0
  688. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +303 -0
  689. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +106 -0
  690. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/context.py +99 -0
  691. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/errors.py +32 -0
  692. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/finding.py +65 -0
  693. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/models.py +40 -0
  694. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/receipt.py +63 -0
  695. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/scope.py +82 -0
  696. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/verdict.py +161 -0
  697. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/__init__.py +1 -0
  698. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +131 -0
  699. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.toml +30 -0
  700. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/docs_shape.py +65 -0
  701. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +791 -0
  702. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.schema.yaml +176 -0
  703. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +791 -0
  704. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/grants-registry.yaml +112 -0
  705. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/grants.py +129 -0
  706. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +281 -0
  707. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +250 -0
  708. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/severity-policy.yaml +114 -0
  709. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/providers/__init__.py +11 -0
  710. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +371 -0
  711. package/packages/repo-controls/package/adl-compliance/uv.lock +199 -0
  712. package/packages/repo-controls/package.json +33 -0
  713. package/packages/repo-controls/scripts/hooks/commit-msg +63 -0
  714. package/packages/repo-controls/scripts/hooks/lib/hook-log.sh +62 -0
  715. package/packages/repo-controls/scripts/hooks/post-checkout +15 -0
  716. package/packages/repo-controls/scripts/hooks/post-commit +18 -0
  717. package/packages/repo-controls/scripts/hooks/pre-commit +82 -0
  718. package/packages/repo-controls/scripts/hooks/pre-push +144 -0
  719. package/packages/repo-controls/src/azdo-push.ts +463 -0
  720. package/packages/repo-controls/src/azure-devops-governance.ts +248 -0
  721. package/packages/repo-controls/src/azure-devops-remote.ts +61 -0
  722. package/packages/repo-controls/src/cli.ts +188 -0
  723. package/packages/repo-controls/src/compliance-config.ts +200 -0
  724. package/packages/repo-controls/src/config.ts +690 -0
  725. package/packages/repo-controls/src/constants.ts +97 -0
  726. package/packages/repo-controls/src/coverage-ratchet.ts +153 -0
  727. package/packages/repo-controls/src/doc-freshness.ts +172 -0
  728. package/packages/repo-controls/src/doctor.ts +304 -0
  729. package/packages/repo-controls/src/findings-ratchet.ts +163 -0
  730. package/packages/repo-controls/src/gitignore-block.ts +253 -0
  731. package/packages/repo-controls/src/hook-log.ts +72 -0
  732. package/packages/repo-controls/src/hook.ts +694 -0
  733. package/packages/repo-controls/src/install.ts +506 -0
  734. package/packages/repo-controls/src/integrity.ts +104 -0
  735. package/packages/repo-controls/src/promote.ts +25 -0
  736. package/packages/repo-controls/src/runtime.ts +110 -0
  737. package/packages/repo-controls/src/workflows-drift.ts +106 -0
  738. package/packages/repo-controls/src/worktree-guard.ts +272 -0
  739. package/packages/repo-controls/src/worktree-limit.ts +11 -0
  740. package/packages/repo-controls/tsconfig.json +14 -0
  741. package/packages/repo-controls/workflows/scripts/release/verify-npm-artifact.ts +119 -0
@@ -0,0 +1,1415 @@
1
+ /**
2
+ * Carrying the session identity (`ADL_AGENT_ID_AGENT`) into every network git
3
+ * invocation of a command-executing tool call.
4
+ *
5
+ * Live gap this closes (2026-09-28, codex session 01a0e611): an
6
+ * `adl run -- codex` session ran `git push -u origin feat/adl-m3-finish`
7
+ * through the lean-ctx MCP tool `mcp__lean_ctx__ctx_shell`; Codex gives
8
+ * `ADL_AGENT_ID_AGENT` only to its own shell tool, so the push executed with
9
+ * no identity and `bin/git-ado`'s HUMAN pass-through (bin/git-ado:191-194)
10
+ * pushed with Tarun's own credential helper. The hook process had the
11
+ * identity all along — it arrives in the hook's own environment, which `adl
12
+ * run` sets — but had no way to put it on the command. This scanner is that
13
+ * way: `git push …` becomes `ADL_AGENT_ID_AGENT=agent-tarun git push …`, and
14
+ * the pre-tool-use handler returns the rewritten command as the host's native
15
+ * "replace the tool input" envelope (`updatedInput` on Claude/Codex,
16
+ * `updated_input` on Cursor). On hosts with no such envelope the handler
17
+ * denies instead — see impl/adl-git-identity.ts.
18
+ *
19
+ * The verb list (`NETWORK_GIT_VERBS`) started as exactly `bin/git-ado`'s
20
+ * credential path (`fetch|pull|clone|ls-remote|push`, bin/git-ado:159-165):
21
+ * those are the commands for which a missing identity silently becomes the
22
+ * human. F2 (independent review, 2026-09-29) widened what this file (and
23
+ * `impl/adl-session-required.ts`'s deny side) actually TREATS as network —
24
+ * see `isNetworkGitInvocation`'s own doc below for the additional shapes
25
+ * (`git remote show|prune|update`/`set-head -a`, `git maintenance run
26
+ * --task=prefetch` specifically — not a bare `git maintenance run`, aligned
27
+ * with `bin/git-ado`'s own narrower routing there, another executor's
28
+ * surface — the transport helpers, `git submodule update|sync --recursive`,
29
+ * `git archive --remote`) — this claim about the bare five-verb
30
+ * `NETWORK_GIT_VERBS` set is no longer the WHOLE story for what this file
31
+ * classifies as network, only for what `bin/git-ado`'s OWN credential path
32
+ * covers today. Reads and local operations (`status`/`log`/`diff`/…) never
33
+ * touch git-ado's identity branch and are deliberately left untouched,
34
+ * idempotency included — a command already prefixed with the SAME identity
35
+ * is returned unchanged, and one prefixed with a DIFFERENT (or emptied)
36
+ * identity is a deny, never a
37
+ * rewrite, because the override would either spoof attribution or launder the
38
+ * command back onto the human path.
39
+ *
40
+ * The scanner is offset-aware and mirrors the shell front end's semantics
41
+ * (`./split.ts`, `./token.ts`, `./prefixes.ts`) for the constructs that
42
+ * matter: quotes, backslash escapes, `$(…)`/`${…}`/backtick/`<(…)` spans,
43
+ * `#` comments, IO operators, `&&`/`||`/`;`/`|`/`&`/newline/paren/brace
44
+ * separators, leading assignments, wrapper commands and git's own global
45
+ * flags. Nested `bash -c '<script>'` script strings are scanned one level at
46
+ * a time (depth-capped), the same shapes `./invocation.ts` walks for the deny
47
+ * rules; a script FILE operand (`sh deploy.sh`) is not on this command line
48
+ * and is not inspected. `sudo`/`doas` are denied rather than rewritten:
49
+ * they strip the environment, so a prefixed identity would silently not
50
+ * survive to git.
51
+ *
52
+ * The identity is validated against `^[A-Za-z0-9_-]+$` before it ever enters
53
+ * shell text — an identity outside that set means the session itself is
54
+ * misconfigured, and injecting it would be code execution.
55
+ */
56
+
57
+ import { findMatchingBacktick, findMatchingParen } from './substitution.ts'
58
+ import { GIT_GLOBAL_EQ, GIT_GLOBAL_TAKES_ARG, GIT_TERMINAL_GLOBALS } from './prefixes.ts'
59
+ import { ADL_AGENT_ID_ENV_VAR, isValidAgent } from '../impl/adl-identity.ts'
60
+ import {
61
+ ADL_AGENT_ID_BINARY_NAMES,
62
+ ADL_AGENT_ID_COMMAND_SHAPES,
63
+ ADL_BINARY_NAMES,
64
+ ADL_IDENTITY_COMMAND_SHAPES,
65
+ HTTP_CLIENT_BINARY_NAMES,
66
+ isAdoUrl,
67
+ matchesIdentityShape,
68
+ skipLeadingDashFlags,
69
+ } from '../impl/git-helpers.ts'
70
+
71
+ /**
72
+ * The five network verbs `bin/git-ado` routes through the agent identity /
73
+ * human pass-through decision. Only these ever need the identity on the
74
+ * command line.
75
+ */
76
+ export const NETWORK_GIT_VERBS: ReadonlySet<string> = new Set(['fetch', 'pull', 'clone', 'ls-remote', 'push'])
77
+
78
+ /**
79
+ * F2 (independent review, 2026-09-29): network-reaching git shapes the
80
+ * walker missed entirely — subcommands other than the five `NETWORK_GIT_VERBS`
81
+ * above, or ones where only a SPECIFIC sub-action/flag combination reaches
82
+ * the network (an innocuous sibling form of the same subcommand must NOT be
83
+ * swept in): `git remote show|prune|update <remote>` (each contacts the
84
+ * remote; `git remote -v`/`add`/`rename`/`remove` never do), `git remote
85
+ * set-head <r> -a` (auto-detects HEAD over the network; `set-head <r>
86
+ * <branch>` with an explicit branch does not), `git maintenance run
87
+ * --task=prefetch` (fetches every registered remote in the background;
88
+ * `--task=gc`/`commit-graph`/… do not), the internal transport helpers
89
+ * `git remote-https`/`git remote-http`/`git http-fetch` (network by
90
+ * construction when invoked directly), `git submodule update|sync
91
+ * --recursive` (recurses into and can fetch nested submodule remotes;
92
+ * without `--recursive` it only touches the top-level submodules already
93
+ * configured), and `git archive --remote[=<repo>]` (fetches the archive
94
+ * data from a remote repository over the git protocol; a bare `git archive`
95
+ * only reads the local tree). `git fetch` (and the other four) via a leading
96
+ * `git -c ...` global is already covered without any change here —
97
+ * `scanGitGlobals`/`scanGitGlobalsValues` (`./prefixes.ts`) already skip
98
+ * every global flag, `-c` included, before either call site below ever
99
+ * inspects the subcommand word.
100
+ *
101
+ * Used by BOTH sides `adl.session-required-for-writes`'s own header names
102
+ * ("deny with no identity / rewrite with one"): `identityBearingReason`
103
+ * (`impl/adl-session-required.ts`) for the deny side, and the REWRITE
104
+ * scanner's own `NETWORK_GIT_VERBS` gate below for the rewrite side — one
105
+ * shared predicate, not two independently-maintained shape lists.
106
+ */
107
+ const REMOTE_NETWORK_ACTIONS: ReadonlySet<string> = new Set(['show', 'prune', 'update'])
108
+ const SUBMODULE_RECURSIVE_ACTIONS: ReadonlySet<string> = new Set(['update', 'sync'])
109
+ const REMOTE_TRANSPORT_HELPERS: ReadonlySet<string> = new Set(['remote-https', 'remote-http', 'http-fetch'])
110
+
111
+ function firstNonOptionValue(values: readonly string[]): string | undefined {
112
+ return values.find((value) => !value.startsWith('-'))
113
+ }
114
+
115
+ export function isNetworkGitInvocation(subcommand: string, restValues: readonly string[]): boolean {
116
+ if (NETWORK_GIT_VERBS.has(subcommand)) return true
117
+ if (REMOTE_TRANSPORT_HELPERS.has(subcommand)) return true
118
+ if (subcommand === 'remote') {
119
+ const action = firstNonOptionValue(restValues)?.toLowerCase()
120
+ if (action === undefined) return false
121
+ if (REMOTE_NETWORK_ACTIONS.has(action)) return true
122
+ if (action === 'set-head') return restValues.some((value) => value === '-a' || value === '--auto')
123
+ return false
124
+ }
125
+ if (subcommand === 'maintenance') {
126
+ const action = firstNonOptionValue(restValues)?.toLowerCase()
127
+ if (action !== 'run') return false
128
+ return restValues.some((value, index) => value === '--task=prefetch' || (value === '--task' && restValues[index + 1] === 'prefetch'))
129
+ }
130
+ if (subcommand === 'submodule') {
131
+ const action = firstNonOptionValue(restValues)?.toLowerCase()
132
+ if (action === undefined || !SUBMODULE_RECURSIVE_ACTIONS.has(action)) return false
133
+ // P2 (independent review, 2026-09-29): `--init` clones every submodule
134
+ // not yet checked out — network, same as `--recursive` recursing into
135
+ // nested ones.
136
+ return restValues.some((value) => value === '--recursive' || value === '--init')
137
+ }
138
+ if (subcommand === 'archive') {
139
+ return restValues.some((value) => value === '--remote' || value.startsWith('--remote='))
140
+ }
141
+ return false
142
+ }
143
+
144
+ const SAFE_IDENTITY = /^[A-Za-z0-9_-]+$/
145
+
146
+ /** Shells whose `-c`/`--command` script string is re-scanned one level deeper (mirrors invocation.ts's shellNames). */
147
+ const SHELL_NAMES: ReadonlySet<string> = new Set(['sh', 'bash', 'zsh', 'dash', 'ksh', 'fish'])
148
+
149
+ const MAX_NESTED_SCRIPT_DEPTH = 16
150
+
151
+ /**
152
+ * `identity-mismatch` covers BOTH shapes the standard treats the same way
153
+ * (STATUS.md 12p, "Decision per identity-bearing
154
+ * shell command"): an inline `ADL_AGENT_ID_AGENT=<value>` that is empty
155
+ * (would launder the command back onto git-ado's human pass-through) or
156
+ * names an agent outside `VALID_AGENTS`. A VALID inline value — even one
157
+ * that differs from the resolved fallback — is no longer a conflict at all:
158
+ * the override wins and the command is left unchanged (`kind: 'unchanged'`).
159
+ * See `applyIdentityOutcome` below.
160
+ */
161
+ /**
162
+ * `network-alias-redefinition` (H2, STATUS.md 12p):
163
+ * a self-referential `git -c alias.<name>=<value>`/`--config-env[=]
164
+ * alias.<name>=<value>` where `<name>` is the literal subcommand this
165
+ * invocation runs and `<value>`'s own verb is network/credential-bearing —
166
+ * see `networkAliasRedefinition`'s own doc.
167
+ */
168
+ /**
169
+ * `dynamic-agent` (M5, STATUS.md 12p): `adl id
170
+ * token <agent>`/`adl-agent-id exec --agent <agent>` whose agent argument
171
+ * is itself dynamic ($-expansion, backtick, or a glob character) — denied
172
+ * outright rather than silently treated as "not identity-bearing", since a
173
+ * dynamically computed agent name could name ANY agent at runtime, evading
174
+ * `adl.token-minting-role`'s static "own identity only" check entirely.
175
+ */
176
+ export type GitIdentityDenyReason =
177
+ | 'identity-mismatch'
178
+ | 'unrewritable-wrapper'
179
+ | 'unsafe-identity'
180
+ | 'network-alias-redefinition'
181
+ | 'dynamic-agent'
182
+
183
+ export type GitIdentityScan =
184
+ | { readonly kind: 'rewrite'; readonly command: string }
185
+ | { readonly kind: 'unchanged' }
186
+ /**
187
+ * An identity-bearing simple command was found, it carries no (valid)
188
+ * inline override, and no fallback identity was resolved (the `fallback`
189
+ * argument was `null`). Nothing was rewritten or denied here — this is a
190
+ * signal for the CALLER (`adl-session-required.ts`) to deny with the
191
+ * standard's own message; this scanner's job is only to rewrite/detect
192
+ * conflicts once an identity — inline or fallback — actually exists.
193
+ */
194
+ | { readonly kind: 'needs-identity' }
195
+ | { readonly kind: 'deny'; readonly reason: GitIdentityDenyReason }
196
+
197
+ /** One scanned word: `value` is the dequoted shell word (token.ts semantics), `start`/`end` its raw span (end exclusive) in the original command. */
198
+ interface Word {
199
+ readonly value: string
200
+ readonly start: number
201
+ readonly end: number
202
+ readonly operator: boolean
203
+ }
204
+
205
+ interface Piece {
206
+ readonly words: readonly Word[]
207
+ }
208
+
209
+ /** Longest-match-first IO operators, so `>&` wins over `>` and `&>>` over `&>`. `|`/`&` alone are separators, never operators. */
210
+ const IO_OPERATORS = ['&>>', '<<<', '<<-', '&>', '>&', '>|', '<&', '>>', '<<', '<>', '<', '>'] as const
211
+
212
+ /**
213
+ * Longest IO operator at `text[index]`, or the bare character when none of
214
+ * the table matches. Every scanPieces call site guarantees `<`, `>`, `&>` or
215
+ * `&>>`, so the fallback is a defensive contract pin, exercised directly in
216
+ * tests/unit/shell-git-identity.test.ts.
217
+ */
218
+ export function matchIoOperator(text: string, index: number): string {
219
+ for (const op of IO_OPERATORS) {
220
+ if (text.startsWith(op, index)) return op
221
+ }
222
+ return text[index] as string
223
+ }
224
+
225
+ /** End index (inclusive) of a `$(…)`, `${…}`, backtick or `<(`/`>(` span starting at `index`, or null — port of token.ts's expansionEnd. */
226
+ function expansionEnd(text: string, index: number): number | null {
227
+ const char = text[index]
228
+ const next = text[index + 1]
229
+ if ((char === '$' || char === '<' || char === '>') && next === '(') {
230
+ const close = findMatchingParen(text, index + 1)
231
+ return close < 0 ? text.length - 1 : close
232
+ }
233
+ if (char === '$' && next === '{') {
234
+ const close = text.indexOf('}', index + 2)
235
+ return close < 0 ? text.length - 1 : close
236
+ }
237
+ if (char === '`') {
238
+ const close = findMatchingBacktick(text, index)
239
+ return close < 0 ? text.length - 1 : close
240
+ }
241
+ return null
242
+ }
243
+
244
+ function isAssignmentValue(value: string): boolean {
245
+ return /^[A-Za-z_][A-Za-z0-9_]*=/.test(value)
246
+ }
247
+
248
+ /** Lower-cased final path segment of a word value — port of token.ts's basename. */
249
+ function basenameValue(value: string): string {
250
+ const segments = value.split(/[/\\]/)
251
+ return (segments[segments.length - 1] ?? '').toLowerCase()
252
+ }
253
+
254
+ /** Port of token.ts's dynamicToken, on dequoted values. */
255
+ function dynamicValue(value: string): boolean {
256
+ return value.includes('$') || value.includes('`') || /[*?]/.test(value)
257
+ }
258
+
259
+ /** Port of git-helpers.ts's isHelpOnlyInvocation, on dequoted values. */
260
+ function valuesHelpOnly(values: readonly string[]): boolean {
261
+ const flags = new Set<string>()
262
+ for (const arg of values) {
263
+ if (arg === '--') break
264
+ if (arg === '--help' || arg === '--version') flags.add('help')
265
+ if (arg === '-h' && values.length === 1) flags.add('help')
266
+ }
267
+ return flags.has('help') && values.every((arg) => arg.startsWith('-') && arg !== '--')
268
+ }
269
+
270
+ /** The first `http(s)://` value among `values` — port of git-helpers.ts's `classifyGitPushDestination`'s private `firstUrl`, on dequoted string values instead of Tokens. */
271
+ function firstHttpUrl(values: readonly string[]): string | null {
272
+ for (const value of values) {
273
+ if (/^https?:\/\//i.test(value)) return value
274
+ }
275
+ return null
276
+ }
277
+
278
+ /**
279
+ * Split `text` (a command, or a nested script string) into pieces at unquoted
280
+ * `&&`/`||`/`;`/`|`/`&`/newlines and group boundaries, tokenizing each piece
281
+ * into words with raw source spans offset by `base` in the original command.
282
+ * Mirrors split.ts's structure handling (quote-aware, comment-aware,
283
+ * substitution-opaque, `>&`/`&>` folding) closely enough for the git-identity
284
+ * analysis; it does not need split.ts's pipe feeds.
285
+ */
286
+ function scanPieces(text: string, base: number, pieces: Piece[]): void {
287
+ const words: Word[] = []
288
+ let value = ''
289
+ let start = -1
290
+ let quote: '"' | "'" | null = null
291
+ let wordStart = true
292
+
293
+ const flush = (endLocal: number): void => {
294
+ if (start >= 0) {
295
+ words.push({ value, start: base + start, end: base + endLocal, operator: false })
296
+ value = ''
297
+ start = -1
298
+ }
299
+ }
300
+ const endPiece = (endLocal: number): void => {
301
+ flush(endLocal)
302
+ if (words.length > 0) pieces.push({ words: [...words] })
303
+ words.length = 0
304
+ }
305
+
306
+ for (let index = 0; index < text.length; index += 1) {
307
+ const char = text[index] as string
308
+
309
+ if (quote === "'") {
310
+ if (char === "'") quote = null
311
+ else value += char
312
+ continue
313
+ }
314
+ if (char === '\\') {
315
+ // Unquoted or inside double quotes (token.ts escapes any next char in both).
316
+ if (start < 0) start = index
317
+ if (index + 1 >= text.length) return // unterminated escape: leave the partial word unflushed; the command fails closed upstream
318
+ value += text[index + 1]
319
+ index += 1
320
+ wordStart = false
321
+ continue
322
+ }
323
+ if (quote === '"') {
324
+ if (char === '"') {
325
+ quote = null
326
+ continue
327
+ }
328
+ const end = char === '$' || char === '`' ? expansionEnd(text, index) : null
329
+ if (end !== null) {
330
+ if (start < 0) start = index
331
+ value += text.slice(index, end + 1)
332
+ index = end
333
+ continue
334
+ }
335
+ value += char
336
+ continue
337
+ }
338
+ if (char === "'" || char === '"') {
339
+ if (start < 0) start = index
340
+ quote = char
341
+ wordStart = false
342
+ continue
343
+ }
344
+
345
+ const expansion = expansionEnd(text, index)
346
+ if (expansion !== null) {
347
+ if (start < 0) start = index
348
+ value += text.slice(index, expansion + 1)
349
+ index = expansion
350
+ wordStart = false
351
+ continue
352
+ }
353
+
354
+ if (char === '\n' || char === ';') {
355
+ flush(index)
356
+ endPiece(index)
357
+ wordStart = true
358
+ continue
359
+ }
360
+
361
+ if (/\s/.test(char)) {
362
+ flush(index)
363
+ wordStart = true
364
+ continue
365
+ }
366
+
367
+ if (char === '#' && wordStart) {
368
+ flush(index)
369
+ let end = index
370
+ while (end + 1 < text.length && text[end + 1] !== '\n') end += 1
371
+ index = end
372
+ wordStart = true
373
+ continue
374
+ }
375
+
376
+ if (char === '&') {
377
+ if (text[index + 1] === '&') {
378
+ flush(index)
379
+ endPiece(index)
380
+ index += 1
381
+ wordStart = true
382
+ continue
383
+ }
384
+ if (text[index + 1] === '>') {
385
+ flush(index)
386
+ const op = matchIoOperator(text, index)
387
+ words.push({ value: op, start: base + index, end: base + index + op.length, operator: true })
388
+ index += op.length - 1
389
+ wordStart = false
390
+ continue
391
+ }
392
+ // A bare `&` backgrounds the preceding command.
393
+ flush(index)
394
+ endPiece(index)
395
+ wordStart = true
396
+ continue
397
+ }
398
+
399
+ if (char === '|') {
400
+ if (text[index + 1] === '|') {
401
+ flush(index)
402
+ endPiece(index)
403
+ index += 1
404
+ } else {
405
+ flush(index)
406
+ endPiece(index)
407
+ }
408
+ wordStart = true
409
+ continue
410
+ }
411
+
412
+ const groupBrace =
413
+ (char === '{' && (index + 1 >= text.length || /\s/.test(text[index + 1] as string))) ||
414
+ (char === '}' && (index === 0 || /[\s;]/.test(text[index - 1] as string)))
415
+ if (char === '(' || char === ')' || groupBrace) {
416
+ flush(index)
417
+ endPiece(index)
418
+ wordStart = true
419
+ continue
420
+ }
421
+
422
+ if (char === '<' || char === '>') {
423
+ flush(index)
424
+ const op = matchIoOperator(text, index)
425
+ words.push({ value: op, start: base + index, end: base + index + op.length, operator: true })
426
+ index += op.length - 1
427
+ wordStart = false
428
+ continue
429
+ }
430
+
431
+ if (start < 0) start = index
432
+ value += char
433
+ wordStart = false
434
+ }
435
+
436
+ endPiece(text.length)
437
+ }
438
+
439
+ /** Port of prefixes.ts's skipDashOptions, on words (operators end the option run). */
440
+ function skipDashOptions(words: readonly Word[], start: number, valueOptions: readonly string[], stopAtDoubleDash: boolean): number {
441
+ let index = start
442
+ while (index < words.length) {
443
+ const word = words[index] as Word
444
+ if (word.operator) break
445
+ const value = word.value
446
+ if (stopAtDoubleDash && value === '--') return index + 1
447
+ if (!value.startsWith('-') || value === '-') break
448
+ index += valueOptions.includes(value) ? 2 : 1
449
+ }
450
+ return index
451
+ }
452
+
453
+ /** Port of prefixes.ts's skipWrapperFlags, on words. */
454
+ function skipWrapperFlagArgs(words: readonly Word[], start: number, withValue: readonly string[]): number {
455
+ let index = start
456
+ while (index < words.length) {
457
+ const word = words[index] as Word
458
+ if (word.operator) break
459
+ const value = word.value
460
+ if (value === '--') return index + 1
461
+ if (withValue.includes(value)) {
462
+ index += 2
463
+ continue
464
+ }
465
+ if (value.startsWith('-') && value !== '-') {
466
+ index += 1
467
+ continue
468
+ }
469
+ break
470
+ }
471
+ return index
472
+ }
473
+
474
+ /** One wrapper's flag scan: the index past its own flags, and whether it strips the environment (H1). */
475
+ interface WrapperScan {
476
+ readonly index: number
477
+ readonly strips: boolean
478
+ }
479
+
480
+ /**
481
+ * Port of prefixes.ts's skipEnvFlags, on words; collects the env command's
482
+ * `NAME=value` arguments for identity-conflict checks, and reports whether
483
+ * `-u`/`--unset[=NAME]` or `-i`/`--ignore-environment` were used (H1).
484
+ *
485
+ * `-u`/`--unset` denies regardless of WHICH name it names — not only
486
+ * `ADL_AGENT_ID_AGENT` itself — the same blanket treatment `sudo`/`doas` get
487
+ * below even though `sudo -E` would actually preserve the environment: this
488
+ * scanner does not try to prove a specific unset is safe, it treats the
489
+ * FLAG as the risk, matching the existing sudo/doas precedent rather than
490
+ * adding a second, narrower standard only env gets.
491
+ */
492
+ function skipEnvArgs(words: readonly Word[], start: number, assignments: string[]): WrapperScan {
493
+ let index = start
494
+ let strips = false
495
+ while (index < words.length) {
496
+ const word = words[index] as Word
497
+ if (word.operator) break
498
+ const value = word.value
499
+ if (value === '--') return { index: index + 1, strips }
500
+ if (value === '-u' || value === '--unset') {
501
+ strips = true
502
+ index += 2
503
+ continue
504
+ }
505
+ if (value === '-i' || value === '--ignore-environment') {
506
+ strips = true
507
+ index += 1
508
+ continue
509
+ }
510
+ if (['-C', '--chdir', '-S', '--split-string'].includes(value)) {
511
+ index += 2
512
+ continue
513
+ }
514
+ if (value.startsWith('--unset=')) {
515
+ strips = true
516
+ index += 1
517
+ continue
518
+ }
519
+ if (value.startsWith('--chdir=') || value.startsWith('-') || value.includes('=')) {
520
+ if (isAssignmentValue(value)) assignments.push(value)
521
+ index += 1
522
+ continue
523
+ }
524
+ break
525
+ }
526
+ return { index, strips }
527
+ }
528
+
529
+ /**
530
+ * `exec`'s `-c`/`-l` flags run the wrapped command with an empty environment
531
+ * (`-c`) or as a login shell that can re-source profiles which reset it
532
+ * (`-l`) — H1 treats both as environment-stripping, like sudo/doas. `-a
533
+ * name` (argv0 only) never strips and is skipped transparently. Bash's own
534
+ * `exec` builtin accepts these bundled (`-cl`, `-la name`, …); any bundle
535
+ * containing `c` or `l` is treated as stripping, fail-closed, rather than
536
+ * trying to prove a particular bundling is safe.
537
+ */
538
+ function skipExecArgs(words: readonly Word[], start: number): WrapperScan {
539
+ let index = start
540
+ let strips = false
541
+ for (;;) {
542
+ const word = words[index]
543
+ if (word === undefined || word.operator) break
544
+ const value = word.value
545
+ if (value === '--') {
546
+ index += 1
547
+ break
548
+ }
549
+ if (/^-[acl]+$/.test(value)) {
550
+ if (value.includes('c') || value.includes('l')) strips = true
551
+ index += value.includes('a') ? 2 : 1
552
+ continue
553
+ }
554
+ break
555
+ }
556
+ return { index, strips }
557
+ }
558
+
559
+ /** Port of prefixes.ts's skipCommandFlags + skipRepeatedTokens(['--','-p']). */
560
+ function skipCommandArgs(words: readonly Word[], start: number): number {
561
+ const first = words[start]
562
+ if (first !== undefined && !first.operator && ['-v', '-V'].includes(first.value)) return words.length
563
+ let index = start
564
+ while (index < words.length) {
565
+ const word = words[index] as Word
566
+ if (word.operator || !['--', '-p'].includes(word.value)) break
567
+ index += 1
568
+ }
569
+ return index
570
+ }
571
+
572
+ /** Port of prefixes.ts's skipTimeoutFlags (wrapper flags, then the one duration operand). */
573
+ function skipTimeoutArgs(words: readonly Word[], start: number): number {
574
+ const index = skipWrapperFlagArgs(words, start, ['-s', '--signal', '-k', '--kill-after'])
575
+ return index < words.length ? index + 1 : index
576
+ }
577
+
578
+ /**
579
+ * Wrapper commands whose environment passes through to the wrapped command —
580
+ * port of prefixes.ts's COMMAND_PREFIXES/PREFIX_HANDLERS. Each value skips the
581
+ * wrapper's own flags/operands and lands on the wrapped command; none of
582
+ * these ever strip the environment. `env` and `exec` are NOT here — H1
583
+ * treats their environment-stripping flags (`env -u`/`--unset`/`-i`, `exec
584
+ * -c`/`-l`) like sudo/doas, so `analyzePiece` special-cases them the same
585
+ * way it special-cases sudo/doas, rather than folding a `strips` result into
586
+ * every entry in this map for the sake of two wrappers that need it.
587
+ */
588
+ const WRAPPER_SKIP: Readonly<Record<string, (words: readonly Word[], start: number, assignments: string[]) => number>> = {
589
+ command: (words, start) => skipCommandArgs(words, start),
590
+ nice: (words, start) => skipDashOptions(words, start, ['-n', '--adjustment'], false),
591
+ time: (words, start) => skipDashOptions(words, start, ['-o', '--output'], false),
592
+ timeout: skipTimeoutArgs,
593
+ gtimeout: skipTimeoutArgs,
594
+ stdbuf: (words, start) => skipWrapperFlagArgs(words, start, ['-i', '-o', '-e']),
595
+ caffeinate: (words, start) => skipWrapperFlagArgs(words, start, ['-t', '-w']),
596
+ arch: (words, start) => skipWrapperFlagArgs(words, start, ['-arch']),
597
+ setsid: (words, start) => skipWrapperFlagArgs(words, start, []),
598
+ nohup: (words, start) => skipDashOptions(words, start, [], true),
599
+ }
600
+
601
+ /** Port of prefixes.ts's scanGitGlobals, on words; operators end the global scan. */
602
+ function scanGitGlobalsValues(words: readonly Word[], start: number): { readonly index: number; readonly terminal: boolean } {
603
+ let index = start
604
+ while (index < words.length) {
605
+ const word = words[index] as Word
606
+ if (word.operator) break
607
+ const value = word.value
608
+ if (GIT_TERMINAL_GLOBALS.has(value) || value.startsWith('--list-cmds=')) return { index, terminal: true }
609
+ if (GIT_GLOBAL_TAKES_ARG.has(value)) {
610
+ index += 2
611
+ continue
612
+ }
613
+ if (GIT_GLOBAL_EQ.some((prefix) => value.startsWith(prefix)) || (value.startsWith('-c') && value.includes('=') && value !== '-c')) {
614
+ index += 1
615
+ continue
616
+ }
617
+ if (value === '--') {
618
+ index += 1
619
+ break
620
+ }
621
+ if (value.startsWith('-') && value !== '-') {
622
+ index += 1
623
+ continue
624
+ }
625
+ break
626
+ }
627
+ return { index, terminal: false }
628
+ }
629
+
630
+ const CREDENTIAL_HELPER_VERBS: ReadonlySet<string> = new Set(['fill', 'approve', 'reject'])
631
+ const SUBMODULE_NETWORK_VERBS: ReadonlySet<string> = new Set(['update', 'sync', 'foreach'])
632
+
633
+ /**
634
+ * The alias VALUE's own verb — split on whitespace, a leading `!` (a shell
635
+ * alias, e.g. `!git push origin main`) stripped first and, when what
636
+ * follows still starts with `git`/`git.exe`, that word skipped too so
637
+ * `!git push …` reads as verb `push`, not `git`. A fully arbitrary shell
638
+ * one-liner alias (`!curl … | sh`) is not further inspected here — that is
639
+ * `shell.remote-script-exec`'s/`parse.uncertain`'s concern, not this one's;
640
+ * this only recognizes the exact network/credential VERB shapes H2 names.
641
+ */
642
+ export function networkAliasVerb(aliasValue: string): boolean {
643
+ const stripped = aliasValue.startsWith('!') ? aliasValue.slice(1).trim() : aliasValue
644
+ const words = stripped.split(/\s+/).filter((word) => word.length > 0)
645
+ const start = words[0]?.toLowerCase() === 'git' || words[0]?.toLowerCase() === 'git.exe' ? 1 : 0
646
+ const first = words[start]?.toLowerCase()
647
+ if (first === undefined) return false
648
+ const second = words[start + 1]?.toLowerCase()
649
+ if (NETWORK_GIT_VERBS.has(first)) return true
650
+ if (first === 'remote' && second === 'update') return true
651
+ if (first === 'submodule' && second !== undefined && SUBMODULE_NETWORK_VERBS.has(second)) return true
652
+ if (first === 'credential' && second !== undefined && CREDENTIAL_HELPER_VERBS.has(second)) return true
653
+ return false
654
+ }
655
+
656
+ /**
657
+ * True when `globalWords` (a git invocation's `-c`/`--config-env` globals,
658
+ * BEFORE alias resolution) define `alias.<subcommand>` — SELF-referential:
659
+ * the literal word this invocation actually runs — to a value whose own
660
+ * verb is network/credential-bearing (`networkAliasVerb`). See H2's comment
661
+ * at this function's one call site for why this is narrower than a blanket
662
+ * `-c alias.*` deny.
663
+ */
664
+ function networkAliasRedefinition(globalWords: readonly Word[], subcommand: string): boolean {
665
+ const targetKey = `alias.${subcommand}`
666
+ for (let index = 0; index < globalWords.length; index += 1) {
667
+ const word = globalWords[index] as Word
668
+ if (word.operator) continue
669
+ const value = word.value
670
+ let setting: string | null = null
671
+ if (value === '-c' || value === '--config-env') {
672
+ const next = globalWords[index + 1]
673
+ if (next !== undefined && !next.operator) setting = next.value
674
+ index += 1
675
+ } else if (value.startsWith('-c') && value.includes('=') && value !== '-c') {
676
+ setting = value.slice(2)
677
+ } else if (value.startsWith('--config-env=')) {
678
+ setting = value.slice('--config-env='.length)
679
+ }
680
+ if (setting === null) continue
681
+ const equals = setting.indexOf('=')
682
+ if (equals < 0) continue
683
+ if (setting.slice(0, equals).toLowerCase() !== targetKey) continue
684
+ if (networkAliasVerb(setting.slice(equals + 1).trim())) return true
685
+ }
686
+ return false
687
+ }
688
+
689
+ /**
690
+ * R4 (STATUS.md 12p, re-review 2026-09-28 night): the
691
+ * ENV-VAR-based sibling of `networkAliasRedefinition` — `GIT_CONFIG_COUNT`/
692
+ * `GIT_CONFIG_KEY_<n>`/`GIT_CONFIG_VALUE_<n>` are git's OWN alternative to
693
+ * `-c`/`--config-env` for supplying ad-hoc config, read the same way
694
+ * `../shell/invocation.ts`'s `environmentGitAlias` already reads them for
695
+ * CLASSIFICATION (so this repo's other rules see the alias's resolved
696
+ * verb) — but classification alone throws away the fact that an alias was
697
+ * used at all once resolved, which is exactly what this SELF-referential
698
+ * check needs to catch: `bin/git-ado` routes on the literal argv verb, so
699
+ * `GIT_CONFIG_KEY_0=alias.p GIT_CONFIG_VALUE_0=push git p` never reaches its
700
+ * identity routing at all, the same live gap `networkAliasRedefinition`
701
+ * closes for the `-c`/`--config-env` spelling.
702
+ */
703
+ function environmentAliasRedefinition(assignments: readonly string[], subcommand: string): boolean {
704
+ const targetKey = `alias.${subcommand}`
705
+ let count: number | null = null
706
+ const keys = new Map<number, string>()
707
+ const values = new Map<number, string>()
708
+ for (const assignment of assignments) {
709
+ const equals = assignment.indexOf('=')
710
+ if (equals < 0) continue
711
+ const name = assignment.slice(0, equals)
712
+ const value = assignment.slice(equals + 1)
713
+ if (name === 'GIT_CONFIG_COUNT') count = /^\d+$/.test(value) ? Number(value) : null
714
+ const keyMatch = /^GIT_CONFIG_KEY_(\d+)$/.exec(name)
715
+ if (keyMatch !== null) keys.set(Number(keyMatch[1]), value)
716
+ const valueMatch = /^GIT_CONFIG_VALUE_(\d+)$/.exec(name)
717
+ if (valueMatch !== null) values.set(Number(valueMatch[1]), value)
718
+ }
719
+ if (count === null || !Number.isSafeInteger(count)) return false
720
+ for (const index of [...keys.keys()].filter((candidate) => candidate < count)) {
721
+ if (keys.get(index)?.toLowerCase() !== targetKey) continue
722
+ const value = values.get(index)
723
+ if (value !== undefined && networkAliasVerb(value)) return true
724
+ }
725
+ return false
726
+ }
727
+
728
+ /** `--global`/`--local`/`--system`/`--worktree`/`--file <path>` etc. ahead of `git config`'s own positional key/value — skipped so `git config --global alias.p push` still finds `alias.p` as the key. */
729
+ function skipGitConfigScopeFlags(words: readonly Word[], start: number): number {
730
+ const VALUE_FLAGS = new Set(['--file', '-f', '--blob'])
731
+ let index = start
732
+ while (index < words.length) {
733
+ const word = words[index] as Word
734
+ if (word.operator) break
735
+ const value = word.value
736
+ if (value === '--') return index + 1
737
+ if (VALUE_FLAGS.has(value)) {
738
+ index += 2
739
+ continue
740
+ }
741
+ if (value.startsWith('-') && value !== '-') {
742
+ index += 1
743
+ continue
744
+ }
745
+ break
746
+ }
747
+ return index
748
+ }
749
+
750
+ /**
751
+ * R4 (STATUS.md 12p, re-review 2026-09-28 night):
752
+ * `git config [--global|--local|…] alias.<verb> <value>` WRITES a
753
+ * persistent alias a LATER, separate command can invoke — pre-loading the
754
+ * exact bypass `networkAliasRedefinition`/`environmentAliasRedefinition`
755
+ * catch inline, just deferred to whenever the alias is next used (by which
756
+ * point this scanner has no visibility into a different command line at
757
+ * all). Denied at the point of DEFINITION instead: `git config alias.p
758
+ * push` never needs `git p` to actually run on the same line to be a real
759
+ * bypass being set up.
760
+ */
761
+ function gitConfigWritesNetworkAlias(rest: readonly Word[]): boolean {
762
+ const index = skipGitConfigScopeFlags(rest, 0)
763
+ const keyWord = rest[index]
764
+ if (keyWord === undefined || keyWord.operator) return false
765
+ const match = /^alias\.(.+)$/i.exec(keyWord.value)
766
+ if (match === null) return false
767
+ const valueWord = rest[index + 1]
768
+ if (valueWord === undefined || valueWord.operator || valueWord.value.startsWith('-')) return false
769
+ return networkAliasVerb(valueWord.value)
770
+ }
771
+
772
+ const COMMAND_OPTION = '--command='
773
+ const SCRIPT_OPTION_TAKES_WORD = new Set(['-c', '--command'])
774
+
775
+ /**
776
+ * The script string a shell's `-c`/`--command` option carries, as a raw
777
+ * content span into the original command — port of invocation.ts's
778
+ * walkShellArgument. `--` ends the option scan; a non-option operand is a
779
+ * script FILE and is not inspected; an unterminated option scan yields null.
780
+ */
781
+ function scriptContentSpan(command: string, words: readonly Word[], start: number): { readonly start: number; readonly end: number } | null {
782
+ for (let index = start; index < words.length; index += 1) {
783
+ const word = words[index] as Word
784
+ if (word.operator) return null
785
+ const value = word.value
786
+ if (value === '--') return null
787
+ if (SCRIPT_OPTION_TAKES_WORD.has(value) || (/^-[^-]*c$/.test(value) && !value.startsWith('--'))) {
788
+ const script = words[index + 1]
789
+ if (script === undefined || script.operator) return null
790
+ // A quoted script: insertions land inside the quotes; unquoted: at the word.
791
+ const rawFirst = command[script.start]
792
+ return rawFirst === "'" || rawFirst === '"' ? { start: script.start + 1, end: script.end - 1 } : { start: script.start, end: script.end }
793
+ }
794
+ if (value.startsWith(COMMAND_OPTION)) {
795
+ const valueStart = word.start + COMMAND_OPTION.length
796
+ if (valueStart >= word.end) return null
797
+ // A quoted inline script inserts INSIDE the quotes; an unquoted one at
798
+ // the value start — either way the prefix becomes part of the script
799
+ // string, never a separate word that would shift the shell's $0.
800
+ const rawFirst = command[valueStart]
801
+ return rawFirst === "'" || rawFirst === '"' ? { start: valueStart + 1, end: word.end - 1 } : { start: valueStart, end: word.end }
802
+ }
803
+ if (!value.startsWith('-')) return null
804
+ }
805
+ return null
806
+ }
807
+
808
+ interface ScanState {
809
+ deny: Extract<GitIdentityScan, { readonly kind: 'deny' }>['reason'] | null
810
+ /** Original-command offsets (ascending) where the identity prefix is inserted. */
811
+ insertions: number[]
812
+ /** Rule mode: detect conflicts only, never compute insertions. */
813
+ conflictsOnly: boolean
814
+ /** Set true the moment an identity-bearing command is found with no valid inline override and `fallback === null`. */
815
+ needsIdentity: boolean
816
+ /** The fallback identity (steps 2-3 of the standard), or null when none resolved — see `applyIdentityOutcome`. */
817
+ fallback: string | null
818
+ /** Set true the moment a valid inline `ADL_AGENT_ID_AGENT=` override is found on any identity-bearing piece — see `commandHasValidInlineIdentity`. */
819
+ foundInlineOverride: boolean
820
+ /**
821
+ * The value of the FIRST valid inline `ADL_AGENT_ID_AGENT=` override found
822
+ * on any identity-bearing piece, or null — same coarseness as
823
+ * `foundInlineOverride` (whole-command, not per-piece); see
824
+ * `effectiveIdentityForCommand`.
825
+ */
826
+ inlineIdentity: string | null
827
+ /**
828
+ * Every token-minting invocation found (`adl id token <Y>` /
829
+ * `adl-agent-id exec --agent <Y>`), paired with the effective identity for
830
+ * THAT piece — see `TokenMintingPiece` and `scanTokenMintingPieces`.
831
+ */
832
+ tokenMinting: TokenMintingPiece[]
833
+ /**
834
+ * Every network git invocation found (`fetch`/`pull`/`clone`/`ls-remote`/
835
+ * `push`), paired with the effective identity for THAT SPECIFIC piece —
836
+ * see `GitInvocationPiece` and `scanGitInvocationPieces`. M2/M3
837
+ * (STATUS.md 12p): a role-grading rule
838
+ * (`adl-git-push-policy.ts`) needs the identity a PARTICULAR push on a
839
+ * compound command line actually carries, not the whole command's coarse
840
+ * `inlineIdentity` (which is set from whichever piece happens to be first
841
+ * — a reviewer session could otherwise smuggle a denied push past the
842
+ * rule by chaining an unrelated, later push with its own valid override
843
+ * on the same line).
844
+ */
845
+ gitInvocations: GitInvocationPiece[]
846
+ /**
847
+ * Every `adl mcp cli <command> …` invocation found, paired with the
848
+ * effective identity for THAT SPECIFIC piece — R5
849
+ * (STATUS.md 12p): the role rules that gate these
850
+ * commands (`adl-workitem-role.ts`, `adl-pr-role.ts`,
851
+ * `adl-pr-review-role.ts`) used to grade `resolveFromEnvOrFile` alone,
852
+ * ignoring a valid inline override on the SAME piece — the exact M3 gap,
853
+ * just for `adl mcp cli` instead of `git push`. See `AdlMcpCliPiece` and
854
+ * `scanAdlMcpCliPieces`.
855
+ */
856
+ mcpCliInvocations: AdlMcpCliPiece[]
857
+ }
858
+
859
+ /**
860
+ * One network git invocation (`fetch`/`pull`/`clone`/`ls-remote`/`push`)
861
+ * found while scanning a command, paired with the effective identity for
862
+ * THAT specific piece: a valid inline `ADL_AGENT_ID_AGENT=` override on the
863
+ * SAME piece, else the resolved fallback, else null. `rest` is the dequoted
864
+ * values after the subcommand — the same shape `Invocation.rest` (Token
865
+ * array, dequoted via `tokenValue`) carries for a `'git'`-kind Invocation
866
+ * from `../shell/invocation.ts`'s classifier, so a rule holding one of
867
+ * those can correlate it to the piece here by comparing values. See
868
+ * `scanGitInvocationPieces`.
869
+ */
870
+ export interface GitInvocationPiece {
871
+ readonly subcommand: string
872
+ readonly rest: readonly string[]
873
+ readonly effectiveAgent: string | null
874
+ }
875
+
876
+ /**
877
+ * One `adl mcp cli <command> …` invocation found while scanning a command,
878
+ * paired with the effective identity for THAT specific piece — R5
879
+ * (STATUS.md 12p). `command` is the gated command name
880
+ * (e.g. `workitem-create-or-update`), lower-cased; `rest` is the dequoted
881
+ * values after it, the same shape `impl/adl-mcp-cli.ts`'s `adlMcpCliCall`
882
+ * reads off `Invocation.args.slice(3)`, so a role rule can correlate a
883
+ * specific `Invocation` to its own piece here by comparing values, exactly
884
+ * like `GitInvocationPiece`/`scanGitInvocationPieces` for `git push`. See
885
+ * `scanAdlMcpCliPieces`.
886
+ */
887
+ export interface AdlMcpCliPiece {
888
+ readonly command: string
889
+ readonly rest: readonly string[]
890
+ readonly effectiveAgent: string | null
891
+ }
892
+
893
+ /**
894
+ * One `adl id token <agent>` or `adl-agent-id exec --agent <agent>` (or
895
+ * `--agent=<agent>`) invocation found while scanning a command, paired with
896
+ * the effective identity for that specific simple command: a valid inline
897
+ * `ADL_AGENT_ID_AGENT=` override on the SAME piece, else the resolved
898
+ * fallback (steps 2-3 of the standard), else null (no identity at all —
899
+ * `adl.session-required-for-writes`'s problem, not this one's).
900
+ * `requestedAgent` is the raw text the command names, unvalidated — the
901
+ * caller (`impl/adl-token-minting-role.ts`) is the one that compares it
902
+ * against `effectiveAgent`.
903
+ */
904
+ export interface TokenMintingPiece {
905
+ readonly kind: 'adl-id-token' | 'adl-agent-id-exec'
906
+ readonly requestedAgent: string
907
+ readonly effectiveAgent: string | null
908
+ }
909
+
910
+ /** The value of a valid (non-empty, `VALID_AGENTS`-member) inline `ADL_AGENT_ID_AGENT=` assignment among `assignments`, or undefined. */
911
+ function validInlineIdentity(assignments: readonly string[]): string | undefined {
912
+ let inlineValue: string | undefined
913
+ for (const assignment of assignments) {
914
+ const equals = assignment.indexOf('=')
915
+ if (assignment.slice(0, equals) !== ADL_AGENT_ID_ENV_VAR) continue
916
+ inlineValue = assignment.slice(equals + 1).trim()
917
+ }
918
+ if (inlineValue === undefined) return undefined
919
+ return inlineValue.length > 0 && isValidAgent(inlineValue) ? inlineValue : undefined
920
+ }
921
+
922
+ /** `adl-agent-id exec`'s `--agent <value>` or `--agent=<value>` argument, or undefined when absent/malformed. */
923
+ function execAgentFlagValue(restValues: readonly string[]): string | undefined {
924
+ const equalsForm = restValues.find((value) => value.startsWith('--agent='))
925
+ if (equalsForm !== undefined) return equalsForm.slice('--agent='.length) || undefined
926
+ const flagIndex = restValues.indexOf('--agent')
927
+ if (flagIndex < 0) return undefined
928
+ const value = restValues[flagIndex + 1]
929
+ return value === undefined || value.startsWith('-') ? undefined : value
930
+ }
931
+
932
+ /**
933
+ * The WORD (not the dequoted string `execAgentFlagValue` reads) carrying
934
+ * `adl-agent-id exec`'s `--agent` value — `--agent=<value>` is itself the
935
+ * carrying word; `--agent <value>` is the word AFTER the flag. Used only to
936
+ * check DYNAMISM (M5, STATUS.md 12p): a
937
+ * `--agent "$AGENT"` argument could name ANY agent at runtime, evading
938
+ * `adl.token-minting-role`'s static "own identity only" check entirely if
939
+ * it were silently treated as "not identity-bearing" the way any other
940
+ * unrelated dynamic flag on the same command is.
941
+ */
942
+ function execAgentFlagWord(rest: readonly Word[]): Word | undefined {
943
+ const equalsForm = rest.find((word) => !word.operator && word.value.startsWith('--agent='))
944
+ if (equalsForm !== undefined) return equalsForm
945
+ const flagIndex = rest.findIndex((word) => !word.operator && word.value === '--agent')
946
+ if (flagIndex < 0) return undefined
947
+ const value = rest[flagIndex + 1]
948
+ return value === undefined || value.operator || value.value.startsWith('-') ? undefined : value
949
+ }
950
+
951
+ /**
952
+ * Shared identity decision for ONE matched identity-bearing simple command
953
+ * (STATUS.md 12p, "Decision per identity-bearing
954
+ * shell command"):
955
+ *
956
+ * - an inline `ADL_AGENT_ID_AGENT=<value>` among `assignments` that is
957
+ * empty or not one of `VALID_AGENTS` is a deny (`identity-mismatch`) —
958
+ * never rewritten over;
959
+ * - a VALID inline value — regardless of whether it matches `state.fallback`
960
+ * — is the override: PASS, nothing inserted (the command already carries
961
+ * a safe, complete identity);
962
+ * - no inline value at all: a `sudo`/`doas` wrapper is `unrewritable-wrapper`
963
+ * (the environment a prefix relies on is stripped); otherwise, with
964
+ * `state.fallback === null`, this piece `needsIdentity` (the caller —
965
+ * `adl-session-required.ts` — is the one that denies this); otherwise the
966
+ * fallback is inserted at `insertAt` (skipped in `conflictsOnly` mode).
967
+ */
968
+ function applyIdentityOutcome(assignments: readonly string[], unrewritableWrapper: boolean, insertAt: number, state: ScanState): void {
969
+ let inlineValue: string | undefined
970
+ for (const assignment of assignments) {
971
+ const equals = assignment.indexOf('=')
972
+ if (assignment.slice(0, equals) !== ADL_AGENT_ID_ENV_VAR) continue
973
+ inlineValue = assignment.slice(equals + 1).trim()
974
+ }
975
+ // An empty or invalid inline value is a deny regardless of a sudo/doas
976
+ // wrapper (it is already unsafe on its own); checked first, exactly like
977
+ // the pre-2026-09-28 mismatch check did.
978
+ if (inlineValue !== undefined && (inlineValue.length === 0 || !isValidAgent(inlineValue))) {
979
+ state.deny = 'identity-mismatch'
980
+ return
981
+ }
982
+ // sudo/doas strips the environment before the wrapped command runs, so
983
+ // even a VALID inline assignment sitting before it never reaches git —
984
+ // this check must come before "a valid inline override is a pass".
985
+ if (unrewritableWrapper) {
986
+ state.deny = 'unrewritable-wrapper'
987
+ return
988
+ }
989
+ if (inlineValue !== undefined) {
990
+ // A valid inline override, no unrewritable wrapper in the way: the
991
+ // command already carries a complete, safe identity — nothing to
992
+ // insert, nothing to deny.
993
+ state.foundInlineOverride = true
994
+ if (state.inlineIdentity === null) state.inlineIdentity = inlineValue
995
+ return
996
+ }
997
+ if (state.fallback === null) {
998
+ state.needsIdentity = true
999
+ return
1000
+ }
1001
+ if (!state.conflictsOnly) state.insertions.push(insertAt)
1002
+ }
1003
+
1004
+ /**
1005
+ * Classify one simple command's words. Returns the insertion offset when the
1006
+ * piece is an identity-bearing invocation (a network git verb; an
1007
+ * identity-bearing `adl`/`adl-agent-id` subcommand; a curl/wget/httpie/xh
1008
+ * call to `dev.azure.com`/`*.visualstudio.com`) that needs the identity,
1009
+ * records a deny on conflict, and recurses into `bash -c '<script>'` shapes.
1010
+ * `insertAt` is the start of the simple command (before leading
1011
+ * assignments), so the new prefix binds to the whole command including any
1012
+ * wrappers.
1013
+ */
1014
+ function analyzePiece(command: string, words: readonly Word[], state: ScanState, depth: number): void {
1015
+ let index = 0
1016
+ // Leading redirections (operator + target word) are not the command.
1017
+ while (index < words.length && (words[index] as Word).operator) {
1018
+ index += 1
1019
+ if (index < words.length && !(words[index] as Word).operator) index += 1
1020
+ }
1021
+ if (index >= words.length) return
1022
+ const insertAt = (words[index] as Word).start
1023
+
1024
+ const assignments: string[] = []
1025
+ while (index < words.length && isAssignmentValue((words[index] as Word).value)) {
1026
+ assignments.push((words[index] as Word).value)
1027
+ index += 1
1028
+ }
1029
+
1030
+ let unrewritableWrapper = false
1031
+ for (;;) {
1032
+ if (index >= words.length) return
1033
+ const name = basenameValue((words[index] as Word).value)
1034
+ if (name === 'sudo' || name === 'doas') {
1035
+ // sudo/doas strip the environment: a prefix would silently not reach git.
1036
+ unrewritableWrapper = true
1037
+ index = skipDashOptions(words, index + 1, ['-u', '-g', '-C', '--user', '--group'], true)
1038
+ while (index < words.length && isAssignmentValue((words[index] as Word).value)) {
1039
+ assignments.push((words[index] as Word).value)
1040
+ index += 1
1041
+ }
1042
+ continue
1043
+ }
1044
+ // H1: env -u/--unset[=NAME]/-i and exec -c/-l can strip the environment
1045
+ // a prefix would rely on, the same way sudo/doas do — see skipEnvArgs's
1046
+ // and skipExecArgs's own docs for why this is blanket (any use of the
1047
+ // flag), not scoped to "only when it names ADL_AGENT_ID_AGENT".
1048
+ if (name === 'env') {
1049
+ // No trailing-assignment loop here, unlike sudo/doas and exec below:
1050
+ // skipEnvArgs already consumes every `NAME=value` argument itself (its
1051
+ // own catch-all branch — anything containing `=` and not a recognized
1052
+ // flag — pushes straight into `assignments`), so by construction it
1053
+ // never returns sitting on an unconsumed assignment.
1054
+ const scan = skipEnvArgs(words, index + 1, assignments)
1055
+ if (scan.strips) unrewritableWrapper = true
1056
+ index = scan.index
1057
+ continue
1058
+ }
1059
+ if (name === 'exec') {
1060
+ const scan = skipExecArgs(words, index + 1)
1061
+ if (scan.strips) unrewritableWrapper = true
1062
+ index = scan.index
1063
+ while (index < words.length && isAssignmentValue((words[index] as Word).value)) {
1064
+ assignments.push((words[index] as Word).value)
1065
+ index += 1
1066
+ }
1067
+ continue
1068
+ }
1069
+ const skipWrapper = WRAPPER_SKIP[name]
1070
+ if (skipWrapper !== undefined) {
1071
+ index = skipWrapper(words, index + 1, assignments)
1072
+ while (index < words.length && isAssignmentValue((words[index] as Word).value)) {
1073
+ assignments.push((words[index] as Word).value)
1074
+ index += 1
1075
+ }
1076
+ continue
1077
+ }
1078
+ break
1079
+ }
1080
+
1081
+ const commandName = basenameValue((words[index] as Word).value)
1082
+
1083
+ if (commandName === 'git' || commandName === 'git.exe') {
1084
+ const globals = scanGitGlobalsValues(words, index + 1)
1085
+ if (globals.terminal) return
1086
+ const subcommandWord = words[globals.index]
1087
+ if (subcommandWord === undefined || subcommandWord.operator) return
1088
+ if (dynamicValue(subcommandWord.value)) return
1089
+ // H2 (STATUS.md 12p, narrowed 2026-09-28): a
1090
+ // SELF-REFERENTIAL `-c alias.<name>=<value>` (or `--config-env[=]
1091
+ // alias.<name>=<value>`) where `<name>` is the LITERAL subcommand word
1092
+ // this invocation runs, and `<value>`'s own verb is a network/credential
1093
+ // one — checked BEFORE the NETWORK_GIT_VERBS gate below, since the
1094
+ // literal word (e.g. `status`) need not itself be a network verb at all.
1095
+ // `/usr/local/bin/git` (bin/git-ado) routes by the literal argv verb; it
1096
+ // does not resolve git aliases itself, so an aliased network verb never
1097
+ // reaches its identity routing. A NON-self-referential alias (`git -c
1098
+ // alias.wt=worktree wt …`) is untouched — see `networkAliasRedefinition`'s
1099
+ // own doc for why this is deliberately narrower than a blanket
1100
+ // `-c alias.*` deny.
1101
+ const globalWords = words.slice(index + 1, globals.index)
1102
+ const literalSubcommand = subcommandWord.value.toLowerCase()
1103
+ if (networkAliasRedefinition(globalWords, literalSubcommand)) {
1104
+ state.deny = 'network-alias-redefinition'
1105
+ return
1106
+ }
1107
+ // R4: the ENV-VAR-based sibling of the check above (`GIT_CONFIG_COUNT`/
1108
+ // `GIT_CONFIG_KEY_n`/`GIT_CONFIG_VALUE_n`, already collected into
1109
+ // `assignments` by the leading-assignment loop above this function's
1110
+ // wrapper-skip loop) — see `environmentAliasRedefinition`'s own doc.
1111
+ if (environmentAliasRedefinition(assignments, literalSubcommand)) {
1112
+ state.deny = 'network-alias-redefinition'
1113
+ return
1114
+ }
1115
+ // R4: `git config [--global|…] alias.<verb> <value>` WRITES a
1116
+ // persistent alias for a LATER command to exploit — see
1117
+ // `gitConfigWritesNetworkAlias`'s own doc. Checked before the
1118
+ // NETWORK_GIT_VERBS gate below since `config` itself is never a network
1119
+ // verb.
1120
+ if (literalSubcommand === 'config' && gitConfigWritesNetworkAlias(words.slice(globals.index + 1))) {
1121
+ state.deny = 'network-alias-redefinition'
1122
+ return
1123
+ }
1124
+ const rest = words.slice(globals.index + 1).map((word) => word.value)
1125
+ // F2 (independent review, 2026-09-29): widened from a plain
1126
+ // `NETWORK_GIT_VERBS.has(literalSubcommand)` gate — see
1127
+ // `isNetworkGitInvocation`'s own doc for the additional shapes.
1128
+ if (!isNetworkGitInvocation(literalSubcommand, rest)) return
1129
+ if (valuesHelpOnly(rest)) return
1130
+ // M2/M3: record this SPECIFIC piece's own effective identity — see
1131
+ // `GitInvocationPiece`'s doc — before the destination exemption below,
1132
+ // so a role-grading rule can correlate to it by subcommand+rest
1133
+ // regardless of whether THIS scanner itself treats the piece as
1134
+ // identity-bearing for rewriting purposes.
1135
+ state.gitInvocations.push({
1136
+ subcommand: subcommandWord.value.toLowerCase(),
1137
+ rest,
1138
+ effectiveAgent: validInlineIdentity(assignments) ?? state.fallback,
1139
+ })
1140
+ // H3 (STATUS.md 12p): an EXPLICIT non-Azure-DevOps
1141
+ // destination is provably safe without an identity — same exemption
1142
+ // `git-helpers.ts`'s `classifyGitPushDestination` gives
1143
+ // `adl.session-required-for-writes`. No explicit URL (a bare `git push`,
1144
+ // or one naming a remote this scanner cannot look up) stays UNRESOLVED
1145
+ // and is treated as identity-bearing, same fail-closed rule. Needed so
1146
+ // `../engine.ts`'s H3 defense-in-depth deny on `needs-identity` does not
1147
+ // deny a legitimate `git fetch https://github.com/...` that has no
1148
+ // identity at all — this scanner previously flagged every network git
1149
+ // verb as identity-bearing regardless of destination, which was harmless
1150
+ // when `needs-identity` was never denied on its own, but is not harmless
1151
+ // now that it is.
1152
+ const explicitUrl = firstHttpUrl(rest)
1153
+ if (explicitUrl !== null && !isAdoUrl(explicitUrl)) return
1154
+ applyIdentityOutcome(assignments, unrewritableWrapper, insertAt, state)
1155
+ return
1156
+ }
1157
+
1158
+ if (ADL_BINARY_NAMES.has(commandName)) {
1159
+ const rest = words.slice(index + 1)
1160
+ const restValues = rest.map((word) => word.value)
1161
+ // The shape check itself does not care about dynamism (a dynamic
1162
+ // position's raw text is never literally "id"/"token"/… anyway), so it
1163
+ // can run before the dynamic checks below — that lets the M5 check just
1164
+ // after it single out the SPECIFIC agent-argument position rather than
1165
+ // bailing on ANY dynamic content anywhere on the line.
1166
+ if (!matchesIdentityShape(restValues, ADL_IDENTITY_COMMAND_SHAPES)) return
1167
+ const isIdToken = restValues[0]?.toLowerCase() === 'id' && restValues[1]?.toLowerCase() === 'token'
1168
+ const isMcpCli = restValues[0]?.toLowerCase() === 'mcp' && restValues[1]?.toLowerCase() === 'cli'
1169
+ // M5 (STATUS.md 12p): `adl id token <agent>` with
1170
+ // a DYNAMIC agent argument is denied outright — a dynamically computed
1171
+ // agent name could name ANY agent at runtime, evading
1172
+ // adl.token-minting-role's static "own identity only" check entirely if
1173
+ // this were silently treated as "not identity-bearing" instead.
1174
+ if (isIdToken) {
1175
+ const agentWord = rest[2]
1176
+ if (agentWord !== undefined && !agentWord.operator && dynamicValue(agentWord.value)) {
1177
+ state.deny = 'dynamic-agent'
1178
+ return
1179
+ }
1180
+ }
1181
+ // Any OTHER dynamic content (an unrelated flag elsewhere on the line)
1182
+ // cannot be proven identity-bearing beyond doubt; leave it alone rather
1183
+ // than guess.
1184
+ if (rest.some((word) => !word.operator && dynamicValue(word.value))) return
1185
+ // `adl id token <agent>`: the requested agent is the third positional
1186
+ // word (after `id token`), when it names one at all (not a flag).
1187
+ if (isIdToken) {
1188
+ const requested = restValues[2]
1189
+ if (requested !== undefined && requested.length > 0 && !requested.startsWith('-')) {
1190
+ state.tokenMinting.push({
1191
+ kind: 'adl-id-token',
1192
+ requestedAgent: requested,
1193
+ effectiveAgent: validInlineIdentity(assignments) ?? state.fallback,
1194
+ })
1195
+ }
1196
+ }
1197
+ // R5: record this SPECIFIC `adl mcp cli <command>` piece's own effective
1198
+ // identity — see `AdlMcpCliPiece`'s doc — so the role rules that gate
1199
+ // these commands can grade the piece actually being invoked, not the
1200
+ // whole command's coarse `inlineIdentity`.
1201
+ if (isMcpCli) {
1202
+ const gatedCommand = restValues[2]
1203
+ if (gatedCommand !== undefined && gatedCommand.length > 0 && !gatedCommand.startsWith('-')) {
1204
+ state.mcpCliInvocations.push({
1205
+ command: gatedCommand.toLowerCase(),
1206
+ rest: restValues.slice(3),
1207
+ effectiveAgent: validInlineIdentity(assignments) ?? state.fallback,
1208
+ })
1209
+ }
1210
+ }
1211
+ applyIdentityOutcome(assignments, unrewritableWrapper, insertAt, state)
1212
+ return
1213
+ }
1214
+
1215
+ if (ADL_AGENT_ID_BINARY_NAMES.has(commandName)) {
1216
+ const rest = words.slice(index + 1)
1217
+ const restValues = rest.map((word) => word.value)
1218
+ // M5: leading dash-prefixed global flags (e.g. `adl-agent-id --verbose
1219
+ // exec --agent …`) are skipped before matching the `['exec']` shape —
1220
+ // see skipLeadingDashFlags's own doc for why this must not require
1221
+ // `exec` sitting at position 0 exactly.
1222
+ if (!matchesIdentityShape(skipLeadingDashFlags(restValues), ADL_AGENT_ID_COMMAND_SHAPES)) return
1223
+ // M5: a DYNAMIC --agent value is denied outright — same reasoning as
1224
+ // the `adl id token` case above, applied to this shape's own
1225
+ // agent-bearing flag instead of a positional argument.
1226
+ const agentWord = execAgentFlagWord(rest)
1227
+ if (agentWord !== undefined && dynamicValue(agentWord.value)) {
1228
+ state.deny = 'dynamic-agent'
1229
+ return
1230
+ }
1231
+ if (rest.some((word) => !word.operator && dynamicValue(word.value))) return
1232
+ const requested = execAgentFlagValue(restValues)
1233
+ if (requested !== undefined) {
1234
+ state.tokenMinting.push({
1235
+ kind: 'adl-agent-id-exec',
1236
+ requestedAgent: requested,
1237
+ effectiveAgent: validInlineIdentity(assignments) ?? state.fallback,
1238
+ })
1239
+ }
1240
+ applyIdentityOutcome(assignments, unrewritableWrapper, insertAt, state)
1241
+ return
1242
+ }
1243
+
1244
+ if (HTTP_CLIENT_BINARY_NAMES.has(commandName)) {
1245
+ const rest = words.slice(index + 1)
1246
+ const targetsAdo = rest.some((word) => !word.operator && isAdoUrl(word.value))
1247
+ if (!targetsAdo) return
1248
+ applyIdentityOutcome(assignments, unrewritableWrapper, insertAt, state)
1249
+ return
1250
+ }
1251
+
1252
+ if (depth < MAX_NESTED_SCRIPT_DEPTH && SHELL_NAMES.has(commandName)) {
1253
+ const span = scriptContentSpan(command, words, index + 1)
1254
+ if (span !== null && span.end > span.start) {
1255
+ const nested: Piece[] = []
1256
+ scanPieces(command.slice(span.start, span.end), span.start, nested)
1257
+ for (const nestedPiece of nested) {
1258
+ if (state.deny !== null) return
1259
+ analyzePiece(command, nestedPiece.words, state, depth + 1)
1260
+ }
1261
+ }
1262
+ }
1263
+ }
1264
+
1265
+ function scan(command: string, fallback: string | null, conflictsOnly: boolean): ScanState {
1266
+ const state: ScanState = {
1267
+ deny: null,
1268
+ insertions: [],
1269
+ conflictsOnly,
1270
+ needsIdentity: false,
1271
+ fallback,
1272
+ foundInlineOverride: false,
1273
+ inlineIdentity: null,
1274
+ tokenMinting: [],
1275
+ gitInvocations: [],
1276
+ mcpCliInvocations: [],
1277
+ }
1278
+ const pieces: Piece[] = []
1279
+ scanPieces(command, 0, pieces)
1280
+ for (const piece of pieces) {
1281
+ if (state.deny !== null) break
1282
+ analyzePiece(command, piece.words, state, 0)
1283
+ }
1284
+ return state
1285
+ }
1286
+
1287
+ /**
1288
+ * Rewrite every identity-bearing invocation in `command` to carry
1289
+ * `fallback`, or report why it cannot be carried. `fallback` is the resolved
1290
+ * identity from steps 2-3 of the standard (`adl-identity.ts`'s
1291
+ * `resolveFromEnvOrFile`), or `null` when nothing resolved — a piece that
1292
+ * needs an identity and has none at all yields `needs-identity` rather than
1293
+ * a deny; the caller (`adl-session-required.ts`) is the one that denies that
1294
+ * case, with the standard's own message. Idempotent; never emits a token or
1295
+ * an identity-bearing prefix the command did not already agree to.
1296
+ *
1297
+ * Narrow by design (Tarun, 2026-09-29, superseding an earlier
1298
+ * whole-command `export` attempt at this same finding — STATUS.md
1299
+ * 12p, R1): this only rewrites the identity-bearing shapes the piece
1300
+ * scanner (`scanPieces`/`analyzePiece`, above) actually recognizes — a
1301
+ * TOP-LEVEL simple command (or one nested inside a recognized `bash -c`/
1302
+ * `sh -c` script). It deliberately does NOT walk into `if`/`!`/`for..do`/
1303
+ * `xargs`/a heredoc or here-string/an interpreter one-liner to find a
1304
+ * buried git/adl/curl invocation — `bin/git-ado` itself now fails closed
1305
+ * (refuses) whenever `ADL_AGENT_ID_AGENT` is unset unless the caller is an
1306
+ * interactive human terminal, so a shape this scanner misses is refused
1307
+ * loudly by git-ado, never silently laundered onto the human credential
1308
+ * path — the risk a broader rewrite here would have hedged against no
1309
+ * longer exists once that source-level fix is in place.
1310
+ */
1311
+ export function rewriteGitNetworkCommands(command: string, fallback: string | null): GitIdentityScan {
1312
+ if (fallback !== null && !SAFE_IDENTITY.test(fallback)) return { kind: 'deny', reason: 'unsafe-identity' }
1313
+ const state = scan(command, fallback, false)
1314
+ if (state.deny !== null) return { kind: 'deny', reason: state.deny }
1315
+ if (state.needsIdentity) return { kind: 'needs-identity' }
1316
+ if (state.insertions.length === 0) return { kind: 'unchanged' }
1317
+ const prefix = `${ADL_AGENT_ID_ENV_VAR}=${fallback} `
1318
+ let rewritten = command
1319
+ for (const at of [...state.insertions].sort((left, right) => right - left)) {
1320
+ rewritten = `${rewritten.slice(0, at)}${prefix}${rewritten.slice(at)}`
1321
+ }
1322
+ return { kind: 'rewrite', command: rewritten }
1323
+ }
1324
+
1325
+ /**
1326
+ * The deny-rule view: which conflict (if any) a command carries — an invalid
1327
+ * or empty inline override, or an unrewritable `sudo`/`doas` wrapper. `null`
1328
+ * when the command is clean (including a VALID inline override, which is no
1329
+ * longer a conflict) or needs no identity at all — the rule stays silent so
1330
+ * the rewrite stage (or plain allow) proceeds. `fallback` only affects
1331
+ * whether a missing-inline piece is treated as "needs identity" internally;
1332
+ * this view never reports that as a conflict (`adl-session-required.ts` owns
1333
+ * that message), so it is safe to call with `fallback: null`.
1334
+ */
1335
+ export function scanGitIdentityConflicts(command: string, fallback: string | null): GitIdentityDenyReason | null {
1336
+ if (fallback !== null && !SAFE_IDENTITY.test(fallback)) return null
1337
+ return scan(command, fallback, true).deny
1338
+ }
1339
+
1340
+ /**
1341
+ * Whole-command, coarse signal used by `adl-session-required.ts`: true when
1342
+ * SOME identity-bearing simple command in `command` carries a syntactically
1343
+ * valid inline `ADL_AGENT_ID_AGENT=` override. This is intentionally
1344
+ * coarser than the per-piece precision `rewriteGitNetworkCommands` itself
1345
+ * uses for the actual rewrite/deny: a compound command mixing an overridden
1346
+ * identity-bearing call with a non-overridden one is not disambiguated here
1347
+ * — this function only answers "is there a valid inline override ANYWHERE on
1348
+ * this line", which is exactly what the common, single-command override
1349
+ * shape (`ADL_AGENT_ID_AGENT=<agent> git push …`) needs, and is a
1350
+ * strictly MORE PERMISSIVE signal than the old session-env-only check it
1351
+ * replaces (a command this returns `false` for was ALSO going to be denied
1352
+ * by the old check).
1353
+ */
1354
+ export function commandHasValidInlineIdentity(command: string): boolean {
1355
+ return scan(command, null, true).foundInlineOverride
1356
+ }
1357
+
1358
+ /**
1359
+ * The EFFECTIVE identity for `command` (W2 build item 2,
1360
+ * STATUS.md 12p): a valid inline
1361
+ * `ADL_AGENT_ID_AGENT=` override anywhere on the command line (step 1 — same
1362
+ * whole-command coarseness as `commandHasValidInlineIdentity`, which is the
1363
+ * shape every real override takes: `ADL_AGENT_ID_AGENT=<agent> git push …`),
1364
+ * else `fallback` (steps 2-3, `adl-identity.ts`'s `resolveFromEnvOrFile`),
1365
+ * else null. Used by the role rules that must grade the identity a COMMAND
1366
+ * actually carries, not merely the session's own — `adl-git-push-policy.ts`'s
1367
+ * `git push`. Callers should check `scanGitIdentityConflicts` first: an
1368
+ * invalid/empty inline override or an unrewritable sudo/doas wrapper is
1369
+ * `adl.git-identity`'s problem to report, not a value this function silently
1370
+ * papers over (an invalid inline value is never picked up here since
1371
+ * `inlineIdentity` is only ever set from a value that already passed
1372
+ * `isValidAgent`).
1373
+ */
1374
+ export function effectiveIdentityForCommand(command: string, fallback: string | null): string | null {
1375
+ const state = scan(command, fallback, true)
1376
+ return state.inlineIdentity ?? fallback
1377
+ }
1378
+
1379
+ /**
1380
+ * Every token-minting invocation (`adl id token <agent>` /
1381
+ * `adl-agent-id exec --agent <agent>`) found in `command`, each paired with
1382
+ * the effective identity for that specific piece — see `TokenMintingPiece`.
1383
+ * Used by `impl/adl-token-minting-role.ts` (W2 build item 1).
1384
+ */
1385
+ export function scanTokenMintingPieces(command: string, fallback: string | null): readonly TokenMintingPiece[] {
1386
+ return scan(command, fallback, true).tokenMinting
1387
+ }
1388
+
1389
+ /**
1390
+ * Every network git invocation (`fetch`/`pull`/`clone`/`ls-remote`/`push`)
1391
+ * found in `command`, each paired with the effective identity for THAT
1392
+ * specific piece — see `GitInvocationPiece`. M2/M3 (STATUS.md
1393
+ * 12p): used by `impl/adl-git-push-policy.ts` to grade a
1394
+ * SPECIFIC push by its own piece's identity, not the whole command's coarse
1395
+ * `effectiveIdentityForCommand` (which a compound command with more than one
1396
+ * identity-bearing piece could use to smuggle one piece's override onto
1397
+ * another's grading).
1398
+ */
1399
+ export function scanGitInvocationPieces(command: string, fallback: string | null): readonly GitInvocationPiece[] {
1400
+ return scan(command, fallback, true).gitInvocations
1401
+ }
1402
+
1403
+ /**
1404
+ * Every `adl mcp cli <command> …` invocation found in `command`, each
1405
+ * paired with the effective identity for THAT specific piece — see
1406
+ * `AdlMcpCliPiece`. R5 (STATUS.md 12p): used by
1407
+ * `impl/adl-workitem-role.ts`/`adl-pr-role.ts`/`adl-pr-review-role.ts` to
1408
+ * grade a SPECIFIC `adl mcp cli` invocation by its own piece's inline
1409
+ * identity, else the fallback — the same M3 correlation
1410
+ * `scanGitInvocationPieces` already gives `git push`, extended to this
1411
+ * command class.
1412
+ */
1413
+ export function scanAdlMcpCliPieces(command: string, fallback: string | null): readonly AdlMcpCliPiece[] {
1414
+ return scan(command, fallback, true).mcpCliInvocations
1415
+ }