testguard-cli 0.13.0__tar.gz → 0.14.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 (334) hide show
  1. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/CHANGELOG.md +87 -0
  2. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/PKG-INFO +14 -8
  3. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/README.md +13 -7
  4. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/action.yml +1 -1
  5. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs/testguard-explained.html +26 -23
  6. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs/testguard-explained.pdf +0 -0
  7. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs-canonical/PRIOR-ART.md +42 -16
  8. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/package-lock.json +2 -2
  9. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/package.json +1 -1
  10. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
  11. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/packaging/homebrew/testguard.rb +2 -2
  12. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/pyproject.toml +1 -1
  13. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/GATE-SEMANTICS.md +8 -0
  14. testguard_cli-0.14.0/spec/conformance/examples/evidence-persistence-signal.json +168 -0
  15. testguard_cli-0.14.0/spec/conformance/invalid/evidence.persistence-signal-not-a-defender.json +168 -0
  16. testguard_cli-0.14.0/spec/conformance/invalid/evidence.persistence-signal-on-a-non-survivor.json +139 -0
  17. testguard_cli-0.14.0/spec/conformance/invalid/evidence.persistence-signal-without-reason.json +167 -0
  18. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/lib/validate.mjs +12 -1
  19. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/evidence.schema.json +35 -2
  20. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/sweep.schema.json +5 -0
  21. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/probe.mjs +8 -1
  22. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/pyimports.mjs +9 -2
  23. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/scaffold/producers.python.mjs +54 -2
  24. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/supply/persistence.mjs +20 -0
  25. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/supply/select.mjs +39 -6
  26. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/sweep/sweep.mjs +30 -10
  27. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/util/glob.mjs +5 -1
  28. testguard_cli-0.14.0/test/evidence-persistence-signal.test.mjs +78 -0
  29. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/glob.test.mjs +4 -1
  30. testguard_cli-0.14.0/test/probe-persistence-signal.test.mjs +107 -0
  31. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/pyimports.test.mjs +10 -0
  32. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/scaffold.python.test.mjs +16 -0
  33. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/supply-select.test.mjs +50 -1
  34. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/sweep.test.mjs +27 -1
  35. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/testguard.claims.json +215 -5
  36. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/testguard.cost-budget.json +23 -12
  37. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.agents/skills/testguard/SKILL.md +0 -0
  38. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.codex/hooks.json +0 -0
  39. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.docguard.json +0 -0
  40. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.docguardignore +0 -0
  41. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.gitattributes +0 -0
  42. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  43. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  44. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  45. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/dependabot.yml +0 -0
  46. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/scripts/approve-held-runs.sh +0 -0
  47. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/scripts/build-one-pager.mjs +0 -0
  48. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/scripts/check-cost-budget.mjs +0 -0
  49. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/scripts/check-python-fixture.mjs +0 -0
  50. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/scripts/install-smoke.mjs +0 -0
  51. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/scripts/publish-homebrew-tap.sh +0 -0
  52. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/scripts/sync-release-version.mjs +0 -0
  53. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/workflows/auto-merge.yml +0 -0
  54. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/workflows/ci.yml +0 -0
  55. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/workflows/release.yml +0 -0
  56. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/workflows/scheduled-release.yml +0 -0
  57. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.github/workflows/supply-chain.yml +0 -0
  58. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.gitignore +0 -0
  59. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.npmignore +0 -0
  60. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.npmrc +0 -0
  61. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.pre-commit-config.yaml +0 -0
  62. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/.pre-commit-hooks.yaml +0 -0
  63. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/AGENTS.md +0 -0
  64. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/CLAUDE.md +0 -0
  65. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/CODE_OF_CONDUCT.md +0 -0
  66. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/CONTRIBUTING.md +0 -0
  67. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/DRIFT-LOG.md +0 -0
  68. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/LICENSE +0 -0
  69. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/PRIVACY.md +0 -0
  70. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/SECURITY.md +0 -0
  71. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/SUPPORT.md +0 -0
  72. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/bench/README.md +0 -0
  73. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/cli/testguard.mjs +0 -0
  74. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs-canonical/ARCHITECTURE.md +0 -0
  75. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs-canonical/DATA-MODEL.md +0 -0
  76. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs-canonical/ENVIRONMENT.md +0 -0
  77. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs-canonical/REQUIREMENTS.md +0 -0
  78. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs-canonical/SECURITY.md +0 -0
  79. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/docs-canonical/TEST-SPEC.md +0 -0
  80. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/.gitignore +0 -0
  81. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/README.md +0 -0
  82. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/expected.json +0 -0
  83. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/package.json +0 -0
  84. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/src/export.mjs +0 -0
  85. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/src/redact.mjs +0 -0
  86. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
  87. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/test/flaky.test.mjs +0 -0
  88. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/test/redact.test.mjs +0 -0
  89. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/testguard.claims.json +0 -0
  90. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer/vitest.config.mjs +0 -0
  91. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/.gitignore +0 -0
  92. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/README.md +0 -0
  93. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/expected.json +0 -0
  94. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/jest.config.js +0 -0
  95. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/package.json +0 -0
  96. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/src/export.js +0 -0
  97. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/src/redact.js +0 -0
  98. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
  99. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
  100. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/test/redact.test.js +0 -0
  101. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/testguard.claims.json +0 -0
  102. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/.gitignore +0 -0
  103. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/README.md +0 -0
  104. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
  105. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
  106. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/expected.json +0 -0
  107. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/package-lock.json +0 -0
  108. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/package.json +0 -0
  109. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
  110. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
  111. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
  112. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
  113. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
  114. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
  115. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/.gitignore +0 -0
  116. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/README.md +0 -0
  117. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/__init__.py +0 -0
  118. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/export.py +0 -0
  119. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/orphan.py +0 -0
  120. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/pipeline.py +0 -0
  121. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/redact.py +0 -0
  122. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/expected.json +0 -0
  123. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/testguard.claims.json +0 -0
  124. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
  125. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
  126. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/tests/test_patched.py +0 -0
  127. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/tests/test_redact.py +0 -0
  128. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/README.md +0 -0
  129. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/baseline.json +0 -0
  130. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/brief.json +0 -0
  131. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/calibration-websec.json +0 -0
  132. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/calibration.json +0 -0
  133. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/claims-methods.json +0 -0
  134. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/claims.json +0 -0
  135. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
  136. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-negative-control.json +0 -0
  137. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-provisional.json +0 -0
  138. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-python.json +0 -0
  139. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-scan-method.json +0 -0
  140. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence.json +0 -0
  141. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/gate.json +0 -0
  142. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/ignore.json +0 -0
  143. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/replay.json +0 -0
  144. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/examples/status.json +0 -0
  145. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
  146. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/baseline.zero-count.json +0 -0
  147. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
  148. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
  149. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
  150. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
  151. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
  152. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
  153. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
  154. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
  155. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
  156. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
  157. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.absolute-path.json +0 -0
  158. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
  159. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
  160. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
  161. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
  162. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.noop-fault.json +0 -0
  163. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
  164. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
  165. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
  166. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
  167. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
  168. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
  169. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
  170. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
  171. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
  172. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
  173. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
  174. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
  175. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
  176. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
  177. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
  178. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
  179. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
  180. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
  181. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
  182. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
  183. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
  184. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
  185. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
  186. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
  187. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
  188. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
  189. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
  190. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
  191. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
  192. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
  193. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
  194. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/ignore.short-reason.json +0 -0
  195. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
  196. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
  197. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.claim-without-file.json +0 -0
  198. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
  199. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
  200. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
  201. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
  202. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
  203. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
  204. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/conformance/schemas.test.mjs +0 -0
  205. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/lib/fingerprint.mjs +0 -0
  206. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/lib/wilson.mjs +0 -0
  207. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/baseline.schema.json +0 -0
  208. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/brief.schema.json +0 -0
  209. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/calibration.schema.json +0 -0
  210. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/claims.schema.json +0 -0
  211. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/common.schema.json +0 -0
  212. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/concerns.schema.json +0 -0
  213. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/gate.schema.json +0 -0
  214. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/ignore.schema.json +0 -0
  215. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/replay.schema.json +0 -0
  216. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/spec/schemas/status.schema.json +0 -0
  217. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/admit/admit.mjs +0 -0
  218. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/baseline/baseline.mjs +0 -0
  219. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/brief/brief.mjs +0 -0
  220. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/claims/anchors.mjs +0 -0
  221. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/claims/annotations.mjs +0 -0
  222. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/claims/load.mjs +0 -0
  223. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/claims/removed.mjs +0 -0
  224. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/cli.mjs +0 -0
  225. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/admit.mjs +0 -0
  226. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/baseline.mjs +0 -0
  227. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/brief.mjs +0 -0
  228. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/claims.mjs +0 -0
  229. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/concerns.mjs +0 -0
  230. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/gate.mjs +0 -0
  231. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/init.mjs +0 -0
  232. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/mcp.mjs +0 -0
  233. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/probe.mjs +0 -0
  234. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/replay.mjs +0 -0
  235. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/scaffold.mjs +0 -0
  236. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/status.mjs +0 -0
  237. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/commands/sweep.mjs +0 -0
  238. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/evidence/writer.mjs +0 -0
  239. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/gate/changed.mjs +0 -0
  240. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/git.mjs +0 -0
  241. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/init/init.mjs +0 -0
  242. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/init/templates/SKILL.md +0 -0
  243. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/mcp/server.mjs +0 -0
  244. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/mcp/tools.mjs +0 -0
  245. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/attribution.mjs +0 -0
  246. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/classify.mjs +0 -0
  247. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/contention.mjs +0 -0
  248. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/cost.mjs +0 -0
  249. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/discover.mjs +0 -0
  250. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/independence.mjs +0 -0
  251. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/inject.mjs +0 -0
  252. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/mocks.mjs +0 -0
  253. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/progress.mjs +0 -0
  254. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/rank.mjs +0 -0
  255. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/index.mjs +0 -0
  256. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/jest.mjs +0 -0
  257. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/playwright.mjs +0 -0
  258. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
  259. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/python/_testguard_report.py +0 -0
  260. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
  261. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/python.mjs +0 -0
  262. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/shared.mjs +0 -0
  263. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/runners/vitest.mjs +0 -0
  264. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/probe/worktree.mjs +0 -0
  265. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/render.mjs +0 -0
  266. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/replay/label.mjs +0 -0
  267. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/replay/replay.mjs +0 -0
  268. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/scaffold/producers.mjs +0 -0
  269. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/scaffold/scaffold.mjs +0 -0
  270. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/status/status.mjs +0 -0
  271. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/status/surface.mjs +0 -0
  272. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/supply/concerns.mjs +0 -0
  273. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/supply/feedback.mjs +0 -0
  274. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/supply/savepath.mjs +0 -0
  275. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/src/util/hash.mjs +0 -0
  276. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/admit.test.mjs +0 -0
  277. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/annotations.test.mjs +0 -0
  278. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/attribution.test.mjs +0 -0
  279. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/baseline.test.mjs +0 -0
  280. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/brief.test.mjs +0 -0
  281. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/calibration.test.mjs +0 -0
  282. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/claim-selection.test.mjs +0 -0
  283. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/claims-anchors.test.mjs +0 -0
  284. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/claims-since.test.mjs +0 -0
  285. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/classify.test.mjs +0 -0
  286. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/contention.test.mjs +0 -0
  287. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/cost.test.mjs +0 -0
  288. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/discover.test.mjs +0 -0
  289. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/gate.test.mjs +0 -0
  290. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/gitlab-template.test.mjs +0 -0
  291. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/independence.test.mjs +0 -0
  292. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/init.test.mjs +0 -0
  293. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/inject.test.mjs +0 -0
  294. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/label.test.mjs +0 -0
  295. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/mcp.test.mjs +0 -0
  296. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/mocks.test.mjs +0 -0
  297. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/negative-control.test.mjs +0 -0
  298. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/one-pager.test.mjs +0 -0
  299. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/probe-error.test.mjs +0 -0
  300. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/probe-preconditions.test.mjs +0 -0
  301. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/probe.fixture.test.mjs +0 -0
  302. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/probe.jest.test.mjs +0 -0
  303. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/probe.playwright.test.mjs +0 -0
  304. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/probe.python.test.mjs +0 -0
  305. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/progress.test.mjs +0 -0
  306. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/rank-aliases.test.mjs +0 -0
  307. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/release-approval.test.mjs +0 -0
  308. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/release-sync.test.mjs +0 -0
  309. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/replay-verdict.test.mjs +0 -0
  310. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/replay.test.mjs +0 -0
  311. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/runner-argv.test.mjs +0 -0
  312. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/runner-command.test.mjs +0 -0
  313. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/runner-playwright.test.mjs +0 -0
  314. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/runner-python.test.mjs +0 -0
  315. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/runner-vitest.test.mjs +0 -0
  316. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/samples/playwright-report.json +0 -0
  317. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/samples/python-report-collect-error.json +0 -0
  318. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/samples/python-report.json +0 -0
  319. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/scaffold.test.mjs +0 -0
  320. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/snapshot.test.mjs +0 -0
  321. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/status.test.mjs +0 -0
  322. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/supply-concerns.test.mjs +0 -0
  323. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/supply-feedback.test.mjs +0 -0
  324. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/supply-persistence.test.mjs +0 -0
  325. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/supply-savepath.test.mjs +0 -0
  326. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/surface.test.mjs +0 -0
  327. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/sweep.fixture.test.mjs +0 -0
  328. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/vitest-config.test.mjs +0 -0
  329. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/worktree.test.mjs +0 -0
  330. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/test/writer.test.mjs +0 -0
  331. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/testguard.ignore.json +0 -0
  332. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/testguard_cli/__init__.py +0 -0
  333. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/testguard_cli/wrapper.py +0 -0
  334. {testguard_cli-0.13.0 → testguard_cli-0.14.0}/vitest.config.mjs +0 -0
@@ -7,6 +7,93 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.14.0] - 2026-09-22
11
+
12
+ Automated weekly release — everything merged since `v0.13.0`.
13
+
14
+ ### Changed
15
+
16
+ - fix: three defects the arid measurement found, presentational JSX set aside, and the claimspec signal bump (#164)
17
+ - docs: carry the 0.12 and 0.13 results into the brief and the prior-art record (#162)
18
+ - chore(homebrew): sha256 for v0.13.0 (#161)
19
+
20
+
21
+ ### Added
22
+
23
+ - **claimspec v1 evidence admits `persistence-payload-unasserted`** to
24
+ `defenders.signals`, the enum shared with every claimspec consumer. The
25
+ signal had lived only in the sweep document, so `probe` could not surface
26
+ why a save-path survivor was missed. Now a `survived` record whose fault
27
+ sits on the write path carries it for each resolved defender that mocks the
28
+ persistence layer, with the mocked `specifiers` and the file's assertion
29
+ `counts` (exact, partial, argument-free). The validator refuses it on any
30
+ other verdict, on a file that is not a resolved defender, or without its
31
+ reason — the same rule the sweep document already enforced on its
32
+ findings, and the sweep now repeats what the record says rather than
33
+ re-deriving it. Conformance carries a valid example and three invalid
34
+ cases. No consumer outside this repository validates claimspec evidence
35
+ today; DocGuard's adoption (docguard#420) is notified rather than broken.
36
+ - `sweep` sets aside a purely presentational JSX element — an icon, or a
37
+ static wrapper with no expression and no handler — instead of spending a
38
+ probe run on it, and says so: `selection.presentational` in the document, a
39
+ line in the report, and the remainder arithmetic now proves
40
+ proposed = probed + deferred + set aside. Measured on a 14-file UI diff:
41
+ 150 of 510 proposals, 29 of 30 probe slots and 7 of 10 survivors were of
42
+ this shape — Google's definition of an arid node, in a syntax Google never
43
+ mutated. Because the ordering learns from survival, those survivors were
44
+ also teaching it to rank the class higher; set-aside proposals never enter
45
+ the evidence, so they no longer can. Google's own five arid categories were
46
+ measured at under 1% of proposals and are not suppressed.
47
+
48
+ ### Fixed
49
+
50
+ - Python defender discovery lost the last name on an import line that ends in
51
+ a comment: `from pkg import cli, demo # noqa: E402` found only `cli`, and
52
+ `import pkg.demo # noqa` found nothing. On a real suite where `# noqa` is
53
+ the idiom of every src-layout test, 12 of 30 probed faults were reported
54
+ NOCOVER for modules whose tests import them — a gating verdict in the
55
+ forbidden direction. Comments are now stripped before names are read.
56
+ - File walking descended into `.claude/`, so a nested agent worktree under
57
+ `.claude/worktrees/` contributed another branch's tests as defenders of this
58
+ tree — files the scratch worktree cannot even contain, because the directory
59
+ is gitignored. `.claude` joins the tool-directory skip list, and the claim
60
+ that promised this now names it.
61
+ - The Python `statement-deleted` producer proposed on lines inside a bracket
62
+ opened earlier — `help="…")` inside an `add_argument(` — and none of those
63
+ faults compiled: 81 of 600 proposals on a real CLI (13.5%). The producer now
64
+ tracks bracket depth across lines (triple-quoted strings included, so a
65
+ docstring's parentheses cannot leave the rest of the file "inside") and
66
+ refuses the `pass` shapes there; dict entries and call arguments keep their
67
+ own producers. Re-measured: 7 of 526 (1.3%).
68
+
69
+ ### Changed
70
+
71
+ - The self-probe cost budget is 1650 s, from 1440. Six new claims (85 s on
72
+ the Node 22 PR leg, worst 22 s, all under the 100 s per-claim ceiling) took
73
+ that leg from 1367 s to 1457 s, over by 17 s. The v0.13.0 release job
74
+ measured 1113 s from scratch, but the PR leg runs the same claims 25-30%
75
+ slower and is where the ceiling is checked, so 1650 restores ~13% headroom
76
+ over the leg that binds. The per-claim ceiling is unchanged.
77
+ - The technical brief carries the 0.12 and 0.13 results it was missing: the
78
+ whole-surface provability measurement (32 of 37 files that write are defended
79
+ only by tests that mock the database) replaces the sample-level number in
80
+ Fig. 7; the ladder's second rung is now a shipped verb with a real finding
81
+ (a five-line concern found an admin route whose owner/manager check can be
82
+ forced to `false` with no test failing) rather than a description of intent;
83
+ and the first rung says its ordering learns from the project's own runs.
84
+ Re-rendered; still eight sheets from eight pages.
85
+ - `docs-canonical/PRIOR-ART.md` records the arid-node measurement instead of
86
+ a promise: every proposal from four sweeps over two of the maintainer's
87
+ repositories was classified by hand against Google's five arid categories,
88
+ and they account for under 1% of what the producers propose. No suppressor
89
+ is built for them. The same pass found what IS material — presentational
90
+ JSX taking 29 of 30 probe slots on a UI diff, and 13.5% of Python proposals
91
+ that cannot compile — and lists both as open, with numbers.
92
+ - `docs-canonical/PRIOR-ART.md` no longer lists the feedback loop and concerns
93
+ as unbuilt. The two gaps that remain are restated precisely: the loop learns
94
+ from verdicts rather than explicit developer clicks, and concerns have no
95
+ model-backed producer.
96
+
10
97
  ## [0.13.0] - 2026-09-22
11
98
 
12
99
  Automated weekly release — everything merged since `v0.12.0`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: testguard-cli
3
- Version: 0.13.0
3
+ Version: 0.14.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.13.0` — see [`action.yml`](./action.yml) |
154
+ | GitHub Action | `uses: raccioly/testguard@v0.14.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.13.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.14.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
@@ -258,6 +258,10 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
258
258
  static signal `mocked-never-asserted` — the cheapest blind-spot signal
259
259
  there is, and the exact signature of one escaped bug in the field
260
260
  reports. Silence it, visibly, with `// unasserted: <why>` above the mock.
261
+ Its sibling `persistence-payload-unasserted` is attached to a **survivor
262
+ on a write path** whose defender mocks the database: the test can prove
263
+ the call shape and never that the row landed, and the signal names the
264
+ mocked layer and the file's assertion mix (exact, partial, argument-free).
261
265
 
262
266
  Runners: **vitest**, **jest**, **Playwright** and **Python** (`--runner
263
267
  auto` picks the first of vitest, jest, python that resolves). A JavaScript
@@ -447,8 +451,9 @@ alarming: something was deliberately broken and not one test failed.
447
451
 
448
452
  ```
449
453
  swept 13 of 14 unclaimed changed files against origin/main.
450
- proposed 510 faults, probed 10 (cap 10), deferred 500.
451
- The 500 deferred are not a verdict: raise --cap, or sweep a smaller change.
454
+ proposed 510 faults, probed 10 (cap 10), deferred 350.
455
+ The 350 deferred are not a verdict: raise --cap, or sweep a smaller change.
456
+ 150 presentational elements (icons, static wrappers) set aside: nobody writes a test for those, and a survivor there would teach the ordering the wrong lesson.
452
457
 
453
458
  9 findings — a deliberate break that no test noticed:
454
459
 
@@ -682,12 +687,12 @@ detected base that does not resolve is a warning for `status` and `brief`
682
687
  # GitHub Actions
683
688
  - uses: actions/checkout@v4
684
689
  with: { fetch-depth: 0 }
685
- - uses: raccioly/testguard@v0.13.0
690
+ - uses: raccioly/testguard@v0.14.0
686
691
  with: { command: gate }
687
692
 
688
693
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
689
694
  include:
690
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.13.0/packaging/gitlab/testguard.gitlab-ci.yml'
695
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.0/packaging/gitlab/testguard.gitlab-ci.yml'
691
696
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
692
697
  ```
693
698
 
@@ -1005,7 +1010,8 @@ parameter-derived argument swapped for `None`, a key dropped from a payload
1005
1010
  `dict` or an allow-list. Two differences are deliberate. A statement is
1006
1011
  removed by replacing it with `pass`, never by deleting the line, because a
1007
1012
  block whose only statement is gone is an `IndentationError`; and a line that
1008
- leaves a bracket open (`COLOURS = {`) is never removed at all. Both exist
1013
+ leaves a bracket open (`COLOURS = {`), or sits inside one opened on an earlier
1014
+ line (`help="…"` in an `add_argument(`), is never removed at all. Both exist
1009
1015
  because a fault that cannot compile is a `fault-invalid` verdict — a probe run
1010
1016
  spent saying nothing about the tests. The two JSX shapes (`element-removed`,
1011
1017
  `handler-dropped`) have no Python meaning and are absent rather than faked.
@@ -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.13.0` — see [`action.yml`](./action.yml) |
130
+ | GitHub Action | `uses: raccioly/testguard@v0.14.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.13.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.14.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
@@ -234,6 +234,10 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
234
234
  static signal `mocked-never-asserted` — the cheapest blind-spot signal
235
235
  there is, and the exact signature of one escaped bug in the field
236
236
  reports. Silence it, visibly, with `// unasserted: <why>` above the mock.
237
+ Its sibling `persistence-payload-unasserted` is attached to a **survivor
238
+ on a write path** whose defender mocks the database: the test can prove
239
+ the call shape and never that the row landed, and the signal names the
240
+ mocked layer and the file's assertion mix (exact, partial, argument-free).
237
241
 
238
242
  Runners: **vitest**, **jest**, **Playwright** and **Python** (`--runner
239
243
  auto` picks the first of vitest, jest, python that resolves). A JavaScript
@@ -423,8 +427,9 @@ alarming: something was deliberately broken and not one test failed.
423
427
 
424
428
  ```
425
429
  swept 13 of 14 unclaimed changed files against origin/main.
426
- proposed 510 faults, probed 10 (cap 10), deferred 500.
427
- The 500 deferred are not a verdict: raise --cap, or sweep a smaller change.
430
+ proposed 510 faults, probed 10 (cap 10), deferred 350.
431
+ The 350 deferred are not a verdict: raise --cap, or sweep a smaller change.
432
+ 150 presentational elements (icons, static wrappers) set aside: nobody writes a test for those, and a survivor there would teach the ordering the wrong lesson.
428
433
 
429
434
  9 findings — a deliberate break that no test noticed:
430
435
 
@@ -658,12 +663,12 @@ detected base that does not resolve is a warning for `status` and `brief`
658
663
  # GitHub Actions
659
664
  - uses: actions/checkout@v4
660
665
  with: { fetch-depth: 0 }
661
- - uses: raccioly/testguard@v0.13.0
666
+ - uses: raccioly/testguard@v0.14.0
662
667
  with: { command: gate }
663
668
 
664
669
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
665
670
  include:
666
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.13.0/packaging/gitlab/testguard.gitlab-ci.yml'
671
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.0/packaging/gitlab/testguard.gitlab-ci.yml'
667
672
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
668
673
  ```
669
674
 
@@ -981,7 +986,8 @@ parameter-derived argument swapped for `None`, a key dropped from a payload
981
986
  `dict` or an allow-list. Two differences are deliberate. A statement is
982
987
  removed by replacing it with `pass`, never by deleting the line, because a
983
988
  block whose only statement is gone is an `IndentationError`; and a line that
984
- leaves a bracket open (`COLOURS = {`) is never removed at all. Both exist
989
+ leaves a bracket open (`COLOURS = {`), or sits inside one opened on an earlier
990
+ line (`help="…"` in an `add_argument(`), is never removed at all. Both exist
985
991
  because a fault that cannot compile is a `fault-invalid` verdict — a probe run
986
992
  spent saying nothing about the tests. The two JSX shapes (`element-removed`,
987
993
  `handler-dropped`) have no Python meaning and are absent rather than faked.
@@ -46,7 +46,7 @@ inputs:
46
46
  version:
47
47
  description: 'testguard-cli version to run'
48
48
  required: false
49
- default: '0.13.0'
49
+ default: '0.14.0'
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.13.0 · MIT</div>
147
+ <div class="meta">Technical Brief · v0.14.0 · MIT</div>
148
148
  </div>
149
149
 
150
150
  <h1>Your tests pass. That is not the same as your tests working.</h1>
@@ -678,7 +678,7 @@
678
678
  <tr><td><code>init</code></td><td>Installs the agent layer — skill, session-start hook, AGENTS.md section — at the git root.</td></tr>
679
679
  <tr><td><code>claims</code></td><td>Lists the claims and reports drift against the <code>@claim</code> annotations in the code.</td></tr>
680
680
  <tr><td><code>scaffold</code></td><td>Proposes faults mechanically for one file, as a draft claims document. A starting point, not an answer.</td></tr>
681
- <tr><td><code>sweep</code></td><td>The cold start: propose and probe faults for changed files that carry <em>no</em> claim.</td></tr>
681
+ <tr><td><code>sweep</code></td><td>The cold start: probe files with <em>no</em> claim — a diff, the write surface, or a one-line <code>concern</code>.</td></tr>
682
682
  <tr><td class="v-good"><code>probe</code></td><td>The engine. Inject each fault, run its defenders, report what survived.</td></tr>
683
683
  <tr><td><code>admit</code></td><td>The two-gate rule as one verb: green on HEAD <em>and</em> failing on every fault of the claim. Otherwise the test is not a defender.</td></tr>
684
684
  <tr><td><code>baseline</code></td><td>Freezes today's unproven findings so only new ones gate.</td></tr>
@@ -755,7 +755,7 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
755
755
  <p>Twenty-six faults injected on the persistence path of seven server actions in a real
756
756
  AI-authored application: the database write itself, and the fields of the payload it carries.</p>
757
757
  <figure style="margin-top:11px">
758
- <svg viewBox="0 0 760 150" role="img" aria-label="Save-path measurement: 12 of 26 faults survived, all of them a dropped payload field; 10 of 10 tests mock the database and none read back">
758
+ <svg viewBox="0 0 760 150" role="img" aria-label="Save-path measurement: 12 of 26 faults survived, all of them a dropped payload field; across the whole write surface, 32 of 37 files are defended only by tests that mock the database">
759
759
  <rect x="0" y="0" width="243" height="74" rx="4" fill="#ffffff" stroke="#e6e2d8"/>
760
760
  <text x="18" y="38" style="font-family:var(--sans);font-size:27px;font-weight:700;letter-spacing:-0.03em;fill:#b85636">12 of 26</text>
761
761
  <text class="s-sub" x="18" y="56" style="font-size:8px">survived a fully green suite</text>
@@ -767,9 +767,9 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
767
767
  <text class="s-sub" x="276" y="68" style="font-size:8px">field — not one was anything else</text>
768
768
 
769
769
  <rect x="516" y="0" width="244" height="74" rx="4" fill="#ffffff" stroke="#e6e2d8"/>
770
- <text x="534" y="38" style="font-family:var(--sans);font-size:27px;font-weight:700;letter-spacing:-0.03em;fill:#191917">10 of 10</text>
771
- <text class="s-sub" x="534" y="56" style="font-size:8px">tests mock the database; none</text>
772
- <text class="s-sub" x="534" y="68" style="font-size:8px">read the record back</text>
770
+ <text x="534" y="38" style="font-family:var(--sans);font-size:27px;font-weight:700;letter-spacing:-0.03em;fill:#b85636">32 of 37</text>
771
+ <text class="s-sub" x="534" y="56" style="font-size:8px">files that write are defended only</text>
772
+ <text class="s-sub" x="534" y="68" style="font-size:8px">by tests that mock the database</text>
773
773
 
774
774
  <rect x="0" y="86" width="760" height="58" rx="4" fill="#fbf1ec" stroke="#e8d5cc"/>
775
775
  <text class="s-tiny" x="16" y="104" style="fill:#b85636">The worst case — user registration</text>
@@ -778,9 +778,10 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
778
778
  </svg>
779
779
  <p class="figcap">Fig. 7 — The save button reports success. The suite agrees. The column is empty.</p>
780
780
  </figure>
781
- <p class="note">This is the page-one exhibit again, measured rather than recounted: an assertion that
782
- names the call but not the payload cannot fail when a field goes missing. It is invisible to coverage,
783
- invisible to a mutation score, and invisible to the test's own author.</p>
781
+ <p class="note">The third number is the read-back answer. A test that replaced the database proves the
782
+ call shape and nothing beyond it — a <code>where</code> matching no rows, a rolled-back transaction, a
783
+ rejected constraint all pass against a mock. On this surface a clean probe is not evidence of
784
+ persistence; it is evidence that nothing could have measured it, and the tool says so before any verdict.</p>
784
785
  </section>
785
786
 
786
787
  <section class="rule-top">
@@ -788,28 +789,28 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
788
789
  <div class="cols-3">
789
790
  <div>
790
791
  <h3>Nothing written</h3>
791
- <p class="note"><code>sweep</code> takes the changed files the gate just called uncovered,
792
- proposes faults with the mechanical producers, probes a bounded selection, and reports what
793
- nothing noticed. A fault that survives needs no claim to be alarming.</p>
792
+ <p class="note"><code>sweep</code> takes the files the gate just called uncovered, proposes
793
+ faults mechanically, probes a bounded selection, and reports what nothing noticed — ordering
794
+ candidates by what this project’s own runs have shown survives, the feedback that took
795
+ Google’s productive rate from 15% to 89%, read from evidence already on disk.</p>
794
796
  </div>
795
797
  <div>
796
798
  <h3>A sentence or ten</h3>
797
- <p class="note">A <em>concern</em> — “every save persists its data” — is one sentence that applies
798
- across two hundred screens. It is human-anchored and machine-instantiated, and every proposal it
799
- produces still has to survive an execution gate.</p>
799
+ <p class="note">A <em>concern</em> names a kind of promise and where to look. Five lines of JSON
800
+ on the same application — “every admin route refuses a caller without the admin role” — found a
801
+ route whose owner/manager check can be forced to <code>false</code> with no test failing. One
802
+ sentence, twenty-four routes, no model.</p>
800
803
  </div>
801
804
  <div>
802
805
  <h3>A claim</h3>
803
806
  <p class="note">The survivors worth defending become the first claims: a statement, its faults,
804
- and a reproducer already in hand. The ladder ends where the rest of this brief begins.</p>
807
+ and a reproducer already in hand.</p>
805
808
  </div>
806
809
  </div>
807
- <p class="note" style="margin-top:9px">Three rules keep the bottom rung safe on a repository that has
808
- never seen this tool. A sweep <strong>never writes the claims file</strong> — a sentence nobody wrote
809
- is not a claim. Its evidence <strong>never replaces</strong> the canonical evidence, which is the
810
- record of what the project actually claims. And it <strong>does not fail on its own bad guess</strong>:
811
- only a survivor or an untested file exits non-zero, because a proposal that would not compile is the
812
- tool’s mistake, and a tool that fails on its own mistakes gets switched off.</p>
810
+ <p class="note" style="margin-top:9px">Three rules keep the lower rungs safe. A sweep <strong>never
811
+ writes the claims file</strong> — a sentence nobody wrote is not a claim. Its evidence <strong>never
812
+ replaces</strong> the canonical record. And it <strong>does not fail on its own bad guess</strong>:
813
+ only a survivor or an untested file exits non-zero.</p>
813
814
  </section>
814
815
 
815
816
  <div class="pull">
@@ -843,7 +844,9 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
843
844
  A later study found reported mutants are coupled to real faults: the bugs they analysed would
844
845
  have been caught had a mutant been surfaced.</p>
845
846
  <p class="note"><em>Taken here:</em> the diff scope, the cap, and a productivity-ordered
846
- selection — measured on real probed faults rather than assumed.</p>
847
+ selection — measured on real probed faults rather than assumed. Their arid-node suppression
848
+ was measured, not copied: Google's categories are under 1% of what the producers propose;
849
+ the arid class here is presentational JSX, now set aside and counted.</p>
847
850
  </div>
848
851
  <div>
849
852
  <h3>Meta — making it specific</h3>
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- docguard:version 1.0.0 -->
4
4
  <!-- docguard:status approved -->
5
- <!-- docguard:last-reviewed 2026-09-21 -->
5
+ <!-- docguard:last-reviewed 2026-09-22 -->
6
6
  <!-- docguard:owner @raccioly -->
7
7
  <!-- docguard:quality passive-voice off — the subject of this document is what was taken from other people's work. "Diff scoping was taken from Google" is the true sentence; rewriting every one to name us as the actor would put the borrower in the foreground of a document about the lender. -->
8
8
  <!-- docguard:quality negation-load off — half the value here is what was deliberately NOT taken: not their mutation score, not their equivalence auto-drop, not a model judging a verdict. A record of borrowings that only lists borrowings is the half that misleads. -->
@@ -49,14 +49,16 @@ numbers are the reason several of our defaults are what they are.
49
49
  | **Diff scoping** — mutate only changed lines, surfaced during code review. | `gate --changed`, and `sweep`, which proposes only for changed files. |
50
50
  | **A hard cap on what is surfaced** — at most 7 × the number of files in the change, because past that the reader stops reading. | `capFor()` in `src/supply/select.mjs`; the remainder is reported as `deferred`, never dropped. |
51
51
  | **Productivity-ordered selection** — order candidates by how often that operator produced a useful mutant in similar context. Developer feedback over six years took their productive rate from **15% to 89%**. | `MEASURED_PRODUCTIVITY` and `scoreOf()` in `src/supply/select.mjs`. Ours is measured on 56 probed faults from a real AI-authored codebase and shrunk toward a neutral prior, so one observation cannot dominate. |
52
- | **Arid-node suppression** — do not mutate code nobody would write a test for (logging, timeouts, config flags). | Partially: our producers are line-oriented and never propose on comments or blank lines, and `testguard.ignore.json` carries path exclusions with reasons. We have no AST-level arid heuristic. |
52
+ | **Arid-node suppression** — do not mutate code nobody would write a test for (logging, timeouts, config flags). | **Measured, not built.** On 2026-09-22 every fault the producers proposed on two of the maintainer's repositories (`sweep --changed` and `sweep --save-paths`, cap 30, 510 + 581 proposals on a Next.js app and 600 on a Python CLI) was classified by hand against Google's five categories — logging, time/sleep/deadline, config flag, memoisation, monitoring counter. They account for **2 of 510, 0 of 581 and about 4 of 600** proposals: under 1% everywhere, against the ~10% that would have justified a suppressor. The reason is structural: the producers are already shape-targeted (a guard, a payload field, a security literal), so they rarely land on a log line in the first place. What the same measurement found instead is recorded under *Known gaps*. |
53
53
  | **Fault coupling as the justification** — their 2021 study found reported mutants are coupled to real faults: bugs they analysed would have been caught had a mutant been surfaced. | The premise behind `replay`, which calibrates fault classes against real fix commits rather than assuming the coupling. |
54
54
 
55
55
  **Not taken.** Their mutation score, their exhaustive per-line generation, and
56
56
  their AST-based arid heuristics (ours is a different scale and a different
57
- language surface). Their selection learns continuously from developer
58
- feedback; **ours does not yet** — see
59
- [Known gaps](#known-gaps-in-what-we-borrowed).
57
+ language surface). Their selection learns from explicit developer feedback;
58
+ **ours learns from the project's own probed verdicts** (v0.13.0) — every
59
+ evidence record already carries a fault class and a verdict, so the feedback
60
+ is a tally rather than a thing to collect. The combination is a Beta-Binomial
61
+ posterior mean, which is strictly proper; see the Laya section.
60
62
 
61
63
  ## Meta — making it specific
62
64
 
@@ -75,7 +77,7 @@ concern, and every step is gated by execution.
75
77
 
76
78
  | Taken | Where it lives here |
77
79
  |---|---|
78
- | **The concern as the unit a human writes.** One plain-text sentence generates faults across a whole surface. | The planned Level 1 `testguard.concerns.json`. Not yet implemented — see [Known gaps](#known-gaps-in-what-we-borrowed). |
80
+ | **The concern as the unit a human writes.** One plain-text sentence generates faults across a whole surface. | `testguard.concerns.json` and `sweep --concern` (v0.13.0). Taken WITHOUT the generator: a concern's core is a named scope plus a producer selection, which the mechanical producers already satisfy, so it ships offline. On a real application a five-line concern found an admin route whose owner/manager check can be forced to `false` with no test failing. |
79
81
  | **An execution gate between every model step and the record.** A drafted fault must build, must survive the existing suite, and must not be equivalent before a test is generated for it; the generated test must build, pass on the original N times, and kill the fault. | The rule that nothing a model proposes enters `testguard.claims.json` without passing `probe` and a human keep — already how `scaffold` drafts work. |
80
82
  | **Show the reviewer the fault as proof.** The engineer sees the concrete fault the test catches, so the model's output is independently checkable rather than self-referential. | Every finding names its fault, its anchor and its reproducer; `admit` reports which fault a candidate test kills. |
81
83
  | **Learned, realistic faults rather than syntactic ones** (Beller 2021: operators learned from a corpus of real Java errors and from changes that caused operational anomalies; **>50% of 15,000 generated mutants survived** their suite). | The premise behind `replay`'s fault-class calibration, and behind the planned intent-based producer. Our built-in producers remain deterministic and line-oriented. |
@@ -148,13 +150,37 @@ difference between a metric and an audit.
148
150
 
149
151
  Recorded so they are not mistaken for decisions.
150
152
 
151
- - **The feedback loop is not built.** Google's 15% → 89% gain came from
152
- developer feedback refining suppression and selection over six years. Ours
153
- ships a static measured prior. Until keep/drop decisions feed back,
154
- `MEASURED_PRODUCTIVITY` is a snapshot of one repository.
155
- - **Concerns are not implemented.** ACH's central idea — the plain-text concern
156
- as the unit a human writes — is planned, not shipped.
157
- - **No arid-node heuristic.** We suppress by path, not by AST shape.
158
- - **The persistence signal is confined to the sweep document.** It is not yet
159
- in `evidence.defenders.signals`, which is a closed enum in the shared
160
- claimspec contract and needs a coordinated bump.
153
+ - **The feedback loop learns from verdicts, not from explicit developer
154
+ clicks.** Google's gain came from "Please fix" / "Not useful" feedback on
155
+ surfaced mutants. Ours reads survival per fault class from the project's own
156
+ evidence — the same signal, but it cannot yet distinguish a survivor the
157
+ developer *acted on* from one they ignored. A keep/drop record would close
158
+ that.
159
+ - **Concerns have no model-backed producer.** The unit shipped; the ACH
160
+ generator that drafts intent-level faults for a concern did not, and is the
161
+ one piece of the rung that would touch the network.
162
+ - **No arid-node heuristic, and the measurement says none is needed for
163
+ Google's categories** (under 1% of proposals; see the Google table). Two
164
+ different things ARE material, measured on the same runs:
165
+ - **Presentational JSX.** On a 14-file UI diff, 150 of 510 proposals (29%)
166
+ were `element-removed` on an icon (`<X />`, `<ChevronLeft />`) or a static
167
+ wrapper (`<div>`, `<span>`, `<p>` with no expression and no handler). The
168
+ ranker spent 29 of 30 probe slots on that class and 7 of the 10 survivors
169
+ were of this shape — findings nobody will write a test for, which is
170
+ Google's definition of arid, in a syntax Google never mutated. Because the
171
+ ordering learns from survival, these survivors also teach it to rank the
172
+ class higher. A line-oriented demotion is designed; it is not built.
173
+ - **Python continuation lines.** 81 of 600 proposals on a Python CLI
174
+ (13.5%) did not compile, 77 of them a `statement-deleted` on a line that
175
+ sits inside a bracket opened on an earlier line. `isIncomplete()` in
176
+ `src/scaffold/producers.python.mjs` checks only whether the line itself
177
+ leaves a bracket open. Measured with the tokenizer's exact depth, a
178
+ running-depth guard on that class catches 74 of the 77 and drops no
179
+ valid proposal. Not arid — a producer that spends probe runs saying
180
+ nothing — but found by the same pass.
181
+ - **The persistence signal reached the evidence on 2026-09-22**
182
+ (`persistence-payload-unasserted` in `evidence.defenders.signals`, a closed
183
+ enum in the shared claimspec contract). No tool outside this repository
184
+ validated claimspec evidence at the time; DocGuard's adoption (docguard#420)
185
+ was notified rather than broken. Listed here because the enum is shared:
186
+ the next addition needs the same notice.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "testguard-cli",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "testguard-cli",
9
- "version": "0.13.0",
9
+ "version": "0.14.0",
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.13.0",
3
+ "version": "0.14.0",
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.13.0/packaging/gitlab/testguard.gitlab-ci.yml'
6
+ # - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.0/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.13.0"
32
+ default: "0.14.0"
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.13.0.tgz"
19
- sha256 "27079e0d5870ad86ccad4df967e50898eddacd9d66b2391120da58970ce57a34"
18
+ url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.14.0.tgz"
19
+ sha256 "7931292332345eee9a6f5deb118e3931f06396b06e98c41c227b0de99c78df1d"
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.13.0"
7
+ version = "0.14.0"
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" }
@@ -343,6 +343,14 @@ the suppress-up-to-count semantics are identical.
343
343
  tool then records `unasserted-annotated` with the reason — silenced, never
344
344
  hidden. A file that mocks the subject is never a discovered defender: a
345
345
  mock cannot detect a fault in what it replaced.
346
+ `persistence-payload-unasserted`: a resolved defender mocks the persistence
347
+ layer, so it can prove the call shape and never that a row landed; the
348
+ reason names the layer and the file's assertion mix. It is attached only to
349
+ a **survived** record whose fault sits on the write path — a field of a
350
+ persisted payload or the write call itself — because there it says why the
351
+ survivor was missed, and anywhere else it says nothing. A conforming
352
+ document never carries it on another verdict, on a file that is not a
353
+ resolved defender, or without its reason.
346
354
 
347
355
  ## Claimed source surface
348
356