@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.46 → 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 (174) hide show
  1. package/bin/adl +1 -1
  2. package/bin/reconcile +7 -2
  3. package/package.json +1 -1
  4. package/packages/agent-hooks/src/rules/impl/git-branch.ts +1 -1
  5. package/packages/agent-skills/assets/write-feature/SKILL.md +13 -15
  6. package/bin/lean-ctx-observe-strip +0 -434
  7. package/bin/lib/lean-ctx-observe-hooks.sh +0 -634
  8. package/bin/update-azure-agent-pool +0 -29
  9. package/packages/agent-hooks/src/hooks/lean-ctx/handler.ts +0 -22
  10. package/packages/agent-hooks/src/hooks/lean-ctx/index.ts +0 -173
  11. package/packages/agent-hooks/src/hooks/lean-ctx/mapping.ts +0 -121
  12. package/packages/agent-hooks/src/hooks/lean-ctx/scope-rewrite.ts +0 -207
  13. package/packages/agent-hooks/src/lean-ctx/env.ts +0 -123
  14. package/packages/agent-hooks/src/lean-ctx/mcp.ts +0 -142
  15. package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +0 -51
  16. package/packages/agent-hooks/src/rules/impl/adl-session-required.ts +0 -403
  17. package/packages/agent-hooks/src/rules/impl/tool-lean-ctx-global.ts +0 -594
  18. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +0 -112
  19. package/packages/agent-settings/assets/lean-ctx/config.toml +0 -42
  20. package/packages/agent-settings/assets/lean-ctx/gitignore +0 -10
  21. package/packages/agent-settings/assets/lean-ctx/layout.toml +0 -7
  22. package/packages/agent-settings/src/deploy/lean-ctx-deploy.ts +0 -230
  23. package/packages/agent-settings/src/deploy/lean-ctx-merge.ts +0 -580
  24. package/packages/agent-skills/assets/adl-mechanics/SKILL.md +0 -38
  25. package/packages/agent-skills/assets/codebase-design/SKILL.md +0 -98
  26. package/packages/agent-skills/assets/codebase-design/references/DEEPENING.md +0 -37
  27. package/packages/agent-skills/assets/codebase-design/references/DESIGN-IT-TWICE.md +0 -44
  28. package/packages/agent-skills/assets/codebase-design/references/TESTABILITY.md +0 -29
  29. package/packages/agent-skills/assets/debug-issue/SKILL.md +0 -41
  30. package/packages/agent-skills/assets/domain-modeling/SKILL.md +0 -86
  31. package/packages/agent-skills/assets/domain-modeling/references/ADR-FORMAT.md +0 -47
  32. package/packages/agent-skills/assets/domain-modeling/references/CONTEXT-FORMAT.md +0 -60
  33. package/packages/agent-skills/assets/implement/SKILL.md +0 -28
  34. package/packages/agent-skills/assets/review-changes/SKILL.md +0 -45
  35. package/packages/agent-skills/assets/tdd/SKILL.md +0 -49
  36. package/packages/agent-skills/assets/tdd/references/mocking.md +0 -59
  37. package/packages/agent-skills/assets/tdd/references/tests.md +0 -77
  38. package/packages/agent-skills/assets/testing/SKILL.md +0 -54
  39. package/packages/agent-skills/assets/testing/references/e2e.md +0 -37
  40. package/packages/agent-skills/assets/testing/references/integration.md +0 -37
  41. package/packages/agent-skills/assets/testing/references/unit.md +0 -32
  42. package/packages/agent-skills/assets/to-feature/SKILL.md +0 -91
  43. package/packages/agent-skills/assets/to-feature/references/FEATURE-SPEC-TEMPLATE.md +0 -88
  44. package/packages/agent-skills/assets/to-workitems/SKILL.md +0 -90
  45. package/packages/agent-skills/assets/to-workitems/references/AZURE-DEVOPS-HIERARCHY.md +0 -121
  46. package/packages/agent-skills/assets/to-workitems/references/TICKET-TEMPLATES.md +0 -70
  47. package/packages/azdo/src/workflow-rules-matrix.csv +0 -85
  48. package/packages/fleet/src/launch-agents.ts +0 -649
  49. package/packages/repo-controls/assets/GLOBAL-INVARIANTS.md +0 -21
  50. package/packages/repo-controls/assets/enable-integration-auto-complete.mjs +0 -79
  51. package/packages/repo-controls/assets/scaffold/docs/notes.md +0 -20
  52. package/packages/repo-controls/assets/scaffold/plans/GAPS-AND-FINDINGS.md +0 -12
  53. package/packages/repo-controls/azure-pipelines.yml +0 -274
  54. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +0 -1
  55. 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
  56. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +0 -17
  57. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +0 -13
  58. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-delivery.md +0 -10
  59. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-recommendation.md +0 -12
  60. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/target-narration.md +0 -10
  61. 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
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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
  68. 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
  69. 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
  70. 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
  71. 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
  72. 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
  73. 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
  74. 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
  75. 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
  76. 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
  77. 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
  78. 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
  79. 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
  80. 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
  81. 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
  82. 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
  83. 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
  84. 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
  85. 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
  86. 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
  87. 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
  88. 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
  89. 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
  90. 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
  91. 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
  92. 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
  93. 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
  94. 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
  95. 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
  96. 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
  97. 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
  98. 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
  99. 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
  100. 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
  101. 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
  102. 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
  103. 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
  104. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/mobile/assets/images/icon.png +0 -1
  105. 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
  106. 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
  107. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/public/pwa/icon-192.png +0 -1
  108. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/resources/product.png +0 -1
  109. 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
  110. 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
  111. 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
  112. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/__init__.py +0 -11
  113. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/check.py +0 -62
  114. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/NotAMigration.sql +0 -1
  115. 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
  116. 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
  117. 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
  118. 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
  119. 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
  120. 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
  121. 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
  122. 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
  123. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/test_check.py +0 -116
  124. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/links.md +0 -20
  125. 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
  126. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/links.md +0 -20
  127. 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
  128. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.changeset/declared.txt +0 -1
  129. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/INDEPENDENT-VERIFICATION.md +0 -3
  130. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/.gitattributes +0 -1
  131. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/large file.bin +0 -3
  132. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/.gitattributes +0 -1
  133. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/large file.bin +0 -3
  134. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/small.txt +0 -1
  135. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/missing-filter/large file.bin +0 -0
  136. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/.gitattributes +0 -1
  137. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/payload.dat +0 -0
  138. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/__init__.py +0 -1
  139. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/__init__.py +0 -12
  140. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/check.py +0 -129
  141. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/disjoint/gate-registry.yaml +0 -13
  142. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/empty/gate-registry.yaml +0 -7
  143. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/overlapping/gate-registry.yaml +0 -13
  144. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/test_check.py +0 -109
  145. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/__init__.py +0 -1
  146. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/__init__.py +0 -11
  147. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/check.py +0 -149
  148. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/clean/docs/readme.md +0 -4
  149. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/relevant/VERSION +0 -1
  150. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/test_check.py +0 -151
  151. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/__init__.py +0 -11
  152. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/check.py +0 -202
  153. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/changesets/package.json +0 -5
  154. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/package.json +0 -5
  155. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/CHANGELOG.md +0 -5
  156. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/VERSION +0 -1
  157. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/changesets/package.json +0 -5
  158. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/package.json +0 -5
  159. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/CHANGELOG.md +0 -5
  160. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/VERSION +0 -1
  161. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/test_check.py +0 -162
  162. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/check.py +0 -103
  163. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.lock +0 -7
  164. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.toml +0 -4
  165. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/rust-toolchain.toml +0 -3
  166. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/src/main.rs +0 -1
  167. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/frontend/bun.lock +0 -1
  168. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.lock +0 -7
  169. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.toml +0 -4
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/rust-toolchain.toml +0 -3
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/src/main.rs +0 -1
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/frontend/bun.lock +0 -0
  173. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/grants-registry.yaml +0 -112
  174. package/packages/repo-controls/workflows/scripts/release/verify-npm-artifact.ts +0 -119
@@ -1,580 +0,0 @@
1
- /**
2
- * The merge contract for a repo-local lean-ctx file, corrected by a
3
- * SECOND independent review (2026-09-29) after the marker-region design
4
- * itself shipped. Three real bugs found there:
5
- *
6
- * 3. HIGH — `shell_allowlist_extra` lived INSIDE the managed block, so a
7
- * real `lean-ctx allow <cmd>` writing an entry into it was wiped by
8
- * the very next deploy's self-heal (a whole-block replace). DECISION:
9
- * the array leaves the managed block and becomes a UNION key —
10
- * resolved by a narrow text scan (string literals only), adl's
11
- * required entries inserted before `]` if missing, nothing ever
12
- * removed or reordered.
13
- * 4. HIGH — migration required the asset's CURRENT text to be found
14
- * verbatim in the existing file. The asset changing at all (as it
15
- * legitimately does, e.g. this same fix) broke migration for every
16
- * already-enrolled repo, including this one. DECISION: migration now
17
- * classifies the existing (markerless) file LINE BY LINE — a line
18
- * matching a KNOWN adl key (`minimal_overhead`, ..., `mode`) is adl's
19
- * own, replaced by the canonical value regardless of what it used to
20
- * say; a blank line is dropped (formatting only); anything else —
21
- * an unrecognized key, a table header, OR A COMMENT (finding 4-minor,
22
- * independent review 2026-09-30, superseding this bullet's original
23
- * "comments are dropped too" — see `migrateByKnownKeys`'s own doc
24
- * comment for why) — is the user's, kept verbatim, in place, after
25
- * the canonical block. This never depends on the asset's exact prior
26
- * text, so a template change cannot break migration again.
27
- * 5. MED — two blind spots: a quoted duplicate key AFTER the block, and
28
- * a user table header BEFORE it (which silently turns adl's own keys
29
- * into `<table>.<key>`), both passed as "unchanged" under pure
30
- * text/region matching. DECISION: after composing (never
31
- * re-serializing — this validates the ACTUAL COMPOSED TEXT, whether
32
- * that text turned out unchanged, self-healed, or migrated), the
33
- * WHOLE file is parsed with a real TOML parser (`Bun.TOML`, not
34
- * `@iarna/toml` — TOML 0.5, rejects valid TOML 1.0) and every adl key
35
- * must resolve AT THE TOP LEVEL to adl's own value; any parse
36
- * failure or mismatch is a conflict, never a silent "unchanged".
37
- * Independent review, 2026-09-30, finding 5-rest: this validation is
38
- * only as good as `scalarValuesOf`'s own coverage — a version that
39
- * recognized only `true`/`false` left layout.toml's own `mode = "xdg"`
40
- * (a string) OUT of `expectedScalars` entirely, so a table header
41
- * before ITS block passed silently. `scalarValuesOf` now parses every
42
- * scalar shape a managed block can carry (bool, number, quoted
43
- * string, simple string array), closing that gap for every target,
44
- * not just config.toml's boolean keys.
45
- *
46
- * Marker-region mechanics (BEGIN/END, malformed markers, migration) are
47
- * shared by every target — config.toml's scalar keys, layout.toml's
48
- * whole content, and (with an append-not-migrate variant — see finding 6,
49
- * `resolveGitignoreWrite`) the `.agents/.gitignore` block. config.toml
50
- * additionally carries the union-key array outside its own block
51
- * (`resolveConfigTomlWrite`).
52
- */
53
-
54
- export const LEAN_CTX_MARKER_START = '# >>> adl managed (do not edit within this block) >>>';
55
- export const LEAN_CTX_MARKER_END = '# <<< adl managed <<<';
56
- export const SHELL_ALLOWLIST_KEY = 'shell_allowlist_extra';
57
-
58
- interface Region {
59
- readonly start: number;
60
- /** Exclusive — the index immediately after the closing marker's own text. */
61
- readonly end: number;
62
- }
63
- type RegionResult = Region | 'missing' | 'malformed';
64
-
65
- function findRegion(content: string): RegionResult {
66
- const start = content.indexOf(LEAN_CTX_MARKER_START);
67
- const stop = content.indexOf(LEAN_CTX_MARKER_END);
68
- if (start < 0 && stop < 0) return 'missing';
69
- if (start < 0 || stop < 0 || stop < start) return 'malformed';
70
- return { start, end: stop + LEAN_CTX_MARKER_END.length };
71
- }
72
-
73
- /** The block's own content between (and including) its markers, no trailing newline. */
74
- function canonicalBlockOf(assetText: string): string {
75
- return assetText.replace(/\n+$/, '');
76
- }
77
-
78
- function spliceRegion(content: string, region: Region, canonicalBlock: string): string {
79
- return `${content.slice(0, region.start)}${canonicalBlock}${content.slice(region.end)}`;
80
- }
81
-
82
- /** `key = ...` names declared directly inside `canonicalBlock` — a plain line scan, not a TOML parse (this module never parses to decide what to write; only to VALIDATE the result — see `validateComposedToml`). */
83
- function declaredKeyNames(canonicalBlock: string): readonly string[] {
84
- const names: string[] = [];
85
- for (const line of canonicalBlock.split('\n')) {
86
- const match = /^([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line);
87
- if (match) names.push(match[1] as string);
88
- }
89
- return names;
90
- }
91
-
92
- function duplicateKeyOutsideRegion(content: string, region: Region, canonicalBlock: string): string | undefined {
93
- const outside = content.slice(0, region.start) + content.slice(region.end);
94
- for (const name of declaredKeyNames(canonicalBlock)) {
95
- if (new RegExp(`^\\s*${name}\\s*=`, 'm').test(outside)) return name;
96
- }
97
- return undefined;
98
- }
99
-
100
- /**
101
- * Migration (finding 4): classifies a markerless file the lock already
102
- * says adl wrote, line by line, against `knownKeys` for THIS target.
103
- * Never depends on the asset's own exact prior text — a `key = value`
104
- * line naming a known key is adl's own (dropped; the canonical block
105
- * replaces it outright, whatever it used to say); a blank line is
106
- * dropped (formatting only); anything else — including a full-line
107
- * comment — is kept verbatim, IN ORDER, as the user's.
108
- *
109
- * A comment is deliberately NOT dropped (independent review, 2026-09-30,
110
- * finding 4-minor — this superseded an earlier version that dropped
111
- * every comment line as "adl's own old commentary, not reliably told
112
- * apart from a user's by text alone"): dropping a genuine user comment
113
- * is permanent, silent data loss; keeping an old adl comment as harmless
114
- * extra text next to the canonical block (which carries its own fresh
115
- * comments regardless) costs nothing. When in doubt, keep it — the same
116
- * rule this module already applies to every OTHER unrecognized line.
117
- *
118
- * `recognizedCount` is how many lines matched a known key — the signal
119
- * that decides whether migration is safe AT ALL (`resolveLeanCtxWrite`'s
120
- * own caller): a file with at least one recognizable adl key is
121
- * plausibly adl's own prior content, whatever else is wrong with it; a
122
- * file with NONE (a whole-file clobber — `"tampered\n"`, Tarun 2026-09-29,
123
- * D.b: this exact scenario is a conflict, not a guess) has no evidence
124
- * left to migrate from at all.
125
- */
126
- function migrateByKnownKeys(content: string, knownKeys: ReadonlySet<string>): { readonly kept: readonly string[]; readonly recognizedCount: number } {
127
- const kept: string[] = [];
128
- let recognizedCount = 0;
129
- for (const line of content.split('\n')) {
130
- const trimmed = line.trim();
131
- if (trimmed.length === 0) continue;
132
- const match = /^([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(trimmed);
133
- if (match && knownKeys.has(match[1] as string)) {
134
- recognizedCount += 1;
135
- continue;
136
- }
137
- kept.push(line);
138
- }
139
- return { kept, recognizedCount };
140
- }
141
-
142
- /** One adl-owned scalar whose value differed and was restored to adl's own (2026-09-30, Tarun's merge-contract correction: "adl-owned entries SELF-HEAL" — a differing value is drift, never a conflict). Named on the write outcome so deploy/doctor can report it explicitly, the same way a healed hook is named. */
143
- export interface RestoredScalar {
144
- readonly key: string;
145
- readonly value: unknown;
146
- readonly previous: unknown;
147
- }
148
-
149
- export type LeanCtxWriteOutcome =
150
- | { readonly action: 'unchanged' }
151
- | { readonly action: 'write'; readonly text: string; readonly restoredScalars?: readonly RestoredScalar[] }
152
- | { readonly action: 'conflict'; readonly reason: string };
153
-
154
- /** Real TOML 1.0 parse (Bun's built-in, not `@iarna/toml` — TOML 0.5) — read-only validation, never used to decide what to write. */
155
- function parseToml(text: string): Record<string, unknown> | 'invalid' {
156
- try {
157
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Bun global, no @types/bun in this package's own deps
158
- const doc = (globalThis as any).Bun.TOML.parse(text);
159
- return typeof doc === 'object' && doc !== null && !Array.isArray(doc) ? (doc as Record<string, unknown>) : 'invalid';
160
- } catch {
161
- return 'invalid';
162
- }
163
- }
164
-
165
- /** True iff `actual` matches `expected`, where `expected` may be a scalar (compared by `===`) or an array (compared element-by-element, same length and order). */
166
- function tomlValueMatches(actual: unknown, expected: unknown): boolean {
167
- if (Array.isArray(expected)) {
168
- return Array.isArray(actual) && actual.length === expected.length && expected.every((entry, i) => actual[i] === entry);
169
- }
170
- return actual === expected;
171
- }
172
-
173
- /**
174
- * Finding 5's safety net: parses the COMPOSED text (whatever this module
175
- * is about to call "unchanged" or "write") and confirms every one of
176
- * `expectedScalars` resolves at the TOP LEVEL to its expected value, and
177
- * (when `expectedArrayEntries` is given) `arrayKey` resolves to an array
178
- * containing every one of them. A duplicate key elsewhere in the file, or
179
- * a table header before the managed block turning adl's own keys into
180
- * `<table>.<key>`, fails this — a conflict, never a silent pass.
181
- */
182
- function validateComposedToml(
183
- text: string,
184
- expectedScalars: Readonly<Record<string, unknown>>,
185
- arrayKey?: string,
186
- expectedArrayEntries?: readonly string[],
187
- ): string | undefined {
188
- const doc = parseToml(text);
189
- if (doc === 'invalid') return 'the composed file is not valid TOML';
190
- for (const [key, value] of Object.entries(expectedScalars)) {
191
- if (!tomlValueMatches(doc[key], value)) return `"${key}" does not resolve to adl's value at the top level (a table header before the managed block, or a duplicate key, is shadowing it)`;
192
- }
193
- if (arrayKey !== undefined && expectedArrayEntries !== undefined) {
194
- const actual = doc[arrayKey];
195
- if (!Array.isArray(actual)) return `"${arrayKey}" does not resolve to an array at the top level`;
196
- const missing = expectedArrayEntries.filter((entry) => !actual.includes(entry));
197
- if (missing.length > 0) return `"${arrayKey}" is missing adl-required entries: ${missing.join(', ')}`;
198
- }
199
- return undefined;
200
- }
201
-
202
- /**
203
- * Parses a single-line TOML scalar value — bool, integer/float, quoted
204
- * string, or a simple (string-literals-only) inline array — or returns
205
- * `undefined` for anything else (a multi-line value, a table, etc.),
206
- * which `scalarValuesOf` then simply skips rather than guessing.
207
- */
208
- function parseTomlScalarValue(raw: string): unknown {
209
- const trimmed = raw.trim();
210
- if (trimmed === 'true') return true;
211
- if (trimmed === 'false') return false;
212
- if (/^-?\d+(\.\d+)?$/.test(trimmed)) return Number(trimmed);
213
- const stringMatch = /^"((?:[^"\\]|\\.)*)"$/.exec(trimmed);
214
- if (stringMatch) return (stringMatch[1] as string).replace(/\\(.)/g, '$1');
215
- const arrayMatch = /^\[(.*)\]$/.exec(trimmed);
216
- if (arrayMatch) {
217
- const entries = parseSimpleStringArrayBody(arrayMatch[1] as string);
218
- if (entries !== 'malformed') return entries;
219
- }
220
- return undefined;
221
- }
222
-
223
- /**
224
- * Every `key = value` line's value, parsed generically (finding 5-rest,
225
- * independent review 2026-09-30 — this used to recognize `true`/`false`
226
- * only, so a STRING-valued key like layout.toml's own `mode = "xdg"` was
227
- * silently excluded from `validateComposedToml`'s `expectedScalars`
228
- * entirely, leaving that target's own table-header-before-block blind
229
- * spot uncaught; see this file's header for the full history). A value
230
- * this module cannot parse (a multi-line construct) is simply skipped,
231
- * never guessed at.
232
- */
233
- function scalarValuesOf(canonicalBlock: string): Record<string, unknown> {
234
- const values: Record<string, unknown> = {};
235
- for (const line of canonicalBlock.split('\n')) {
236
- const match = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+)$/.exec(line.trim());
237
- if (!match) continue;
238
- const value = parseTomlScalarValue(match[2] as string);
239
- if (value !== undefined) values[match[1] as string] = value;
240
- }
241
- return values;
242
- }
243
-
244
- /**
245
- * Marker-region-only target (layout.toml): the whole file's meaningful
246
- * content is inside the block. Callers only reach this once
247
- * `currentText !== assetText` — an exact match is always `unchanged`.
248
- */
249
- export function resolveLeanCtxWrite(assetText: string, currentText: string, wasWrittenByUs: boolean): LeanCtxWriteOutcome {
250
- const canonicalBlock = canonicalBlockOf(assetText);
251
- const region = findRegion(currentText);
252
-
253
- if (region === 'malformed') {
254
- return { action: 'conflict', reason: 'managed block markers are malformed (BEGIN/END unpaired or out of order); left untouched' };
255
- }
256
-
257
- if (region === 'missing') {
258
- if (!wasWrittenByUs) {
259
- return { action: 'conflict', reason: 'no adl-managed block found in this file, and it is not recorded as adl-written; left untouched' };
260
- }
261
- const knownKeys = new Set(declaredKeyNames(canonicalBlock));
262
- const { kept, recognizedCount } = migrateByKnownKeys(currentText, knownKeys);
263
- if (recognizedCount === 0) {
264
- return { action: 'conflict', reason: 'no recognizable adl-owned content found to migrate; left untouched' };
265
- }
266
- const composed = kept.length > 0 ? `${canonicalBlock}\n\n${kept.join('\n').replace(/\n+$/, '')}\n` : `${canonicalBlock}\n`;
267
- const invalid = validateComposedToml(composed, scalarValuesOf(canonicalBlock));
268
- if (invalid !== undefined) return { action: 'conflict', reason: `cannot migrate safely: ${invalid}` };
269
- return { action: 'write', text: composed };
270
- }
271
-
272
- const duplicate = duplicateKeyOutsideRegion(currentText, region, canonicalBlock);
273
- if (duplicate !== undefined) {
274
- return { action: 'conflict', reason: `"${duplicate}" is declared both inside and outside the adl-managed block; left untouched` };
275
- }
276
-
277
- const installed = currentText.slice(region.start, region.end);
278
- const composed = installed === canonicalBlock ? currentText : spliceRegion(currentText, region, canonicalBlock);
279
- const invalid = validateComposedToml(composed, scalarValuesOf(canonicalBlock));
280
- if (invalid !== undefined) return { action: 'conflict', reason: invalid };
281
- return installed === canonicalBlock ? { action: 'unchanged' } : { action: 'write', text: composed };
282
- }
283
-
284
- /** A simple (string-literals-only) array's parsed entries, or 'malformed' if it contains anything else (a number, a nested array, a bare identifier). */
285
- function parseSimpleStringArrayBody(body: string): readonly string[] | 'malformed' {
286
- const trimmed = body.trim();
287
- if (trimmed.length === 0) return [];
288
- const entries: string[] = [];
289
- for (const piece of trimmed.split(',').map((p) => p.trim())) {
290
- if (piece.length === 0) continue;
291
- const match = /^"((?:[^"\\]|\\.)*)"$/.exec(piece);
292
- if (!match) return 'malformed';
293
- entries.push(match[1] as string);
294
- }
295
- return entries;
296
- }
297
-
298
- interface ArraySpan {
299
- readonly start: number;
300
- readonly end: number;
301
- readonly body: string;
302
- }
303
- function findArray(text: string, key: string): ArraySpan | 'missing' | 'malformed' {
304
- const startMatch = new RegExp(`^${key}\\s*=\\s*\\[`, 'm').exec(text);
305
- if (!startMatch) return 'missing';
306
- const openBracket = startMatch.index + startMatch[0].length - 1;
307
- const closeBracket = text.indexOf(']', openBracket);
308
- if (closeBracket === -1) return 'malformed';
309
- const body = text.slice(openBracket + 1, closeBracket);
310
- if (body.includes('[')) return 'malformed'; // a nested array -- not "a simple string array"
311
- return { start: startMatch.index, end: closeBracket + 1, body };
312
- }
313
-
314
- /** Text-splices `missingEntries` into an existing simple array's body, right before its closing `]`, never touching what is already there. */
315
- function insertMissingEntries(text: string, array: ArraySpan, missingEntries: readonly string[]): string {
316
- const closeBracketIndex = array.end - 1; // position of ']'
317
- const bodyTrimmedEnd = text.slice(array.start, closeBracketIndex).replace(/\s+$/, '');
318
- const needsLeadingComma = /[^\s[]$/.test(bodyTrimmedEnd) && !bodyTrimmedEnd.endsWith(',');
319
- const insertion = `${needsLeadingComma ? ',' : ''}\n${missingEntries.map((e) => ` "${e}",`).join('\n')}\n`;
320
- return `${text.slice(0, closeBracketIndex)}${insertion}${text.slice(closeBracketIndex)}`;
321
- }
322
-
323
- /**
324
- * The portion of `text` that TOML grammar guarantees is ALL bare
325
- * (top-level) key/value pairs — everything up to (not including) the
326
- * first `[table]` or `[[array-of-tables]]` header. A key/value LINE
327
- * found inside this prefix is unambiguously top-level; the same key name
328
- * appearing after a table header belongs to that table instead
329
- * (`table.key`, a completely different key), which a pure line/regex
330
- * scan cannot tell apart from a top-level line on its own — this keeps
331
- * that distinction correct without needing to track table context.
332
- */
333
- function topLevelPrefixEnd(text: string): number {
334
- const match = /^\[/m.exec(text);
335
- return match ? match.index : text.length;
336
- }
337
-
338
- /**
339
- * Removes the (top-level-only, see `topLevelPrefixEnd`) `key = value`
340
- * line for each key in `keys` from `text` — used for every adl-owned
341
- * scalar key the current file ALREADY declares at the top level, whether
342
- * its value matches adl's canonical one or not: the FULL canonical block
343
- * (always inserted whole, never partial — every other reader of this
344
- * module assumes the managed block IS the complete canonical text) is
345
- * about to declare it again, and TOML forbids a duplicate top-level key
346
- * regardless of whether the two values agree. This is not "deleting a
347
- * user's line" in the sense the merge decision forbids — a matching
348
- * value is unchanged and un-lost, merely consolidated into the block
349
- * exactly where an ordinary deploy would have put it; a DIFFERING value
350
- * is adl-owned drift, and adl's own merge contract ("adl-owned entries
351
- * SELF-HEAL") already says adl's value wins — the caller names the
352
- * restore (`RestoredScalar`) so it is never silent.
353
- */
354
- function removeTopLevelScalarLines(text: string, keys: ReadonlySet<string>): string {
355
- if (keys.size === 0) return text;
356
- const prefixEnd = topLevelPrefixEnd(text);
357
- const prefix = text.slice(0, prefixEnd);
358
- const rest = text.slice(prefixEnd);
359
- const keptPrefixLines = prefix.split('\n').filter((line) => {
360
- const match = /^([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line.trim());
361
- return match === null || !keys.has(match[1] as string);
362
- });
363
- return `${keptPrefixLines.join('\n')}${rest}`;
364
- }
365
-
366
- /**
367
- * config.toml specifically (finding 3 + 4 + 5, and MERGED entirely,
368
- * Tarun 2026-09-30 — see this function's own migration branch, below,
369
- * and the describe block in lean-ctx-merge-contract.test.ts headed
370
- * "config.toml's allow list is MERGED entry-by-entry" for the full
371
- * decision): its own scalar keys live in the marker-region block exactly
372
- * like `resolveLeanCtxWrite`, but `shell_allowlist_extra` lives OUTSIDE
373
- * the markers as a UNION key — located anywhere in the file by a narrow
374
- * text scan, adl's required entries inserted before `]` if missing,
375
- * nothing ever removed/reordered. A non-simple array (anything but
376
- * string literals) is a conflict.
377
- */
378
- export function resolveConfigTomlWrite(assetText: string, currentText: string, wasWrittenByUs: boolean): LeanCtxWriteOutcome {
379
- const assetArray = findArray(assetText, SHELL_ALLOWLIST_KEY);
380
- if (assetArray === 'missing' || assetArray === 'malformed') {
381
- throw new Error(`agent-settings lean-ctx config.toml asset itself has no valid "${SHELL_ALLOWLIST_KEY}" array — this is a packaging bug, not a target-repo conflict`);
382
- }
383
- const requiredEntries = parseSimpleStringArrayBody(assetArray.body);
384
- if (requiredEntries === 'malformed') {
385
- throw new Error(`agent-settings lean-ctx config.toml asset's own "${SHELL_ALLOWLIST_KEY}" array is not a simple string array — this is a packaging bug, not a target-repo conflict`);
386
- }
387
- const canonicalBlock = canonicalBlockOf(assetText.slice(0, assetArray.start));
388
- const scalarValues = scalarValuesOf(canonicalBlock);
389
-
390
- let working = currentText;
391
- const region = findRegion(working);
392
- if (region === 'malformed') return { action: 'conflict', reason: 'managed block markers are malformed (BEGIN/END unpaired or out of order); left untouched' };
393
-
394
- if (region === 'missing') {
395
- if (!wasWrittenByUs) return { action: 'conflict', reason: 'no adl-managed block found in this file, and it is not recorded as adl-written; left untouched' };
396
-
397
- // Tarun, 2026-09-30 (corrected the same day — Tarun's merge contract
398
- // is "adl-owned entries SELF-HEAL; user additions are kept"): "Merge
399
- // the allow list in config.toml, the standard allow list must be
400
- // checked entry by entry, missing should be added, user entries must
401
- // not be removed." A markerless file is a MERGE target whenever it
402
- // is valid TOML at all — even a WHOLE-FILE overwrite with the user's
403
- // own unrelated content — never a conflict. An adl-owned scalar
404
- // already present at the top level ALWAYS self-heals to adl's own
405
- // value (an older asset default, or a user edit, is drift, not a
406
- // conflict — otherwise every already-enrolled repo carrying an older
407
- // adl default would conflict on upgrade); the restore is named
408
- // (`restoredScalars`, below) so it is never silent. Conflict remains
409
- // ONLY where merging is genuinely unsafe: invalid TOML (nowhere to
410
- // safely place a block), or an allow-list value that isn't a simple
411
- // string array. Nothing of the user's is ever deleted or reordered.
412
- const parsedCurrent = parseToml(currentText);
413
- if (parsedCurrent === 'invalid') {
414
- return { action: 'conflict', reason: 'current file is not valid TOML; cannot place the managed block safely' };
415
- }
416
- if (Object.prototype.hasOwnProperty.call(parsedCurrent, SHELL_ALLOWLIST_KEY) && !Array.isArray(parsedCurrent[SHELL_ALLOWLIST_KEY])) {
417
- return { action: 'conflict', reason: `"${SHELL_ALLOWLIST_KEY}" is not a simple string array; cannot merge safely` };
418
- }
419
-
420
- // Every adl-owned scalar key the current file ALREADY declares at
421
- // the top level — matching value or not — is removed from its
422
- // original line: the FULL canonical block (always inserted whole; a
423
- // "partial" block would make the managed-region invariant every
424
- // other code path relies on unstable across repeated runs) is about
425
- // to declare it again, and TOML forbids a duplicate top-level key
426
- // regardless of whether the two values agree. A DIFFERING value is
427
- // named in `restoredScalars` — adl's own value always wins.
428
- const presentKeys = new Set(Object.keys(scalarValues).filter((key) => Object.prototype.hasOwnProperty.call(parsedCurrent, key)));
429
- const restoredScalars: RestoredScalar[] = [...presentKeys]
430
- .filter((key) => !tomlValueMatches(parsedCurrent[key], scalarValues[key]))
431
- .map((key) => ({ key, value: scalarValues[key], previous: parsedCurrent[key] }));
432
- const restOfFile = removeTopLevelScalarLines(currentText, presentKeys).replace(/^\n+/, '');
433
-
434
- // Insert AT THE TOP, before any table header — always syntactically
435
- // safe for valid TOML (bare key/value pairs may appear anywhere
436
- // before the first table), and scalar collisions are already ruled
437
- // out above.
438
- let composed = restOfFile.length > 0 ? `${canonicalBlock}\n\n${restOfFile}` : `${canonicalBlock}\n`;
439
-
440
- const arraySpan = findArray(composed, SHELL_ALLOWLIST_KEY);
441
- if (arraySpan === 'malformed') {
442
- return { action: 'conflict', reason: `"${SHELL_ALLOWLIST_KEY}" is not a simple string array; cannot merge safely` };
443
- }
444
- if (arraySpan === 'missing') {
445
- // The user has no such array at all -- create it, with just the
446
- // standard entries, right after the inserted block.
447
- const arrayText = `${SHELL_ALLOWLIST_KEY} = [\n${requiredEntries.map((e) => ` "${e}",`).join('\n')}\n]\n`;
448
- composed = `${canonicalBlock}\n${arrayText}\n${restOfFile}`;
449
- } else {
450
- const currentEntries = parseSimpleStringArrayBody(arraySpan.body);
451
- if (currentEntries === 'malformed') {
452
- return { action: 'conflict', reason: `"${SHELL_ALLOWLIST_KEY}" is not a simple string array; cannot merge safely` };
453
- }
454
- // Entry by entry: only the STANDARD entries missing from the
455
- // user's OWN array are appended, at the end, byte-for-byte -- the
456
- // user's own entries, and their order, are never touched.
457
- const missingEntries = requiredEntries.filter((entry) => !currentEntries.includes(entry));
458
- if (missingEntries.length > 0) composed = insertMissingEntries(composed, arraySpan, missingEntries);
459
- }
460
-
461
- const invalid = validateComposedToml(composed, scalarValues, SHELL_ALLOWLIST_KEY, requiredEntries);
462
- if (invalid !== undefined) return { action: 'conflict', reason: `cannot merge safely: ${invalid}` };
463
- return restoredScalars.length > 0 ? { action: 'write', text: composed, restoredScalars } : { action: 'write', text: composed };
464
- }
465
-
466
- const duplicate = duplicateKeyOutsideRegion(working, region, canonicalBlock);
467
- if (duplicate !== undefined) return { action: 'conflict', reason: `"${duplicate}" is declared both inside and outside the adl-managed block; left untouched` };
468
-
469
- const installedBlock = working.slice(region.start, region.end);
470
- if (installedBlock !== canonicalBlock) working = spliceRegion(working, region, canonicalBlock);
471
-
472
- // Step 2: the union array, resolved fresh against the (possibly just-healed) text.
473
- const currentArray = findArray(working, SHELL_ALLOWLIST_KEY);
474
- if (currentArray === 'missing') return { action: 'conflict', reason: `expected "${SHELL_ALLOWLIST_KEY}" array not found` };
475
- if (currentArray === 'malformed') return { action: 'conflict', reason: `"${SHELL_ALLOWLIST_KEY}" is not a simple string array (contains something other than string literals)` };
476
- const currentEntries = parseSimpleStringArrayBody(currentArray.body);
477
- if (currentEntries === 'malformed') return { action: 'conflict', reason: `"${SHELL_ALLOWLIST_KEY}" is not a simple string array (contains something other than string literals)` };
478
- const missing = requiredEntries.filter((entry) => !currentEntries.includes(entry));
479
- if (missing.length > 0) working = insertMissingEntries(working, currentArray, missing);
480
-
481
- if (working === currentText) {
482
- const invalid = validateComposedToml(working, scalarValues, SHELL_ALLOWLIST_KEY, requiredEntries);
483
- if (invalid !== undefined) return { action: 'conflict', reason: invalid };
484
- return { action: 'unchanged' };
485
- }
486
- const invalid = validateComposedToml(working, scalarValues, SHELL_ALLOWLIST_KEY, requiredEntries);
487
- if (invalid !== undefined) return { action: 'conflict', reason: invalid };
488
- return { action: 'write', text: working };
489
- }
490
-
491
- /** The block's own ignore-pattern lines (no markers, no comments) — what "already present" is checked against: a negation of one of these BEFORE an existing block is a conflict (round 5), and a bare duplicate of one of these OUTSIDE an existing block is ambiguous and stays a conflict too. Round 5 (Tarun, 2026-09-30) stopped using this to DROP lines during migration — see `resolveGitignoreWrite`'s own header for why. */
492
- function gitignorePatternLines(canonicalBlock: string): readonly string[] {
493
- return canonicalBlock
494
- .split('\n')
495
- .map((l) => l.trim())
496
- .filter((l) => l.length > 0 && l !== LEAN_CTX_MARKER_START && l !== LEAN_CTX_MARKER_END && !l.startsWith('#'));
497
- }
498
-
499
- /**
500
- * `.agents/.gitignore` (finding 6, REDECIDED 2026-09-30 — a real repro
501
- * showed the round-3 fix never actually ran: `lean-ctx-deploy.ts`'s own
502
- * top-level `exists && !managedByUs` guard short-circuited to a hard
503
- * conflict before this function was even called, and this function's
504
- * OWN `!wasWrittenByUs` check would have refused it too). A markerless
505
- * `.gitignore` is no longer a hard conflict EITHER WAY — written by adl
506
- * before, or a plain user file that has simply never been touched.
507
- *
508
- * ROUND 5 (fourth independent review, same day) found the FIRST fix's
509
- * own ordering bug: it deleted any existing line that happened to match
510
- * one of adl's own patterns, then appended the canonical block at the
511
- * END. A user's `*.bak` (redundant with adl's own pattern) followed by
512
- * their OWN deliberate `!keep.bak` override got the `*.bak` line
513
- * deleted, and adl's later copy of it (inside the appended block) then
514
- * silently outranked `!keep.bak` — gitignore's last-match-wins rule
515
- * turned the user's override backwards.
516
- *
517
- * DECISION (Tarun, 2026-09-30): never delete or reorder a user's own
518
- * line, full stop — not even one that looks exactly like adl's own
519
- * pattern (the cosmetic cost of a harmless duplicate line is strictly
520
- * better than ever risking a real one). For a markerless file, the
521
- * canonical block is PREPENDED instead of appended: every existing line
522
- * (all of them, unmodified, in their original order) then comes AFTER
523
- * the block and keeps winning, including any negation — exactly
524
- * gitignore's own "last match wins" semantics, by construction, with no
525
- * special-casing needed for negations in this branch at all (a
526
- * pre-existing negation is now IMPOSSIBLE to place before the block,
527
- * since the block is always first).
528
- *
529
- * For an EXISTING marked file, the block never moves from wherever it
530
- * already sits — but a NEW negation added BEFORE it (which the block's
531
- * own later copy of that pattern would silently override) is its own
532
- * conflict, reported rather than silently accepted. A negation added
533
- * AFTER the block already wins correctly and is not a conflict.
534
- *
535
- * The "bare duplicate outside the block" check (below) is scoped to
536
- * BEFORE the block only — found while verifying this very fix against
537
- * this repo's own REAL data, not asked for by name: a markerless
538
- * migration keeps every existing line, including ones that happen to be
539
- * adl's own canonical patterns (this repo's own actual pre-marker
540
- * `.agents/.gitignore` is exactly that shape), and those necessarily end
541
- * up AFTER the newly-prepended block. A duplicate AFTER the block is
542
- * provably inert (gitignore's last-match-wins rule makes a trailing
543
- * repeat of the same pattern a no-op), so checking it unscoped turned
544
- * every such migration into a conflict on EVERY subsequent deploy
545
- * forever. A duplicate BEFORE the block is the one that still matters:
546
- * it is exactly the negation risk above, generalized to a bare
547
- * (non-negated) repeat that could still be reordered or edited later in
548
- * a way the block's own later copy would silently shadow.
549
- */
550
- export function resolveGitignoreWrite(assetText: string, currentText: string, _wasWrittenByUs: boolean): LeanCtxWriteOutcome {
551
- const canonicalBlock = canonicalBlockOf(assetText);
552
- const region = findRegion(currentText);
553
- if (region === 'malformed') return { action: 'conflict', reason: 'managed block markers are malformed (BEGIN/END unpaired or out of order); left untouched' };
554
-
555
- const patternLines = new Set(gitignorePatternLines(canonicalBlock));
556
-
557
- if (region === 'missing') {
558
- const trimmed = currentText.replace(/\n+$/, '');
559
- return { action: 'write', text: trimmed.length > 0 ? `${canonicalBlock}\n\n${trimmed}\n` : `${canonicalBlock}\n` };
560
- }
561
-
562
- const beforeLines = new Set(
563
- currentText
564
- .slice(0, region.start)
565
- .split('\n')
566
- .map((l) => l.trim()),
567
- );
568
- for (const pattern of patternLines) {
569
- if (beforeLines.has(`!${pattern}`)) {
570
- return { action: 'conflict', reason: `"!${pattern}" appears before the managed block and would be silently overridden by the block's own later "${pattern}"; left untouched` };
571
- }
572
- if (beforeLines.has(pattern)) {
573
- return { action: 'conflict', reason: `"${pattern}" already appears before the managed block; left untouched` };
574
- }
575
- }
576
-
577
- const installed = currentText.slice(region.start, region.end);
578
- if (installed === canonicalBlock) return { action: 'unchanged' };
579
- return { action: 'write', text: spliceRegion(currentText, region, canonicalBlock) };
580
- }
@@ -1,38 +0,0 @@
1
- ---
2
- name: adl-mechanics
3
- description: How agents reach Azure DevOps in an adl-controlled repository — agent identity, the git front controller, the adl MCP tools and their CLI fallback, repository-control hooks, and when `adl run` may start a sub-agent.
4
- ---
5
-
6
- # adl mechanics
7
-
8
- How adl behaves on every machine and in every repository it controls.
9
-
10
- ## Use When
11
-
12
- - You are about to run a git network command, open or update a PR, read or write a work item, or start a build.
13
- - A command is refused for identity, credentials or hooks, and you need to know why.
14
- - The operator asks you to start a sub-agent session with `adl run`.
15
-
16
- ## Agent identity
17
-
18
- - Every Azure DevOps access (git, PRs, work items, builds, reads included) runs as an agent identity, never as the human. The default is the machine's default agent, set by fleet reconcile; it can always be overridden.
19
- - **Override:** prefix a command with `ADL_AGENT_ID_AGENT=<agent-id>`. An MCP tool call can't carry a prefix, so it uses the session environment or the machine default; the CLI fallback (`adl mcp cli <command>`) takes the prefix like any command.
20
- - Never read vault files. Never pass a token.
21
- - **Never set `ADL_HUMAN_AZDO`.** It is the owner's human-terminal marker for `git-ado`'s and `adl id whoami`'s fail-closed human pass-through (stdin is a TTY and `ADL_HUMAN_AZDO=1`, exactly). An agent carrying it would let a child process forge that pass-through.
22
-
23
- ## Git and Azure DevOps operations
24
-
25
- - Use ordinary `git` for every Git operation. The managed `/usr/local/bin/git` front controller hands local and non-Azure DevOps work to the system Git, and routes HTTPS Azure DevOps network commands through `adl-agent-id exec --agent "$ADL_AGENT_ID_AGENT"`. `git-ado` handles the credential boundary; there is no second account or secret location.
26
- - PRs, work items and builds go through the `adl` MCP server (`adl mcp serve`): `pr_list_or_get`, `pr_create_or_update`, `pr_review`, `build_list_or_get`, `build_run`, `workitem_list_or_get`, `workitem_create_or_update`. If the server is not working, fall back to `adl mcp cli <command>`.
27
-
28
- ## Repository controls
29
-
30
- - Run via `adl repo controls`. Git hooks run pre-commit (local checks) and pre-push (Azure DevOps governance).
31
- - Each repository is reconciled on its own, with its `.agents/`, `.claude/`, `.codex/`, `.cursor/`, `.kimi-code/` and `.opencode/` projections.
32
-
33
- ## Sub-agent sessions (`adl run`)
34
-
35
- - Only on the operator's explicit request; an agent never starts one on its own. It is not an identity route and not a fallback for anything.
36
- - **Headless:** `adl run <surface> --headless "<prompt>"`, surface one of `claude`, `codex`, `opencode`, `kimi`, `cursor`.
37
- - **Identity:** add `--as <agent>`.
38
- - **Model:** add `--model <model>`. adl passes each surface's own flag (claude and cursor `--model`; codex, opencode and kimi `-m`); a surface without one refuses it (exit 2). `--model` cannot be combined with `adl run -- <cmd>`.