@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.37 → 0.1.47

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 (249) hide show
  1. package/bin/adl +2 -2
  2. package/bin/reconcile +10 -13
  3. package/bin/setup +15 -15
  4. package/package.json +1 -1
  5. package/packages/agent-hooks/package.json +1 -1
  6. package/packages/agent-hooks/src/main.ts +23 -2
  7. package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +12 -8
  8. package/packages/agent-hooks/src/rules/impl/adl-run-required.ts +11 -5
  9. package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +18 -5
  10. package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +30 -26
  11. package/packages/agent-hooks/src/rules/impl/git-branch.ts +1 -1
  12. package/packages/agent-hooks/src/rules/impl/git-target.ts +5 -0
  13. package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +3 -1
  14. package/packages/agent-hooks/src/rules/impl/tool-frontend.ts +230 -11
  15. package/packages/agent-hooks/src/rules/shell/git-identity.ts +1 -1
  16. package/packages/agent-hooks/src/rules/shell/invocation.ts +14 -2
  17. package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +10 -11
  18. package/packages/agent-settings/assets/TARUN-GLOBAL-AGENTS-INSTRUCTIONS.md +2 -2
  19. package/packages/agent-settings/package.json +1 -1
  20. package/packages/agent-settings/src/deploy/agents-deploy.ts +19 -1
  21. package/packages/agent-settings/src/deploy/index.ts +1 -0
  22. package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +4 -6
  23. package/packages/agent-skills/assets/finalize-feature/SKILL.md +3 -3
  24. package/packages/agent-skills/assets/implement-feature/SKILL.md +17 -20
  25. package/packages/agent-skills/assets/implement-feature/references/briefs.md +3 -3
  26. package/packages/agent-skills/assets/implement-item/SKILL.md +3 -3
  27. package/packages/agent-skills/assets/mechanics/SKILL.md +1 -1
  28. package/packages/agent-skills/assets/plan-feature/SKILL.md +10 -10
  29. package/packages/agent-skills/assets/prepare-promotion/SKILL.md +3 -4
  30. package/packages/agent-skills/assets/report-bug/SKILL.md +2 -2
  31. package/packages/agent-skills/assets/review-change/SKILL.md +3 -3
  32. package/packages/agent-skills/assets/review-plan/SKILL.md +4 -4
  33. package/packages/agent-skills/assets/triage-workitems/SKILL.md +4 -4
  34. package/packages/agent-skills/assets/validate-design/SKILL.md +6 -12
  35. package/packages/agent-skills/assets/verify-item/SKILL.md +5 -5
  36. package/packages/agent-skills/assets/wayfinder/SKILL.md +1 -1
  37. package/packages/agent-skills/assets/workitems-process/SKILL.md +19 -21
  38. package/packages/agent-skills/assets/workitems-process/references/ado-and-git.md +36 -3
  39. package/packages/agent-skills/assets/workitems-process/references/evidence.md +4 -4
  40. package/packages/agent-skills/assets/write-feature/SKILL.md +15 -18
  41. package/packages/agent-skills/dist/deploy.d.ts +45 -0
  42. package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
  43. package/packages/agent-skills/dist/deploy.js +167 -3
  44. package/packages/agent-skills/dist/deploy.js.map +1 -1
  45. package/packages/agent-skills/dist/index.d.ts +1 -1
  46. package/packages/agent-skills/dist/index.d.ts.map +1 -1
  47. package/packages/agent-skills/dist/index.js +1 -1
  48. package/packages/agent-skills/dist/index.js.map +1 -1
  49. package/packages/agent-skills/package.json +1 -1
  50. package/packages/agent-skills/src/deploy.ts +241 -3
  51. package/packages/agent-skills/src/index.ts +10 -1
  52. package/packages/azdo/README.md +13 -4
  53. package/packages/azdo/bunfig.toml +6 -0
  54. package/packages/azdo/package.json +1 -1
  55. package/packages/azdo/src/azdo-attachments.ts +2 -1
  56. package/packages/azdo/src/azdo-pr.ts +182 -6
  57. package/packages/azdo/src/azdo-wit.ts +290 -41
  58. package/packages/azdo/src/cli.ts +79 -12
  59. package/packages/azdo/src/lib/client.ts +88 -16
  60. package/packages/azdo/src/mcp-server.ts +75 -10
  61. package/packages/azdo/src/tool-dispatch.ts +61 -26
  62. package/packages/fleet/src/install-adl-package.ts +4 -3
  63. package/packages/repo-controls/assets/promote.ts +1 -0
  64. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/no-type.md +1 -1
  65. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-experiment.md +1 -1
  66. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-research.md +1 -1
  67. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +1 -1
  68. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +1 -1
  69. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +1 -1
  70. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +1 -1
  71. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-last-update.md +1 -1
  72. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +1 -1
  73. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +1 -1
  74. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +1 -1
  75. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +1 -1
  76. package/packages/repo-controls/package.json +1 -1
  77. package/packages/repo-controls/src/config.ts +21 -2
  78. package/packages/repo-controls/src/doctor.ts +9 -14
  79. package/packages/repo-controls/src/findings-ratchet.ts +12 -0
  80. package/packages/repo-controls/src/hook.ts +61 -4
  81. package/bin/lean-ctx-observe-strip +0 -434
  82. package/bin/lib/lean-ctx-observe-hooks.sh +0 -634
  83. package/bin/update-azure-agent-pool +0 -29
  84. package/packages/agent-hooks/src/hooks/lean-ctx/handler.ts +0 -22
  85. package/packages/agent-hooks/src/hooks/lean-ctx/index.ts +0 -173
  86. package/packages/agent-hooks/src/hooks/lean-ctx/mapping.ts +0 -121
  87. package/packages/agent-hooks/src/hooks/lean-ctx/scope-rewrite.ts +0 -207
  88. package/packages/agent-hooks/src/lean-ctx/env.ts +0 -123
  89. package/packages/agent-hooks/src/lean-ctx/mcp.ts +0 -142
  90. package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +0 -51
  91. package/packages/agent-hooks/src/rules/impl/adl-session-required.ts +0 -403
  92. package/packages/agent-hooks/src/rules/impl/tool-lean-ctx-global.ts +0 -594
  93. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +0 -112
  94. package/packages/agent-settings/assets/lean-ctx/config.toml +0 -42
  95. package/packages/agent-settings/assets/lean-ctx/gitignore +0 -10
  96. package/packages/agent-settings/assets/lean-ctx/layout.toml +0 -7
  97. package/packages/agent-settings/src/deploy/lean-ctx-deploy.ts +0 -230
  98. package/packages/agent-settings/src/deploy/lean-ctx-merge.ts +0 -580
  99. package/packages/agent-skills/assets/adl-mechanics/SKILL.md +0 -38
  100. package/packages/agent-skills/assets/codebase-design/SKILL.md +0 -98
  101. package/packages/agent-skills/assets/codebase-design/references/DEEPENING.md +0 -37
  102. package/packages/agent-skills/assets/codebase-design/references/DESIGN-IT-TWICE.md +0 -44
  103. package/packages/agent-skills/assets/codebase-design/references/TESTABILITY.md +0 -29
  104. package/packages/agent-skills/assets/debug-issue/SKILL.md +0 -41
  105. package/packages/agent-skills/assets/domain-modeling/SKILL.md +0 -86
  106. package/packages/agent-skills/assets/domain-modeling/references/ADR-FORMAT.md +0 -47
  107. package/packages/agent-skills/assets/domain-modeling/references/CONTEXT-FORMAT.md +0 -60
  108. package/packages/agent-skills/assets/implement/SKILL.md +0 -28
  109. package/packages/agent-skills/assets/review-changes/SKILL.md +0 -45
  110. package/packages/agent-skills/assets/tdd/SKILL.md +0 -49
  111. package/packages/agent-skills/assets/tdd/references/mocking.md +0 -59
  112. package/packages/agent-skills/assets/tdd/references/tests.md +0 -77
  113. package/packages/agent-skills/assets/testing/SKILL.md +0 -54
  114. package/packages/agent-skills/assets/testing/references/e2e.md +0 -37
  115. package/packages/agent-skills/assets/testing/references/integration.md +0 -37
  116. package/packages/agent-skills/assets/testing/references/unit.md +0 -32
  117. package/packages/agent-skills/assets/to-feature/SKILL.md +0 -91
  118. package/packages/agent-skills/assets/to-feature/references/FEATURE-SPEC-TEMPLATE.md +0 -88
  119. package/packages/agent-skills/assets/to-workitems/SKILL.md +0 -90
  120. package/packages/agent-skills/assets/to-workitems/references/AZURE-DEVOPS-HIERARCHY.md +0 -121
  121. package/packages/agent-skills/assets/to-workitems/references/TICKET-TEMPLATES.md +0 -70
  122. package/packages/azdo/src/workflow-rules-matrix.csv +0 -85
  123. package/packages/fleet/src/launch-agents.ts +0 -649
  124. package/packages/repo-controls/assets/GLOBAL-INVARIANTS.md +0 -21
  125. package/packages/repo-controls/assets/enable-integration-auto-complete.mjs +0 -79
  126. package/packages/repo-controls/assets/scaffold/docs/notes.md +0 -20
  127. package/packages/repo-controls/assets/scaffold/plans/GAPS-AND-FINDINGS.md +0 -12
  128. package/packages/repo-controls/azure-pipelines.yml +0 -274
  129. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +0 -1
  130. 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 +0 -1
  131. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +0 -17
  132. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +0 -13
  133. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-delivery.md +0 -10
  134. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-recommendation.md +0 -12
  135. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/target-narration.md +0 -10
  136. 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 +0 -1
  137. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/a.ts +0 -1
  138. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/b.ts +0 -1
  139. 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 +0 -1
  140. 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 +0 -1
  141. 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 +0 -1
  142. 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 +0 -1
  143. 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 +0 -1
  144. 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 +0 -1
  145. 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 +0 -1
  146. 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 +0 -1
  147. 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 +0 -1
  148. 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 +0 -1
  149. 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 +0 -1
  150. 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 +0 -1
  151. 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 +0 -1
  152. 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 +0 -1
  153. 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 +0 -2
  154. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  155. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/a.ts +0 -1
  156. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/b.ts +0 -1
  157. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/c.ts +0 -1
  158. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  159. 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 +0 -1
  160. 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 +0 -1
  161. 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 +0 -1
  162. 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 +0 -1
  163. 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 +0 -1
  164. 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 +0 -1
  165. 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 +0 -1
  166. 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 +0 -1
  167. 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 +0 -1
  168. 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 +0 -1
  169. 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 +0 -1
  170. 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 +0 -1
  171. 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 +0 -1
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  173. 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 +0 -5
  174. 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 +0 -10
  175. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/agent/crates/enact-providers/src/snapshots/README.md +0 -1
  176. 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 +0 -1
  177. 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 +0 -1
  178. 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 +0 -1
  179. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/mobile/assets/images/icon.png +0 -1
  180. 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 +0 -1
  181. 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 +0 -1
  182. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/public/pwa/icon-192.png +0 -1
  183. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/resources/product.png +0 -1
  184. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-stray/backend/crates/enact-session/src/backend/README.md +0 -1
  185. 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 +0 -12
  186. 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 +0 -12
  187. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/__init__.py +0 -11
  188. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/check.py +0 -62
  189. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/NotAMigration.sql +0 -1
  190. 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 +0 -1
  191. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/001_init.sql +0 -1
  192. 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 +0 -1
  193. 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 +0 -1
  194. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/004_MixedCase.sql +0 -1
  195. 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 +0 -1
  196. 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 +0 -1
  197. 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 +0 -1
  198. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/test_check.py +0 -116
  199. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/links.md +0 -20
  200. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/route-target.md +0 -8
  201. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/links.md +0 -20
  202. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/route-target.md +0 -8
  203. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.changeset/declared.txt +0 -1
  204. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/INDEPENDENT-VERIFICATION.md +0 -3
  205. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/.gitattributes +0 -1
  206. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/large file.bin +0 -3
  207. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/.gitattributes +0 -1
  208. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/large file.bin +0 -3
  209. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/small.txt +0 -1
  210. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/missing-filter/large file.bin +0 -0
  211. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/.gitattributes +0 -1
  212. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/payload.dat +0 -0
  213. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/__init__.py +0 -1
  214. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/__init__.py +0 -12
  215. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/check.py +0 -129
  216. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/disjoint/gate-registry.yaml +0 -13
  217. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/empty/gate-registry.yaml +0 -7
  218. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/overlapping/gate-registry.yaml +0 -13
  219. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/test_check.py +0 -109
  220. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/__init__.py +0 -1
  221. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/__init__.py +0 -11
  222. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/check.py +0 -149
  223. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/clean/docs/readme.md +0 -4
  224. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/relevant/VERSION +0 -1
  225. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/test_check.py +0 -151
  226. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/__init__.py +0 -11
  227. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/check.py +0 -202
  228. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/changesets/package.json +0 -5
  229. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/package.json +0 -5
  230. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/CHANGELOG.md +0 -5
  231. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/VERSION +0 -1
  232. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/changesets/package.json +0 -5
  233. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/package.json +0 -5
  234. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/CHANGELOG.md +0 -5
  235. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/VERSION +0 -1
  236. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/test_check.py +0 -162
  237. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/check.py +0 -103
  238. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.lock +0 -7
  239. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.toml +0 -4
  240. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/rust-toolchain.toml +0 -3
  241. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/src/main.rs +0 -1
  242. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/frontend/bun.lock +0 -1
  243. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.lock +0 -7
  244. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.toml +0 -4
  245. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/rust-toolchain.toml +0 -3
  246. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/src/main.rs +0 -1
  247. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/frontend/bun.lock +0 -0
  248. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/grants-registry.yaml +0 -112
  249. package/packages/repo-controls/workflows/scripts/release/verify-npm-artifact.ts +0 -119
@@ -35,12 +35,49 @@ export function resetClientForTests(): void {
35
35
  const ALLOWED_TYPES = ['Feature', 'Product Backlog Item', 'Bug'] as const;
36
36
  type AllowedType = (typeof ALLOWED_TYPES)[number];
37
37
  export type WorkItemListOrGetArgs = { id?: number; ids?: number[]; type?: string; state?: string; assignedTo?: string; limit?: number; raw?: boolean };
38
- export type WorkItemWriteArgs = {
39
- id?: number; type?: string; title?: string; description?: string; parentId?: number;
38
+
39
+ /** The per-type write surface (azdo-type-split plan). One arg shape per type; the
40
+ * three sibling tool names — `workitem_create_or_update_feature` /
41
+ * `_pbi` / `_bug` — are dispatched to `workitemCreateOrUpdateFeature` /
42
+ * `Pbi` / `Bug` (see tool-dispatch.ts). The blanket
43
+ * `workitem_create_or_update` is gone. */
44
+ export type WorkItemFeatureWriteArgs = {
45
+ id?: number; title?: string; description?: string; parentId?: number;
40
46
  predecessors?: number[]; related?: number[]; fields?: Record<string, unknown>; state?: string; comment?: string;
41
47
  assignedTo?: string; acceptanceCriteria?: string; priority?: number; startDate?: string; targetDate?: string;
42
- /** T3: the Feature plan fields (board-rules.md § Plan fields), Feature type only. */
48
+ /** T3: the Feature plan fields (board-rules.md § Plan fields). Required on
49
+ * the New → In Progress move; a Feature created at New does not carry them
50
+ * until it walks its ladder. */
43
51
  planRevision?: number; planHash?: string; baselineSha?: string;
52
+ /** az698: drop a single predecessor or related link by id (idempotent on a missing link). Update path only. */
53
+ removePredecessor?: number; removeRelated?: number;
54
+ };
55
+ export type WorkItemPbiWriteArgs = {
56
+ id?: number; title?: string; description?: string; parentId?: number;
57
+ predecessors?: number[]; related?: number[]; fields?: Record<string, unknown>; state?: string; comment?: string;
58
+ assignedTo?: string; acceptanceCriteria?: string; priority?: number; effort?: number; startDate?: string; targetDate?: string;
59
+ /** az698: drop a single predecessor or related link by id. */
60
+ removePredecessor?: number; removeRelated?: number;
61
+ };
62
+ export type WorkItemBugWriteArgs = {
63
+ id?: number; title?: string; description?: string; parentId?: number;
64
+ predecessors?: number[]; related?: number[]; fields?: Record<string, unknown>; state?: string; comment?: string;
65
+ assignedTo?: string; acceptanceCriteria?: string; priority?: number; effort?: number; startDate?: string; targetDate?: string;
66
+ /** az698: drop a single predecessor or related link by id. */
67
+ removePredecessor?: number; removeRelated?: number;
68
+ };
69
+
70
+ /** The merged shape all three per-type arg shapes satisfy — what
71
+ * `tool-dispatch.ts` builds once and hands to whichever type-named tool was
72
+ * called. The board type is never an argument: it is the tool name. */
73
+ export type WorkItemWriteArgs = WorkItemFeatureWriteArgs & WorkItemPbiWriteArgs & WorkItemBugWriteArgs;
74
+
75
+ /** The tool-name suffix each board type writes through — `Product Backlog Item`
76
+ * is the one type whose board name is not its tool name. */
77
+ const TOOL_SUFFIX: Record<AllowedType, 'feature' | 'pbi' | 'bug'> = {
78
+ Feature: 'feature',
79
+ 'Product Backlog Item': 'pbi',
80
+ Bug: 'bug',
44
81
  };
45
82
 
46
83
  const isAllowedType = (type: string | undefined): type is AllowedType => (ALLOWED_TYPES as readonly string[]).includes(type ?? '');
@@ -110,6 +147,10 @@ interface WorkItemView {
110
147
  id: number; title: string; type: string; state: string; assignedTo: string | undefined; description: string | undefined; url: string | undefined;
111
148
  links: { parent: number | null; children: number[]; predecessors: number[]; successors: number[]; related: number[] };
112
149
  effort: number | null; failureReason: string | null; planRevision: number | null; planHash: string | null; baselineSha: string | null;
150
+ /** Priority is the literal set {1, 2, 3, 4} (1=Very High, 2=High, 3=Medium, 4=Low). null when unset or not one of the four (azdo-type-split plan). */
151
+ priority: number | null;
152
+ /** Acceptance Criteria; null when unset. Required on a Feature's New → In Progress move; optional for PBI / Bug. */
153
+ acceptanceCriteria: string | null;
113
154
  fields?: Record<string, unknown>; relations?: unknown[];
114
155
  /** L2/L3 staged refusals: the write succeeded, but the board rule that will harden rides here. */
115
156
  warnings?: string[];
@@ -129,7 +170,28 @@ function formatItem(wi: any, raw: boolean): WorkItemView {
129
170
  // T3: the Feature plan fields ride the standard view (null when unset);
130
171
  // raw mode still exposes every field verbatim.
131
172
  const planRevision = fields['Custom.PlanRevision']; const planHash = fields['Custom.PlanHash']; const baselineSha = fields['Custom.BaselineSha'];
132
- const out: WorkItemView = { id: wi.id, title: fields['System.Title'], type: fields['System.WorkItemType'], state: fields['System.State'], assignedTo: fields['System.AssignedTo']?.displayName, description: fields['System.Description'], url: wi._links?.html?.href, links, effort: typeof effort === 'number' ? effort : null, failureReason: typeof failureReason === 'string' && failureReason ? failureReason : null, planRevision: typeof planRevision === 'number' ? planRevision : null, planHash: typeof planHash === 'string' && planHash ? planHash : null, baselineSha: typeof baselineSha === 'string' && baselineSha ? baselineSha : null };
173
+ // Priority is the literal set {1, 2, 3, 4}; anything else reads as null (mirrors effort's lax null rule).
174
+ const rawPriority = fields['Microsoft.VSTS.Common.Priority'];
175
+ const priority = typeof rawPriority === 'number' && [1, 2, 3, 4].includes(rawPriority) ? rawPriority : null;
176
+ const ac = fields['Microsoft.VSTS.Common.AcceptanceCriteria'];
177
+ const acceptanceCriteria = typeof ac === 'string' && ac ? ac : null;
178
+ const out: WorkItemView = {
179
+ id: wi.id,
180
+ title: fields['System.Title'],
181
+ type: fields['System.WorkItemType'],
182
+ state: fields['System.State'],
183
+ assignedTo: fields['System.AssignedTo']?.displayName,
184
+ description: fields['System.Description'],
185
+ url: wi._links?.html?.href,
186
+ links,
187
+ effort: typeof effort === 'number' ? effort : null,
188
+ failureReason: typeof failureReason === 'string' && failureReason ? failureReason : null,
189
+ planRevision: typeof planRevision === 'number' ? planRevision : null,
190
+ planHash: typeof planHash === 'string' && planHash ? planHash : null,
191
+ baselineSha: typeof baselineSha === 'string' && baselineSha ? baselineSha : null,
192
+ priority,
193
+ acceptanceCriteria,
194
+ };
133
195
  if (raw) { out.fields = fields; out.relations = wi.relations ?? []; }
134
196
  return out;
135
197
  }
@@ -165,12 +227,18 @@ export async function workitemListOrGet(args: WorkItemListOrGetArgs = {}): Promi
165
227
  return JSON.stringify({ items: items.map((item) => formatItem(item, args.raw ?? false)), count: selected.length, truncated: ids.length > limit, defaultsApplied: args.state === undefined }, null, 2);
166
228
  }
167
229
 
168
- function buildWriteOps(args: WorkItemWriteArgs): any[] {
230
+ /** The per-type write arg shapes (azdo-type-split plan) give `buildWriteOps`
231
+ * a narrowed surface by construction. The `fields` escape hatch still
232
+ * cannot land managed fields — see the `Custom.*` check below.
233
+ * planRevision / planHash / baselineSha remain Feature-only at the type
234
+ * level (PBI/Bug args don't carry them at all); effort rides Feature +
235
+ * PBI + Bug. */
236
+ function buildWriteOps(args: { title?: string; description?: string; assignedTo?: string; acceptanceCriteria?: string; priority?: number; effort?: number; startDate?: string; targetDate?: string; state?: string; comment?: string; planRevision?: number; planHash?: string; baselineSha?: string; fields?: Record<string, unknown>; parentId?: number; predecessors?: number[]; related?: number[]; }): any[] {
169
237
  const ops: any[] = [];
170
238
  const add = (field: string, value: unknown) => { if (value !== undefined) ops.push({ op: 'add', path: `/fields/${field}`, value }); };
171
- add('System.Title', args.title); add('System.Description', args.description); add('System.AssignedTo', args.assignedTo); add('Microsoft.VSTS.Common.AcceptanceCriteria', args.acceptanceCriteria); add('Microsoft.VSTS.Common.Priority', args.priority); add('Microsoft.VSTS.Scheduling.StartDate', args.startDate); add('Microsoft.VSTS.Scheduling.TargetDate', args.targetDate); add('System.State', args.state); add('System.History', args.comment);
172
- // T3: the named plan-field arguments (validated Feature-only by the
173
- // caller) write the presumed Custom.* reference names.
239
+ add('System.Title', args.title); add('System.Description', args.description); add('System.AssignedTo', args.assignedTo); add('Microsoft.VSTS.Common.AcceptanceCriteria', args.acceptanceCriteria); add('Microsoft.VSTS.Common.Priority', args.priority); add('Microsoft.VSTS.Scheduling.Effort', args.effort); add('Microsoft.VSTS.Scheduling.StartDate', args.startDate); add('Microsoft.VSTS.Scheduling.TargetDate', args.targetDate); add('System.State', args.state); add('System.History', args.comment);
240
+ // T3: the named plan-field arguments are Feature-only; the type-specific
241
+ // caller is responsible for never passing them on PBI/Bug.
174
242
  add('Custom.PlanRevision', args.planRevision); add('Custom.PlanHash', args.planHash); add('Custom.BaselineSha', args.baselineSha);
175
243
  for (const [field, value] of Object.entries(args.fields ?? {})) {
176
244
  if (!field.startsWith('Custom.')) throw new Error(`fields may only set Custom.* fields; '${field}' is managed by this tool`);
@@ -184,14 +252,17 @@ function buildWriteOps(args: WorkItemWriteArgs): any[] {
184
252
  return ops;
185
253
  }
186
254
 
187
- /** T3: the plan fields exist on Features only — PBIs and Bugs carry none. */
188
- function assertPlanFieldsAllowed(type: string | undefined): void {
189
- if (type === 'Feature') return;
255
+ /**
256
+ * T3: the plan fields exist on Features only — PBIs and Bugs carry none.
257
+ * Both call sites below already guard `type !== 'Feature'` before calling, so
258
+ * this refuses unconditionally; the caller owns the type test.
259
+ */
260
+ function assertPlanFieldsAllowed(): void {
190
261
  throw new Error('plan fields (planRevision, planHash, baselineSha) are Feature-only');
191
262
  }
192
263
 
193
264
  /** T3 value shape: a plan revision is a positive integer; the hashes are non-empty strings. */
194
- function assertPlanFieldValues(args: WorkItemWriteArgs): void {
265
+ function assertPlanFieldValues(args: { planRevision?: number; planHash?: string; baselineSha?: string }): void {
195
266
  if (args.planRevision !== undefined && (!Number.isInteger(args.planRevision) || (args.planRevision as number) < 1)) {
196
267
  throw new Error('planRevision must be a positive integer');
197
268
  }
@@ -203,9 +274,139 @@ function assertPlanFieldValues(args: WorkItemWriteArgs): void {
203
274
  }
204
275
  }
205
276
 
206
- export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<string> {
277
+ /** Priority is the literal set {1, 2, 3, 4} — 1=Very High, 2=High, 3=Medium, 4=Low. Not any integer. */
278
+ function assertPriority(priority: number): void {
279
+ if (!Number.isInteger(priority) || ![1, 2, 3, 4].includes(priority)) {
280
+ throw new Error('priority must be one of 1 (Very High), 2 (High), 3 (Medium), 4 (Low)');
281
+ }
282
+ }
283
+
284
+ /** Effort is days, finite, ≥ 0. Floats accepted. */
285
+ function assertEffort(effort: number): void {
286
+ if (!Number.isFinite(effort) || effort < 0) {
287
+ throw new Error('effort must be a non-negative number (days; floats accepted)');
288
+ }
289
+ }
290
+
291
+ /** Every heading in `headings` must appear in `description` as an H2 section.
292
+ * Headings may sit at the start of the description, on their own line, or
293
+ * after a blank line — the gate only checks presence, not prose quality
294
+ * (azdo-type-split plan § Out of Scope). */
295
+ function assertDescriptionHasHeadings(description: string, headings: readonly string[], shapeName: string): void {
296
+ for (const heading of headings) {
297
+ // The heading is present if any line of the description equals `## <heading>`
298
+ // (a heading at the start of a line) — case-sensitive, exact. Matches
299
+ // the heading at end of description with or without a trailing newline.
300
+ const atLineStart = new RegExp(`(^|\\n)\\s*##\\s+${heading.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\b`);
301
+ if (!atLineStart.test(description)) {
302
+ throw new Error(`description must carry the '${heading}' section (${shapeName})`);
303
+ }
304
+ }
305
+ }
306
+
307
+ /** The board file each type's description is written from, and the H2 sections
308
+ * that file carries — a Feature from intent.md, a PBI and a Bug from their
309
+ * item file. */
310
+ const DESCRIPTION_SHAPE: Record<AllowedType, { file: string; headings: readonly string[] }> = {
311
+ Feature: { file: 'intent.md', headings: ['Why', 'Outcome', 'Constraints', 'Non-goals'] },
312
+ 'Product Backlog Item': { file: 'item file', headings: ['What it delivers', 'Verification'] },
313
+ Bug: {
314
+ file: 'bug item file',
315
+ headings: ['Current behaviour', 'Expected behaviour', 'Behaviour that must not change', 'Responsible Feature', 'Verification'],
316
+ },
317
+ };
318
+
319
+ /** What the description is missing: nothing, or the sections it must carry.
320
+ * Returns the wording for a refusal rather than throwing, so the caller can
321
+ * list every missing field in one message. */
322
+ function missingDescription(type: AllowedType, description: string | undefined): string | undefined {
323
+ if (description === undefined || description.trim() === '') return 'non-empty';
324
+ const { file, headings } = DESCRIPTION_SHAPE[type];
325
+ try {
326
+ assertDescriptionHasHeadings(description, headings, `${file} shape`);
327
+ return undefined;
328
+ } catch {
329
+ return `carrying ${headings.map((heading) => `## ${heading}`).join(', ')} (${file})`;
330
+ }
331
+ }
332
+
333
+ /** Every create lands at New: a Feature walks its own ladder only after it
334
+ * exists, and a PBI/Bug starts at New (G1 approval, merge automation and
335
+ * human cancellation all happen later). */
336
+ function assertCreateState(type: AllowedType, state: string | undefined): void {
337
+ if (state === undefined || state === 'New') return;
338
+ if (type === 'Feature') {
339
+ throw new Error('Features are created only at New; move the Feature along its ladder after create');
340
+ }
341
+ const violations = evaluateWorkflowTransition(type, 'New', state);
342
+ if (violations.length) throw new Error(formatWorkflowRefusal(violations));
343
+ }
344
+
345
+ /** Per-type mandatory fields (Tarun, 2026-10-15), create path:
346
+ * - Feature: title, parentId (an Epic), priority, description (intent.md shape).
347
+ * - PBI / Bug: title, parentId (a Feature), description (item file shape), effort.
348
+ * Runs before any network call and lists every missing field in one message. */
349
+ function assertMandatoryOnCreate(type: AllowedType, args: WorkItemWriteArgs): void {
350
+ const missing: string[] = [];
351
+ if (args.title === undefined || args.title.trim() === '') missing.push('title');
352
+ if (args.parentId === undefined) missing.push('parentId');
353
+ else if (!Number.isInteger(args.parentId) || args.parentId < 1) missing.push('parentId (a positive integer)');
354
+ const missingShape = missingDescription(type, args.description);
355
+ if (missingShape !== undefined) missing.push(`description (${missingShape})`);
356
+ if (type === 'Feature') {
357
+ if (args.priority === undefined) missing.push('priority (1=Very High, 2=High, 3=Medium, 4=Low)');
358
+ else assertPriority(args.priority);
359
+ } else if (args.effort === undefined) {
360
+ missing.push('effort (days)');
361
+ } else {
362
+ assertEffort(args.effort);
363
+ }
364
+ if (missing.length) {
365
+ throw new Error(`a ${type} create requires ${missing.join(', ')}`);
366
+ }
367
+ }
368
+
369
+ /** Per-type mandatory fields on the update path (Tarun, 2026-10-15). Every
370
+ * update carries a comment. A Feature at New must re-send its create fields;
371
+ * the New → In Progress move additionally requires the acceptance criteria
372
+ * itemised per requirements.md and the three plan fields. A PBI or Bug carries
373
+ * nothing beyond the comment once it exists. */
374
+ function assertMandatoryOnUpdate(type: AllowedType, currentState: string, targetState: string, args: WorkItemWriteArgs): void {
375
+ assertUpdateComment(args.comment);
376
+ if (type !== 'Feature' || currentState !== 'New') return;
377
+ const missing: string[] = [];
378
+ if (args.title === undefined || args.title.trim() === '') missing.push('title');
379
+ if (args.parentId === undefined) missing.push('parentId');
380
+ if (args.priority === undefined) missing.push('priority');
381
+ else assertPriority(args.priority);
382
+ const missingShape = missingDescription(type, args.description);
383
+ if (missingShape !== undefined) missing.push(`description (${missingShape})`);
384
+ if (targetState === 'In Progress') {
385
+ if (args.acceptanceCriteria === undefined || args.acceptanceCriteria.trim() === '') missing.push('acceptanceCriteria');
386
+ if (args.planRevision === undefined) missing.push('planRevision');
387
+ if (args.planHash === undefined || args.planHash.trim() === '') missing.push('planHash');
388
+ if (args.baselineSha === undefined || args.baselineSha.trim() === '') missing.push('baselineSha');
389
+ }
390
+ if (missing.length) {
391
+ throw new Error(`a Feature at New requires ${missing.join(', ')} on this update`);
392
+ }
393
+ }
394
+
395
+ /** `comment` is mandatory on every update; on create, it's optional.
396
+ * Trims; throws on empty/whitespace. */
397
+ function assertUpdateComment(comment: string | undefined): void {
398
+ if (typeof comment !== 'string' || comment.trim() === '') {
399
+ throw new Error('comment is required on every update (mandatory evidence)');
400
+ }
401
+ }
402
+
403
+ /** The one write implementation behind the three per-type tools. `type` is the
404
+ * tool's own name, never an argument — a Feature write can no longer land on a
405
+ * PBI by passing the wrong `type`. */
406
+ async function writeWorkItem(type: AllowedType, args: WorkItemWriteArgs): Promise<string> {
207
407
  if (args.id === undefined) {
208
- assertAllowedType(args.type); if (!args.title) throw new Error('title is required for create'); if (args.parentId === undefined) throw new Error(`${args.type} requires parentId`);
408
+ // Per-type mandatory fields (Tarun, 2026-10-15) run before any network.
409
+ assertMandatoryOnCreate(type, args);
209
410
  assertPlanFieldValues(args);
210
411
  // FENCE 1 (create): System.State via the raw fields escape hatch is
211
412
  // fenced for the gated types — the dedicated `state` argument exists so
@@ -213,7 +414,7 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
213
414
  // Feature ladder instead (F3, evaluateFeatureTransition); for them the
214
415
  // generic managed-field refusal in buildWriteOps still applies
215
416
  // (unchanged behavior).
216
- if (args.type !== 'Feature' && args.fields !== undefined && 'System.State' in args.fields) {
417
+ if (type !== 'Feature' && args.fields !== undefined && 'System.State' in args.fields) {
217
418
  throw new Error('System.State via raw fields is fenced — pass the dedicated `state` argument so the workflow gate can evaluate it.');
218
419
  }
219
420
  // FENCE 2 (create): a new PBI/Bug starts at New, so a create whose
@@ -222,38 +423,31 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
222
423
  // create. A Feature is created only at New (F3): any other requested
223
424
  // state is refused outright, and the POST below carries no
224
425
  // System.State op for Features at all.
225
- if (args.type === 'Feature') {
226
- if (args.state !== undefined && args.state !== 'New') {
227
- throw new Error('Features are created only at New; move the Feature along its ladder after create');
228
- }
229
- } else if (args.state !== undefined && args.state !== 'New') {
230
- const violations = evaluateWorkflowTransition(args.type, 'New', args.state)
231
- if (violations.length) throw new Error(formatWorkflowRefusal(violations))
232
- }
426
+ assertCreateState(type, args.state);
233
427
  // T3: plan fields are Feature-only (type is known at create).
234
- if (args.type !== 'Feature' && (args.planRevision !== undefined || args.planHash !== undefined || args.baselineSha !== undefined)) {
235
- assertPlanFieldsAllowed(args.type);
428
+ if (type !== 'Feature' && (args.planRevision !== undefined || args.planHash !== undefined || args.baselineSha !== undefined)) {
429
+ assertPlanFieldsAllowed();
236
430
  }
237
431
  // F3: a Feature is created only at New, so the create POST carries no System.State op.
238
432
  const createArgs: WorkItemWriteArgs = { ...args };
239
433
  delete createArgs.state;
240
- const ops = buildWriteOps(args.type === 'Feature' ? createArgs : args);
434
+ const ops = buildWriteOps(type === 'Feature' ? createArgs : args);
241
435
  const c = await getClient();
242
436
  const parent = await c.get<any>(`/wit/workitems/${args.parentId}`, { 'api-version': '7.1' });
243
- const expected = args.type === 'Feature' ? 'Epic' : 'Feature';
244
- if (parent.fields?.['System.WorkItemType'] !== expected) throw new Error(`${args.type} must be parented to a ${expected}; work item ${args.parentId} is a ${parent.fields?.['System.WorkItemType']}`);
437
+ const expected = type === 'Feature' ? 'Epic' : 'Feature';
438
+ if (parent.fields?.['System.WorkItemType'] !== expected) throw new Error(`${type} must be parented to a ${expected}; work item ${args.parentId} is a ${parent.fields?.['System.WorkItemType']}`);
245
439
  // L1: a child may not be added under a closed Feature (Awaiting
246
440
  // Release or Done), on create.
247
441
  if (expected === 'Feature' && CLOSED_FEATURE_STATES.has(parent.fields?.['System.State'])) {
248
442
  throw new Error(`work item ${args.parentId} is a Feature at ${parent.fields?.['System.State']}: no child can be added to a closed Feature (L1)`);
249
443
  }
250
444
  const warnings: string[] = [];
251
- if (args.type === 'Feature') {
445
+ if (type === 'Feature') {
252
446
  // L2 (soft, staged refusal): a Feature needs at least one
253
447
  // predecessor Feature; only the very first Feature in a project
254
448
  // (no other Features besides Fixes containers) carries none.
255
449
  if (!(args.predecessors ?? []).length && await projectHasNonFixesFeature(c)) warnings.push(L2_WARNING);
256
- } else if (args.type === 'Bug' && parent.fields?.['System.Title'] === FIXES_FEATURE_TITLE) {
450
+ } else if (type === 'Bug' && parent.fields?.['System.Title'] === FIXES_FEATURE_TITLE) {
257
451
  // L4 (hard): a Bug under 'Fixes to closed Features' needs a Related
258
452
  // link to its Responsible Feature, in any state, and its predecessor
259
453
  // must be another Bug in the same Fixes Feature (none for the first).
@@ -263,12 +457,11 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
263
457
  // L3 (soft, staged refusal): a PBI carries no predecessor links.
264
458
  warnings.push(L3_WARNING);
265
459
  }
266
- const created = await c.post<any>(`/wit/workitems/$${encodeURIComponent(args.type)}`, ops, { 'api-version': '7.1' }, 'application/json-patch+json');
460
+ const created = await c.post<any>(`/wit/workitems/$${encodeURIComponent(type)}`, ops, { 'api-version': '7.1' }, 'application/json-patch+json');
267
461
  const createdView = formatItem(created, false);
268
462
  if (warnings.length) createdView.warnings = warnings;
269
463
  return JSON.stringify(createdView, null, 2);
270
464
  }
271
- if (args.type !== undefined) throw new Error('type cannot be changed on an existing work item');
272
465
  // System.State arriving through the raw fields escape hatch is deferred:
273
466
  // its refusal is type-dependent (FENCE 1 below) and the type is only known
274
467
  // after the read. Every OTHER managed field is still refused inside
@@ -276,15 +469,23 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
276
469
  const fields = args.fields ?? {};
277
470
  const stateViaRawFields = 'System.State' in fields;
278
471
  const fieldsForOps = stateViaRawFields ? Object.fromEntries(Object.entries(fields).filter(([name]) => name !== 'System.State')) : fields;
279
- let ops = buildWriteOps({ ...args, fields: fieldsForOps }); if (!ops.length && !stateViaRawFields) throw new Error('nothing to write: no fields, state, comment, or relations were supplied');
472
+ let ops = buildWriteOps({ ...args, fields: fieldsForOps }); if (!ops.length && !stateViaRawFields && args.removePredecessor === undefined && args.removeRelated === undefined) throw new Error('nothing to write: no fields, state, comment, or relations were supplied');
280
473
  const c = await getClient();
281
474
  const current = await c.get<any>(`/wit/workitems/${args.id}`, { 'api-version': '7.1', $expand: 'all' });
282
475
  assertTracked(current);
283
476
  const currentType: AllowedType = current.fields?.['System.WorkItemType'];
477
+ // The tool name IS the type: writing a PBI through `..._feature` is refused
478
+ // by name rather than silently landing on the item.
479
+ if (currentType !== type) {
480
+ throw new Error(`work item ${args.id} is a ${currentType}; write it with the workitem_create_or_update_${TOOL_SUFFIX[currentType]} tool`);
481
+ }
482
+ const currentState = current.fields?.['System.State']
483
+ // Per-type mandatory fields on an update (Tarun, 2026-10-15).
484
+ assertMandatoryOnUpdate(type, currentState, args.state ?? currentState, args);
284
485
  assertPlanFieldValues(args);
285
486
  // T3: plan fields are Feature-only (the type is only known after the read).
286
487
  if (currentType !== 'Feature' && (args.planRevision !== undefined || args.planHash !== undefined || args.baselineSha !== undefined)) {
287
- assertPlanFieldsAllowed(currentType);
488
+ assertPlanFieldsAllowed();
288
489
  }
289
490
  // FENCE 1: System.State via raw fields on a gated type. Features keep the
290
491
  // generic managed-field refusal instead; their ladder runs through the
@@ -297,7 +498,6 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
297
498
  // that moves System.State — a move that skips gates, reaches a human-owned
298
499
  // state (Approved/Done/Removed) or walks off the ladder is refused. Writes
299
500
  // that leave the state untouched are not gated.
300
- const currentState = current.fields?.['System.State']
301
501
  if (currentType === 'Feature') {
302
502
  // F5: the standing Fixes container never walks the Feature ladder.
303
503
  if (current.fields?.['System.Title'] === FIXES_FEATURE_TITLE && (args.state ?? currentState) !== 'In Progress') {
@@ -346,13 +546,31 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
346
546
  .map((relation: any, index: number) => relation.rel === 'System.LinkTypes.Hierarchy-Reverse' ? index : -1)
347
547
  .filter((index: number) => index >= 0)
348
548
  .sort((left: number, right: number) => right - left);
349
- const parentAdd = ops.find((operation) => operation.path === '/relations/-' && operation.value?.rel === 'System.LinkTypes.Hierarchy-Reverse');
549
+ // buildWriteOps emitted the Hierarchy-Reverse add for every parentId, so the find always hits.
550
+ const parentAdd = ops.find((operation) => operation.path === '/relations/-' && operation.value?.rel === 'System.LinkTypes.Hierarchy-Reverse')!;
350
551
  ops = [
351
552
  ...ops.filter((operation) => operation !== parentAdd),
352
553
  ...oldParentIndexes.map((index: number) => ({ op: 'remove', path: `/relations/${index}` })),
353
- ...(parentAdd ? [parentAdd] : []),
554
+ parentAdd,
354
555
  ];
355
556
  }
557
+ // az698: --remove-predecessor / --remove-related drop a single relation
558
+ // link by id. The relation index comes from the LIVE read of `current.relations`
559
+ // (mirrors the parent-remove pattern above), and the op is idempotent: a
560
+ // remove on an id that is not present is a no-op (zero ops added), and the
561
+ // PATCH still lands for the rest of the write. L1–L4 re-validation below
562
+ // uses the post-write set (current ∪ added − removed) for the L4 check.
563
+ for (const [rel, removeId] of [
564
+ ['System.LinkTypes.Dependency-Reverse', args.removePredecessor],
565
+ ['System.LinkTypes.Related', args.removeRelated],
566
+ ] as const) {
567
+ if (removeId === undefined) continue;
568
+ const indexes = (current.relations ?? [])
569
+ .map((relation: any, index: number) => relation.rel === rel && Number(/(\d+)\/?$/.exec(relation.url ?? '')?.[1]) === removeId ? index : -1)
570
+ .filter((index: number) => index >= 0)
571
+ .sort((left: number, right: number) => right - left);
572
+ for (const index of indexes) ops.push({ op: 'remove', path: `/relations/${index}` });
573
+ }
356
574
  const warnings: string[] = [];
357
575
  if (currentType === 'Bug') {
358
576
  // L4 (hard, in any state): a Bug under 'Fixes to closed Features'
@@ -360,8 +578,14 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
360
578
  // predecessor must be a sibling Bug in the same Fixes Feature.
361
579
  const parent = updateParent ?? await fetchItemParent(c, current);
362
580
  if (parent !== undefined && parent.fields?.['System.Title'] === FIXES_FEATURE_TITLE) {
363
- const relatedIds = [...relationIds(current, 'System.LinkTypes.Related'), ...(args.related ?? [])];
364
- if (!relatedIds.length) {
581
+ // az698: the post-write Related set is current ∪ added − removed. A
582
+ // --remove-related that would clear the last Related link to the
583
+ // Responsible Feature is refused here, before any PATCH, with the
584
+ // same L4 wording the add path uses.
585
+ const relatedAfter = new Set(relationIds(current, 'System.LinkTypes.Related'));
586
+ for (const id of args.related ?? []) relatedAfter.add(id);
587
+ if (args.removeRelated !== undefined) relatedAfter.delete(args.removeRelated);
588
+ if (!relatedAfter.size) {
365
589
  throw new Error(`a Bug under '${FIXES_FEATURE_TITLE}' needs a Related link to its Responsible Feature (L4)`);
366
590
  }
367
591
  const predecessorIds = [...new Set([...relationIds(current, 'System.LinkTypes.Dependency-Reverse'), ...(args.predecessors ?? [])])];
@@ -375,8 +599,11 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
375
599
  } else if (args.predecessors !== undefined) {
376
600
  // L2 (soft): a Feature's predecessor set after this write must be
377
601
  // non-empty once any other Feature exists in the project.
378
- const predecessorIds = [...new Set([...relationIds(current, 'System.LinkTypes.Dependency-Reverse'), ...args.predecessors])];
379
- if (!predecessorIds.length && await projectHasNonFixesFeature(c, args.id)) warnings.push(L2_WARNING);
602
+ // az698: a --remove-predecessor is part of the post-write set.
603
+ const predecessorAfter = new Set(relationIds(current, 'System.LinkTypes.Dependency-Reverse'));
604
+ for (const id of args.predecessors) predecessorAfter.add(id);
605
+ if (args.removePredecessor !== undefined) predecessorAfter.delete(args.removePredecessor);
606
+ if (!predecessorAfter.size && await projectHasNonFixesFeature(c, args.id)) warnings.push(L2_WARNING);
380
607
  }
381
608
  const updated = await c.patch<any>(`/wit/workitems/${args.id}`, ops, { 'api-version': '7.1' }, 'application/json-patch+json');
382
609
  const updatedView = formatItem(updated, false);
@@ -384,6 +611,28 @@ export async function workitemCreateOrUpdate(args: WorkItemWriteArgs): Promise<s
384
611
  return JSON.stringify(updatedView, null, 2);
385
612
  }
386
613
 
614
+ /** Create or update a Feature under its Epic (Tarun's per-type write surface).
615
+ * Mandatory: title, parentId, priority, description (intent.md shape) on
616
+ * create and while the Feature is at New; acceptanceCriteria plus the three
617
+ * plan fields on the New → In Progress move; a comment on every update. */
618
+ export function workitemCreateOrUpdateFeature(args: WorkItemFeatureWriteArgs): Promise<string> {
619
+ return writeWorkItem('Feature', args);
620
+ }
621
+
622
+ /** Create or update a PBI under its Feature. Mandatory on create: title,
623
+ * parentId, description (item/*.md shape), effort in days. A comment is
624
+ * mandatory on every update. */
625
+ export function workitemCreateOrUpdatePbi(args: WorkItemPbiWriteArgs): Promise<string> {
626
+ return writeWorkItem('Product Backlog Item', args);
627
+ }
628
+
629
+ /** Create or update a Bug under its Feature (usually a Fixes Feature).
630
+ * Mandatory on create: title, parentId, description (item/*.md shape), effort
631
+ * in days. A comment is mandatory on every update. */
632
+ export function workitemCreateOrUpdateBug(args: WorkItemBugWriteArgs): Promise<string> {
633
+ return writeWorkItem('Bug', args);
634
+ }
635
+
387
636
  /** L3/L4: the parent of an item, fetched through its Hierarchy-Reverse relation (undefined when parentless). */
388
637
  async function fetchItemParent(c: AdoClient, item: any): Promise<any | undefined> {
389
638
  const [parentId] = relationIds(item, 'System.LinkTypes.Hierarchy-Reverse');