@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.10 → 0.1.26
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/git +17 -18
- package/bin/git-ado +27 -26
- package/bin/git-defaults +173 -0
- package/bin/health-check +8 -1
- package/bin/kimi-hooks +1 -1
- package/bin/lib/adl-package.sh +257 -0
- package/bin/lib/bun-compile-build.sh +1 -1
- package/bin/lib/fleet-host.sh +62 -0
- package/bin/lib/lean-ctx-observe-hooks.sh +9 -9
- package/bin/reconcile +693 -470
- package/bin/reconcile-host +121 -0
- package/bin/setup +219 -0
- package/bin/shell-path-policy +15 -5
- package/bin/sync-and-reconcile +79 -68
- package/bin/validate-manifest +46 -51
- package/package.json +2 -12
- package/packages/agent-hooks/README.md +14 -9
- package/packages/agent-hooks/assets/hooks.json +0 -20
- package/packages/agent-hooks/package.json +1 -2
- package/packages/agent-hooks/src/cli/args.ts +1 -1
- package/packages/agent-hooks/src/cli/doctor.ts +6 -58
- package/packages/agent-hooks/src/deploy/deploy.ts +12 -41
- package/packages/agent-hooks/src/deploy/doctor.ts +8 -11
- package/packages/agent-hooks/src/deploy/index.ts +14 -14
- package/packages/agent-hooks/src/deploy/json-merge.ts +7 -8
- package/packages/agent-hooks/src/deploy/legacy-lock.ts +2 -2
- package/packages/agent-hooks/src/deploy/list.ts +1 -1
- package/packages/agent-hooks/src/deploy/lock.ts +31 -12
- package/packages/agent-hooks/src/deploy/profile.ts +3 -4
- package/packages/agent-hooks/src/deploy/source.ts +1 -1
- package/packages/agent-hooks/src/gating/config.ts +10 -24
- package/packages/agent-hooks/src/gating/root.ts +3 -3
- package/packages/agent-hooks/src/hooks/briefing/handler.ts +2 -2
- package/packages/agent-hooks/src/hooks/briefing/index.ts +27 -287
- package/packages/agent-hooks/src/hooks/contract.ts +17 -17
- package/packages/agent-hooks/src/hooks/crg/background.ts +27 -0
- package/packages/agent-hooks/src/hooks/crg/index.ts +7 -6
- package/packages/agent-hooks/src/hooks/log.ts +8 -10
- package/packages/agent-hooks/src/hooks/user-prompt-submit/match.ts +1 -1
- package/packages/agent-hooks/src/hooks/user-prompt-submit/tokenizer.ts +1 -1
- package/packages/agent-hooks/src/hooks/user-prompt-submit/vector.ts +6 -5
- package/packages/agent-hooks/src/hosts/registry.ts +1 -1
- package/packages/agent-hooks/src/log/adl-dir.ts +44 -0
- package/packages/agent-hooks/src/log/writer.ts +16 -2
- package/packages/agent-hooks/src/main.ts +7 -41
- package/packages/agent-hooks/src/pre-tool-use/handler.ts +71 -82
- package/packages/agent-hooks/src/rules/engine.ts +13 -17
- package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +3 -3
- package/packages/agent-hooks/src/rules/impl/adl-git-credential-bypass.ts +12 -14
- package/packages/agent-hooks/src/rules/impl/adl-git-identity.ts +28 -28
- package/packages/agent-hooks/src/rules/impl/adl-git-push-policy.ts +8 -9
- package/packages/agent-hooks/src/rules/impl/adl-human-azdo-bypass.ts +56 -59
- package/packages/agent-hooks/src/rules/impl/adl-identity.ts +16 -23
- package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +19 -16
- package/packages/agent-hooks/src/rules/impl/adl-pr-complete-pending.ts +2 -2
- package/packages/agent-hooks/src/rules/impl/adl-pr-review-role.ts +9 -12
- package/packages/agent-hooks/src/rules/impl/adl-pr-role.ts +5 -7
- package/packages/agent-hooks/src/rules/impl/adl-run-required.ts +411 -0
- package/packages/agent-hooks/src/rules/impl/adl-token-minting-role.ts +4 -5
- package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +6 -7
- package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +77 -70
- package/packages/agent-hooks/src/rules/impl/deny-allowlist.ts +159 -0
- package/packages/agent-hooks/src/rules/impl/fs-trash.ts +2 -2
- package/packages/agent-hooks/src/rules/impl/git-helpers.ts +21 -23
- package/packages/agent-hooks/src/rules/impl/git-worktree.ts +14 -28
- package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +3 -3
- package/packages/agent-hooks/src/rules/impl/messages.ts +1 -1
- package/packages/agent-hooks/src/rules/impl/pr-completion.ts +1 -1
- package/packages/agent-hooks/src/rules/impl/tool-azure-devops.ts +35 -46
- package/packages/agent-hooks/src/rules/impl/tool-find.ts +34 -7
- package/packages/agent-hooks/src/rules/impl/tool-grep.ts +34 -12
- package/packages/agent-hooks/src/rules/impl/tool-native-search.ts +52 -9
- package/packages/agent-hooks/src/rules/ports.ts +5 -7
- package/packages/agent-hooks/src/rules/registry.ts +4 -6
- package/packages/agent-hooks/src/rules/shell/git-identity.ts +86 -101
- package/packages/agent-hooks/src/rules/shell/git-internals.ts +1 -1
- package/packages/agent-hooks/src/rules/shell/invocation.ts +24 -118
- package/packages/agent-hooks/src/rules/shell/piped-script.ts +1 -1
- package/packages/agent-hooks/src/rules/shell/prefixes.ts +1 -1
- package/packages/agent-hooks/src/rules/shell/protected-carrier-writes.ts +11 -10
- package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +26 -26
- package/packages/agent-hooks/src/rules/shell/substitution.ts +1 -1
- package/packages/agent-hooks/src/rules/shell/xargs.ts +2 -2
- package/packages/agent-hooks/src/rules/spec.ts +2 -2
- package/packages/agent-settings/README.md +287 -0
- package/packages/agent-settings/assets/GLOBAL-AGENTS.md +18 -18
- package/packages/agent-settings/assets/agents/architect.toml +2 -0
- package/packages/agent-settings/assets/agents/code-reviewer.toml +2 -0
- package/packages/agent-settings/assets/agents/executor.toml +2 -0
- package/packages/agent-settings/assets/agents/explore.toml +2 -0
- package/packages/agent-settings/assets/agents/planner.toml +2 -0
- package/packages/agent-settings/assets/mcp.json +0 -4
- package/packages/agent-settings/deploy-profile.json +139 -0
- package/packages/agent-settings/deploy-profile.schema.json +147 -0
- package/packages/agent-settings/managed/README.md +29 -30
- package/packages/agent-settings/managed/codex-requirements.toml +9 -9
- package/packages/agent-settings/package.json +20 -0
- package/packages/agent-settings/src/deploy/agent-render.ts +2 -2
- package/packages/agent-settings/src/deploy/agent-toml.ts +1 -1
- package/packages/agent-settings/src/deploy/agents-deploy.ts +17 -3
- package/packages/agent-settings/src/deploy/doctor.ts +3 -83
- package/packages/agent-settings/src/deploy/index.ts +6 -33
- package/packages/agent-settings/src/deploy/legacy-lock.ts +6 -6
- package/packages/agent-settings/src/deploy/list.ts +5 -19
- package/packages/agent-settings/src/deploy/lock.ts +8 -18
- package/packages/agent-settings/src/deploy/mcp-deploy.ts +10 -5
- package/packages/agent-settings/src/deploy/mcp-merge.ts +22 -3
- package/packages/agent-settings/src/deploy/mcp-source.ts +1 -1
- package/packages/agent-settings/src/deploy/profile.ts +11 -35
- package/packages/agent-settings/src/deploy/retire-lean-ctx.ts +184 -0
- package/packages/agent-settings/src/deploy/settings.ts +17 -22
- package/packages/agent-settings/src/global-surface-sweep.ts +5 -17
- package/packages/agent-settings/src/kimi-hooks.ts +339 -192
- package/packages/agent-settings/src/opencode-hooks.ts +1 -1
- package/packages/agent-settings/tsconfig.json +23 -0
- package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +120 -0
- package/packages/agent-skills/assets/agent-search-tool-audit/scripts/audit.sh +276 -0
- package/packages/agent-skills/assets/debug-issue/SKILL.md +8 -7
- package/packages/agent-skills/assets/explore-codebase/SKILL.md +13 -13
- package/packages/agent-skills/assets/refactor-safely/SKILL.md +12 -11
- package/packages/agent-skills/assets/review-changes/SKILL.md +7 -7
- package/packages/agent-skills/assets/testing/references/e2e.md +37 -0
- package/packages/agent-skills/assets/testing/references/integration.md +37 -0
- package/packages/agent-skills/assets/testing/references/unit.md +32 -0
- package/packages/agent-skills/dist/deploy.d.ts +2 -0
- package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
- package/packages/agent-skills/dist/deploy.js +27 -1
- package/packages/agent-skills/dist/deploy.js.map +1 -1
- package/packages/agent-skills/dist/doctor.d.ts +0 -2
- package/packages/agent-skills/dist/doctor.d.ts.map +1 -1
- package/packages/agent-skills/dist/doctor.js +1 -1
- package/packages/agent-skills/dist/doctor.js.map +1 -1
- package/packages/agent-skills/dist/fs-utils.d.ts +0 -1
- package/packages/agent-skills/dist/fs-utils.d.ts.map +1 -1
- package/packages/agent-skills/dist/fs-utils.js +1 -1
- package/packages/agent-skills/dist/fs-utils.js.map +1 -1
- package/packages/agent-skills/package.json +1 -1
- package/packages/agent-skills/src/deploy.ts +29 -1
- package/packages/agent-skills/src/doctor.ts +1 -1
- package/packages/agent-skills/src/fs-utils.ts +1 -1
- package/packages/azdo/README.md +10 -9
- package/packages/azdo/package.json +2 -5
- package/packages/azdo/src/azdo-pl.ts +2 -54
- package/packages/azdo/src/azdo-pr-review.ts +4 -4
- package/packages/azdo/src/azdo-repo-doctor.ts +101 -52
- package/packages/azdo/src/azdo-wit.ts +41 -2
- package/packages/azdo/src/lib/agent-identity.ts +12 -14
- package/packages/azdo/src/lib/agent-token.ts +2 -2
- package/packages/azdo/src/lib/client.ts +0 -5
- package/packages/azdo/src/lib/graph-identity.ts +4 -4
- package/packages/azdo/src/workflow-rules-matrix.csv +85 -0
- package/packages/azdo/src/workflow-rules.ts +128 -0
- package/packages/fleet/manifest.json +38 -33
- package/packages/fleet/manifest.schema.json +67 -65
- package/packages/fleet/src/default-agent-file.ts +3 -3
- package/packages/fleet/src/install-adl-package.ts +73 -15
- package/packages/fleet/src/install-embedding-model.ts +14 -14
- package/packages/fleet/src/managed-settings.ts +7 -7
- package/packages/fleet/src/repo-maintain.ts +439 -0
- package/packages/fleet/src/scratch-reap.ts +6 -6
- package/packages/fleet/src/vscode-settings.ts +5 -5
- package/packages/repo-controls/README.md +141 -43
- package/packages/repo-controls/assets/promote.ts +4 -4
- package/packages/repo-controls/assets/reconcile-guard.sh +10 -46
- package/packages/repo-controls/assets/version-bump.sh +31 -84
- package/packages/repo-controls/bunfig.toml +6 -7
- package/packages/repo-controls/package/adl-compliance/AGENTS.md +5 -6
- package/packages/repo-controls/package/adl-compliance/conftest.py +3 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/__init__.py +4 -16
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/adr_lifecycle/test_check.py +9 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/test_check.py +11 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/__init__.py +7 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/check.py +266 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.drafts/c.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.drafts/d.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/.hidden.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/data.csv +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/diagram.png +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/mock.png +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs/guide/sub/nested.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/docs.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/grants.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p1.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p2.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p3.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p4.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-assets/plans/deep/p5.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/build.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/architecture/overview.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/install.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/docs/guide/start.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-clean/grants.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-001-tenancy.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-002-carrier.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/adr-003-cli.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/archive/adr-000-old.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/decisions/archive/adr-000-older.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/docs/guide/c.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-decisions/grants.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-01.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-02.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-03.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-04.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-05.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-06.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-07.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-08.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-09.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-10.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-11.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-12.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-13.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-14.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-15.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/docs/guide/page-16.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-default-cap/grants.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/docs/guide/c.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-exempt/grants.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/docs/guide/c.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-grew/grants.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/c.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/d.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/docs/guide/e.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-no-section/grants.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/guide/c.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/docs/short/only.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-over-cap/grants.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/c.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/held/d.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/docs/shrunk/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-stale/grants.yaml +6 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/a.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/b.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/docs/guide/c.md +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/fixtures/docs-size-unusable/grants.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/directory_size/test_check.py +345 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/check.py +18 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/test_check.py +35 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/__init__.py +7 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/check.py +91 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/AGENTS.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/CLAUDE.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/-leading-hyphen.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/AGENTS.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/double--hyphen.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/getting started.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/nested/page-here.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/trailing-hyphen-.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/ui-contract.md +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/architecture/user_profile.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/UI-CONTRACT.png +2 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/assets/diagram.png +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/index.md +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/notes.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/reference.html +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/2026-10-experiment.md +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs/research/UI-CONTRACT.md +6 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/fixtures/docs.md +5 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/filename/test_check.py +194 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/check.py +14 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/docs-knowledge-indexes.yaml +5 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-shipped-register-legacy/docs/compliance/index.md +10 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/fixtures/knowledge-indexes-shipped-register-type/docs/compliance/index.md +10 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/indexes/test_check.py +58 -6
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/ledger/test_check.py +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/check.py +64 -17
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/docs-okf-profiles.yaml +60 -9
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +17 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +13 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs/architecture/demo.md +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-clean/docs-okf-profiles.yaml +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/no-type.md +9 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-experiment.md +10 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-research.md +10 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs-okf-profiles.yaml +5 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs/architecture/bad-status.md +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-violation/docs-okf-profiles.yaml +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/experiment-ok.md +15 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +15 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +16 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +11 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +11 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-last-update.md +11 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-sources.md +10 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +11 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +11 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +16 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +14 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/architecture/created-date.md +21 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/architecture/research-shaped.md +19 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research-neighbours/docs/operations/runbook.md +26 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/test_check.py +228 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/register.py +5 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/test_check.py +11 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/__init__.py +5 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/check.py +160 -44
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/grants.yaml +0 -2
- 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 +1 -0
- 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 +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/grants.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/grants.yaml +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 +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/grants.yaml +5 -0
- 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 +1 -0
- 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 +1 -0
- 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 +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/grants.yaml +4 -0
- 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 +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/grants.yaml +5 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/grants.yaml +0 -2
- 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 +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/grants.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/register.py +127 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/test_check.py +265 -29
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/check.py +75 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/markdown_trees/test_check.py +194 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/check.py +56 -10
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/test_check.py +256 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/check.py +40 -12
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/test_check.py +108 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/check.py +2 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/check.py +23 -6
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/test_check.py +76 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/check.py +58 -17
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/test_check.py +161 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/check.py +35 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/scripts_size_depth/test_check.py +108 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/check.py +281 -61
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/absolute-path.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/backslash-path.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/baseline-is-a-list.yaml +2 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/boolean-size.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/dot-segment.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/duplicate-path.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-file.yaml +0 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/empty-segment.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/float-size.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/negative-size.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-over-limit-baseline.yaml +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/no-schema-version.yaml +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/non-string-path.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/not-yaml.yaml +2 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/null-size.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/parent-segment.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/string-size.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/top-level-is-a-list.yaml +2 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/trailing-slash.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/unknown-key.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/wrong-schema-version.yaml +2 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/malformed/zero-size.yaml +3 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/valid.yaml +4 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/test_check.py +758 -45
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/check.py +4 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/ignored_paths/test_check.py +2 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/cargo_lock.py +490 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/check.py +368 -55
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_check.py +4 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_declared_paths.py +523 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/test_stale_lock.py +1179 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/cli.py +122 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/check.py +33 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/constants.py +0 -11
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/core/context.py +16 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/config.py +1 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/docs_shape.py +12 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.alt.yaml +133 -147
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/gate-registry.yaml +133 -147
- 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 +52 -10
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/runner.py +23 -4
- package/packages/repo-controls/package.json +2 -10
- package/packages/repo-controls/scripts/hooks/post-merge +13 -0
- package/packages/repo-controls/scripts/hooks/pre-push +3 -3
- package/packages/repo-controls/src/azdo-push.ts +64 -36
- package/packages/repo-controls/src/azure-devops-governance.ts +7 -9
- package/packages/repo-controls/src/azure-devops-remote.ts +1 -1
- package/packages/repo-controls/src/cli.ts +29 -8
- package/packages/repo-controls/src/compliance-config.ts +410 -2
- package/packages/repo-controls/src/config.ts +133 -60
- package/packages/repo-controls/src/constants.ts +13 -27
- package/packages/repo-controls/src/coverage-ratchet.ts +4 -4
- package/packages/repo-controls/src/doctor.ts +46 -19
- package/packages/repo-controls/src/gitignore-block.ts +5 -29
- package/packages/repo-controls/src/hook-log.ts +1 -2
- package/packages/repo-controls/src/hook.ts +201 -45
- package/packages/repo-controls/src/install.ts +14 -8
- package/packages/repo-controls/src/integrity.ts +3 -3
- package/packages/repo-controls/src/lfs.ts +111 -0
- package/packages/repo-controls/src/promote.ts +8 -8
- package/packages/repo-controls/src/runtime.ts +9 -1
- package/packages/repo-controls/src/workflows-drift.ts +4 -5
- package/packages/repo-controls/src/worktree-guard.ts +3 -3
- package/packages/fleet/jobs/git-maintain/src/main.ts +0 -848
- package/packages/fleet/systemd/adl-azure-agent-update.service +0 -13
- package/packages/fleet/systemd/adl-azure-agent-update.timer +0 -10
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository maintenance: the repository step of `adl reconcile`
|
|
3
|
+
* (`adl maintain --location DIR`). It replaces the retired git-maintain
|
|
4
|
+
* LaunchAgent (2026-09-30, Tarun: "one binary controlling everything").
|
|
5
|
+
*
|
|
6
|
+
* Scope is exactly ONE directory, the host's `repo_location` from the fleet
|
|
7
|
+
* manifest, and only its IMMEDIATE children that carry `adl-config.toml` and
|
|
8
|
+
* are git top-levels. Nothing else is ever touched or mentioned: every child
|
|
9
|
+
* is resolved (symlinks followed) and must still be a direct child of the
|
|
10
|
+
* location BEFORE any file inside it is stat'ed, so a symlink can never make
|
|
11
|
+
* this step read, fetch into or install into anything outside the location.
|
|
12
|
+
* A directory without the marker is not this step's business at all.
|
|
13
|
+
*
|
|
14
|
+
* Per repository, in order:
|
|
15
|
+
* 1. `git fetch origin --prune`;
|
|
16
|
+
* 2. fast-forward the local `integration` and `main` (whichever exist on
|
|
17
|
+
* both sides) -- only ever a true fast-forward. A branch checked out in a
|
|
18
|
+
* worktree (this one or another) is moved only when that worktree is
|
|
19
|
+
* clean (`merge --ff-only`), else WARN and skip; a branch checked out
|
|
20
|
+
* nowhere is moved with a compare-and-swap `update-ref`;
|
|
21
|
+
* 3. the safe-only local branch prune (ported from git-maintain): protected
|
|
22
|
+
* integration/main/master/develop and HEAD are never deleted, the KEEP
|
|
23
|
+
* most recent branches are kept, and only "[gone]" upstreams (-D) or
|
|
24
|
+
* branches merged into the baseline (-d) are deleted;
|
|
25
|
+
* 4. the stale-branch prune (only when `branchMaxAgeDays` is configured, from
|
|
26
|
+
* the manifest's `config.repo_maintain.branch_max_age_days`): every local
|
|
27
|
+
* branch whose TIP commit is older than that many days is deleted with
|
|
28
|
+
* `git branch -D` -- never integration/main/master/develop, the checked-out
|
|
29
|
+
* HEAD, or a branch checked out in any worktree. Each deletion is a
|
|
30
|
+
* `WARN repo-maintain: <repo>: deleted stale branch <name> at <sha> (last
|
|
31
|
+
* commit <date>)` line, which is how a deleted branch is recovered from the
|
|
32
|
+
* log (`git branch <name> <sha>`); a dry run lists them without deleting;
|
|
33
|
+
* 5. `adl install --repo <repo>` -- the existing per-repo enrolment.
|
|
34
|
+
*
|
|
35
|
+
* This runs as the device administrator, never root (the caller,
|
|
36
|
+
* bin/reconcile-host, drops privileges and `adl maintain --expect-user`
|
|
37
|
+
* refuses otherwise). It never reads, migrates or modifies enact-config.toml:
|
|
38
|
+
* a repo may carry both markers, and adl-config.toml alone decides scope.
|
|
39
|
+
*
|
|
40
|
+
* Reporting: plain progress lines go to `out`; every warning and failure goes
|
|
41
|
+
* to `err` as `WARN repo-maintain: ...` / `ERROR repo-maintain: ...` (the
|
|
42
|
+
* level word `adl logs` filters on). A failure never stops the other
|
|
43
|
+
* repositories; the run returns `ok: false` when any occurred.
|
|
44
|
+
*/
|
|
45
|
+
import { spawnSync } from 'node:child_process'
|
|
46
|
+
import { readdirSync, realpathSync, statSync, type Dirent } from 'node:fs'
|
|
47
|
+
import { basename, dirname, isAbsolute, join } from 'node:path'
|
|
48
|
+
import { NO_AGENT_IDENTITY_MESSAGE, resolveAgentIdentity } from '../../azdo/src/lib/agent-identity.ts'
|
|
49
|
+
|
|
50
|
+
export const TCC_DENIED_MARKER = 'TCC denied'
|
|
51
|
+
|
|
52
|
+
/** The file whose presence puts a repository in this step's scope. */
|
|
53
|
+
const MARKER = 'adl-config.toml'
|
|
54
|
+
|
|
55
|
+
/** Branches fast-forwarded, in this order. */
|
|
56
|
+
const SYNCED_BRANCHES = ['integration', 'main'] as const
|
|
57
|
+
const PROTECTED = new Set(['integration', 'main', 'master', 'develop'])
|
|
58
|
+
const DEFAULT_KEEP = 3
|
|
59
|
+
|
|
60
|
+
/** Case-insensitive, by name and by realpath: never an Azure Pipelines agent install. */
|
|
61
|
+
const FORBIDDEN_PATH_PATTERN = /azagent|vsts\.agent/i
|
|
62
|
+
|
|
63
|
+
export interface MaintainOptions {
|
|
64
|
+
/** Absolute path of the host's `repo_location`. */
|
|
65
|
+
readonly location: string
|
|
66
|
+
/** The running adl binary, named in the Full Disk Access remedy. */
|
|
67
|
+
readonly executable: string
|
|
68
|
+
/** How to run `adl`: `<command> <...leadingArgs> install --repo <repo>`. */
|
|
69
|
+
readonly install: { readonly command: string; readonly leadingArgs: readonly string[] }
|
|
70
|
+
/** Most recent non-protected local branches kept by the prune. Default 3. */
|
|
71
|
+
readonly keep?: number
|
|
72
|
+
/** Report only: fetches, but never moves a ref, deletes a branch or runs install. */
|
|
73
|
+
readonly dryRun?: boolean
|
|
74
|
+
/**
|
|
75
|
+
* Delete local branches whose tip commit is older than this many days (a
|
|
76
|
+
* positive whole number). Unset: no age-based deletion. The manifest's
|
|
77
|
+
* `config.repo_maintain.branch_max_age_days`, never an environment variable.
|
|
78
|
+
*/
|
|
79
|
+
readonly branchMaxAgeDays?: number
|
|
80
|
+
/** The clock, injectable for tests. Default: the current time. */
|
|
81
|
+
readonly now?: () => Date
|
|
82
|
+
/** Environment for identity resolution and every child. Default process.env. */
|
|
83
|
+
readonly env?: NodeJS.ProcessEnv
|
|
84
|
+
readonly out?: (line: string) => void
|
|
85
|
+
readonly err?: (line: string) => void
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface MaintainResult {
|
|
89
|
+
readonly ok: boolean
|
|
90
|
+
readonly repositories: number
|
|
91
|
+
readonly failures: readonly string[]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Lines explaining why `root` could not be listed. EPERM from readdir is what
|
|
96
|
+
* macOS privacy protection (TCC) returns -- plain permission bits give EACCES
|
|
97
|
+
* -- so it names the exact binary the owner must grant Full Disk Access.
|
|
98
|
+
*/
|
|
99
|
+
export function listFailureLines(root: string, error: unknown, executable: string): readonly string[] {
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
101
|
+
const code = (error as { code?: unknown } | null)?.code
|
|
102
|
+
if (code === 'ENOTDIR') return [`${root}: ${message}`]
|
|
103
|
+
const lines = [`${root}: cannot list: ${message}`]
|
|
104
|
+
if (code === 'EPERM') lines.push(tccDeniedLine(root, executable))
|
|
105
|
+
return lines
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The one Full Disk Access hint, for a refused readdir and for a git child refused the same way. */
|
|
109
|
+
function tccDeniedLine(path: string, executable: string): string {
|
|
110
|
+
return (
|
|
111
|
+
`${path}: ${TCC_DENIED_MARKER}: macOS privacy protection blocked ${executable}; ` +
|
|
112
|
+
`one-time fix: System Settings › Privacy & Security › Full Disk Access › + › add ${executable}`
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface GitResult {
|
|
117
|
+
readonly ok: boolean
|
|
118
|
+
readonly stdout: string
|
|
119
|
+
readonly stderr: string
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function errorCode(error: unknown): string | undefined {
|
|
123
|
+
const code = (error as { code?: unknown } | null)?.code
|
|
124
|
+
return typeof code === 'string' ? code : undefined
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function lines(output: string): string[] {
|
|
128
|
+
return output.split('\n').filter((line) => line.length > 0)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function isForbiddenAzurePipelinesPath(path: string): boolean {
|
|
132
|
+
if (FORBIDDEN_PATH_PATTERN.test(path)) return true
|
|
133
|
+
try {
|
|
134
|
+
return FORBIDDEN_PATH_PATTERN.test(realpathSync(path))
|
|
135
|
+
} catch {
|
|
136
|
+
return false
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function maintainRepositories(options: MaintainOptions): MaintainResult {
|
|
141
|
+
const env = options.env ?? process.env
|
|
142
|
+
const keep = options.keep ?? DEFAULT_KEEP
|
|
143
|
+
const dry = options.dryRun ?? false
|
|
144
|
+
const out = options.out ?? ((line: string) => void process.stdout.write(`${line}\n`))
|
|
145
|
+
const errOut = options.err ?? ((line: string) => void process.stderr.write(`${line}\n`))
|
|
146
|
+
const info = (message: string): void => out(`repo-maintain: ${message}`)
|
|
147
|
+
const warn = (message: string): void => errOut(`WARN repo-maintain: ${message}`)
|
|
148
|
+
const failures: string[] = []
|
|
149
|
+
const fail = (message: string, subject: string): void => {
|
|
150
|
+
errOut(`ERROR repo-maintain: ${message}`)
|
|
151
|
+
failures.push(subject)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let identityEnv: NodeJS.ProcessEnv = {}
|
|
155
|
+
const childEnv = (): NodeJS.ProcessEnv => ({ ...env, ...identityEnv })
|
|
156
|
+
const git = (repo: string, ...args: string[]): GitResult => {
|
|
157
|
+
const result = spawnSync('git', ['-C', repo, ...args], { encoding: 'utf8', env: childEnv() })
|
|
158
|
+
if (result.error !== undefined) throw result.error
|
|
159
|
+
return { ok: result.status === 0, stdout: result.stdout, stderr: result.stderr ?? '' }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const maxAgeDays = options.branchMaxAgeDays
|
|
163
|
+
if (maxAgeDays !== undefined && (!Number.isInteger(maxAgeDays) || maxAgeDays < 1)) {
|
|
164
|
+
fail(`branch max age must be a positive whole number of days: ${maxAgeDays}`, 'branch-max-age-days')
|
|
165
|
+
return { ok: false, repositories: 0, failures }
|
|
166
|
+
}
|
|
167
|
+
const now = options.now ?? ((): Date => new Date())
|
|
168
|
+
|
|
169
|
+
const { location } = options
|
|
170
|
+
if (!isAbsolute(location)) {
|
|
171
|
+
fail(`location must be an absolute path: ${location}`, location)
|
|
172
|
+
return { ok: false, repositories: 0, failures }
|
|
173
|
+
}
|
|
174
|
+
if (isForbiddenAzurePipelinesPath(location)) {
|
|
175
|
+
fail(`${location}: refusing an Azure Pipelines agent path`, location)
|
|
176
|
+
return { ok: false, repositories: 0, failures }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Resolve the Azure DevOps agent identity ONCE, before touching any repo
|
|
180
|
+
// (F6): this job runs with no TTY and no ADL_AGENT_ID_AGENT, and bin/git-ado
|
|
181
|
+
// fails closed without one. A real resolution error is fatal; "nothing
|
|
182
|
+
// configured" is only fatal for Azure DevOps remotes, which are skipped
|
|
183
|
+
// loudly below rather than fetched with no identity.
|
|
184
|
+
let agent: string | undefined
|
|
185
|
+
try {
|
|
186
|
+
agent = resolveAgentIdentity(env).agent
|
|
187
|
+
} catch (error) {
|
|
188
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
189
|
+
if (message !== NO_AGENT_IDENTITY_MESSAGE) {
|
|
190
|
+
fail(`cannot resolve an Azure DevOps agent identity: ${message}`, 'identity')
|
|
191
|
+
return { ok: false, repositories: 0, failures }
|
|
192
|
+
}
|
|
193
|
+
warn(
|
|
194
|
+
'no Azure DevOps agent identity resolved (no ADL_AGENT_ID_AGENT, no default-agent file): ' +
|
|
195
|
+
'Azure DevOps repos will be skipped this run, not silently fetched with no identity',
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
if (agent !== undefined) identityEnv = { ADL_AGENT_ID_AGENT: agent }
|
|
199
|
+
|
|
200
|
+
info(`start location=${location} keep=${keep} dry=${dry ? 1 : 0}${maxAgeDays === undefined ? '' : ` branch_max_age_days=${maxAgeDays}`}`)
|
|
201
|
+
|
|
202
|
+
let entries: Dirent[]
|
|
203
|
+
let locationReal: string
|
|
204
|
+
try {
|
|
205
|
+
locationReal = realpathSync(location)
|
|
206
|
+
entries = readdirSync(locationReal, { withFileTypes: true })
|
|
207
|
+
} catch (error) {
|
|
208
|
+
for (const line of listFailureLines(location, error, options.executable)) errOut(`ERROR repo-maintain: ${line}`)
|
|
209
|
+
failures.push(location)
|
|
210
|
+
return { ok: false, repositories: 0, failures }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const seen = new Set<string>()
|
|
214
|
+
let repositories = 0
|
|
215
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
216
|
+
// Files and other non-directories (a symlink is the one kind resolved below).
|
|
217
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue
|
|
218
|
+
const candidate = join(locationReal, entry.name)
|
|
219
|
+
|
|
220
|
+
// 1. Resolve, then scope-check, BEFORE looking inside. A broken symlink
|
|
221
|
+
// points nowhere and so carries no marker; any other failure to
|
|
222
|
+
// resolve is reported, because it may hide an in-scope repository.
|
|
223
|
+
let real: string
|
|
224
|
+
try {
|
|
225
|
+
real = realpathSync(candidate)
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (errorCode(error) === 'ENOENT') continue
|
|
228
|
+
fail(`${candidate}: cannot resolve: ${error instanceof Error ? error.message : String(error)}`, candidate)
|
|
229
|
+
continue
|
|
230
|
+
}
|
|
231
|
+
if (dirname(real) !== locationReal) continue
|
|
232
|
+
if (seen.has(real)) continue
|
|
233
|
+
|
|
234
|
+
// 2. The marker decides scope. Not there: not this step's business, and
|
|
235
|
+
// silent. Anything other than "not there" is a named failure.
|
|
236
|
+
try {
|
|
237
|
+
if (!statSync(join(real, MARKER)).isFile()) continue
|
|
238
|
+
} catch (error) {
|
|
239
|
+
const code = errorCode(error)
|
|
240
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') continue
|
|
241
|
+
fail(`${real}: cannot read ${MARKER}: ${error instanceof Error ? error.message : String(error)}`, real)
|
|
242
|
+
continue
|
|
243
|
+
}
|
|
244
|
+
seen.add(real)
|
|
245
|
+
|
|
246
|
+
// 3. In scope from here on. enact-config.toml may sit beside the marker; it is never read.
|
|
247
|
+
const toplevel = gitToplevelState(real)
|
|
248
|
+
if (toplevel === 'failed') continue
|
|
249
|
+
if (toplevel === 'not-toplevel') {
|
|
250
|
+
warn(`${real}: ${MARKER} but not a git top-level; skipped`)
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
repositories += 1
|
|
254
|
+
maintainRepo(real)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
info(`done (${repositories} repositories, ${failures.length} failed)`)
|
|
258
|
+
return { ok: failures.length === 0, repositories, failures }
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 'not-toplevel' only on git's own "not a git repository" answer (exit 128) or
|
|
262
|
+
* a toplevel that is a different directory. Anything else (spawn error, other
|
|
263
|
+
* exit status, unresolvable toplevel) is reported here as a failure naming the
|
|
264
|
+
* directory, and the repo is not maintained: it may hide an in-scope repository.
|
|
265
|
+
*/
|
|
266
|
+
function gitToplevelState(directory: string): 'toplevel' | 'not-toplevel' | 'failed' {
|
|
267
|
+
const command = 'git rev-parse --show-toplevel'
|
|
268
|
+
const top = spawnSync('git', ['-C', directory, 'rev-parse', '--show-toplevel'], { encoding: 'utf8', env: childEnv() })
|
|
269
|
+
if (top.error !== undefined) {
|
|
270
|
+
fail(`${directory}: cannot run ${command}: ${top.error.message}`, directory)
|
|
271
|
+
return 'failed'
|
|
272
|
+
}
|
|
273
|
+
const stderr = (top.stderr ?? '').trim()
|
|
274
|
+
if (top.status === 128 && stderr.includes('not a git repository')) return 'not-toplevel'
|
|
275
|
+
if (top.status !== 0) {
|
|
276
|
+
const shown = stderr.split('\n').slice(0, 3).join(' | ')
|
|
277
|
+
fail(`${directory}: ${command} exited ${top.status ?? `by signal ${top.signal}`}: ${shown}`, directory)
|
|
278
|
+
// git's "Operation not permitted" is TCC refusing the files to this job's responsible process (adl).
|
|
279
|
+
if (stderr.includes('Operation not permitted')) errOut(`ERROR repo-maintain: ${tccDeniedLine(directory, options.executable)}`)
|
|
280
|
+
return 'failed'
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
return realpathSync(top.stdout.trim()) === directory ? 'toplevel' : 'not-toplevel'
|
|
284
|
+
} catch (error) {
|
|
285
|
+
fail(`${directory}: cannot resolve git toplevel ${top.stdout.trim()}: ${error instanceof Error ? error.message : String(error)}`, directory)
|
|
286
|
+
return 'failed'
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function baselineFor(repo: string): string {
|
|
291
|
+
for (const branch of ['integration', 'main', 'master']) {
|
|
292
|
+
if (git(repo, 'show-ref', '--verify', '--quiet', `refs/remotes/origin/${branch}`).ok) return branch
|
|
293
|
+
}
|
|
294
|
+
return ''
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function maintainRepo(repo: string): void {
|
|
298
|
+
const name = basename(repo)
|
|
299
|
+
|
|
300
|
+
// An Azure DevOps remote needs an agent identity; without one the fetch
|
|
301
|
+
// would only fail (or be refused) far from the cause.
|
|
302
|
+
if (agent === undefined) {
|
|
303
|
+
const url = git(repo, 'remote', 'get-url', 'origin')
|
|
304
|
+
if (url.ok && /^https:\/\/[^/]*dev\.azure\.com\//.test(url.stdout.trim())) {
|
|
305
|
+
fail(`${name}: skipping: Azure DevOps remote but no agent identity resolved (see the start-of-run warning)`, repo)
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const head = git(repo, 'symbolic-ref', '--short', '-q', 'HEAD')
|
|
311
|
+
const current = head.ok ? head.stdout.trim() : '(detached)'
|
|
312
|
+
|
|
313
|
+
// 1. fetch + prune. Report what git SAID, never a guess at what it meant.
|
|
314
|
+
const fetched = git(repo, 'fetch', 'origin', '--prune', '--quiet')
|
|
315
|
+
if (!fetched.ok) {
|
|
316
|
+
const reason = fetched.stderr.trim().split('\n').pop() || 'no stderr from git'
|
|
317
|
+
fail(`${name}: fetch failed, skipping repo: ${reason}`, repo)
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// 2. fast-forward integration and main.
|
|
322
|
+
for (const branch of SYNCED_BRANCHES) fastForward(repo, name, branch)
|
|
323
|
+
|
|
324
|
+
// 3. guarded prune.
|
|
325
|
+
prune(repo, name, current)
|
|
326
|
+
|
|
327
|
+
// 4. stale-branch prune: by the tip commit's age.
|
|
328
|
+
if (maxAgeDays !== undefined) pruneStale(repo, name, current, maxAgeDays)
|
|
329
|
+
|
|
330
|
+
// 5. the per-repo enrolment.
|
|
331
|
+
const argv = [...options.install.leadingArgs, 'install', '--repo', repo]
|
|
332
|
+
const shown = `adl install --repo ${repo}`
|
|
333
|
+
if (dry) {
|
|
334
|
+
info(`${name}: [dry] would run ${shown}`)
|
|
335
|
+
return
|
|
336
|
+
}
|
|
337
|
+
const result = spawnSync(options.install.command, argv, { encoding: 'utf8', env: childEnv() })
|
|
338
|
+
if (result.error !== undefined) {
|
|
339
|
+
fail(`${name}: could not run ${options.install.command}: ${result.error.message}`, repo)
|
|
340
|
+
} else if (result.status !== 0) {
|
|
341
|
+
fail(`${name}: ${shown} exited ${result.status}: ${(result.stderr ?? '').trim()}`, repo)
|
|
342
|
+
} else {
|
|
343
|
+
info(`${name}: ${shown}: ok`)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function fastForward(repo: string, name: string, branch: string): void {
|
|
348
|
+
const local = `refs/heads/${branch}`
|
|
349
|
+
const remote = `refs/remotes/origin/${branch}`
|
|
350
|
+
if (!git(repo, 'show-ref', '--verify', '--quiet', local).ok) return
|
|
351
|
+
if (!git(repo, 'show-ref', '--verify', '--quiet', remote).ok) return
|
|
352
|
+
const localSha = git(repo, 'rev-parse', local).stdout.trim()
|
|
353
|
+
const remoteSha = git(repo, 'rev-parse', remote).stdout.trim()
|
|
354
|
+
if (localSha === remoteSha) return
|
|
355
|
+
if (!git(repo, 'merge-base', '--is-ancestor', local, remote).ok) {
|
|
356
|
+
// Not a fast-forward (local has commits origin lacks). Never moved.
|
|
357
|
+
if (!git(repo, 'merge-base', '--is-ancestor', remote, local).ok) {
|
|
358
|
+
warn(`${name}: ${branch} has diverged from origin/${branch}; not moved`)
|
|
359
|
+
}
|
|
360
|
+
return
|
|
361
|
+
}
|
|
362
|
+
// Where is it checked out, if anywhere? (`%(worktreepath)` covers this
|
|
363
|
+
// worktree and every linked one.)
|
|
364
|
+
const worktree = git(repo, 'for-each-ref', local, '--format=%(worktreepath)').stdout.trim()
|
|
365
|
+
if (worktree !== '') {
|
|
366
|
+
if (git(worktree, 'status', '--porcelain').stdout.trim() !== '') {
|
|
367
|
+
warn(`${name}: ${branch} checked out but dirty; skipped fast-forward`)
|
|
368
|
+
} else if (dry) {
|
|
369
|
+
info(`${name}: [dry] would fast-forward ${branch} (checked out)`)
|
|
370
|
+
} else if (git(worktree, 'merge', '--ff-only', `origin/${branch}`, '--quiet').ok) {
|
|
371
|
+
info(`${name}: ${branch} fast-forwarded (checked out)`)
|
|
372
|
+
} else {
|
|
373
|
+
fail(`${name}: ${branch} could not be fast-forwarded (checked out)`, repo)
|
|
374
|
+
}
|
|
375
|
+
return
|
|
376
|
+
}
|
|
377
|
+
if (dry) {
|
|
378
|
+
info(`${name}: [dry] would fast-forward ${branch} (background)`)
|
|
379
|
+
} else if (git(repo, 'update-ref', '-m', `adl maintain: fast-forward ${branch}`, local, remoteSha, localSha).ok) {
|
|
380
|
+
info(`${name}: ${branch} ref fast-forwarded (background)`)
|
|
381
|
+
} else {
|
|
382
|
+
fail(`${name}: ${branch} ref could not be fast-forwarded (background)`, repo)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function pruneStale(repo: string, name: string, current: string, days: number): void {
|
|
387
|
+
const cutoff = now().getTime() / 1000 - days * 86_400
|
|
388
|
+
// %1f (unit separator) cannot appear in a ref name, unlike `|`.
|
|
389
|
+
const format = '%(refname:short)%1f%(objectname)%1f%(committerdate:unix)%1f%(worktreepath)'
|
|
390
|
+
for (const line of lines(git(repo, 'for-each-ref', 'refs/heads/', `--format=${format}`).stdout)) {
|
|
391
|
+
const [branch = '', sha = '', committed = '', worktree = ''] = line.split('\x1f')
|
|
392
|
+
if (branch === '' || PROTECTED.has(branch) || branch === current) continue
|
|
393
|
+
const tip = Number(committed)
|
|
394
|
+
if (!Number.isFinite(tip) || tip >= cutoff) continue
|
|
395
|
+
// A branch checked out in any worktree (the main one included) is in use.
|
|
396
|
+
if (worktree !== '') {
|
|
397
|
+
info(`${name}: kept stale branch ${branch} (checked out in ${worktree})`)
|
|
398
|
+
continue
|
|
399
|
+
}
|
|
400
|
+
const described = `stale branch ${branch} at ${sha} (last commit ${new Date(tip * 1000).toISOString().slice(0, 10)})`
|
|
401
|
+
if (dry) info(`${name}: [dry] would delete ${described}`)
|
|
402
|
+
else if (git(repo, 'branch', '-D', branch).ok) warn(`${name}: deleted ${described}`)
|
|
403
|
+
else warn(`${name}: could not delete ${described}`)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function prune(repo: string, name: string, current: string): void {
|
|
408
|
+
const base = baselineFor(repo)
|
|
409
|
+
const keptRecent = new Set(
|
|
410
|
+
lines(git(repo, 'for-each-ref', '--sort=-committerdate', 'refs/heads/', '--format=%(refname:short)').stdout)
|
|
411
|
+
.filter((branch) => !PROTECTED.has(branch) && branch !== current)
|
|
412
|
+
.slice(0, keep),
|
|
413
|
+
)
|
|
414
|
+
const merged =
|
|
415
|
+
base === ''
|
|
416
|
+
? new Set<string>()
|
|
417
|
+
: new Set(lines(git(repo, 'branch', '--merged', `origin/${base}`, '--format=%(refname:short)').stdout))
|
|
418
|
+
|
|
419
|
+
for (const line of lines(git(repo, 'for-each-ref', 'refs/heads/', '--format=%(refname:short)|%(upstream:track)').stdout)) {
|
|
420
|
+
const separator = line.indexOf('|')
|
|
421
|
+
const branch = line.slice(0, separator)
|
|
422
|
+
const track = line.slice(separator + 1)
|
|
423
|
+
if (PROTECTED.has(branch) || branch === current || keptRecent.has(branch)) continue
|
|
424
|
+
|
|
425
|
+
if (track.includes('[gone]')) {
|
|
426
|
+
// remote branch deleted after merge -> proven safe
|
|
427
|
+
if (dry) info(`${name}: [dry] would delete (upstream gone) ${branch}`)
|
|
428
|
+
else if (git(repo, 'branch', '-D', branch).ok) info(`${name}: deleted (upstream gone) ${branch}`)
|
|
429
|
+
else warn(`${name}: could not delete (upstream gone) ${branch}`)
|
|
430
|
+
} else if (base !== '' && merged.has(branch)) {
|
|
431
|
+
if (dry) info(`${name}: [dry] would delete (merged into ${base}) ${branch}`)
|
|
432
|
+
else if (git(repo, 'branch', '-d', branch).ok) info(`${name}: deleted (merged into ${base}) ${branch}`)
|
|
433
|
+
else info(`${name}: kept (${branch}: -d refused)`)
|
|
434
|
+
} else {
|
|
435
|
+
info(`${name}: kept (unconfirmed merged) ${branch}`)
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `git worktree prune` is not a substitute for any of this. It only clears
|
|
21
21
|
* metadata for worktrees whose directory has ALREADY been deleted.
|
|
22
22
|
*
|
|
23
|
-
* Invoked by reconcile's
|
|
23
|
+
* Invoked by reconcile's 12-hourly pass (bin/reconcile), gated on
|
|
24
24
|
* config.scratch_reap.enabled and the per-device scratch_repos list. Devices
|
|
25
25
|
* that declare no scratch_repos are never touched — a developer machine's
|
|
26
26
|
* scratch worktrees are working state, not fleet state.
|
|
@@ -34,12 +34,12 @@ import { defaultAgentFilePath, type FleetPlatform } from './default-agent-file.t
|
|
|
34
34
|
// apps/cli/src/commands/mcp.ts's own cross-package import of packages/azdo:
|
|
35
35
|
// this workspace has no node_modules/@adl/* symlinks without `bun install`.
|
|
36
36
|
// packages/azdo/src/lib/agent-identity.ts is now THE one place the
|
|
37
|
-
// default-agent file is READ from
|
|
38
|
-
//
|
|
39
|
-
//
|
|
37
|
+
// default-agent file is READ from; this package's own default-agent-file.ts
|
|
38
|
+
// keeps only the WRITE side (`reconcileDefaultAgentFile`) plus the path
|
|
39
|
+
// computation both sides need.
|
|
40
40
|
import { NO_AGENT_IDENTITY_MESSAGE, resolveAgentIdentity } from '../../azdo/src/lib/agent-identity.ts'
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
type Mode = 'plan' | 'apply'
|
|
43
43
|
|
|
44
44
|
export type GitResult = { ok: boolean; stdout: string; stderr: string }
|
|
45
45
|
export type Git = (args: string[], cwd: string) => GitResult
|
|
@@ -530,7 +530,7 @@ function usage(): never {
|
|
|
530
530
|
process.exit(2)
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
-
|
|
533
|
+
function main(argv: string[]): number {
|
|
534
534
|
const repos: string[] = []
|
|
535
535
|
let mode: Mode = 'plan'
|
|
536
536
|
let idleDays = 1
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* format and default/named-profile directory shape, under its own
|
|
22
22
|
* `Application Support`/`.config` folder name (`Cursor` instead of `Code`) —
|
|
23
23
|
* this is the standard, well-known Cursor Application Support location
|
|
24
|
-
* (docs/
|
|
24
|
+
* (docs/research/research-2026-09-27-worktree-controls-per-surface.md confirms
|
|
25
25
|
* `~/.cursor/worktrees/<repo>/` for worktree storage and that
|
|
26
26
|
* `cursor.worktreeMaxCount` lives in "the regular Cursor settings.json (user
|
|
27
27
|
* or workspace scope, VS Code-style)", but does not separately spell out the
|
|
@@ -51,14 +51,14 @@ import { basename, dirname, isAbsolute, join } from 'node:path'
|
|
|
51
51
|
export type VscodePlatform = 'mac' | 'linux'
|
|
52
52
|
|
|
53
53
|
/** A managed setting's value: VS Code's two owned keys are boolean; Cursor's `worktreeMaxCount` is numeric. */
|
|
54
|
-
|
|
54
|
+
type SettingValue = boolean | number
|
|
55
55
|
|
|
56
56
|
export const REQUIRED_VSCODE_SETTINGS = {
|
|
57
57
|
'git.detectWorktrees': true,
|
|
58
58
|
'scm.alwaysShowRepositories': true,
|
|
59
59
|
} as const
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
const APPLY_TO_ALL_PROFILES_SETTING = 'workbench.settings.applyToAllProfiles'
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* `cursor.worktreeMaxCount` — docs/operations/worktree-guard.md L0. Deployed as a
|
|
@@ -75,7 +75,7 @@ export const REQUIRED_CURSOR_SETTINGS = {
|
|
|
75
75
|
'cursor.worktreeMaxCount': 3,
|
|
76
76
|
} as const
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
interface EditorSettingsSpec {
|
|
79
79
|
readonly requiredSettings: Readonly<Record<string, SettingValue>>
|
|
80
80
|
/** VS Code only: the setting id that collects a profile-wide apply-to-all list. Omit for editors with no confirmed equivalent (Cursor). */
|
|
81
81
|
readonly applyToAllProfilesSetting?: string
|
|
@@ -90,7 +90,7 @@ const CURSOR_SPEC: EditorSettingsSpec = {
|
|
|
90
90
|
requiredSettings: REQUIRED_CURSOR_SETTINGS,
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
interface VscodeSettingsFileResult {
|
|
94
94
|
readonly path: string
|
|
95
95
|
readonly changed: boolean
|
|
96
96
|
readonly missing: boolean
|