testguard-cli 0.10.0__tar.gz → 0.10.2__tar.gz

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 (310) hide show
  1. testguard_cli-0.10.2/.github/scripts/publish-homebrew-tap.sh +68 -0
  2. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/workflows/release.yml +25 -13
  3. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/workflows/scheduled-release.yml +1 -1
  4. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/AGENTS.md +4 -2
  5. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/CHANGELOG.md +43 -0
  6. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/CONTRIBUTING.md +7 -3
  7. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/PKG-INFO +11 -7
  8. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/README.md +10 -6
  9. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/SECURITY.md +6 -4
  10. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/action.yml +1 -1
  11. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/docs/testguard-explained.html +1 -1
  12. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/docs/testguard-explained.pdf +0 -0
  13. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/docs-canonical/ARCHITECTURE.md +5 -5
  14. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/docs-canonical/REQUIREMENTS.md +4 -1
  15. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/docs-canonical/SECURITY.md +14 -5
  16. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/docs-canonical/TEST-SPEC.md +1 -1
  17. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/package-lock.json +2 -2
  18. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/package.json +1 -1
  19. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
  20. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/packaging/homebrew/testguard.rb +2 -2
  21. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/pyproject.toml +1 -1
  22. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/GATE-SEMANTICS.md +10 -0
  23. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/status.json +14 -0
  24. testguard_cli-0.10.2/spec/conformance/invalid/status.partial-scope-counts-disagree.json +31 -0
  25. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/lib/validate.mjs +7 -0
  26. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/status.schema.json +93 -1
  27. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/cli.mjs +2 -2
  28. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/admit.mjs +10 -2
  29. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/probe.mjs +30 -3
  30. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/scaffold.mjs +10 -1
  31. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/probe.mjs +14 -11
  32. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/admit.test.mjs +4 -1
  33. testguard_cli-0.10.2/test/claim-selection.test.mjs +74 -0
  34. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/probe.fixture.test.mjs +23 -5
  35. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/release-sync.test.mjs +74 -0
  36. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/scaffold.test.mjs +3 -0
  37. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/testguard.claims.json +204 -0
  38. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/testguard.cost-budget.json +19 -14
  39. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.agents/skills/testguard/SKILL.md +0 -0
  40. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.codex/hooks.json +0 -0
  41. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.docguard.json +0 -0
  42. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.docguardignore +0 -0
  43. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.gitattributes +0 -0
  44. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  45. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  46. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  47. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/dependabot.yml +0 -0
  48. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/scripts/approve-held-runs.sh +0 -0
  49. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/scripts/build-one-pager.mjs +0 -0
  50. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/scripts/check-cost-budget.mjs +0 -0
  51. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/scripts/check-python-fixture.mjs +0 -0
  52. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/scripts/install-smoke.mjs +0 -0
  53. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/scripts/sync-release-version.mjs +0 -0
  54. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/workflows/auto-merge.yml +0 -0
  55. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/workflows/ci.yml +0 -0
  56. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.github/workflows/supply-chain.yml +0 -0
  57. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.gitignore +0 -0
  58. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.npmignore +0 -0
  59. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.npmrc +0 -0
  60. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.pre-commit-config.yaml +0 -0
  61. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/.pre-commit-hooks.yaml +0 -0
  62. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/CLAUDE.md +0 -0
  63. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/CODE_OF_CONDUCT.md +0 -0
  64. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/DRIFT-LOG.md +0 -0
  65. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/LICENSE +0 -0
  66. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/PRIVACY.md +0 -0
  67. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/SUPPORT.md +0 -0
  68. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/bench/README.md +0 -0
  69. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/cli/testguard.mjs +0 -0
  70. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/docs-canonical/DATA-MODEL.md +0 -0
  71. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/docs-canonical/ENVIRONMENT.md +0 -0
  72. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/.gitignore +0 -0
  73. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/README.md +0 -0
  74. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/expected.json +0 -0
  75. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/package.json +0 -0
  76. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/src/export.mjs +0 -0
  77. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/src/redact.mjs +0 -0
  78. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
  79. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/test/flaky.test.mjs +0 -0
  80. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/test/redact.test.mjs +0 -0
  81. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/testguard.claims.json +0 -0
  82. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer/vitest.config.mjs +0 -0
  83. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/.gitignore +0 -0
  84. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/README.md +0 -0
  85. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/expected.json +0 -0
  86. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/jest.config.js +0 -0
  87. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/package.json +0 -0
  88. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/src/export.js +0 -0
  89. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/src/redact.js +0 -0
  90. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
  91. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
  92. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/test/redact.test.js +0 -0
  93. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-jest/testguard.claims.json +0 -0
  94. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/.gitignore +0 -0
  95. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/README.md +0 -0
  96. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
  97. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
  98. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/expected.json +0 -0
  99. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/package-lock.json +0 -0
  100. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/package.json +0 -0
  101. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
  102. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
  103. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
  104. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
  105. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
  106. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
  107. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/.gitignore +0 -0
  108. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/README.md +0 -0
  109. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/__init__.py +0 -0
  110. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/export.py +0 -0
  111. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/orphan.py +0 -0
  112. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/pipeline.py +0 -0
  113. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/demo/redact.py +0 -0
  114. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/expected.json +0 -0
  115. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/testguard.claims.json +0 -0
  116. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
  117. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
  118. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/tests/test_patched.py +0 -0
  119. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/fixtures/known-answer-python/tests/test_redact.py +0 -0
  120. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/README.md +0 -0
  121. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/baseline.json +0 -0
  122. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/brief.json +0 -0
  123. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/calibration-websec.json +0 -0
  124. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/calibration.json +0 -0
  125. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/claims-methods.json +0 -0
  126. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/claims.json +0 -0
  127. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
  128. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-negative-control.json +0 -0
  129. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-provisional.json +0 -0
  130. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-python.json +0 -0
  131. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/evidence-scan-method.json +0 -0
  132. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/evidence.json +0 -0
  133. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/gate.json +0 -0
  134. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/ignore.json +0 -0
  135. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/examples/replay.json +0 -0
  136. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
  137. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/baseline.zero-count.json +0 -0
  138. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
  139. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
  140. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
  141. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
  142. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
  143. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
  144. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
  145. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
  146. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
  147. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
  148. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.absolute-path.json +0 -0
  149. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
  150. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
  151. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
  152. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
  153. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.noop-fault.json +0 -0
  154. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
  155. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
  156. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
  157. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
  158. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
  159. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
  160. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
  161. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
  162. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
  163. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
  164. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
  165. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
  166. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
  167. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
  168. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
  169. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
  170. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
  171. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
  172. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
  173. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
  174. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
  175. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
  176. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
  177. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
  178. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
  179. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
  180. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
  181. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
  182. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
  183. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
  184. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
  185. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/ignore.short-reason.json +0 -0
  186. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
  187. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
  188. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/status.claim-without-file.json +0 -0
  189. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
  190. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
  191. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
  192. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
  193. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
  194. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/conformance/schemas.test.mjs +0 -0
  195. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/lib/fingerprint.mjs +0 -0
  196. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/lib/wilson.mjs +0 -0
  197. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/baseline.schema.json +0 -0
  198. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/brief.schema.json +0 -0
  199. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/calibration.schema.json +0 -0
  200. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/claims.schema.json +0 -0
  201. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/common.schema.json +0 -0
  202. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/evidence.schema.json +0 -0
  203. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/gate.schema.json +0 -0
  204. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/ignore.schema.json +0 -0
  205. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/spec/schemas/replay.schema.json +0 -0
  206. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/admit/admit.mjs +0 -0
  207. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/baseline/baseline.mjs +0 -0
  208. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/brief/brief.mjs +0 -0
  209. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/claims/anchors.mjs +0 -0
  210. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/claims/annotations.mjs +0 -0
  211. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/claims/load.mjs +0 -0
  212. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/claims/removed.mjs +0 -0
  213. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/baseline.mjs +0 -0
  214. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/brief.mjs +0 -0
  215. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/claims.mjs +0 -0
  216. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/gate.mjs +0 -0
  217. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/init.mjs +0 -0
  218. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/mcp.mjs +0 -0
  219. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/replay.mjs +0 -0
  220. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/commands/status.mjs +0 -0
  221. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/evidence/writer.mjs +0 -0
  222. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/gate/changed.mjs +0 -0
  223. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/git.mjs +0 -0
  224. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/init/init.mjs +0 -0
  225. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/init/templates/SKILL.md +0 -0
  226. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/mcp/server.mjs +0 -0
  227. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/mcp/tools.mjs +0 -0
  228. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/attribution.mjs +0 -0
  229. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/classify.mjs +0 -0
  230. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/contention.mjs +0 -0
  231. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/cost.mjs +0 -0
  232. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/discover.mjs +0 -0
  233. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/independence.mjs +0 -0
  234. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/inject.mjs +0 -0
  235. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/mocks.mjs +0 -0
  236. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/progress.mjs +0 -0
  237. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/pyimports.mjs +0 -0
  238. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/rank.mjs +0 -0
  239. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/index.mjs +0 -0
  240. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/jest.mjs +0 -0
  241. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/playwright.mjs +0 -0
  242. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
  243. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/python/_testguard_report.py +0 -0
  244. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
  245. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/python.mjs +0 -0
  246. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/shared.mjs +0 -0
  247. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/runners/vitest.mjs +0 -0
  248. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/probe/worktree.mjs +0 -0
  249. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/render.mjs +0 -0
  250. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/replay/label.mjs +0 -0
  251. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/replay/replay.mjs +0 -0
  252. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/scaffold/producers.mjs +0 -0
  253. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/scaffold/producers.python.mjs +0 -0
  254. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/scaffold/scaffold.mjs +0 -0
  255. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/status/status.mjs +0 -0
  256. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/status/surface.mjs +0 -0
  257. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/util/glob.mjs +0 -0
  258. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/src/util/hash.mjs +0 -0
  259. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/annotations.test.mjs +0 -0
  260. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/attribution.test.mjs +0 -0
  261. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/baseline.test.mjs +0 -0
  262. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/brief.test.mjs +0 -0
  263. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/calibration.test.mjs +0 -0
  264. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/claims-anchors.test.mjs +0 -0
  265. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/claims-since.test.mjs +0 -0
  266. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/classify.test.mjs +0 -0
  267. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/contention.test.mjs +0 -0
  268. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/cost.test.mjs +0 -0
  269. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/discover.test.mjs +0 -0
  270. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/gate.test.mjs +0 -0
  271. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/gitlab-template.test.mjs +0 -0
  272. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/glob.test.mjs +0 -0
  273. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/independence.test.mjs +0 -0
  274. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/init.test.mjs +0 -0
  275. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/inject.test.mjs +0 -0
  276. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/label.test.mjs +0 -0
  277. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/mcp.test.mjs +0 -0
  278. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/mocks.test.mjs +0 -0
  279. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/negative-control.test.mjs +0 -0
  280. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/one-pager.test.mjs +0 -0
  281. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/probe-error.test.mjs +0 -0
  282. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/probe-preconditions.test.mjs +0 -0
  283. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/probe.jest.test.mjs +0 -0
  284. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/probe.playwright.test.mjs +0 -0
  285. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/probe.python.test.mjs +0 -0
  286. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/progress.test.mjs +0 -0
  287. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/pyimports.test.mjs +0 -0
  288. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/rank-aliases.test.mjs +0 -0
  289. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/release-approval.test.mjs +0 -0
  290. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/replay-verdict.test.mjs +0 -0
  291. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/replay.test.mjs +0 -0
  292. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/runner-argv.test.mjs +0 -0
  293. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/runner-command.test.mjs +0 -0
  294. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/runner-playwright.test.mjs +0 -0
  295. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/runner-python.test.mjs +0 -0
  296. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/runner-vitest.test.mjs +0 -0
  297. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/samples/playwright-report.json +0 -0
  298. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/samples/python-report-collect-error.json +0 -0
  299. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/samples/python-report.json +0 -0
  300. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/scaffold.python.test.mjs +0 -0
  301. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/snapshot.test.mjs +0 -0
  302. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/status.test.mjs +0 -0
  303. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/surface.test.mjs +0 -0
  304. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/vitest-config.test.mjs +0 -0
  305. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/worktree.test.mjs +0 -0
  306. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/test/writer.test.mjs +0 -0
  307. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/testguard.ignore.json +0 -0
  308. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/testguard_cli/__init__.py +0 -0
  309. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/testguard_cli/wrapper.py +0 -0
  310. {testguard_cli-0.10.0 → testguard_cli-0.10.2}/vitest.config.mjs +0 -0
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ fail() {
6
+ echo "::error::$*" >&2
7
+ exit 1
8
+ }
9
+
10
+ VERSION="${1:-}"
11
+ [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || fail "usage: publish-homebrew-tap.sh <stable-semver>"
12
+
13
+ SOURCE_FORMULA="${HOMEBREW_TAP_SOURCE:-packaging/homebrew/testguard.rb}"
14
+ TAP_REMOTE="${HOMEBREW_TAP_REMOTE:-git@github.com:raccioly/homebrew-tap.git}"
15
+ [[ -f "$SOURCE_FORMULA" ]] || fail "source formula not found: $SOURCE_FORMULA"
16
+
17
+ FORMULA_VERSION=$(sed -nE 's#^[[:space:]]*url ".*testguard-cli-([0-9]+\.[0-9]+\.[0-9]+)\.tgz"#\1#p' "$SOURCE_FORMULA")
18
+ FORMULA_SHA=$(sed -nE 's/^[[:space:]]*sha256 "([0-9a-f]+)"/\1/p' "$SOURCE_FORMULA")
19
+ [[ "$FORMULA_VERSION" = "$VERSION" ]] || fail "formula version $FORMULA_VERSION does not match release $VERSION"
20
+ [[ "$FORMULA_SHA" =~ ^[0-9a-f]{64}$ ]] || fail "formula sha256 is not 64 lowercase hex characters"
21
+
22
+ TEMP_ROOT="${RUNNER_TEMP:-${TMPDIR:-/tmp}}"
23
+ [[ -d "$TEMP_ROOT" ]] || fail "temporary root does not exist: $TEMP_ROOT"
24
+ WORK_DIR=$(mktemp -d "$TEMP_ROOT/testguard-homebrew-tap.XXXXXX")
25
+
26
+ cleanup() {
27
+ case "$WORK_DIR" in
28
+ "$TEMP_ROOT"/testguard-homebrew-tap.*) rm -rf -- "$WORK_DIR" ;;
29
+ *) echo "::warning::refusing to remove unexpected temporary path: $WORK_DIR" >&2 ;;
30
+ esac
31
+ }
32
+ trap cleanup EXIT
33
+
34
+ if [[ "$TAP_REMOTE" = git@github.com:* ]]; then
35
+ [[ -n "${HOMEBREW_TAP_DEPLOY_KEY:-}" ]] || fail "HOMEBREW_TAP_DEPLOY_KEY is required for the GitHub tap"
36
+ command -v gh >/dev/null || fail "gh is required to verify GitHub's SSH host keys"
37
+ KEY_FILE="$WORK_DIR/deploy_key"
38
+ KNOWN_HOSTS="$WORK_DIR/known_hosts"
39
+ umask 077
40
+ printf '%s\n' "$HOMEBREW_TAP_DEPLOY_KEY" > "$KEY_FILE"
41
+ gh api meta --jq '.ssh_keys[] | "github.com " + .' > "$KNOWN_HOSTS"
42
+ [[ -s "$KNOWN_HOSTS" ]] || fail "GitHub API returned no SSH host keys"
43
+ export GIT_SSH_COMMAND="ssh -i \"$KEY_FILE\" -o IdentitiesOnly=yes -o UserKnownHostsFile=\"$KNOWN_HOSTS\" -o StrictHostKeyChecking=yes"
44
+ fi
45
+
46
+ TAP_DIR="$WORK_DIR/tap"
47
+ git clone --quiet --depth 1 "$TAP_REMOTE" "$TAP_DIR"
48
+ TAP_FORMULA="$TAP_DIR/Formula/testguard.rb"
49
+ [[ -f "$TAP_FORMULA" ]] || fail "tap is missing Formula/testguard.rb"
50
+
51
+ if cmp -s "$SOURCE_FORMULA" "$TAP_FORMULA"; then
52
+ echo "✅ raccioly/tap already carries TestGuard v$VERSION ($FORMULA_SHA)"
53
+ exit 0
54
+ fi
55
+
56
+ install -m 0644 "$SOURCE_FORMULA" "$TAP_FORMULA"
57
+ ruby -c "$TAP_FORMULA" >/dev/null
58
+ git -C "$TAP_DIR" diff --check
59
+ git -C "$TAP_DIR" config user.name "github-actions[bot]"
60
+ git -C "$TAP_DIR" config user.email "github-actions[bot]@users.noreply.github.com"
61
+ git -C "$TAP_DIR" add Formula/testguard.rb
62
+ git -C "$TAP_DIR" commit --quiet -m "testguard $VERSION" -m "Published from raccioly/testguard's release workflow."
63
+ git -C "$TAP_DIR" push --quiet origin HEAD:main
64
+ git -C "$TAP_DIR" fetch --quiet origin main
65
+ cmp "$SOURCE_FORMULA" <(git -C "$TAP_DIR" show origin/main:Formula/testguard.rb) \
66
+ || fail "live tap does not match the validated source formula after push"
67
+
68
+ echo "✅ published TestGuard v$VERSION to raccioly/tap ($FORMULA_SHA)"
@@ -1,8 +1,9 @@
1
- name: Auto Release — Tag, GitHub Release, npm, PyPI
1
+ name: Auto Release — GitHub, npm, PyPI, Homebrew
2
2
 
3
3
  # TAG-DRIVEN and idempotent: a version needs releasing iff no tag v<version>
4
- # exists. Re-runs never double-publish. No long-lived secrets anywhere —
5
- # npm and PyPI both authenticate with OIDC Trusted Publishing.
4
+ # exists. Re-runs never double-publish. npm and PyPI authenticate with OIDC
5
+ # Trusted Publishing. Homebrew uses one long-lived SSH deploy key scoped only
6
+ # to the distribution tap; it cannot access this repository or any other one.
6
7
  #
7
8
  # ONE-TIME SETUP (maintainer, cannot be automated):
8
9
  # npm: the first `npm publish` of testguard-cli is manual (Trusted Publishing
@@ -12,6 +13,9 @@ name: Auto Release — Tag, GitHub Release, npm, PyPI
12
13
  # PyPI: pypi.org → Your account → Publishing → add a PENDING publisher for
13
14
  # project testguard-cli: owner raccioly, repo testguard, workflow
14
15
  # release.yml, environment pypi. The first release creates the project.
16
+ # Brew: add a write deploy key named testguard-release-homebrew-tap to
17
+ # raccioly/homebrew-tap and store its private half here as the Actions
18
+ # secret HOMEBREW_TAP_DEPLOY_KEY.
15
19
 
16
20
  on:
17
21
  workflow_dispatch: # manual recovery
@@ -231,15 +235,17 @@ jobs:
231
235
  # for byte from `npm pack` — after npm serves it (a publish is `PUT 202`;
232
236
  # the file can take minutes to appear, so the script polls, bounded).
233
237
  #
234
- # It cannot commit to main: branch protection requires the three CI legs
235
- # on every commit and github-actions[bot] is no admin. So the one-line
236
- # change lands as a bot PR with native auto-merge armed, and this job
237
- # dispatches ci.yml on the branch itself — a PR opened with GITHUB_TOKEN
238
- # gets no pull_request run, but workflow_dispatch is the documented
239
- # exception. The PR's own runs may still appear held for first-time-
240
- # contributor approval, so this job clears those holds before returning.
241
- # That also means the merge triggers nothing here; the hourly sweep
242
- # observes the merged formula and finds brew_missing=false.
238
+ # The validated formula is also copied to the live raccioly/homebrew-tap
239
+ # repository with a write deploy key scoped only to that tap. The source
240
+ # copy cannot commit to main directly: branch protection requires the
241
+ # three CI legs on every commit and github-actions[bot] is no admin. So
242
+ # that one-line change lands as a bot PR with native auto-merge armed, and
243
+ # this job dispatches ci.yml on the branch itself — a PR opened with
244
+ # GITHUB_TOKEN gets no pull_request run, but workflow_dispatch is the
245
+ # documented exception. The PR's own runs may still appear held for
246
+ # first-time-contributor approval, so this job clears those holds before
247
+ # returning. That also means the merge triggers nothing here; the hourly
248
+ # sweep observes the merged source formula and finds brew_missing=false.
243
249
  #
244
250
  # Idempotent: an existing branch is reused, an existing open PR is left
245
251
  # alone (CI re-dispatched only if its head has no check runs), a closed
@@ -265,9 +271,10 @@ jobs:
265
271
  - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
266
272
  with:
267
273
  node-version: 24
268
- - name: Set sha256 from the published tarball; land it as an auto-merging PR
274
+ - name: Set sha256, publish the live tap, and land the source formula PR
269
275
  env:
270
276
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
277
+ HOMEBREW_TAP_DEPLOY_KEY: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}
271
278
  run: |
272
279
  set -euo pipefail
273
280
  VERSION="${{ needs.detect-version.outputs.version }}"
@@ -290,6 +297,11 @@ jobs:
290
297
  # other surface drifted. Nothing is pushed unless this passes.
291
298
  node .github/scripts/sync-release-version.mjs --check --online
292
299
 
300
+ # Homebrew users install from raccioly/homebrew-tap, not this staged
301
+ # copy. Publish the exact formula that passed the online check. The
302
+ # helper is idempotent and fails closed if the tap cannot be updated.
303
+ .github/scripts/publish-homebrew-tap.sh "$VERSION"
304
+
293
305
  if git diff --quiet -- packaging/homebrew/testguard.rb; then
294
306
  echo "✅ $BRANCH already carries the published sha256"
295
307
  else
@@ -148,7 +148,7 @@ jobs:
148
148
  git add -A
149
149
  git commit -m "release: v${NEW_VERSION} — automated weekly batch"
150
150
  git push origin "$BRANCH"
151
- printf 'Automated weekly release. Version bump + CHANGELOG entry + synced version surfaces. Merging tags and publishes to npm and PyPI via release.yml.\n\nTwo things a GITHUB_TOKEN PR needs done for it, both handled by this workflow: it raises no `pull_request` event, so ci.yml was dispatched on `%s` itself; and the `pull_request` runs it does create are held at `action_required` (github-actions[bot] is permanently a first-time contributor), so those were approved too. Auto-merge is armed and completes once the three Node legs pass.\n\nIf it sits anyway: `gh workflow run ci.yml --ref %s` for missing checks, or check for runs still awaiting approval on the head sha.\n' "$BRANCH" "$BRANCH" > /tmp/pr-body.md
151
+ printf 'Automated weekly release. Version bump + CHANGELOG entry + synced version surfaces. Merging tags and publishes to GitHub, npm, PyPI, and Homebrew via release.yml.\n\nTwo things a GITHUB_TOKEN PR needs done for it, both handled by this workflow: it raises no `pull_request` event, so ci.yml was dispatched on `%s` itself; and the `pull_request` runs it does create are held at `action_required` (github-actions[bot] is permanently a first-time contributor), so those were approved too. Auto-merge is armed and completes once the three Node legs pass.\n\nIf it sits anyway: `gh workflow run ci.yml --ref %s` for missing checks, or check for runs still awaiting approval on the head sha.\n' "$BRANCH" "$BRANCH" > /tmp/pr-body.md
152
152
  # A bare `gh pr create` failure here reads as a GraphQL error with no
153
153
  # hint of the cause, which is how v0.8.1 lost its release PR after
154
154
  # everything else in the release had succeeded.
@@ -1,6 +1,6 @@
1
1
  # AI Agent Instructions — TestGuard
2
2
 
3
- <!-- docguard:last-reviewed 2026-09-18 -->
3
+ <!-- docguard:last-reviewed 2026-09-19 -->
4
4
 
5
5
  Read this before changing anything. It is short on purpose.
6
6
 
@@ -63,7 +63,9 @@ node cli/testguard.mjs scaffold src/probe/classify.mjs --json # what the produ
63
63
 
64
64
  Dependabot minor/patch PRs and automated release PRs auto-merge when the exact
65
65
  CI run is green on all three Node legs. Majors and anything else wait for a
66
- human. Release publishing is OIDC-only; there are no tokens in this repo.
66
+ human. npm and PyPI publishing is OIDC-only; Homebrew uses a dedicated SSH
67
+ deploy key scoped only to `raccioly/homebrew-tap`. There are no personal or
68
+ registry tokens in this repo.
67
69
 
68
70
  <!-- testguard:begin -->
69
71
  ## TestGuard
@@ -7,6 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.10.2] - 2026-09-20
11
+
12
+ Automated weekly release — everything merged since `v0.10.1`.
13
+
14
+ ### Changed
15
+
16
+ - fix(release): publish the live Homebrew tap (#142)
17
+ - chore(homebrew): sha256 for v0.10.1 (#141)
18
+
19
+
20
+ ### Fixed
21
+
22
+ - Releases now publish the registry-verified Homebrew formula to the live
23
+ `raccioly/tap` repository with a dedicated, tap-scoped deploy key. The
24
+ release fails closed if that copy cannot be made, instead of updating only
25
+ the staging formula in this repository while users remain on an old version.
26
+
27
+ ### Changed
28
+
29
+ - The self-probe cost budget is 1440 s, from 1230. The cold v0.10.1 release
30
+ measured 1210 s before the four-fault live-tap claim added about 53 s; 1440
31
+ restores the established ~14% CI headroom. The 100 s per-claim ceiling is
32
+ unchanged, so an individually expensive claim still fails the gate.
33
+
34
+ ## [0.10.1] - 2026-09-19
35
+
36
+ Automated weekly release — everything merged since `v0.10.0`.
37
+
38
+ ### Changed
39
+
40
+ - fix(probe): preserve repeated claim selections (#139)
41
+ - chore(homebrew): sha256 for v0.10.0 (#138)
42
+
43
+
44
+ ### Fixed
45
+
46
+ - Repeated `probe --claim` flags no longer collapse silently to the last value.
47
+ Repeated and comma-separated ids are flattened in first-seen order,
48
+ duplicates are collapsed, empty selections fail, and both human and JSON
49
+ partial-run output show requested versus probed ids and counts. Probe JSON is
50
+ now validated against the published status schema before it is emitted. The
51
+ singular `scaffold` and `admit` forms reject ambiguous repeated values.
52
+
10
53
  ## [0.10.0] - 2026-09-19
11
54
 
12
55
  Automated weekly release — everything merged since `v0.9.4`.
@@ -79,12 +79,16 @@ on the fault.
79
79
  `package.json` is the single source of truth for the version;
80
80
  `.github/scripts/sync-release-version.mjs` propagates it. A weekly workflow
81
81
  opens a release PR; merging it tags and publishes to npm and PyPI via OIDC
82
- Trusted Publishing. There are no tokens to rotate.
82
+ Trusted Publishing. There are no npm or PyPI tokens to rotate. Homebrew uses a
83
+ dedicated SSH deploy key that can write only to `raccioly/homebrew-tap`; its
84
+ private half is stored as the `HOMEBREW_TAP_DEPLOY_KEY` Actions secret.
83
85
 
84
86
  The Homebrew formula's `sha256` is the one surface that cannot be known before
85
87
  publishing: `release.yml` sets it after `npm publish`, from the registry
86
- tarball (`sync-release-version.mjs --sha256`), and lands it as a bot PR with
87
- auto-merge armed — `main` is protected, so no job commits to it directly.
88
+ tarball (`sync-release-version.mjs --sha256`), copies that verified formula
89
+ byte-for-byte to the live tap, and lands the source copy as a bot PR with
90
+ auto-merge armed — this repository's `main` is protected, so no job commits to
91
+ it directly.
88
92
  `--check` (offline) refuses a placeholder hash; `--check --online` compares
89
93
  against the tarball and is what decides, on every hourly sweep, whether the
90
94
  formula is still owed.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: testguard-cli
3
- Version: 0.10.0
3
+ Version: 0.10.2
4
4
  Summary: Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest. Ships the Node.js CLI (requires Node.js 20+).
5
5
  Project-URL: Homepage, https://github.com/raccioly/testguard
6
6
  Project-URL: Documentation, https://github.com/raccioly/testguard#readme
@@ -151,9 +151,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
151
151
  | npm | `npm i -D testguard-cli` then `npx testguard probe` |
152
152
  | pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
153
153
  | Homebrew | `brew tap raccioly/tap && brew install testguard` |
154
- | GitHub Action | `uses: raccioly/testguard@v0.10.0` — see [`action.yml`](./action.yml) |
154
+ | GitHub Action | `uses: raccioly/testguard@v0.10.2` — see [`action.yml`](./action.yml) |
155
155
  | pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
156
- | GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.0/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
156
+ | GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
157
157
 
158
158
  Projects that set `min-release-age` in `.npmrc` cannot see a version published
159
159
  less than that many days ago (`ENOVERSIONS`); install that one with
@@ -303,8 +303,12 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
303
303
  because it fails when the claim is false.
304
304
 
305
305
  Practical loop: first pass `--no-escalate` (escalation re-runs the whole
306
- suite N times per survivor); iterate on one claim with `--claim <ID>` and
307
- either `--include-dirty` or `--in-place` (only fault target files must be
306
+ suite N times per survivor); iterate on one claim with `--claim <ID>`, or
307
+ select a batch with commas or repeated flags (`--claim A,B --claim C`).
308
+ Every occurrence is retained in first-seen order, duplicates are collapsed,
309
+ and partial human/JSON output names both the requested and probed counts, so
310
+ a green subset cannot masquerade as the selection the operator asked for.
311
+ Use either `--include-dirty` or `--in-place` (only fault target files must be
308
312
  clean there; test files may be dirty), optionally `--confirm 1` for a fast
309
313
  **provisional** signal — verdicts print with a `?`, evidence goes to
310
314
  `evidence-provisional.json`, and `baseline` refuses it; final pass with
@@ -493,12 +497,12 @@ detected base that does not resolve is a warning for `status` and `brief`
493
497
  # GitHub Actions
494
498
  - uses: actions/checkout@v4
495
499
  with: { fetch-depth: 0 }
496
- - uses: raccioly/testguard@v0.10.0
500
+ - uses: raccioly/testguard@v0.10.2
497
501
  with: { command: gate }
498
502
 
499
503
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
500
504
  include:
501
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.0/packaging/gitlab/testguard.gitlab-ci.yml'
505
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml'
502
506
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
503
507
  ```
504
508
 
@@ -127,9 +127,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
127
127
  | npm | `npm i -D testguard-cli` then `npx testguard probe` |
128
128
  | pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
129
129
  | Homebrew | `brew tap raccioly/tap && brew install testguard` |
130
- | GitHub Action | `uses: raccioly/testguard@v0.10.0` — see [`action.yml`](./action.yml) |
130
+ | GitHub Action | `uses: raccioly/testguard@v0.10.2` — see [`action.yml`](./action.yml) |
131
131
  | pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
132
- | GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.0/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
132
+ | GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
133
133
 
134
134
  Projects that set `min-release-age` in `.npmrc` cannot see a version published
135
135
  less than that many days ago (`ENOVERSIONS`); install that one with
@@ -279,8 +279,12 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
279
279
  because it fails when the claim is false.
280
280
 
281
281
  Practical loop: first pass `--no-escalate` (escalation re-runs the whole
282
- suite N times per survivor); iterate on one claim with `--claim <ID>` and
283
- either `--include-dirty` or `--in-place` (only fault target files must be
282
+ suite N times per survivor); iterate on one claim with `--claim <ID>`, or
283
+ select a batch with commas or repeated flags (`--claim A,B --claim C`).
284
+ Every occurrence is retained in first-seen order, duplicates are collapsed,
285
+ and partial human/JSON output names both the requested and probed counts, so
286
+ a green subset cannot masquerade as the selection the operator asked for.
287
+ Use either `--include-dirty` or `--in-place` (only fault target files must be
284
288
  clean there; test files may be dirty), optionally `--confirm 1` for a fast
285
289
  **provisional** signal — verdicts print with a `?`, evidence goes to
286
290
  `evidence-provisional.json`, and `baseline` refuses it; final pass with
@@ -469,12 +473,12 @@ detected base that does not resolve is a warning for `status` and `brief`
469
473
  # GitHub Actions
470
474
  - uses: actions/checkout@v4
471
475
  with: { fetch-depth: 0 }
472
- - uses: raccioly/testguard@v0.10.0
476
+ - uses: raccioly/testguard@v0.10.2
473
477
  with: { command: gate }
474
478
 
475
479
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
476
480
  include:
477
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.0/packaging/gitlab/testguard.gitlab-ci.yml'
481
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml'
478
482
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
479
483
  ```
480
484
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |---------|-----------|
7
- | 0.1.x | ✅ current |
7
+ | 0.10.x | ✅ current |
8
8
 
9
9
  ## Reporting a vulnerability
10
10
 
@@ -27,9 +27,11 @@ TestGuard is a local CLI with a deliberately small surface:
27
27
  scratch git worktrees under the system temp directory, which are removed
28
28
  after each run. With `--in-place`, the mutated files are restored — on
29
29
  success, on failure, and on `SIGINT`/`SIGTERM`.
30
- - **Publishing uses OIDC Trusted Publishing** to npm and PyPI. The release
31
- pipeline holds no long-lived tokens; npm provenance attestations are
32
- generated automatically.
30
+ - **Publishing uses OIDC Trusted Publishing** to npm and PyPI, so the release
31
+ pipeline holds no registry tokens and npm provenance attestations are
32
+ generated automatically. Homebrew publishing uses a dedicated SSH deploy
33
+ key scoped only to `raccioly/homebrew-tap`; it cannot access this repository
34
+ or any other repository.
33
35
 
34
36
  ## The claims file is code
35
37
 
@@ -46,7 +46,7 @@ inputs:
46
46
  version:
47
47
  description: 'testguard-cli version to run'
48
48
  required: false
49
- default: '0.10.0'
49
+ default: '0.10.2'
50
50
  node-version:
51
51
  description: 'Node.js version (20+)'
52
52
  required: false
@@ -144,7 +144,7 @@
144
144
  <div class="page">
145
145
  <div class="masthead">
146
146
  <div class="wordmark">Test<span class="mark">Guard</span></div>
147
- <div class="meta">Technical Brief · v0.10.0 · MIT</div>
147
+ <div class="meta">Technical Brief · v0.10.2 · MIT</div>
148
148
  </div>
149
149
 
150
150
  <h1>Your tests pass. That is not the same as your tests working.</h1>
@@ -85,7 +85,7 @@ other tools, and it must remain portable out of this repository.
85
85
  | Supported runners | vitest, jest (project runners); Playwright (per file) | They share a JSON report shape; Playwright is selected per file, not per project |
86
86
  | Schemas | JSON Schema 2020-12 | Readable without the tool; portable to other languages |
87
87
  | Distribution | npm, PyPI, Homebrew, GitHub Action, pre-commit, GitLab component | Meet teams in the pipeline they already run |
88
- | Publishing | OIDC Trusted Publishing | No long-lived tokens exist to leak or rotate |
88
+ | Publishing | OIDC for npm/PyPI; tap-scoped SSH deploy key for Homebrew | Registry credentials remain tokenless; the only stored publishing secret cannot access any repository except the tap |
89
89
 
90
90
  ## Configuration Files
91
91
 
@@ -101,7 +101,7 @@ appears here but not on disk, or on disk but not here, is drift.
101
101
  | `.pre-commit-hooks.yaml` | Hook definitions **we publish for consumers** of TestGuard — a different file for a different audience | consumers' `.pre-commit-config.yaml` |
102
102
  | `action.yml` | The GitHub Action surface; `version` input synced from `package.json` | consumers' workflows |
103
103
  | `packaging/gitlab/testguard.gitlab-ci.yml` | The GitLab component; tag and `TESTGUARD_VERSION` synced | consumers' pipelines |
104
- | `packaging/homebrew/testguard.rb` | The Homebrew formula; tarball URL synced | `raccioly/homebrew-tap` |
104
+ | `packaging/homebrew/testguard.rb` | The staged Homebrew formula; tarball URL and registry-derived hash synced, then copied byte-for-byte | `raccioly/homebrew-tap` |
105
105
  | `.docguard.json`, `.docguardignore` | Which canonical documents exist and which validators run | DocGuard, in CI and pre-commit |
106
106
  | `.npmrc`, `.npmignore`, `.gitattributes` | Packaging and line-ending hygiene | npm, git |
107
107
 
@@ -115,7 +115,7 @@ and no license server.
115
115
  | `ajv` (npm, bundled) | Validate every document against the spec | none — local | Install-time only; the install smoke gate catches a missing runtime dep |
116
116
  | `git` (system binary) | Worktree isolation, diffs, snapshots, patch ids | none — local | Precondition failure with a stated reason, exit 2 |
117
117
  | The project's test runner | Runs the defenders | none — local | Precondition failure, exit 2; never a verdict about a claim |
118
- | npm / PyPI registries | Distribution only | build time | A publish fails; the tool itself never calls them at run time |
118
+ | npm / PyPI registries and Homebrew tap | Distribution only | build time | A publish fails; the tool itself never calls them at run time |
119
119
 
120
120
  ## Infrastructure (IaC)
121
121
 
@@ -133,8 +133,8 @@ to every other surface and `--check` fails the build on drift.
133
133
  |---|---|---|
134
134
  | Pull request | `ci.yml` | Suite on Node 20/22/24, syntax check of every source, install-from-tarball smoke, Python wrapper import, fixture oracles for vitest and jest, Playwright job, change gate, removed-claim check, anchor/replacement preflight before self-probe |
135
135
  | Release gate | `ci.yml` | TestGuard probes its own claims; every fault must be killed |
136
- | Merge to main | `release.yml` | A version with no tag is tagged, released, and published to npm and PyPI over OIDC |
137
- | Recovery | `release.yml` | Hourly sweep; each registry is gated on its own state, so a partial publish is fixed by re-running |
136
+ | Merge to main | `release.yml` | A version with no tag is tagged, released, and published to npm and PyPI over OIDC; the verified formula is pushed to the live Homebrew tap with its repository-scoped deploy key |
137
+ | Recovery | `release.yml` | Hourly sweep; each registry and the formula hash are gated on their own state, so a partial publish is fixed by re-running |
138
138
 
139
139
  ## Diagrams
140
140
 
@@ -1,6 +1,6 @@
1
1
  # Requirements
2
2
 
3
- <!-- docguard:version 1.0.0 -->
3
+ <!-- docguard:version 1.1.0 -->
4
4
  <!-- docguard:status approved -->
5
5
  <!-- docguard:last-reviewed 2026-09-19 -->
6
6
  <!-- docguard:owner @raccioly -->
@@ -27,6 +27,7 @@
27
27
  | FR-13 | Replay historical fix commits to measure whether the suite would have caught them, and calibrate by fault class | P3 |
28
28
  | FR-14 | Serve the read-only operating loop over MCP so the loop survives a change of agent harness | P3 |
29
29
  | FR-15 | Validate every exact fault anchor and the syntax of supported in-memory replacements before starting a test runner (`claims --check-anchors`), without guessing repairs | P1 |
30
+ | FR-16 | Preserve every repeated or comma-separated `probe --claim` selection in first-seen order, collapse duplicates, and report requested versus probed scope without allowing an empty selection to pass | P1 |
30
31
 
31
32
  ## Non-Functional Requirements
32
33
 
@@ -101,6 +102,7 @@ fails the pull request naming the unclaimed file, and `status` reports
101
102
  | FR-13 | `src/replay/` | `test/replay.test.mjs`, `test/calibration.test.mjs`, `TG-REPLAY-FLAKY-IS-NEVER-CAUGHT`, `TG-CALIBRATION-COUNTS-NOCOVER-AS-A-MISS`, `TG-CALIBRATION-COUNTS-ONLY-BUGS` |
102
103
  | FR-14 | `src/mcp/` | `test/mcp.test.mjs` |
103
104
  | FR-15 | `src/claims/anchors.mjs`, `src/commands/claims.mjs`, `src/status/status.mjs` | `test/claims-anchors.test.mjs`, `test/status.test.mjs`, `TG-ANCHOR-PREFLIGHT-FAILS-FAST` |
105
+ | FR-16 | `src/cli.mjs`, `src/commands/probe.mjs`, `src/probe/probe.mjs`, `src/commands/scaffold.mjs`, `src/commands/admit.mjs` | `test/claim-selection.test.mjs`, `test/probe.fixture.test.mjs`, `TG-REPEATED-CLAIMS-ARE-ALL-PROBED` |
104
106
  | NFR-01 | `src/init/init.mjs`, runner resolution | `TG-INIT-HOOK-NO-NETWORK`, `TG-README-HOOK-MATCHES-THE-CODE` (every surface, not only the README), `TG-GITIGNORE-ADVICE-IS-ONE-LIST` |
105
107
  | NFR-02 | `package.json` | `npm run test:install` in CI |
106
108
  | NFR-03 | `src/probe/inject.mjs`, `src/probe/probe.mjs` | `TG-DIRTY-DEFENDERS-REFUSED`, `TG-IGNORED-DIRTY-RECORDED`, `TG-IN-PLACE-STILL-RAISES-A-MISSING-TARGET`, `TG-CONTROL-RESTORES-THE-SUBJECT` |
@@ -114,4 +116,5 @@ fails the pull request naming the unclaimed file, and `status` reports
114
116
 
115
117
  | Version | Date | Change | Author |
116
118
  |---|---|---|---|
119
+ | 1.1.0 | 2026-09-19 | Require lossless, visible partial claim selection | @raccioly |
117
120
  | 1.0.0 | 2026-09-18 | First canonical requirements, written against v0.6.0 | @raccioly |
@@ -2,9 +2,9 @@
2
2
 
3
3
  <!-- docguard:version 1.0.0 -->
4
4
  <!-- docguard:status approved -->
5
- <!-- docguard:last-reviewed 2026-09-18 -->
5
+ <!-- docguard:last-reviewed 2026-09-19 -->
6
6
  <!-- docguard:owner @raccioly -->
7
- <!-- docguard:quality negation-load off — a security posture is a list of things that must not happen: no network, no secrets, no tokens, no telemetry. Stating them positively would weaken them. -->
7
+ <!-- docguard:quality negation-load off — a security posture is a list of things that must not happen: no runtime network, no CLI secrets, no registry tokens, no telemetry. Stating them positively would weaken them. -->
8
8
  <!-- docguard:quality passive-voice off — threat and control statements name what is protected, not who protects it; the actor is the system throughout. -->
9
9
 
10
10
  > Canonical. Code that contradicts this document is drift.
@@ -27,8 +27,14 @@ happens in CI, never on a developer machine:
27
27
  | npm | OIDC Trusted Publishing | none |
28
28
  | PyPI | OIDC Trusted Publishing (environment `pypi`) | none |
29
29
  | GitHub releases | `GITHUB_TOKEN`, scoped to the run | none stored |
30
+ | Homebrew tap | SSH deploy key scoped to `raccioly/homebrew-tap` | `HOMEBREW_TAP_DEPLOY_KEY` |
30
31
 
31
- There are no tokens in this repository to rotate or leak.
32
+ npm and PyPI have no registry tokens to rotate or leak. Homebrew publishing
33
+ uses one stored credential: a dedicated Ed25519 deploy key whose public half
34
+ has write access only to `raccioly/homebrew-tap` and whose private half is the
35
+ `HOMEBREW_TAP_DEPLOY_KEY` Actions secret in this repository. It is not a
36
+ personal key and cannot access another repository. Rotate it by replacing both
37
+ the tap deploy key and the Actions secret.
32
38
 
33
39
  ## Authorization
34
40
 
@@ -53,8 +59,11 @@ guarantees are about **what the tool is allowed to touch**:
53
59
 
54
60
  ## Secrets Management
55
61
 
56
- TestGuard neither reads nor stores secrets. It has no credential file, no
57
- keychain access and no environment variable holding a secret.
62
+ The TestGuard CLI neither reads nor stores secrets. It has no credential file,
63
+ no keychain access and no environment variable holding a secret. The release
64
+ workflow is the deliberate exception: its Homebrew step receives the
65
+ tap-scoped deploy key only while it copies a registry-verified formula to the
66
+ distribution repository. npm and PyPI publishing remain OIDC-only.
58
67
 
59
68
  Two consequences worth stating for reviewers:
60
69
 
@@ -21,7 +21,7 @@ the test that defends it, and CI fails if any injected fault survives.
21
21
  | Conformance | `spec/conformance/` | Every valid example validates; every must-reject document is rejected for the defect its filename names | ~1 s |
22
22
  | Anchor preflight | `test/claims-anchors.test.mjs` | Exact anchors still locate, supported replacements parse, the check stays below one second, and CI reaches it before self-probe | <1 s |
23
23
  | Fixture acceptance | `test/probe.fixture.test.mjs`, `test/probe.jest.test.mjs`, `test/probe.playwright.test.mjs` | The whole pipeline reproduces a known-answer oracle, one per runner | 10–50 s each |
24
- | Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **13.4 min** cold (measured 2026-09-18 at `cf5a083`: 96 faults, 576 defender runs, 804 s, `--serial`, idle machine); seconds when verdicts are reused, which is why CI restores the previous evidence from cache. Budgeted at 950 s in `testguard.cost-budget.json` and **gated** in CI — the figure above went 9.6 → 23.6 min across one merged change while `--cost` printed it into a log nobody read |
24
+ | Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **20.2 min** cold (measured by the v0.10.1 release job on 2026-09-19 at `70e9d61`: 154 faults, 924 defender runs, 1210 s); seconds when verdicts are reused, which is why PR CI restores previous evidence from cache. Budgeted at 1440 s in `testguard.cost-budget.json` for the additional four-fault live-tap claim and **gated** in CI — cost growth is reviewed instead of disappearing into a passing log |
25
25
  | Install smoke | `.github/scripts/install-smoke.mjs` | The **packed tarball** runs with production dependencies only | ~20 s |
26
26
  | Runtime budget | `ci.yml` | The suite has not silently started walking the wrong tree | gate, not a test |
27
27
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "testguard-cli",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "testguard-cli",
9
- "version": "0.10.0",
9
+ "version": "0.10.2",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "ajv": "8.20.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testguard-cli",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -3,7 +3,7 @@
3
3
  # Include it from a project's .gitlab-ci.yml, pinned to a release:
4
4
  #
5
5
  # include:
6
- # - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.0/packaging/gitlab/testguard.gitlab-ci.yml'
6
+ # - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.2/packaging/gitlab/testguard.gitlab-ci.yml'
7
7
  # inputs:
8
8
  # dir: backend # where testguard.claims.json lives (default ".")
9
9
  # post_note: true # post the brief as a merge-request note (needs TESTGUARD_GITLAB_TOKEN, api scope)
@@ -29,7 +29,7 @@ spec:
29
29
  inputs:
30
30
  version:
31
31
  description: testguard-cli version to run
32
- default: "0.10.0"
32
+ default: "0.10.2"
33
33
  dir:
34
34
  description: directory whose test runner is invoked (where testguard.claims.json lives)
35
35
  default: "."
@@ -15,8 +15,8 @@
15
15
  class Testguard < Formula
16
16
  desc "Proves a test suite defends the claims a project makes"
17
17
  homepage "https://github.com/raccioly/testguard"
18
- url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.10.0.tgz"
19
- sha256 "ab42501d45ba0a71b534e12aa86da2a9fc2f664a1da4f38a4e436dfd30c02c3c"
18
+ url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.10.2.tgz"
19
+ sha256 "dae61a90d2eb57f116597f3bc7734d3bb9759cdcfbf00a6447b9607b87933e88"
20
20
  license "MIT"
21
21
 
22
22
  depends_on "node"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "testguard-cli"
7
- version = "0.10.0"
7
+ version = "0.10.2"
8
8
  description = "Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest. Ships the Node.js CLI (requires Node.js 20+)."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -436,6 +436,16 @@ new anchor is a semantic edit to the claim and must be reviewed and re-probed.
436
436
  `unclaimed-changes`, when a change reference is known). Its next action is
437
437
  `repair-fault`, naming the first fault and `testguard claims --check-anchors`.
438
438
 
439
+ ## Partial probe scope
440
+
441
+ Every `--claim` occurrence contributes to the selection. Comma-separated ids
442
+ inside each occurrence are flattened in first-seen order and duplicates are
443
+ collapsed. An explicitly empty selection is a usage error, never a successful
444
+ zero-record probe. Partial human output and the `probe --json` run wrapper name
445
+ the requested and actually probed claim ids and counts. Commands whose
446
+ `--claim` is singular (`scaffold` and `admit`) reject repeated or comma-separated
447
+ values instead of silently choosing one.
448
+
439
449
  ## Severity floor
440
450
 
441
451
  `--severity <level>` gates only findings whose claim severity is at or above
@@ -18,6 +18,20 @@
18
18
  }
19
19
  },
20
20
  "provisional": false,
21
+ "run": {
22
+ "id": "run-20260917T020000",
23
+ "evidence": ".testguard/evidence-partial.json",
24
+ "provisional": false,
25
+ "records": 4,
26
+ "newSinceBaseline": 1,
27
+ "exitCode": 1,
28
+ "scope": {
29
+ "requestedClaims": ["REDACT-002", "REDACT-001"],
30
+ "requestedCount": 2,
31
+ "probedClaims": ["REDACT-002", "REDACT-001"],
32
+ "probedCount": 2
33
+ }
34
+ },
21
35
  "surface": {
22
36
  "sourceModules": 3,
23
37
  "claimedModules": 2,