@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.29 → 0.1.32
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 +2 -2
- package/bin/health-check +2 -2
- package/bin/reconcile +41 -21
- package/bin/setup +2 -1
- package/package.json +1 -1
- package/packages/agent-hooks/README.md +77 -8
- package/packages/agent-hooks/assets/opencode-plugin.template.ts +2 -6
- package/packages/agent-hooks/src/cli/args.ts +2 -2
- package/packages/agent-hooks/src/cli/doctor.ts +2 -1
- package/packages/agent-hooks/src/cli/rules.ts +2 -1
- package/packages/agent-hooks/src/deploy/opencode-template.ts +5 -3
- package/packages/agent-hooks/src/gating/config.ts +11 -0
- package/packages/agent-hooks/src/gating/running-adl.ts +24 -0
- package/packages/agent-hooks/src/hooks/crg/background.ts +7 -4
- package/packages/agent-hooks/src/hooks/log.ts +12 -12
- package/packages/agent-hooks/src/hooks/user-prompt-submit/handler.ts +2 -2
- package/packages/agent-hooks/src/log/writer.ts +170 -24
- package/packages/agent-hooks/src/main.ts +11 -4
- package/packages/agent-hooks/src/pre-tool-use/handler.ts +5 -4
- package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +1 -1
- package/packages/agent-hooks/src/rules/shell/invocation.ts +2 -1
- package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +24 -2
- package/packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh +19 -45
- package/packages/agent-skills/assets/diagnosing-bugs/SKILL.md +9 -0
- package/packages/agent-skills/assets/diagnosing-bugs/scripts/hitl-loop.template.sh +1 -12
- package/packages/azdo/src/azdo-wit.ts +8 -2
- package/packages/azdo/src/mcp-server.ts +1 -1
- package/packages/fleet/manifest.json +2 -1
- package/packages/fleet/src/repo-maintain.ts +5 -2
- package/packages/repo-controls/AGENTS.md +7 -4
- package/packages/repo-controls/README.md +346 -56
- package/packages/repo-controls/assets/GLOBAL-INVARIANTS.md +21 -0
- package/packages/repo-controls/assets/promote.ts +7 -85
- package/packages/repo-controls/assets/reconcile-guard.sh +1 -14
- package/packages/repo-controls/assets/scaffold/docs/AGENTS.md +25 -0
- package/packages/repo-controls/assets/scaffold/docs/CLAUDE.md +1 -0
- package/packages/repo-controls/assets/scaffold/docs/decisions/index.md +17 -0
- package/packages/repo-controls/assets/scaffold/docs/notes.md +20 -0
- package/packages/repo-controls/assets/scaffold/plans/AGENTS.md +28 -0
- package/packages/repo-controls/assets/scaffold/plans/CLAUDE.md +1 -0
- package/packages/repo-controls/assets/scaffold/plans/GAPS-AND-FINDINGS.md +12 -0
- package/packages/repo-controls/assets/scaffold/plans/LEDGER.md +8 -0
- package/packages/repo-controls/assets/scaffold/plans/features/.gitkeep +0 -0
- package/packages/repo-controls/assets/scaffold/root/AGENTS.md +22 -0
- package/packages/repo-controls/assets/scaffold/root/CLAUDE.md +1 -0
- package/packages/repo-controls/assets/scaffold/workflows/AGENTS.md +16 -0
- package/packages/repo-controls/assets/scaffold/workflows/CLAUDE.md +1 -0
- package/packages/repo-controls/assets/scaffold/workflows/pipelines/.gitkeep +0 -0
- package/packages/repo-controls/assets/scaffold/workflows/scripts/.gitkeep +0 -0
- package/packages/repo-controls/assets/version-bump.sh +7 -48
- package/packages/repo-controls/package/adl-compliance/AGENTS.md +25 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +6 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +7 -42
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/excluded/web/i18n/locales/zh-CN/strings.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +10 -44
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/check.py +1 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/index_updated/test_check.py +12 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +15 -23
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +0 -136
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +18 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +2 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +0 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/check.py +2 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/stable-delivery.md +10 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/stable-recommendation.md +12 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/guides/target-narration.md +10 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +6 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/ci_paths/test_check.py +3 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +10 -12
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/web/src/renderer/solo/item.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/web/src/a.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/web/src/b.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/fresh/fresh.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub1/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub10/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub11/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub2/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub3/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub4/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub5/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub6/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub7/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub8/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/many/sub9/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/services/i18n/locales/de/common.json +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/renderer/solo/item.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/web/src/vendored-crate/Cargo.toml +2 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/web/src/renderer/solo/item.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/web/src/a.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/web/src/b.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/web/src/c.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +2 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/web/src/renderer/solo/item.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/fresh/fresh.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub1/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub10/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub11/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub2/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub3/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub4/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub5/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub6/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub7/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub8/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/many/sub9/only.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/web/src/renderer/solo/item.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/web/src/renderer/solo/item.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +46 -54
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +16 -76
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +35 -32
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +46 -213
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/README.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +76 -194
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/check.py +18 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +49 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/guides/links.md +20 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/guides/route-target.md +8 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/guides/links.md +20 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/guides/route-target.md +8 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/test_check.py +4 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +3 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.notes/declared.txt +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +8 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +15 -135
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +98 -50
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +14 -10
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/__init__.py +3 -10
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/base.py +42 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/test_check.py +82 -49
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/test_scope.py +71 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/__init__.py +7 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/check.py +15 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/legacy_search/test_check.py +32 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +56 -124
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/Cargo.lock +7 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/Cargo.toml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/bun.lock +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/package.json +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/rust-toolchain.toml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/src/main.rs +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/Cargo.lock +7 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/Cargo.toml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/bun.lock +0 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/package.json +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/rust-toolchain.toml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/src/main.rs +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +8 -37
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +95 -130
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_root_lockfiles.py +101 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +25 -13
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/__init__.py +7 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/check.py +124 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/fixtures/valid-register.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/scanner.py +217 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/no_multiline_comments/test_check.py +391 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/__init__.py +7 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/check.py +16 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/vcs_in_scripts/test_check.py +32 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +12 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/ast_grep.py +2 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/declarations.py +24 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/receipt.py +2 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/scripts.py +120 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.toml +4 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +93 -99
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +93 -99
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/registry.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/root_shape.py +6 -47
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +54 -8
- package/packages/repo-controls/scripts/hooks/pre-commit +12 -2
- package/packages/repo-controls/scripts/hooks/pre-push +38 -20
- package/packages/repo-controls/src/cli.ts +22 -3
- package/packages/repo-controls/src/compliance-config.ts +132 -65
- package/packages/repo-controls/src/config.ts +55 -46
- package/packages/repo-controls/src/constants.ts +9 -0
- package/packages/repo-controls/src/coverage-ratchet.ts +19 -4
- package/packages/repo-controls/src/doctor.ts +20 -30
- package/packages/repo-controls/src/findings-ratchet.ts +14 -0
- package/packages/repo-controls/src/gitignore-block.ts +3 -13
- package/packages/repo-controls/src/hook-log.ts +7 -10
- package/packages/repo-controls/src/hook.ts +127 -83
- package/packages/repo-controls/src/install-compliance.ts +112 -0
- package/packages/repo-controls/src/install.ts +133 -30
package/bin/adl
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/bin/adl
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 82920928 bytes
|
|
4
|
+
sha256: f63a82963b12de92399625846d611dc9a469ceec692522e95fbae7ff3f702acb
|
package/bin/health-check
CHANGED
|
@@ -17,8 +17,8 @@ fi
|
|
|
17
17
|
|
|
18
18
|
case "$device" in
|
|
19
19
|
m5)
|
|
20
|
-
launchctl print system/vsts.agent.amsterdamdatalabs.Default.mac-m5-ci 2>/dev/null
|
|
21
|
-
|
|
20
|
+
runner_state="$(launchctl print system/vsts.agent.amsterdamdatalabs.Default.mac-m5-ci 2>/dev/null)"
|
|
21
|
+
[[ "$runner_state" == *'state = running'* ]]
|
|
22
22
|
;;
|
|
23
23
|
hostinger-vps-1)
|
|
24
24
|
# One unit per call: `systemctl is-active` given several units exits 0 when at
|
package/bin/reconcile
CHANGED
|
@@ -368,7 +368,26 @@ SURFACE_UPDATE_TIMEOUT_SECONDS="${RECONCILE_SURFACE_UPDATE_TIMEOUT_SECONDS:-300}
|
|
|
368
368
|
# brew's output for a cask whose upgrade needed sudo but was not allowed to ask: Homebrew refusing
|
|
369
369
|
# ("sudo is disabled by HOMEBREW_NO_SUDO."), or sudo -n failing fast. It never blocks.
|
|
370
370
|
brew_output_needs_password() {
|
|
371
|
-
|
|
371
|
+
local lowered="${1,,}" nl=$'\n'
|
|
372
|
+
local sudo_re="sudo: [^${nl}]*(password|terminal|tty|askpass)"
|
|
373
|
+
[[ "$lowered" == *'sudo is disabled by homebrew_no_sudo'* ]] && return 0
|
|
374
|
+
[[ "$lowered" =~ $sudo_re ]] && return 0
|
|
375
|
+
[[ "$lowered" == 'password:'* || "$lowered" == *"${nl}password:"* ]]
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
# Whole-line membership of $1 in newline list $2, pure bash (runs before ripgrep exists).
|
|
379
|
+
list_has_line() {
|
|
380
|
+
local nl=$'\n'
|
|
381
|
+
[[ "${nl}${2}${nl}" == *"${nl}${1}${nl}"* ]]
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
# True when file $2 has a line exactly equal to $1. Pure bash, same reason.
|
|
385
|
+
file_has_line() {
|
|
386
|
+
local line
|
|
387
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
388
|
+
[[ "$line" == "$1" ]] && return 0
|
|
389
|
+
done <"$2"
|
|
390
|
+
return 1
|
|
372
391
|
}
|
|
373
392
|
|
|
374
393
|
# Upgrades the outdated Homebrew formulae and casks, from ONE `brew outdated --json=v2` (the
|
|
@@ -430,7 +449,7 @@ run_brew_upgrade_all_body() {
|
|
|
430
449
|
excluded_names="$(jq -r '(.config.brew_upgrade_exclude // [])[]' "$manifest")"
|
|
431
450
|
while IFS=$'\t' read -r kind name installed available; do
|
|
432
451
|
[[ -n "$name" ]] || continue
|
|
433
|
-
if [[ -n "$excluded_names" ]] &&
|
|
452
|
+
if [[ -n "$excluded_names" ]] && list_has_line "$name" "$excluded_names"; then
|
|
434
453
|
echo "global-tools: brew upgrade skips excluded: $name ($installed → $available)"
|
|
435
454
|
skipped=$((skipped + 1))
|
|
436
455
|
continue
|
|
@@ -878,12 +897,12 @@ install_surface_policy_file() {
|
|
|
878
897
|
# "v" belongs to "dev"/"rev", not a version prefix -- silently matched and
|
|
879
898
|
# would have skipped a real upgrade). The RIGHT boundary is a space or a
|
|
880
899
|
# string end and never accepts "v", or "0.39.3" would incorrectly match
|
|
881
|
-
# inside "0.39.30".
|
|
882
|
-
#
|
|
900
|
+
# inside "0.39.30". The match is a literal, space-bounded glob (no regex), so
|
|
901
|
+
# dots in the version are never "any character."
|
|
883
902
|
release_archive_version_matches() {
|
|
884
|
-
local version_line="$1" version="$2"
|
|
885
|
-
|
|
886
|
-
|
|
903
|
+
local version_line="$1" version="$2" padded
|
|
904
|
+
padded=" ${version_line//$'\n'/ } "
|
|
905
|
+
[[ "$padded" == *" $version "* || "$padded" == *" v$version "* ]]
|
|
887
906
|
}
|
|
888
907
|
|
|
889
908
|
# Resolves the release tag GitHub's own "latest" redirect currently points
|
|
@@ -1040,7 +1059,7 @@ install_one_tool() {
|
|
|
1040
1059
|
}
|
|
1041
1060
|
fi
|
|
1042
1061
|
if [[ "$rc" -eq 0 && -n "$tap" && "$mode" == apply ]]; then
|
|
1043
|
-
|
|
1062
|
+
list_has_line "$tap" "$(brew_query tap)" || run_brew tap "$tap" || {
|
|
1044
1063
|
echo "ERROR $tool_id: brew tap $tap failed" >&2
|
|
1045
1064
|
rc=1
|
|
1046
1065
|
}
|
|
@@ -1070,7 +1089,7 @@ install_one_tool() {
|
|
|
1070
1089
|
# whole-tap grant ("Trusted taps: owner/repo") can never satisfy the
|
|
1071
1090
|
# item check.
|
|
1072
1091
|
if [[ "$rc" -eq 0 ]]; then
|
|
1073
|
-
brew_query trust | awk '{$1=$1};1'
|
|
1092
|
+
list_has_line "$package" "$(brew_query trust | awk '{$1=$1};1')" || run_brew trust "--$trust_kind" "$package" || {
|
|
1074
1093
|
echo "ERROR $tool_id: brew trust --$trust_kind $package failed" >&2
|
|
1075
1094
|
rc=1
|
|
1076
1095
|
}
|
|
@@ -1426,8 +1445,8 @@ managed_repo_script_legacy_marker='# Managed by Enact global-tools.'
|
|
|
1426
1445
|
is_managed_repo_script() {
|
|
1427
1446
|
local executable="$1"
|
|
1428
1447
|
[[ -f "$executable" && ! -L "$executable" ]] || return 1
|
|
1429
|
-
|
|
1430
|
-
|
|
1448
|
+
file_has_line "$managed_repo_script_marker" "$executable" ||
|
|
1449
|
+
file_has_line "$managed_repo_script_legacy_marker" "$executable"
|
|
1431
1450
|
}
|
|
1432
1451
|
|
|
1433
1452
|
atomic_install_managed_repo_script() {
|
|
@@ -2094,20 +2113,21 @@ run_surface_update() {
|
|
|
2094
2113
|
# update path, native/npm and Homebrew-cask alike, rather than one
|
|
2095
2114
|
# per-surface parser.
|
|
2096
2115
|
#
|
|
2097
|
-
#
|
|
2098
|
-
# inner `if var=$(grep ...); then` below (not `grep | sed`) for why -- under
|
|
2099
|
-
# this file's `set -euo pipefail`, grep's ordinary "no match" exit would
|
|
2100
|
-
# otherwise abort the run on every surface that DIDN'T report an error. An
|
|
2101
|
-
# `if`-tested command is exempt from that abort by design; the function's
|
|
2102
|
-
# OWN exit status is therefore never meaningful (an `if` with no `else`
|
|
2103
|
-
# always returns 0), so callers check the printed string's emptiness, never
|
|
2104
|
-
# this function's exit status.
|
|
2116
|
+
# Pure bash (no ripgrep yet); callers check the printed string, never the exit status.
|
|
2105
2117
|
surface_update_error_reason() {
|
|
2106
2118
|
local output="$1" plain line
|
|
2107
2119
|
# LC_ALL=C: the strip is bytewise. Under en_US.UTF-8 glibc resolves [@-~]
|
|
2108
2120
|
# by collation, so the escape's final letter would never match.
|
|
2109
2121
|
plain="$(LC_ALL=C sed -E $'s/\x1b\\[[0-9;]*[@-~]//g' <<<"$output")"
|
|
2110
|
-
|
|
2122
|
+
local error_re='^[[:space:]]*(✖[[:space:]]*)?[eE][rR][rR][oO][rR]:' candidate
|
|
2123
|
+
line=""
|
|
2124
|
+
while IFS= read -r candidate; do
|
|
2125
|
+
if [[ "$candidate" =~ $error_re ]]; then
|
|
2126
|
+
line="$candidate"
|
|
2127
|
+
break
|
|
2128
|
+
fi
|
|
2129
|
+
done <<<"$plain"
|
|
2130
|
+
if [[ -n "$line" ]]; then
|
|
2111
2131
|
sed -E 's/^[[:space:]]*(✖[[:space:]]*)?[eE][rR][rR][oO][rR]:[[:space:]]*//' <<<"$line"
|
|
2112
2132
|
fi
|
|
2113
2133
|
}
|
|
@@ -2309,7 +2329,7 @@ reconcile_one_surface_cli() {
|
|
|
2309
2329
|
# the per-tool brew path in install_one_tool; the kind is certain here).
|
|
2310
2330
|
# Bare names are core casks, trusted by default -- no grant.
|
|
2311
2331
|
if [[ "$name" == */* ]]; then
|
|
2312
|
-
brew_query trust | awk '{$1=$1};1'
|
|
2332
|
+
list_has_line "$name" "$(brew_query trust | awk '{$1=$1};1')" || run_brew trust --cask "$name" || {
|
|
2313
2333
|
echo "ERROR surface $name: brew trust --cask $name failed" >&2
|
|
2314
2334
|
return 1
|
|
2315
2335
|
}
|
package/bin/setup
CHANGED
|
@@ -112,7 +112,8 @@ if [[ "$fleet_host_platform" == linux ]]; then
|
|
|
112
112
|
"$brew_prefix/Homebrew" \
|
|
113
113
|
"$brew_prefix/Homebrew/Library/Taps/adl-tan-theta/homebrew-tap"; do
|
|
114
114
|
[[ -d "$brew_repo/.git" ]] || continue
|
|
115
|
-
|
|
115
|
+
safe_dirs="$(cd /tmp && sudo -u "$brew_owner" -H git config --global --get-all safe.directory 2>/dev/null)" || safe_dirs=""
|
|
116
|
+
if [[ $'\n'"$safe_dirs"$'\n' != *$'\n'"$brew_repo"$'\n'* ]]; then
|
|
116
117
|
(cd /tmp && sudo -u "$brew_owner" -H git config --global --add safe.directory "$brew_repo")
|
|
117
118
|
fi
|
|
118
119
|
done
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amsterdamdatalabs/adl-agent-control-linux-x64",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "adl CLI binary for linux/x64 (agent identity + Azure DevOps front door — see docs/architecture/cli-design.md)",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"os": [
|
|
@@ -78,14 +78,16 @@ adl hook doctor [--repo <path>]
|
|
|
78
78
|
In a managed repo doctor also prints both hook-log paths with their row
|
|
79
79
|
counts and echoes the last 5 deny-log rows.
|
|
80
80
|
|
|
81
|
-
## Hook logs: `.adl/logs/hooks
|
|
81
|
+
## Hook logs: `.adl/logs/agent-hooks/`
|
|
82
82
|
|
|
83
83
|
Written only for a managed repo with hooks enabled (same gate as every hook;
|
|
84
84
|
unmanaged or `[hooks] enabled = false` writes nothing). Only agent hook
|
|
85
85
|
invocations are logged. Logging is strictly best-effort: the directory is
|
|
86
86
|
created lazily with the first row, one append per file per invocation, and a
|
|
87
|
-
write failure (read-only dir, full disk) never changes a hook's stdout
|
|
88
|
-
|
|
87
|
+
write failure (read-only dir, full disk) never changes a hook's stdout
|
|
88
|
+
or exit code; it prints one `adl agent-hooks: could not write log row: ...`
|
|
89
|
+
line to stderr, so a lost row is never silent (git hooks print the same with
|
|
90
|
+
the prefix `adl repo controls:`). Environment variables are never logged.
|
|
89
91
|
|
|
90
92
|
This is a hard cut: legacy `.agents/hooks/hooks.log` and
|
|
91
93
|
`.agents/hooks/hooks-activity.log` files are neither read, copied, appended,
|
|
@@ -94,7 +96,7 @@ CSV files this format replaces (docs/architecture/cli-design.md §5a, Tarun 2026
|
|
|
94
96
|
`.enact/logs/` is no longer written at all.
|
|
95
97
|
|
|
96
98
|
Both files are **JSON Lines** — one JSON object per line, written by the one
|
|
97
|
-
shared logger every `.adl/logs
|
|
99
|
+
shared logger every `.adl/logs/<owner>/*.log` file uses
|
|
98
100
|
(`src/log/writer.ts`'s `writeLogEntry`, moved here from `apps/cli` so
|
|
99
101
|
agent-hooks "holds logs"; `apps/cli/src/log.ts` re-exports it unchanged).
|
|
100
102
|
Fields are written in a fixed order and an absent field is OMITTED, never
|
|
@@ -104,10 +106,60 @@ characters (`...<truncated>`, unchanged from the CSV era) and then redacted
|
|
|
104
106
|
(`redact()`: bearer tokens, `Authorization:` headers, JWTs, `*_TOKEN=`/
|
|
105
107
|
`*_SECRET=`/… name=value pairs, `--password`-style flags, URL userinfo).
|
|
106
108
|
Files live at `0600`, the directory at `0700`; a file that would exceed 10 MiB
|
|
107
|
-
is rotated to `.1`…`.5`, dropping `.5
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
is rotated to `.1`…`.5`, dropping `.5`, under a per-file lock so concurrent
|
|
110
|
+
hook processes normally rotate once (see "Rotation-race hardening" for the
|
|
111
|
+
residual cases).
|
|
112
|
+
|
|
113
|
+
Bug 600 (Tarun, 2026-10-08): `.adl/logs` has one folder per owner. This
|
|
114
|
+
package owns `agent-hooks/` and writes nothing else; git hooks write
|
|
115
|
+
`git-hooks/`, `adl run` writes `agents/`. The old top-level `hooks.log`,
|
|
116
|
+
`hooks-deny.log` and `crg-update.log` are never migrated, moved or deleted —
|
|
117
|
+
they are left where they are. `agent-hooks/crg-update.log` is the raw stdout+stderr
|
|
118
|
+
of the detached `code-review-graph update` and rotates by the same rule before
|
|
119
|
+
each spawn.
|
|
120
|
+
|
|
121
|
+
Rotation race (Bug 600): on 2026-10-08 Enact's logs showed `hooks.log.1` = 580 B
|
|
122
|
+
and `hooks.log.2` = 10 MB. Two hook processes both saw size > max; the first
|
|
123
|
+
rotated the full file to `.1` and appended, the second (stale size check)
|
|
124
|
+
rotated again, pushing the full file to `.2`. The per-file lock fixes this:
|
|
125
|
+
the size is re-read inside the lock, so a writer that lost the race finds the
|
|
126
|
+
file fresh and does not rotate again. Rotation is idempotent: the live file is
|
|
127
|
+
renamed aside first (a missing file means someone else already rotated, so it
|
|
128
|
+
stops), then the backups shift. An empty live file is never rotated, so one
|
|
129
|
+
row larger than the maximum cannot push an empty file into `.1`.
|
|
130
|
+
`crg-update.log` was observed unbounded (2.8 MB, no rotation) before it joined
|
|
131
|
+
the same rule.
|
|
132
|
+
|
|
133
|
+
Rotation-race hardening (Bug 600 verifiers, 2026-10-08): every pre-write stat
|
|
134
|
+
and open tolerates the live log being renamed away by another writer's
|
|
135
|
+
rotation (ENOENT means "fresh file": the append recreates it at 0600), so a
|
|
136
|
+
writer that does not itself rotate never loses its row. The lock file carries
|
|
137
|
+
a `pid-random` token. A stale lock (older than 5 s) is broken by unlinking it
|
|
138
|
+
only if it still holds the token we observed; a holder removes the lock only
|
|
139
|
+
if it still holds its own token. This is read-compare-unlink, so a narrow race
|
|
140
|
+
remains where two writers can briefly both hold the lock. No step of rotation
|
|
141
|
+
overwrites a backup: shifting and the final placement into `.1` are hard-link
|
|
142
|
+
then unlink, a slot that is already taken is shifted again (at most 5 attempts,
|
|
143
|
+
then rotation fails loudly and keeps the aside file), so a double lock holder
|
|
144
|
+
can duplicate or shuffle backups but never lose a live file. The one deliberate
|
|
145
|
+
loss is the oldest backup falling off `.5`. A crash between the aside rename and
|
|
146
|
+
the final placement leaves `<log>.rotating-<pid>-<random>`; the next rotation
|
|
147
|
+
(inside the lock) adopts every such file whose owner pid is dead, or whose mtime
|
|
148
|
+
is over 5 s old, into the chain, oldest first. A crash between the link and the
|
|
149
|
+
unlink can leave one duplicate of a backup.
|
|
150
|
+
A row is never dropped because of the lock: on a lock timeout or any other
|
|
151
|
+
lock error the writer appends the row WITHOUT rotating and prints one
|
|
152
|
+
`adl log: rotation deferred, row kept: ...` line to stderr. `adl run` guards
|
|
153
|
+
its own two rows the same way (`adl run: could not write log row: ...`, never
|
|
154
|
+
aborting the run). `agent-hooks/crg-update` is not a JSON `LogFileName`: crg
|
|
155
|
+
output is raw text written by `src/hooks/crg/background.ts`.
|
|
156
|
+
|
|
157
|
+
The weekly search-tool audit (`agent-search-tool-audit`) reads the per-owner
|
|
158
|
+
folders and still reads the legacy top-level `hooks.log` (never migrated)
|
|
159
|
+
without writing to it.
|
|
160
|
+
|
|
161
|
+
**`agent-hooks/hooks.log`** — one row per hook invocation of every kind (allow, deny,
|
|
162
|
+
route). **`agent-hooks/deny.log`** — the same row, additionally, only when the
|
|
111
163
|
invocation was a blocked pre-tool-use decision.
|
|
112
164
|
|
|
113
165
|
`src/hooks/log.ts`'s field mapping (the shared schema has no dedicated
|
|
@@ -414,3 +466,20 @@ patterns, and `test/unit/coverage-policy.test.ts` asserts that policy (and
|
|
|
414
466
|
that a below-threshold run really exits non-zero — Bun ignores the singular
|
|
415
467
|
`line`/`function` keys). `bun test <file>` without `--coverage` stays usable
|
|
416
468
|
for targeted runs.
|
|
469
|
+
|
|
470
|
+
## Generated OpenCode plugin
|
|
471
|
+
|
|
472
|
+
`adl install` deploys one OpenCode project plugin, `.opencode/plugins/adl-repo.ts`,
|
|
473
|
+
rendered by `src/deploy/opencode-template.ts` from `assets/opencode-plugin.template.ts`,
|
|
474
|
+
`assets/hooks.json` and the OpenCode surface in `deploy-profile.json`. Do not edit
|
|
475
|
+
the deployed file by hand; re-run `adl install`.
|
|
476
|
+
|
|
477
|
+
- The deployed file has no multi-line comments (Tarun 2026-10-08, Bug 602), so it
|
|
478
|
+
passes compliance control REP-007. Its header is a single line pointing here.
|
|
479
|
+
- Fallback projection: an event with no documented OpenCode equivalent runs on a
|
|
480
|
+
substitute event, per the surface's `eventFallback` map in
|
|
481
|
+
`packages/agent-hooks/deploy-profile.json` (today `SessionEnd -> SessionIdle`).
|
|
482
|
+
The generated file lists the map as one line, `// Event fallbacks: A -> B, C -> D`,
|
|
483
|
+
and emits no line at all when the map is empty.
|
|
484
|
+
- Source of truth for hook commands is `assets/hooks.json`; the generated arrays
|
|
485
|
+
carry `command`, `matcher`, `timeoutMs` and `fallbackFor` per entry.
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
// AUTO-GENERATED by packages/agent-hooks
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
// Projected via fallback (owner rule: an event with no documented OpenCode
|
|
5
|
-
// equivalent runs on a substitute event instead — see
|
|
6
|
-
// this surface's `eventFallback` in packages/agent-hooks/deploy-profile.json):
|
|
1
|
+
// AUTO-GENERATED by packages/agent-hooks from assets/hooks.json; do not edit. See packages/agent-hooks/README.md "Generated OpenCode plugin".
|
|
2
|
+
|
|
7
3
|
__ADL_FALLBACK_ENTRIES__
|
|
8
4
|
|
|
9
5
|
interface HookCommandSpec {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { readFileSync, statSync } from 'node:fs'
|
|
13
13
|
import { resolve } from 'node:path'
|
|
14
|
-
import { decodeConfig, type AdlRepoConfig } from '../gating/config.ts'
|
|
14
|
+
import { decodeConfig, describeConfigIssue, type AdlRepoConfig } from '../gating/config.ts'
|
|
15
15
|
import { MARKER, realRootIO, resolveManagedRepo } from '../gating/root.ts'
|
|
16
16
|
import type { HookResult } from '../hooks/contract.ts'
|
|
17
17
|
|
|
@@ -88,7 +88,7 @@ export function loadRepo(cwd: string, repoArg: string | undefined): LoadedRepo |
|
|
|
88
88
|
if (managed.configPath === null) return { ok: true, state, config: defaultConfig() }
|
|
89
89
|
const decoded = decodeConfig(readFileSync(managed.configPath, 'utf8'))
|
|
90
90
|
if (!decoded.ok) {
|
|
91
|
-
return { ok: false, state, error:
|
|
91
|
+
return { ok: false, state, error: describeConfigIssue(managed.configPath, decoded.issue) }
|
|
92
92
|
}
|
|
93
93
|
return { ok: true, state, config: decoded.value }
|
|
94
94
|
}
|
|
@@ -33,6 +33,7 @@ import { scanSkills } from '../hooks/user-prompt-submit/skills.ts'
|
|
|
33
33
|
import { loadTokenizer } from '../hooks/user-prompt-submit/tokenizer.ts'
|
|
34
34
|
import { loadVectorHalf, resolveGlobalArtifactDir } from '../hooks/user-prompt-submit/vector.ts'
|
|
35
35
|
import { HOOKS_DENY_LOG_REL, HOOKS_LOG_REL } from '../hooks/log.ts'
|
|
36
|
+
import { runningAdl } from '../gating/running-adl.ts'
|
|
36
37
|
import { buildEngineContext } from '../rules/engine.ts'
|
|
37
38
|
import { liveEnv, liveRepoIndex } from '../rules/ports.ts'
|
|
38
39
|
import { BUILTIN_RULES, requireRegistered } from '../rules/registry.ts'
|
|
@@ -67,7 +68,7 @@ function repoChecks(loaded: LoadedRepo): { readonly checks: Check[]; readonly co
|
|
|
67
68
|
if (!loaded.ok) return { checks: [repoLine, check('FAIL', 'config', loaded.error)], config: null }
|
|
68
69
|
const root = state.root as string
|
|
69
70
|
const ctx = buildEngineContext(loaded.config.rules, loaded.config.repo, requireRegistered(BUILTIN_RULES), liveRepoIndex(root), liveEnv, undefined, loaded.config.agent)
|
|
70
|
-
if (!ctx.ok) return { checks: [repoLine, check('FAIL', 'config', `${ctx.issue.pointer}: ${ctx.issue.message}`)], config: null }
|
|
71
|
+
if (!ctx.ok) return { checks: [repoLine, check('FAIL', 'config', `${ctx.issue.pointer}: ${ctx.issue.message}${runningAdl()}`)], config: null }
|
|
71
72
|
return { checks: [repoLine, check('ok', 'config', 'valid')], config: loaded.config }
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { buildEngineContext } from '../rules/engine.ts'
|
|
15
15
|
import { liveEnv, liveRepoIndex } from '../rules/ports.ts'
|
|
16
|
+
import { runningAdl } from '../gating/running-adl.ts'
|
|
16
17
|
import { BUILTIN_RULES, requireRegistered } from '../rules/registry.ts'
|
|
17
18
|
import type { AnyRuleSpec } from '../rules/spec.ts'
|
|
18
19
|
import { type CliResult, type RepoState, cliError, loadRepo, parseArgs, unmanagedReason } from './args.ts'
|
|
@@ -42,7 +43,7 @@ function resolveRules(cwd: string, repoArg: string | undefined): Resolved | CliR
|
|
|
42
43
|
const { state, config } = loaded
|
|
43
44
|
const registered = requireRegistered(BUILTIN_RULES)
|
|
44
45
|
const ctx = buildEngineContext(config.rules, config.repo, registered, liveRepoIndex(state.root ?? state.start), liveEnv, undefined, config.agent)
|
|
45
|
-
if (!ctx.ok) return cliError(`${state.configPath} ${ctx.issue.pointer}: ${ctx.issue.message}`)
|
|
46
|
+
if (!ctx.ok) return cliError(`${state.configPath} ${ctx.issue.pointer}: ${ctx.issue.message}${runningAdl()}`)
|
|
46
47
|
const hooksEnabled = config.hooks.enabled
|
|
47
48
|
const rules = [...registered.values()].map((spec) => ({
|
|
48
49
|
spec,
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* (render.ts's renderOpencodeSlots), the profile's pluginName, and the
|
|
4
4
|
* identifier derived from it — reproducing this repo's own committed
|
|
5
5
|
* `.opencode/plugins/adl-repo.ts` byte-for-byte for the current
|
|
6
|
-
* deploy-profile.json + assets/hooks.json, except the header
|
|
7
|
-
*
|
|
6
|
+
* deploy-profile.json + assets/hooks.json, except the header (a one-line
|
|
7
|
+
* comment plus an optional one-line fallback comment; no multi-line comments in
|
|
8
|
+
* deployed code, Bug 602). See README "Generated OpenCode plugin".
|
|
8
9
|
*/
|
|
9
10
|
import { readFileSync } from 'node:fs'
|
|
10
11
|
import { join } from 'node:path'
|
|
@@ -54,7 +55,8 @@ export function renderOpencodePlugin(options: RenderOpencodePluginOptions): stri
|
|
|
54
55
|
|
|
55
56
|
if (!template.includes('__ADL_FALLBACK_ENTRIES__')) throw new Error(`opencode plugin template is missing marker __ADL_FALLBACK_ENTRIES__: ${templatePath}`)
|
|
56
57
|
const fallbackEntries = Object.entries(surface.eventFallback ?? {})
|
|
57
|
-
|
|
58
|
+
const fallbackComment = fallbackEntries.length === 0 ? '' : `// Event fallbacks: ${fallbackEntries.map(([from, to]) => `${from} -> ${to}`).join(', ')}`
|
|
59
|
+
let out = template.replace('__ADL_FALLBACK_ENTRIES__', fallbackComment)
|
|
58
60
|
|
|
59
61
|
const slots = renderOpencodeSlots(hooksDoc, surface)
|
|
60
62
|
for (const slot of OPENCODE_SLOTS) {
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
*/
|
|
63
63
|
|
|
64
64
|
import { MARKER } from './root.ts'
|
|
65
|
+
import { runningAdl } from './running-adl.ts'
|
|
65
66
|
|
|
66
67
|
interface RuleSection {
|
|
67
68
|
readonly enabled: boolean
|
|
@@ -146,6 +147,16 @@ export interface AdlRepoConfig {
|
|
|
146
147
|
|
|
147
148
|
export type ConfigDecodeIssue = { readonly pointer: string; readonly message: string }
|
|
148
149
|
|
|
150
|
+
/**
|
|
151
|
+
* The one place a config-decode failure is rendered for an operator:
|
|
152
|
+
* `invalid <config> at <pointer>: <message> (adl <version> at <exec path>)`
|
|
153
|
+
* (`./running-adl.ts`). Added at rendering only — `ConfigDecodeIssue` stays
|
|
154
|
+
* untouched for programmatic consumers.
|
|
155
|
+
*/
|
|
156
|
+
export function describeConfigIssue(configPath: string, issue: ConfigDecodeIssue): string {
|
|
157
|
+
return `invalid ${configPath} at ${issue.pointer}: ${issue.message}${runningAdl()}`
|
|
158
|
+
}
|
|
159
|
+
|
|
149
160
|
export type ConfigDecodeResult =
|
|
150
161
|
| { readonly ok: true; readonly value: AdlRepoConfig }
|
|
151
162
|
| { readonly ok: false; readonly issue: ConfigDecodeIssue }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which adl is decoding the config. A stale binary rejecting a newer config's
|
|
3
|
+
* keys ("unknown key(s) 'fleet'") looks identical to a bad config, so every
|
|
4
|
+
* config rejection an operator sees carries `(adl <version> at <exec path>)`.
|
|
5
|
+
*
|
|
6
|
+
* The version belongs to the CLI (`apps/cli/package.json`, what `adl
|
|
7
|
+
* --version` prints). This package must not import upward into the app, so
|
|
8
|
+
* `adl hook` injects it through `run(argv, adlVersion)` in `../main.ts`. A
|
|
9
|
+
* caller that does not inject one (the standalone agent-hooks entry, in-process
|
|
10
|
+
* tests) renders `version unknown` — never a made-up number.
|
|
11
|
+
*
|
|
12
|
+
* The path is `process.execPath`: the compiled binary itself (there
|
|
13
|
+
* `process.argv[1]` is a virtual `/$bunfs` path); under `bun run` it is bun,
|
|
14
|
+
* which is what is actually running.
|
|
15
|
+
*/
|
|
16
|
+
let injectedVersion: string | undefined;
|
|
17
|
+
|
|
18
|
+
export function setRunningAdlVersion(version: string | undefined): void {
|
|
19
|
+
injectedVersion = version;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function runningAdl(): string {
|
|
23
|
+
return ` (adl ${injectedVersion ?? "version unknown"} at ${process.execPath})`;
|
|
24
|
+
}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* Protocol (all state under `<root>/.adl/`, already git-ignored):
|
|
12
12
|
* - `crg-update.lock` holds the pid of the process running the update.
|
|
13
13
|
* - `crg-update.pending` marker: "an edit landed while an update ran".
|
|
14
|
-
* - `logs/crg-update.log` the update's own stdout+stderr (append
|
|
14
|
+
* - `logs/agent-hooks/crg-update.log` the update's own stdout+stderr (append;
|
|
15
|
+
* rotated by the shared writer's rule before each spawn, Bug 600).
|
|
15
16
|
*
|
|
16
17
|
* `requestUpdate` (the hook side):
|
|
17
18
|
* 1. Acquire the lock atomically (`wx`), holding the hook's own pid as a
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
* Self-heal: if `code-review-graph update --skip-flows` exits non-zero,
|
|
34
35
|
* the runner runs `code-review-graph build --skip-flows` once and logs a
|
|
35
36
|
* `SELF-HEAL:` marker to stderr (captured by the spawn into
|
|
36
|
-
* `crg-update.log`). The original error is already in the log because
|
|
37
|
+
* `agent-hooks/crg-update.log`). The original error is already in the log because
|
|
37
38
|
* the spawn captures the update's own stderr; the marker is appended
|
|
38
39
|
* after, so the failure is never silently swallowed. The build runs at
|
|
39
40
|
* most once per runner invocation (a per-runner shell variable), so a
|
|
@@ -47,10 +48,11 @@ import { closeSync, mkdirSync, openSync, readFileSync, rmSync, statSync, writeFi
|
|
|
47
48
|
import { join } from 'node:path'
|
|
48
49
|
|
|
49
50
|
import { ensureAdlGitignore } from '../../log/adl-dir.ts'
|
|
51
|
+
import { rotateIfNeeded } from '../../log/writer.ts'
|
|
50
52
|
|
|
51
53
|
export const LOCK_REL = '.adl/crg-update.lock'
|
|
52
54
|
export const PENDING_REL = '.adl/crg-update.pending'
|
|
53
|
-
export const UPDATE_LOG_REL = '.adl/logs/crg-update.log'
|
|
55
|
+
export const UPDATE_LOG_REL = '.adl/logs/agent-hooks/crg-update.log'
|
|
54
56
|
|
|
55
57
|
/** A lock older than this is presumed left by a crash, whatever its pid says. */
|
|
56
58
|
export const MAX_LOCK_AGE_MS = 30 * 60 * 1000
|
|
@@ -113,6 +115,7 @@ function isPidAlive(pid: number): boolean {
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
function spawnDetached(spec: SpawnSpec): number {
|
|
118
|
+
rotateIfNeeded(spec.logFile, 0)
|
|
116
119
|
const fd = openSync(spec.logFile, 'a', 0o600)
|
|
117
120
|
try {
|
|
118
121
|
const proc = Bun.spawn([spec.cmd, ...spec.args], {
|
|
@@ -185,7 +188,7 @@ function acquireAndStart(deps: BackgroundDeps, root: string, env: SpawnSpec['env
|
|
|
185
188
|
}
|
|
186
189
|
|
|
187
190
|
export function requestUpdate(deps: BackgroundDeps, root: string, env: SpawnSpec['env']): UpdateRequest {
|
|
188
|
-
mkdirSync(join(root, '.adl', 'logs'), { recursive: true, mode: 0o700 })
|
|
191
|
+
mkdirSync(join(root, '.adl', 'logs', 'agent-hooks'), { recursive: true, mode: 0o700 })
|
|
189
192
|
ensureAdlGitignore(join(root, '.adl'))
|
|
190
193
|
const lockPath = join(root, LOCK_REL)
|
|
191
194
|
const pendingPath = join(root, PENDING_REL)
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The agent-hook decision logs: `.adl/logs/hooks.log` (every
|
|
3
|
-
* allow, deny, route) and `.adl/logs/hooks
|
|
4
|
-
* rule and the `adl` hint shown to the agent) — docs/architecture/cli-design.md §5a.
|
|
2
|
+
* The agent-hook decision logs: `.adl/logs/agent-hooks/hooks.log` (every
|
|
3
|
+
* decision — allow, deny, route) and `.adl/logs/agent-hooks/deny.log` (every
|
|
4
|
+
* deny, with the rule and the `adl` hint shown to the agent) — docs/architecture/cli-design.md §5a; one folder per owner (README, "Hook logs").
|
|
5
5
|
*
|
|
6
6
|
* BEHAVIOUR CHANGE (Tarun, 2026-09-27, §5a): this used to write two CSV
|
|
7
7
|
* files under `.enact/logs/` (`adl-hook-deny.log` / `adl-hook-activity.log`,
|
|
8
8
|
* ported from enact-m5's `.agents/hooks/pre-tool-use/src/deny.mjs`
|
|
9
9
|
* `logDenial`). §5a's later decision replaces that with ONE JSON-Lines
|
|
10
|
-
* format shared by
|
|
11
|
-
* (`../log/writer.ts`'s `writeLogEntry`), written to `.adl/logs/hooks.log`
|
|
12
|
-
* and `.adl/logs/hooks
|
|
10
|
+
* format shared by every `.adl/logs/<owner>/*.log` file
|
|
11
|
+
* (`../log/writer.ts`'s `writeLogEntry`), written to `.adl/logs/agent-hooks/hooks.log`
|
|
12
|
+
* and `.adl/logs/agent-hooks/deny.log`. `.enact/logs/` is a **hard cut**: it is no
|
|
13
13
|
* longer written (the old files, if present, are left in place — see
|
|
14
14
|
* `../log/writer.ts`'s header and `writeLogEntry`'s rotation/redaction/0700
|
|
15
15
|
* '0600 behaviour, which this file now delegates to instead of owning its
|
|
@@ -76,8 +76,8 @@ import type { LogEntryInput } from '../log/writer.ts'
|
|
|
76
76
|
import { writeLogEntry } from '../log/writer.ts'
|
|
77
77
|
import type { ActivityNote, HookHandler, HookInvocation, HookResult } from './contract.ts'
|
|
78
78
|
|
|
79
|
-
export const HOOKS_LOG_REL = '.adl/logs/hooks.log'
|
|
80
|
-
export const HOOKS_DENY_LOG_REL = '.adl/logs/hooks
|
|
79
|
+
export const HOOKS_LOG_REL = '.adl/logs/agent-hooks/hooks.log'
|
|
80
|
+
export const HOOKS_DENY_LOG_REL = '.adl/logs/agent-hooks/deny.log'
|
|
81
81
|
const MAX_LOGGED_COMMAND_LENGTH = 2000
|
|
82
82
|
|
|
83
83
|
function truncateCommand(value: string): string {
|
|
@@ -212,10 +212,10 @@ export async function runLogged(inv: HookInvocation, handler: HookHandler, now:
|
|
|
212
212
|
const stamped = now()
|
|
213
213
|
const sameNow = () => stamped
|
|
214
214
|
try {
|
|
215
|
-
writeLogEntry('hooks', entry, { dir, now: sameNow })
|
|
216
|
-
if (outcome.denyRuleId !== undefined) writeLogEntry('hooks
|
|
217
|
-
} catch {
|
|
218
|
-
|
|
215
|
+
writeLogEntry('agent-hooks/hooks', entry, { dir, now: sameNow })
|
|
216
|
+
if (outcome.denyRuleId !== undefined) writeLogEntry('agent-hooks/deny', entry, { dir, now: sameNow })
|
|
217
|
+
} catch (error) {
|
|
218
|
+
process.stderr.write(`adl agent-hooks: could not write log row: ${error instanceof Error ? error.message : String(error)}\n`)
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
let result: HookResult
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { existsSync, readFileSync } from 'node:fs'
|
|
16
16
|
import { join } from 'node:path'
|
|
17
17
|
import type { HookHandler, HookInvocation } from '../contract.ts'
|
|
18
|
-
import { decodeConfig } from '../../gating/config.ts'
|
|
18
|
+
import { decodeConfig, describeConfigIssue } from '../../gating/config.ts'
|
|
19
19
|
import { MARKER } from '../../gating/root.ts'
|
|
20
20
|
import { DEFAULT_SKILLS_DIR } from './constants.ts'
|
|
21
21
|
import { skillRootsFor } from './skill-roots.ts'
|
|
@@ -52,7 +52,7 @@ function resolveSkillRoots(inv: HookInvocation): readonly string[] {
|
|
|
52
52
|
const text = readFileSync(configPath, 'utf8')
|
|
53
53
|
const decoded = decodeConfig(text)
|
|
54
54
|
if (!decoded.ok) {
|
|
55
|
-
throw new Error(
|
|
55
|
+
throw new Error(describeConfigIssue(configPath, decoded.issue))
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const explicit = decoded.value.hooks.skillsDir
|