testguard-cli 0.9.1__tar.gz → 0.9.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 (301) hide show
  1. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.agents/skills/testguard/SKILL.md +3 -0
  2. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/workflows/ci.yml +7 -0
  3. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/CHANGELOG.md +19 -0
  4. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/PKG-INFO +17 -7
  5. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/README.md +16 -6
  6. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/action.yml +1 -1
  7. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/docs/testguard-explained.html +1 -1
  8. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/docs/testguard-explained.pdf +0 -0
  9. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/docs-canonical/ARCHITECTURE.md +6 -3
  10. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/docs-canonical/DATA-MODEL.md +6 -1
  11. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/docs-canonical/REQUIREMENTS.md +3 -1
  12. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/docs-canonical/TEST-SPEC.md +6 -1
  13. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/package-lock.json +2 -2
  14. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/package.json +1 -1
  15. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
  16. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/packaging/homebrew/testguard.rb +2 -2
  17. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/pyproject.toml +1 -1
  18. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/GATE-SEMANTICS.md +20 -0
  19. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/status.json +1 -0
  20. testguard_cli-0.9.2/spec/conformance/invalid/status.invalid-anchor-hidden.json +27 -0
  21. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/lib/validate.mjs +4 -1
  22. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/brief.schema.json +1 -1
  23. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/status.schema.json +20 -1
  24. testguard_cli-0.9.2/src/claims/anchors.mjs +218 -0
  25. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/cli.mjs +5 -2
  26. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/claims.mjs +10 -2
  27. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/init/templates/SKILL.md +5 -1
  28. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/status/status.mjs +19 -0
  29. testguard_cli-0.9.2/test/claims-anchors.test.mjs +90 -0
  30. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/gate.test.mjs +11 -0
  31. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/mcp.test.mjs +11 -0
  32. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/probe.fixture.test.mjs +1 -1
  33. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/status.test.mjs +26 -0
  34. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/testguard.claims.json +64 -0
  35. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/testguard.ignore.json +7 -0
  36. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.codex/hooks.json +0 -0
  37. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.docguard.json +0 -0
  38. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.docguardignore +0 -0
  39. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.gitattributes +0 -0
  40. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  41. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  42. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  43. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/dependabot.yml +0 -0
  44. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/scripts/build-one-pager.mjs +0 -0
  45. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/scripts/check-cost-budget.mjs +0 -0
  46. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/scripts/check-python-fixture.mjs +0 -0
  47. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/scripts/install-smoke.mjs +0 -0
  48. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/scripts/sync-release-version.mjs +0 -0
  49. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/workflows/auto-merge.yml +0 -0
  50. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/workflows/release.yml +0 -0
  51. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/workflows/scheduled-release.yml +0 -0
  52. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.github/workflows/supply-chain.yml +0 -0
  53. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.gitignore +0 -0
  54. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.npmignore +0 -0
  55. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.npmrc +0 -0
  56. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.pre-commit-config.yaml +0 -0
  57. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/.pre-commit-hooks.yaml +0 -0
  58. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/AGENTS.md +0 -0
  59. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/CLAUDE.md +0 -0
  60. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/CODE_OF_CONDUCT.md +0 -0
  61. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/CONTRIBUTING.md +0 -0
  62. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/DRIFT-LOG.md +0 -0
  63. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/LICENSE +0 -0
  64. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/PRIVACY.md +0 -0
  65. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/SECURITY.md +0 -0
  66. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/SUPPORT.md +0 -0
  67. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/bench/README.md +0 -0
  68. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/cli/testguard.mjs +0 -0
  69. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/docs-canonical/ENVIRONMENT.md +0 -0
  70. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/docs-canonical/SECURITY.md +0 -0
  71. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/.gitignore +0 -0
  72. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/README.md +0 -0
  73. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/expected.json +0 -0
  74. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/package.json +0 -0
  75. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/src/export.mjs +0 -0
  76. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/src/redact.mjs +0 -0
  77. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
  78. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/test/flaky.test.mjs +0 -0
  79. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/test/redact.test.mjs +0 -0
  80. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/testguard.claims.json +0 -0
  81. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer/vitest.config.mjs +0 -0
  82. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/.gitignore +0 -0
  83. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/README.md +0 -0
  84. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/expected.json +0 -0
  85. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/jest.config.js +0 -0
  86. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/package.json +0 -0
  87. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/src/export.js +0 -0
  88. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/src/redact.js +0 -0
  89. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
  90. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
  91. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/test/redact.test.js +0 -0
  92. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-jest/testguard.claims.json +0 -0
  93. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/.gitignore +0 -0
  94. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/README.md +0 -0
  95. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
  96. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
  97. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/expected.json +0 -0
  98. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/package-lock.json +0 -0
  99. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/package.json +0 -0
  100. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
  101. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
  102. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
  103. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
  104. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
  105. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
  106. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/.gitignore +0 -0
  107. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/README.md +0 -0
  108. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/demo/__init__.py +0 -0
  109. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/demo/export.py +0 -0
  110. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/demo/orphan.py +0 -0
  111. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/demo/pipeline.py +0 -0
  112. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/demo/redact.py +0 -0
  113. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/expected.json +0 -0
  114. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/testguard.claims.json +0 -0
  115. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
  116. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
  117. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/tests/test_patched.py +0 -0
  118. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/fixtures/known-answer-python/tests/test_redact.py +0 -0
  119. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/README.md +0 -0
  120. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/baseline.json +0 -0
  121. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/brief.json +0 -0
  122. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/calibration-websec.json +0 -0
  123. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/calibration.json +0 -0
  124. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/claims-methods.json +0 -0
  125. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/claims.json +0 -0
  126. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
  127. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/evidence-negative-control.json +0 -0
  128. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/evidence-provisional.json +0 -0
  129. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/evidence-python.json +0 -0
  130. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/evidence-scan-method.json +0 -0
  131. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/evidence.json +0 -0
  132. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/gate.json +0 -0
  133. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/ignore.json +0 -0
  134. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/examples/replay.json +0 -0
  135. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
  136. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/baseline.zero-count.json +0 -0
  137. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
  138. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
  139. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
  140. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
  141. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
  142. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
  143. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
  144. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
  145. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
  146. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
  147. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/claims.absolute-path.json +0 -0
  148. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
  149. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
  150. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
  151. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
  152. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/claims.noop-fault.json +0 -0
  153. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
  154. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
  155. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
  156. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
  157. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
  158. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
  159. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
  160. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
  161. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
  162. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
  163. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
  164. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
  165. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
  166. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
  167. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
  168. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
  169. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
  170. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
  171. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
  172. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
  173. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
  174. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
  175. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
  176. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
  177. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
  178. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
  179. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
  180. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
  181. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
  182. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
  183. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
  184. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/ignore.short-reason.json +0 -0
  185. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
  186. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
  187. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/status.claim-without-file.json +0 -0
  188. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
  189. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
  190. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
  191. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/conformance/schemas.test.mjs +0 -0
  192. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/lib/fingerprint.mjs +0 -0
  193. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/lib/wilson.mjs +0 -0
  194. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/baseline.schema.json +0 -0
  195. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/calibration.schema.json +0 -0
  196. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/claims.schema.json +0 -0
  197. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/common.schema.json +0 -0
  198. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/evidence.schema.json +0 -0
  199. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/gate.schema.json +0 -0
  200. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/ignore.schema.json +0 -0
  201. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/spec/schemas/replay.schema.json +0 -0
  202. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/admit/admit.mjs +0 -0
  203. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/baseline/baseline.mjs +0 -0
  204. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/brief/brief.mjs +0 -0
  205. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/claims/annotations.mjs +0 -0
  206. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/claims/load.mjs +0 -0
  207. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/claims/removed.mjs +0 -0
  208. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/admit.mjs +0 -0
  209. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/baseline.mjs +0 -0
  210. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/brief.mjs +0 -0
  211. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/gate.mjs +0 -0
  212. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/init.mjs +0 -0
  213. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/mcp.mjs +0 -0
  214. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/probe.mjs +0 -0
  215. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/replay.mjs +0 -0
  216. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/scaffold.mjs +0 -0
  217. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/commands/status.mjs +0 -0
  218. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/evidence/writer.mjs +0 -0
  219. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/gate/changed.mjs +0 -0
  220. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/git.mjs +0 -0
  221. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/init/init.mjs +0 -0
  222. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/mcp/server.mjs +0 -0
  223. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/mcp/tools.mjs +0 -0
  224. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/attribution.mjs +0 -0
  225. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/classify.mjs +0 -0
  226. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/contention.mjs +0 -0
  227. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/cost.mjs +0 -0
  228. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/discover.mjs +0 -0
  229. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/independence.mjs +0 -0
  230. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/inject.mjs +0 -0
  231. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/mocks.mjs +0 -0
  232. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/probe.mjs +0 -0
  233. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/progress.mjs +0 -0
  234. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/pyimports.mjs +0 -0
  235. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/rank.mjs +0 -0
  236. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/index.mjs +0 -0
  237. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/jest.mjs +0 -0
  238. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/playwright.mjs +0 -0
  239. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
  240. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/python/_testguard_report.py +0 -0
  241. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
  242. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/python.mjs +0 -0
  243. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/shared.mjs +0 -0
  244. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/runners/vitest.mjs +0 -0
  245. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/probe/worktree.mjs +0 -0
  246. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/render.mjs +0 -0
  247. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/replay/label.mjs +0 -0
  248. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/replay/replay.mjs +0 -0
  249. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/scaffold/producers.mjs +0 -0
  250. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/scaffold/producers.python.mjs +0 -0
  251. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/scaffold/scaffold.mjs +0 -0
  252. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/util/glob.mjs +0 -0
  253. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/src/util/hash.mjs +0 -0
  254. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/admit.test.mjs +0 -0
  255. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/annotations.test.mjs +0 -0
  256. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/attribution.test.mjs +0 -0
  257. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/baseline.test.mjs +0 -0
  258. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/brief.test.mjs +0 -0
  259. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/calibration.test.mjs +0 -0
  260. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/claims-since.test.mjs +0 -0
  261. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/classify.test.mjs +0 -0
  262. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/contention.test.mjs +0 -0
  263. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/cost.test.mjs +0 -0
  264. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/discover.test.mjs +0 -0
  265. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/gitlab-template.test.mjs +0 -0
  266. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/glob.test.mjs +0 -0
  267. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/independence.test.mjs +0 -0
  268. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/init.test.mjs +0 -0
  269. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/inject.test.mjs +0 -0
  270. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/label.test.mjs +0 -0
  271. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/mocks.test.mjs +0 -0
  272. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/negative-control.test.mjs +0 -0
  273. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/one-pager.test.mjs +0 -0
  274. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/probe-error.test.mjs +0 -0
  275. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/probe-preconditions.test.mjs +0 -0
  276. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/probe.jest.test.mjs +0 -0
  277. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/probe.playwright.test.mjs +0 -0
  278. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/probe.python.test.mjs +0 -0
  279. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/progress.test.mjs +0 -0
  280. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/pyimports.test.mjs +0 -0
  281. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/rank-aliases.test.mjs +0 -0
  282. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/release-sync.test.mjs +0 -0
  283. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/replay-verdict.test.mjs +0 -0
  284. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/replay.test.mjs +0 -0
  285. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/runner-argv.test.mjs +0 -0
  286. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/runner-command.test.mjs +0 -0
  287. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/runner-playwright.test.mjs +0 -0
  288. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/runner-python.test.mjs +0 -0
  289. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/runner-vitest.test.mjs +0 -0
  290. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/samples/playwright-report.json +0 -0
  291. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/samples/python-report-collect-error.json +0 -0
  292. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/samples/python-report.json +0 -0
  293. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/scaffold.python.test.mjs +0 -0
  294. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/scaffold.test.mjs +0 -0
  295. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/snapshot.test.mjs +0 -0
  296. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/worktree.test.mjs +0 -0
  297. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/test/writer.test.mjs +0 -0
  298. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/testguard.cost-budget.json +0 -0
  299. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/testguard_cli/__init__.py +0 -0
  300. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/testguard_cli/wrapper.py +0 -0
  301. {testguard_cli-0.9.1 → testguard_cli-0.9.2}/vitest.config.mjs +0 -0
@@ -21,6 +21,7 @@ which files exist. `state` is one of:
21
21
  | state | meaning | you do |
22
22
  |---|---|---|
23
23
  | `no-claims` | no `testguard.claims.json` | `testguard scaffold <file>` for a file with guards; replace every `TODO:` statement with what the code guarantees; keep or drop each proposal; move the claims into `testguard.claims.json` |
24
+ | `invalid-anchors` | an exact fault anchor moved or became ambiguous | run `testguard claims --check-anchors`; repair `next.target` in the claims file without changing what the fault means, then re-probe. Never guess or auto-fix an anchor. |
24
25
  | `unprobed` | claims never probed | `testguard probe` |
25
26
  | `evidence-stale` | code, tests or claims changed since the evidence | `testguard probe --include-dirty` (or `--claim <ID>` for one) |
26
27
  | `provisional-only` | only `--confirm 1` evidence exists | `testguard probe --confirm 3` |
@@ -44,6 +45,8 @@ Exit codes: `0` clean · `1` unproven claims (or drift) · `2` precondition fail
44
45
  ## The fix loop
45
46
 
46
47
  1. `testguard status --json` → take `next.target`.
48
+ After editing guarded code, run `testguard claims --check-anchors` before
49
+ any probe; repair a named fault definition without weakening its meaning.
47
50
  2. Write the test. It must **fail when the fault is applied and pass on HEAD**. To check the first half by hand: apply `find` → `replace` in the target file, run the defender, restore.
48
51
  3. `testguard probe --claim <ID> --include-dirty` — probes your uncommitted test without touching the tree. `--confirm 1` gives a fast **provisional** signal; a `?` on a verdict means unconfirmed.
49
52
  4. When it is `killed` at `--confirm 3`, commit the test.
@@ -185,6 +185,13 @@ jobs:
185
185
  if: github.event_name == 'pull_request'
186
186
  run: node cli/testguard.mjs gate . --changed "origin/${{ github.base_ref }}"
187
187
 
188
+ # Exact anchors and parseable replacements are knowable without a test
189
+ # runner. Fail here instead of spending the long self-probe merely to
190
+ # discover that a fault could never be applied.
191
+ - name: claims — fault anchors and replacements are valid
192
+ if: matrix.node-version == 22
193
+ run: node cli/testguard.mjs claims . --check-anchors
194
+
188
195
  # ── Self-verification: TestGuard is governed by the standard it defines.
189
196
  # testguard.claims.json states invariants of the tool itself; every
190
197
  # fault must be killed by the tool's own tests. Exit 0 = all defended. ──
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.9.2] - 2026-09-19
11
+
12
+ Automated weekly release — everything merged since `v0.9.1`.
13
+
14
+ ### Changed
15
+
16
+ - feat(claims): preflight fault anchors (#126)
17
+ - chore(homebrew): sha256 for v0.9.1 (#125)
18
+
19
+
20
+ ### Added
21
+
22
+ - `claims --check-anchors` locates every exact fault anchor and syntax-checks
23
+ supported in-memory JavaScript and Python replacements without starting a
24
+ runner or touching the tree. CI runs the sub-second preflight before the
25
+ self-probe, while `status` reports rotted anchors as `invalid-anchors` with
26
+ the precise fault to repair. Repairs remain a reviewed judgement call:
27
+ there is deliberately no fuzzy match or `--fix`.
28
+
10
29
  ## [0.9.1] - 2026-09-19
11
30
 
12
31
  Automated weekly release — everything merged since `v0.9.0`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: testguard-cli
3
- Version: 0.9.1
3
+ Version: 0.9.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.9.1` — see [`action.yml`](./action.yml) |
154
+ | GitHub Action | `uses: raccioly/testguard@v0.9.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.9.1/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.9.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
@@ -165,6 +165,7 @@ less than that many days ago (`ENOVERSIONS`); install that one with
165
165
  npx testguard-cli init # install the agent layer at the git root
166
166
  npx testguard-cli status --json # where the project is, and the ONE next action
167
167
  npx testguard-cli claims # what does this project claim, and is each claim probeable?
168
+ npx testguard-cli claims --check-anchors # fail fast if an exact anchor moved or a replacement no longer parses
168
169
  npx testguard-cli scaffold src/auth.ts # propose faults for a file, as a draft to keep or drop
169
170
  npx testguard-cli probe # try to falsify each claim; report what the tests missed
170
171
  npx testguard-cli admit test/auth.test.ts --claim AUTH-ADMIN # does this test satisfy the two-gate rule?
@@ -175,8 +176,9 @@ npx testguard-cli replay --since v1.0..HEAD # would this suite have caught the b
175
176
  npx testguard-cli mcp # serve the read-only loop over MCP, on stdio
176
177
  ```
177
178
 
178
- Exit codes are the contract: `0` nothing new to prove, `1` unproven claims or
179
- unclaimed changes, `2` a precondition failed and nothing was probed, `3` usage.
179
+ Exit codes are the contract: `0` nothing new to prove, `1` unproven claims,
180
+ unclaimed changes or invalid fault anchors, `2` a precondition failed and
181
+ nothing was probed, `3` usage.
180
182
  Every command accepts `--json`.
181
183
 
182
184
  ## How it works
@@ -201,6 +203,10 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
201
203
  claim and every fault records who produced it. `testguard claims`
202
204
  validates the file and reports drift against `@claim <ID>` annotations in
203
205
  source. Test files are deliberately not scanned — a claim asserted by a test is the authorship trap the tool exists for — and annotation ids must contain a hyphen so prose is never mistaken for one.
206
+ `claims --check-anchors` additionally locates every exact anchor and parses
207
+ each in-memory JavaScript or Python replacement. It runs no tests, creates
208
+ no worktree and changes no file, so a moved anchor is reported before an
209
+ expensive probe. It reports and stops; it never guesses a new anchor.
204
210
  2. **Probe** confirms the defenders are green N times unmodified, applies
205
211
  each fault in a scratch git worktree (your tree is never touched), runs
206
212
  the defenders N times, re-runs survivors against the whole suite with
@@ -469,6 +475,10 @@ is reported as `EXPIRED`.
469
475
  With a reference known, `status --changed <ref>` reports `unclaimed-changes`
470
476
  **before** any evidence state and makes the claim the next action; the brief
471
477
  lists the unclaimed files first. The claim is written before more code.
478
+ After claim coverage, `status` reports `invalid-anchors` before any evidence
479
+ state when an exact fault anchor is missing or ambiguous, and points at the
480
+ fault definition to repair. Repair preserves the fault's meaning and requires
481
+ a re-probe; there is deliberately no `--fix`.
472
482
 
473
483
  **In CI the base is detected** — GitHub Actions (`GITHUB_BASE_REF`) and GitLab
474
484
  merge request pipelines (`CI_MERGE_REQUEST_DIFF_BASE_SHA`, then
@@ -483,12 +493,12 @@ detected base that does not resolve is a warning for `status` and `brief`
483
493
  # GitHub Actions
484
494
  - uses: actions/checkout@v4
485
495
  with: { fetch-depth: 0 }
486
- - uses: raccioly/testguard@v0.9.1
496
+ - uses: raccioly/testguard@v0.9.2
487
497
  with: { command: gate }
488
498
 
489
499
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
490
500
  include:
491
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.1/packaging/gitlab/testguard.gitlab-ci.yml'
501
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.2/packaging/gitlab/testguard.gitlab-ci.yml'
492
502
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
493
503
  ```
494
504
 
@@ -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.9.1` — see [`action.yml`](./action.yml) |
130
+ | GitHub Action | `uses: raccioly/testguard@v0.9.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.9.1/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.9.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
@@ -141,6 +141,7 @@ less than that many days ago (`ENOVERSIONS`); install that one with
141
141
  npx testguard-cli init # install the agent layer at the git root
142
142
  npx testguard-cli status --json # where the project is, and the ONE next action
143
143
  npx testguard-cli claims # what does this project claim, and is each claim probeable?
144
+ npx testguard-cli claims --check-anchors # fail fast if an exact anchor moved or a replacement no longer parses
144
145
  npx testguard-cli scaffold src/auth.ts # propose faults for a file, as a draft to keep or drop
145
146
  npx testguard-cli probe # try to falsify each claim; report what the tests missed
146
147
  npx testguard-cli admit test/auth.test.ts --claim AUTH-ADMIN # does this test satisfy the two-gate rule?
@@ -151,8 +152,9 @@ npx testguard-cli replay --since v1.0..HEAD # would this suite have caught the b
151
152
  npx testguard-cli mcp # serve the read-only loop over MCP, on stdio
152
153
  ```
153
154
 
154
- Exit codes are the contract: `0` nothing new to prove, `1` unproven claims or
155
- unclaimed changes, `2` a precondition failed and nothing was probed, `3` usage.
155
+ Exit codes are the contract: `0` nothing new to prove, `1` unproven claims,
156
+ unclaimed changes or invalid fault anchors, `2` a precondition failed and
157
+ nothing was probed, `3` usage.
156
158
  Every command accepts `--json`.
157
159
 
158
160
  ## How it works
@@ -177,6 +179,10 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
177
179
  claim and every fault records who produced it. `testguard claims`
178
180
  validates the file and reports drift against `@claim <ID>` annotations in
179
181
  source. Test files are deliberately not scanned — a claim asserted by a test is the authorship trap the tool exists for — and annotation ids must contain a hyphen so prose is never mistaken for one.
182
+ `claims --check-anchors` additionally locates every exact anchor and parses
183
+ each in-memory JavaScript or Python replacement. It runs no tests, creates
184
+ no worktree and changes no file, so a moved anchor is reported before an
185
+ expensive probe. It reports and stops; it never guesses a new anchor.
180
186
  2. **Probe** confirms the defenders are green N times unmodified, applies
181
187
  each fault in a scratch git worktree (your tree is never touched), runs
182
188
  the defenders N times, re-runs survivors against the whole suite with
@@ -445,6 +451,10 @@ is reported as `EXPIRED`.
445
451
  With a reference known, `status --changed <ref>` reports `unclaimed-changes`
446
452
  **before** any evidence state and makes the claim the next action; the brief
447
453
  lists the unclaimed files first. The claim is written before more code.
454
+ After claim coverage, `status` reports `invalid-anchors` before any evidence
455
+ state when an exact fault anchor is missing or ambiguous, and points at the
456
+ fault definition to repair. Repair preserves the fault's meaning and requires
457
+ a re-probe; there is deliberately no `--fix`.
448
458
 
449
459
  **In CI the base is detected** — GitHub Actions (`GITHUB_BASE_REF`) and GitLab
450
460
  merge request pipelines (`CI_MERGE_REQUEST_DIFF_BASE_SHA`, then
@@ -459,12 +469,12 @@ detected base that does not resolve is a warning for `status` and `brief`
459
469
  # GitHub Actions
460
470
  - uses: actions/checkout@v4
461
471
  with: { fetch-depth: 0 }
462
- - uses: raccioly/testguard@v0.9.1
472
+ - uses: raccioly/testguard@v0.9.2
463
473
  with: { command: gate }
464
474
 
465
475
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
466
476
  include:
467
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.1/packaging/gitlab/testguard.gitlab-ci.yml'
477
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.2/packaging/gitlab/testguard.gitlab-ci.yml'
468
478
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
469
479
  ```
470
480
 
@@ -46,7 +46,7 @@ inputs:
46
46
  version:
47
47
  description: 'testguard-cli version to run'
48
48
  required: false
49
- default: '0.9.1'
49
+ default: '0.9.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.9.1 · MIT</div>
147
+ <div class="meta">Technical Brief · v0.9.2 · MIT</div>
148
148
  </div>
149
149
 
150
150
  <h1>Your tests pass. That is not the same as your tests working.</h1>
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- docguard:version 1.0.0 -->
4
4
  <!-- docguard:status approved -->
5
- <!-- docguard:last-reviewed 2026-09-18 -->
5
+ <!-- docguard:last-reviewed 2026-09-19 -->
6
6
  <!-- docguard:owner @raccioly -->
7
7
  <!-- docguard:quality negation-load off — this tool is defined by what must not happen: no network, no telemetry, never optimistic under uncertainty. Stating those positively would misdescribe them. -->
8
8
 
@@ -35,7 +35,7 @@ code were wrong, would anything fail?*
35
35
  |---|---|---|
36
36
  | CLI dispatch | Parse argv, select a command, map errors to exit codes | `src/cli.mjs`, `cli/testguard.mjs` |
37
37
  | Commands | Thin wrappers: read flags, call a module, render output | `src/commands/*.mjs` |
38
- | Claims | Load and validate the claims file; reconcile `@claim` annotations; detect removed claims | `src/claims/` |
38
+ | Claims | Load and validate the claims file; reconcile `@claim` annotations; detect removed claims; preflight exact anchors and supported replacement syntax | `src/claims/` |
39
39
  | Probe engine | Isolate, inject, run, escalate, restore, classify, rank | `src/probe/probe.mjs` |
40
40
  | Verdict function | The pure decision: runs in, verdict out | `src/probe/classify.mjs` |
41
41
  | Injector | Apply and restore a fault; anchor location | `src/probe/inject.mjs` |
@@ -131,7 +131,7 @@ to every other surface and `--check` fails the build on drift.
131
131
 
132
132
  | Stage | Where | Gate |
133
133
  |---|---|---|
134
- | Pull request | `ci.yml` | Suite on Node 20/22/24, syntax check of every source, install-from-tarball smoke, Python wrapper import, fixture oracles for vitest and jest, Playwright job, change gate, removed-claim check |
134
+ | Pull request | `ci.yml` | Suite on Node 20/22/24, syntax check of every source, install-from-tarball smoke, Python wrapper import, fixture oracles for vitest and jest, Playwright job, change gate, removed-claim check, anchor/replacement preflight before self-probe |
135
135
  | Release gate | `ci.yml` | TestGuard probes its own claims; every fault must be killed |
136
136
  | Merge to main | `release.yml` | A version with no tag is tagged, released, and published to npm and PyPI over OIDC |
137
137
  | Recovery | `release.yml` | Hourly sweep; each registry is gated on its own state, so a partial publish is fixed by re-running |
@@ -141,6 +141,9 @@ to every other surface and `--check` fails the build on drift.
141
141
  ```
142
142
  testguard.claims.json (what must be true)
143
143
  │
144
+ claims --check-anchors
145
+ (exact anchors + syntax, no tests)
146
+ │
144
147
  ▼
145
148
  ┌─────────── probe ────────────────────────────────────────────┐
146
149
  │ scratch git worktree ←── isolation, never your tree │
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- docguard:version 1.0.0 -->
4
4
  <!-- docguard:status approved -->
5
- <!-- docguard:last-reviewed 2026-09-18 -->
5
+ <!-- docguard:last-reviewed 2026-09-19 -->
6
6
  <!-- docguard:owner @raccioly -->
7
7
  <!-- docguard:quality negation-load off — the data model is largely a set of prohibitions (no database, no server state, fingerprints never derived from fault text). -->
8
8
  <!-- docguard:quality passive-voice off — this document describes what happens to documents as they flow through validation, where the artifact is the subject and the actor is irrelevant. -->
@@ -86,6 +86,11 @@ status.json ◀── derived from claims + evidence + baseline + working tree
86
86
  brief.json ◀── derived from evidence + baseline + status
87
87
  ```
88
88
 
89
+ Status also carries `invalidFaults`, the current exact-anchor failures with
90
+ their observed and expected hit counts. When it is non-empty, the state is
91
+ `invalid-anchors` and the next action is `repair-fault`; evidence cannot make a
92
+ fault that no longer applies trustworthy.
93
+
89
94
  A fingerprint is `sha256(claimId \n subjectId \n file \n verdict)`. It is
90
95
  derived from identity and outcome, never from the fault's text, so repairing a
91
96
  rotted anchor does not churn the baseline, while a change of verdict does
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- docguard:version 1.0.0 -->
4
4
  <!-- docguard:status approved -->
5
- <!-- docguard:last-reviewed 2026-09-18 -->
5
+ <!-- docguard:last-reviewed 2026-09-19 -->
6
6
  <!-- docguard:owner @raccioly -->
7
7
  <!-- docguard:quality passive-voice off — requirements are written in the standard "the system shall ..." register, where the agent is the system throughout. -->
8
8
 
@@ -26,6 +26,7 @@
26
26
  | FR-12 | Answer whether one test satisfies the two-gate rule for a claim (`admit`) | P2 |
27
27
  | FR-13 | Replay historical fix commits to measure whether the suite would have caught them, and calibrate by fault class | P3 |
28
28
  | FR-14 | Serve the read-only operating loop over MCP so the loop survives a change of agent harness | P3 |
29
+ | FR-15 | Validate every exact fault anchor and the syntax of supported in-memory replacements before starting a test runner (`claims --check-anchors`), without guessing repairs | P1 |
29
30
 
30
31
  ## Non-Functional Requirements
31
32
 
@@ -99,6 +100,7 @@ fails the pull request naming the unclaimed file, and `status` reports
99
100
  | FR-12 | `src/admit/admit.mjs` | `test/admit.test.mjs`, `TG-ADMIT-NEEDS-ALL-KILLED` |
100
101
  | FR-13 | `src/replay/` | `test/replay.test.mjs`, `test/calibration.test.mjs`, `TG-REPLAY-FLAKY-IS-NEVER-CAUGHT`, `TG-CALIBRATION-COUNTS-NOCOVER-AS-A-MISS`, `TG-CALIBRATION-COUNTS-ONLY-BUGS` |
101
102
  | FR-14 | `src/mcp/` | `test/mcp.test.mjs` |
103
+ | FR-15 | `src/claims/anchors.mjs`, `src/commands/claims.mjs`, `src/status/status.mjs` | `test/claims-anchors.test.mjs`, `test/status.test.mjs`, `TG-ANCHOR-PREFLIGHT-FAILS-FAST` |
102
104
  | NFR-01 | `src/init/init.mjs`, runner resolution | `TG-INIT-HOOK-NO-NETWORK`, `TG-README-HOOK-MATCHES-THE-CODE` (every surface, not only the README), `TG-GITIGNORE-ADVICE-IS-ONE-LIST` |
103
105
  | NFR-02 | `package.json` | `npm run test:install` in CI |
104
106
  | NFR-03 | `src/probe/inject.mjs`, `src/probe/probe.mjs` | `TG-DIRTY-DEFENDERS-REFUSED`, `TG-IGNORED-DIRTY-RECORDED`, `TG-IN-PLACE-STILL-RAISES-A-MISSING-TARGET`, `TG-CONTROL-RESTORES-THE-SUBJECT` |
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- docguard:version 1.0.0 -->
4
4
  <!-- docguard:status approved -->
5
- <!-- docguard:last-reviewed 2026-09-18 -->
5
+ <!-- docguard:last-reviewed 2026-09-19 -->
6
6
  <!-- docguard:owner @raccioly -->
7
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
 
@@ -19,6 +19,7 @@ the test that defends it, and CI fails if any injected fault survives.
19
19
  |---|---|---|---|
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
+ | 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 |
22
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
24
  | Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **13.4 min** cold (measured 2026-09-18 at `cf5a083`: 96 faults, 576 defender runs, 804 s, `--serial`, idle machine); seconds when verdicts are reused, which is why CI restores the previous evidence from cache. Budgeted at 950 s in `testguard.cost-budget.json` and **gated** in CI — the figure above went 9.6 → 23.6 min across one merged change while `--cost` printed it into a log nobody read |
24
25
  | Install smoke | `.github/scripts/install-smoke.mjs` | The **packed tarball** runs with production dependencies only | ~20 s |
@@ -57,6 +58,9 @@ What is gated instead:
57
58
  4. **Every spec change ships its conformance case** — schema, semantic rule,
58
59
  valid example and must-reject document, in the same pull request.
59
60
  5. **A new fault shape needs a synthetic-file test and a README row.**
61
+ 6. **Every fault must pass the cheap preflight before self-verification.** A
62
+ missing or ambiguous anchor, or a supported replacement that does not
63
+ parse, fails CI before the long self-probe starts.
60
64
 
61
65
  ## Service-to-Test Map
62
66
 
@@ -64,6 +68,7 @@ What is gated instead:
64
68
  |---|---|
65
69
  | `src/probe/classify.mjs` | `test/classify.test.mjs`, `test/negative-control.test.mjs` |
66
70
  | `src/probe/inject.mjs` | `test/inject.test.mjs` |
71
+ | `src/claims/anchors.mjs` | `test/claims-anchors.test.mjs` |
67
72
  | `src/probe/probe.mjs` | `test/probe.fixture.test.mjs`, `test/probe-preconditions.test.mjs`, `test/probe-error.test.mjs` |
68
73
  | `src/probe/discover.mjs`, `src/probe/mocks.mjs` | `test/discover.test.mjs`, `test/mocks.test.mjs` |
69
74
  | `src/probe/rank.mjs` | `test/rank-aliases.test.mjs`, `test/discover.test.mjs` |
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "testguard-cli",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "testguard-cli",
9
- "version": "0.9.1",
9
+ "version": "0.9.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.9.1",
3
+ "version": "0.9.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.9.1/packaging/gitlab/testguard.gitlab-ci.yml'
6
+ # - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.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.9.1"
32
+ default: "0.9.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.9.1.tgz"
19
- sha256 "e8ebb4e78e1d91f21a47dee1edb63ee7fac72f96d2834b08654f89c18812ef7d"
18
+ url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.9.2.tgz"
19
+ sha256 "118c895b89b336b919ca61d9af35915fded75c27d08feeabaf2340d6e5a9a847"
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.9.1"
7
+ version = "0.9.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" }
@@ -395,6 +395,26 @@ changed); `1` at least one uncovered file (or strict mode over an
395
395
  all-excluded change); `2` the change cannot be evaluated (unresolvable
396
396
  reference, invalid claims or ignore file, no repository); `3` no reference.
397
397
 
398
+ ## Anchor preflight
399
+
400
+ `claims --check-anchors` answers what is knowable before a runner starts. For
401
+ every fault-injection fault it reads the target, applies the same exact
402
+ `locate()` arithmetic as a probe, and reports `ok`, `anchor-missing` or
403
+ `anchor-ambiguous` with the observed and expected hit counts. When a language
404
+ has a cheap parser, it also parses the in-memory replacement (`node --check`
405
+ semantics for JavaScript modules, `compile()` for Python). Unsupported
406
+ languages are anchor-checked and otherwise skipped without inference.
407
+
408
+ One non-`ok` anchor or non-parsing supported replacement exits `1`. The check
409
+ runs no tests, creates no worktree, reads no git state and writes no source.
410
+ It is not evidence that a defender detects the fault; the self-probe still
411
+ runs after it. There is no automatic repair or fuzzy relocation: choosing a
412
+ new anchor is a semantic edit to the claim and must be reviewed and re-probed.
413
+
414
+ `invalid-anchors` precedes every evidence state in the status document (after
415
+ `unclaimed-changes`, when a change reference is known). Its next action is
416
+ `repair-fault`, naming the first fault and `testguard claims --check-anchors`.
417
+
398
418
  ## Severity floor
399
419
 
400
420
  `--severity <level>` gates only findings whose claim severity is at or above
@@ -32,6 +32,7 @@
32
32
  "baselined": 4
33
33
  },
34
34
  "stale": [],
35
+ "invalidFaults": [],
35
36
  "changedFaults": [
36
37
  {
37
38
  "claimId": "REDACT-002",
@@ -0,0 +1,27 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "tool": { "name": "testguard", "version": "0.9.1" },
4
+ "generatedAt": "2026-09-19T00:00:00Z",
5
+ "state": "unprobed",
6
+ "next": {
7
+ "action": "probe",
8
+ "command": "testguard probe",
9
+ "why": "Claims have not been probed."
10
+ },
11
+ "provisional": false,
12
+ "counts": { "claims": 1, "faults": 1 },
13
+ "stale": [],
14
+ "invalidFaults": [
15
+ {
16
+ "claimId": "C-1",
17
+ "subjectId": "F1",
18
+ "file": "src/a.mjs",
19
+ "reason": "anchor-missing",
20
+ "hits": 0,
21
+ "expected": 1
22
+ }
23
+ ],
24
+ "changedFaults": [],
25
+ "findings": [],
26
+ "paths": { "claims": "testguard.claims.json" }
27
+ }
@@ -233,12 +233,15 @@ const semantic = {
233
233
 
234
234
  status(doc) {
235
235
  const errors = [];
236
- if (['write-test', 'review-fault-change'].includes(doc.next.action) && !doc.next.target) errors.push({ path: '/next/target', message: `${doc.next.action} requires a target` });
236
+ if (['write-test', 'review-fault-change', 'repair-fault'].includes(doc.next.action) && !doc.next.target) errors.push({ path: '/next/target', message: `${doc.next.action} requires a target` });
237
237
  if (doc.next.action === 'claim' && !doc.next.file) errors.push({ path: '/next/file', message: 'claim requires the file the claim is about' });
238
238
  if (doc.state === 'no-claims' && doc.counts.claims !== 0) errors.push({ path: '/counts/claims', message: 'no-claims with a non-zero claim count' });
239
239
  if (doc.state === 'clean' && (doc.counts.new ?? 0) > 0) errors.push({ path: '/state', message: 'clean with new findings' });
240
240
  if (doc.state === 'unclaimed-changes' && !(doc.changes && doc.changes.uncovered.length > 0)) errors.push({ path: '/changes/uncovered', message: 'unclaimed-changes requires at least one uncovered changed file' });
241
241
  if (doc.state === 'unclaimed-changes' && doc.next.action !== 'claim') errors.push({ path: '/next/action', message: 'unclaimed-changes requires next.action = claim' });
242
+ if (doc.state === 'invalid-anchors' && !(doc.invalidFaults?.length > 0)) errors.push({ path: '/invalidFaults', message: 'invalid-anchors requires at least one invalid fault' });
243
+ if (doc.state === 'invalid-anchors' && doc.next.action !== 'repair-fault') errors.push({ path: '/next/action', message: 'invalid-anchors requires next.action = repair-fault' });
244
+ if (doc.invalidFaults?.length > 0 && doc.state !== 'invalid-anchors') errors.push({ path: '/state', message: 'invalid fault anchors are hidden behind a later state; invalid-anchors precedes every evidence state' });
242
245
  if (doc.evidenceSource && !doc.evidenceHead) errors.push({ path: '/evidenceHead', message: 'evidence was read, so the commit it describes must be recorded' });
243
246
  if (doc.changes && doc.changes.uncovered.length > 0 && !['no-claims', 'unclaimed-changes'].includes(doc.state)) errors.push({ path: '/state', message: 'uncovered changed files are hidden behind a later state; unclaimed-changes precedes every evidence state' });
244
247
  return errors;
@@ -39,7 +39,7 @@
39
39
  "type": "object",
40
40
  "required": ["action", "command", "why"],
41
41
  "properties": {
42
- "action": { "enum": ["scaffold", "claim", "probe", "write-test", "baseline", "review-fault-change", "none"] },
42
+ "action": { "enum": ["scaffold", "claim", "probe", "write-test", "baseline", "repair-fault", "review-fault-change", "none"] },
43
43
  "command": { "type": "string", "minLength": 1, "maxLength": 1024 },
44
44
  "why": { "type": "string", "minLength": 1, "maxLength": 2000 }
45
45
  },
@@ -26,10 +26,11 @@
26
26
  "$ref": "urn:claimspec:v1:common#/$defs/isoDateTime"
27
27
  },
28
28
  "state": {
29
- "description": "no-claims: no claims file. unclaimed-changes: the change measured against a reference touches source files that carry no claim and no excusing ignore entry (only computed when a reference is known; it precedes every evidence state because the claim is written before more code). unprobed: claims but no confirmed evidence. evidence-stale: evidence exists but a claim, fault, target or defender changed since. provisional-only: only sub-N evidence exists. unproven: current evidence has findings not covered by the baseline. clean: every finding is killed or baselined.",
29
+ "description": "no-claims: no claims file. unclaimed-changes: the change measured against a reference touches source files that carry no claim and no excusing ignore entry (only computed when a reference is known; it precedes every evidence state because the claim is written before more code). invalid-anchors: one or more exact fault anchors are missing or ambiguous, so probing would be unable to apply them. unprobed: claims but no confirmed evidence. evidence-stale: evidence exists but a claim, fault, target or defender changed since. provisional-only: only sub-N evidence exists. unproven: current evidence has findings not covered by the baseline. clean: every finding is killed or baselined.",
30
30
  "enum": [
31
31
  "no-claims",
32
32
  "unclaimed-changes",
33
+ "invalid-anchors",
33
34
  "unprobed",
34
35
  "evidence-stale",
35
36
  "provisional-only",
@@ -54,6 +55,7 @@
54
55
  "write-test",
55
56
  "baseline",
56
57
  "review-fault-change",
58
+ "repair-fault",
57
59
  "none"
58
60
  ]
59
61
  },
@@ -188,6 +190,23 @@
188
190
  "additionalProperties": false
189
191
  }
190
192
  },
193
+ "invalidFaults": {
194
+ "description": "Faults whose exact text anchor does not locate in the current target. These precede every evidence state: a probe cannot learn anything from a fault it cannot apply.",
195
+ "type": "array",
196
+ "items": {
197
+ "type": "object",
198
+ "required": ["claimId", "subjectId", "file", "reason", "hits", "expected"],
199
+ "properties": {
200
+ "claimId": { "$ref": "urn:claimspec:v1:common#/$defs/identifier" },
201
+ "subjectId": { "$ref": "urn:claimspec:v1:common#/$defs/identifier" },
202
+ "file": { "$ref": "urn:claimspec:v1:common#/$defs/repoPath" },
203
+ "reason": { "enum": ["anchor-missing", "anchor-ambiguous"] },
204
+ "hits": { "type": "integer", "minimum": 0 },
205
+ "expected": { "type": "integer", "minimum": 1 }
206
+ },
207
+ "additionalProperties": false
208
+ }
209
+ },
191
210
  "findings": {
192
211
  "description": "Unproven findings from the current evidence, ranked, new first, capped.",
193
212
  "type": "array",