testguard-cli 0.11.0__tar.gz → 0.13.0__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 (331) hide show
  1. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.docguard.json +2 -1
  2. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/CHANGELOG.md +108 -0
  3. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/PKG-INFO +142 -5
  4. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/README.md +141 -4
  5. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/action.yml +1 -1
  6. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs/testguard-explained.html +172 -11
  7. testguard_cli-0.13.0/docs/testguard-explained.pdf +0 -0
  8. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/ARCHITECTURE.md +2 -1
  9. testguard_cli-0.13.0/docs-canonical/PRIOR-ART.md +160 -0
  10. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/package-lock.json +2 -2
  11. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/package.json +1 -1
  12. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
  13. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/packaging/homebrew/testguard.rb +2 -2
  14. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/pyproject.toml +1 -1
  15. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/lib/validate.mjs +49 -1
  16. testguard_cli-0.13.0/spec/schemas/concerns.schema.json +41 -0
  17. testguard_cli-0.13.0/spec/schemas/sweep.schema.json +366 -0
  18. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/cli.mjs +12 -2
  19. testguard_cli-0.13.0/src/commands/concerns.mjs +32 -0
  20. testguard_cli-0.13.0/src/commands/sweep.mjs +109 -0
  21. testguard_cli-0.13.0/src/supply/concerns.mjs +162 -0
  22. testguard_cli-0.13.0/src/supply/feedback.mjs +146 -0
  23. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/supply/persistence.mjs +53 -0
  24. testguard_cli-0.13.0/src/supply/savepath.mjs +216 -0
  25. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/sweep/sweep.mjs +111 -7
  26. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/util/glob.mjs +8 -0
  27. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/glob.test.mjs +5 -2
  28. testguard_cli-0.13.0/test/supply-concerns.test.mjs +124 -0
  29. testguard_cli-0.13.0/test/supply-feedback.test.mjs +134 -0
  30. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/supply-persistence.test.mjs +41 -1
  31. testguard_cli-0.13.0/test/supply-savepath.test.mjs +165 -0
  32. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/sweep.test.mjs +68 -3
  33. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard.claims.json +229 -1
  34. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard.cost-budget.json +1 -1
  35. testguard_cli-0.11.0/docs/testguard-explained.pdf +0 -0
  36. testguard_cli-0.11.0/spec/schemas/sweep.schema.json +0 -127
  37. testguard_cli-0.11.0/src/commands/sweep.mjs +0 -74
  38. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.agents/skills/testguard/SKILL.md +0 -0
  39. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.codex/hooks.json +0 -0
  40. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.docguardignore +0 -0
  41. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.gitattributes +0 -0
  42. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  43. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  44. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  45. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/dependabot.yml +0 -0
  46. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/approve-held-runs.sh +0 -0
  47. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/build-one-pager.mjs +0 -0
  48. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/check-cost-budget.mjs +0 -0
  49. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/check-python-fixture.mjs +0 -0
  50. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/install-smoke.mjs +0 -0
  51. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/publish-homebrew-tap.sh +0 -0
  52. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/sync-release-version.mjs +0 -0
  53. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/auto-merge.yml +0 -0
  54. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/ci.yml +0 -0
  55. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/release.yml +0 -0
  56. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/scheduled-release.yml +0 -0
  57. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/supply-chain.yml +0 -0
  58. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.gitignore +0 -0
  59. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.npmignore +0 -0
  60. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.npmrc +0 -0
  61. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.pre-commit-config.yaml +0 -0
  62. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.pre-commit-hooks.yaml +0 -0
  63. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/AGENTS.md +0 -0
  64. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/CLAUDE.md +0 -0
  65. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/CODE_OF_CONDUCT.md +0 -0
  66. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/CONTRIBUTING.md +0 -0
  67. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/DRIFT-LOG.md +0 -0
  68. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/LICENSE +0 -0
  69. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/PRIVACY.md +0 -0
  70. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/SECURITY.md +0 -0
  71. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/SUPPORT.md +0 -0
  72. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/bench/README.md +0 -0
  73. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/cli/testguard.mjs +0 -0
  74. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/DATA-MODEL.md +0 -0
  75. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/ENVIRONMENT.md +0 -0
  76. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/REQUIREMENTS.md +0 -0
  77. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/SECURITY.md +0 -0
  78. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/TEST-SPEC.md +0 -0
  79. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/.gitignore +0 -0
  80. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/README.md +0 -0
  81. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/expected.json +0 -0
  82. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/package.json +0 -0
  83. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/src/export.mjs +0 -0
  84. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/src/redact.mjs +0 -0
  85. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
  86. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/test/flaky.test.mjs +0 -0
  87. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/test/redact.test.mjs +0 -0
  88. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/testguard.claims.json +0 -0
  89. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/vitest.config.mjs +0 -0
  90. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/.gitignore +0 -0
  91. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/README.md +0 -0
  92. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/expected.json +0 -0
  93. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/jest.config.js +0 -0
  94. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/package.json +0 -0
  95. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/src/export.js +0 -0
  96. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/src/redact.js +0 -0
  97. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
  98. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
  99. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/test/redact.test.js +0 -0
  100. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/testguard.claims.json +0 -0
  101. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/.gitignore +0 -0
  102. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/README.md +0 -0
  103. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
  104. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
  105. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/expected.json +0 -0
  106. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/package-lock.json +0 -0
  107. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/package.json +0 -0
  108. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
  109. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
  110. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
  111. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
  112. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
  113. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
  114. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/.gitignore +0 -0
  115. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/README.md +0 -0
  116. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/__init__.py +0 -0
  117. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/export.py +0 -0
  118. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/orphan.py +0 -0
  119. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/pipeline.py +0 -0
  120. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/redact.py +0 -0
  121. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/expected.json +0 -0
  122. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/testguard.claims.json +0 -0
  123. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
  124. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
  125. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/tests/test_patched.py +0 -0
  126. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/tests/test_redact.py +0 -0
  127. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/GATE-SEMANTICS.md +0 -0
  128. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/README.md +0 -0
  129. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/baseline.json +0 -0
  130. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/brief.json +0 -0
  131. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/calibration-websec.json +0 -0
  132. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/calibration.json +0 -0
  133. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/claims-methods.json +0 -0
  134. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/claims.json +0 -0
  135. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
  136. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-negative-control.json +0 -0
  137. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-provisional.json +0 -0
  138. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-python.json +0 -0
  139. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-scan-method.json +0 -0
  140. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence.json +0 -0
  141. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/gate.json +0 -0
  142. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/ignore.json +0 -0
  143. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/replay.json +0 -0
  144. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/status.json +0 -0
  145. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
  146. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/baseline.zero-count.json +0 -0
  147. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
  148. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
  149. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
  150. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
  151. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
  152. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
  153. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
  154. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
  155. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
  156. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
  157. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.absolute-path.json +0 -0
  158. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
  159. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
  160. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
  161. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
  162. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.noop-fault.json +0 -0
  163. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
  164. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
  165. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
  166. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
  167. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
  168. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
  169. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
  170. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
  171. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
  172. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
  173. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
  174. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
  175. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
  176. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
  177. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
  178. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
  179. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
  180. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
  181. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
  182. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
  183. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
  184. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
  185. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
  186. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
  187. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
  188. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
  189. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
  190. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
  191. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
  192. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
  193. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
  194. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/ignore.short-reason.json +0 -0
  195. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
  196. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
  197. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.claim-without-file.json +0 -0
  198. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
  199. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
  200. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
  201. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
  202. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
  203. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
  204. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/schemas.test.mjs +0 -0
  205. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/lib/fingerprint.mjs +0 -0
  206. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/lib/wilson.mjs +0 -0
  207. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/baseline.schema.json +0 -0
  208. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/brief.schema.json +0 -0
  209. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/calibration.schema.json +0 -0
  210. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/claims.schema.json +0 -0
  211. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/common.schema.json +0 -0
  212. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/evidence.schema.json +0 -0
  213. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/gate.schema.json +0 -0
  214. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/ignore.schema.json +0 -0
  215. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/replay.schema.json +0 -0
  216. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/status.schema.json +0 -0
  217. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/admit/admit.mjs +0 -0
  218. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/baseline/baseline.mjs +0 -0
  219. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/brief/brief.mjs +0 -0
  220. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/claims/anchors.mjs +0 -0
  221. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/claims/annotations.mjs +0 -0
  222. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/claims/load.mjs +0 -0
  223. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/claims/removed.mjs +0 -0
  224. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/admit.mjs +0 -0
  225. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/baseline.mjs +0 -0
  226. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/brief.mjs +0 -0
  227. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/claims.mjs +0 -0
  228. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/gate.mjs +0 -0
  229. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/init.mjs +0 -0
  230. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/mcp.mjs +0 -0
  231. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/probe.mjs +0 -0
  232. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/replay.mjs +0 -0
  233. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/scaffold.mjs +0 -0
  234. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/status.mjs +0 -0
  235. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/evidence/writer.mjs +0 -0
  236. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/gate/changed.mjs +0 -0
  237. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/git.mjs +0 -0
  238. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/init/init.mjs +0 -0
  239. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/init/templates/SKILL.md +0 -0
  240. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/mcp/server.mjs +0 -0
  241. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/mcp/tools.mjs +0 -0
  242. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/attribution.mjs +0 -0
  243. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/classify.mjs +0 -0
  244. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/contention.mjs +0 -0
  245. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/cost.mjs +0 -0
  246. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/discover.mjs +0 -0
  247. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/independence.mjs +0 -0
  248. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/inject.mjs +0 -0
  249. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/mocks.mjs +0 -0
  250. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/probe.mjs +0 -0
  251. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/progress.mjs +0 -0
  252. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/pyimports.mjs +0 -0
  253. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/rank.mjs +0 -0
  254. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/index.mjs +0 -0
  255. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/jest.mjs +0 -0
  256. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/playwright.mjs +0 -0
  257. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
  258. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/python/_testguard_report.py +0 -0
  259. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
  260. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/python.mjs +0 -0
  261. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/shared.mjs +0 -0
  262. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/vitest.mjs +0 -0
  263. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/worktree.mjs +0 -0
  264. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/render.mjs +0 -0
  265. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/replay/label.mjs +0 -0
  266. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/replay/replay.mjs +0 -0
  267. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/scaffold/producers.mjs +0 -0
  268. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/scaffold/producers.python.mjs +0 -0
  269. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/scaffold/scaffold.mjs +0 -0
  270. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/status/status.mjs +0 -0
  271. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/status/surface.mjs +0 -0
  272. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/supply/select.mjs +0 -0
  273. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/util/hash.mjs +0 -0
  274. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/admit.test.mjs +0 -0
  275. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/annotations.test.mjs +0 -0
  276. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/attribution.test.mjs +0 -0
  277. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/baseline.test.mjs +0 -0
  278. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/brief.test.mjs +0 -0
  279. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/calibration.test.mjs +0 -0
  280. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/claim-selection.test.mjs +0 -0
  281. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/claims-anchors.test.mjs +0 -0
  282. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/claims-since.test.mjs +0 -0
  283. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/classify.test.mjs +0 -0
  284. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/contention.test.mjs +0 -0
  285. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/cost.test.mjs +0 -0
  286. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/discover.test.mjs +0 -0
  287. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/gate.test.mjs +0 -0
  288. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/gitlab-template.test.mjs +0 -0
  289. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/independence.test.mjs +0 -0
  290. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/init.test.mjs +0 -0
  291. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/inject.test.mjs +0 -0
  292. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/label.test.mjs +0 -0
  293. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/mcp.test.mjs +0 -0
  294. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/mocks.test.mjs +0 -0
  295. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/negative-control.test.mjs +0 -0
  296. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/one-pager.test.mjs +0 -0
  297. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe-error.test.mjs +0 -0
  298. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe-preconditions.test.mjs +0 -0
  299. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe.fixture.test.mjs +0 -0
  300. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe.jest.test.mjs +0 -0
  301. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe.playwright.test.mjs +0 -0
  302. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe.python.test.mjs +0 -0
  303. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/progress.test.mjs +0 -0
  304. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/pyimports.test.mjs +0 -0
  305. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/rank-aliases.test.mjs +0 -0
  306. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/release-approval.test.mjs +0 -0
  307. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/release-sync.test.mjs +0 -0
  308. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/replay-verdict.test.mjs +0 -0
  309. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/replay.test.mjs +0 -0
  310. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-argv.test.mjs +0 -0
  311. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-command.test.mjs +0 -0
  312. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-playwright.test.mjs +0 -0
  313. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-python.test.mjs +0 -0
  314. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-vitest.test.mjs +0 -0
  315. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/samples/playwright-report.json +0 -0
  316. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/samples/python-report-collect-error.json +0 -0
  317. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/samples/python-report.json +0 -0
  318. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/scaffold.python.test.mjs +0 -0
  319. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/scaffold.test.mjs +0 -0
  320. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/snapshot.test.mjs +0 -0
  321. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/status.test.mjs +0 -0
  322. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/supply-select.test.mjs +0 -0
  323. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/surface.test.mjs +0 -0
  324. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/sweep.fixture.test.mjs +0 -0
  325. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/vitest-config.test.mjs +0 -0
  326. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/worktree.test.mjs +0 -0
  327. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/writer.test.mjs +0 -0
  328. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard.ignore.json +0 -0
  329. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard_cli/__init__.py +0 -0
  330. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard_cli/wrapper.py +0 -0
  331. {testguard_cli-0.11.0 → testguard_cli-0.13.0}/vitest.config.mjs +0 -0
@@ -17,7 +17,8 @@
17
17
  "docs-canonical/SECURITY.md",
18
18
  "docs-canonical/TEST-SPEC.md",
19
19
  "docs-canonical/ENVIRONMENT.md",
20
- "docs-canonical/REQUIREMENTS.md"
20
+ "docs-canonical/REQUIREMENTS.md",
21
+ "docs-canonical/PRIOR-ART.md"
21
22
  ]
22
23
  },
23
24
  "validators": {
@@ -7,6 +7,114 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.13.0] - 2026-09-22
11
+
12
+ Automated weekly release — everything merged since `v0.12.0`.
13
+
14
+ ### Changed
15
+
16
+ - chore(homebrew): sha256 for v0.12.0 (#159)
17
+ - feat: persistence provability, and an ordering that learns from this project's own verdicts (#157)
18
+
19
+
20
+ ## [0.12.0] - 2026-09-22
21
+
22
+ Automated weekly release — everything merged since `v0.11.0`.
23
+
24
+ ### Changed
25
+
26
+ - feat(sweep): --save-paths, and the denominator a save report is read against (#156)
27
+ - docs: make the brief describe what the tool actually does, and record the prior art (#155)
28
+ - chore(homebrew): sha256 for v0.11.0 (#153)
29
+
30
+
31
+ ### Added
32
+
33
+ - **Concerns** — one sentence a human writes that generates faults across a
34
+ whole surface, the rung between writing nothing and writing a claim per
35
+ behaviour. A concern says *where* to look and *which shapes of break* are
36
+ relevant: `"every write persists the data it was given"` targets the write
37
+ sites and applies the payload producers. `testguard concerns` lists them,
38
+ `sweep --concern <ID>` aims a sweep by one, and `--save-paths` is now sugar
39
+ for the built-in `SAVE-PERSISTS`.
40
+ - Two built-ins ship, and only two: an authorization concern would need a
41
+ heuristic for "which files check permissions", and a guess there is the
42
+ non-actionable noise that gets a check switched off. A project that knows
43
+ its own auth layer says so with a `glob` target in three lines.
44
+ - A project concern with the same id **replaces** a built-in, so a team can
45
+ retune `SAVE-PERSISTS` for an ORM this tool does not recognise without
46
+ forking anything. The replacement is reported, never silent.
47
+ - `urn:claimspec:v1:concerns`, with rules that refuse a glob target naming no
48
+ globs (it would match nothing and report clean) and an empty fault-class
49
+ list (a typo for "every class", every time).
50
+ - **No model is required, deliberately.** A concern's useful core is a named
51
+ scope plus a producer selection, which the mechanical producers already
52
+ satisfy — so every verifying command stays offline and NFR-01 is unchanged.
53
+ An LLM can later propose faults a line-oriented producer cannot see, as an
54
+ additional source for a concern that already works.
55
+
56
+ ### Added
57
+
58
+ - A save-path sweep now reports **what its defenders could prove**, before it
59
+ reports what they did: per file, whether every defender mocks the persistence
60
+ layer (call shape only), at least one does not (a real write is possible), or
61
+ there is no defender at all. When nothing is unmocked the report says so
62
+ outright — a clean probe there is not evidence of persistence, it is evidence
63
+ that nothing could have measured it. Measured on a real AI-authored
64
+ application: of 37 files that write to storage, 32 were defended only by
65
+ mocking tests.
66
+ - `scope.provability` on the sweep document, with a validator rule that every
67
+ target is classified exactly once.
68
+ - The candidate ordering now **learns from this project's own probed verdicts**
69
+ instead of only the shipped productivity prior. Every evidence record already
70
+ carries a `faultClass` and a `verdict`, so "how often does a fault of this
71
+ class survive *here*" is a tally rather than a new thing to capture. A sweep
72
+ persists its own evidence to `.testguard/sweep-evidence.json` — beside the
73
+ canonical one, never replacing it — so the next run learns from
74
+ machine-proposed faults, the same distribution the ranker orders. The sweep
75
+ document records `ordering.observed` and `ordering.sources`, because an
76
+ ordering nobody can trace is a number nobody should trust.
77
+
78
+ ### Added
79
+
80
+ - `sweep --save-paths` — sweep every file that writes to storage instead of the
81
+ diff, and report the surface as the denominator the findings are read
82
+ against. A diff cannot answer how many save paths exist, and a clean report
83
+ over an unstated denominator is a sample of unknown size. The sweep document
84
+ gains `scope.mode`, `scope.writeSites` and `scope.payloadFields`, and the
85
+ validator refuses a save-paths sweep that omits its surface.
86
+ - `src/supply/savepath.mjs` — the write-site enumerator: which calls put data
87
+ into storage, and which payload keys sit on their own line (the ones a
88
+ `field-dropped` fault can anchor to). Measured on a real AI-authored
89
+ application: 119 writes across 37 files carrying 226 payload fields.
90
+
91
+ ### Fixed
92
+
93
+ - `walk()` skipped `dist` and `coverage` but not `.next`, `build`, `out`,
94
+ `.svelte-kit`, `.turbo` and their siblings, while the claim above it already
95
+ promised that a build's test files cannot be picked up. A scan of a real
96
+ Next.js application counted 104 files instead of 32, because `.next/` carries
97
+ compiled copies of the application — its writes and its tests. The claim was
98
+ right; the list was short.
99
+
100
+ ### Added
101
+
102
+ - `docs-canonical/PRIOR-ART.md` — the canonical record of what was taken from
103
+ Google's and Meta's mutation-testing programmes, what was deliberately not
104
+ taken, the measured reason no model decides a verdict, and the known gaps in
105
+ what was borrowed. Registered with DocGuard.
106
+ - README prior-art section pointing at it.
107
+
108
+ ### Changed
109
+
110
+ - The technical brief now describes what the tool actually does. A new
111
+ *Starting from zero* page carries Field report C (the persistence-path
112
+ measurement: 12 of 26 faults survived a green suite, all of them a dropped
113
+ payload field) and the claim-supply ladder; prior art names Google and Meta
114
+ and what each solved; the decision-model result is stated as a measurement
115
+ rather than a design assertion; and a *what it deliberately is not* section
116
+ closes it. `sweep` added to the verb table. Six pages to eight, re-rendered.
117
+
10
118
  ## [0.11.0] - 2026-09-21
11
119
 
12
120
  Automated weekly release — everything merged since `v0.10.4`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: testguard-cli
3
- Version: 0.11.0
3
+ Version: 0.13.0
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.11.0` — see [`action.yml`](./action.yml) |
154
+ | GitHub Action | `uses: raccioly/testguard@v0.13.0` — 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.11.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.13.0/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
@@ -475,6 +475,14 @@ Three rules make it safe to run on a repository that has never seen this tool:
475
475
  is reported and never gates — a tool that fails because its own guess was bad
476
476
  is a tool people switch off.
477
477
 
478
+ The ordering **learns from your own runs**. Every evidence record already
479
+ carries a fault class and a verdict, so "how often does a fault of this class
480
+ survive here" is a tally, not a new thing to collect — a sweep persists its
481
+ evidence and the next one reads it. A project with no evidence falls back to
482
+ the shipped prior and says so; one with its own records overrides it in
483
+ proportion to how many it has. The report names which, because an ordering
484
+ nobody can trace is a number nobody should trust.
485
+
478
486
  The cap and the ordering are Google's: their mutation service surfaces at most
479
487
  7 × |files| mutants per change and orders candidates on the measured
480
488
  productivity of their operator in similar context, which took their productive
@@ -490,6 +498,109 @@ A sweep is weaker evidence than a probe, and says so. Nobody stated that the
490
498
  behaviour mattered. It is stronger than nothing, which is what a repository
491
499
  with no claims has — and the survivors worth defending become the first claims.
492
500
 
501
+ #### Concerns: one sentence, two hundred screens
502
+
503
+ A claim names one promise precisely, which is why a repository with two hundred
504
+ screens never finishes writing them. A **concern** names a *kind* of promise and
505
+ says where to look for it:
506
+
507
+ ```bash
508
+ npx testguard-cli concerns # what this project can be swept by
509
+ npx testguard-cli sweep --concern ADMIN-GUARDS # aim a sweep at one
510
+ ```
511
+
512
+ ```json
513
+ {
514
+ "schemaVersion": 1,
515
+ "concerns": [
516
+ {
517
+ "id": "ADMIN-GUARDS",
518
+ "statement": "Every admin route refuses a caller without the admin role.",
519
+ "severity": "critical",
520
+ "targets": { "kind": "glob", "globs": ["src/app/api/admin/**"] },
521
+ "faultClasses": ["guard-removed", "condition-forced", "return-altered"]
522
+ }
523
+ ]
524
+ }
525
+ ```
526
+
527
+ Five lines, and the sweep reports:
528
+
529
+ ```
530
+ concern ADMIN-GUARDS matches 24 files. Swept 23.
531
+ 158 further proposals were outside this concern's fault classes.
532
+
533
+ SURVIVED src/app/api/admin/claims/[id]/route.ts
534
+ [line 41] Guard never triggers: `if (!membership || (membership.role !== 'owner'
535
+ && membership.role !== 'manager'))` becomes `if (false)`.
536
+ ```
537
+
538
+ The idea is Meta's: ACH has an engineer describe an area of concern in plain
539
+ text and gates every generated step by execution. What is taken here is the
540
+ **unit** — the concern as the thing a human writes — not their generator, and
541
+ **no model is required**. A concern's useful core is a named scope plus a
542
+ producer selection, which the mechanical producers already satisfy, so every
543
+ verifying command stays offline.
544
+
545
+ Two concerns ship built in, and only two. An authorization concern would need a
546
+ heuristic for *"which files check permissions"*, and a guess there is exactly
547
+ the noise that gets a check switched off — so `SAVE-PERSISTS` and
548
+ `CHANGED-CODE` are the defaults, and a project that knows its own auth layer
549
+ says so in three lines. Declaring a concern with a built-in's id replaces it,
550
+ and the replacement is reported rather than silent.
551
+
552
+ A concern is **not** a claim and never becomes one by itself. It says where to
553
+ look; the probe says what it found; a human states the sentence worth defending.
554
+
555
+ #### Sweeping the save surface instead of the diff
556
+
557
+ A diff cannot answer *"when the user clicks save, does it actually save?"* — it
558
+ only knows what changed today. `--save-paths` points the same sweep at every
559
+ file that writes to storage:
560
+
561
+ ```bash
562
+ npx testguard-cli sweep --save-paths --cap 20
563
+ ```
564
+
565
+ ```
566
+ 119 writes to storage across 37 files, carrying 226 payload fields. Swept 36.
567
+ proposed 762 faults, probed 20 (cap 20), deferred 742.
568
+
569
+ SURVIVED src/app/actions/register.ts [write path]
570
+ [line 59] Field dropped: `password_hash` is no longer written.
571
+ src/tests/actions/register.test.ts mocks @/lib/prisma; 1 exact, 8 partial,
572
+ 31 argument-free call assertions in the file. A field dropped from the write
573
+ payload fails only against an assertion that names that field exactly.
574
+ ```
575
+
576
+ The first line is the point. A report that lists findings without saying how
577
+ much surface it looked at invites you to assume the rest is fine, so the
578
+ denominator comes first and the document carries `writeSites` and
579
+ `payloadFields` whether or not anything survived. Measured on a real
580
+ AI-authored application: **12 of 26 probed persistence faults survived a fully
581
+ green suite, and all 12 were a dropped payload field.**
582
+
583
+ It also states, before any verdict, **what the defenders could prove at all**:
584
+
585
+ ```
586
+ of those 37 files: 32 defended only by tests that mock the persistence layer,
587
+ 1 with an unmocked defender, 4 with no defender at all.
588
+ ```
589
+
590
+ That line is the read-back answer. A test that replaced the database can prove
591
+ the *call shape* and nothing beyond it — a `where` that matches no rows, a
592
+ rolled-back transaction and a rejected constraint all pass against a mock that
593
+ recorded the arguments and returned a plausible object. So rather than pretend
594
+ to measure persistence, TestGuard states the limit, the same way `nocover` says
595
+ "no test imports this" instead of guessing.
596
+
597
+ When every defender mocks the layer, the report says so outright: *nothing in
598
+ this suite can prove a write reached storage*. A clean probe there is not
599
+ evidence of persistence — it is evidence that nothing could have measured it.
600
+
601
+ `unmocked` is deliberately weaker than "proves persistence": a test that does
602
+ not replace the database may simply never reach it. Possible is the honest word.
603
+
493
604
  ### Every change needs a claim
494
605
 
495
606
  `probe` can only verify claims that exist. Every escaped defect in the field
@@ -571,12 +682,12 @@ detected base that does not resolve is a warning for `status` and `brief`
571
682
  # GitHub Actions
572
683
  - uses: actions/checkout@v4
573
684
  with: { fetch-depth: 0 }
574
- - uses: raccioly/testguard@v0.11.0
685
+ - uses: raccioly/testguard@v0.13.0
575
686
  with: { command: gate }
576
687
 
577
688
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
578
689
  include:
579
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.11.0/packaging/gitlab/testguard.gitlab-ci.yml'
690
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.13.0/packaging/gitlab/testguard.gitlab-ci.yml'
580
691
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
581
692
  ```
582
693
 
@@ -604,6 +715,32 @@ talks to the network; only the job posts, and only when told to.
604
715
  toward green. The one pass is `killed`; everything else gates.
605
716
  - **One exact-pinned runtime dependency** (`ajv`), Node ≥ 20, MIT.
606
717
 
718
+ ### Prior art, and what was taken from where
719
+
720
+ Breaking code to test the tests is mutation testing, and it dates to the 1970s.
721
+ Two industrial programmes solved the parts that make it usable, and this tool
722
+ takes from both.
723
+
724
+ **Google** made it affordable: mutate only *changed* lines, cap what is
725
+ surfaced at seven per file, and order candidates by the measured productivity
726
+ of the operator in similar context — developer feedback took their productive
727
+ rate from 15% to 89%
728
+ ([Petrović et al., 2021](https://arxiv.org/abs/2102.11378)). `sweep`'s diff
729
+ scope, its cap and its ordering are theirs; the productivity prior here is
730
+ measured on real probed faults rather than assumed.
731
+
732
+ **Meta** made it specific: an engineer writes a *concern* in plain text, a
733
+ model drafts faults for it, and every step is gated by execution before a
734
+ human ever sees it — 73% of the resulting tests were accepted
735
+ ([Foster et al., FSE 2025](https://arxiv.org/abs/2501.12862)). The rule that
736
+ nothing a model proposes reaches the claims file without passing `probe` and a
737
+ human keep is theirs.
738
+
739
+ What neither does is bind a fault to a **stated promise**, which is what this
740
+ tool is for. The full record — including what was deliberately *not* taken, the
741
+ measured reason no model decides a verdict, and the gaps in what we borrowed —
742
+ is in [`docs-canonical/PRIOR-ART.md`](docs-canonical/PRIOR-ART.md).
743
+
607
744
  ### What TestGuard is not
608
745
 
609
746
  - **Not a test generator.** It judges a test the agent wrote (`admit`); the
@@ -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.11.0` — see [`action.yml`](./action.yml) |
130
+ | GitHub Action | `uses: raccioly/testguard@v0.13.0` — 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.11.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.13.0/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
@@ -451,6 +451,14 @@ Three rules make it safe to run on a repository that has never seen this tool:
451
451
  is reported and never gates — a tool that fails because its own guess was bad
452
452
  is a tool people switch off.
453
453
 
454
+ The ordering **learns from your own runs**. Every evidence record already
455
+ carries a fault class and a verdict, so "how often does a fault of this class
456
+ survive here" is a tally, not a new thing to collect — a sweep persists its
457
+ evidence and the next one reads it. A project with no evidence falls back to
458
+ the shipped prior and says so; one with its own records overrides it in
459
+ proportion to how many it has. The report names which, because an ordering
460
+ nobody can trace is a number nobody should trust.
461
+
454
462
  The cap and the ordering are Google's: their mutation service surfaces at most
455
463
  7 × |files| mutants per change and orders candidates on the measured
456
464
  productivity of their operator in similar context, which took their productive
@@ -466,6 +474,109 @@ A sweep is weaker evidence than a probe, and says so. Nobody stated that the
466
474
  behaviour mattered. It is stronger than nothing, which is what a repository
467
475
  with no claims has — and the survivors worth defending become the first claims.
468
476
 
477
+ #### Concerns: one sentence, two hundred screens
478
+
479
+ A claim names one promise precisely, which is why a repository with two hundred
480
+ screens never finishes writing them. A **concern** names a *kind* of promise and
481
+ says where to look for it:
482
+
483
+ ```bash
484
+ npx testguard-cli concerns # what this project can be swept by
485
+ npx testguard-cli sweep --concern ADMIN-GUARDS # aim a sweep at one
486
+ ```
487
+
488
+ ```json
489
+ {
490
+ "schemaVersion": 1,
491
+ "concerns": [
492
+ {
493
+ "id": "ADMIN-GUARDS",
494
+ "statement": "Every admin route refuses a caller without the admin role.",
495
+ "severity": "critical",
496
+ "targets": { "kind": "glob", "globs": ["src/app/api/admin/**"] },
497
+ "faultClasses": ["guard-removed", "condition-forced", "return-altered"]
498
+ }
499
+ ]
500
+ }
501
+ ```
502
+
503
+ Five lines, and the sweep reports:
504
+
505
+ ```
506
+ concern ADMIN-GUARDS matches 24 files. Swept 23.
507
+ 158 further proposals were outside this concern's fault classes.
508
+
509
+ SURVIVED src/app/api/admin/claims/[id]/route.ts
510
+ [line 41] Guard never triggers: `if (!membership || (membership.role !== 'owner'
511
+ && membership.role !== 'manager'))` becomes `if (false)`.
512
+ ```
513
+
514
+ The idea is Meta's: ACH has an engineer describe an area of concern in plain
515
+ text and gates every generated step by execution. What is taken here is the
516
+ **unit** — the concern as the thing a human writes — not their generator, and
517
+ **no model is required**. A concern's useful core is a named scope plus a
518
+ producer selection, which the mechanical producers already satisfy, so every
519
+ verifying command stays offline.
520
+
521
+ Two concerns ship built in, and only two. An authorization concern would need a
522
+ heuristic for *"which files check permissions"*, and a guess there is exactly
523
+ the noise that gets a check switched off — so `SAVE-PERSISTS` and
524
+ `CHANGED-CODE` are the defaults, and a project that knows its own auth layer
525
+ says so in three lines. Declaring a concern with a built-in's id replaces it,
526
+ and the replacement is reported rather than silent.
527
+
528
+ A concern is **not** a claim and never becomes one by itself. It says where to
529
+ look; the probe says what it found; a human states the sentence worth defending.
530
+
531
+ #### Sweeping the save surface instead of the diff
532
+
533
+ A diff cannot answer *"when the user clicks save, does it actually save?"* — it
534
+ only knows what changed today. `--save-paths` points the same sweep at every
535
+ file that writes to storage:
536
+
537
+ ```bash
538
+ npx testguard-cli sweep --save-paths --cap 20
539
+ ```
540
+
541
+ ```
542
+ 119 writes to storage across 37 files, carrying 226 payload fields. Swept 36.
543
+ proposed 762 faults, probed 20 (cap 20), deferred 742.
544
+
545
+ SURVIVED src/app/actions/register.ts [write path]
546
+ [line 59] Field dropped: `password_hash` is no longer written.
547
+ src/tests/actions/register.test.ts mocks @/lib/prisma; 1 exact, 8 partial,
548
+ 31 argument-free call assertions in the file. A field dropped from the write
549
+ payload fails only against an assertion that names that field exactly.
550
+ ```
551
+
552
+ The first line is the point. A report that lists findings without saying how
553
+ much surface it looked at invites you to assume the rest is fine, so the
554
+ denominator comes first and the document carries `writeSites` and
555
+ `payloadFields` whether or not anything survived. Measured on a real
556
+ AI-authored application: **12 of 26 probed persistence faults survived a fully
557
+ green suite, and all 12 were a dropped payload field.**
558
+
559
+ It also states, before any verdict, **what the defenders could prove at all**:
560
+
561
+ ```
562
+ of those 37 files: 32 defended only by tests that mock the persistence layer,
563
+ 1 with an unmocked defender, 4 with no defender at all.
564
+ ```
565
+
566
+ That line is the read-back answer. A test that replaced the database can prove
567
+ the *call shape* and nothing beyond it — a `where` that matches no rows, a
568
+ rolled-back transaction and a rejected constraint all pass against a mock that
569
+ recorded the arguments and returned a plausible object. So rather than pretend
570
+ to measure persistence, TestGuard states the limit, the same way `nocover` says
571
+ "no test imports this" instead of guessing.
572
+
573
+ When every defender mocks the layer, the report says so outright: *nothing in
574
+ this suite can prove a write reached storage*. A clean probe there is not
575
+ evidence of persistence — it is evidence that nothing could have measured it.
576
+
577
+ `unmocked` is deliberately weaker than "proves persistence": a test that does
578
+ not replace the database may simply never reach it. Possible is the honest word.
579
+
469
580
  ### Every change needs a claim
470
581
 
471
582
  `probe` can only verify claims that exist. Every escaped defect in the field
@@ -547,12 +658,12 @@ detected base that does not resolve is a warning for `status` and `brief`
547
658
  # GitHub Actions
548
659
  - uses: actions/checkout@v4
549
660
  with: { fetch-depth: 0 }
550
- - uses: raccioly/testguard@v0.11.0
661
+ - uses: raccioly/testguard@v0.13.0
551
662
  with: { command: gate }
552
663
 
553
664
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
554
665
  include:
555
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.11.0/packaging/gitlab/testguard.gitlab-ci.yml'
666
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.13.0/packaging/gitlab/testguard.gitlab-ci.yml'
556
667
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
557
668
  ```
558
669
 
@@ -580,6 +691,32 @@ talks to the network; only the job posts, and only when told to.
580
691
  toward green. The one pass is `killed`; everything else gates.
581
692
  - **One exact-pinned runtime dependency** (`ajv`), Node ≥ 20, MIT.
582
693
 
694
+ ### Prior art, and what was taken from where
695
+
696
+ Breaking code to test the tests is mutation testing, and it dates to the 1970s.
697
+ Two industrial programmes solved the parts that make it usable, and this tool
698
+ takes from both.
699
+
700
+ **Google** made it affordable: mutate only *changed* lines, cap what is
701
+ surfaced at seven per file, and order candidates by the measured productivity
702
+ of the operator in similar context — developer feedback took their productive
703
+ rate from 15% to 89%
704
+ ([Petrović et al., 2021](https://arxiv.org/abs/2102.11378)). `sweep`'s diff
705
+ scope, its cap and its ordering are theirs; the productivity prior here is
706
+ measured on real probed faults rather than assumed.
707
+
708
+ **Meta** made it specific: an engineer writes a *concern* in plain text, a
709
+ model drafts faults for it, and every step is gated by execution before a
710
+ human ever sees it — 73% of the resulting tests were accepted
711
+ ([Foster et al., FSE 2025](https://arxiv.org/abs/2501.12862)). The rule that
712
+ nothing a model proposes reaches the claims file without passing `probe` and a
713
+ human keep is theirs.
714
+
715
+ What neither does is bind a fault to a **stated promise**, which is what this
716
+ tool is for. The full record — including what was deliberately *not* taken, the
717
+ measured reason no model decides a verdict, and the gaps in what we borrowed —
718
+ is in [`docs-canonical/PRIOR-ART.md`](docs-canonical/PRIOR-ART.md).
719
+
583
720
  ### What TestGuard is not
584
721
 
585
722
  - **Not a test generator.** It judges a test the agent wrote (`admit`); the
@@ -46,7 +46,7 @@ inputs:
46
46
  version:
47
47
  description: 'testguard-cli version to run'
48
48
  required: false
49
- default: '0.11.0'
49
+ default: '0.13.0'
50
50
  node-version:
51
51
  description: 'Node.js version (20+)'
52
52
  required: false