testguard-cli 0.18.1__tar.gz → 0.18.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 (472) hide show
  1. testguard_cli-0.18.2/.github/scripts/benchmark-node-direct.mjs +106 -0
  2. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/workflows/ci.yml +26 -0
  3. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/CHANGELOG.md +16 -0
  4. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/PKG-INFO +5 -5
  5. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/README.md +4 -4
  6. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/action.yml +1 -1
  7. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs/testguard-explained.html +1 -1
  8. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs/testguard-explained.pdf +0 -0
  9. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs-canonical/ARCHITECTURE.md +1 -1
  10. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs-canonical/REQUIREMENTS.md +3 -3
  11. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/package-lock.json +2 -2
  12. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/package.json +1 -1
  13. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
  14. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/packaging/homebrew/testguard.rb +2 -2
  15. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/pyproject.toml +1 -1
  16. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/GATE-SEMANTICS.md +15 -1
  17. testguard_cli-0.18.2/src/probe/runners/node/direct.mjs +55 -0
  18. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/node/entry.mjs +6 -0
  19. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/node-test.mjs +20 -6
  20. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/annotation-advisory.test.mjs +0 -17
  21. testguard_cli-0.18.2/test/annotation-id-bounds.test.mjs +33 -0
  22. testguard_cli-0.18.1/test/claim-selection.test.mjs → testguard_cli-0.18.2/test/claim-options.test.mjs +2 -51
  23. testguard_cli-0.18.2/test/claim-selection.test.mjs +62 -0
  24. testguard_cli-0.18.2/test/gate-coverage.test.mjs +71 -0
  25. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/gate.test.mjs +0 -54
  26. testguard_cli-0.18.2/test/runner-node-direct.test.mjs +98 -0
  27. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/testguard.claims.json +79 -7
  28. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.agents/skills/testguard/SKILL.md +0 -0
  29. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.docguard.json +0 -0
  30. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.docguardignore +0 -0
  31. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.gitattributes +0 -0
  32. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  33. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  34. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  35. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/dependabot.yml +0 -0
  36. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/approve-held-runs.sh +0 -0
  37. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/benchmark-node-runner.mjs +0 -0
  38. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/build-one-pager.mjs +0 -0
  39. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/check-cost-budget.mjs +0 -0
  40. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/check-node-fixture.mjs +0 -0
  41. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/check-python-fixture.mjs +0 -0
  42. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/install-smoke.mjs +0 -0
  43. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/publish-homebrew-tap.sh +0 -0
  44. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/scripts/sync-release-version.mjs +0 -0
  45. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/workflows/auto-merge.yml +0 -0
  46. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/workflows/release.yml +0 -0
  47. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/workflows/scheduled-release.yml +0 -0
  48. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.github/workflows/supply-chain.yml +0 -0
  49. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.gitignore +0 -0
  50. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.npmignore +0 -0
  51. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.npmrc +0 -0
  52. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.pre-commit-config.yaml +0 -0
  53. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/.pre-commit-hooks.yaml +0 -0
  54. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/AGENTS.md +0 -0
  55. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/CLAUDE.md +0 -0
  56. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/CODE_OF_CONDUCT.md +0 -0
  57. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/CONTRIBUTING.md +0 -0
  58. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/DRIFT-LOG.md +0 -0
  59. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/LICENSE +0 -0
  60. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/PRIVACY.md +0 -0
  61. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/SECURITY.md +0 -0
  62. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/SUPPORT.md +0 -0
  63. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/bench/README.md +0 -0
  64. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/cli/testguard.mjs +0 -0
  65. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs-canonical/DATA-MODEL.md +0 -0
  66. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs-canonical/ENVIRONMENT.md +0 -0
  67. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs-canonical/PRIOR-ART.md +0 -0
  68. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs-canonical/SECURITY.md +0 -0
  69. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/docs-canonical/TEST-SPEC.md +0 -0
  70. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/.gitignore +0 -0
  71. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/README.md +0 -0
  72. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/expected.json +0 -0
  73. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/package.json +0 -0
  74. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/src/export.mjs +0 -0
  75. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/src/redact.mjs +0 -0
  76. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
  77. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/test/flaky.test.mjs +0 -0
  78. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/test/redact.test.mjs +0 -0
  79. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/testguard.claims.json +0 -0
  80. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer/vitest.config.mjs +0 -0
  81. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/.gitignore +0 -0
  82. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/README.md +0 -0
  83. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/expected.json +0 -0
  84. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/jest.config.js +0 -0
  85. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/package.json +0 -0
  86. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/src/export.js +0 -0
  87. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/src/redact.js +0 -0
  88. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
  89. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
  90. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/test/redact.test.js +0 -0
  91. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-jest/testguard.claims.json +0 -0
  92. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/.gitignore +0 -0
  93. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/README.md +0 -0
  94. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/expected.json +0 -0
  95. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/package.json +0 -0
  96. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/src/policy.mjs +0 -0
  97. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/src/unused.mjs +0 -0
  98. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/test/flaky.test.mjs +0 -0
  99. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/test/mixed.test.mjs +0 -0
  100. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/test/policy.test.mjs +0 -0
  101. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-node/testguard.claims.json +0 -0
  102. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/.gitignore +0 -0
  103. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/README.md +0 -0
  104. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
  105. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
  106. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/expected.json +0 -0
  107. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/package-lock.json +0 -0
  108. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/package.json +0 -0
  109. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
  110. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
  111. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
  112. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
  113. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
  114. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/.gitignore +0 -0
  115. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/README.md +0 -0
  116. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/demo/__init__.py +0 -0
  117. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/demo/export.py +0 -0
  118. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/demo/orphan.py +0 -0
  119. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/demo/pipeline.py +0 -0
  120. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/demo/redact.py +0 -0
  121. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/expected.json +0 -0
  122. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/testguard.claims.json +0 -0
  123. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
  124. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
  125. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/tests/test_patched.py +0 -0
  126. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/fixtures/known-answer-python/tests/test_redact.py +0 -0
  127. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/README.md +0 -0
  128. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/annotations.test.mjs +0 -0
  129. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/authoring-input.test.mjs +0 -0
  130. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/draft-append.test.mjs +0 -0
  131. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/annotations.json +0 -0
  132. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/authoring-input.json +0 -0
  133. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/baseline.json +0 -0
  134. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/brief.json +0 -0
  135. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/calibration-websec.json +0 -0
  136. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/calibration.json +0 -0
  137. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/claims-methods.json +0 -0
  138. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/claims.json +0 -0
  139. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
  140. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/evidence-negative-control.json +0 -0
  141. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/evidence-persistence-signal.json +0 -0
  142. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/evidence-provisional.json +0 -0
  143. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/evidence-python.json +0 -0
  144. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/evidence-resources.json +0 -0
  145. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/evidence-scan-method.json +0 -0
  146. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/evidence.json +0 -0
  147. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/gate.json +0 -0
  148. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/ignore.json +0 -0
  149. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/replay.json +0 -0
  150. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/status-resource-cost.json +0 -0
  151. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/examples/status.json +0 -0
  152. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
  153. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/baseline.zero-count.json +0 -0
  154. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
  155. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
  156. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
  157. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
  158. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
  159. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
  160. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
  161. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
  162. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
  163. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
  164. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/claims.absolute-path.json +0 -0
  165. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
  166. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
  167. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
  168. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
  169. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/claims.noop-fault.json +0 -0
  170. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
  171. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
  172. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
  173. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
  174. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
  175. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
  176. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
  177. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
  178. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
  179. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
  180. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
  181. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
  182. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
  183. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
  184. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
  185. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
  186. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
  187. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.persistence-signal-not-a-defender.json +0 -0
  188. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.persistence-signal-on-a-non-survivor.json +0 -0
  189. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.persistence-signal-without-reason.json +0 -0
  190. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
  191. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
  192. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
  193. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.resource-accounting.json +0 -0
  194. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
  195. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.serial-worker-conflict.json +0 -0
  196. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
  197. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
  198. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
  199. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
  200. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
  201. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
  202. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
  203. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
  204. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
  205. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
  206. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/ignore.short-reason.json +0 -0
  207. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
  208. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
  209. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/replay.nocover-when-no-test-file-remained.json +0 -0
  210. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/status.claim-without-file.json +0 -0
  211. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
  212. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
  213. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
  214. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/status.resource-accounting.json +0 -0
  215. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
  216. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
  217. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
  218. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/origin-policy.test.mjs +0 -0
  219. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/origin-summaries.test.mjs +0 -0
  220. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/origins.test.mjs +0 -0
  221. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/conformance/schemas.test.mjs +0 -0
  222. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/lib/authoring-input.mjs +0 -0
  223. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/lib/fingerprint.mjs +0 -0
  224. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/lib/origins.mjs +0 -0
  225. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/lib/validate.mjs +0 -0
  226. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/lib/wilson.mjs +0 -0
  227. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/annotations.schema.json +0 -0
  228. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/authoring-input.schema.json +0 -0
  229. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/baseline.schema.json +0 -0
  230. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/brief.schema.json +0 -0
  231. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/calibration.schema.json +0 -0
  232. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/claims.schema.json +0 -0
  233. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/common.schema.json +0 -0
  234. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/concerns.schema.json +0 -0
  235. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/evidence.schema.json +0 -0
  236. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/gate.schema.json +0 -0
  237. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/ignore.schema.json +0 -0
  238. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/replay.schema.json +0 -0
  239. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/status.schema.json +0 -0
  240. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/spec/schemas/sweep.schema.json +0 -0
  241. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/admit/admit.mjs +0 -0
  242. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/baseline/baseline.mjs +0 -0
  243. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/brief/brief.mjs +0 -0
  244. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/claims/anchors.mjs +0 -0
  245. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/claims/annotation-placement.mjs +0 -0
  246. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/claims/annotations.mjs +0 -0
  247. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/claims/load.mjs +0 -0
  248. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/claims/removed.mjs +0 -0
  249. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/cli.mjs +0 -0
  250. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/command-budget.mjs +0 -0
  251. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/admit.mjs +0 -0
  252. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/annotations.mjs +0 -0
  253. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/baseline.mjs +0 -0
  254. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/brief.mjs +0 -0
  255. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/claims.mjs +0 -0
  256. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/concerns.mjs +0 -0
  257. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/gate.mjs +0 -0
  258. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/init.mjs +0 -0
  259. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/mcp.mjs +0 -0
  260. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/probe.mjs +0 -0
  261. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/replay.mjs +0 -0
  262. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/scaffold.mjs +0 -0
  263. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/status.mjs +0 -0
  264. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/commands/sweep.mjs +0 -0
  265. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/evidence/writer.mjs +0 -0
  266. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/gate/changed.mjs +0 -0
  267. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/git.mjs +0 -0
  268. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/init/init.mjs +0 -0
  269. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/init/templates/SKILL.md +0 -0
  270. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/mcp/server.mjs +0 -0
  271. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/mcp/tools.mjs +0 -0
  272. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/attribution.mjs +0 -0
  273. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/classify.mjs +0 -0
  274. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/contention.mjs +0 -0
  275. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/cost.mjs +0 -0
  276. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/discover.mjs +0 -0
  277. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/imports.mjs +0 -0
  278. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/independence.mjs +0 -0
  279. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/inject.mjs +0 -0
  280. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/mocks.mjs +0 -0
  281. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/origin-policy.mjs +0 -0
  282. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/policy-freshness.mjs +0 -0
  283. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/probe.mjs +0 -0
  284. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/progress.mjs +0 -0
  285. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/pyimports.mjs +0 -0
  286. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/rank.mjs +0 -0
  287. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/discovery.mjs +0 -0
  288. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/index.mjs +0 -0
  289. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/jest.mjs +0 -0
  290. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/lifecycle.mjs +0 -0
  291. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/node/report.mjs +0 -0
  292. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/node/runner.mjs +0 -0
  293. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/playwright.mjs +0 -0
  294. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
  295. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/python/_testguard_report.py +0 -0
  296. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
  297. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/python.mjs +0 -0
  298. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/shared.mjs +0 -0
  299. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/runners/vitest.mjs +0 -0
  300. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/universe.mjs +0 -0
  301. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/probe/worktree.mjs +0 -0
  302. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/render.mjs +0 -0
  303. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/replay/label.mjs +0 -0
  304. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/replay/replay.mjs +0 -0
  305. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/admission.mjs +0 -0
  306. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/append.mjs +0 -0
  307. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/apply-append.mjs +0 -0
  308. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/fix-input.mjs +0 -0
  309. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/fix-paths.mjs +0 -0
  310. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/intent.mjs +0 -0
  311. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/open-append.mjs +0 -0
  312. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/plan-append.mjs +0 -0
  313. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/plan-intent-input.mjs +0 -0
  314. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/prepare-append.mjs +0 -0
  315. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/producers.mjs +0 -0
  316. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/producers.python.mjs +0 -0
  317. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/request.mjs +0 -0
  318. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/scaffold/scaffold.mjs +0 -0
  319. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/status/status.mjs +0 -0
  320. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/status/surface.mjs +0 -0
  321. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/supply/concerns.mjs +0 -0
  322. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/supply/feedback.mjs +0 -0
  323. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/supply/persistence.mjs +0 -0
  324. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/supply/savepath.mjs +0 -0
  325. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/supply/select.mjs +0 -0
  326. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/sweep/pool.mjs +0 -0
  327. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/sweep/sweep.mjs +0 -0
  328. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/util/glob.mjs +0 -0
  329. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/src/util/hash.mjs +0 -0
  330. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/admit-decision.test.mjs +0 -0
  331. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/admit.test.mjs +0 -0
  332. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/anchor-admission.test.mjs +0 -0
  333. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/annotation-advisory-limits.test.mjs +0 -0
  334. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/annotation-apply.test.mjs +0 -0
  335. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/annotation-cli.test.mjs +0 -0
  336. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/annotation-placement.test.mjs +0 -0
  337. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/annotations.test.mjs +0 -0
  338. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/attribution.test.mjs +0 -0
  339. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/baseline.test.mjs +0 -0
  340. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/brief.test.mjs +0 -0
  341. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/calibration.test.mjs +0 -0
  342. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/claims-anchors.test.mjs +0 -0
  343. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/claims-since.test.mjs +0 -0
  344. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/classify.test.mjs +0 -0
  345. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/cli-unexpected-error.test.mjs +0 -0
  346. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/command-budget-write-boundaries.test.mjs +0 -0
  347. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/command-budget.test.mjs +0 -0
  348. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/contention.test.mjs +0 -0
  349. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/cost.test.mjs +0 -0
  350. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/discover.test.mjs +0 -0
  351. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/discovery-config.test.mjs +0 -0
  352. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/draft-admission.test.mjs +0 -0
  353. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/draft-append-apply.test.mjs +0 -0
  354. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/draft-append-cli.test.mjs +0 -0
  355. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/draft-append-open.test.mjs +0 -0
  356. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/draft-append-plan.test.mjs +0 -0
  357. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/draft-append-prepare.test.mjs +0 -0
  358. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/draft-append.test.mjs +0 -0
  359. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/evidence-persistence-signal.test.mjs +0 -0
  360. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/fault-defender-orchestration.test.mjs +0 -0
  361. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/fault-defenders.test.mjs +0 -0
  362. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/fix-input.test.mjs +0 -0
  363. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/fix-inventory.test.mjs +0 -0
  364. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/fix-nested.test.mjs +0 -0
  365. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/fix-paths.test.mjs +0 -0
  366. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/fix-projects.test.mjs +0 -0
  367. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/gate-ref.test.mjs +0 -0
  368. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/gitlab-template.test.mjs +0 -0
  369. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/glob.test.mjs +0 -0
  370. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/helpers/gate-project.mjs +0 -0
  371. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/helpers/probe-assertions.mjs +0 -0
  372. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/helpers/release-project.mjs +0 -0
  373. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/imports.test.mjs +0 -0
  374. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/independence.test.mjs +0 -0
  375. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/init.test.mjs +0 -0
  376. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/inject.test.mjs +0 -0
  377. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/intent-authoring.test.mjs +0 -0
  378. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/intent-document.test.mjs +0 -0
  379. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/intent-input-cli.test.mjs +0 -0
  380. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/intent-input-plan.test.mjs +0 -0
  381. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/intent-input-request.test.mjs +0 -0
  382. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/intent-project.test.mjs +0 -0
  383. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/label.test.mjs +0 -0
  384. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/mcp-next.test.mjs +0 -0
  385. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/mcp-protocol.test.mjs +0 -0
  386. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/mcp.test.mjs +0 -0
  387. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/mocks.test.mjs +0 -0
  388. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/native-universe-hash.test.mjs +0 -0
  389. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/native-universe.test.mjs +0 -0
  390. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/negative-control-capabilities.test.mjs +0 -0
  391. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/negative-control.test.mjs +0 -0
  392. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/one-pager.test.mjs +0 -0
  393. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/origin-policy-admission.test.mjs +0 -0
  394. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/origin-policy-cli.test.mjs +0 -0
  395. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/origin-policy-projections.test.mjs +0 -0
  396. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/origin-policy-request.test.mjs +0 -0
  397. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/origin-policy.test.mjs +0 -0
  398. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/origin-reporting.test.mjs +0 -0
  399. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/origin-summary.test.mjs +0 -0
  400. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/policy-freshness.test.mjs +0 -0
  401. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-control.test.mjs +0 -0
  402. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-dirty-admission.test.mjs +0 -0
  403. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-dirty-paths.test.mjs +0 -0
  404. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-error-boundary.test.mjs +0 -0
  405. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-error.test.mjs +0 -0
  406. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-isolation.test.mjs +0 -0
  407. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-native-manifest.test.mjs +0 -0
  408. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-persistence-signal.test.mjs +0 -0
  409. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-preconditions.test.mjs +0 -0
  410. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe-records.test.mjs +0 -0
  411. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe.fixture.test.mjs +0 -0
  412. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe.jest.test.mjs +0 -0
  413. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe.node.test.mjs +0 -0
  414. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe.playwright.test.mjs +0 -0
  415. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/probe.python.test.mjs +0 -0
  416. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/progress.test.mjs +0 -0
  417. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/pyimports.test.mjs +0 -0
  418. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/rank-aliases.test.mjs +0 -0
  419. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/release-approval.test.mjs +0 -0
  420. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/release-sync.test.mjs +0 -0
  421. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/release-tap.test.mjs +0 -0
  422. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/release-version.test.mjs +0 -0
  423. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/replay-mixed-runners.test.mjs +0 -0
  424. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/replay-no-test-file-left.test.mjs +0 -0
  425. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/replay-python-defenders.test.mjs +0 -0
  426. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/replay-selection.test.mjs +0 -0
  427. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/replay-verdict.test.mjs +0 -0
  428. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/replay.test.mjs +0 -0
  429. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/resource-safety.test.mjs +0 -0
  430. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/resource-timing.test.mjs +0 -0
  431. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-argv.test.mjs +0 -0
  432. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-command.test.mjs +0 -0
  433. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-discovery.test.mjs +0 -0
  434. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-node-admission.test.mjs +0 -0
  435. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-node-controller.test.mjs +0 -0
  436. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-node-report.test.mjs +0 -0
  437. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-output-bounds.test.mjs +0 -0
  438. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-playwright.test.mjs +0 -0
  439. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-project-resolution.test.mjs +0 -0
  440. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-python.test.mjs +0 -0
  441. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-report-allocation.test.mjs +0 -0
  442. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-report.test.mjs +0 -0
  443. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-selection-blind.test.mjs +0 -0
  444. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-vitest.test.mjs +0 -0
  445. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/runner-worker-argv.test.mjs +0 -0
  446. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/samples/playwright-report.json +0 -0
  447. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/samples/python-report-collect-error.json +0 -0
  448. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/samples/python-report.json +0 -0
  449. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/scaffold-admitted-source.test.mjs +0 -0
  450. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/scaffold-placeholder.test.mjs +0 -0
  451. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/scaffold.python.test.mjs +0 -0
  452. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/scaffold.test.mjs +0 -0
  453. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/snapshot.test.mjs +0 -0
  454. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/status.test.mjs +0 -0
  455. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/supply-concerns.test.mjs +0 -0
  456. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/supply-feedback.test.mjs +0 -0
  457. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/supply-persistence.test.mjs +0 -0
  458. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/supply-savepath.test.mjs +0 -0
  459. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/supply-select.test.mjs +0 -0
  460. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/surface.test.mjs +0 -0
  461. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/sweep-pool.test.mjs +0 -0
  462. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/sweep.fixture.test.mjs +0 -0
  463. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/sweep.test.mjs +0 -0
  464. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/vitest-config.test.mjs +0 -0
  465. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/worker-policy.test.mjs +0 -0
  466. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/worktree.test.mjs +0 -0
  467. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/test/writer.test.mjs +0 -0
  468. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/testguard.cost-budget.json +0 -0
  469. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/testguard.ignore.json +0 -0
  470. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/testguard_cli/__init__.py +0 -0
  471. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/testguard_cli/wrapper.py +0 -0
  472. {testguard_cli-0.18.1 → testguard_cli-0.18.2}/vitest.config.mjs +0 -0
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ // Same fixture commit and recipes, N=3, fresh workers, no reused verdicts.
3
+ // Warmups excluded; ABBA timing is informational, mismatched proof fails.
4
+ // Run without competing test processes. Keep the receipt directory for audit.
5
+ // node .github/scripts/benchmark-node-direct.mjs --before-ref <SHA> --out <dir>
6
+ import assert from 'node:assert/strict';
7
+ import { cpSync, mkdtempSync, readFileSync, mkdirSync, writeFileSync, rmSync, readdirSync, symlinkSync } from 'node:fs';
8
+ import { execFileSync } from 'node:child_process';
9
+ import { tmpdir } from 'node:os';
10
+ import { dirname, join, resolve } from 'node:path';
11
+ import { fileURLToPath, pathToFileURL } from 'node:url';
12
+ import { parseArgs } from 'node:util';
13
+ import { performance } from 'node:perf_hooks';
14
+ import { probe as after } from '../../src/probe/probe.mjs';
15
+ import { readSpecDoc, writeSpecDoc } from '../../src/evidence/writer.mjs';
16
+ import { detectContention } from '../../src/probe/contention.mjs';
17
+
18
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
19
+ const { values } = parseArgs({ options: { out: { type: 'string' }, 'before-ref': { type: 'string' } } });
20
+ assert(values.out && /^[0-9a-f]{40}$/.test(values['before-ref'] ?? ''), '--out and an exact --before-ref commit SHA are required');
21
+ const out = resolve(values.out);
22
+ mkdirSync(out, { recursive: true, mode: 0o700 });
23
+ mkdirSync(join(out, 'workers'), { mode: 0o700 }); // Refuse to overwrite a previous study.
24
+ const tool = mkdtempSync(join(tmpdir(), 'tg-direct-reference-'));
25
+ const dir = mkdtempSync(join(tmpdir(), 'tg-direct-bench-'));
26
+ const git = (...args) => execFileSync('git', args, { cwd: dir, encoding: 'utf8', timeout: 10000 }).trim();
27
+ const projection = (record) => ({
28
+ id: record.claim.id + '/' + record.subject.id,
29
+ subject: record.subject,
30
+ verdict: record.verdict,
31
+ reason: record.detail.reason ?? null,
32
+ defenders: record.defenders,
33
+ targetHash: record.inputs?.targetHash,
34
+ defenderHashes: record.inputs?.defenderHashes,
35
+ discoveryHashes: record.inputs?.discoveryHashes,
36
+ baseline: (record.detail.baselineRuns ?? []).map(({ durationMs, ...run }) => run),
37
+ probe: (record.detail.probeRuns ?? []).map(({ durationMs, ...run }) => run),
38
+ negativeControl: record.detail.negativeControl ?? null,
39
+ });
40
+ // testUniverseHash intentionally changes: it binds the changed adapter itself.
41
+ // The unchanged entries, source bytes, defender bytes and outcomes are checked.
42
+ try {
43
+ const archive = execFileSync('git', ['archive', '--format=tar', values['before-ref']], { cwd: root, maxBuffer: 32 * 1024 * 1024, timeout: 10000 });
44
+ execFileSync('tar', ['-xf', '-', '-C', tool], { input: archive, timeout: 10000 });
45
+ symlinkSync(join(root, 'node_modules'), join(tool, 'node_modules'), 'junction');
46
+ const { probe: before } = await import(pathToFileURL(join(tool, 'src/probe/probe.mjs')).href);
47
+ cpSync(join(root, 'fixtures/known-answer-node'), dir, { recursive: true });
48
+ for (const file of readdirSync(join(dir, 'test'))) {
49
+ const path = join(dir, 'test', file);
50
+ const receipt = `import {writeFileSync as receipt} from 'node:fs';if(process.env.TESTGUARD_NODE_COLLECT!=='1')receipt(${JSON.stringify(join(out, 'workers'))}+'/'+process.pid+'.json',JSON.stringify({pid:process.pid,ppid:process.ppid,version:process.versions.node,file:${JSON.stringify(file)}}),{flag:'wx'});\n`;
51
+ writeFileSync(path, receipt + readFileSync(path, 'utf8'));
52
+ }
53
+ for (const args of [['init', '-q'], ['add', '.'], ['-c', 'user.name=research', '-c', 'user.email=research@example.invalid', 'commit', '-qm', 'same inputs']]) git(...args);
54
+ const head = git('rev-parse', 'HEAD');
55
+ const claims = readSpecDoc('claims', join(dir, 'testguard.claims.json'));
56
+ const expected = JSON.parse(readFileSync(join(dir, 'expected.json'))).expected;
57
+ const results = [];
58
+ let reference;
59
+ async function wave(strategy, label, measured) {
60
+ assert(!detectContention().detected, 'foreign runner contention');
61
+ const ids = new Set(readdirSync(join(out, 'workers')));
62
+ const start = performance.now();
63
+ const evidence = await ({ before, after }[strategy])({ projectDir: dir, claims, runnerName: 'node-test', confirmRuns: 3, workers: 1, serial: true, budgetMs: 10000, escalate: false });
64
+ const elapsedMs = performance.now() - start;
65
+ assert.equal(evidence.run.repo.head, head);
66
+ assert.equal(git('status', '--porcelain'), '');
67
+ assert.equal(git('worktree', 'list', '--porcelain').split('worktree ').length - 1, 1);
68
+ assert.equal(evidence.records.length, Object.keys(expected).length);
69
+ assert.deepStrictEqual(evidence.records.map((record) => record.claim.id + '/' + record.subject.id), claims.claims.flatMap((claim) => claim.faults.map((fault) => claim.id + '/' + fault.id)));
70
+ for (const record of evidence.records) {
71
+ const oracle = expected[record.claim.id + '/' + record.subject.id];
72
+ assert.equal(record.verdict, oracle.verdict);
73
+ if (oracle.reason) assert.equal(record.detail.reason, oracle.reason);
74
+ assert(!record.reusedFrom);
75
+ if (record.verdict === 'killed') {
76
+ assert.equal(record.detail.probeRuns.length, 3);
77
+ assert.equal(record.detail.baselineRuns.length, 3);
78
+ assert(record.detail.baselineRuns.every((run) => run.outcome === 'pass'));
79
+ assert(record.detail.probeRuns.every((run) => run.outcome === 'fail' && run.assertionFailures > 0));
80
+ }
81
+ }
82
+ const projected = evidence.records.map(projection);
83
+ reference ??= projected;
84
+ assert.deepStrictEqual(projected, reference);
85
+ writeSpecDoc('evidence', join(out, label + '-' + strategy + '.json'), evidence);
86
+ const receipts = readdirSync(join(out, 'workers')).filter((file) => !ids.has(file)).map((file) => JSON.parse(readFileSync(join(out, 'workers', file))));
87
+ for (const receipt of receipts) assert.equal(receipt.version, process.versions.node);
88
+ if (measured) results.push({ strategy, label, elapsedMs, measurements: evidence.run.measurements, workerReceipts: receipts.length, parentPids: new Set(receipts.map((receipt) => receipt.ppid)).size });
89
+ }
90
+ await wave('before', 'warm', false);
91
+ await wave('after', 'warm', false);
92
+ for (const [i, strategy] of ['before', 'after', 'after', 'before'].entries()) await wave(strategy, String(i), true);
93
+ const mean = (strategy) => results.filter((result) => result.strategy === strategy).reduce((sum, result) => sum + result.elapsedMs, 0) / 2;
94
+ const summary = {
95
+ version: process.versions.node,
96
+ toolHead: execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(),
97
+ beforeRef: values['before-ref'], head,
98
+ beforeMs: mean('before'), afterMs: mean('after'), speedup: mean('before') / mean('after'),
99
+ verifiedOutcomes: 40, results,
100
+ };
101
+ writeFileSync(join(out, 'summary.json'), JSON.stringify(summary, null, 2) + '\n');
102
+ console.log(JSON.stringify(summary));
103
+ } finally {
104
+ rmSync(dir, { recursive: true, force: true });
105
+ rmSync(tool, { recursive: true, force: true });
106
+ }
@@ -247,6 +247,32 @@ jobs:
247
247
  path: .testguard/ci-self-evidence.json
248
248
  if-no-files-found: warn
249
249
 
250
+ native-performance:
251
+ if: github.event_name == 'pull_request'
252
+ runs-on: ubuntu-latest
253
+ strategy:
254
+ matrix:
255
+ node-version: [22, 24]
256
+ steps:
257
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
258
+ with:
259
+ fetch-depth: 0
260
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
261
+ with:
262
+ node-version: ${{ matrix.node-version }}
263
+ - run: npm ci --omit=dev
264
+ - name: Matched native probes against the exact base commit
265
+ env:
266
+ BEFORE_REF: ${{ github.event.pull_request.base.sha }}
267
+ run: node .github/scripts/benchmark-node-direct.mjs --before-ref "$BEFORE_REF" --out "$RUNNER_TEMP/native-performance"
268
+ - name: Retain timings, worker receipts and every N=3 proof
269
+ if: always()
270
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
271
+ with:
272
+ name: native-performance-${{ matrix.node-version }}
273
+ path: ${{ runner.temp }}/native-performance
274
+ if-no-files-found: error
275
+
250
276
  native-node-floor:
251
277
  runs-on: ubuntu-latest
252
278
  steps:
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.18.2] - 2026-10-02
11
+
12
+ Automated weekly release — everything merged since `v0.18.1`.
13
+
14
+ ### Changed
15
+
16
+ - perf: use one fresh process for single-entry native probes (#200)
17
+ - perf: focus gate annotation and claim selection defenders (#198)
18
+ - chore(homebrew): sha256 for v0.18.1 (#197)
19
+
20
+
21
+ ### Performance
22
+
23
+ - Eligible single-file native Node confirmations use one fresh process, avoiding a second process launch while preserving real CLI entry identity, ordinary tests beside `.only`, import and late-error refusals, budgets, and process cleanup. Discovery, multiple files and older runtimes keep the existing isolated route.
24
+ - Focus gate coverage, repeated claim selection and annotation ID bounds defenders on their relevant checks, retaining all original assertions in the mandatory acceptance suite and all fault recipes.
25
+
10
26
  ## [0.18.1] - 2026-10-01
11
27
 
12
28
  Automated weekly release — everything merged since `v0.18.0`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: testguard-cli
3
- Version: 0.18.1
3
+ Version: 0.18.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.18.1` — see [`action.yml`](./action.yml) |
154
+ | GitHub Action | `uses: raccioly/testguard@v0.18.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.18.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.18.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
@@ -809,12 +809,12 @@ detected base that does not resolve is a warning for `status` and `brief`
809
809
  # GitHub Actions
810
810
  - uses: actions/checkout@v4
811
811
  with: { fetch-depth: 0 }
812
- - uses: raccioly/testguard@v0.18.1
812
+ - uses: raccioly/testguard@v0.18.2
813
813
  with: { command: gate }
814
814
 
815
815
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
816
816
  include:
817
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/packaging/gitlab/testguard.gitlab-ci.yml'
817
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.2/packaging/gitlab/testguard.gitlab-ci.yml'
818
818
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
819
819
  ```
820
820
 
@@ -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.18.1` — see [`action.yml`](./action.yml) |
130
+ | GitHub Action | `uses: raccioly/testguard@v0.18.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.18.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.18.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
@@ -785,12 +785,12 @@ detected base that does not resolve is a warning for `status` and `brief`
785
785
  # GitHub Actions
786
786
  - uses: actions/checkout@v4
787
787
  with: { fetch-depth: 0 }
788
- - uses: raccioly/testguard@v0.18.1
788
+ - uses: raccioly/testguard@v0.18.2
789
789
  with: { command: gate }
790
790
 
791
791
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
792
792
  include:
793
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/packaging/gitlab/testguard.gitlab-ci.yml'
793
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.2/packaging/gitlab/testguard.gitlab-ci.yml'
794
794
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
795
795
  ```
796
796
 
@@ -50,7 +50,7 @@ inputs:
50
50
  version:
51
51
  description: 'testguard-cli version to run'
52
52
  required: false
53
- default: '0.18.1'
53
+ default: '0.18.2'
54
54
  node-version:
55
55
  description: 'Node.js version (20+)'
56
56
  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.18.1 · MIT</div>
147
+ <div class="meta">Technical Brief · v0.18.2 · MIT</div>
148
148
  </div>
149
149
 
150
150
  <h1>Your tests pass. That is not the same as your tests working.</h1>
@@ -85,7 +85,7 @@ other tools, and it must remain portable out of this repository.
85
85
  | Runtime | Node.js >= 20, ESM | `node:util.parseArgs` is the floor; the first target runners are JavaScript |
86
86
  | Runtime dependencies | exactly one, `ajv` 8.20.0, exact-pinned | Every document is validated before it is written; everything else is standard library |
87
87
  | Test runner (ours) | vitest | Also our first supported runner, so we dogfood it |
88
- | Supported runners | vitest, jest, explicit node-test (project runners); Playwright and Python (per file) | Native Node uses an injected entry preload and bounded structured reporter, reusing shared process budgets; auto selection does not infer Node eligibility from its availability |
88
+ | Supported runners | vitest, jest, explicit node-test (project runners); Playwright and Python (per file) | Native Node uses authenticated entry receipts and a bounded structured reporter, reusing shared process budgets. Eligible single-entry confirmations use one fresh process; discovery and multiple entries retain isolated controller/workers. Auto selection does not infer Node eligibility from its availability |
89
89
  | Schemas | JSON Schema 2020-12 | Readable without the tool; portable to other languages |
90
90
  | Distribution | npm, PyPI, Homebrew, GitHub Action, pre-commit, GitLab component | Meet teams in the pipeline they already run |
91
91
  | Publishing | OIDC for npm/PyPI; tap-scoped SSH deploy key for Homebrew | Registry credentials remain tokenless; the only stored publishing secret cannot access any repository except the tap |
@@ -95,15 +95,15 @@ fails the pull request naming the unclaimed file, and `status` reports
95
95
  | FR-05 | `src/probe/classify.mjs` | `TG-TIMEOUT-NEVER-KILLS`, `TG-PW-TIMEOUT-NEVER-KILLS` |
96
96
  | FR-06 | `src/baseline/baseline.mjs` | `test/baseline.test.mjs`, `TG-RESTAMP-REQUIRES-SAME-FINGERPRINTS` |
97
97
  | FR-07 | `src/brief/`, `src/status/`, `src/mcp/` | `test/brief.test.mjs`, `test/status.test.mjs`, `TG-MCP-IS-READ-ONLY` |
98
- | FR-08 | `src/gate/changed.mjs` | `test/gate.test.mjs`, `TG-GATE-UNCLAIMED-EXITS-1` |
98
+ | FR-08 | `src/gate/changed.mjs` | `test/gate.test.mjs`, `test/gate-coverage.test.mjs`, `TG-GATE-UNCLAIMED-EXITS-1` |
99
99
  | FR-09 | `src/probe/mocks.mjs` | `test/mocks.test.mjs`, `TG-MOCKING-FILE-IS-NOT-A-DEFENDER` |
100
- | FR-10 | `src/probe/runners/` | fixture acceptance per runner, `TG-RUNNER-FROM-PROJECT-FIRST` |
100
+ | FR-10 | `src/probe/runners/` | fixture acceptance per runner, `TG-RUNNER-FROM-PROJECT-FIRST`, `test/runner-node-direct.test.mjs` and `TG-NODE-DIRECT-KEEPS-FAILURE-BOUNDARIES` |
101
101
  | FR-11 | `src/scaffold/producers.mjs` | `test/scaffold.test.mjs`, `test/scaffold.python.test.mjs`, `TG-SCAFFOLD-ANCHORS-HIT`, `TG-SCAFFOLD-NEVER-PROPOSES-A-NO-OP`, `TG-SCAFFOLD-LOOP-GUARD-IS-A-GUARD` |
102
102
  | FR-12 | `src/admit/admit.mjs` | `test/admit.test.mjs`, `TG-ADMIT-NEEDS-ALL-KILLED` |
103
103
  | 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` |
104
104
  | FR-14 | `src/mcp/` | `test/mcp.test.mjs` |
105
105
  | 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` |
106
- | FR-16 | `src/cli.mjs`, `src/commands/probe.mjs`, `src/probe/probe.mjs`, `src/commands/scaffold.mjs`, `src/commands/admit.mjs` | `test/claim-selection.test.mjs`, `test/probe.fixture.test.mjs`, `TG-REPEATED-CLAIMS-ARE-ALL-PROBED` |
106
+ | FR-16 | `src/cli.mjs`, `src/commands/probe.mjs`, `src/probe/probe.mjs`, `src/commands/scaffold.mjs`, `src/commands/admit.mjs` | `test/claim-selection.test.mjs`, `test/claim-options.test.mjs`, `test/probe.fixture.test.mjs`, `TG-REPEATED-CLAIMS-ARE-ALL-PROBED` |
107
107
  | FR-17 | `src/probe/attribution.mjs`, `src/probe/probe.mjs`, `src/commands/claims.mjs`, `src/status/status.mjs`, `src/probe/cost.mjs` | `test/fault-defenders.test.mjs`, `test/attribution.test.mjs`, `test/status.test.mjs`, `test/cost.test.mjs`, `TG-FAULT-DEFENDER-SELECTION`, `TG-FAULT-DEFENDER-ORCHESTRATION` |
108
108
  | 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` |
109
109
  | NFR-02 | `package.json` | `npm run test:install` in CI |
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "testguard-cli",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "testguard-cli",
9
- "version": "0.18.1",
9
+ "version": "0.18.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.18.1",
3
+ "version": "0.18.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.18.1/packaging/gitlab/testguard.gitlab-ci.yml'
6
+ # - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.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.18.1"
32
+ default: "0.18.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.18.1.tgz"
19
- sha256 "b722d8656f634198a483ff50db7891c21cfccae3aa136faaf9436fc79df33d7d"
18
+ url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.18.2.tgz"
19
+ sha256 "ce853672bf41f9570e21f661d8544c4dda6872317b4863f5aeccba1744c78c9c"
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.18.1"
7
+ version = "0.18.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" }
@@ -321,7 +321,21 @@ Rules that follow from the table:
321
321
  supports plain JavaScript under Node's default collection, without custom
322
322
  loaders, `NODE_OPTIONS` or `NODE_PATH`; it is never selected by `auto`.
323
323
  Each confirmation uses fresh, isolated Node test processes under the
324
- existing worker ceiling. Reports count executed test results, not individual
324
+ existing worker ceiling. On Node22.8+, Node24 and Node26, a single selected
325
+ entry can run inside one fresh process through a reporting preload and the
326
+ public in-process API; Node evaluates the actual CLI entry, preserving
327
+ CommonJS `require.main`, ESM `import.meta.main` and argv identity. This process
328
+ serves no other entry or confirmation. Discovery, multiple
329
+ entries and other runtime families retain the isolated controller/worker
330
+ route. Single-entry execution preserves ordinary tests beside `.only`,
331
+ records entry provenance before evaluation, monitors entry-load failures
332
+ after test registration, and publishes only at process exit when no
333
+ out-of-test failure or incompatible exit code occurred. A complete stream
334
+ cannot turn a later exception, rejection or retained-handle timeout into a
335
+ kill. Report and entry destinations are captured before project evaluation;
336
+ project environment edits cannot redirect these owned outputs. Both routes
337
+ bind their implementation to native discovery identity.
338
+ Reports count executed test results, not individual
325
339
  assertion calls: suite and file-container summaries are not extra tests,
326
340
  and skipped/TODO results cannot establish a green defender by themselves.
327
341
  A test-body failure counts as a rejection; a load error, hook failure,
@@ -0,0 +1,55 @@
1
+ import { appendFileSync, writeFileSync } from 'node:fs';
2
+ import { readBoundedJsonFile } from '../discovery.mjs';
3
+ import { createReport } from './report.mjs';
4
+
5
+ // This preload returns before Node evaluates its actual CLI entry. Running the
6
+ // file as the entry preserves require.main, import.meta.main and process.argv.
7
+ const file = process.argv[1];
8
+ if (!file) throw new Error('direct node-test requires an entry');
9
+ const entriesFile = process.env.TESTGUARD_NODE_ENTRIES;
10
+ const reportFile = process.env.TESTGUARD_NODE_REPORT;
11
+ const diagnosticFile = `${entriesFile}.error`;
12
+ const report = createReport({ projectDir: process.cwd(), version: process.versions.node });
13
+ let document;
14
+ let outsideFailure = false;
15
+ process.on('uncaughtExceptionMonitor', (error) => {
16
+ outsideFailure = true;
17
+ writeFileSync(diagnosticFile, JSON.stringify({ name: String(error?.name ?? 'Error').slice(0, 100), message: String(error?.message ?? error).slice(0, 1000) }), { mode: 0o600 });
18
+ });
19
+ process.on('unhandledRejection', () => { outsideFailure = true; process.exitCode = 1; });
20
+ // A complete stream is insufficient: a retained timer can fail or hang later.
21
+ // Publish only at process exit, after CLI loading and out-of-test failures settle.
22
+ process.on('exit', (code) => {
23
+ if (!document || outsideFailure || code !== (document.failed ? 1 : 0)) return;
24
+ const text = JSON.stringify(document);
25
+ if (Buffer.byteLength(text) > 16 * 1024 * 1024) return;
26
+ writeFileSync(reportFile, text, { mode: 0o600 });
27
+ });
28
+ const receipt = JSON.stringify({ file, pid: process.pid, version: process.versions.node });
29
+ if (Buffer.byteLength(receipt) > 8192) throw new Error('node-test entry receipt exceeded its byte bound');
30
+ appendFileSync(entriesFile, `${receipt},`);
31
+
32
+ // Node24+ otherwise enables implicit .only filtering for isolation:none,
33
+ // even with run({only:false}). Preserve isolated-worker filtering semantics.
34
+ process.env.NODE_TEST_CONTEXT = 'child-v8';
35
+ const { run } = await import('node:test');
36
+ const stream = run({ files: [], isolation: 'none', only: false, setup: () => new Promise((resolve) => {
37
+ // Keep the public root open until Node has loaded the real CLI entry. An
38
+ // awaited stream here would block entry evaluation; the drain runs below.
39
+ process.once('beforeExit', resolve);
40
+ }) });
41
+ (async () => {
42
+ try {
43
+ for await (const event of stream) report.accept(event);
44
+ appendFileSync(entriesFile, 'null]}');
45
+ const entries = readBoundedJsonFile(entriesFile).entries;
46
+ if (!Array.isArray(entries) || entries.length !== 2 || entries.pop() !== null) throw new Error('incomplete direct node-test entry receipt');
47
+ document = report.finish(entries);
48
+ if (document.failed) process.exitCode = 1;
49
+ } catch (error) {
50
+ outsideFailure = true;
51
+ stream.destroy();
52
+ process.stderr.write(`node-test reporter failed: ${String(error.message ?? error).slice(0, 1000)}\n`);
53
+ process.exitCode = 1;
54
+ }
55
+ })();
@@ -9,3 +9,9 @@ if (process.env.NODE_TEST_CONTEXT?.startsWith('child')) {
9
9
  }
10
10
  appendFileSync(process.env.TESTGUARD_NODE_ENTRIES, `${receipt},`);
11
11
  }
12
+
13
+ // A fresh single-entry process initializes reporting before its real CLI
14
+ // entry. Inherited child context must not bootstrap a second reporter in a fork.
15
+ if (process.env.TESTGUARD_NODE_DIRECT === '1' && process.env.NODE_TEST_CONTEXT === '') {
16
+ await import('./direct.mjs');
17
+ }
@@ -1,10 +1,10 @@
1
- import { closeSync, mkdtempSync, openSync, readFileSync, readSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
1
+ import { closeSync, existsSync, mkdtempSync, openSync, readFileSync, readSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { runProcess } from './shared.mjs';
7
- import { createDiscoveryManifest, DiscoveryError, hashDiscoveryConfigs, normalizeDiscoveredFiles, runDiscoveryProcess } from './discovery.mjs';
7
+ import { createDiscoveryManifest, DiscoveryError, hashDiscoveryConfigs, normalizeDiscoveredFiles, readBoundedJsonFile, runDiscoveryProcess } from './discovery.mjs';
8
8
 
9
9
  export const name = 'node-test';
10
10
  export const testGlobs = []; // Native collection is authoritative; never guess from globs.
@@ -12,6 +12,11 @@ const HERE = join(dirname(fileURLToPath(import.meta.url)), 'node');
12
12
  const RUNNER = join(HERE, 'runner.mjs');
13
13
  const ENTRY = join(HERE, 'entry.mjs');
14
14
  const VERSION = process.versions.node;
15
+ /** Admit only maintained runtime families with the in-process public API. */
16
+ export function supportsSingleProcess(version = VERSION) {
17
+ const [major, minor] = version.split('.').map(Number);
18
+ return major === 22 && minor >= 8 || major === 24 || major === 26;
19
+ }
15
20
  const strategyHash = () => {
16
21
  const digest = createHash('sha256');
17
22
  const descriptor = openSync(process.execPath, 'r');
@@ -20,7 +25,7 @@ const strategyHash = () => {
20
25
  let length;
21
26
  while ((length = readSync(descriptor, bytes, 0, bytes.length, null)) > 0) digest.update(bytes.subarray(0, length));
22
27
  } finally { closeSync(descriptor); }
23
- for (const file of [fileURLToPath(import.meta.url), ...['runner.mjs', 'entry.mjs', 'report.mjs'].map((file) => join(HERE, file)), ...['shared.mjs', 'discovery.mjs', 'lifecycle.mjs'].map((file) => join(HERE, '..', file))]) digest.update(readFileSync(file));
28
+ for (const file of [fileURLToPath(import.meta.url), ...['runner.mjs', 'direct.mjs', 'entry.mjs', 'report.mjs'].map((file) => join(HERE, file)), ...['shared.mjs', 'discovery.mjs', 'lifecycle.mjs'].map((file) => join(HERE, '..', file))]) digest.update(readFileSync(file));
24
29
  return digest.digest('hex');
25
30
  };
26
31
  const admissionError = () => Number(VERSION.split('.')[0]) < 20 ? 'node-test requires Node20 or newer'
@@ -37,15 +42,16 @@ const receipts = () => {
37
42
  writeFileSync(file, '{"entries":[', { mode: 0o600 });
38
43
  return { dir, file };
39
44
  };
40
- const environment = (file, collect, workers) => ({
45
+ const environment = (file, collect, workers, direct = false) => ({
41
46
  TESTGUARD_NODE_ENTRIES: file,
42
47
  TESTGUARD_NODE_COLLECT: collect ? '1' : '0',
43
48
  TESTGUARD_NODE_WORKERS: String(workers),
44
49
  TESTGUARD_NODE_REPORT: '{out}',
50
+ TESTGUARD_NODE_DIRECT: direct ? '1' : '0',
45
51
  NODE_TEST_CONTEXT: '',
46
52
  NODE_DISABLE_COMPILE_CACHE: '1',
47
53
  });
48
- export const argvFor = (projectDir, files) => [process.execPath, `--import=${ENTRY}`, RUNNER, ...files.map((file) => resolve(realpathSync(projectDir), file))];
54
+ export const argvFor = (projectDir, files, { direct = false } = {}) => [process.execPath, `--import=${ENTRY}`, ...(!direct ? [RUNNER] : []), ...files.map((file) => resolve(realpathSync(projectDir), file))];
49
55
 
50
56
  /** Reject unsupported/incomplete envelopes before deriving any verdict inputs. */
51
57
  export function validateReport(report) {
@@ -120,11 +126,19 @@ export async function run(opts) {
120
126
  const files = normalizeDiscoveredFiles(opts.projectDir, opts.files);
121
127
  const spool = receipts();
122
128
  try {
123
- return await runProcess({ ...opts, files, cleanupOnClose: true, env: environment(spool.file, false, opts.serial ? 1 : opts.workers ?? 1), argv: (selected) => argvFor(opts.projectDir, selected), parse: (report, durationMs) => {
129
+ const direct = files.length === 1 && supportsSingleProcess();
130
+ const result = await runProcess({ ...opts, files, cleanupOnClose: true, env: environment(spool.file, false, opts.serial ? 1 : opts.workers ?? 1, direct), argv: (selected) => argvFor(opts.projectDir, selected, { direct }), parse: (report, durationMs) => {
124
131
  validateReport(report);
125
132
  const entries = normalizeDiscoveredFiles(opts.projectDir, report.entries.map((entry) => entry.file));
126
133
  if (entries.length !== report.entries.length || JSON.stringify(entries) !== JSON.stringify(files)) throw new DiscoveryError('node-test did not execute every selected entry exactly');
127
134
  return parseReport(report, durationMs);
128
135
  } });
136
+ // CLI loading can fail before a complete stream exists. Keep the actual
137
+ // bounded exception identity rather than Node's trailing version banner.
138
+ if (result.run.outcome === 'error' && existsSync(`${spool.file}.error`)) {
139
+ const diagnostic = readBoundedJsonFile(`${spool.file}.error`, 8192);
140
+ if (typeof diagnostic.name === 'string' && diagnostic.name.length <= 100 && typeof diagnostic.message === 'string' && diagnostic.message.length <= 1000) result.loadMessage = `${diagnostic.name}: ${diagnostic.message}`;
141
+ }
142
+ return result;
129
143
  } finally { rmSync(spool.dir, { recursive: true, force: true }); }
130
144
  }
@@ -2,8 +2,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
2
2
  import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
- import { spawnSync } from 'node:child_process';
7
5
  import { claimsCommand } from '../src/commands/claims.mjs';
8
6
  import { computeStatus, renderStatus } from '../src/status/status.mjs';
9
7
  import { validate } from '../spec/lib/validate.mjs';
@@ -88,21 +86,6 @@ describe('annotation guidance is not verification', () => {
88
86
  expect(status.notes.join('\n')).not.toContain('EACCES');
89
87
  expect(status.notes.join('\n')).not.toContain('Missing source annotation:');
90
88
  });
91
- it('finishes lexical scanning for long prose tokens without a hyphen', () => {
92
- const dir = project(); writeFileSync(join(dir, 'prose.md'), `@claim ${'a'.repeat(256)}\n`);
93
- const run = spawnSync(process.execPath, ['--input-type=module', '-e',
94
- 'const { projectAnnotationAdvisory, scanAnnotations } = await import(process.argv[1]); const advice = projectAnnotationAdvisory(process.argv[2], { claims: [{ id: "A-1" }] }); if (advice.missingIds.join() !== "A-1" || scanAnnotations(process.argv[2]).length) process.exit(1);',
95
- new URL('../src/claims/annotations.mjs', import.meta.url).href, dir], { encoding: 'utf8', timeout: 2000 });
96
- expect(run.error).toBeUndefined(); expect(run.status).toBe(0);
97
- });
98
- it('promptly refuses valid shared-format IDs outside placement vocabulary', () => {
99
- const dir = project(); const path = join(dir, 'testguard.claims.json');
100
- const claims = { schemaVersion: 1, claims: [{ id: 'a'.repeat(128), statement: 'Intended behavior.', severity: 'high', source: { kind: 'manual' }, producedBy: { producer: 'human' }, faults: [{ id: 'F1', file: 'a.mjs', find: 'true', replace: 'false', description: 'Break behavior.', faultClass: 'other', producedBy: { producer: 'human' } }] }] };
101
- expect(validate('claims', claims).ok).toBe(true); writeFileSync(path, JSON.stringify(claims));
102
- const run = spawnSync(process.execPath, [fileURLToPath(new URL('../cli/testguard.mjs', import.meta.url)), 'claims', dir, '--annotate', '--json'], { encoding: 'utf8', timeout: 2000 });
103
- expect(run.error).toBeUndefined(); expect(run.status).toBe(2);
104
- expect(JSON.parse(run.stdout).refused[0].reason).toBe('unsupported-claim-id');
105
- });
106
89
  it('explicitly preserves custom claims selection in preview advice', async () => {
107
90
  const dir = project(); const custom = join(dir, 'custom.json');
108
91
  writeFileSync(custom, JSON.stringify({ schemaVersion: 1, claims: [{ id: 'B-1', statement: 'Custom intent.', severity: 'high', source: { kind: 'manual' }, producedBy: { producer: 'human' }, faults: [{ id: 'F1', file: 'a.mjs', find: 'true', replace: 'false', description: 'Break condition.', faultClass: 'other', producedBy: { producer: 'human' } }] }] }));
@@ -0,0 +1,33 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { spawnSync } from 'node:child_process';
7
+ import { validate } from '../spec/lib/validate.mjs';
8
+
9
+ const roots = [];
10
+ afterEach(() => roots.splice(0).forEach((dir) => rmSync(dir, { recursive: true, force: true })));
11
+ function project() {
12
+ const dir = mkdtempSync(join(tmpdir(), 'tg-annotation-bounds-')); roots.push(dir);
13
+ writeFileSync(join(dir, 'a.mjs'), 'export const allowed = true;\n');
14
+ return dir;
15
+ }
16
+ describe('bounded annotation ID matching', () => {
17
+
18
+ it('finishes lexical scanning for long prose tokens without a hyphen', () => {
19
+ const dir = project(); writeFileSync(join(dir, 'prose.md'), `@claim ${'a'.repeat(256)}\n`);
20
+ const run = spawnSync(process.execPath, ['--input-type=module', '-e',
21
+ 'const { projectAnnotationAdvisory, scanAnnotations } = await import(process.argv[1]); const advice = projectAnnotationAdvisory(process.argv[2], { claims: [{ id: "A-1" }] }); if (advice.missingIds.join() !== "A-1" || scanAnnotations(process.argv[2]).length) process.exit(1);',
22
+ new URL('../src/claims/annotations.mjs', import.meta.url).href, dir], { encoding: 'utf8', timeout: 2000 });
23
+ expect(run.error).toBeUndefined(); expect(run.status).toBe(0);
24
+ });
25
+ it('promptly refuses valid shared-format IDs outside placement vocabulary', () => {
26
+ const dir = project(); const path = join(dir, 'testguard.claims.json');
27
+ const claims = { schemaVersion: 1, claims: [{ id: 'a'.repeat(128), statement: 'Intended behavior.', severity: 'high', source: { kind: 'manual' }, producedBy: { producer: 'human' }, faults: [{ id: 'F1', file: 'a.mjs', find: 'true', replace: 'false', description: 'Break behavior.', faultClass: 'other', producedBy: { producer: 'human' } }] }] };
28
+ expect(validate('claims', claims).ok).toBe(true); writeFileSync(path, JSON.stringify(claims));
29
+ const run = spawnSync(process.execPath, [fileURLToPath(new URL('../cli/testguard.mjs', import.meta.url)), 'claims', dir, '--annotate', '--json'], { encoding: 'utf8', timeout: 2000 });
30
+ expect(run.error).toBeUndefined(); expect(run.status).toBe(2);
31
+ expect(JSON.parse(run.stdout).refused[0].reason).toBe('unsupported-claim-id');
32
+ });
33
+ });