testguard-cli 0.14.0__tar.gz → 0.14.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.gitignore +1 -0
  2. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.npmignore +1 -0
  3. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/AGENTS.md +2 -1
  4. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/CHANGELOG.md +81 -0
  5. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/PKG-INFO +14 -7
  6. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/README.md +13 -6
  7. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/action.yml +1 -1
  8. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs/testguard-explained.html +13 -11
  9. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs/testguard-explained.pdf +0 -0
  10. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs-canonical/TEST-SPEC.md +2 -2
  11. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/package-lock.json +2 -2
  12. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/package.json +1 -1
  13. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
  14. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/packaging/homebrew/testguard.rb +2 -2
  15. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/pyproject.toml +1 -1
  16. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/GATE-SEMANTICS.md +12 -3
  17. testguard_cli-0.14.2/spec/conformance/invalid/replay.nocover-when-no-test-file-remained.json +44 -0
  18. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/lib/validate.mjs +8 -0
  19. testguard_cli-0.14.2/spec/schemas/replay.schema.json +157 -0
  20. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/index.mjs +21 -2
  21. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/replay/replay.mjs +22 -1
  22. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/scaffold/producers.python.mjs +20 -1
  23. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/supply/feedback.mjs +14 -2
  24. testguard_cli-0.14.2/test/replay-no-test-file-left.test.mjs +69 -0
  25. testguard_cli-0.14.2/test/replay-python-defenders.test.mjs +87 -0
  26. testguard_cli-0.14.2/test/runner-selection-blind.test.mjs +77 -0
  27. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/scaffold.python.test.mjs +33 -0
  28. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/supply-feedback.test.mjs +28 -0
  29. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/testguard.claims.json +189 -29
  30. testguard_cli-0.14.0/.codex/hooks.json +0 -71
  31. testguard_cli-0.14.0/fixtures/known-answer-playwright/test-results/.last-run.json +0 -4
  32. testguard_cli-0.14.0/spec/schemas/replay.schema.json +0 -85
  33. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.agents/skills/testguard/SKILL.md +0 -0
  34. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.docguard.json +0 -0
  35. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.docguardignore +0 -0
  36. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.gitattributes +0 -0
  37. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  38. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  39. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  40. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/dependabot.yml +0 -0
  41. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/scripts/approve-held-runs.sh +0 -0
  42. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/scripts/build-one-pager.mjs +0 -0
  43. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/scripts/check-cost-budget.mjs +0 -0
  44. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/scripts/check-python-fixture.mjs +0 -0
  45. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/scripts/install-smoke.mjs +0 -0
  46. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/scripts/publish-homebrew-tap.sh +0 -0
  47. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/scripts/sync-release-version.mjs +0 -0
  48. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/workflows/auto-merge.yml +0 -0
  49. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/workflows/ci.yml +0 -0
  50. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/workflows/release.yml +0 -0
  51. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/workflows/scheduled-release.yml +0 -0
  52. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.github/workflows/supply-chain.yml +0 -0
  53. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.npmrc +0 -0
  54. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.pre-commit-config.yaml +0 -0
  55. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/.pre-commit-hooks.yaml +0 -0
  56. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/CLAUDE.md +0 -0
  57. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/CODE_OF_CONDUCT.md +0 -0
  58. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/CONTRIBUTING.md +0 -0
  59. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/DRIFT-LOG.md +0 -0
  60. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/LICENSE +0 -0
  61. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/PRIVACY.md +0 -0
  62. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/SECURITY.md +0 -0
  63. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/SUPPORT.md +0 -0
  64. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/bench/README.md +0 -0
  65. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/cli/testguard.mjs +0 -0
  66. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs-canonical/ARCHITECTURE.md +0 -0
  67. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs-canonical/DATA-MODEL.md +0 -0
  68. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs-canonical/ENVIRONMENT.md +0 -0
  69. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs-canonical/PRIOR-ART.md +0 -0
  70. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs-canonical/REQUIREMENTS.md +0 -0
  71. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/docs-canonical/SECURITY.md +0 -0
  72. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/.gitignore +0 -0
  73. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/README.md +0 -0
  74. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/expected.json +0 -0
  75. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/package.json +0 -0
  76. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/src/export.mjs +0 -0
  77. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/src/redact.mjs +0 -0
  78. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
  79. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/test/flaky.test.mjs +0 -0
  80. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/test/redact.test.mjs +0 -0
  81. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/testguard.claims.json +0 -0
  82. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer/vitest.config.mjs +0 -0
  83. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/.gitignore +0 -0
  84. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/README.md +0 -0
  85. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/expected.json +0 -0
  86. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/jest.config.js +0 -0
  87. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/package.json +0 -0
  88. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/src/export.js +0 -0
  89. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/src/redact.js +0 -0
  90. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
  91. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
  92. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/test/redact.test.js +0 -0
  93. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-jest/testguard.claims.json +0 -0
  94. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/.gitignore +0 -0
  95. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/README.md +0 -0
  96. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
  97. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
  98. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/expected.json +0 -0
  99. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/package-lock.json +0 -0
  100. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/package.json +0 -0
  101. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
  102. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
  103. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
  104. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
  105. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
  106. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/.gitignore +0 -0
  107. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/README.md +0 -0
  108. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/demo/__init__.py +0 -0
  109. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/demo/export.py +0 -0
  110. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/demo/orphan.py +0 -0
  111. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/demo/pipeline.py +0 -0
  112. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/demo/redact.py +0 -0
  113. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/expected.json +0 -0
  114. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/testguard.claims.json +0 -0
  115. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
  116. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
  117. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/tests/test_patched.py +0 -0
  118. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/fixtures/known-answer-python/tests/test_redact.py +0 -0
  119. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/README.md +0 -0
  120. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/baseline.json +0 -0
  121. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/brief.json +0 -0
  122. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/calibration-websec.json +0 -0
  123. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/calibration.json +0 -0
  124. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/claims-methods.json +0 -0
  125. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/claims.json +0 -0
  126. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
  127. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/evidence-negative-control.json +0 -0
  128. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/evidence-persistence-signal.json +0 -0
  129. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/evidence-provisional.json +0 -0
  130. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/evidence-python.json +0 -0
  131. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/evidence-scan-method.json +0 -0
  132. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/evidence.json +0 -0
  133. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/gate.json +0 -0
  134. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/ignore.json +0 -0
  135. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/replay.json +0 -0
  136. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/examples/status.json +0 -0
  137. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
  138. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/baseline.zero-count.json +0 -0
  139. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
  140. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
  141. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
  142. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
  143. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
  144. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
  145. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
  146. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
  147. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
  148. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
  149. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/claims.absolute-path.json +0 -0
  150. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
  151. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
  152. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
  153. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
  154. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/claims.noop-fault.json +0 -0
  155. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
  156. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
  157. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
  158. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
  159. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
  160. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
  161. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
  162. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
  163. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
  164. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
  165. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
  166. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
  167. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
  168. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
  169. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
  170. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
  171. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
  172. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.persistence-signal-not-a-defender.json +0 -0
  173. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.persistence-signal-on-a-non-survivor.json +0 -0
  174. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.persistence-signal-without-reason.json +0 -0
  175. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
  176. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
  177. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
  178. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
  179. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
  180. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
  181. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
  182. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
  183. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
  184. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
  185. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
  186. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
  187. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
  188. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
  189. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/ignore.short-reason.json +0 -0
  190. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
  191. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
  192. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/status.claim-without-file.json +0 -0
  193. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
  194. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
  195. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
  196. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
  197. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
  198. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
  199. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/conformance/schemas.test.mjs +0 -0
  200. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/lib/fingerprint.mjs +0 -0
  201. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/lib/wilson.mjs +0 -0
  202. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/baseline.schema.json +0 -0
  203. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/brief.schema.json +0 -0
  204. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/calibration.schema.json +0 -0
  205. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/claims.schema.json +0 -0
  206. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/common.schema.json +0 -0
  207. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/concerns.schema.json +0 -0
  208. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/evidence.schema.json +0 -0
  209. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/gate.schema.json +0 -0
  210. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/ignore.schema.json +0 -0
  211. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/status.schema.json +0 -0
  212. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/spec/schemas/sweep.schema.json +0 -0
  213. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/admit/admit.mjs +0 -0
  214. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/baseline/baseline.mjs +0 -0
  215. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/brief/brief.mjs +0 -0
  216. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/claims/anchors.mjs +0 -0
  217. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/claims/annotations.mjs +0 -0
  218. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/claims/load.mjs +0 -0
  219. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/claims/removed.mjs +0 -0
  220. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/cli.mjs +0 -0
  221. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/admit.mjs +0 -0
  222. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/baseline.mjs +0 -0
  223. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/brief.mjs +0 -0
  224. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/claims.mjs +0 -0
  225. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/concerns.mjs +0 -0
  226. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/gate.mjs +0 -0
  227. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/init.mjs +0 -0
  228. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/mcp.mjs +0 -0
  229. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/probe.mjs +0 -0
  230. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/replay.mjs +0 -0
  231. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/scaffold.mjs +0 -0
  232. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/status.mjs +0 -0
  233. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/commands/sweep.mjs +0 -0
  234. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/evidence/writer.mjs +0 -0
  235. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/gate/changed.mjs +0 -0
  236. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/git.mjs +0 -0
  237. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/init/init.mjs +0 -0
  238. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/init/templates/SKILL.md +0 -0
  239. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/mcp/server.mjs +0 -0
  240. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/mcp/tools.mjs +0 -0
  241. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/attribution.mjs +0 -0
  242. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/classify.mjs +0 -0
  243. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/contention.mjs +0 -0
  244. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/cost.mjs +0 -0
  245. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/discover.mjs +0 -0
  246. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/independence.mjs +0 -0
  247. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/inject.mjs +0 -0
  248. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/mocks.mjs +0 -0
  249. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/probe.mjs +0 -0
  250. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/progress.mjs +0 -0
  251. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/pyimports.mjs +0 -0
  252. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/rank.mjs +0 -0
  253. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/jest.mjs +0 -0
  254. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/playwright.mjs +0 -0
  255. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
  256. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/python/_testguard_report.py +0 -0
  257. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
  258. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/python.mjs +0 -0
  259. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/shared.mjs +0 -0
  260. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/runners/vitest.mjs +0 -0
  261. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/probe/worktree.mjs +0 -0
  262. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/render.mjs +0 -0
  263. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/replay/label.mjs +0 -0
  264. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/scaffold/producers.mjs +0 -0
  265. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/scaffold/scaffold.mjs +0 -0
  266. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/status/status.mjs +0 -0
  267. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/status/surface.mjs +0 -0
  268. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/supply/concerns.mjs +0 -0
  269. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/supply/persistence.mjs +0 -0
  270. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/supply/savepath.mjs +0 -0
  271. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/supply/select.mjs +0 -0
  272. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/sweep/sweep.mjs +0 -0
  273. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/util/glob.mjs +0 -0
  274. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/src/util/hash.mjs +0 -0
  275. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/admit.test.mjs +0 -0
  276. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/annotations.test.mjs +0 -0
  277. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/attribution.test.mjs +0 -0
  278. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/baseline.test.mjs +0 -0
  279. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/brief.test.mjs +0 -0
  280. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/calibration.test.mjs +0 -0
  281. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/claim-selection.test.mjs +0 -0
  282. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/claims-anchors.test.mjs +0 -0
  283. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/claims-since.test.mjs +0 -0
  284. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/classify.test.mjs +0 -0
  285. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/contention.test.mjs +0 -0
  286. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/cost.test.mjs +0 -0
  287. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/discover.test.mjs +0 -0
  288. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/evidence-persistence-signal.test.mjs +0 -0
  289. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/gate.test.mjs +0 -0
  290. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/gitlab-template.test.mjs +0 -0
  291. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/glob.test.mjs +0 -0
  292. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/independence.test.mjs +0 -0
  293. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/init.test.mjs +0 -0
  294. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/inject.test.mjs +0 -0
  295. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/label.test.mjs +0 -0
  296. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/mcp.test.mjs +0 -0
  297. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/mocks.test.mjs +0 -0
  298. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/negative-control.test.mjs +0 -0
  299. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/one-pager.test.mjs +0 -0
  300. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/probe-error.test.mjs +0 -0
  301. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/probe-persistence-signal.test.mjs +0 -0
  302. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/probe-preconditions.test.mjs +0 -0
  303. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/probe.fixture.test.mjs +0 -0
  304. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/probe.jest.test.mjs +0 -0
  305. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/probe.playwright.test.mjs +0 -0
  306. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/probe.python.test.mjs +0 -0
  307. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/progress.test.mjs +0 -0
  308. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/pyimports.test.mjs +0 -0
  309. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/rank-aliases.test.mjs +0 -0
  310. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/release-approval.test.mjs +0 -0
  311. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/release-sync.test.mjs +0 -0
  312. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/replay-verdict.test.mjs +0 -0
  313. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/replay.test.mjs +0 -0
  314. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/runner-argv.test.mjs +0 -0
  315. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/runner-command.test.mjs +0 -0
  316. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/runner-playwright.test.mjs +0 -0
  317. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/runner-python.test.mjs +0 -0
  318. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/runner-vitest.test.mjs +0 -0
  319. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/samples/playwright-report.json +0 -0
  320. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/samples/python-report-collect-error.json +0 -0
  321. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/samples/python-report.json +0 -0
  322. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/scaffold.test.mjs +0 -0
  323. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/snapshot.test.mjs +0 -0
  324. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/status.test.mjs +0 -0
  325. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/supply-concerns.test.mjs +0 -0
  326. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/supply-persistence.test.mjs +0 -0
  327. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/supply-savepath.test.mjs +0 -0
  328. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/supply-select.test.mjs +0 -0
  329. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/surface.test.mjs +0 -0
  330. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/sweep.fixture.test.mjs +0 -0
  331. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/sweep.test.mjs +0 -0
  332. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/vitest-config.test.mjs +0 -0
  333. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/worktree.test.mjs +0 -0
  334. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/test/writer.test.mjs +0 -0
  335. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/testguard.cost-budget.json +0 -0
  336. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/testguard.ignore.json +0 -0
  337. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/testguard_cli/__init__.py +0 -0
  338. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/testguard_cli/wrapper.py +0 -0
  339. {testguard_cli-0.14.0 → testguard_cli-0.14.2}/vitest.config.mjs +0 -0
@@ -14,6 +14,7 @@ coverage/
14
14
  # Local agent tooling — not part of the public project
15
15
  .wolf/
16
16
  .claude/
17
+ .codex/
17
18
  .agent/
18
19
  CLAUDE.local.md
19
20
  graphify-out/
@@ -3,6 +3,7 @@
3
3
  .github/
4
4
  .wolf/
5
5
  .claude/
6
+ .codex/
6
7
  graphify-out/
7
8
  test/
8
9
  fixtures/
@@ -1,6 +1,6 @@
1
1
  # AI Agent Instructions — TestGuard
2
2
 
3
- <!-- docguard:last-reviewed 2026-09-19 -->
3
+ <!-- docguard:last-reviewed 2026-09-22 -->
4
4
 
5
5
  Read this before changing anything. It is short on purpose.
6
6
 
@@ -17,6 +17,7 @@ their first consumer.
17
17
 
18
18
  ```bash
19
19
  npm test # unit + fixture acceptance (~15s)
20
+ npm ci --prefix fixtures/known-answer-playwright # once; without it the Playwright fixture acceptance silently skips
20
21
  npm run test:spec # conformance suite only
21
22
  npm run self:probe # TestGuard probes its own claims; must exit 0
22
23
  npm run test:install # the packed tarball must run with production deps only
@@ -7,6 +7,87 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.14.2] - 2026-09-28
11
+
12
+ Automated weekly release — everything merged since `v0.14.1`.
13
+
14
+ ### Changed
15
+
16
+ - chore(homebrew): sha256 for v0.14.1 (#171)
17
+
18
+
19
+ ## [0.14.1] - 2026-09-27
20
+
21
+ Automated weekly release — everything merged since `v0.14.0`.
22
+
23
+ ### Changed
24
+
25
+ - fix: four defects that made `nocover` report discovery failures as findings (#169)
26
+ - chore: untrack local agent tooling and a generated fixture artifact (#168)
27
+ - chore(homebrew): sha256 for v0.14.0 (#167)
28
+ - docs(brief): page 6 carries what 0.14.0 changed there (#166)
29
+
30
+
31
+ ### Fixed
32
+
33
+ - **`sweep` no longer refuses to write after a sweep that learned nothing.**
34
+ `learnedProductivity` named every evidence document it *read* as an ordering
35
+ source, while `observed` counts only `killed` and `survived` records. A
36
+ document whose records are all `nocover` — the ordinary shape of a first
37
+ sweep — therefore produced `sources: [...]` with `observed: 0`, which the
38
+ sweep validator correctly refuses. Because that document is written to
39
+ `.testguard/sweep-evidence.json`, **every later sweep failed the same way,
40
+ permanently**, until someone deleted a gitignored file nothing mentioned. An
41
+ ordering now names only the documents that contributed an observation, which
42
+ is what "the documents it was learned from" always meant. Measured on an
43
+ external corpus: this fired on 10 of 34 sweep invocations.
44
+ - **`replay` resolves Python importers with the Python resolver.**
45
+ `replay` matched test files against a reverted source file with the
46
+ JavaScript importer for every language. `from pkg.mod import x` carries no
47
+ quoted specifier, so nothing ever matched, the defender set was always empty
48
+ for Python, and **every Python verdict was `nocover`** — "no test imports the
49
+ reverted source" — however complete the suite. `discoverDefendersDetailed`
50
+ has always branched on the target's language; `replay` now does the same.
51
+ - **A runner that matches no test file is no longer selected silently.**
52
+ `--runner auto` tries vitest, jest, then python. A TypeScript project on
53
+ Playwright resolves neither JavaScript runner, so `auto` reached python
54
+ wherever a `python3` existed, globbed for `.py`, found none, and reported
55
+ every fault as `nocover`. `auto` now prefers a resolvable runner that can see
56
+ test files, and when none can it says so (`no runner matched a test file …
57
+ every verdict will be nocover`) instead of letting discovery's failure read
58
+ as a finding about the project. An explicitly named `--runner` is still
59
+ honoured; every selection now reports `testFiles`.
60
+
61
+ - **`replay` no longer reports `nocover` when it removed the only test file.**
62
+ Removing the fix's own test removes the whole FILE, and on a project with
63
+ few, large test files that also removes tests which pre-dated the fix and
64
+ might have caught the bug. Nothing is left to run, so nothing can be
65
+ concluded: the verdict is now `unverifiable` with reason
66
+ `the-fix-shipped-the-only-test-file`. `nocover` is a statement about the
67
+ PROJECT — "no test exercises this" — and it enters the calibration as a
68
+ miss, so the old behaviour charged a project for evidence the measurement
69
+ itself destroyed. The validator now refuses the mislabelled form, and
70
+ `GATE-SEMANTICS.md` carries it as a third rule beside de-duplication and
71
+ test removal.
72
+
73
+ ### Changed
74
+
75
+ - **Python: a module-level dunder assignment is no longer proposed as a
76
+ fault.** `__version__`, `__all__` and `__author__` at module level are
77
+ metadata nobody writes a test for, so a survivor on one teaches the
78
+ survival-learned ordering to prefer a barren class — the same reasoning that
79
+ already sets a presentational JSX element aside. Narrow on purpose: a
80
+ module-level *constant* such as `DEFAULT_MAX_SIZE = 100` is still proposed,
81
+ because removing a real default changes a real default.
82
+
83
+ ### Changed
84
+
85
+ - The brief's page 6 says what 0.14.0 changed there: the evidence now names
86
+ the mocked layer and the assertion shape beside a save-path survivor's
87
+ verdict, and the fourth rule of the lower rungs — whatever a sweep does not
88
+ probe, it counts, presentational elements included. Re-rendered; still eight
89
+ sheets from eight pages.
90
+
10
91
  ## [0.14.0] - 2026-09-22
11
92
 
12
93
  Automated weekly release — everything merged since `v0.13.0`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: testguard-cli
3
- Version: 0.14.0
3
+ Version: 0.14.2
4
4
  Summary: Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest. Ships the Node.js CLI (requires Node.js 20+).
5
5
  Project-URL: Homepage, https://github.com/raccioly/testguard
6
6
  Project-URL: Documentation, https://github.com/raccioly/testguard#readme
@@ -151,9 +151,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
151
151
  | npm | `npm i -D testguard-cli` then `npx testguard probe` |
152
152
  | pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
153
153
  | Homebrew | `brew tap raccioly/tap && brew install testguard` |
154
- | GitHub Action | `uses: raccioly/testguard@v0.14.0` — see [`action.yml`](./action.yml) |
154
+ | GitHub Action | `uses: raccioly/testguard@v0.14.2` — see [`action.yml`](./action.yml) |
155
155
  | pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
156
- | GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.14.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.2/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
157
157
 
158
158
  Projects that set `min-release-age` in `.npmrc` cannot see a version published
159
159
  less than that many days ago (`ENOVERSIONS`); install that one with
@@ -687,12 +687,12 @@ detected base that does not resolve is a warning for `status` and `brief`
687
687
  # GitHub Actions
688
688
  - uses: actions/checkout@v4
689
689
  with: { fetch-depth: 0 }
690
- - uses: raccioly/testguard@v0.14.0
690
+ - uses: raccioly/testguard@v0.14.2
691
691
  with: { command: gate }
692
692
 
693
693
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
694
694
  include:
695
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.0/packaging/gitlab/testguard.gitlab-ci.yml'
695
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.2/packaging/gitlab/testguard.gitlab-ci.yml'
696
696
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
697
697
  ```
698
698
 
@@ -1007,13 +1007,20 @@ The table above is JavaScript. `scaffold` reads Python too, and proposes the
1007
1007
  same fault classes in Python syntax: `if <guard>:` → `if False:`,
1008
1008
  `return <check>` → `return True`, `verify=True` → `verify=False`, a
1009
1009
  parameter-derived argument swapped for `None`, a key dropped from a payload
1010
- `dict` or an allow-list. Two differences are deliberate. A statement is
1010
+ `dict` or an allow-list. Three differences are deliberate. A statement is
1011
1011
  removed by replacing it with `pass`, never by deleting the line, because a
1012
1012
  block whose only statement is gone is an `IndentationError`; and a line that
1013
1013
  leaves a bracket open (`COLOURS = {`), or sits inside one opened on an earlier
1014
1014
  line (`help="…"` in an `add_argument(`), is never removed at all. Both exist
1015
1015
  because a fault that cannot compile is a `fault-invalid` verdict — a probe run
1016
- spent saying nothing about the tests. The two JSX shapes (`element-removed`,
1016
+ spent saying nothing about the tests. Third, a **module-level dunder
1017
+ assignment** (`__version__`, `__all__`, `__author__`) is not proposed. Deleting
1018
+ one does change behaviour, so it is not an equivalent mutant; it is something
1019
+ nobody writes a test for, which is the disqualification an icon already gets on
1020
+ the JSX side — a survivor there teaches the survival-learned ordering to prefer
1021
+ a barren class. The rule is narrow on purpose: a module-level *constant*
1022
+ (`DEFAULT_MAX_SIZE = 100`) is still proposed, because removing a real default
1023
+ changes a real default. The two JSX shapes (`element-removed`,
1017
1024
  `handler-dropped`) have no Python meaning and are absent rather than faked.
1018
1025
 
1019
1026
  Every proposal's `find` is the exact line with `expectHits`/`occurrence`
@@ -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.14.0` — see [`action.yml`](./action.yml) |
130
+ | GitHub Action | `uses: raccioly/testguard@v0.14.2` — see [`action.yml`](./action.yml) |
131
131
  | pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
132
- | GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.14.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.2/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
133
133
 
134
134
  Projects that set `min-release-age` in `.npmrc` cannot see a version published
135
135
  less than that many days ago (`ENOVERSIONS`); install that one with
@@ -663,12 +663,12 @@ detected base that does not resolve is a warning for `status` and `brief`
663
663
  # GitHub Actions
664
664
  - uses: actions/checkout@v4
665
665
  with: { fetch-depth: 0 }
666
- - uses: raccioly/testguard@v0.14.0
666
+ - uses: raccioly/testguard@v0.14.2
667
667
  with: { command: gate }
668
668
 
669
669
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
670
670
  include:
671
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.0/packaging/gitlab/testguard.gitlab-ci.yml'
671
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.2/packaging/gitlab/testguard.gitlab-ci.yml'
672
672
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
673
673
  ```
674
674
 
@@ -983,13 +983,20 @@ The table above is JavaScript. `scaffold` reads Python too, and proposes the
983
983
  same fault classes in Python syntax: `if <guard>:` → `if False:`,
984
984
  `return <check>` → `return True`, `verify=True` → `verify=False`, a
985
985
  parameter-derived argument swapped for `None`, a key dropped from a payload
986
- `dict` or an allow-list. Two differences are deliberate. A statement is
986
+ `dict` or an allow-list. Three differences are deliberate. A statement is
987
987
  removed by replacing it with `pass`, never by deleting the line, because a
988
988
  block whose only statement is gone is an `IndentationError`; and a line that
989
989
  leaves a bracket open (`COLOURS = {`), or sits inside one opened on an earlier
990
990
  line (`help="…"` in an `add_argument(`), is never removed at all. Both exist
991
991
  because a fault that cannot compile is a `fault-invalid` verdict — a probe run
992
- spent saying nothing about the tests. The two JSX shapes (`element-removed`,
992
+ spent saying nothing about the tests. Third, a **module-level dunder
993
+ assignment** (`__version__`, `__all__`, `__author__`) is not proposed. Deleting
994
+ one does change behaviour, so it is not an equivalent mutant; it is something
995
+ nobody writes a test for, which is the disqualification an icon already gets on
996
+ the JSX side — a survivor there teaches the survival-learned ordering to prefer
997
+ a barren class. The rule is narrow on purpose: a module-level *constant*
998
+ (`DEFAULT_MAX_SIZE = 100`) is still proposed, because removing a real default
999
+ changes a real default. The two JSX shapes (`element-removed`,
993
1000
  `handler-dropped`) have no Python meaning and are absent rather than faked.
994
1001
 
995
1002
  Every proposal's `find` is the exact line with `expectHits`/`occurrence`
@@ -46,7 +46,7 @@ inputs:
46
46
  version:
47
47
  description: 'testguard-cli version to run'
48
48
  required: false
49
- default: '0.14.0'
49
+ default: '0.14.2'
50
50
  node-version:
51
51
  description: 'Node.js version (20+)'
52
52
  required: false
@@ -144,7 +144,7 @@
144
144
  <div class="page">
145
145
  <div class="masthead">
146
146
  <div class="wordmark">Test<span class="mark">Guard</span></div>
147
- <div class="meta">Technical Brief · v0.14.0 · MIT</div>
147
+ <div class="meta">Technical Brief · v0.14.2 · MIT</div>
148
148
  </div>
149
149
 
150
150
  <h1>Your tests pass. That is not the same as your tests working.</h1>
@@ -752,8 +752,8 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
752
752
 
753
753
  <section class="rule-top">
754
754
  <h2>Field report C — where a save button actually fails</h2>
755
- <p>Twenty-six faults injected on the persistence path of seven server actions in a real
756
- AI-authored application: the database write itself, and the fields of the payload it carries.</p>
755
+ <p>Twenty-six faults on the persistence path of seven server actions in a real AI-authored
756
+ application: the write itself, and the fields it carries.</p>
757
757
  <figure style="margin-top:11px">
758
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"/>
@@ -781,7 +781,9 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
781
781
  <p class="note">The third number is the read-back answer. A test that replaced the database proves the
782
782
  call shape and nothing beyond it — a <code>where</code> matching no rows, a rolled-back transaction, a
783
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
+ persistence; it is evidence that nothing could have measured it, and the tool says so before any verdict.
785
+ The evidence now names the mocked layer and the assertion shape — exact, partial or argument-free —
786
+ beside the survivor’s verdict.</p>
785
787
  </section>
786
788
 
787
789
  <section class="rule-top">
@@ -792,12 +794,11 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
792
794
  <p class="note"><code>sweep</code> takes the files the gate just called uncovered, proposes
793
795
  faults mechanically, probes a bounded selection, and reports what nothing noticed — ordering
794
796
  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>
797
+ Google’s productive rate from 15% to 89%.</p>
796
798
  </div>
797
799
  <div>
798
800
  <h3>A sentence or ten</h3>
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
+ <p class="note">A <em>concern</em> names a kind of promise and where to look. Five lines of JSON — “every admin route refuses a caller without the admin role” — found a
801
802
  route whose owner/manager check can be forced to <code>false</code> with no test failing. One
802
803
  sentence, twenty-four routes, no model.</p>
803
804
  </div>
@@ -807,15 +808,16 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
807
808
  and a reproducer already in hand.</p>
808
809
  </div>
809
810
  </div>
810
- <p class="note" style="margin-top:9px">Three rules keep the lower rungs safe. A sweep <strong>never
811
+ <p class="note" style="margin-top:9px">Four rules keep the lower rungs safe. A sweep <strong>never
811
812
  writes the claims file</strong> — a sentence nobody wrote is not a claim. Its evidence <strong>never
812
813
  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>
814
+ only a survivor or an untested file exits non-zero. And whatever it does not probe, it counts —
815
+ past the cap, outside the concern, or presentational: an icon or a static wrapper nobody would test.</p>
814
816
  </section>
815
817
 
816
818
  <div class="pull">
817
- <p>A sweep is weaker evidence than a probe, and says so. Nobody stated that the behaviour mattered.
818
- It is stronger than nothing, which is what a repository with no claims has.</p>
819
+ <p>A sweep is weaker evidence than a probe, and says so. It is stronger than nothing,
820
+ which is what a repository with no claims has.</p>
819
821
  <div class="attrib">Why the ladder has a bottom rung</div>
820
822
  </div>
821
823
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- docguard:version 1.0.0 -->
4
4
  <!-- docguard:status approved -->
5
- <!-- docguard:last-reviewed 2026-09-19 -->
5
+ <!-- docguard:last-reviewed 2026-09-22 -->
6
6
  <!-- docguard:owner @raccioly -->
7
7
  <!-- docguard:quality negation-load off — the governing rule is a prohibition (a test that would pass with the behaviour broken is not a test), and the verdict rules are defined by what does not count as detection. -->
8
8
 
@@ -20,7 +20,7 @@ the test that defends it, and CI fails if any injected fault survives.
20
20
  | Unit | `test/*.test.mjs` | One pure function's branches, without a runner | milliseconds |
21
21
  | Conformance | `spec/conformance/` | Every valid example validates; every must-reject document is rejected for the defect its filename names | ~1 s |
22
22
  | Anchor preflight | `test/claims-anchors.test.mjs` | Exact anchors still locate, supported replacements parse, the check stays below one second, and CI reaches it before self-probe | <1 s |
23
- | Fixture acceptance | `test/probe.fixture.test.mjs`, `test/probe.jest.test.mjs`, `test/probe.playwright.test.mjs` | The whole pipeline reproduces a known-answer oracle, one per runner | 10–50 s each |
23
+ | Fixture acceptance | `test/probe.fixture.test.mjs`, `test/probe.jest.test.mjs`, `test/probe.playwright.test.mjs` | The whole pipeline reproduces a known-answer oracle, one per runner. The Playwright fixture carries its own dependencies: without `npm ci --prefix fixtures/known-answer-playwright` that file skips, and a skipped acceptance is not a passing one | 10–50 s each |
24
24
  | Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **20.2 min** cold (measured by the v0.10.1 release job on 2026-09-19 at `70e9d61`: 154 faults, 924 defender runs, 1210 s); seconds when verdicts are reused, which is why PR CI restores previous evidence from cache. Budgeted at 1440 s in `testguard.cost-budget.json` for the additional four-fault live-tap claim and **gated** in CI — cost growth is reviewed instead of disappearing into a passing log |
25
25
  | Install smoke | `.github/scripts/install-smoke.mjs` | The **packed tarball** runs with production dependencies only | ~20 s |
26
26
  | Runtime budget | `ci.yml` | The suite has not silently started walking the wrong tree | gate, not a test |
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "testguard-cli",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "testguard-cli",
9
- "version": "0.14.0",
9
+ "version": "0.14.2",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "ajv": "8.20.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testguard-cli",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -3,7 +3,7 @@
3
3
  # Include it from a project's .gitlab-ci.yml, pinned to a release:
4
4
  #
5
5
  # include:
6
- # - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.0/packaging/gitlab/testguard.gitlab-ci.yml'
6
+ # - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.2/packaging/gitlab/testguard.gitlab-ci.yml'
7
7
  # inputs:
8
8
  # dir: backend # where testguard.claims.json lives (default ".")
9
9
  # post_note: true # post the brief as a merge-request note (needs TESTGUARD_GITLAB_TOKEN, api scope)
@@ -29,7 +29,7 @@ spec:
29
29
  inputs:
30
30
  version:
31
31
  description: testguard-cli version to run
32
- default: "0.14.0"
32
+ default: "0.14.2"
33
33
  dir:
34
34
  description: directory whose test runner is invoked (where testguard.claims.json lives)
35
35
  default: "."
@@ -15,8 +15,8 @@
15
15
  class Testguard < Formula
16
16
  desc "Proves a test suite defends the claims a project makes"
17
17
  homepage "https://github.com/raccioly/testguard"
18
- url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.14.0.tgz"
19
- sha256 "7931292332345eee9a6f5deb118e3931f06396b06e98c41c227b0de99c78df1d"
18
+ url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.14.2.tgz"
19
+ sha256 "42651c054396fdf7506605ee19c198611713f698931755d3acf3656d7b96d8cb"
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.14.0"
7
+ version = "0.14.2"
8
8
  description = "Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest. Ships the Node.js CLI (requires Node.js 20+)."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -197,8 +197,8 @@ Its verdicts mirror the probe's, for the same reasons:
197
197
  |---|---|
198
198
  | `caught` | a remaining test failed **by assertion** on the reverted source, every run. The suite knew. |
199
199
  | `blind` | the suite stayed green on known-broken code. |
200
- | `nocover` | no test imports the reverted files. Worse than `blind`: nothing was even tried. |
201
- | `unverifiable` | the revert did not apply, the suite failed to load, or it timed out. Carries a `reason`. |
200
+ | `nocover` | no test imports the reverted files, **and at least one test file remained to ask**. Worse than `blind`: nothing was even tried. |
201
+ | `unverifiable` | the revert did not apply, the suite failed to load, it timed out, or removing the fix's own test left **no test file at all** (`the-fix-shipped-the-only-test-file`). Carries a `reason`. |
202
202
  | `flaky` | the runs disagreed. A flaky failure reads as "the suite caught it", so flakiness biases this metric **optimistically** — mixed runs are never `caught`. |
203
203
 
204
204
  Two rules that follow:
@@ -208,6 +208,13 @@ Two rules that follow:
208
208
  twice corrupts the corpus a calibration is computed from.
209
209
  2. **The fix's own test is removed before the run.** It proves nothing about
210
210
  what the suite knew before the fix existed.
211
+ 3. **If that leaves no test file, the run is `unverifiable`, not `nocover`.**
212
+ Removal takes the whole FILE, so on a project with few, large test files it
213
+ also removes tests that pre-dated the fix and might have caught the bug. The
214
+ suite the measurement needed no longer exists, so nothing can be concluded.
215
+ `nocover` is a statement about the PROJECT — "no test exercises this" — and
216
+ using it here would charge the project, in the calibration, for evidence the
217
+ method itself destroyed.
211
218
 
212
219
  `caught`, `blind` and `nocover` are measurements and enter the calibration
213
220
  ratio: `caught` as a hit, the other two as misses. `nocover` counts as a miss
@@ -216,7 +223,9 @@ because it is one — the worst kind. Leaving it out would let a project with
216
223
  since its worst outcomes would leave the denominator before the ratio is
217
224
  taken; Stryker and PIT count no-coverage mutants against the headline score
218
225
  for the same reason. `flaky` and `unverifiable` are failed measurements and
219
- enter neither side.
226
+ enter neither side — which is why rule 3 above matters: a run with no test
227
+ file left is a failed measurement wearing `nocover`'s clothes, and left
228
+ unclassified it would inflate every miss rate computed from the corpus.
220
229
 
221
230
  ### Calibration arithmetic is reproducible
222
231
 
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "../../schemas/replay.schema.json",
3
+ "schemaVersion": 1,
4
+ "tool": {
5
+ "name": "testguard",
6
+ "version": "0.5.0"
7
+ },
8
+ "run": {
9
+ "id": "replay-20260917T220000",
10
+ "startedAt": "2026-09-17T22:00:00Z",
11
+ "finishedAt": "2026-09-17T22:14:00Z",
12
+ "range": "HEAD~50..HEAD",
13
+ "runner": {
14
+ "name": "vitest",
15
+ "version": "5.0.1"
16
+ },
17
+ "confirmRuns": 3,
18
+ "candidates": 12,
19
+ "duplicates": 4
20
+ },
21
+ "records": [
22
+ {
23
+ "commit": "4a1c9f2e77b0d3a8c5e61f04b9d27a3e8c6f01b2",
24
+ "patchId": "9f2e77b0d3a8c5e61f04b9d27a3e8c6f01b24a1c",
25
+ "subject": "fix(redact): keep the raw input out of the audit row",
26
+ "verdict": "nocover",
27
+ "source": [
28
+ "src/redact.mjs"
29
+ ],
30
+ "removedTests": [
31
+ "test/audit-fix.test.mjs"
32
+ ],
33
+ "ranTests": 0,
34
+ "faultClass": "field-dropped",
35
+ "runs": [
36
+ {
37
+ "outcome": "error",
38
+ "durationMs": 0
39
+ }
40
+ ],
41
+ "reason": "the-fix-shipped-the-only-test-file"
42
+ }
43
+ ]
44
+ }
@@ -297,6 +297,14 @@ const semantic = {
297
297
  }
298
298
  if (r.verdict === 'nocover' && r.ranTests) errors.push({ path: `${p}/ranTests`, message: 'nocover means no test exercises the reverted files; it cannot have run tests' });
299
299
  if (r.verdict === 'unverifiable' && !r.reason) errors.push({ path: `${p}/reason`, message: 'unverifiable requires a reason (e.g. revert-did-not-apply, suite-failed-to-load)' });
300
+ // A run that had no test file left is a FAILED MEASUREMENT, never a
301
+ // finding about the project: removing the fix's test removed the file it
302
+ // lived in, and with it any pre-existing test that might have caught the
303
+ // bug. Recording that as `nocover` would put it in the calibration as a
304
+ // miss and charge the project for evidence the method destroyed.
305
+ if (r.reason === 'the-fix-shipped-the-only-test-file' && r.verdict !== 'unverifiable') {
306
+ errors.push({ path: `${p}/verdict`, message: 'no test file remained after removing the fix\'s own, so nothing could be concluded: that is unverifiable, not a finding about the project' });
307
+ }
300
308
  });
301
309
  return errors;
302
310
  },