@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.46 → 0.1.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/bin/adl +1 -1
  2. package/bin/reconcile +7 -2
  3. package/package.json +1 -1
  4. package/packages/agent-hooks/src/rules/impl/git-branch.ts +1 -1
  5. package/packages/agent-skills/assets/write-feature/SKILL.md +13 -15
  6. package/bin/lean-ctx-observe-strip +0 -434
  7. package/bin/lib/lean-ctx-observe-hooks.sh +0 -634
  8. package/bin/update-azure-agent-pool +0 -29
  9. package/packages/agent-hooks/src/hooks/lean-ctx/handler.ts +0 -22
  10. package/packages/agent-hooks/src/hooks/lean-ctx/index.ts +0 -173
  11. package/packages/agent-hooks/src/hooks/lean-ctx/mapping.ts +0 -121
  12. package/packages/agent-hooks/src/hooks/lean-ctx/scope-rewrite.ts +0 -207
  13. package/packages/agent-hooks/src/lean-ctx/env.ts +0 -123
  14. package/packages/agent-hooks/src/lean-ctx/mcp.ts +0 -142
  15. package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +0 -51
  16. package/packages/agent-hooks/src/rules/impl/adl-session-required.ts +0 -403
  17. package/packages/agent-hooks/src/rules/impl/tool-lean-ctx-global.ts +0 -594
  18. package/packages/agent-settings/assets/GLOBAL-AGENTS.md +0 -112
  19. package/packages/agent-settings/assets/lean-ctx/config.toml +0 -42
  20. package/packages/agent-settings/assets/lean-ctx/gitignore +0 -10
  21. package/packages/agent-settings/assets/lean-ctx/layout.toml +0 -7
  22. package/packages/agent-settings/src/deploy/lean-ctx-deploy.ts +0 -230
  23. package/packages/agent-settings/src/deploy/lean-ctx-merge.ts +0 -580
  24. package/packages/agent-skills/assets/adl-mechanics/SKILL.md +0 -38
  25. package/packages/agent-skills/assets/codebase-design/SKILL.md +0 -98
  26. package/packages/agent-skills/assets/codebase-design/references/DEEPENING.md +0 -37
  27. package/packages/agent-skills/assets/codebase-design/references/DESIGN-IT-TWICE.md +0 -44
  28. package/packages/agent-skills/assets/codebase-design/references/TESTABILITY.md +0 -29
  29. package/packages/agent-skills/assets/debug-issue/SKILL.md +0 -41
  30. package/packages/agent-skills/assets/domain-modeling/SKILL.md +0 -86
  31. package/packages/agent-skills/assets/domain-modeling/references/ADR-FORMAT.md +0 -47
  32. package/packages/agent-skills/assets/domain-modeling/references/CONTEXT-FORMAT.md +0 -60
  33. package/packages/agent-skills/assets/implement/SKILL.md +0 -28
  34. package/packages/agent-skills/assets/review-changes/SKILL.md +0 -45
  35. package/packages/agent-skills/assets/tdd/SKILL.md +0 -49
  36. package/packages/agent-skills/assets/tdd/references/mocking.md +0 -59
  37. package/packages/agent-skills/assets/tdd/references/tests.md +0 -77
  38. package/packages/agent-skills/assets/testing/SKILL.md +0 -54
  39. package/packages/agent-skills/assets/testing/references/e2e.md +0 -37
  40. package/packages/agent-skills/assets/testing/references/integration.md +0 -37
  41. package/packages/agent-skills/assets/testing/references/unit.md +0 -32
  42. package/packages/agent-skills/assets/to-feature/SKILL.md +0 -91
  43. package/packages/agent-skills/assets/to-feature/references/FEATURE-SPEC-TEMPLATE.md +0 -88
  44. package/packages/agent-skills/assets/to-workitems/SKILL.md +0 -90
  45. package/packages/agent-skills/assets/to-workitems/references/AZURE-DEVOPS-HIERARCHY.md +0 -121
  46. package/packages/agent-skills/assets/to-workitems/references/TICKET-TEMPLATES.md +0 -70
  47. package/packages/azdo/src/workflow-rules-matrix.csv +0 -85
  48. package/packages/fleet/src/launch-agents.ts +0 -649
  49. package/packages/repo-controls/assets/GLOBAL-INVARIANTS.md +0 -21
  50. package/packages/repo-controls/assets/enable-integration-auto-complete.mjs +0 -79
  51. package/packages/repo-controls/assets/scaffold/docs/notes.md +0 -20
  52. package/packages/repo-controls/assets/scaffold/plans/GAPS-AND-FINDINGS.md +0 -12
  53. package/packages/repo-controls/azure-pipelines.yml +0 -274
  54. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +0 -1
  55. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/excluded/frontend/mobile/src/i18n/locales/zh-CN/strings.ts +0 -1
  56. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +0 -17
  57. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +0 -13
  58. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-delivery.md +0 -10
  59. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-recommendation.md +0 -12
  60. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/target-narration.md +0 -10
  61. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  62. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/a.ts +0 -1
  63. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/b.ts +0 -1
  64. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/fresh/fresh.ts +0 -1
  65. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub1/only.ts +0 -1
  66. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub10/only.ts +0 -1
  67. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub11/only.ts +0 -1
  68. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub2/only.ts +0 -1
  69. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub3/only.ts +0 -1
  70. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub4/only.ts +0 -1
  71. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub5/only.ts +0 -1
  72. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub6/only.ts +0 -1
  73. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub7/only.ts +0 -1
  74. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub8/only.ts +0 -1
  75. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub9/only.ts +0 -1
  76. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/services/i18n/locales/de/common.json +0 -1
  77. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  78. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/vendored-crate/Cargo.toml +0 -2
  79. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  80. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/a.ts +0 -1
  81. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/b.ts +0 -1
  82. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/c.ts +0 -1
  83. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  84. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/fresh/fresh.ts +0 -1
  85. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub1/only.ts +0 -1
  86. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub10/only.ts +0 -1
  87. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub11/only.ts +0 -1
  88. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub2/only.ts +0 -1
  89. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub3/only.ts +0 -1
  90. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub4/only.ts +0 -1
  91. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub5/only.ts +0 -1
  92. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub6/only.ts +0 -1
  93. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub7/only.ts +0 -1
  94. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub8/only.ts +0 -1
  95. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub9/only.ts +0 -1
  96. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  97. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
  98. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/fixtures/open-work-cap-clean/plans/GAPS-AND-FINDINGS.md +0 -5
  99. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/fixtures/open-work-cap-clean/plans/features/az601-clean/spec.md +0 -10
  100. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/agent/crates/enact-providers/src/snapshots/README.md +0 -1
  101. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-app/assets/builtin-assistants/avatars/assistant.jpg +0 -1
  102. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-assets/assets/logos/tools/coding/tool.png +0 -1
  103. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-session/src/backend/codex_init_prompt.md +0 -1
  104. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/mobile/assets/images/icon.png +0 -1
  105. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/packages/desktop/src/renderer/assets/logos/brand/app.png +0 -1
  106. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/packages/desktop/src/renderer/assets/themes/default-theme.png +0 -1
  107. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/public/pwa/icon-192.png +0 -1
  108. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/resources/product.png +0 -1
  109. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-stray/backend/crates/enact-session/src/backend/README.md +0 -1
  110. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/spec.md +0 -12
  111. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-violation/plans/features/az347-test-coverage/spec.md +0 -12
  112. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/__init__.py +0 -11
  113. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/check.py +0 -62
  114. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/NotAMigration.sql +0 -1
  115. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/0001_long_prefix.sql +0 -1
  116. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/001_init.sql +0 -1
  117. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/002_add_index.sql +0 -1
  118. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/003-dashed-slug.sql +0 -1
  119. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/004_MixedCase.sql +0 -1
  120. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/005_trailing space.sql +0 -1
  121. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/01_short_prefix.sql +0 -1
  122. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/not-a-migration.sql +0 -1
  123. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/test_check.py +0 -116
  124. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/links.md +0 -20
  125. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/route-target.md +0 -8
  126. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/links.md +0 -20
  127. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/route-target.md +0 -8
  128. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.changeset/declared.txt +0 -1
  129. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/INDEPENDENT-VERIFICATION.md +0 -3
  130. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/.gitattributes +0 -1
  131. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/large file.bin +0 -3
  132. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/.gitattributes +0 -1
  133. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/large file.bin +0 -3
  134. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/small.txt +0 -1
  135. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/missing-filter/large file.bin +0 -0
  136. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/.gitattributes +0 -1
  137. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/payload.dat +0 -0
  138. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/__init__.py +0 -1
  139. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/__init__.py +0 -12
  140. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/check.py +0 -129
  141. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/disjoint/gate-registry.yaml +0 -13
  142. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/empty/gate-registry.yaml +0 -7
  143. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/overlapping/gate-registry.yaml +0 -13
  144. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/test_check.py +0 -109
  145. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/__init__.py +0 -1
  146. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/__init__.py +0 -11
  147. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/check.py +0 -149
  148. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/clean/docs/readme.md +0 -4
  149. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/relevant/VERSION +0 -1
  150. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/test_check.py +0 -151
  151. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/__init__.py +0 -11
  152. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/check.py +0 -202
  153. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/changesets/package.json +0 -5
  154. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/package.json +0 -5
  155. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/CHANGELOG.md +0 -5
  156. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/VERSION +0 -1
  157. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/changesets/package.json +0 -5
  158. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/package.json +0 -5
  159. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/CHANGELOG.md +0 -5
  160. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/VERSION +0 -1
  161. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/test_check.py +0 -162
  162. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/check.py +0 -103
  163. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.lock +0 -7
  164. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.toml +0 -4
  165. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/rust-toolchain.toml +0 -3
  166. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/src/main.rs +0 -1
  167. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/frontend/bun.lock +0 -1
  168. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.lock +0 -7
  169. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.toml +0 -4
  170. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/rust-toolchain.toml +0 -3
  171. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/src/main.rs +0 -1
  172. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/frontend/bun.lock +0 -0
  173. package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/grants-registry.yaml +0 -112
  174. package/packages/repo-controls/workflows/scripts/release/verify-npm-artifact.ts +0 -119
@@ -1,109 +0,0 @@
1
- """PIP-004 — ported from
2
- `compliance_new/checks/pipeline/phase-disjoint/test.sh`.
3
-
4
- Same three named corpora as the shell fixtures, ported byte-for-byte
5
- (`overlapping`, `disjoint`, `empty`), minus the `policy/` wrapper directory
6
- the shell version needed to reassemble a whole package around the copied
7
- gate script — the Python check reads its named input directly, so the
8
- committed fixture is just the one document each case is about.
9
-
10
- This wave's checks are wired into `checks/__init__.py` in a later, single
11
- pass, so `PhaseDisjoint` is not yet registered under `checks.lookup`. Rather
12
- than call `runner.run()` — which would resolve `PIP-004` to "no check
13
- implements it" and report `COULD_NOT_RUN` for the wrong reason, a
14
- coincidental pass — `run_check` below builds the `Context` directly (with the
15
- one named input this gate declares) and drives the check the same way
16
- `runner.run` would, so the real row (`policy/gate-registry.yaml`'s `PIP-004`)
17
- and the real check class are exercised without depending on the registry
18
- wiring. This is the same bypass `contracts.transport-boundaries`'s test uses
19
- for an unwired, entirely-structural gate.
20
-
21
- The shell `test.sh` also asserts two cases this port does not reproduce:
22
-
23
- * "no set given is could-not-run" — a scope guard `check.sh` itself enforced
24
- by hand (`ADL_CHANGED_FILES` unset). Here scope resolution belongs to
25
- `runner.run`/the CLI, never to a check (K-29..K-31), and this gate reads no
26
- member of the given file set at all (`graded` is unimplemented), so there
27
- is no equivalent guard inside `PhaseDisjoint` for a test to exercise.
28
- * the exact registry source LINE each finding points at (`yq '.id | line'`).
29
- `yaml.safe_load` does not carry line numbers back into the parsed model,
30
- and no other YAML-register gate ported so far (`layout.directory-ratchet`,
31
- `contracts.transport-boundaries`) attaches one either; this port follows
32
- that precedent and reports the registry's path without a line.
33
-
34
- See `check.py`'s docstring for why neither the currently shipped shell
35
- registry nor this package's own carries a single row with `phases:` today —
36
- the scheduling model MET-001 grades was already retired in favour of
37
- `engine.scope`-based triggering before this row was ported. Ported here
38
- faithfully anyway, against the same three corpora `check.sh` was proven
39
- against.
40
- """
41
-
42
- from __future__ import annotations
43
-
44
- import pathlib
45
-
46
- from adl_compliance.checks.pipeline.phase_disjoint import PhaseDisjoint
47
- from adl_compliance.core.context import Context, FileSet
48
- from adl_compliance.core.errors import CouldNotRun
49
- from adl_compliance.core.verdict import Outcome, Verdict, could_not_run, decide
50
- from adl_compliance.policy.registry import load
51
-
52
- FIXTURES = pathlib.Path(__file__).parent / "fixtures"
53
- OVERLAPPING = FIXTURES / "overlapping" / "gate-registry.yaml"
54
- DISJOINT = FIXTURES / "disjoint" / "gate-registry.yaml"
55
- EMPTY = FIXTURES / "empty" / "gate-registry.yaml"
56
- NOT_COMMITTED = FIXTURES / "gone.yaml"
57
-
58
- ROW = load().by_id("PIP-004")
59
-
60
-
61
- def run_check(registry: pathlib.Path) -> Verdict:
62
- """`runner.run`'s grade-then-decide, against this check directly — the
63
- same bypass `contracts.transport-boundaries`'s test uses for an unwired
64
- check."""
65
- ctx = Context(root=FIXTURES, row=ROW, inputs={"registry": registry})
66
- try:
67
- findings = tuple(PhaseDisjoint().run(ctx, FileSet()))
68
- except CouldNotRun as exc:
69
- return could_not_run(ROW, str(exc))
70
- return decide(ROW, findings, 0, ())
71
-
72
-
73
- # ------------------------------------------------------------- both overlaps
74
-
75
- def test_both_overlaps_are_reported_and_the_clean_row_is_not() -> None:
76
- verdict = run_check(OVERLAPPING)
77
- assert verdict.outcome is Outcome.FINDINGS, verdict.render()
78
- assert verdict.rules == ["MET-001", "MET-001"]
79
-
80
- messages = [f.message for f in verdict.findings]
81
- assert any("BAD-01 runs at both pre-commit and pre-push" in m for m in messages)
82
- assert any("BAD-02 runs at both pr_validation and qua_deploy" in m for m in messages)
83
- assert not any("OK-01" in m for m in messages)
84
-
85
-
86
- # --------------------------------------------------------- every row disjoint
87
-
88
- def test_a_disjoint_registry_reports_nothing() -> None:
89
- verdict = run_check(DISJOINT)
90
- assert verdict.outcome is Outcome.CLEAN, verdict.render()
91
- assert verdict.findings == ()
92
-
93
-
94
- # ------------------------------------------------- zero rows is could-not-run
95
-
96
- def test_a_registry_that_decodes_to_zero_rows_is_could_not_run() -> None:
97
- """A decode that yields nothing must not read as a clean estate — the
98
- assertion that separates "every row passed" from "the gate read nothing"."""
99
- verdict = run_check(EMPTY)
100
- assert verdict.outcome is Outcome.COULD_NOT_RUN, verdict.render()
101
- assert "decoded zero rows" in verdict.message
102
-
103
-
104
- # --------------------------------------------------------- a missing registry
105
-
106
- def test_a_missing_registry_is_could_not_run_not_a_pass() -> None:
107
- verdict = run_check(NOT_COMMITTED)
108
- assert verdict.outcome is Outcome.COULD_NOT_RUN, verdict.render()
109
- assert "missing input" in verdict.message
@@ -1,11 +0,0 @@
1
- """The `release.changeset-plan` gate.
2
-
3
- A folder rather than a module because this gate's fixture corpora — a clean
4
- one and a release-relevant one — belong beside the check that reads them.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- from .check import ChangesetPlan
10
-
11
- __all__ = ["ChangesetPlan"]
@@ -1,149 +0,0 @@
1
- """REL-001 — ported from
2
- `compliance_new/checks/release/changeset-plan/check.sh`.
3
-
4
- Entirely `graded`: the rule is "does the given set contain a release-relevant
5
- path", and there is no named path this gate probes unconditionally — an empty
6
- or irrelevant set is a silent pass, exactly as the shell script's own
7
- `needs_changeset` guard makes it.
8
-
9
- **The shell script's own case list, carried over untranslated.** `check.sh`'s
10
- `case "$path" in .workflows/changesets/package.json | ... | .changeset/*)`
11
- matches with bash's `case` pattern semantics, where `*` matches any string
12
- INCLUDING one containing `/` (case patterns are not restricted to filesystem
13
- pathname semantics the way `for f in *` is). `.changeset/*` therefore matches
14
- `.changeset/plan.md` and `.changeset/nested/plan.md` alike — the same reach as
15
- the registry row's own `.changeset/**` scope entry, not a narrower one.
16
- `fnmatch.fnmatchcase` has the identical property (a straight `*` -> `.*` regex
17
- translation, no path-separator special-casing) and, unlike plain `fnmatch`,
18
- no host-dependent case folding either, so it reproduces the shell patterns
19
- verbatim rather than approximating them with a hand-rolled prefix test.
20
-
21
- **Linear, not accumulating**, like the shell version: `fail()` there reports
22
- one finding and exits immediately, so at most one REL-001 finding ever comes
23
- out of a single run. Each branch below yields once and returns.
24
-
25
- **The jq-optional schema check does not survive.** `check.sh` skips its
26
- "changeset status output is not a JSON object" check entirely when `jq` is not
27
- on the host (`echo "... jq not installed for schema check"; exit 0`) — jq was
28
- an EXTERNAL tool the shell script had no other way to parse JSON with. This
29
- port has `json.loads` unconditionally, so the schema check always runs; there
30
- is no equivalent "jq missing" skip because there is nothing here that can be
31
- missing. This is a deliberate behavioural difference, not an oversight: it
32
- makes the gate strictly stricter (it now catches a malformed JSON object on
33
- every host, not only the ones with jq installed), never looser.
34
-
35
- **`release/changeset-tooling.sh` is not a registered tree.** It is invoked as
36
- a literal repository-relative path, exactly as `check.sh` invoked it — never
37
- through a `<tree>` placeholder, because no `release` entry exists in
38
- `policy/trees.toml` (K-42 governs a NAMED tree; this is one specific script
39
- this one gate drives, declared as a plain `inputs` path like any other
40
- consumer-owned file). Declared via `requires = ("bash",)` because that is the
41
- interpreter the shell tool was always invoked through and is exactly what
42
- `Check.requires` exists to verify before this code runs at all.
43
-
44
- **A real limit, inherited rather than introduced.** `release/` does not exist
45
- in this checkout (nor, per `git log`, has it ever been committed at any
46
- reachable ref), so the "changeset status succeeded and its JSON was graded"
47
- branch has never been exercised by anything this port can run — the shell
48
- test's own docstring says the same thing about its own fixture. This port
49
- proves exactly what the shell test proved: a release-relevant path in the
50
- given set reaches the tool invocation, and the tool's failure becomes a
51
- REL-001 finding rather than a crash or a silent pass.
52
- """
53
-
54
- from __future__ import annotations
55
-
56
- import fnmatch
57
- import json
58
- import subprocess
59
- import tempfile
60
- from collections.abc import Iterable
61
- from pathlib import Path
62
- from typing import ClassVar
63
-
64
- from ....core.check import Check
65
- from ....core.context import Context, FileSet
66
- from ....core.finding import Report
67
-
68
-
69
- class ChangesetPlan(Check):
70
- id: ClassVar[str] = "REL-001"
71
- rule: ClassVar[str] = "REL-001"
72
- requires: ClassVar[tuple[str, ...]] = ("bash",)
73
-
74
- # check.sh's own `case "$path" in ...)` alternatives. `VERSION`,
75
- # `CHANGELOG.md`, and `.changeset/*` are root-relative subject matter the
76
- # gate owns outright, never a consumer's tree choice; the two
77
- # `.workflows`-rooted ones are fixed 2026-09-15 (K-42 sweep) — they used
78
- # to be hardcoded literals FILTERING the given set (`_is_release_relevant`
79
- # below), so a repository declaring `workflows` away from its packaged
80
- # default had every real release-relevant change silently excluded and
81
- # this gate went quiet over content it never read — the CON-002 shape.
82
- ROOT_RELEVANT: ClassVar[tuple[str, ...]] = ("VERSION", "CHANGELOG.md", ".changeset/*")
83
-
84
- inputs: ClassVar[dict[str, str]] = {
85
- # A literal repository-relative path, not a `<tree>` placeholder: no
86
- # `release` tree is registered in `policy/trees.toml`, and this is the
87
- # one script this one gate drives, not a named consumer tree.
88
- "changeset_tool": "release/changeset-tooling.sh",
89
- "workflows_package": "<workflows>/package.json",
90
- "changesets_package": "<workflows>/changesets/package.json",
91
- "changesets_lock": "<workflows>/changesets/bun.lock",
92
- }
93
-
94
- def graded(self, ctx: Context, files: FileSet) -> Iterable[Report]:
95
- release_relevant = self.ROOT_RELEVANT + (
96
- ctx.relative(ctx.inputs["workflows_package"]),
97
- ctx.relative(ctx.inputs["changesets_package"]),
98
- ctx.relative(ctx.inputs["changesets_lock"]),
99
- )
100
- if not any(self._is_release_relevant(path, release_relevant) for path in files):
101
- return
102
-
103
- with tempfile.TemporaryDirectory(prefix="changeset-status.") as work:
104
- status_file = Path(work) / "status.json"
105
- completed = subprocess.run(
106
- (
107
- "bash", str(ctx.inputs["changeset_tool"]),
108
- "changeset", "status", "--output", str(status_file),
109
- ),
110
- cwd=ctx.root, capture_output=True, text=True, check=False,
111
- )
112
- if completed.returncode != 0:
113
- yield self.report(
114
- ".changeset",
115
- "changeset status could not be computed for a release-relevant change",
116
- "add a .changeset/*.md plan for the release-relevant change, or fix "
117
- "the failing 'release/changeset-tooling.sh changeset status' invocation",
118
- )
119
- return
120
-
121
- if not status_file.is_file():
122
- yield self.report(
123
- ".changeset",
124
- "changeset status did not write its output file",
125
- "investigate why 'release/changeset-tooling.sh changeset status "
126
- "--output <file>' did not create the file",
127
- )
128
- return
129
-
130
- body = status_file.read_text(encoding="utf-8", errors="replace")
131
- try:
132
- parsed = json.loads(body)
133
- except json.JSONDecodeError:
134
- parsed = None
135
- if not isinstance(parsed, dict):
136
- yield self.report(
137
- "release/changeset-tooling.sh",
138
- "changeset status output is not a JSON object",
139
- "fix release/changeset-tooling.sh's 'changeset status --output' to "
140
- "emit a JSON object",
141
- )
142
-
143
- @staticmethod
144
- def _is_release_relevant(path: str, patterns: tuple[str, ...]) -> bool:
145
- # `fnmatchcase`, not `fnmatch`: the shell `case` statement this ports
146
- # is byte-for-byte case-sensitive, and plain `fnmatch` normalises case
147
- # through `os.path.normcase` — a no-op on POSIX but not on Windows, so
148
- # this would silently diverge from the shell semantics on that host.
149
- return any(fnmatch.fnmatchcase(path, pattern) for pattern in patterns)
@@ -1,4 +0,0 @@
1
- # fixture
2
-
3
- A file that is not release-relevant by check-changeset-plan.sh's own case list.
4
- Its presence in the given set must not trigger a changeset-status check.
@@ -1,151 +0,0 @@
1
- """REL-001 — ported from
2
- `compliance_new/checks/release/changeset-plan/test.sh`.
3
-
4
- Two committed fixtures, copied byte-for-byte from the shell tree's corpora:
5
- `fixtures/clean/docs/readme.md` (no release-relevant path) and
6
- `fixtures/relevant/VERSION` (release-relevant, and the corpus deliberately
7
- carries no `release/` tree so `bash release/changeset-tooling.sh` fails for
8
- the same reason every time — see `check.py`'s module docstring for why that
9
- is this gate's real, inherited limit as a fixture subject, not a gap this port
10
- introduced).
11
-
12
- This check is intentionally NOT wired into `checks/__init__.py` yet (a
13
- separate step wires this whole wave's checks into the registry in one shot),
14
- so these tests call `ChangesetPlan.graded` directly against a hand-built
15
- `Context`/`FileSet` — the same bypass `docs/markdown_ast/test_check.py` and
16
- `pipeline/parser_tool_range/test_check.py` use for an unwired check, not a
17
- shortcut around row-scope narrowing.
18
-
19
- `test.sh`'s three cases map onto the three tests below exactly: a set with no
20
- release-relevant path exits 0 and reports nothing; a set naming `VERSION`
21
- reaches the tool invocation and turns its failure into exactly one REL-001
22
- finding on `.changeset`; an empty set grades nothing at all.
23
- """
24
-
25
- from __future__ import annotations
26
-
27
- import pathlib
28
- import shutil
29
-
30
- import pytest
31
-
32
- from adl_compliance.checks.release.changeset_plan import ChangesetPlan
33
- from adl_compliance.core.context import Context, FileSet
34
- from adl_compliance.core.verdict import Outcome, decide
35
- from adl_compliance.policy.registry import load
36
-
37
- FIXTURES = pathlib.Path(__file__).parent / "fixtures"
38
- CLEAN = FIXTURES / "clean"
39
- RELEVANT = FIXTURES / "relevant"
40
-
41
- ROW = load().by_id("REL-001")
42
-
43
- pytestmark = pytest.mark.skipif(
44
- shutil.which("bash") is None, reason="release.changeset-plan drives bash"
45
- )
46
-
47
-
48
- def corpus_files(root: pathlib.Path) -> tuple[str, ...]:
49
- """Every file in the corpus, relative to it — the same shape
50
- `_corpus-set.sh`'s `corpus_set` produced for the shell test."""
51
- return tuple(
52
- sorted(
53
- str(path.relative_to(root))
54
- for path in root.rglob("*")
55
- if path.is_file()
56
- )
57
- )
58
-
59
-
60
- def graded(root: pathlib.Path, given: tuple[str, ...]):
61
- ctx = Context(
62
- root=root, row=ROW,
63
- inputs={
64
- "changeset_tool": root / "release" / "changeset-tooling.sh",
65
- "workflows_package": root / ".workflows" / "package.json",
66
- "changesets_package": root / ".workflows" / "changesets" / "package.json",
67
- "changesets_lock": root / ".workflows" / "changesets" / "bun.lock",
68
- },
69
- )
70
- findings = tuple(ChangesetPlan().graded(ctx, FileSet(paths=given)))
71
- return decide(ROW, findings, 0, ())
72
-
73
-
74
- def test_the_row_declares_the_gate_this_check_ports() -> None:
75
- assert ROW.domain == "release"
76
- assert ROW.name == "changeset-plan"
77
-
78
-
79
- def test_the_fixture_premise_holds() -> None:
80
- """A mistyped fixture path would leave every assertion below vacuous."""
81
- assert (CLEAN / "docs" / "readme.md").is_file()
82
- assert (RELEVANT / "VERSION").is_file()
83
- assert not (RELEVANT / "release").exists()
84
-
85
-
86
- def test_no_release_relevant_path_is_a_clean_no_op() -> None:
87
- verdict = graded(CLEAN, corpus_files(CLEAN))
88
- assert verdict.outcome is Outcome.CLEAN, verdict.render()
89
- assert verdict.findings == ()
90
-
91
-
92
- def test_a_release_relevant_path_reaches_the_tool_and_reports_its_failure() -> None:
93
- verdict = graded(RELEVANT, corpus_files(RELEVANT))
94
- assert verdict.outcome is Outcome.FINDINGS, verdict.render()
95
- assert len(verdict.findings) == 1
96
- finding = verdict.findings[0]
97
- assert finding.rule == "REL-001"
98
- assert finding.path == ".changeset"
99
- assert finding.message == (
100
- "changeset status could not be computed for a release-relevant change"
101
- )
102
-
103
-
104
- def test_an_empty_set_grades_nothing() -> None:
105
- verdict = graded(CLEAN, ())
106
- assert verdict.outcome is Outcome.CLEAN, verdict.render()
107
- assert verdict.findings == ()
108
-
109
-
110
- def test_changeset_star_matches_a_nested_path_like_the_shell_case_pattern() -> None:
111
- """bash `case` patterns are not restricted to filesystem pathname
112
- semantics: `.changeset/*` matches a path with more than one segment
113
- beneath it, the same reach as the row's own `.changeset/**` scope."""
114
- patterns = ChangesetPlan.ROOT_RELEVANT + (".workflows/package.json",)
115
- assert ChangesetPlan._is_release_relevant(".changeset/nested/plan.md", patterns)
116
- assert ChangesetPlan._is_release_relevant(".changeset/plan.md", patterns)
117
- assert not ChangesetPlan._is_release_relevant("docs/readme.md", patterns)
118
-
119
-
120
- def test_a_redeclared_workflows_tree_is_honoured() -> None:
121
- """FIXED 2026-09-15 (K-42 sweep): the bug this pins is SILENT — the old
122
- `RELEASE_RELEVANT` tuple hardcoded `.workflows/...`, and `_is_release_
123
- relevant` filtered the given set against it directly. A repository
124
- declaring `workflows = "my-workflows"` with a real, changed `my-workflows
125
- /package.json` in the given set would have that file match NOTHING,
126
- `graded` would return on its very first line, and this gate would go
127
- quiet over a release-relevant change it never read — never an error,
128
- never a finding, a clean pass over unread content. This drives the real
129
- `<workflows>` resolution (not the manual `inputs` override every other
130
- test in this file uses) via `runner.resolve_inputs`/`effective_trees`,
131
- and a given set containing ONLY the redeclared-tree path (nothing under
132
- `.workflows/` at all) — proving the match comes from the resolved tree,
133
- not a coincidental literal overlap."""
134
- from contextlib import ExitStack
135
-
136
- from adl_compliance import runner
137
- from adl_compliance.policy.registry import effective_trees
138
-
139
- tool = RELEVANT / "release" / "changeset-tooling.sh"
140
- with ExitStack() as stack:
141
- inputs = runner.resolve_inputs(
142
- ChangesetPlan, RELEVANT, {ChangesetPlan.id: {"changeset_tool": tool}}, stack,
143
- effective_trees({"workflows": "my-workflows"}),
144
- )
145
- ctx = Context(root=RELEVANT, row=ROW, inputs=inputs)
146
- findings = tuple(
147
- ChangesetPlan().graded(ctx, FileSet(paths=("my-workflows/package.json",)))
148
- )
149
- verdict = decide(ROW, findings, 0, ())
150
- assert verdict.outcome is Outcome.FINDINGS, verdict.render()
151
- assert verdict.findings[0].rule == "REL-001"
@@ -1,11 +0,0 @@
1
- """The `release.version-release` gate.
2
-
3
- A folder rather than a module because this gate's fixture corpora — an
4
- aligned checkout and a drifted one — belong beside the check that reads them.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- from .check import VersionRelease
10
-
11
- __all__ = ["VersionRelease"]
@@ -1,202 +0,0 @@
1
- """REL-004 — VERSION, CHANGELOG.md, and the two workflow manifests, aligned.
2
-
3
- Ported from `compliance_new/checks/release/version-release/check.sh`. Entirely
4
- `structural`: every file this gate reads — VERSION, CHANGELOG.md, and the two
5
- entries in the shell's `versioned_files` array — is a FIXED, named path, never
6
- a member of the given set. That script's own comment says the same thing:
7
- "This gate reads no file set: the manifests it aligns are a FIXED list, named
8
- in the row's `engine.scope` so the runner selects it when one of them
9
- changes." There is no `graded` override for the same reason `COV-003` and
10
- `PIP-001` have none: nothing here is answered by iterating the given set.
11
-
12
- Two tiers, ported as two distinct behaviours because the shell script itself
13
- keeps them apart:
14
-
15
- VERREL-INPUT the shell's own bare `fail()` calls — VERSION missing, empty,
16
- or not `x.y.z`; CHANGELOG.md missing or carrying no readable
17
- latest-release heading; a versioned manifest missing or
18
- carrying no readable version field. Each is a HARD STOP in
19
- `check.sh`: it calls `fail`, which prints to stderr and does
20
- `exit 1` directly, WITHOUT ever calling `report_finding` —
21
- so at most one such condition is ever surfaced per run, and
22
- the script never reaches the alignment checks below it once
23
- one has fired. This port matches that: every branch below
24
- yields at most one `VERREL-INPUT` finding and returns. No
25
- shell rule id exists for this tier; `VERREL-INPUT` is invented
26
- here the same way `COV-003`'s port invented `COVBASE-INPUT`
27
- and `PIP-001`'s invented `PIPTR-INPUT`, both for shell scripts
28
- that only ever wrote ad hoc prose to stderr on this same kind
29
- of precondition failure.
30
-
31
- VER-001 the shell's own `report_finding VER-001 ...` calls — a
32
- manifest's or CHANGELOG.md's version does not match VERSION's.
33
- ACCUMULATING, exactly as the shell script's `for` loop and
34
- trailing CHANGELOG.md comparison never stop after the first
35
- mismatch: every versioned file is graded independently, and
36
- the CHANGELOG.md comparison always runs afterward regardless
37
- of what the loop found.
38
-
39
- `read_toml_version`'s case-statement arm (`check.sh`'s `case "$file" in
40
- *.json) ...;; *) toml;; esac`) is ported as `_TOML_VERSION` even though both of
41
- today's `VERSIONED_FILES` entries are `.json` and never reach it — the shell
42
- dispatches on each entry's own suffix, not on a fixed list of json-only names,
43
- so a future `.toml` entry would already be handled correctly by this port too.
44
- """
45
-
46
- from __future__ import annotations
47
-
48
- import re
49
- from collections.abc import Iterable
50
- from typing import ClassVar
51
-
52
- from ....core.check import Check
53
- from ....core.context import Context
54
- from ....core.finding import Report
55
-
56
-
57
- class VersionRelease(Check):
58
- id: ClassVar[str] = "REL-004"
59
- rule: ClassVar[str] = "VER-001"
60
-
61
- INPUT: ClassVar[str] = "VERREL-INPUT"
62
-
63
- # check.sh's own `versioned_files` array, verbatim order: changesets
64
- # first, then the workflow root manifest. Fixed 2026-09-15 (K-42 sweep):
65
- # these were hardcoded `.workflows/...` literals read directly via
66
- # `self.text(ctx, relative)`, bypassing `ctx.inputs`/tree resolution
67
- # entirely — a repository declaring `workflows` away from its packaged
68
- # default got both manifests reported as flatly missing (LOUD, but
69
- # against the wrong path).
70
- inputs: ClassVar[dict[str, str]] = {
71
- "changesets_package": "<workflows>/changesets/package.json",
72
- "workflows_package": "<workflows>/package.json",
73
- }
74
-
75
- _SEMVER: ClassVar[re.Pattern[str]] = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+$")
76
- # `read_json_version`'s sed: leading whitespace tolerated before the key.
77
- _JSON_VERSION: ClassVar[re.Pattern[str]] = re.compile(r'^\s*"version"\s*:\s*"([^"]*)".*$')
78
- # `read_toml_version`'s sed: no leading whitespace tolerated at all.
79
- _TOML_VERSION: ClassVar[re.Pattern[str]] = re.compile(r'^version = "([^"]*)".*$')
80
- # `tr -d '[:space:]'`'s POSIX class, spelled out rather than Python's wider
81
- # (Unicode-including) `\s`.
82
- _POSIX_SPACE: ClassVar[re.Pattern[str]] = re.compile(r"[ \t\n\r\v\f]")
83
- _UNRELEASED_BRACKET: ClassVar[re.Pattern[str]] = re.compile(r"^## \[Unreleased\]")
84
-
85
- def structural(self, ctx: Context) -> Iterable[Report]:
86
- version_body = self.text(ctx, "VERSION")
87
- if version_body is None:
88
- yield self._input(
89
- "VERSION", "VERSION is missing",
90
- "create a VERSION file at the repository root holding the "
91
- "canonical semantic version",
92
- )
93
- return
94
-
95
- canonical_version = self._POSIX_SPACE.sub("", version_body)
96
- if not canonical_version:
97
- yield self._input(
98
- "VERSION", "VERSION is empty",
99
- "write the canonical semantic version into VERSION",
100
- )
101
- return
102
- if not self._SEMVER.match(canonical_version):
103
- yield self._input(
104
- "VERSION", f"VERSION is not semantic x.y.z: {canonical_version}",
105
- f"rewrite VERSION to hold a semantic x.y.z version instead of "
106
- f"'{canonical_version}'",
107
- )
108
- return
109
-
110
- changelog_body = self.text(ctx, "CHANGELOG.md")
111
- if changelog_body is None:
112
- yield self._input(
113
- "CHANGELOG.md", "CHANGELOG.md is missing",
114
- "create CHANGELOG.md recording the release history",
115
- )
116
- return
117
-
118
- changelog_version = self._latest_changelog_version(changelog_body)
119
- if not changelog_version:
120
- yield self._input(
121
- "CHANGELOG.md",
122
- "unable to read latest CHANGELOG.md release version",
123
- "add a release heading to CHANGELOG.md (## x.y.z or "
124
- "## [x.y.z]) naming the current release",
125
- )
126
- return
127
-
128
- for name in ("changesets_package", "workflows_package"):
129
- path = ctx.inputs[name]
130
- relative = ctx.relative(path)
131
- body = self.text(ctx, path)
132
- if body is None:
133
- yield self._input(
134
- relative, f"{relative} is missing",
135
- f"create {relative} declaring a version field",
136
- )
137
- return
138
- file_version = self._file_version(relative, body)
139
- if not file_version:
140
- yield self._input(
141
- relative, f"unable to read version from {relative}",
142
- f"add a version field to {relative}",
143
- )
144
- return
145
- if file_version != canonical_version:
146
- yield self.report(
147
- relative,
148
- f"{relative} has version {file_version}, expected "
149
- f"{canonical_version} (from VERSION)",
150
- f"update {relative}'s version field to {canonical_version}",
151
- )
152
-
153
- if changelog_version != canonical_version:
154
- yield self.report(
155
- "CHANGELOG.md",
156
- f"CHANGELOG.md's latest release heading is {changelog_version}, "
157
- f"expected {canonical_version} (from VERSION)",
158
- f"add or rename CHANGELOG.md's latest release heading to "
159
- f"## {canonical_version}",
160
- )
161
-
162
- # ------------------------------------------------------------- hard stops
163
-
164
- def _input(self, path: str, message: str, fix: str) -> Report:
165
- return self.report(path, message, fix, rule=self.INPUT)
166
-
167
- # ------------------------------------------------------------- reading
168
-
169
- def _file_version(self, relative: str, body: str) -> str | None:
170
- pattern = self._JSON_VERSION if relative.endswith(".json") else self._TOML_VERSION
171
- for line in body.splitlines():
172
- match = pattern.match(line)
173
- if match is not None:
174
- return match.group(1)
175
- return None
176
-
177
- def _latest_changelog_version(self, body: str) -> str | None:
178
- """awk's one-pass scan: the FIRST `## ` heading that is not the
179
- Unreleased section, stripped of its `## [`/`]…`/`## ` wrapping —
180
- in that fixed order, so a bracketed and a bare heading both resolve to
181
- the bare version text."""
182
- for line in body.splitlines():
183
- if not line.startswith("## "):
184
- continue
185
- if self._UNRELEASED_BRACKET.match(line):
186
- continue
187
- if line == "## Unreleased":
188
- continue
189
- return self._strip_heading(line)
190
- return None
191
-
192
- @staticmethod
193
- def _strip_heading(line: str) -> str:
194
- text = line
195
- if text.startswith("## ["):
196
- text = text[4:]
197
- end = text.find("]")
198
- if end != -1:
199
- text = text[:end]
200
- if text.startswith("## "):
201
- text = text[3:]
202
- return text
@@ -1,5 +0,0 @@
1
- {
2
- "name": "workflows-fixture",
3
- "version": "1.2.3",
4
- "private": true
5
- }