@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.12 → 0.1.28
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 +16 -17
- package/bin/git-ado +25 -24
- 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/fleet-host.sh +8 -5
- package/bin/reconcile +482 -232
- package/bin/reconcile-host +30 -20
- package/bin/setup +20 -17
- package/bin/shell-path-policy +15 -5
- package/bin/sync-and-reconcile +62 -64
- package/bin/validate-manifest +33 -9
- package/package.json +2 -10
- package/packages/agent-hooks/README.md +14 -9
- package/packages/agent-hooks/assets/hooks.json +0 -20
- 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 +12 -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 +131 -0
- 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 +33 -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 +14 -16
- 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 +0 -2
- 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/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 +1 -3
- package/packages/azdo/scripts/probe-ado-rules.ts +311 -0
- 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 +44 -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-ladder.csv +241 -0
- package/packages/azdo/src/workflow-rules-matrix.csv +85 -0
- package/packages/azdo/src/workflow-rules.ts +194 -0
- package/packages/fleet/manifest.json +35 -11
- package/packages/fleet/manifest.schema.json +62 -10
- 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 +13 -13
- package/packages/fleet/src/managed-settings.ts +7 -7
- package/packages/fleet/src/repo-maintain.ts +91 -55
- package/packages/fleet/src/scratch-reap.ts +5 -5
- 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 +258 -2
- 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/plan_metadata.py +9 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/check.py +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/items/az351-restore-entrypoint.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/test_check.py +3 -3
- 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 -9
- 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
|
@@ -15,9 +15,10 @@ actually RUNNING `test.sh` (with a `compliance` symlink pointed at
|
|
|
15
15
|
internally) confirms it cannot execute even once: `env: init: No such file or
|
|
16
16
|
directory`, from `fixture_() { env ... "$@"; }` being called as `fixture_
|
|
17
17
|
init -q`, i.e. `env -C "$FIXTURE" init -q` -- the missing word is the command
|
|
18
|
-
name itself. This is a harder case than `checks/coverage/config_committed`'s
|
|
19
|
-
that gate's shell tool could
|
|
20
|
-
|
|
18
|
+
name itself. This is a harder case than `checks/coverage/config_committed`'s
|
|
19
|
+
(deleted 2026-10-01 with the `coverage` domain): that gate's shell tool could
|
|
20
|
+
still run and its broken behaviour was directly observed and ported; here
|
|
21
|
+
nothing runs even once, so there is no executed
|
|
21
22
|
behaviour to carry forward, only the intent the surrounding text still states
|
|
22
23
|
unambiguously.
|
|
23
24
|
|
|
@@ -11,8 +11,8 @@ for why the fixture is named `.gitignore` (not `.ignore`, which is what
|
|
|
11
11
|
REP-005 is not yet listed in `checks/__init__.py` (a later step wires this
|
|
12
12
|
whole wave in one shot), so dispatch-by-id would report `COULD_NOT_RUN` for
|
|
13
13
|
every case regardless of this check's own behaviour -- the same reason
|
|
14
|
-
`coverage/config_committed`
|
|
15
|
-
for the identical choice.
|
|
14
|
+
`coverage/config_committed` (deleted 2026-10-01 with the `coverage` domain) and
|
|
15
|
+
`layout/plans_layout`'s own test suites give for the identical choice.
|
|
16
16
|
|
|
17
17
|
Every scenario below hands `.gitignore` itself in the given set alongside the
|
|
18
18
|
candidate paths, because this check reads `.gitignore` content from the GIVEN
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
"""What a `Cargo.lock` must contain for the manifests beside it, judged without cargo.
|
|
2
|
+
|
|
3
|
+
REP-002's `LCK-002` used to be cargo's exit code alone: `cargo metadata --locked
|
|
4
|
+
--offline --no-deps`. With `--no-deps` cargo never resolves, so it never reads the
|
|
5
|
+
lockfile, and a lock that was `version = 4` alone, was not TOML, or said
|
|
6
|
+
`version = 99` passed (measured through the real check, 2026-10-01). This module
|
|
7
|
+
is the content half: deterministic Python (`tomllib`) that reads only the lock,
|
|
8
|
+
the workspace manifest, the member manifests and the path-dependency manifests the
|
|
9
|
+
manifest text names. It spawns nothing and never touches the registry cache or the
|
|
10
|
+
network, so a verdict cannot differ between a machine that has the crate sources
|
|
11
|
+
cached and one that does not. (Dropping `--no-deps` would make it differ: cargo
|
|
12
|
+
then loads every dependency's manifest, which fails offline when the sources are
|
|
13
|
+
not cached.)
|
|
14
|
+
|
|
15
|
+
What is judged, each finding naming the lock path and the exact thing missing:
|
|
16
|
+
|
|
17
|
+
* the lock is UTF-8 and TOML;
|
|
18
|
+
* its top-level `version` is 3 or 4, or absent (the legacy V1/V2 encodings).
|
|
19
|
+
cargo 0.99.0, `src/cargo/core/resolver/encode.rs`, `EncodableResolve::into_resolve`:
|
|
20
|
+
`Some(4)` and `Some(3)` are read, `Some(5)` only on nightly with
|
|
21
|
+
`-Znext-lockfile-bump`, any other number bails ("lock file version `n` was found,
|
|
22
|
+
but this version of Cargo does not understand this lock file"), and no `version`
|
|
23
|
+
key means V1, "so this could either be the V1 or V2 encoding". An explicit
|
|
24
|
+
`version = 1` or `2` is therefore NOT supported, whatever the encodings' names;
|
|
25
|
+
* `[[package]]` is a non-empty array of tables, each with a string `name` and
|
|
26
|
+
`version`;
|
|
27
|
+
* the lock is closed over itself: every `name`, `name version` or `name version
|
|
28
|
+
(source)` a package lists in `dependencies` is a package the lock holds (name, and
|
|
29
|
+
version when the entry gives one). A package dropped from the lock while its
|
|
30
|
+
dependents still list it is a lock cargo refuses to parse, and no manifest shows it
|
|
31
|
+
when the package is not a direct dependency. This is judged from the lock alone,
|
|
32
|
+
before any manifest is read, as are the first three points;
|
|
33
|
+
* every workspace member appears in the lock as a path package (no `source`) with
|
|
34
|
+
its name AND version. Members are the root `[package]`, every `[workspace].members`
|
|
35
|
+
entry (expanded as cargo expands it: the `glob` crate's patterns, hidden
|
|
36
|
+
directories included) minus `[workspace].exclude`, and every path dependency that
|
|
37
|
+
lives inside the workspace root (cargo: "All path dependencies residing in the
|
|
38
|
+
workspace directory automatically become members") unless excluded or a workspace
|
|
39
|
+
root of its own;
|
|
40
|
+
* every direct dependency of every member appears under its REAL package name, and in
|
|
41
|
+
the member's OWN lock entry's `dependencies` (a name the lock holds for some other
|
|
42
|
+
package does not satisfy it: `tokio` is in nearly every lock already, and a manifest
|
|
43
|
+
that gained it would otherwise pass while `cargo build --locked` refuses the lock):
|
|
44
|
+
`[dependencies]`, `[dev-dependencies]`, `[build-dependencies]` (and the underscore
|
|
45
|
+
spellings cargo accepts) and each `[target.<cfg>.<those>]`, optional ones included
|
|
46
|
+
because cargo locks every feature. A package that dev-depends on itself (to enable
|
|
47
|
+
features) is not asked to list itself. `package = "x"` is the real name when present,
|
|
48
|
+
else the key; `workspace = true` reads the `[workspace.dependencies]` entry (whose
|
|
49
|
+
`path` is relative to the workspace root); a path dependency's real name is the
|
|
50
|
+
`[package].name` of the manifest it points at, and it must be a path package.
|
|
51
|
+
|
|
52
|
+
What cannot be judged from the manifest text raises `Undecidable` naming it, and the
|
|
53
|
+
gate turns that into could-not-run. It is never skipped: a manifest this module
|
|
54
|
+
cannot parse, a root with neither `[package]` nor `[workspace]`, a `members` entry
|
|
55
|
+
matching no directory or a directory with no `Cargo.toml`, a member with no
|
|
56
|
+
`[package].name`, an `exclude` entry using glob syntax (cargo's treatment of it is not
|
|
57
|
+
verified, and a wrong guess invents a finding or hides a member), a dependency
|
|
58
|
+
specification that is neither a string nor a table, `workspace = true` with no
|
|
59
|
+
`[workspace.dependencies]` entry, and a path dependency whose manifest cannot be read.
|
|
60
|
+
|
|
61
|
+
**The limits, stated plainly.**
|
|
62
|
+
|
|
63
|
+
* Version-REQUIREMENT drift is not detected: a manifest that raises
|
|
64
|
+
`serde = "1.0.200"` past a locked `1.0.150` still passes here. That is
|
|
65
|
+
`cargo metadata` without `--no-deps`, which needs the registry, and it stays the
|
|
66
|
+
authority for it. This module covers presence, structure and membership.
|
|
67
|
+
* A path dependency that is NOT a workspace member (any path dependency of a manifest
|
|
68
|
+
with no `[workspace]`, or one outside the workspace root) is judged by its own
|
|
69
|
+
presence in the lock only; its own dependencies are not read, so a dependency added
|
|
70
|
+
to such a crate (enact-combined's `plugins/enact-keychain` is one) is not detected.
|
|
71
|
+
Reading them would need that crate's own workspace for its `workspace = true`
|
|
72
|
+
entries and cargo's feature rules for its optional ones (an optional dependency of a
|
|
73
|
+
non-member is locked only when a feature activates it), and a wrong guess is a false
|
|
74
|
+
positive on a good lock.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
from __future__ import annotations
|
|
78
|
+
|
|
79
|
+
import glob
|
|
80
|
+
import os
|
|
81
|
+
import tomllib
|
|
82
|
+
from collections.abc import Callable
|
|
83
|
+
from pathlib import Path
|
|
84
|
+
from typing import ClassVar, NamedTuple
|
|
85
|
+
|
|
86
|
+
Table = dict[str, object]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Undecidable(Exception):
|
|
90
|
+
"""The lock cannot be judged from the manifests' text. Could-not-run, never a pass."""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Package(NamedTuple):
|
|
94
|
+
"""One `[[package]]` of the lock."""
|
|
95
|
+
|
|
96
|
+
name: str
|
|
97
|
+
version: str
|
|
98
|
+
path: bool
|
|
99
|
+
"""No `source`: a path package (a workspace member or a path dependency)."""
|
|
100
|
+
needs: tuple[str, ...]
|
|
101
|
+
"""Its `dependencies = [...]`, verbatim: `name`, `name version` or `name version (source)`."""
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class Dependency(NamedTuple):
|
|
105
|
+
"""A direct dependency, resolved to the package the lock must list."""
|
|
106
|
+
|
|
107
|
+
key: str
|
|
108
|
+
"""The name it is declared under (differs from `name` when renamed)."""
|
|
109
|
+
name: str
|
|
110
|
+
"""The REAL package name."""
|
|
111
|
+
section: str
|
|
112
|
+
"""`[dependencies]`, `[target.'cfg(unix)'.dev-dependencies]`, ... as written."""
|
|
113
|
+
target: Path | None
|
|
114
|
+
"""The manifest a path dependency points at; `None` for a registry or git one."""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Member(NamedTuple):
|
|
118
|
+
manifest: Path
|
|
119
|
+
name: str
|
|
120
|
+
version: str
|
|
121
|
+
dependencies: tuple[Dependency, ...]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class CargoLock:
|
|
125
|
+
"""One lockfile against the manifest of the workspace it belongs to."""
|
|
126
|
+
|
|
127
|
+
VERSIONS: ClassVar[tuple[int, ...]] = (3, 4)
|
|
128
|
+
SECTIONS: ClassVar[tuple[str, ...]] = (
|
|
129
|
+
"dependencies",
|
|
130
|
+
"dev-dependencies",
|
|
131
|
+
"dev_dependencies",
|
|
132
|
+
"build-dependencies",
|
|
133
|
+
"build_dependencies",
|
|
134
|
+
)
|
|
135
|
+
GLOB_SYNTAX: ClassVar[str] = "*?["
|
|
136
|
+
|
|
137
|
+
def __init__(self, lock: Path, manifest: Path, relative: Callable[[Path], str]) -> None:
|
|
138
|
+
self.lock = lock
|
|
139
|
+
self.manifest = manifest
|
|
140
|
+
self.shown = relative(lock)
|
|
141
|
+
self.relative = relative
|
|
142
|
+
self.root = manifest.parent
|
|
143
|
+
self.root_document: Table = {}
|
|
144
|
+
|
|
145
|
+
# ------------------------------------------------------------------ the verdict
|
|
146
|
+
|
|
147
|
+
def problems(self) -> list[str]:
|
|
148
|
+
"""Every way the lock fails its manifests, in a stable order; empty when it holds."""
|
|
149
|
+
try:
|
|
150
|
+
text = self.lock.read_text(encoding="utf-8")
|
|
151
|
+
except UnicodeDecodeError:
|
|
152
|
+
return [f"{self.shown} is not valid UTF-8 text"]
|
|
153
|
+
try:
|
|
154
|
+
document: Table = tomllib.loads(text)
|
|
155
|
+
except tomllib.TOMLDecodeError as exc:
|
|
156
|
+
return [f"{self.shown} is not valid TOML: {exc}"]
|
|
157
|
+
|
|
158
|
+
packages, found = self._packages(document)
|
|
159
|
+
found = [*self._version(document), *found]
|
|
160
|
+
if found:
|
|
161
|
+
# The structure is wrong, so there is nothing sound to compare the manifests with.
|
|
162
|
+
return found
|
|
163
|
+
# What the lock says about itself is conclusive without any manifest.
|
|
164
|
+
return self._closure(packages) or self._membership(packages)
|
|
165
|
+
|
|
166
|
+
# ------------------------------------------------------------------ the lock itself
|
|
167
|
+
|
|
168
|
+
def _version(self, document: Table) -> list[str]:
|
|
169
|
+
if "version" not in document:
|
|
170
|
+
return [] # the legacy V1/V2 encodings carry no version key; cargo reads them
|
|
171
|
+
value = document["version"]
|
|
172
|
+
if isinstance(value, bool) or not isinstance(value, int) or value not in self.VERSIONS:
|
|
173
|
+
return [
|
|
174
|
+
f"{self.shown} version {value!r} is not supported "
|
|
175
|
+
"(cargo reads lock versions 3 and 4)"
|
|
176
|
+
]
|
|
177
|
+
return []
|
|
178
|
+
|
|
179
|
+
def _packages(self, document: Table) -> tuple[list[Package], list[str]]:
|
|
180
|
+
raw = document.get("package")
|
|
181
|
+
if raw is None or raw == []:
|
|
182
|
+
return [], [f"{self.shown} lists no packages ([[package]] is absent or empty)"]
|
|
183
|
+
if not isinstance(raw, list):
|
|
184
|
+
return [], [
|
|
185
|
+
f"{self.shown} has a `package` key that is not an array of [[package]] tables"
|
|
186
|
+
]
|
|
187
|
+
packages: list[Package] = []
|
|
188
|
+
found: list[str] = []
|
|
189
|
+
for number, entry in enumerate(raw, start=1):
|
|
190
|
+
where = f"{self.shown} has a [[package]] entry (number {number})"
|
|
191
|
+
if not isinstance(entry, dict):
|
|
192
|
+
found.append(f"{where} that is not a table")
|
|
193
|
+
continue
|
|
194
|
+
name, version = entry.get("name"), entry.get("version")
|
|
195
|
+
needs = entry.get("dependencies", [])
|
|
196
|
+
if not isinstance(name, str):
|
|
197
|
+
found.append(f"{where} without a string name")
|
|
198
|
+
elif not isinstance(version, str):
|
|
199
|
+
found.append(f"{where} without a string version")
|
|
200
|
+
elif not isinstance(needs, list) or not all(isinstance(n, str) for n in needs):
|
|
201
|
+
found.append(f"{where} whose `dependencies` is not a list of strings")
|
|
202
|
+
else:
|
|
203
|
+
packages.append(Package(name, version, "source" not in entry, tuple(needs)))
|
|
204
|
+
return packages, found
|
|
205
|
+
|
|
206
|
+
def _closure(self, packages: list[Package]) -> list[str]:
|
|
207
|
+
"""A package some other package lists as a dependency must itself be in the lock.
|
|
208
|
+
|
|
209
|
+
Internal to the lock, so it needs no manifest. A lock with a package removed (a
|
|
210
|
+
merge that dropped a block, a hand edit) is one cargo refuses to parse, and the
|
|
211
|
+
manifests never show it when the missing package is not a direct dependency.
|
|
212
|
+
"""
|
|
213
|
+
held = {(p.name, p.version) for p in packages}
|
|
214
|
+
names = {p.name for p in packages}
|
|
215
|
+
found: list[str] = []
|
|
216
|
+
listed: set[str] = set()
|
|
217
|
+
for package in packages:
|
|
218
|
+
for need in package.needs:
|
|
219
|
+
name, _, rest = need.partition(" ")
|
|
220
|
+
version = rest.partition(" ")[0]
|
|
221
|
+
if need in listed or ((name, version) in held if version else name in names):
|
|
222
|
+
continue
|
|
223
|
+
listed.add(need)
|
|
224
|
+
found.append(
|
|
225
|
+
f"{self.shown} lists dependency '{need}' of package "
|
|
226
|
+
f"'{package.name}' {package.version} but has no such package"
|
|
227
|
+
)
|
|
228
|
+
return found
|
|
229
|
+
|
|
230
|
+
# ------------------------------------------------------------- lock against manifests
|
|
231
|
+
|
|
232
|
+
def _membership(self, packages: list[Package]) -> list[str]:
|
|
233
|
+
members = self._members()
|
|
234
|
+
found: list[str] = []
|
|
235
|
+
missing_members: set[str] = set()
|
|
236
|
+
entries: dict[Path, Package] = {} # a member's manifest -> the lock entry that is it
|
|
237
|
+
|
|
238
|
+
for member in members:
|
|
239
|
+
locked = [p for p in packages if p.name == member.name and p.path]
|
|
240
|
+
at = f"workspace member {self.relative(member.manifest)}"
|
|
241
|
+
if not locked:
|
|
242
|
+
missing_members.add(member.name)
|
|
243
|
+
found.append(f"{self.shown} lists no path package '{member.name}' ({at})")
|
|
244
|
+
elif all(p.version != member.version for p in locked):
|
|
245
|
+
versions = ", ".join(sorted({p.version for p in locked}))
|
|
246
|
+
found.append(
|
|
247
|
+
f"{self.shown} lists '{member.name}' at version {versions} but {at} "
|
|
248
|
+
f"declares {member.version}"
|
|
249
|
+
)
|
|
250
|
+
else:
|
|
251
|
+
entries[member.manifest] = next(p for p in locked if p.version == member.version)
|
|
252
|
+
|
|
253
|
+
# A package nothing in the lock holds is one finding however many members need it. One
|
|
254
|
+
# the lock holds, but that this member's own entry does not list, is a finding per member:
|
|
255
|
+
# `tokio` is in nearly every lock already, so presence anywhere would pass the commonest
|
|
256
|
+
# stale lock (a manifest that gained a dependency the lock has for someone else).
|
|
257
|
+
absent: set[tuple[str, bool]] = set()
|
|
258
|
+
unlisted: set[tuple[Path, str]] = set()
|
|
259
|
+
for member in members:
|
|
260
|
+
entry = entries.get(member.manifest)
|
|
261
|
+
listed = {need.partition(" ")[0] for need in entry.needs} if entry else None
|
|
262
|
+
for dependency in member.dependencies:
|
|
263
|
+
name, path = dependency.name, dependency.target is not None
|
|
264
|
+
if name == member.name or (path and name in missing_members):
|
|
265
|
+
# A package may dev-depend on itself to enable features; a missing member
|
|
266
|
+
# was already reported as the member it is.
|
|
267
|
+
continue
|
|
268
|
+
if not any(p.name == name and (p.path or not path) for p in packages):
|
|
269
|
+
if (name, path) not in absent:
|
|
270
|
+
absent.add((name, path))
|
|
271
|
+
found.append(
|
|
272
|
+
f"{self.shown} lists no {'path ' if path else ''}package '{name}' "
|
|
273
|
+
f"(dependency '{dependency.key}' in {dependency.section} of "
|
|
274
|
+
f"{self.relative(member.manifest)})"
|
|
275
|
+
)
|
|
276
|
+
elif (
|
|
277
|
+
listed is not None
|
|
278
|
+
and name not in listed
|
|
279
|
+
and (member.manifest, name) not in unlisted
|
|
280
|
+
):
|
|
281
|
+
unlisted.add((member.manifest, name))
|
|
282
|
+
found.append(
|
|
283
|
+
f"{self.shown} package '{member.name}' {member.version} does not list "
|
|
284
|
+
f"'{name}' as a dependency ({dependency.section} of "
|
|
285
|
+
f"{self.relative(member.manifest)} declares dependency '{dependency.key}')"
|
|
286
|
+
)
|
|
287
|
+
return found
|
|
288
|
+
|
|
289
|
+
# ------------------------------------------------------------------------- members
|
|
290
|
+
|
|
291
|
+
def _members(self) -> list[Member]:
|
|
292
|
+
"""The workspace's members, root first, then declared, then implicit path members."""
|
|
293
|
+
self.root_document = self._load(self.manifest)
|
|
294
|
+
workspace = self.root_document.get("workspace")
|
|
295
|
+
if workspace is not None and not isinstance(workspace, dict):
|
|
296
|
+
raise Undecidable(
|
|
297
|
+
f"{self.relative(self.manifest)} has a [workspace] that is not a table"
|
|
298
|
+
)
|
|
299
|
+
if "package" not in self.root_document and workspace is None:
|
|
300
|
+
raise Undecidable(
|
|
301
|
+
f"{self.relative(self.manifest)} has neither a [package] nor a [workspace] table"
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
queue: list[Path] = []
|
|
305
|
+
if "package" in self.root_document:
|
|
306
|
+
queue.append(self.manifest)
|
|
307
|
+
if workspace is not None:
|
|
308
|
+
queue.extend(self._declared(workspace))
|
|
309
|
+
|
|
310
|
+
members: dict[Path, Member] = {}
|
|
311
|
+
while queue:
|
|
312
|
+
manifest = queue.pop(0)
|
|
313
|
+
if manifest in members:
|
|
314
|
+
continue
|
|
315
|
+
document = self.root_document if manifest == self.manifest else self._load(manifest)
|
|
316
|
+
member = self._member(manifest, document)
|
|
317
|
+
members[manifest] = member
|
|
318
|
+
if workspace is None:
|
|
319
|
+
continue
|
|
320
|
+
for dependency in member.dependencies:
|
|
321
|
+
if dependency.target is not None and self._implicit(dependency.target, workspace):
|
|
322
|
+
queue.append(dependency.target)
|
|
323
|
+
return list(members.values())
|
|
324
|
+
|
|
325
|
+
def _declared(self, workspace: Table) -> list[Path]:
|
|
326
|
+
"""`members` globs expanded on disk, minus `exclude`, as manifests."""
|
|
327
|
+
shown = self.relative(self.manifest)
|
|
328
|
+
members = self._strings(workspace, "members", shown)
|
|
329
|
+
for entry in self._strings(workspace, "exclude", shown):
|
|
330
|
+
if any(char in entry for char in self.GLOB_SYNTAX):
|
|
331
|
+
raise Undecidable(
|
|
332
|
+
f"{shown} [workspace].exclude entry {entry!r} uses glob syntax, which this "
|
|
333
|
+
"check does not interpret"
|
|
334
|
+
)
|
|
335
|
+
manifests: list[Path] = []
|
|
336
|
+
for entry in members:
|
|
337
|
+
matched = sorted(
|
|
338
|
+
glob.glob(entry, root_dir=self.root, recursive=True, include_hidden=True)
|
|
339
|
+
)
|
|
340
|
+
directories = [self._normal(self.root / m) for m in matched if (self.root / m).is_dir()]
|
|
341
|
+
if not directories:
|
|
342
|
+
raise Undecidable(
|
|
343
|
+
f"{shown} [workspace].members entry {entry!r} matches no directory"
|
|
344
|
+
)
|
|
345
|
+
for directory in directories:
|
|
346
|
+
if self._excluded(directory, workspace):
|
|
347
|
+
continue
|
|
348
|
+
manifest = directory / "Cargo.toml"
|
|
349
|
+
if not manifest.is_file():
|
|
350
|
+
raise Undecidable(
|
|
351
|
+
f"workspace member directory {self.relative(directory)} (members entry "
|
|
352
|
+
f"{entry!r} of {shown}) has no Cargo.toml"
|
|
353
|
+
)
|
|
354
|
+
manifests.append(manifest)
|
|
355
|
+
return manifests
|
|
356
|
+
|
|
357
|
+
def _implicit(self, manifest: Path, workspace: Table) -> bool:
|
|
358
|
+
"""A path dependency inside the workspace root is a member, unless it opts out."""
|
|
359
|
+
directory = manifest.parent
|
|
360
|
+
if not directory.is_relative_to(self.root) or self._excluded(directory, workspace):
|
|
361
|
+
return False
|
|
362
|
+
return "workspace" not in self._load(manifest)
|
|
363
|
+
|
|
364
|
+
def _excluded(self, directory: Path, workspace: Table) -> bool:
|
|
365
|
+
exclude = self._strings(workspace, "exclude", self.relative(self.manifest))
|
|
366
|
+
return any(directory.is_relative_to(self._normal(self.root / entry)) for entry in exclude)
|
|
367
|
+
|
|
368
|
+
def _member(self, manifest: Path, document: Table) -> Member:
|
|
369
|
+
shown = self.relative(manifest)
|
|
370
|
+
package = document.get("package")
|
|
371
|
+
if not isinstance(package, dict):
|
|
372
|
+
raise Undecidable(f"{shown} is a workspace member but has no [package] table")
|
|
373
|
+
name = package.get("name")
|
|
374
|
+
if not isinstance(name, str):
|
|
375
|
+
raise Undecidable(f"{shown} [package].name is not a string")
|
|
376
|
+
return Member(
|
|
377
|
+
manifest,
|
|
378
|
+
name,
|
|
379
|
+
self._version_of(shown, package),
|
|
380
|
+
self._dependencies(manifest, document),
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def _version_of(self, shown: str, package: Table) -> str:
|
|
384
|
+
raw = package.get("version", "0.0.0") # cargo's default for a package with no version
|
|
385
|
+
if isinstance(raw, str):
|
|
386
|
+
return raw
|
|
387
|
+
if isinstance(raw, dict) and raw.get("workspace") is True:
|
|
388
|
+
workspace = self.root_document.get("workspace")
|
|
389
|
+
inherited = workspace.get("package") if isinstance(workspace, dict) else None
|
|
390
|
+
version = inherited.get("version") if isinstance(inherited, dict) else None
|
|
391
|
+
if isinstance(version, str):
|
|
392
|
+
return version
|
|
393
|
+
raise Undecidable(
|
|
394
|
+
f"{shown} [package].version is `version.workspace = true` but "
|
|
395
|
+
f"[workspace.package].version of {self.relative(self.manifest)} is not a string"
|
|
396
|
+
)
|
|
397
|
+
raise Undecidable(
|
|
398
|
+
f"{shown} [package].version is neither a string nor `version.workspace = true`"
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
# ---------------------------------------------------------------------- dependencies
|
|
402
|
+
|
|
403
|
+
def _dependencies(self, manifest: Path, document: Table) -> tuple[Dependency, ...]:
|
|
404
|
+
shown = self.relative(manifest)
|
|
405
|
+
sections: list[tuple[str, object]] = [
|
|
406
|
+
(f"[{key}]", document[key]) for key in self.SECTIONS if key in document
|
|
407
|
+
]
|
|
408
|
+
target = document.get("target")
|
|
409
|
+
if target is not None:
|
|
410
|
+
if not isinstance(target, dict):
|
|
411
|
+
raise Undecidable(f"{shown} [target] is not a table")
|
|
412
|
+
for cfg, body in target.items():
|
|
413
|
+
if not isinstance(body, dict):
|
|
414
|
+
raise Undecidable(f"{shown} [target.{cfg!r}] is not a table")
|
|
415
|
+
label = cfg if all(c.isalnum() or c in "_-" for c in cfg) else f"'{cfg}'"
|
|
416
|
+
sections.extend(
|
|
417
|
+
(f"[target.{label}.{key}]", body[key]) for key in self.SECTIONS if key in body
|
|
418
|
+
)
|
|
419
|
+
found: list[Dependency] = []
|
|
420
|
+
for section, table in sections:
|
|
421
|
+
if not isinstance(table, dict):
|
|
422
|
+
raise Undecidable(f"{shown} {section} is not a table")
|
|
423
|
+
for key, spec in table.items():
|
|
424
|
+
found.append(self._dependency(manifest, section, key, spec))
|
|
425
|
+
return tuple(found)
|
|
426
|
+
|
|
427
|
+
def _dependency(self, manifest: Path, section: str, key: str, spec: object) -> Dependency:
|
|
428
|
+
where = f"dependency {key!r} in {section} of {self.relative(manifest)}"
|
|
429
|
+
base = manifest.parent
|
|
430
|
+
if isinstance(spec, dict) and "workspace" in spec:
|
|
431
|
+
if spec["workspace"] is not True:
|
|
432
|
+
given = str(spec["workspace"]).lower()
|
|
433
|
+
raise Undecidable(f"{where} has `workspace = {given}`, not `workspace = true`")
|
|
434
|
+
workspace = self.root_document.get("workspace")
|
|
435
|
+
entries = workspace.get("dependencies") if isinstance(workspace, dict) else None
|
|
436
|
+
if not isinstance(entries, dict) or key not in entries:
|
|
437
|
+
raise Undecidable(
|
|
438
|
+
f"{where} is `workspace = true` but [workspace.dependencies] of "
|
|
439
|
+
f"{self.relative(self.manifest)} has no entry {key!r}"
|
|
440
|
+
)
|
|
441
|
+
spec, base = entries[key], self.root # a workspace path is relative to the root
|
|
442
|
+
if isinstance(spec, str):
|
|
443
|
+
return Dependency(key, key, section, None)
|
|
444
|
+
if not isinstance(spec, dict):
|
|
445
|
+
raise Undecidable(f"{where} is neither a version string nor a table")
|
|
446
|
+
|
|
447
|
+
package, path = spec.get("package"), spec.get("path")
|
|
448
|
+
if package is not None and not isinstance(package, str):
|
|
449
|
+
raise Undecidable(f"{where} has a `package` that is not a string")
|
|
450
|
+
if path is not None and not isinstance(path, str):
|
|
451
|
+
raise Undecidable(f"{where} has a `path` that is not a string")
|
|
452
|
+
if path is None:
|
|
453
|
+
return Dependency(key, package or key, section, None)
|
|
454
|
+
target = self._normal(base / path / "Cargo.toml")
|
|
455
|
+
name = package or self._name_at(target, where)
|
|
456
|
+
return Dependency(key, name, section, target)
|
|
457
|
+
|
|
458
|
+
def _name_at(self, manifest: Path, where: str) -> str:
|
|
459
|
+
"""A path dependency's real name: the `[package].name` of the manifest it points at."""
|
|
460
|
+
if not manifest.is_file():
|
|
461
|
+
raise Undecidable(f"{where} points at {self.relative(manifest)}, which is not a file")
|
|
462
|
+
package = self._load(manifest).get("package")
|
|
463
|
+
name = package.get("name") if isinstance(package, dict) else None
|
|
464
|
+
if not isinstance(name, str):
|
|
465
|
+
raise Undecidable(
|
|
466
|
+
f"{where} points at {self.relative(manifest)}, which has no [package].name"
|
|
467
|
+
)
|
|
468
|
+
return name
|
|
469
|
+
|
|
470
|
+
# ------------------------------------------------------------------------- plumbing
|
|
471
|
+
|
|
472
|
+
def _load(self, manifest: Path) -> Table:
|
|
473
|
+
shown = self.relative(manifest)
|
|
474
|
+
try:
|
|
475
|
+
return tomllib.loads(manifest.read_text(encoding="utf-8"))
|
|
476
|
+
except tomllib.TOMLDecodeError as exc:
|
|
477
|
+
raise Undecidable(f"{shown} is not valid TOML for this check: {exc}") from exc
|
|
478
|
+
except (OSError, UnicodeDecodeError) as exc:
|
|
479
|
+
raise Undecidable(f"{shown} could not be read: {exc}") from exc
|
|
480
|
+
|
|
481
|
+
@staticmethod
|
|
482
|
+
def _strings(table: Table, key: str, shown: str) -> list[str]:
|
|
483
|
+
value = table.get(key, [])
|
|
484
|
+
if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
|
|
485
|
+
raise Undecidable(f"{shown} [workspace].{key} is not a list of strings")
|
|
486
|
+
return value
|
|
487
|
+
|
|
488
|
+
@staticmethod
|
|
489
|
+
def _normal(path: Path) -> Path:
|
|
490
|
+
return Path(os.path.normpath(path))
|