@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,649 +0,0 @@
1
- /**
2
- * User LaunchAgents owned by adl.
3
- *
4
- * `packages/fleet/manifest.json` declares them under `mac.common.launch_agents`
5
- * (and optionally `mac.devices.<device>.launch_agents`; validate-manifest
6
- * rejects them on linux). For each declared job, reconcile (as root, apply):
7
- *
8
- * 1. compiles the job's TypeScript entry (`source`/`entry`) with
9
- * `bun build --compile` into the dedicated binary
10
- * `/usr/local/libexec/adl/<job id>` (root-owned, 0755 --
11
- * never user-writable, since launchd runs it on a schedule), ad-hoc
12
- * signed with the stable identifier `<label>` and the explicit designated
13
- * requirement `identifier "<label>"`. It rebuilds only when a content
14
- * hash of the source directory changes (or the binary is missing).
15
- *
16
- * Why a dedicated binary: under launchd the program in ProgramArguments
17
- * is the responsible process for macOS privacy protection (TCC), and the
18
- * `git` children it spawns are attributed to it. A job run by /bin/bash
19
- * would need Full Disk Access granted to every bash script; a dedicated
20
- * binary lets the owner grant exactly that one path. TCC matches a grant
21
- * against the code's designated requirement; plain ad-hoc signing pins it
22
- * to the cdhash (TN3127), so every rebuild would silently void the grant.
23
- * The explicit identifier requirement keeps rebuilds satisfying it; the
24
- * grant itself is still keyed to this root-owned path;
25
- * 2. renders `~/Library/LaunchAgents/<label>.plist` for the device's
26
- * administrator, expanding a leading `$HOME` in environment values and the
27
- * home-relative `log` path (launchd itself expands nothing);
28
- * 3. replaces any differing declared plist from source and removes every
29
- * retired `replaces` path outright;
30
- * 4. (re)loads the job in `gui/<uid>` when anything changed or it is not
31
- * loaded. Without a GUI session for that user the job is left installed
32
- * and launchd loads it at the next login (RunAtLoad applies then).
33
- *
34
- * A job present in the previously applied manifest but absent now is booted
35
- * out, its plist and binary and source hash removed. The libexec dir itself
36
- * is shared with other managers (e.g. `repo-dir` targets such as
37
- * `agent-skills`/`repo-controls`/`agent-hooks`), so it is never swept
38
- * wholesale: only an entry whose id this component currently declares, or
39
- * once built and recorded a `<id>.source-hash` marker for under `stateDir`,
40
- * is eligible for stale removal. Anything else in the dir -- another
41
- * manager's target, a hand-placed file -- is left untouched.
42
- *
43
- * When a job's last log line is a TCC denial (git-maintain's
44
- * `TCC_DENIED_MARKER`), plan and apply surface it verbatim, remedy included.
45
- *
46
- * `global-surface-sweep` derives LaunchAgent protection from the same
47
- * declarations (`declaredLaunchAgentPlists`), so a shipped job is never a
48
- * sweep candidate.
49
- */
50
-
51
- import { spawnSync } from 'node:child_process'
52
- import { createHash } from 'node:crypto'
53
- import {
54
- chmodSync,
55
- chownSync,
56
- existsSync,
57
- lstatSync,
58
- mkdirSync,
59
- readFileSync,
60
- readdirSync,
61
- renameSync,
62
- rmSync,
63
- statSync,
64
- writeFileSync,
65
- } from 'node:fs'
66
- import { dirname, isAbsolute, join, relative, resolve } from 'node:path'
67
- import { TCC_DENIED_MARKER } from '../jobs/git-maintain/src/main.ts'
68
-
69
- export const DEFAULT_LIBEXEC_DIR = '/usr/local/libexec/adl'
70
- const LAUNCH_AGENTS_DIR = 'Library/LaunchAgents'
71
-
72
- export interface RepositoryScope {
73
- readonly root: string
74
- readonly glob: string
75
- readonly recursive: true
76
- }
77
-
78
- export interface LaunchAgentSpec {
79
- readonly label: string
80
- /** Checkout-relative job directory; its content hash decides rebuilds. */
81
- readonly source: string
82
- /** Entry file relative to `source`. */
83
- readonly entry: string
84
- readonly start_interval_seconds: number
85
- readonly run_at_load: boolean
86
- readonly repo_scope?: RepositoryScope
87
- readonly environment: Readonly<Record<string, string>>
88
- readonly log: string
89
- readonly replaces: readonly string[]
90
- }
91
-
92
- export type Platform = 'mac' | 'linux'
93
-
94
- function isRecord(value: unknown): value is Record<string, unknown> {
95
- return value !== null && typeof value === 'object' && !Array.isArray(value)
96
- }
97
-
98
- function decodeRepoScope(id: string, raw: unknown): RepositoryScope {
99
- if (
100
- !isRecord(raw) ||
101
- typeof raw.root !== 'string' ||
102
- raw.root.length === 0 ||
103
- typeof raw.glob !== 'string' ||
104
- raw.glob.length === 0 ||
105
- raw.recursive !== true
106
- ) {
107
- throw new Error(`invalid launch agent repo_scope: ${id} (need root: string, glob: string, recursive: true)`)
108
- }
109
- return { root: raw.root, glob: raw.glob, recursive: true }
110
- }
111
-
112
- function decodeSpec(id: string, raw: unknown): LaunchAgentSpec {
113
- if (
114
- !isRecord(raw) ||
115
- typeof raw.label !== 'string' ||
116
- typeof raw.source !== 'string' ||
117
- typeof raw.entry !== 'string' ||
118
- raw.script !== undefined ||
119
- typeof raw.start_interval_seconds !== 'number' ||
120
- typeof raw.run_at_load !== 'boolean' ||
121
- !isRecord(raw.environment) ||
122
- typeof raw.log !== 'string' ||
123
- (raw.replaces !== undefined && !Array.isArray(raw.replaces))
124
- ) {
125
- throw new Error(`invalid launch agent declaration: ${id}`)
126
- }
127
- const environment: Record<string, string> = {}
128
- for (const [key, value] of Object.entries(raw.environment)) {
129
- if (typeof value !== 'string') throw new Error(`invalid launch agent environment value: ${id}.${key}`)
130
- environment[key] = value
131
- }
132
- const replaces = ((raw.replaces ?? []) as unknown[]).map((entry) => {
133
- if (typeof entry !== 'string') throw new Error(`invalid launch agent replaces entry: ${id}`)
134
- return entry
135
- })
136
- const repo_scope = raw.repo_scope === undefined ? undefined : decodeRepoScope(id, raw.repo_scope)
137
- return {
138
- label: raw.label,
139
- source: raw.source,
140
- entry: raw.entry,
141
- start_interval_seconds: raw.start_interval_seconds,
142
- run_at_load: raw.run_at_load,
143
- ...(repo_scope === undefined ? {} : { repo_scope }),
144
- environment,
145
- log: raw.log,
146
- replaces,
147
- }
148
- }
149
-
150
- function agentsAt(value: unknown, id: string): Record<string, LaunchAgentSpec> {
151
- if (value === undefined) return {}
152
- if (!isRecord(value)) throw new Error(`launch_agents must be an object: ${id}`)
153
- const result: Record<string, LaunchAgentSpec> = {}
154
- for (const [key, raw] of Object.entries(value)) result[key] = decodeSpec(key, raw)
155
- return result
156
- }
157
-
158
- /** `<platform>.common.launch_agents` merged with `<platform>.devices.<device>.launch_agents` (device wins). */
159
- export function resolveLaunchAgents(manifest: unknown, platform: Platform, device: string): Record<string, LaunchAgentSpec> {
160
- if (!isRecord(manifest)) throw new Error('manifest must be an object')
161
- const section = manifest[platform]
162
- if (!isRecord(section) || !isRecord(section.common) || !isRecord(section.devices)) {
163
- throw new Error(`manifest has no ${platform} section`)
164
- }
165
- const deviceEntry = section.devices[device]
166
- if (!isRecord(deviceEntry)) throw new Error(`device ${device} is not declared under ${platform}`)
167
- return {
168
- ...agentsAt(section.common.launch_agents, `${platform}.common`),
169
- ...agentsAt(deviceEntry.launch_agents, `${platform}.devices.${device}`),
170
- }
171
- }
172
-
173
- /**
174
- * Every LaunchAgent plist (home-relative) any mac device in the manifest
175
- * declares, with its declaration path. Protection needs only each label, so a
176
- * job's build fields never gate it; an entry without a valid label fails loud.
177
- */
178
- export function declaredLaunchAgentPlists(manifest: unknown): readonly { readonly path: string; readonly declaredAt: string }[] {
179
- if (!isRecord(manifest) || !isRecord(manifest.mac)) return []
180
- const mac = manifest.mac
181
- const found = new Map<string, string>()
182
- const collect = (value: unknown, where: string): void => {
183
- if (value === undefined) return
184
- if (!isRecord(value)) throw new Error(`launch_agents must be an object: ${where}`)
185
- for (const [id, raw] of Object.entries(value)) {
186
- if (!isRecord(raw) || typeof raw.label !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(raw.label)) {
187
- throw new Error(`invalid launch agent label: mac.${where}.launch_agents.${id}`)
188
- }
189
- const path = `${LAUNCH_AGENTS_DIR}/${raw.label}.plist`
190
- if (!found.has(path)) found.set(path, `mac.${where}.launch_agents.${id}`)
191
- }
192
- }
193
- if (isRecord(mac.common)) collect(mac.common.launch_agents, 'common')
194
- if (isRecord(mac.devices)) {
195
- for (const [device, entry] of Object.entries(mac.devices)) {
196
- if (isRecord(entry)) collect(entry.launch_agents, `devices.${device}`)
197
- }
198
- }
199
- return [...found.entries()].map(([path, declaredAt]) => ({ path, declaredAt })).sort((a, b) => a.path.localeCompare(b.path))
200
- }
201
-
202
- function xml(value: string): string {
203
- return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;')
204
- }
205
-
206
- function expandHome(value: string, home: string): string {
207
- if (value === '$HOME') return home
208
- if (value.startsWith('$HOME/')) return `${home}${value.slice('$HOME'.length)}`
209
- return value
210
- }
211
-
212
- /** `<platform>` launch agent labels by job id, tolerating older declaration shapes (removal needs only the label). */
213
- function resolveLabels(manifest: unknown, platform: Platform, device: string): Record<string, string> {
214
- const labels: Record<string, string> = {}
215
- if (!isRecord(manifest) || !isRecord(manifest[platform])) return labels
216
- const section = manifest[platform] as Record<string, unknown>
217
- const collect = (value: unknown): void => {
218
- if (!isRecord(value)) return
219
- for (const [id, raw] of Object.entries(value)) {
220
- if (isRecord(raw) && typeof raw.label === 'string') labels[id] = raw.label
221
- }
222
- }
223
- if (isRecord(section.common)) collect(section.common.launch_agents)
224
- if (isRecord(section.devices) && isRecord(section.devices[device])) collect(section.devices[device].launch_agents)
225
- return labels
226
- }
227
-
228
- export function installedProgramPath(id: string, libexecDir: string): string {
229
- return join(libexecDir, id)
230
- }
231
-
232
- function sourceHashPath(stateDir: string, id: string): string {
233
- return join(stateDir, 'launch-agents', `${id}.source-hash`)
234
- }
235
-
236
- const SOURCE_HASH_SUFFIX = '.source-hash'
237
-
238
- /**
239
- * ids this component may treat as its own in `libexecDir`: every currently
240
- * declared job, plus any id it previously built and still holds a
241
- * `<id>.source-hash` marker for in `stateDir`. An id with neither is not
242
- * ours to remove, however stale-looking it is -- it belongs to whatever
243
- * other manager (or hand-placed file) put it there.
244
- */
245
- function ownedProgramIds(stateDir: string, currentIds: ReadonlySet<string>): Set<string> {
246
- const owned = new Set(currentIds)
247
- let markers: string[]
248
- try {
249
- markers = readdirSync(join(stateDir, 'launch-agents'))
250
- } catch {
251
- markers = []
252
- }
253
- for (const name of markers) {
254
- if (name.endsWith(SOURCE_HASH_SUFFIX)) owned.add(name.slice(0, -SOURCE_HASH_SUFFIX.length))
255
- }
256
- return owned
257
- }
258
-
259
- function listFiles(dir: string): string[] {
260
- const out: string[] = []
261
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
262
- if (entry.name === 'node_modules' || entry.name.endsWith('.bun-build')) continue
263
- const path = join(dir, entry.name)
264
- if (entry.isDirectory()) out.push(...listFiles(path))
265
- else if (entry.isFile()) out.push(path)
266
- }
267
- return out
268
- }
269
-
270
- /** sha256 over every file (relative path + content) in the job's source directory. */
271
- export function sourceHash(sourceDir: string): string {
272
- const hash = createHash('sha256')
273
- for (const file of listFiles(sourceDir).sort()) {
274
- hash.update(`${relative(sourceDir, file)}\0`)
275
- hash.update(readFileSync(file))
276
- hash.update('\0')
277
- }
278
- return hash.digest('hex')
279
- }
280
-
281
- export interface ProgramBuilder {
282
- /** Compile `entryPath` to `outputPath` (a fresh file) signed with `identifier`; throw on failure. */
283
- build(entryPath: string, outputPath: string, identifier: string): void
284
- }
285
-
286
- /**
287
- * `bun build --compile` through the shared bun-compile-build.sh (throwaway
288
- * CWD, so no `*.bun-build` leaks), then an ad-hoc re-sign with the stable
289
- * identifier as an explicit designated requirement.
290
- */
291
- export function systemProgramBuilder(bunBin: string, buildLibPath: string): ProgramBuilder {
292
- return {
293
- build: (entryPath, outputPath, identifier) => {
294
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(identifier)) throw new Error(`unsafe signing identifier: ${identifier}`)
295
- const compiled = spawnSync(
296
- '/bin/bash',
297
- ['-c', 'set -euo pipefail; source "$1"; bun_compile_build "$2" "$3" "$4"', 'bun-compile', buildLibPath, bunBin, entryPath, outputPath],
298
- { encoding: 'utf8' },
299
- )
300
- if (compiled.status !== 0) {
301
- throw new Error(`bun build --compile ${entryPath} failed: ${`${compiled.stdout}${compiled.stderr}`.trim()}`)
302
- }
303
- const signed = spawnSync(
304
- 'codesign',
305
- ['--force', '--sign', '-', '--identifier', identifier, `-r=designated => identifier "${identifier}"`, outputPath],
306
- { encoding: 'utf8' },
307
- )
308
- if (signed.status !== 0) throw new Error(`codesign ${outputPath} failed: ${`${signed.stdout}${signed.stderr}`.trim()}`)
309
- },
310
- }
311
- }
312
-
313
- /** The job's last log line when it is a TCC denial, so reconcile output carries the remedy. */
314
- function lastRunDenial(spec: LaunchAgentSpec, home: string): string | null {
315
- const content = readIfFile(join(home, spec.log))
316
- if (content === null) return null
317
- const last = content.split('\n').filter((line) => line.length > 0).at(-1)
318
- return last !== undefined && last.includes(TCC_DENIED_MARKER) ? `launch-agent: ${spec.label} last run: ${last}` : null
319
- }
320
-
321
- export function renderLaunchAgentPlist(
322
- spec: LaunchAgentSpec,
323
- options: { readonly home: string; readonly programPath: string },
324
- ): string {
325
- const logPath = join(options.home, spec.log)
326
- const environment = Object.keys(spec.environment)
327
- .sort()
328
- .flatMap((key) => [
329
- ` <key>${xml(key)}</key>`,
330
- ` <string>${xml(expandHome(spec.environment[key]!, options.home))}</string>`,
331
- ])
332
- return [
333
- '<?xml version="1.0" encoding="UTF-8"?>',
334
- '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
335
- '<!-- Generated by adl global-tools (manifest launch_agents) -- do not edit; reconcile overwrites it. -->',
336
- '<plist version="1.0">',
337
- '<dict>',
338
- ' <key>Label</key>',
339
- ` <string>${xml(spec.label)}</string>`,
340
- ' <key>ProgramArguments</key>',
341
- ' <array>',
342
- ` <string>${xml(options.programPath)}</string>`,
343
- ' </array>',
344
- ' <key>EnvironmentVariables</key>',
345
- ' <dict>',
346
- ...environment,
347
- ' </dict>',
348
- ' <key>RunAtLoad</key>',
349
- spec.run_at_load ? ' <true/>' : ' <false/>',
350
- ' <key>StartInterval</key>',
351
- ` <integer>${spec.start_interval_seconds}</integer>`,
352
- ' <key>StandardOutPath</key>',
353
- ` <string>${xml(logPath)}</string>`,
354
- ' <key>StandardErrorPath</key>',
355
- ` <string>${xml(logPath)}</string>`,
356
- ' <key>ProcessType</key>',
357
- ' <string>Background</string>',
358
- ' <key>LowPriorityIO</key>',
359
- ' <true/>',
360
- ' <key>Nice</key>',
361
- ' <integer>10</integer>',
362
- '</dict>',
363
- '</plist>',
364
- '',
365
- ].join('\n')
366
- }
367
-
368
- export interface Launchctl {
369
- hasGuiDomain(uid: number): boolean
370
- isLoaded(uid: number, label: string): boolean
371
- /** Unload; a job that is not loaded is the harmless common case. */
372
- bootout(uid: number, label: string): void
373
- bootstrap(uid: number, plistPath: string): { readonly ok: boolean; readonly detail: string }
374
- }
375
-
376
- export const systemLaunchctl: Launchctl = {
377
- hasGuiDomain: (uid) => spawnSync('launchctl', ['print', `gui/${uid}`], { encoding: 'utf8' }).status === 0,
378
- isLoaded: (uid, label) => spawnSync('launchctl', ['print', `gui/${uid}/${label}`], { encoding: 'utf8' }).status === 0,
379
- bootout: (uid, label) => {
380
- spawnSync('launchctl', ['bootout', `gui/${uid}/${label}`], { encoding: 'utf8' })
381
- },
382
- bootstrap: (uid, plistPath) => {
383
- const result = spawnSync('launchctl', ['bootstrap', `gui/${uid}`, plistPath], { encoding: 'utf8' })
384
- return { ok: result.status === 0, detail: `${result.stdout}${result.stderr}`.trim() }
385
- },
386
- }
387
-
388
- export interface ApplyOptions {
389
- readonly manifest: unknown
390
- readonly previousManifest: unknown | null
391
- readonly platform: Platform
392
- readonly device: string
393
- readonly home: string
394
- readonly repoRoot: string
395
- readonly libexecDir: string
396
- readonly stateDir: string
397
- readonly launchctl: Launchctl
398
- readonly builder: ProgramBuilder
399
- }
400
-
401
- export interface ApplyResult {
402
- readonly lines: readonly string[]
403
- }
404
-
405
- function readIfFile(path: string): string | null {
406
- try {
407
- if (!lstatSync(path).isFile()) return null
408
- } catch {
409
- return null
410
- }
411
- return readFileSync(path, 'utf8')
412
- }
413
-
414
- function atomicWrite(path: string, content: string, mode: number, owner: { uid: number; gid: number } | null): void {
415
- mkdirSync(dirname(path), { recursive: true })
416
- const temporary = `${path}.adl-new`
417
- writeFileSync(temporary, content, { mode })
418
- chmodSync(temporary, mode)
419
- if (owner !== null) chownSync(temporary, owner.uid, owner.gid)
420
- renameSync(temporary, path)
421
- }
422
-
423
- function ensureOwnedDir(path: string, owner: { uid: number; gid: number } | null): void {
424
- mkdirSync(path, { recursive: true })
425
- if (owner !== null) chownSync(path, owner.uid, owner.gid)
426
- }
427
-
428
- function needsBuild(id: string, spec: LaunchAgentSpec, options: { repoRoot: string; libexecDir: string; stateDir: string }): string | null {
429
- const hash = sourceHash(join(options.repoRoot, spec.source))
430
- const programPath = installedProgramPath(id, options.libexecDir)
431
- const recorded = readIfFile(sourceHashPath(options.stateDir, id))?.trim()
432
- return recorded === hash && existsSync(programPath) ? null : hash
433
- }
434
-
435
- export function planLaunchAgents(options: Omit<ApplyOptions, 'launchctl' | 'builder'>): readonly string[] {
436
- const lines: string[] = []
437
- const agents = resolveLaunchAgents(options.manifest, options.platform, options.device)
438
- for (const [id, spec] of Object.entries(agents)) {
439
- const plistPath = join(options.home, LAUNCH_AGENTS_DIR, `${spec.label}.plist`)
440
- const programPath = installedProgramPath(id, options.libexecDir)
441
- lines.push(`would install launch agent: ${spec.label} -> ${plistPath} (program ${programPath})`)
442
- if (readIfFile(join(options.repoRoot, spec.source, spec.entry)) === null) {
443
- lines.push(`launch agent job entry missing from checkout: ${join(spec.source, spec.entry)}`)
444
- } else {
445
- const hash = needsBuild(id, spec, options)
446
- if (hash !== null) lines.push(`would build ${programPath} (source ${hash})`)
447
- }
448
- for (const legacy of spec.replaces) {
449
- if (existsSync(join(options.home, legacy))) lines.push(`would remove retired ${join(options.home, legacy)}`)
450
- }
451
- const denial = lastRunDenial(spec, options.home)
452
- if (denial !== null) lines.push(denial)
453
- }
454
- if (options.previousManifest !== null) {
455
- const current = new Set(Object.values(agents).map((spec) => spec.label))
456
- for (const label of Object.values(resolveLabels(options.previousManifest, options.platform, options.device))) {
457
- if (!current.has(label)) lines.push(`would remove launch agent: ${label}`)
458
- }
459
- }
460
- return lines
461
- }
462
-
463
- export function applyLaunchAgents(options: ApplyOptions): ApplyResult {
464
- for (const [label, value] of [
465
- ['home', options.home],
466
- ['repo root', options.repoRoot],
467
- ['libexec dir', options.libexecDir],
468
- ['state dir', options.stateDir],
469
- ] as const) {
470
- if (!isAbsolute(value)) throw new Error(`${label} must be absolute: ${value}`)
471
- }
472
- const home = resolve(options.home)
473
- const agents = resolveLaunchAgents(options.manifest, options.platform, options.device)
474
- const previous =
475
- options.previousManifest === null ? {} : resolveLabels(options.previousManifest, options.platform, options.device)
476
-
477
- // Fail before the first mutation if any declared job entry is missing.
478
- for (const spec of Object.values(agents)) {
479
- if (readIfFile(join(options.repoRoot, spec.source, spec.entry)) === null) {
480
- throw new Error(`launch agent job entry missing from checkout: ${join(spec.source, spec.entry)}`)
481
- }
482
- }
483
-
484
- const homeStat = statSync(home)
485
- const uid = homeStat.uid
486
- const owner = typeof process.getuid === 'function' && process.getuid() === 0 ? { uid, gid: homeStat.gid } : null
487
- const lines: string[] = []
488
-
489
- const currentLabels = new Set(Object.values(agents).map((spec) => spec.label))
490
- for (const [id, label] of Object.entries(previous)) {
491
- if (currentLabels.has(label)) continue
492
- const relativePlist = `${LAUNCH_AGENTS_DIR}/${label}.plist`
493
- options.launchctl.bootout(uid, label)
494
- if (options.launchctl.isLoaded(uid, label)) {
495
- throw new Error(`launch agent ${label} is still loaded after bootout; refusing to remove it`)
496
- }
497
- rmSync(join(home, relativePlist), { force: true })
498
- if (!(id in agents)) {
499
- rmSync(installedProgramPath(id, options.libexecDir), { force: true })
500
- rmSync(sourceHashPath(options.stateDir, id), { force: true })
501
- }
502
- lines.push(`launch-agent: removed ${label} (deleted from manifest)`)
503
- }
504
-
505
- // Only remove entries this component owns: currently declared ids, or ids
506
- // it previously built (a `.source-hash` marker under stateDir). The dir is
507
- // shared with other managers (e.g. repo-dir targets), so an entry with
508
- // neither is left alone even if it looks stale.
509
- if (existsSync(options.libexecDir)) {
510
- const expected = new Set(Object.keys(agents))
511
- const owned = ownedProgramIds(options.stateDir, expected)
512
- for (const name of readdirSync(options.libexecDir)) {
513
- if (expected.has(name) || !owned.has(name)) continue
514
- rmSync(join(options.libexecDir, name), { recursive: true, force: true })
515
- lines.push(`launch-agent: removed stale ${join(options.libexecDir, name)}`)
516
- }
517
- }
518
-
519
- for (const [id, spec] of Object.entries(agents)) {
520
- const relativePlist = `${LAUNCH_AGENTS_DIR}/${spec.label}.plist`
521
- const plistPath = join(home, relativePlist)
522
- const programPath = installedProgramPath(id, options.libexecDir)
523
- const plistContent = renderLaunchAgentPlist(spec, { home, programPath })
524
-
525
- let changed = false
526
- const hash = needsBuild(id, spec, options)
527
- if (hash !== null) {
528
- // Build to a staged path so a failed build never clobbers a working binary.
529
- const staged = `${programPath}.staged`
530
- mkdirSync(options.libexecDir, { recursive: true, mode: 0o755 })
531
- rmSync(staged, { force: true })
532
- try {
533
- options.builder.build(join(options.repoRoot, spec.source, spec.entry), staged, spec.label)
534
- chmodSync(staged, 0o755)
535
- } catch (cause) {
536
- rmSync(staged, { force: true })
537
- throw cause
538
- }
539
- renameSync(staged, programPath)
540
- atomicWrite(sourceHashPath(options.stateDir, id), `${hash}\n`, 0o644, null)
541
- changed = true
542
- lines.push(`launch-agent: built ${programPath} (identifier ${spec.label})`)
543
- }
544
- const existingPlist = readIfFile(plistPath)
545
- if (existingPlist !== plistContent) {
546
- ensureOwnedDir(join(home, 'Library'), owner)
547
- ensureOwnedDir(join(home, LAUNCH_AGENTS_DIR), owner)
548
- atomicWrite(plistPath, plistContent, 0o644, owner)
549
- changed = true
550
- lines.push(`launch-agent: wrote ${plistPath}`)
551
- }
552
- for (const legacy of spec.replaces) {
553
- if (existsSync(join(home, legacy))) {
554
- rmSync(join(home, legacy), { recursive: true, force: true })
555
- lines.push(`launch-agent: removed retired ${join(home, legacy)}`)
556
- changed = true
557
- }
558
- }
559
- const denial = lastRunDenial(spec, home)
560
- if (denial !== null) lines.push(denial)
561
- let logDir = home
562
- for (const part of dirname(spec.log).split('/')) {
563
- logDir = join(logDir, part)
564
- ensureOwnedDir(logDir, owner)
565
- }
566
-
567
- if (!options.launchctl.hasGuiDomain(uid)) {
568
- lines.push(`launch-agent: ${spec.label} installed; no GUI session for uid ${uid}, launchd loads it at next login`)
569
- continue
570
- }
571
- if (!changed && options.launchctl.isLoaded(uid, spec.label)) {
572
- lines.push(`launch-agent: already up to date: ${spec.label}`)
573
- continue
574
- }
575
- options.launchctl.bootout(uid, spec.label)
576
- const loaded = options.launchctl.bootstrap(uid, plistPath)
577
- if (!loaded.ok) throw new Error(`launchctl bootstrap gui/${uid} ${plistPath} failed: ${loaded.detail}`)
578
- lines.push(`launch-agent: loaded gui/${uid}/${spec.label}`)
579
- }
580
-
581
- return { lines }
582
- }
583
-
584
- function usage(): never {
585
- throw new Error(
586
- 'usage: launch-agents.ts --mode plan|apply --manifest FILE [--previous-manifest FILE] --platform mac|linux ' +
587
- '--device NAME --home ABSOLUTE_PATH --state-dir ABSOLUTE_PATH [--libexec-dir ABSOLUTE_PATH]',
588
- )
589
- }
590
-
591
- export function main(argv: readonly string[]): void {
592
- const args = new Map<string, string>()
593
- for (let index = 0; index < argv.length; index += 2) {
594
- const key = argv[index]
595
- const value = argv[index + 1]
596
- if (key === undefined || !key.startsWith('--') || value === undefined) usage()
597
- args.set(key.slice(2), value)
598
- }
599
- const mode = args.get('mode')
600
- const manifestPath = args.get('manifest')
601
- const platform = args.get('platform')
602
- const device = args.get('device')
603
- const home = args.get('home')
604
- const stateDir = args.get('state-dir')
605
- if (
606
- (mode !== 'plan' && mode !== 'apply') ||
607
- manifestPath === undefined ||
608
- (platform !== 'mac' && platform !== 'linux') ||
609
- device === undefined ||
610
- home === undefined ||
611
- stateDir === undefined
612
- ) {
613
- usage()
614
- }
615
- const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
616
- const previousPath = args.get('previous-manifest')
617
- const previousManifest = previousPath === undefined ? null : JSON.parse(readFileSync(previousPath, 'utf8'))
618
- const libexecDir = args.get('libexec-dir') ?? DEFAULT_LIBEXEC_DIR
619
- const repoRoot = resolve(import.meta.dir, '..', '..', '..')
620
-
621
- if (mode === 'plan') {
622
- for (const line of planLaunchAgents({ manifest, previousManifest, platform, device, home, repoRoot, libexecDir, stateDir })) {
623
- console.log(line)
624
- }
625
- return
626
- }
627
- const result = applyLaunchAgents({
628
- manifest,
629
- previousManifest,
630
- platform,
631
- device,
632
- home,
633
- repoRoot,
634
- libexecDir,
635
- stateDir,
636
- launchctl: systemLaunchctl,
637
- builder: systemProgramBuilder(process.execPath, join(repoRoot, 'bin/lib/bun-compile-build.sh')),
638
- })
639
- for (const line of result.lines) console.log(line)
640
- }
641
-
642
- if (import.meta.main) {
643
- try {
644
- main(process.argv.slice(2))
645
- } catch (cause) {
646
- console.error(`launch-agents: ${cause instanceof Error ? cause.message : String(cause)}`)
647
- process.exitCode = 1
648
- }
649
- }
@@ -1,21 +0,0 @@
1
- # Invariants
2
-
3
- The things this repository must never regress. These apply to every repository
4
- adl manages; add this repository's own below, one bullet each, stating the rule
5
- and how it is verified.
6
-
7
- - The compliance gates enabled in `adl-config.toml` pass before every commit
8
- (verified by `adl repo controls` pre-commit and CI). A gate is switched off by
9
- removing it from `[controls.compliance] enabled`, which is the owner's decision.
10
- - Protected paths (`adl-config.toml`, `.workflows/`, the agent surface folders,
11
- `workflows/` and the declared compliance register folder) are landed only by the
12
- owner. An agent leaves them uncommitted and asks (verified by the pre-commit
13
- integrity gate).
14
- - Ratchets only shrink. A register names the specific files granted, and a grant
15
- is removed once its file is clean, never added to (verified by the gate that reads
16
- the register).
17
- - No multi-line comments in code. The explanation lives in `docs/`, and the code
18
- keeps at most a one-line pointer (verified by `repository.no-multiline-comments`).
19
- - The standard trees `docs`, `plans`, `workflows` and `compliance` are declared in
20
- `[controls.compliance.trees]`, and layout rules follow those declarations
21
- (verified by the layout and docs gates).