@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.46 → 0.1.48
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.
- package/bin/adl +1 -1
- package/bin/reconcile +7 -2
- package/package.json +1 -1
- package/packages/agent-hooks/src/rules/impl/git-branch.ts +1 -1
- package/packages/agent-skills/assets/write-feature/SKILL.md +13 -15
- package/bin/lean-ctx-observe-strip +0 -434
- package/bin/lib/lean-ctx-observe-hooks.sh +0 -634
- package/bin/update-azure-agent-pool +0 -29
- package/packages/agent-hooks/src/hooks/lean-ctx/handler.ts +0 -22
- package/packages/agent-hooks/src/hooks/lean-ctx/index.ts +0 -173
- package/packages/agent-hooks/src/hooks/lean-ctx/mapping.ts +0 -121
- package/packages/agent-hooks/src/hooks/lean-ctx/scope-rewrite.ts +0 -207
- package/packages/agent-hooks/src/lean-ctx/env.ts +0 -123
- package/packages/agent-hooks/src/lean-ctx/mcp.ts +0 -142
- package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +0 -51
- package/packages/agent-hooks/src/rules/impl/adl-session-required.ts +0 -403
- package/packages/agent-hooks/src/rules/impl/tool-lean-ctx-global.ts +0 -594
- package/packages/agent-settings/assets/GLOBAL-AGENTS.md +0 -112
- package/packages/agent-settings/assets/lean-ctx/config.toml +0 -42
- package/packages/agent-settings/assets/lean-ctx/gitignore +0 -10
- package/packages/agent-settings/assets/lean-ctx/layout.toml +0 -7
- package/packages/agent-settings/src/deploy/lean-ctx-deploy.ts +0 -230
- package/packages/agent-settings/src/deploy/lean-ctx-merge.ts +0 -580
- package/packages/agent-skills/assets/adl-mechanics/SKILL.md +0 -38
- package/packages/agent-skills/assets/codebase-design/SKILL.md +0 -98
- package/packages/agent-skills/assets/codebase-design/references/DEEPENING.md +0 -37
- package/packages/agent-skills/assets/codebase-design/references/DESIGN-IT-TWICE.md +0 -44
- package/packages/agent-skills/assets/codebase-design/references/TESTABILITY.md +0 -29
- package/packages/agent-skills/assets/debug-issue/SKILL.md +0 -41
- package/packages/agent-skills/assets/domain-modeling/SKILL.md +0 -86
- package/packages/agent-skills/assets/domain-modeling/references/ADR-FORMAT.md +0 -47
- package/packages/agent-skills/assets/domain-modeling/references/CONTEXT-FORMAT.md +0 -60
- package/packages/agent-skills/assets/implement/SKILL.md +0 -28
- package/packages/agent-skills/assets/review-changes/SKILL.md +0 -45
- package/packages/agent-skills/assets/tdd/SKILL.md +0 -49
- package/packages/agent-skills/assets/tdd/references/mocking.md +0 -59
- package/packages/agent-skills/assets/tdd/references/tests.md +0 -77
- package/packages/agent-skills/assets/testing/SKILL.md +0 -54
- package/packages/agent-skills/assets/testing/references/e2e.md +0 -37
- package/packages/agent-skills/assets/testing/references/integration.md +0 -37
- package/packages/agent-skills/assets/testing/references/unit.md +0 -32
- package/packages/agent-skills/assets/to-feature/SKILL.md +0 -91
- package/packages/agent-skills/assets/to-feature/references/FEATURE-SPEC-TEMPLATE.md +0 -88
- package/packages/agent-skills/assets/to-workitems/SKILL.md +0 -90
- package/packages/agent-skills/assets/to-workitems/references/AZURE-DEVOPS-HIERARCHY.md +0 -121
- package/packages/agent-skills/assets/to-workitems/references/TICKET-TEMPLATES.md +0 -70
- package/packages/azdo/src/workflow-rules-matrix.csv +0 -85
- package/packages/fleet/src/launch-agents.ts +0 -649
- package/packages/repo-controls/assets/GLOBAL-INVARIANTS.md +0 -21
- package/packages/repo-controls/assets/enable-integration-auto-complete.mjs +0 -79
- package/packages/repo-controls/assets/scaffold/docs/notes.md +0 -20
- package/packages/repo-controls/assets/scaffold/plans/GAPS-AND-FINDINGS.md +0 -12
- package/packages/repo-controls/azure-pipelines.yml +0 -274
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +0 -1
- 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
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +0 -17
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +0 -13
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-delivery.md +0 -10
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-recommendation.md +0 -12
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/target-narration.md +0 -10
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/mobile/assets/images/icon.png +0 -1
- 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
- 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
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/public/pwa/icon-192.png +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/resources/product.png +0 -1
- 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
- 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
- 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
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/__init__.py +0 -11
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/check.py +0 -62
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/NotAMigration.sql +0 -1
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/test_check.py +0 -116
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/links.md +0 -20
- 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
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/links.md +0 -20
- 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
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.changeset/declared.txt +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/INDEPENDENT-VERIFICATION.md +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/.gitattributes +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/large file.bin +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/.gitattributes +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/large file.bin +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/small.txt +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/missing-filter/large file.bin +0 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/.gitattributes +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/payload.dat +0 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/__init__.py +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/__init__.py +0 -12
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/check.py +0 -129
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/disjoint/gate-registry.yaml +0 -13
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/empty/gate-registry.yaml +0 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/overlapping/gate-registry.yaml +0 -13
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/test_check.py +0 -109
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/__init__.py +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/__init__.py +0 -11
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/check.py +0 -149
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/clean/docs/readme.md +0 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/relevant/VERSION +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/test_check.py +0 -151
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/__init__.py +0 -11
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/check.py +0 -202
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/changesets/package.json +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/package.json +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/CHANGELOG.md +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/VERSION +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/changesets/package.json +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/package.json +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/CHANGELOG.md +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/VERSION +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/test_check.py +0 -162
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/check.py +0 -103
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.lock +0 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.toml +0 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/rust-toolchain.toml +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/src/main.rs +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/frontend/bun.lock +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.lock +0 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.toml +0 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/rust-toolchain.toml +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/src/main.rs +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/frontend/bun.lock +0 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/grants-registry.yaml +0 -112
- package/packages/repo-controls/workflows/scripts/release/verify-npm-artifact.ts +0 -119
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `adl hook lean-ctx mcp` (work item A.1): a true stdio-passthrough spawn
|
|
3
|
-
* of the global `lean-ctx mcp` server.
|
|
4
|
-
*
|
|
5
|
-
* This is fundamentally NOT a `HookHandler`: every other kind reads one JSON
|
|
6
|
-
* payload off stdin, decides something, and writes one JSON/text result —
|
|
7
|
-
* `main.ts`'s `run()` buffers stdin into a string and buffers a handler's
|
|
8
|
-
* whole stdout/stderr into a `HookResult` for exactly that shape. `lean-ctx
|
|
9
|
-
* mcp` is a long-running MCP server that speaks JSON-RPC continuously over
|
|
10
|
-
* stdin/stdout for the lifetime of the process; buffering either stream
|
|
11
|
-
* would break the protocol (and blocking on "read all of stdin first" would
|
|
12
|
-
* simply hang forever, since an MCP client never closes stdin). So `main.ts`
|
|
13
|
-
* dispatches `lean-ctx mcp` here directly, before it ever reads stdin as a
|
|
14
|
-
* JSON payload or checks `--host` — see `main.ts`'s own dispatch for exactly
|
|
15
|
-
* where this branches off.
|
|
16
|
-
*
|
|
17
|
-
* Two responsibilities:
|
|
18
|
-
*
|
|
19
|
-
* 1. `resolveLeanCtxMcpEnv` — walk from `cwd` to the git root and check for
|
|
20
|
-
* `adl-config.toml` (`../gating/root.ts`'s `MARKER`), exactly
|
|
21
|
-
* like every other kind's gating BUT with a different outcome on
|
|
22
|
-
* failure: an unmanaged repo (no git root, or a git root with no
|
|
23
|
-
* marker) still starts the MCP server — just with `lean-ctx`'s own
|
|
24
|
-
* built-in defaults (no `LEAN_CTX_*` vars set at all) instead of this
|
|
25
|
-
* repo's `.agents/lean-ctx/...` — plus a one-line stderr note. Every
|
|
26
|
-
* other kind's gating treats "unmanaged" as "exit 0, do nothing"; that
|
|
27
|
-
* is wrong here because an editor's MCP client expects the server it
|
|
28
|
-
* just spawned to actually start, not silently exit.
|
|
29
|
-
* 2. `runLeanCtxMcp` — the real spawn: `stdin`/`stdout`/`stderr` all set to
|
|
30
|
-
* `'inherit'` (Bun.spawn's true fd-passthrough, not a piped-and-copied
|
|
31
|
-
* stream — no buffering, no JS-side copy loop that could stall or
|
|
32
|
-
* reorder JSON-RPC frames), `SIGINT`/`SIGTERM` forwarded from this
|
|
33
|
-
* process to the child so the MCP client's own shutdown signal reaches
|
|
34
|
-
* `lean-ctx` instead of being swallowed by `adl hook`, and the child's
|
|
35
|
-
* own exit code propagated back out as `main.ts`'s process exit code.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
import { join } from 'node:path'
|
|
39
|
-
import { findGitRoot, MARKER, realRootIO, type RootIO } from '../gating/root.ts'
|
|
40
|
-
import { LEAN_CTX_HEADLESS_ENV, leanCtxEnvFor } from './env.ts'
|
|
41
|
-
|
|
42
|
-
export interface LeanCtxMcpEnvResult {
|
|
43
|
-
readonly env: Readonly<Record<string, string | undefined>>
|
|
44
|
-
/** Non-null only for an unmanaged repo — see this module's header. */
|
|
45
|
-
readonly note: string | null
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Resolve the env `lean-ctx mcp` should run with, from `cwd` alone (there is
|
|
50
|
-
* no JSON payload for this subcommand — see this module's header). Injected
|
|
51
|
-
* `io` defaults to the real filesystem; tests supply a fake one, matching
|
|
52
|
-
* `../gating/root.ts`'s own `RootIO` pattern.
|
|
53
|
-
*/
|
|
54
|
-
export function resolveLeanCtxMcpEnv(
|
|
55
|
-
cwd: string,
|
|
56
|
-
baseEnv: Readonly<Record<string, string | undefined>>,
|
|
57
|
-
io: RootIO = realRootIO,
|
|
58
|
-
): LeanCtxMcpEnvResult {
|
|
59
|
-
const { root } = findGitRoot(io, cwd)
|
|
60
|
-
const managed = root !== null && io.isFile(join(root, MARKER))
|
|
61
|
-
|
|
62
|
-
if (!managed || root === null) {
|
|
63
|
-
return {
|
|
64
|
-
env: { ...baseEnv },
|
|
65
|
-
note: `adl hook: lean-ctx mcp: no managed repo found from ${cwd} (missing ${MARKER}); starting lean-ctx with its own defaults\n`,
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return { env: leanCtxEnvFor(root, baseEnv), note: null }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** The minimal shape of `Bun.spawn`'s return value this module needs — narrowed so a test can inject a fake. */
|
|
73
|
-
export interface SpawnedProcess {
|
|
74
|
-
readonly exited: Promise<number>
|
|
75
|
-
kill(signal?: number | NodeJS.Signals): void
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export type SpawnMcp = (
|
|
79
|
-
cmd: readonly string[],
|
|
80
|
-
opts: { readonly cwd: string; readonly env: Record<string, string | undefined> },
|
|
81
|
-
) => SpawnedProcess
|
|
82
|
-
|
|
83
|
-
const defaultSpawn: SpawnMcp = (cmd, opts) =>
|
|
84
|
-
Bun.spawn(cmd as string[], {
|
|
85
|
-
cwd: opts.cwd,
|
|
86
|
-
env: opts.env as Record<string, string>,
|
|
87
|
-
stdin: 'inherit',
|
|
88
|
-
stdout: 'inherit',
|
|
89
|
-
stderr: 'inherit',
|
|
90
|
-
}) as unknown as SpawnedProcess
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Spawn `lean-ctx mcp` with fully inherited stdio, forward `SIGINT`/`SIGTERM`
|
|
94
|
-
* to it, and resolve with its exit code (never throws for a normal child
|
|
95
|
-
* exit — `main.ts` passes this straight to `process.exit`).
|
|
96
|
-
*/
|
|
97
|
-
export async function runLeanCtxMcp(
|
|
98
|
-
cwd: string,
|
|
99
|
-
baseEnv: Readonly<Record<string, string | undefined>>,
|
|
100
|
-
deps: {
|
|
101
|
-
readonly spawn?: SpawnMcp
|
|
102
|
-
readonly writeStderr?: (text: string) => void
|
|
103
|
-
readonly signalTarget?: NodeJS.Process
|
|
104
|
-
readonly io?: RootIO
|
|
105
|
-
} = {},
|
|
106
|
-
): Promise<number> {
|
|
107
|
-
const spawn = deps.spawn ?? defaultSpawn
|
|
108
|
-
const writeStderr = deps.writeStderr ?? ((text: string) => process.stderr.write(text))
|
|
109
|
-
const signalTarget = deps.signalTarget ?? process
|
|
110
|
-
|
|
111
|
-
const { env, note } = resolveLeanCtxMcpEnv(cwd, baseEnv, deps.io ?? realRootIO)
|
|
112
|
-
if (note) writeStderr(note)
|
|
113
|
-
|
|
114
|
-
// Headless in an unmanaged folder too: see LEAN_CTX_HEADLESS_ENV — without
|
|
115
|
-
// it this very start re-writes lean-ctx's hooks into the global settings.
|
|
116
|
-
const child = spawn(['lean-ctx', 'mcp'], { cwd, env: { ...env, ...LEAN_CTX_HEADLESS_ENV } })
|
|
117
|
-
|
|
118
|
-
const onSigint = () => {
|
|
119
|
-
try {
|
|
120
|
-
child.kill('SIGINT')
|
|
121
|
-
} catch {
|
|
122
|
-
// already dead
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const onSigterm = () => {
|
|
126
|
-
try {
|
|
127
|
-
child.kill('SIGTERM')
|
|
128
|
-
} catch {
|
|
129
|
-
// already dead
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
signalTarget.on('SIGINT', onSigint)
|
|
133
|
-
signalTarget.on('SIGTERM', onSigterm)
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
const code = await child.exited
|
|
137
|
-
return code
|
|
138
|
-
} finally {
|
|
139
|
-
signalTarget.off('SIGINT', onSigint)
|
|
140
|
-
signalTarget.off('SIGTERM', onSigterm)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `adl.build-run-denied` — docs/operations/agent-permissions.md row 12 (build run):
|
|
3
|
-
* denied for EVERY identity, unconditionally — "pipelines run on their own
|
|
4
|
-
* triggers". Unlike the other role rules in this package, this one does not
|
|
5
|
-
* branch on `ADL_AGENT_ID_AGENT` at all: the matrix's own hook-action column
|
|
6
|
-
* shows ❌ for both `agent-tarun`/`agent-vivek` and `agent-reviewer`, so there
|
|
7
|
-
* is no identity this tool is allowed for. With no identity at all,
|
|
8
|
-
* `adl.run-required-for-writes` (registered ahead of this rule) reports
|
|
9
|
-
* first with its own, more specific "no session" message; this rule is what
|
|
10
|
-
* fires once an identity IS present, which is the case that rule leaves
|
|
11
|
-
* alone.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { Invocation } from '../shell/invocation.ts'
|
|
15
|
-
import type { Finding, MessageContext, RuleSpec } from '../spec.ts'
|
|
16
|
-
import { noParams } from '../spec.ts'
|
|
17
|
-
import { detailString } from './messages.ts'
|
|
18
|
-
import { mcpToolName } from './mcp-tool.ts'
|
|
19
|
-
import { toolCallFor } from './adl-mcp-cli.ts'
|
|
20
|
-
|
|
21
|
-
const RULE_ID = 'adl.build-run-denied'
|
|
22
|
-
|
|
23
|
-
export const adlBuildRunDenied: RuleSpec = {
|
|
24
|
-
id: RULE_ID,
|
|
25
|
-
category: 'tool-call',
|
|
26
|
-
severity: 'deny',
|
|
27
|
-
enabledByDefault: true,
|
|
28
|
-
summary: 'Deny build_run for every identity (docs/operations/agent-permissions.md row 12) — the MCP tool AND its adl mcp cli equivalent.',
|
|
29
|
-
params: noParams,
|
|
30
|
-
match(inv: Invocation): Finding | null {
|
|
31
|
-
// Also catch `adl mcp cli build-run` — same tool, same deny.
|
|
32
|
-
const call = toolCallFor(inv)
|
|
33
|
-
if (call === null) return null
|
|
34
|
-
if (mcpToolName(call.tool) !== 'build_run') return null
|
|
35
|
-
return { ruleId: RULE_ID, severity: 'deny', detail: { tool: call.tool } }
|
|
36
|
-
},
|
|
37
|
-
message(finding: Finding, ctx: MessageContext) {
|
|
38
|
-
const tool = detailString(finding, 'tool')
|
|
39
|
-
const user = 'pipelines run on their own triggers'
|
|
40
|
-
return {
|
|
41
|
-
user,
|
|
42
|
-
agent:
|
|
43
|
-
`DENIED ${tool}: ${user}. No agent identity may queue a build — use build_list_or_get to inspect an ` +
|
|
44
|
-
`already-running or already-queued build instead. Blocked: ${ctx.snippet}`,
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
corpus: {
|
|
48
|
-
deny: [],
|
|
49
|
-
allow: ['ls -la', 'git status'],
|
|
50
|
-
},
|
|
51
|
-
}
|
|
@@ -1,403 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `adl.session-required-for-writes` — the managed baseline hook (docs/architecture/cli-design.md
|
|
3
|
-
* §2/§3, item 10).
|
|
4
|
-
*
|
|
5
|
-
* WIDENED 2026-09-28 (STATUS.md 12p, "The
|
|
6
|
-
* standard" / W2 build item 3): the name is kept for git-history continuity,
|
|
7
|
-
* but this rule's job is no longer "writes only". Every Azure DevOps access
|
|
8
|
-
* — reads included — needs a resolvable agent identity now: `adl run --as
|
|
9
|
-
* <agent>`'s session env, the machine default-agent file
|
|
10
|
-
* (`adl-identity.ts`'s `resolveFromEnvOrFile`, steps 2-3 of the standard),
|
|
11
|
-
* or a valid inline `ADL_AGENT_ID_AGENT=<agent>` override on the command
|
|
12
|
-
* itself (step 1, detected via `../shell/git-identity.ts`'s
|
|
13
|
-
* `commandHasValidInlineIdentity` — see `checkResolvableIdentity` below). With
|
|
14
|
-
* NONE of those present, this rule denies every identity-bearing command
|
|
15
|
-
* class (git network reads/writes, the azdo MCP tools including their reads,
|
|
16
|
-
* `adl mcp cli`/`adl repo doctor`/`adl repo controls azdo-push`/`adl id
|
|
17
|
-
* token`/`adl-agent-id exec`, and curl/wget/httpie/xh to
|
|
18
|
-
* dev.azure.com/*.visualstudio.com under any HTTP method) with the
|
|
19
|
-
* standard's own message; every other command is left alone. Once an
|
|
20
|
-
* identity resolves, this rule gets out of the way entirely — `git-ado`/
|
|
21
|
-
* `adl`'s own rules, and the role rules, police what an authenticated
|
|
22
|
-
* session may do.
|
|
23
|
-
*
|
|
24
|
-
* Four write surfaces, each inspected in the shape the shell/tool-call
|
|
25
|
-
* classifier already hands every other rule:
|
|
26
|
-
*
|
|
27
|
-
* - `git push` (`../shell/invocation.ts`'s `'git'` kind). The hook cannot run
|
|
28
|
-
* `git remote get-url` (it decides before the child runs, statically, off
|
|
29
|
-
* argv alone) the way the deployed `bin/git` front controller can — so an
|
|
30
|
-
* explicit `https://.../dev.azure.com/...` URL anywhere in the push's
|
|
31
|
-
* arguments is authoritative, and otherwise the destination is UNRESOLVED
|
|
32
|
-
* (a bare `git push`, or `git push origin main` naming a remote this rule
|
|
33
|
-
* cannot look up) and fails closed: unresolved is treated exactly like
|
|
34
|
-
* "proven Azure DevOps", because the rule cannot prove otherwise. Only an
|
|
35
|
-
* explicit NON-Azure-DevOps URL is provably safe and lets a write through
|
|
36
|
-
* without an identity. `git-verify.ts`'s `git.no-verify` corpus already
|
|
37
|
-
* anticipates this: its own `allow` list deliberately never contains a bare
|
|
38
|
-
* `git push` or `git push -n`, see the comment there and
|
|
39
|
-
* `rules-corpus-executed.test.ts`'s "corrected corpus entries" check.
|
|
40
|
-
* - `az devops|repos|pipelines|boards|artifacts ...` (the `'azure-devops'`
|
|
41
|
-
* kind). `tool.azure-devops` (`./tool-azure-devops.ts`) already denies
|
|
42
|
-
* EVERY direct invocation of this family outright, read or write,
|
|
43
|
-
* identity or not, steering everything at the MCP server — and it is
|
|
44
|
-
* registered ahead of this rule, so in the real engine it always reports
|
|
45
|
-
* first. This rule's own az-CLI classification (read verbs `list`/`show`/
|
|
46
|
-
* `get`/`query` vs. everything else, fail-closed to "write" when neither
|
|
47
|
-
* matches) is therefore defense in depth for a repository that disables
|
|
48
|
-
* `tool.azure-devops` (`[rules."tool.azure-devops"] enabled = false`), not
|
|
49
|
-
* something reachable through `decide()` today — see
|
|
50
|
-
* `tests/unit/rules-adl-session-required.test.ts`, which exercises it via
|
|
51
|
-
* a direct `match()` call rather than the shared corpus (which runs
|
|
52
|
-
* through the real engine and would otherwise always see `tool.azure-devops`
|
|
53
|
-
* win first).
|
|
54
|
-
* - `curl`/`wget`/httpie (`http`/`https`)/`xh` (the generic `'command'` kind)
|
|
55
|
-
* with a non-GET method against a `dev.azure.com` URL. curl's method is
|
|
56
|
-
* explicit (`-X`/`--request`) or implied by a data flag (`-d`/`--data*`,
|
|
57
|
-
* `-F`/`--form`, `--json`); wget's by `--method=`/`--post-data`/
|
|
58
|
-
* `--post-file`; httpie/xh's by an explicit leading method argument or a
|
|
59
|
-
* request-item/`--form`/`--raw` implying a body (httpie's own semantics:
|
|
60
|
-
* a request with data defaults to POST, one without defaults to GET).
|
|
61
|
-
* - An Azure DevOps MCP write tool (the `'tool-call'` kind), named
|
|
62
|
-
* `mcp__<prefix>__<tool>` where `<tool>` is one of the four mutating
|
|
63
|
-
* tools `packages/azdo/src/mcp-server.ts` actually registers:
|
|
64
|
-
* `pr_create_or_update`, `pr_review`, `build_run`, `workitem_create_or_update`. The
|
|
65
|
-
* match is on the TOOL NAME alone, not the server prefix — the package is
|
|
66
|
-
* `azdo`, P4 folds the server under `adl`, and Claude Code's plugin-scoped
|
|
67
|
-
* form (`mcp__plugin_<plugin>_<server>__<tool>`) adds a third spelling —
|
|
68
|
-
* so `mcp__azdo__...`, `mcp__adl__...`, `mcp__azure-devops-mcp__...` and
|
|
69
|
-
* `mcp__plugin_adl_azdo__...` are all matched the same way. Its three read
|
|
70
|
-
* tools (`pr_list_or_get`, `build_list_or_get`, `workitem_list_or_get`)
|
|
71
|
-
* are left alone under every one of those prefixes too.
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
import type { Invocation } from '../shell/invocation.ts'
|
|
75
|
-
import type { Token } from '../shell/token.ts'
|
|
76
|
-
import { dynamicToken, tokenValue } from '../shell/token.ts'
|
|
77
|
-
import { commandHasValidInlineIdentity, isNetworkGitInvocation } from '../shell/git-identity.ts'
|
|
78
|
-
import type { EnvPort } from '../ports.ts'
|
|
79
|
-
import type { Finding, MessageContext, RuleContext, RuleSpec } from '../spec.ts'
|
|
80
|
-
import { noParams } from '../spec.ts'
|
|
81
|
-
import {
|
|
82
|
-
ADL_AGENT_ID_BINARY_NAMES,
|
|
83
|
-
ADL_AGENT_ID_COMMAND_SHAPES,
|
|
84
|
-
ADL_BINARY_NAMES,
|
|
85
|
-
ADL_IDENTITY_COMMAND_SHAPES,
|
|
86
|
-
classifyGitPushDestination,
|
|
87
|
-
containsIdentityBearingToken,
|
|
88
|
-
HTTP_CLIENT_BINARY_NAMES,
|
|
89
|
-
httpClientMethod,
|
|
90
|
-
isAdoUrl,
|
|
91
|
-
isHelpOnlyInvocation,
|
|
92
|
-
matchesIdentityShape,
|
|
93
|
-
skipLeadingDashFlags,
|
|
94
|
-
} from './git-helpers.ts'
|
|
95
|
-
import { NO_AGENT_IDENTITY_MESSAGE, resolveFromEnvOrFile } from './adl-identity.ts'
|
|
96
|
-
import { detailString } from './messages.ts'
|
|
97
|
-
import { mcpToolName } from './mcp-tool.ts'
|
|
98
|
-
|
|
99
|
-
const RULE_ID = 'adl.session-required-for-writes'
|
|
100
|
-
const ENV_VAR = 'ADL_AGENT_ID_AGENT'
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* M7 (STATUS.md 12p): whether an identity is
|
|
104
|
-
* resolvable for `command`, and if not, the EXACT message to show — the
|
|
105
|
-
* one standard `NO_AGENT_IDENTITY_MESSAGE` when nothing is configured at
|
|
106
|
-
* all, or the SPECIFIC named error when something IS configured but is
|
|
107
|
-
* invalid (an unrecognized `ADL_AGENT_ID_AGENT` value, or a default-agent
|
|
108
|
-
* file naming an agent outside `VALID_AGENTS`) — `resolveFromEnvOrFile`'s
|
|
109
|
-
* own `UnresolvedIdentity.message` already carries exactly the right text
|
|
110
|
-
* for either case (see `adl-identity.ts`'s `resolveFromEnvOrFile`: it is
|
|
111
|
-
* `NO_AGENT_IDENTITY_MESSAGE` verbatim for `reason: 'none'`, and the named
|
|
112
|
-
* `invalidAgentError` text for `reason: 'invalid'`), so this reads that
|
|
113
|
-
* message through rather than reporting the SAME generic "run the fleet
|
|
114
|
-
* reconcile" text regardless of which of the two actually happened — an
|
|
115
|
-
* invalid identity is a materially different, more actionable diagnosis
|
|
116
|
-
* than none configured at all, and reporting the generic text for both
|
|
117
|
-
* hid that distinction from the agent.
|
|
118
|
-
*
|
|
119
|
-
* A valid inline `ADL_AGENT_ID_AGENT=<agent>` override anywhere on the
|
|
120
|
-
* command line (step 1 of the standard — coarse, whole-command; see
|
|
121
|
-
* `commandHasValidInlineIdentity`'s own doc for what that trades off)
|
|
122
|
-
* resolves too, without needing the session env / machine default-agent
|
|
123
|
-
* file (steps 2-3) at all.
|
|
124
|
-
*/
|
|
125
|
-
type IdentityCheck = { readonly resolvable: true } | { readonly resolvable: false; readonly message: string }
|
|
126
|
-
|
|
127
|
-
function checkResolvableIdentity(command: string, env: EnvPort): IdentityCheck {
|
|
128
|
-
if (commandHasValidInlineIdentity(command)) return { resolvable: true }
|
|
129
|
-
const resolution = resolveFromEnvOrFile(env)
|
|
130
|
-
return resolution.ok ? { resolvable: true } : { resolvable: false, message: resolution.message }
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const classifyPush = classifyGitPushDestination
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* az CLI verbs that only read. Fail-closed by design: anything NOT
|
|
137
|
-
* recognized here (including a verb this list simply does not know about
|
|
138
|
-
* yet) is treated as a write, never the reverse. Recognized against the
|
|
139
|
-
* FULL args after `az` (the subcommand group included, e.g. `repos pr
|
|
140
|
-
* list`) — a flag's VALUE (`--status active`) could in principle collide
|
|
141
|
-
* with a verb spelling, an accepted heuristic risk given `tool.azure-devops`
|
|
142
|
-
* denies the whole family outright regardless (see header).
|
|
143
|
-
*/
|
|
144
|
-
const AZ_READ_VERBS: ReadonlySet<string> = new Set(['list', 'show', 'get', 'query'])
|
|
145
|
-
|
|
146
|
-
function azCommandIsWrite(args: readonly Token[]): boolean {
|
|
147
|
-
for (const token of args) {
|
|
148
|
-
const value = tokenValue(token).toLowerCase()
|
|
149
|
-
if (value.startsWith('-')) continue
|
|
150
|
-
if (AZ_READ_VERBS.has(value)) return false
|
|
151
|
-
}
|
|
152
|
-
return true
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* M10 (STATUS.md 12p): the name set and the
|
|
157
|
-
* per-binary method detection both moved to `git-helpers.ts`'s
|
|
158
|
-
* `HTTP_CLIENT_BINARY_NAMES`/`httpClientMethod` — shared with
|
|
159
|
-
* `tool-azure-devops.ts`'s own new WRITE-outright deny (docs/operations/agent-permissions.md
|
|
160
|
-
* row 13), so the two cannot classify the same command two different ways.
|
|
161
|
-
*/
|
|
162
|
-
const HTTP_CLIENT_NAMES = HTTP_CLIENT_BINARY_NAMES
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Azure DevOps MCP write tools, derived from `packages/azdo/src/mcp-server.ts`'s
|
|
166
|
-
* `ListToolsRequestSchema` handler (re-grepped 2026-09-27 for C3) — the
|
|
167
|
-
* complete tool roster there is `pr_list_or_get`/`pr_create_or_update`/
|
|
168
|
-
* `pr_review`, `build_list_or_get`/`build_run`, `workitem_list_or_get`/
|
|
169
|
-
* `workitem_create_or_update`; the three not in this set are the read half.
|
|
170
|
-
* `pr_review` (C3, docs/operations/agent-permissions.md rows 6-8: comment + vote) joined
|
|
171
|
-
* this set the same day `adl-pr-review-role.ts` started gating it by role —
|
|
172
|
-
* a caller with no session identity at all must be denied here FIRST, the
|
|
173
|
-
* same "no session, not merely the wrong role" ordering `adl-pr-role.ts`
|
|
174
|
-
* documents for `pr_create_or_update`.
|
|
175
|
-
*/
|
|
176
|
-
const ADO_MCP_WRITE_TOOLS: ReadonlySet<string> = new Set([
|
|
177
|
-
'pr_create_or_update',
|
|
178
|
-
'pr_review',
|
|
179
|
-
'build_run',
|
|
180
|
-
'workitem_create_or_update',
|
|
181
|
-
])
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* WIDENED 2026-09-28: the standard requires an identity for every Azure
|
|
185
|
-
* DevOps MCP tool call, reads included — the read half no longer gets a
|
|
186
|
-
* pass just because it cannot mutate anything. `mcp-server.ts`'s complete
|
|
187
|
-
* roster: `pr_list_or_get`, `build_list_or_get`, `workitem_list_or_get`
|
|
188
|
-
* (reads) alongside the four writes above.
|
|
189
|
-
*/
|
|
190
|
-
const ADO_MCP_ALL_TOOLS: ReadonlySet<string> = new Set([...ADO_MCP_WRITE_TOOLS, 'pr_list_or_get', 'build_list_or_get', 'workitem_list_or_get'])
|
|
191
|
-
|
|
192
|
-
/** `adl <...>` shapes that need an identity even with no HTTP client or git verb involved — see `git-helpers.ts`'s `ADL_IDENTITY_COMMAND_SHAPES`. */
|
|
193
|
-
const ADL_BINARY_ALL_NAMES: ReadonlySet<string> = ADL_BINARY_NAMES
|
|
194
|
-
const ADL_AGENT_ID_BINARY_ALL_NAMES: ReadonlySet<string> = ADL_AGENT_ID_BINARY_NAMES
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* R3 (STATUS.md 12p, re-review 2026-09-28 night): interpreter
|
|
198
|
-
* one-liners whose `-c`/`-e`/`--eval` code string names an identity-bearing
|
|
199
|
-
* operation — `../shell/invocation.ts`'s walker classifies these as a plain,
|
|
200
|
-
* unclassified `'command'` Invocation (it only re-parses `-c` as shell for
|
|
201
|
-
* `sh`/`bash`/`zsh`/`dash`/`ksh`/`fish`, never an interpreter's own code), so
|
|
202
|
-
* `python3 -c "import subprocess; subprocess.run(['git','push'])"` was
|
|
203
|
-
* previously invisible to this rule entirely. Deliberately coarse
|
|
204
|
-
* (`containsIdentityBearingToken`, textual) — precise parsing of arbitrary
|
|
205
|
-
* Python/JS/Perl/Ruby/AppleScript source is not this rule's job.
|
|
206
|
-
*/
|
|
207
|
-
const INTERPRETER_ONE_LINER_NAMES: ReadonlySet<string> = new Set([
|
|
208
|
-
'python', 'python.exe', 'python2', 'python2.exe', 'python3', 'python3.exe',
|
|
209
|
-
'node', 'node.exe', 'perl', 'perl.exe', 'ruby', 'ruby.exe', 'bun', 'bun.exe',
|
|
210
|
-
'osascript', 'osascript.exe',
|
|
211
|
-
])
|
|
212
|
-
const INTERPRETER_CODE_FLAGS: ReadonlySet<string> = new Set(['-c', '-e', '--eval'])
|
|
213
|
-
|
|
214
|
-
/** Every code string passed via `-c`/`-e`/`--eval` among `args` — the token immediately following the flag. */
|
|
215
|
-
function interpreterCodeStrings(args: readonly Token[]): readonly string[] {
|
|
216
|
-
const codes: string[] = []
|
|
217
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
218
|
-
if (!INTERPRETER_CODE_FLAGS.has(tokenValue(args[index] as Token))) continue
|
|
219
|
-
const next = args[index + 1]
|
|
220
|
-
if (next !== undefined) codes.push(tokenValue(next))
|
|
221
|
-
}
|
|
222
|
-
return codes
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Whether `inv` is a candidate this rule cares about at all, and if so, the
|
|
227
|
-
* "reason" text for its Finding — computed WITHOUT ever resolving an
|
|
228
|
-
* identity (see `match`'s own L4 comment for why the ordering matters). The
|
|
229
|
-
* four surfaces are exactly the ones documented in this file's header.
|
|
230
|
-
*/
|
|
231
|
-
/**
|
|
232
|
-
* Exported for `../../pre-tool-use/handler.ts`'s own broader net (R3,
|
|
233
|
-
* STATUS.md 12p): a walker-visible identity-bearing shape
|
|
234
|
-
* the narrow `adl.git-identity` rewrite scanner does not recognize (buried
|
|
235
|
-
* inside `if`/`!`/`for..do`/`xargs`/a heredoc or here-string/a piped-into
|
|
236
|
-
* shell) still needs denying when the resolved identity cannot actually
|
|
237
|
-
* reach THIS specific host/tool-class's child process — this rule alone
|
|
238
|
-
* cannot make that call (it has no host/tool-class in its `RuleContext`),
|
|
239
|
-
* but it IS the one place that already classifies every one of those shapes
|
|
240
|
-
* correctly (see this file's header), so the handler reuses this function
|
|
241
|
-
* rather than re-deriving the same walker-based classification a second
|
|
242
|
-
* time.
|
|
243
|
-
*/
|
|
244
|
-
export function identityBearingReason(inv: Invocation): string | null {
|
|
245
|
-
if (inv.kind === 'git') {
|
|
246
|
-
// F2 (independent review, 2026-09-29): widened from a plain
|
|
247
|
-
// `NETWORK_GIT_VERBS.has(inv.subcommand)` gate — see
|
|
248
|
-
// `isNetworkGitInvocation`'s own doc for the additional network-reaching
|
|
249
|
-
// shapes now covered (git remote show/prune/update/set-head -a, git
|
|
250
|
-
// maintenance run --task=prefetch, the remote-https/remote-http/
|
|
251
|
-
// http-fetch transport helpers, git submodule update/sync --recursive,
|
|
252
|
-
// git archive --remote).
|
|
253
|
-
if (!isNetworkGitInvocation(inv.subcommand, inv.rest.map(tokenValue))) return null
|
|
254
|
-
if (isHelpOnlyInvocation(inv.rest)) return null
|
|
255
|
-
const risk = classifyPush(inv.rest)
|
|
256
|
-
if (risk === 'non-ado') return null
|
|
257
|
-
const verbLabel = inv.subcommand === 'push' ? 'git push' : `git ${inv.subcommand}`
|
|
258
|
-
return risk === 'ado' ? `${verbLabel} against a dev.azure.com remote` : `${verbLabel} with an unresolved (implicit) remote destination`
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (inv.kind === 'azure-devops') {
|
|
262
|
-
// Defense in depth only — tool.azure-devops (registered ahead of this
|
|
263
|
-
// rule in ../registry.ts) already denies the whole `az devops|repos|
|
|
264
|
-
// pipelines|boards|artifacts` family outright, read or write, identity
|
|
265
|
-
// or not. See this file's header.
|
|
266
|
-
return azCommandIsWrite(inv.args) ? 'az CLI Azure DevOps write' : null
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (inv.kind === 'command') {
|
|
270
|
-
if (HTTP_CLIENT_NAMES.has(inv.name)) {
|
|
271
|
-
const targetsAdo = inv.args.some((token) => isAdoUrl(tokenValue(token)))
|
|
272
|
-
if (!targetsAdo) return null
|
|
273
|
-
// WIDENED: every method now needs an identity, GET included — the
|
|
274
|
-
// standard covers reads too, not only the mutating methods this rule
|
|
275
|
-
// used to single out.
|
|
276
|
-
const method = httpClientMethod(inv.name, inv.args)
|
|
277
|
-
return `${inv.name} ${method} to dev.azure.com/visualstudio.com`
|
|
278
|
-
}
|
|
279
|
-
if (ADL_BINARY_ALL_NAMES.has(inv.name)) {
|
|
280
|
-
const values = inv.args.map((token) => tokenValue(token))
|
|
281
|
-
if (inv.args.some((token) => dynamicToken(token))) return null
|
|
282
|
-
if (!matchesIdentityShape(values, ADL_IDENTITY_COMMAND_SHAPES)) return null
|
|
283
|
-
return `${inv.name} ${values.slice(0, 3).join(' ')}`
|
|
284
|
-
}
|
|
285
|
-
if (ADL_AGENT_ID_BINARY_ALL_NAMES.has(inv.name)) {
|
|
286
|
-
const values = inv.args.map((token) => tokenValue(token))
|
|
287
|
-
if (inv.args.some((token) => dynamicToken(token))) return null
|
|
288
|
-
// M5 (STATUS.md 12p): leading dash-prefixed
|
|
289
|
-
// global flags are skipped before matching the `['exec']` shape —
|
|
290
|
-
// see git-helpers.ts's skipLeadingDashFlags doc.
|
|
291
|
-
if (!matchesIdentityShape(skipLeadingDashFlags(values), ADL_AGENT_ID_COMMAND_SHAPES)) return null
|
|
292
|
-
return `${inv.name} ${values.slice(0, 3).join(' ')}`
|
|
293
|
-
}
|
|
294
|
-
if (INTERPRETER_ONE_LINER_NAMES.has(inv.name)) {
|
|
295
|
-
const identityBearing = interpreterCodeStrings(inv.args).some((code) => containsIdentityBearingToken(code))
|
|
296
|
-
return identityBearing ? `${inv.name} interpreter one-liner` : null
|
|
297
|
-
}
|
|
298
|
-
return null
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (inv.kind === 'tool-call') {
|
|
302
|
-
const toolName = mcpToolName(inv.tool)
|
|
303
|
-
if (toolName === null || !ADO_MCP_ALL_TOOLS.has(toolName)) return null
|
|
304
|
-
return `Azure DevOps MCP tool ${inv.tool}`
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return null
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export const adlSessionRequiredForWrites: RuleSpec = {
|
|
311
|
-
id: RULE_ID,
|
|
312
|
-
category: 'tool-call',
|
|
313
|
-
severity: 'deny',
|
|
314
|
-
enabledByDefault: true,
|
|
315
|
-
summary: 'Deny Azure DevOps access (git, az CLI, curl/wget/httpie, adl entry points, MCP tools) with no resolvable ADL agent identity.',
|
|
316
|
-
params: noParams,
|
|
317
|
-
match(inv: Invocation, ctx: RuleContext<Record<string, never>>): Finding | null {
|
|
318
|
-
// L4 (STATUS.md 12p): the shape check runs
|
|
319
|
-
// FIRST, entirely without touching `ctx.env` — `checkResolvableIdentity`
|
|
320
|
-
// can hit the real filesystem (the machine default-agent file) once
|
|
321
|
-
// neither the command's own inline override nor the session env
|
|
322
|
-
// resolves, so it must not run for every invocation this rule is asked
|
|
323
|
-
// about. The vast majority (ls, cat, git status, an unrelated tool
|
|
324
|
-
// call, …) are not identity-bearing at all and, with this ordering,
|
|
325
|
-
// never reach `checkResolvableIdentity` — only a genuine candidate does.
|
|
326
|
-
const reason = identityBearingReason(inv)
|
|
327
|
-
if (reason === null) return null
|
|
328
|
-
|
|
329
|
-
const identity = checkResolvableIdentity(ctx.command ?? '', ctx.env)
|
|
330
|
-
if (identity.resolvable) return null
|
|
331
|
-
// M7: the exact message this specific failure earns — NOT a fixed
|
|
332
|
-
// generic string — carried in the Finding so message() reports it
|
|
333
|
-
// faithfully instead of a constant that cannot distinguish "nothing
|
|
334
|
-
// configured" from "something IS configured but it's wrong".
|
|
335
|
-
return { ruleId: RULE_ID, severity: 'deny', detail: { reason, identityMessage: identity.message } }
|
|
336
|
-
},
|
|
337
|
-
message(finding: Finding, ctx: MessageContext) {
|
|
338
|
-
const reason = detailString(finding, 'reason')
|
|
339
|
-
const identityMessage = detailString(finding, 'identityMessage')
|
|
340
|
-
// R8 (STATUS.md 12p, re-review 2026-09-28 night):
|
|
341
|
-
// NO_AGENT_IDENTITY_MESSAGE (the "nothing configured at all" case) IS
|
|
342
|
-
// ALREADY "prefix the command with ADL_AGENT_ID_AGENT=<agent>, or start
|
|
343
|
-
// the session with `adl run <surface> --as <agent>`" — appending the
|
|
344
|
-
// same advice a second time, in slightly different wording, said it
|
|
345
|
-
// twice. The INVALID case (something IS configured, but names an agent
|
|
346
|
-
// outside VALID_AGENTS) has no such text at all, so the "how to fix it"
|
|
347
|
-
// sentence is the only place that advice appears for it and stays.
|
|
348
|
-
const advice = identityMessage === NO_AGENT_IDENTITY_MESSAGE ? '' : ` Prefix the command with ${ENV_VAR}=<agent>, or start the session with adl run --as <agent>.`
|
|
349
|
-
return {
|
|
350
|
-
user: identityMessage,
|
|
351
|
-
agent: `DENIED ${reason} without an ADL agent identity. ${identityMessage}${advice} Blocked: ${ctx.snippet}`,
|
|
352
|
-
}
|
|
353
|
-
},
|
|
354
|
-
corpus: {
|
|
355
|
-
deny: [
|
|
356
|
-
'git push https://dev.azure.com/org/project/_git/repo main',
|
|
357
|
-
'git push origin main',
|
|
358
|
-
'git push',
|
|
359
|
-
// WIDENED (2026-09-28): git network READS now need an identity too —
|
|
360
|
-
// every Azure DevOps access does, not only writes.
|
|
361
|
-
'git fetch origin main',
|
|
362
|
-
'git clone https://dev.azure.com/org/project/_git/repo',
|
|
363
|
-
'git ls-remote https://dev.azure.com/org/project/_git/repo',
|
|
364
|
-
'curl -X POST https://dev.azure.com/org/project/_apis/git/repositories',
|
|
365
|
-
'wget --method=POST https://dev.azure.com/org/project/_apis/git/repositories',
|
|
366
|
-
// WIDENED: a plain GET to dev.azure.com now needs an identity too.
|
|
367
|
-
'curl https://dev.azure.com/org/project/_apis/git/repositories',
|
|
368
|
-
'wget https://dev.azure.com/org/project/_apis/git/repositories',
|
|
369
|
-
// WIDENED: the adl entry points that reach Azure DevOps.
|
|
370
|
-
'adl mcp cli pr-list-or-get',
|
|
371
|
-
'adl repo doctor',
|
|
372
|
-
'adl repo controls azdo-push',
|
|
373
|
-
'adl id token agent-tarun',
|
|
374
|
-
'adl-agent-id exec --agent agent-tarun -- adl id token agent-tarun --json',
|
|
375
|
-
// Already denied outright by tool.azure-devops (steers at the MCP server) —
|
|
376
|
-
// kept here too so this rule's own corpus is non-empty and honest about
|
|
377
|
-
// az CLI being a write surface it cares about; see the header for why
|
|
378
|
-
// its OWN read/write split is exercised in a dedicated unit test instead.
|
|
379
|
-
'az repos pr create --title x',
|
|
380
|
-
],
|
|
381
|
-
allow: [
|
|
382
|
-
// Explicit non-ADO destination: provably safe without an identity.
|
|
383
|
-
'git push https://github.com/org/repo main',
|
|
384
|
-
'git fetch https://github.com/org/repo main',
|
|
385
|
-
'curl https://github.com/org/repo',
|
|
386
|
-
'wget https://github.com/org/repo',
|
|
387
|
-
// Non-identity-bearing local commands and adl subcommands are untouched.
|
|
388
|
-
'git status',
|
|
389
|
-
'ls -la',
|
|
390
|
-
'adl run -- claude',
|
|
391
|
-
'adl repo controls compliance --repo .',
|
|
392
|
-
// A valid inline override supplies its own identity. A feature-branch
|
|
393
|
-
// destination (not `main`/`integration`) is used here so this stays a
|
|
394
|
-
// clean example of THIS rule's own concern — since W2 build item 2
|
|
395
|
-
// widened adl.git-push-branch-policy to grade the command's EFFECTIVE
|
|
396
|
-
// identity too, an inline-only override no longer bypasses branch
|
|
397
|
-
// protection: `ADL_AGENT_ID_AGENT=agent-tarun git push origin main`
|
|
398
|
-
// is correctly denied by that rule now, not allowed by this one.
|
|
399
|
-
'ADL_AGENT_ID_AGENT=agent-tarun git push origin feat/x',
|
|
400
|
-
'ADL_AGENT_ID_AGENT=agent-tarun adl id token agent-tarun',
|
|
401
|
-
],
|
|
402
|
-
},
|
|
403
|
-
}
|