testguard-cli 0.17.0__tar.gz → 0.18.1__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 (466) hide show
  1. testguard_cli-0.18.1/.github/scripts/benchmark-node-runner.mjs +140 -0
  2. testguard_cli-0.18.1/.github/scripts/check-node-fixture.mjs +28 -0
  3. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/scripts/install-smoke.mjs +13 -0
  4. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/workflows/ci.yml +18 -1
  5. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/CHANGELOG.md +40 -0
  6. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/PKG-INFO +22 -6
  7. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/README.md +21 -5
  8. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/action.yml +1 -1
  9. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs/testguard-explained.html +1 -1
  10. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs/testguard-explained.pdf +0 -0
  11. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs-canonical/ARCHITECTURE.md +1 -1
  12. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs-canonical/REQUIREMENTS.md +2 -2
  13. testguard_cli-0.18.1/fixtures/known-answer-node/.gitignore +3 -0
  14. testguard_cli-0.18.1/fixtures/known-answer-node/README.md +15 -0
  15. testguard_cli-0.18.1/fixtures/known-answer-node/expected.json +51 -0
  16. testguard_cli-0.18.1/fixtures/known-answer-node/package.json +9 -0
  17. testguard_cli-0.18.1/fixtures/known-answer-node/src/policy.mjs +15 -0
  18. testguard_cli-0.18.1/fixtures/known-answer-node/src/unused.mjs +1 -0
  19. testguard_cli-0.18.1/fixtures/known-answer-node/test/flaky.test.mjs +11 -0
  20. testguard_cli-0.18.1/fixtures/known-answer-node/test/mixed.test.mjs +11 -0
  21. testguard_cli-0.18.1/fixtures/known-answer-node/test/policy.test.mjs +15 -0
  22. testguard_cli-0.18.1/fixtures/known-answer-node/testguard.claims.json +285 -0
  23. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/package-lock.json +2 -2
  24. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/package.json +1 -1
  25. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
  26. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/packaging/homebrew/testguard.rb +2 -2
  27. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/pyproject.toml +1 -1
  28. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/GATE-SEMANTICS.md +21 -0
  29. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/schemas.test.mjs +14 -0
  30. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/lib/validate.mjs +3 -0
  31. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/evidence.schema.json +3 -2
  32. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/cli.mjs +1 -1
  33. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/probe.mjs +1 -1
  34. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/discovery.mjs +12 -1
  35. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/index.mjs +3 -1
  36. testguard_cli-0.18.1/src/probe/runners/node/entry.mjs +11 -0
  37. testguard_cli-0.18.1/src/probe/runners/node/report.mjs +66 -0
  38. testguard_cli-0.18.1/src/probe/runners/node/runner.mjs +42 -0
  39. testguard_cli-0.18.1/src/probe/runners/node-test.mjs +130 -0
  40. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/shared.mjs +2 -1
  41. testguard_cli-0.18.1/test/probe.node.test.mjs +200 -0
  42. testguard_cli-0.18.1/test/runner-node-admission.test.mjs +35 -0
  43. testguard_cli-0.18.1/test/runner-node-controller.test.mjs +52 -0
  44. testguard_cli-0.18.1/test/runner-node-report.test.mjs +82 -0
  45. testguard_cli-0.18.1/test/runner-report-allocation.test.mjs +81 -0
  46. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/testguard.claims.json +289 -0
  47. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/testguard.ignore.json +7 -0
  48. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.agents/skills/testguard/SKILL.md +0 -0
  49. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.docguard.json +0 -0
  50. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.docguardignore +0 -0
  51. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.gitattributes +0 -0
  52. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  53. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  54. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  55. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/dependabot.yml +0 -0
  56. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/scripts/approve-held-runs.sh +0 -0
  57. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/scripts/build-one-pager.mjs +0 -0
  58. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/scripts/check-cost-budget.mjs +0 -0
  59. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/scripts/check-python-fixture.mjs +0 -0
  60. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/scripts/publish-homebrew-tap.sh +0 -0
  61. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/scripts/sync-release-version.mjs +0 -0
  62. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/workflows/auto-merge.yml +0 -0
  63. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/workflows/release.yml +0 -0
  64. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/workflows/scheduled-release.yml +0 -0
  65. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.github/workflows/supply-chain.yml +0 -0
  66. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.gitignore +0 -0
  67. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.npmignore +0 -0
  68. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.npmrc +0 -0
  69. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.pre-commit-config.yaml +0 -0
  70. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/.pre-commit-hooks.yaml +0 -0
  71. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/AGENTS.md +0 -0
  72. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/CLAUDE.md +0 -0
  73. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/CODE_OF_CONDUCT.md +0 -0
  74. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/CONTRIBUTING.md +0 -0
  75. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/DRIFT-LOG.md +0 -0
  76. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/LICENSE +0 -0
  77. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/PRIVACY.md +0 -0
  78. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/SECURITY.md +0 -0
  79. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/SUPPORT.md +0 -0
  80. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/bench/README.md +0 -0
  81. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/cli/testguard.mjs +0 -0
  82. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs-canonical/DATA-MODEL.md +0 -0
  83. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs-canonical/ENVIRONMENT.md +0 -0
  84. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs-canonical/PRIOR-ART.md +0 -0
  85. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs-canonical/SECURITY.md +0 -0
  86. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/docs-canonical/TEST-SPEC.md +0 -0
  87. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/.gitignore +0 -0
  88. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/README.md +0 -0
  89. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/expected.json +0 -0
  90. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/package.json +0 -0
  91. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/src/export.mjs +0 -0
  92. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/src/redact.mjs +0 -0
  93. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
  94. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/test/flaky.test.mjs +0 -0
  95. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/test/redact.test.mjs +0 -0
  96. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/testguard.claims.json +0 -0
  97. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer/vitest.config.mjs +0 -0
  98. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/.gitignore +0 -0
  99. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/README.md +0 -0
  100. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/expected.json +0 -0
  101. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/jest.config.js +0 -0
  102. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/package.json +0 -0
  103. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/src/export.js +0 -0
  104. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/src/redact.js +0 -0
  105. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
  106. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
  107. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/test/redact.test.js +0 -0
  108. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/testguard.claims.json +0 -0
  109. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/.gitignore +0 -0
  110. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/README.md +0 -0
  111. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
  112. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
  113. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/expected.json +0 -0
  114. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/package-lock.json +0 -0
  115. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/package.json +0 -0
  116. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
  117. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
  118. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
  119. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
  120. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
  121. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/.gitignore +0 -0
  122. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/README.md +0 -0
  123. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/__init__.py +0 -0
  124. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/export.py +0 -0
  125. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/orphan.py +0 -0
  126. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/pipeline.py +0 -0
  127. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/redact.py +0 -0
  128. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/expected.json +0 -0
  129. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/testguard.claims.json +0 -0
  130. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
  131. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
  132. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/tests/test_patched.py +0 -0
  133. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/tests/test_redact.py +0 -0
  134. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/README.md +0 -0
  135. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/annotations.test.mjs +0 -0
  136. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/authoring-input.test.mjs +0 -0
  137. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/draft-append.test.mjs +0 -0
  138. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/annotations.json +0 -0
  139. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/authoring-input.json +0 -0
  140. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/baseline.json +0 -0
  141. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/brief.json +0 -0
  142. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/calibration-websec.json +0 -0
  143. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/calibration.json +0 -0
  144. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/claims-methods.json +0 -0
  145. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/claims.json +0 -0
  146. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
  147. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-negative-control.json +0 -0
  148. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-persistence-signal.json +0 -0
  149. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-provisional.json +0 -0
  150. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-python.json +0 -0
  151. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-resources.json +0 -0
  152. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-scan-method.json +0 -0
  153. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence.json +0 -0
  154. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/gate.json +0 -0
  155. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/ignore.json +0 -0
  156. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/replay.json +0 -0
  157. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/status-resource-cost.json +0 -0
  158. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/examples/status.json +0 -0
  159. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
  160. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/baseline.zero-count.json +0 -0
  161. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
  162. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
  163. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
  164. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
  165. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
  166. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
  167. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
  168. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
  169. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
  170. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
  171. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.absolute-path.json +0 -0
  172. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
  173. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
  174. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
  175. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
  176. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.noop-fault.json +0 -0
  177. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
  178. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
  179. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
  180. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
  181. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
  182. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
  183. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
  184. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
  185. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
  186. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
  187. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
  188. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
  189. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
  190. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
  191. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
  192. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
  193. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
  194. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.persistence-signal-not-a-defender.json +0 -0
  195. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.persistence-signal-on-a-non-survivor.json +0 -0
  196. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.persistence-signal-without-reason.json +0 -0
  197. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
  198. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
  199. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
  200. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.resource-accounting.json +0 -0
  201. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
  202. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.serial-worker-conflict.json +0 -0
  203. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
  204. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
  205. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
  206. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
  207. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
  208. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
  209. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
  210. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
  211. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
  212. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
  213. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/ignore.short-reason.json +0 -0
  214. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
  215. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
  216. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/replay.nocover-when-no-test-file-remained.json +0 -0
  217. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.claim-without-file.json +0 -0
  218. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
  219. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
  220. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
  221. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.resource-accounting.json +0 -0
  222. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
  223. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
  224. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
  225. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/origin-policy.test.mjs +0 -0
  226. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/origin-summaries.test.mjs +0 -0
  227. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/conformance/origins.test.mjs +0 -0
  228. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/lib/authoring-input.mjs +0 -0
  229. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/lib/fingerprint.mjs +0 -0
  230. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/lib/origins.mjs +0 -0
  231. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/lib/wilson.mjs +0 -0
  232. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/annotations.schema.json +0 -0
  233. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/authoring-input.schema.json +0 -0
  234. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/baseline.schema.json +0 -0
  235. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/brief.schema.json +0 -0
  236. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/calibration.schema.json +0 -0
  237. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/claims.schema.json +0 -0
  238. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/common.schema.json +0 -0
  239. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/concerns.schema.json +0 -0
  240. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/gate.schema.json +0 -0
  241. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/ignore.schema.json +0 -0
  242. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/replay.schema.json +0 -0
  243. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/status.schema.json +0 -0
  244. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/spec/schemas/sweep.schema.json +0 -0
  245. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/admit/admit.mjs +0 -0
  246. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/baseline/baseline.mjs +0 -0
  247. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/brief/brief.mjs +0 -0
  248. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/claims/anchors.mjs +0 -0
  249. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/claims/annotation-placement.mjs +0 -0
  250. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/claims/annotations.mjs +0 -0
  251. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/claims/load.mjs +0 -0
  252. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/claims/removed.mjs +0 -0
  253. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/command-budget.mjs +0 -0
  254. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/admit.mjs +0 -0
  255. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/annotations.mjs +0 -0
  256. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/baseline.mjs +0 -0
  257. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/brief.mjs +0 -0
  258. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/claims.mjs +0 -0
  259. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/concerns.mjs +0 -0
  260. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/gate.mjs +0 -0
  261. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/init.mjs +0 -0
  262. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/mcp.mjs +0 -0
  263. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/probe.mjs +0 -0
  264. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/replay.mjs +0 -0
  265. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/scaffold.mjs +0 -0
  266. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/status.mjs +0 -0
  267. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/commands/sweep.mjs +0 -0
  268. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/evidence/writer.mjs +0 -0
  269. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/gate/changed.mjs +0 -0
  270. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/git.mjs +0 -0
  271. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/init/init.mjs +0 -0
  272. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/init/templates/SKILL.md +0 -0
  273. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/mcp/server.mjs +0 -0
  274. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/mcp/tools.mjs +0 -0
  275. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/attribution.mjs +0 -0
  276. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/classify.mjs +0 -0
  277. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/contention.mjs +0 -0
  278. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/cost.mjs +0 -0
  279. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/discover.mjs +0 -0
  280. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/imports.mjs +0 -0
  281. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/independence.mjs +0 -0
  282. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/inject.mjs +0 -0
  283. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/mocks.mjs +0 -0
  284. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/origin-policy.mjs +0 -0
  285. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/policy-freshness.mjs +0 -0
  286. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/progress.mjs +0 -0
  287. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/pyimports.mjs +0 -0
  288. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/rank.mjs +0 -0
  289. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/jest.mjs +0 -0
  290. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/lifecycle.mjs +0 -0
  291. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/playwright.mjs +0 -0
  292. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
  293. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/python/_testguard_report.py +0 -0
  294. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
  295. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/python.mjs +0 -0
  296. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/runners/vitest.mjs +0 -0
  297. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/universe.mjs +0 -0
  298. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/probe/worktree.mjs +0 -0
  299. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/render.mjs +0 -0
  300. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/replay/label.mjs +0 -0
  301. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/replay/replay.mjs +0 -0
  302. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/admission.mjs +0 -0
  303. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/append.mjs +0 -0
  304. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/apply-append.mjs +0 -0
  305. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/fix-input.mjs +0 -0
  306. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/fix-paths.mjs +0 -0
  307. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/intent.mjs +0 -0
  308. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/open-append.mjs +0 -0
  309. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/plan-append.mjs +0 -0
  310. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/plan-intent-input.mjs +0 -0
  311. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/prepare-append.mjs +0 -0
  312. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/producers.mjs +0 -0
  313. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/producers.python.mjs +0 -0
  314. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/request.mjs +0 -0
  315. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/scaffold/scaffold.mjs +0 -0
  316. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/status/status.mjs +0 -0
  317. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/status/surface.mjs +0 -0
  318. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/supply/concerns.mjs +0 -0
  319. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/supply/feedback.mjs +0 -0
  320. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/supply/persistence.mjs +0 -0
  321. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/supply/savepath.mjs +0 -0
  322. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/supply/select.mjs +0 -0
  323. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/sweep/pool.mjs +0 -0
  324. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/sweep/sweep.mjs +0 -0
  325. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/util/glob.mjs +0 -0
  326. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/src/util/hash.mjs +0 -0
  327. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/admit-decision.test.mjs +0 -0
  328. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/admit.test.mjs +0 -0
  329. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/anchor-admission.test.mjs +0 -0
  330. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/annotation-advisory-limits.test.mjs +0 -0
  331. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/annotation-advisory.test.mjs +0 -0
  332. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/annotation-apply.test.mjs +0 -0
  333. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/annotation-cli.test.mjs +0 -0
  334. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/annotation-placement.test.mjs +0 -0
  335. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/annotations.test.mjs +0 -0
  336. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/attribution.test.mjs +0 -0
  337. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/baseline.test.mjs +0 -0
  338. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/brief.test.mjs +0 -0
  339. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/calibration.test.mjs +0 -0
  340. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/claim-selection.test.mjs +0 -0
  341. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/claims-anchors.test.mjs +0 -0
  342. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/claims-since.test.mjs +0 -0
  343. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/classify.test.mjs +0 -0
  344. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/cli-unexpected-error.test.mjs +0 -0
  345. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/command-budget-write-boundaries.test.mjs +0 -0
  346. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/command-budget.test.mjs +0 -0
  347. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/contention.test.mjs +0 -0
  348. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/cost.test.mjs +0 -0
  349. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/discover.test.mjs +0 -0
  350. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/discovery-config.test.mjs +0 -0
  351. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/draft-admission.test.mjs +0 -0
  352. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/draft-append-apply.test.mjs +0 -0
  353. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/draft-append-cli.test.mjs +0 -0
  354. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/draft-append-open.test.mjs +0 -0
  355. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/draft-append-plan.test.mjs +0 -0
  356. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/draft-append-prepare.test.mjs +0 -0
  357. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/draft-append.test.mjs +0 -0
  358. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/evidence-persistence-signal.test.mjs +0 -0
  359. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/fault-defender-orchestration.test.mjs +0 -0
  360. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/fault-defenders.test.mjs +0 -0
  361. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/fix-input.test.mjs +0 -0
  362. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/fix-inventory.test.mjs +0 -0
  363. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/fix-nested.test.mjs +0 -0
  364. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/fix-paths.test.mjs +0 -0
  365. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/fix-projects.test.mjs +0 -0
  366. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/gate-ref.test.mjs +0 -0
  367. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/gate.test.mjs +0 -0
  368. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/gitlab-template.test.mjs +0 -0
  369. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/glob.test.mjs +0 -0
  370. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/helpers/gate-project.mjs +0 -0
  371. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/helpers/probe-assertions.mjs +0 -0
  372. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/helpers/release-project.mjs +0 -0
  373. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/imports.test.mjs +0 -0
  374. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/independence.test.mjs +0 -0
  375. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/init.test.mjs +0 -0
  376. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/inject.test.mjs +0 -0
  377. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/intent-authoring.test.mjs +0 -0
  378. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/intent-document.test.mjs +0 -0
  379. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/intent-input-cli.test.mjs +0 -0
  380. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/intent-input-plan.test.mjs +0 -0
  381. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/intent-input-request.test.mjs +0 -0
  382. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/intent-project.test.mjs +0 -0
  383. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/label.test.mjs +0 -0
  384. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/mcp-next.test.mjs +0 -0
  385. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/mcp-protocol.test.mjs +0 -0
  386. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/mcp.test.mjs +0 -0
  387. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/mocks.test.mjs +0 -0
  388. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/native-universe-hash.test.mjs +0 -0
  389. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/native-universe.test.mjs +0 -0
  390. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/negative-control-capabilities.test.mjs +0 -0
  391. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/negative-control.test.mjs +0 -0
  392. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/one-pager.test.mjs +0 -0
  393. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/origin-policy-admission.test.mjs +0 -0
  394. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/origin-policy-cli.test.mjs +0 -0
  395. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/origin-policy-projections.test.mjs +0 -0
  396. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/origin-policy-request.test.mjs +0 -0
  397. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/origin-policy.test.mjs +0 -0
  398. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/origin-reporting.test.mjs +0 -0
  399. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/origin-summary.test.mjs +0 -0
  400. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/policy-freshness.test.mjs +0 -0
  401. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-control.test.mjs +0 -0
  402. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-dirty-admission.test.mjs +0 -0
  403. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-dirty-paths.test.mjs +0 -0
  404. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-error-boundary.test.mjs +0 -0
  405. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-error.test.mjs +0 -0
  406. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-isolation.test.mjs +0 -0
  407. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-native-manifest.test.mjs +0 -0
  408. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-persistence-signal.test.mjs +0 -0
  409. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-preconditions.test.mjs +0 -0
  410. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe-records.test.mjs +0 -0
  411. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe.fixture.test.mjs +0 -0
  412. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe.jest.test.mjs +0 -0
  413. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe.playwright.test.mjs +0 -0
  414. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/probe.python.test.mjs +0 -0
  415. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/progress.test.mjs +0 -0
  416. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/pyimports.test.mjs +0 -0
  417. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/rank-aliases.test.mjs +0 -0
  418. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/release-approval.test.mjs +0 -0
  419. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/release-sync.test.mjs +0 -0
  420. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/release-tap.test.mjs +0 -0
  421. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/release-version.test.mjs +0 -0
  422. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/replay-mixed-runners.test.mjs +0 -0
  423. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/replay-no-test-file-left.test.mjs +0 -0
  424. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/replay-python-defenders.test.mjs +0 -0
  425. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/replay-selection.test.mjs +0 -0
  426. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/replay-verdict.test.mjs +0 -0
  427. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/replay.test.mjs +0 -0
  428. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/resource-safety.test.mjs +0 -0
  429. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/resource-timing.test.mjs +0 -0
  430. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-argv.test.mjs +0 -0
  431. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-command.test.mjs +0 -0
  432. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-discovery.test.mjs +0 -0
  433. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-output-bounds.test.mjs +0 -0
  434. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-playwright.test.mjs +0 -0
  435. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-project-resolution.test.mjs +0 -0
  436. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-python.test.mjs +0 -0
  437. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-report.test.mjs +0 -0
  438. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-selection-blind.test.mjs +0 -0
  439. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-vitest.test.mjs +0 -0
  440. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/runner-worker-argv.test.mjs +0 -0
  441. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/samples/playwright-report.json +0 -0
  442. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/samples/python-report-collect-error.json +0 -0
  443. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/samples/python-report.json +0 -0
  444. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/scaffold-admitted-source.test.mjs +0 -0
  445. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/scaffold-placeholder.test.mjs +0 -0
  446. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/scaffold.python.test.mjs +0 -0
  447. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/scaffold.test.mjs +0 -0
  448. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/snapshot.test.mjs +0 -0
  449. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/status.test.mjs +0 -0
  450. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/supply-concerns.test.mjs +0 -0
  451. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/supply-feedback.test.mjs +0 -0
  452. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/supply-persistence.test.mjs +0 -0
  453. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/supply-savepath.test.mjs +0 -0
  454. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/supply-select.test.mjs +0 -0
  455. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/surface.test.mjs +0 -0
  456. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/sweep-pool.test.mjs +0 -0
  457. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/sweep.fixture.test.mjs +0 -0
  458. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/sweep.test.mjs +0 -0
  459. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/vitest-config.test.mjs +0 -0
  460. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/worker-policy.test.mjs +0 -0
  461. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/worktree.test.mjs +0 -0
  462. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/test/writer.test.mjs +0 -0
  463. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/testguard.cost-budget.json +0 -0
  464. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/testguard_cli/__init__.py +0 -0
  465. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/testguard_cli/wrapper.py +0 -0
  466. {testguard_cli-0.17.0 → testguard_cli-0.18.1}/vitest.config.mjs +0 -0
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+ // Narrow, reproducible comparison: the same classifier inputs and four existing
3
+ // fault recipes, through real probe() calls with fresh workers and N=3.
4
+ // Run under each Node executable with no other test process active:
5
+ // node .github/scripts/benchmark-node-runner.mjs --out /absolute/receipt-dir
6
+ import assert from 'node:assert/strict';
7
+ import { execFileSync } from 'node:child_process';
8
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
9
+ import { createHash } from 'node:crypto';
10
+ import { dirname, join, resolve } from 'node:path';
11
+ import { tmpdir, platform, arch } from 'node:os';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { parseArgs } from 'node:util';
14
+ import { performance } from 'node:perf_hooks';
15
+ import { probe } from '../../src/probe/probe.mjs';
16
+ import { writeSpecDoc } from '../../src/evidence/writer.mjs';
17
+
18
+ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
19
+ const { values } = parseArgs({ options: { out: { type: 'string' }, pairs: { type: 'string', default: '2' } } });
20
+ assert(values.out, '--out is required; timings and proof receipts are retained there');
21
+ const pairs = Number(values.pairs);
22
+ assert(Number.isSafeInteger(pairs) && pairs >= 1 && pairs <= 10, '--pairs must be 1..10');
23
+ const out = resolve(values.out);
24
+ mkdirSync(out, { recursive: true });
25
+ const classifier = readFileSync(join(ROOT, 'src/probe/classify.mjs'), 'utf8');
26
+ const inventory = JSON.parse(readFileSync(join(ROOT, 'testguard.claims.json'), 'utf8'));
27
+ const ids = ['TG-KILL-NEEDS-N', 'TG-TIMEOUT-NEVER-KILLS', 'TG-COMPILE-ERROR-NAMED', 'TG-SURVIVOR-PROVES-THE-SUBJECT-RUNS'];
28
+ const claims = { ...inventory, claims: ids.map((id) => {
29
+ const claim = structuredClone(inventory.claims.find((c) => c.id === id));
30
+ assert(claim && claim.faults.length === 1, `expected one unchanged recipe for ${id}`);
31
+ claim.defendedBy = ['test/classify.test.mjs'];
32
+ return claim;
33
+ }) };
34
+ const pass = { outcome: 'pass', assertionFailures: 0, timeouts: 0 };
35
+ const kill = { outcome: 'fail', assertionFailures: 1, timeouts: 0 };
36
+ const timeout = { outcome: 'fail', assertionFailures: 0, timeouts: 1 };
37
+ const error = { outcome: 'error', assertionFailures: 0, timeouts: 0 };
38
+ const healthy = { defenders: ['test/classify.test.mjs'], anchor: { status: 'ok' }, baselineRuns: [pass, pass, pass], probeRuns: [pass, pass, pass], confirmRuns: 3 };
39
+ const rows = [
40
+ ['nocover precedes missing anchor', { defenders: [], anchor: { status: 'anchor-missing' } }, { verdict: 'nocover' }],
41
+ ['missing anchor', { anchor: { status: 'anchor-missing' } }, { verdict: 'unverifiable', reason: 'anchor-missing' }],
42
+ ['ambiguous anchor', { anchor: { status: 'anchor-ambiguous' } }, { verdict: 'unverifiable', reason: 'anchor-ambiguous' }],
43
+ ['failed defender load', { anchor: { status: 'defenders-failed-to-load' }, baselineRuns: [error] }, { verdict: 'unverifiable', reason: 'defenders-failed-to-load' }],
44
+ ['red baseline', { baselineRuns: [pass, kill] }, { verdict: 'flaky-defender', reason: 'defenders-not-green' }],
45
+ ['empty baseline', { baselineRuns: [] }, { verdict: 'flaky-defender', reason: 'defenders-not-green' }],
46
+ ['load failure', { probeRuns: [error] }, { verdict: 'fault-invalid', reason: 'suite-failed-to-load' }],
47
+ ['syntax error', { probeRuns: [{ ...error, loadMessage: 'Failed to parse source: invalid JS syntax' }] }, { verdict: 'fault-invalid', reason: 'replacement-does-not-compile' }],
48
+ ['esbuild syntax error', { probeRuns: [{ ...error, loadMessage: 'Transform failed with 1 error: Expected ")" but found ";"' }] }, { verdict: 'fault-invalid', reason: 'replacement-does-not-compile' }],
49
+ ['test timeout', { probeRuns: [timeout] }, { verdict: 'timeout', reason: 'test-timed-out' }],
50
+ ['budget timeout', { probeRuns: [{ ...timeout, outcome: 'timeout' }] }, { verdict: 'timeout', reason: 'test-timed-out' }],
51
+ ['confirmed kill', { probeRuns: [kill, kill, kill] }, { verdict: 'killed' }],
52
+ ['confirmed survivor', {}, { verdict: 'survived' }],
53
+ ['mixed confirmations', { probeRuns: [kill, pass, kill] }, { verdict: 'flaky-defender', reason: 'inconsistent-probe' }],
54
+ ['failure without assertions', { probeRuns: [{ outcome: 'fail', assertionFailures: 0, timeouts: 0 }, pass, pass] }, { verdict: 'flaky-defender', reason: 'inconsistent-probe' }],
55
+ ['unreached survivor', { subjectReached: false }, { verdict: 'unverifiable', reason: 'subject-not-executed' }],
56
+ ['reached survivor', { subjectReached: true }, { verdict: 'survived' }],
57
+ ];
58
+ const corpus = rows.map(([name, changes, expected]) => [name, { ...healthy, ...changes }, expected]);
59
+ const sha256 = (text) => createHash('sha256').update(text).digest('hex');
60
+ const toolSources = Object.fromEntries(['node-test.mjs', 'node/runner.mjs', 'node/entry.mjs', 'node/report.mjs', 'shared.mjs', 'discovery.mjs', 'lifecycle.mjs', 'vitest.mjs'].map((file) => [file, sha256(readFileSync(join(ROOT, 'src/probe/runners', file)))]));
61
+ const vitestVersion = JSON.parse(readFileSync(join(ROOT, 'node_modules/vitest/package.json'), 'utf8')).version;
62
+ const proofProjection = (evidence) => evidence.records.map((r) => ({
63
+ id: `${r.claim.id}/${r.subject.id}`, verdict: r.verdict, reason: r.detail.reason ?? null,
64
+ baseline: r.detail.baselineRuns.map(({ durationMs, ...run }) => run),
65
+ probe: r.detail.probeRuns.map(({ durationMs, ...run }) => run),
66
+ }));
67
+ const results = [];
68
+ async function measure(engine, label) {
69
+ const dir = mkdtempSync(join(tmpdir(), 'testguard-benchmark-'));
70
+ const receipts = join(out, `${label}-${engine}-workers.jsonl`);
71
+ const outcomes = join(out, `${label}-${engine}-outcomes.jsonl`);
72
+ writeFileSync(receipts, '', { mode: 0o600 });
73
+ writeFileSync(outcomes, '', { mode: 0o600 });
74
+ try {
75
+ mkdirSync(join(dir, 'src/probe'), { recursive: true }); mkdirSync(join(dir, 'test'));
76
+ writeFileSync(join(dir, 'src/probe/classify.mjs'), classifier);
77
+ writeFileSync(join(dir, 'package.json'), JSON.stringify({ private: true, type: 'module', ...(engine === 'vitest' ? { devDependencies: { vitest: vitestVersion } } : {}) }));
78
+ writeFileSync(join(dir, 'testguard.claims.json'), JSON.stringify(claims));
79
+ const imports = engine === 'vitest' ? "import {it,expect} from 'vitest';" : "import {test as it} from 'node:test'; import assert from 'node:assert/strict';";
80
+ const assertion = engine === 'vitest' ? 'expect(classify(input)).toEqual(expected)' : 'assert.deepStrictEqual(classify(input),expected)';
81
+ writeFileSync(join(dir, 'test/classify.test.mjs'), `${imports}
82
+ import {appendFileSync,readFileSync} from 'node:fs'; import {createHash} from 'node:crypto'; import {classify} from '../src/probe/classify.mjs';
83
+ const sourceHash=createHash('sha256').update(readFileSync(new URL('../src/probe/classify.mjs',import.meta.url))).digest('hex');
84
+ appendFileSync(${JSON.stringify(receipts)},JSON.stringify({pid:process.pid,execPath:process.execPath,version:process.versions.node,collect:process.env.TESTGUARD_NODE_COLLECT==='1'})+'\\n');
85
+ const record=(name,passed)=>appendFileSync(${JSON.stringify(outcomes)},JSON.stringify({pid:process.pid,sourceHash,name,passed})+'\\n');
86
+ for(const [name,input,expected] of ${JSON.stringify(corpus)}) it(name,()=>{try{${assertion};record(name,true);}catch(error){record(name,false);throw error;}});\n`);
87
+ if (engine === 'vitest') symlinkSync(join(ROOT, 'node_modules'), join(dir, 'node_modules'), 'junction');
88
+ writeFileSync(join(dir, '.gitignore'), 'node_modules/\n.testguard/\n');
89
+ for (const args of [['init', '-q'], ['add', '.'], ['-c', 'user.name=benchmark', '-c', 'user.email=benchmark@example.invalid', 'commit', '-qm', 'benchmark']]) execFileSync('git', args, { cwd: dir, timeout: 5000 });
90
+ const start = performance.now();
91
+ const evidence = await probe({ projectDir: dir, claims, runnerName: engine, confirmRuns: 3, budgetMs: 30_000, workers: 1, serial: true, escalate: false, toolVersion: 'benchmark' });
92
+ const wallMs = performance.now() - start;
93
+ writeSpecDoc('evidence', join(out, `${label}-${engine}-evidence.json`), evidence);
94
+ assert.equal(evidence.records.length, 4);
95
+ for (const r of evidence.records) {
96
+ assert.equal(r.verdict, 'killed', `${r.claim.id} must be caught by both frameworks`);
97
+ assert.equal(r.detail.baselineRuns.length, 3); assert.equal(r.detail.probeRuns.length, 3);
98
+ for (const run of r.detail.baselineRuns) { assert.equal(run.outcome, 'pass'); assert.equal(run.tests.total, corpus.length); }
99
+ for (const run of r.detail.probeRuns) { assert.equal(run.outcome, 'fail'); assert(run.assertionFailures > 0); assert.equal(run.tests.total, corpus.length); }
100
+ }
101
+ const workers = readFileSync(receipts, 'utf8').trim().split('\n').map(JSON.parse).filter((r) => !r.collect);
102
+ assert.equal(workers.length, 15, 'three shared baseline runs plus four faults times three confirmations');
103
+ assert.equal(new Set(workers.map((r) => r.pid)).size, 15, 'each confirmation must have a fresh worker');
104
+ for (const r of workers) { assert.equal(r.version, process.versions.node); assert.equal(r.execPath, process.execPath); }
105
+ const testResults = readFileSync(outcomes, 'utf8').trim().split('\n').map(JSON.parse);
106
+ assert.equal(testResults.length, 15 * corpus.length, 'every test must execute in every measured worker');
107
+ const bySource = new Map();
108
+ for (const worker of workers) {
109
+ const tests = testResults.filter((r) => r.pid === worker.pid);
110
+ assert.equal(tests.length, corpus.length); assert.equal(new Set(tests.map((r) => r.name)).size, corpus.length);
111
+ assert.equal(new Set(tests.map((r) => r.sourceHash)).size, 1);
112
+ const { sourceHash } = tests[0]; const projection = tests.map(({ name, passed }) => ({ name, passed }));
113
+ const prior = bySource.get(sourceHash);
114
+ if (prior) { assert.deepStrictEqual(prior.tests, projection, 'confirmations must agree per test'); prior.confirmations++; }
115
+ else bySource.set(sourceHash, { sourceHash, confirmations: 1, tests: projection });
116
+ }
117
+ assert.equal(bySource.size, 5, 'healthy source and four distinct mutations');
118
+ for (const group of bySource.values()) assert.equal(group.confirmations, 3);
119
+ assert.equal(execFileSync('git', ['status', '--porcelain'], { cwd: dir, encoding: 'utf8' }), '', 'probe must restore the measured tree');
120
+ const runnerMs = evidence.records.flatMap((r, i) => [...(i === 0 ? r.detail.baselineRuns : []), ...r.detail.probeRuns]).reduce((sum, r) => sum + r.durationMs, 0);
121
+ const result = { label, engine, wallMs, runnerMs, workerCount: workers.length, projection: proofProjection(evidence), testProjection: [...bySource.values()].sort((a,b) => a.sourceHash.localeCompare(b.sourceHash)) };
122
+ console.log(JSON.stringify({ label, engine, wallMs: Math.round(wallMs), runnerMs }));
123
+ return result;
124
+ } finally { rmSync(dir, { recursive: true, force: true }); }
125
+ }
126
+ // One unmeasured warm-up per engine, then reversed-order pairs. No parallel
127
+ // execution, evidence reuse or reduction in confirmations contributes to speed.
128
+ await measure('vitest', 'warmup'); await measure('node-test', 'warmup');
129
+ for (let i = 0; i < pairs; i++) {
130
+ const pair = [];
131
+ for (const engine of i % 2 ? ['node-test', 'vitest'] : ['vitest', 'node-test']) pair.push(await measure(engine, `pair-${i + 1}`));
132
+ assert.deepStrictEqual(pair[0].projection, pair[1].projection, 'every verdict and run count must agree');
133
+ assert.deepStrictEqual(pair[0].testProjection, pair[1].testProjection, 'every baseline, failed test identity and passed test must agree');
134
+ results.push(...pair);
135
+ }
136
+ const median = (numbers) => { const sorted = [...numbers].sort((a,b) => a-b); const middle = Math.floor(sorted.length/2); return sorted.length%2 ? sorted[middle] : (sorted[middle-1]+sorted[middle])/2; };
137
+ const medians = Object.fromEntries(['vitest', 'node-test'].map((engine) => [engine, median(results.filter((r) => r.engine === engine).map((r) => r.wallMs))]));
138
+ const receipt = { scope: 'One classifier file, 17 identical inputs, four unchanged fault recipes, N=3; no generic framework compatibility claim.', generatedAt: new Date().toISOString(), runtime: { version: process.versions.node, execPath: process.execPath, platform: platform(), arch: arch(), vitestVersion }, classifierHash: sha256(classifier), corpusHash: sha256(JSON.stringify(corpus)), claimsHash: sha256(JSON.stringify(claims)), toolSources, pairs, medians, speedup: medians.vitest/medians['node-test'], results };
139
+ writeFileSync(join(out, 'summary.json'), JSON.stringify(receipt, null, 2)+'\n');
140
+ console.log(JSON.stringify({ medians, speedup: receipt.speedup }));
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ // No test-framework dependencies: this also verifies the exact Node20.0.0 floor.
3
+ import assert from 'node:assert/strict';
4
+ import { cpSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
5
+ import { execFileSync, spawnSync } from 'node:child_process';
6
+ import { tmpdir } from 'node:os';
7
+ import { dirname, join, resolve } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { readSpecDoc } from '../../src/evidence/writer.mjs';
10
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
11
+ const scratch = mkdtempSync(join(tmpdir(), 'tg-native-oracle-'));
12
+ try {
13
+ cpSync(join(root, 'fixtures/known-answer-node'), scratch, { recursive: true });
14
+ for (const args of [['init', '-q'], ['add', '.'], ['-c', 'user.name=fixture', '-c', 'user.email=fixture@example.invalid', 'commit', '-qm', 'fixture']]) execFileSync('git', args, { cwd: scratch, timeout: 5000 });
15
+ const result = spawnSync(process.execPath, [join(root, 'cli/testguard.mjs'), 'probe', scratch, '--runner', 'node-test', '--budget', '10000', '--quiet'], { encoding: 'utf8', timeout: 60_000 });
16
+ assert.equal(result.status, 1, `${result.error ?? ''}\n${result.stderr}\n${result.stdout}`);
17
+ const evidence = readSpecDoc('evidence', join(scratch, '.testguard/evidence.json'));
18
+ const expected = JSON.parse(readFileSync(join(root, 'fixtures/known-answer-node/expected.json'), 'utf8')).expected;
19
+ assert.deepStrictEqual(evidence.run.runner, { name: 'node-test', source: 'builtin', version: process.versions.node });
20
+ assert.equal(evidence.records.length, Object.keys(expected).length);
21
+ for (const record of evidence.records) {
22
+ const oracle = expected[`${record.claim.id}/${record.subject.id}`];
23
+ assert(oracle); assert.equal(record.verdict, oracle.verdict);
24
+ if (oracle.reason) assert.equal(record.detail.reason, oracle.reason);
25
+ }
26
+ assert.equal(execFileSync('git', ['status', '--porcelain'], { cwd: scratch, encoding: 'utf8' }), '');
27
+ console.log(`native Node ${process.versions.node}: all ${evidence.records.length} independently verified verdicts match; targets restored`);
28
+ } finally { rmSync(scratch, { recursive: true, force: true }); }
@@ -30,6 +30,19 @@ try {
30
30
 
31
31
  const bin = join(consumer, 'node_modules', '.bin', process.platform === 'win32' ? 'testguard.cmd' : 'testguard');
32
32
  const version = run(bin, ['--version'], consumer).trim();
33
+ const nativeProject = join(scratch, 'native-node');
34
+ mkdirSync(join(nativeProject, 'test'), { recursive: true });
35
+ writeFileSync(join(nativeProject, 'package.json'), '{"type":"module"}');
36
+ writeFileSync(join(nativeProject, 'test', 'native.test.mjs'), "import {test} from 'node:test';import assert from 'node:assert/strict';test('installed native runner',()=>assert.equal(2+2,4));");
37
+ const installedNodeRunner = join(consumer, 'node_modules', 'testguard-cli', 'src', 'probe', 'runners', 'node-test.mjs');
38
+ run(process.execPath, ['--input-type=module', '-e',
39
+ `import {pathToFileURL} from 'node:url'; const {discoverTests,run}=await import(pathToFileURL(process.argv[1]));
40
+ const projectDir=process.argv[2];const manifest=await discoverTests({projectDir,version:process.versions.node});
41
+ if(JSON.stringify(manifest.files)!==JSON.stringify(['test/native.test.mjs']))throw new Error('installed native entry discovery failed');
42
+ const result=await run({projectDir,files:manifest.files,budgetMs:5000});
43
+ if(result.run.outcome!=='pass'||result.run.tests.passed!==1)throw new Error('production-only native Node runner failed');`,
44
+ installedNodeRunner, nativeProject,
45
+ ], consumer);
33
46
  const emptyProject = join(scratch, 'empty-adoption');
34
47
  mkdirSync(emptyProject);
35
48
  writeFileSync(join(emptyProject, 'testguard.claims.json'), JSON.stringify({ schemaVersion: 1, claims: [] }));
@@ -122,6 +122,9 @@ jobs:
122
122
  console.log('jest fixture verdicts match expected.json:', ev.records.length);
123
123
  "
124
124
 
125
+ - name: probe the native Node fixture without test dependencies
126
+ run: node .github/scripts/check-node-fixture.mjs
127
+
125
128
  # ── The MCP server, driven as a client would: handshake, list, call.
126
129
  # A harness-independent surface is only worth having if it actually
127
130
  # speaks the protocol from a cold start. ──
@@ -205,7 +208,7 @@ jobs:
205
208
  # a survivor here is a failure either way. ──
206
209
  - name: Restore previous self-probe evidence (verdict reuse)
207
210
  if: matrix.node-version == 22
208
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
211
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
209
212
  with:
210
213
  path: .testguard/ci-self-evidence.json
211
214
  key: self-evidence-${{ hashFiles('testguard.claims.json') }}-${{ github.sha }}
@@ -244,6 +247,20 @@ jobs:
244
247
  path: .testguard/ci-self-evidence.json
245
248
  if-no-files-found: warn
246
249
 
250
+ native-node-floor:
251
+ runs-on: ubuntu-latest
252
+ steps:
253
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
254
+ with:
255
+ fetch-depth: 0
256
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
257
+ with:
258
+ node-version: '20.0.0'
259
+ - name: Install production dependencies only
260
+ run: npm ci --omit=dev
261
+ - name: Verify the native Node floor against the independent oracle
262
+ run: node .github/scripts/check-node-fixture.mjs
263
+
247
264
  # ── Python: two engines, one oracle. The stdlib leg runs FIRST and with no
248
265
  # pip install of any kind, because "a project with no test dependencies
249
266
  # still probes" is a claim about the tool and the only way to keep it true
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.18.1] - 2026-10-01
11
+
12
+ Automated weekly release — everything merged since `v0.18.0`.
13
+
14
+ ### Changed
15
+
16
+ - test: consolidate native runtime acceptance checks
17
+ - perf: isolate native admission mutation defenders
18
+ - perf: size bounded report buffers to observed files
19
+ - chore(homebrew): sha256 for v0.18.0 (#194)
20
+
21
+
22
+ ### Changed
23
+
24
+ - Runner report and discovery-config reads reserve memory for the observed
25
+ file size instead of the maximum allowed size, retaining the hard byte
26
+ ceiling and checking file growth before parsing.
27
+ - Native-runner admission self-probes use focused defenders, preserving the
28
+ fault recipes and full runner acceptance coverage while avoiding unrelated
29
+ timeout and process-cleanup checks for each admission confirmation.
30
+
31
+ ## [0.18.0] - 2026-10-01
32
+
33
+ Automated weekly release — everything merged since `v0.17.0`.
34
+
35
+ ### Changed
36
+
37
+ - Merge branch 'main' into dependabot/github_actions/all-actions-d5b8684899
38
+ - feat: native Node probes with measured startup savings (#192)
39
+ - chore(homebrew): sha256 for v0.17.0 (#191)
40
+ - chore(deps): bump actions/cache
41
+
42
+
43
+ ### Added
44
+
45
+ - Explicit `--runner node-test` support for plain JavaScript projects using
46
+ Node's built-in test framework, with native entry-file discovery, fresh
47
+ process confirmations and bounded structured reports. No runner is installed
48
+ and existing automatic runner selection is unchanged.
49
+
10
50
  ## [0.17.0] - 2026-10-01
11
51
 
12
52
  Automated weekly release — everything merged since `v0.16.0`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: testguard-cli
3
- Version: 0.17.0
3
+ Version: 0.18.1
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.17.0` — see [`action.yml`](./action.yml) |
154
+ | GitHub Action | `uses: raccioly/testguard@v0.18.1` — 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.17.0/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
156
+ | GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/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
@@ -326,7 +326,23 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
326
326
  version, its own bin script — and only then from an executable on PATH,
327
327
  which the evidence records as `runner.source: "path"`; `npx` is never
328
328
  asked, because its cache answers for packages a project does not have.
329
- Anything else goes through `--runner-cmd`. Each runner is proven against
329
+ **Node's built-in framework** uses explicit `--runner node-test` with plain
330
+ JavaScript test files and default Node collection. It uses the Node executable
331
+ running TestGuard, records `runner.source: "builtin"` and its actual version,
332
+ and installs nothing. Discovery loads test modules with test bodies filtered
333
+ out, so top-level code can run. Custom loaders, `NODE_OPTIONS`, `NODE_PATH`
334
+ and module-mocking configurations are outside this initial adapter's scope.
335
+ `auto` keeps its existing runner order. Each confirmation uses fresh isolated
336
+ processes and the same worker, timeout and confirmation rules.
337
+
338
+ To reproduce the narrow native-runner comparison, run
339
+ `node .github/scripts/benchmark-node-runner.mjs --out /absolute/receipt-dir`
340
+ from this repository after installing its pinned development dependencies.
341
+ It compares 17 identical classifier inputs and four existing faults through
342
+ complete probes, with three confirmations, fresh worker receipts and
343
+ reversed execution order. Results describe that workload and runtime only.
344
+
345
+ Other frameworks go through `--runner-cmd`. Each runner is proven against
330
346
  its own copy of the known-answer fixture.
331
347
 
332
348
  **Python** runs under `pytest` when the project's interpreter can import
@@ -793,12 +809,12 @@ detected base that does not resolve is a warning for `status` and `brief`
793
809
  # GitHub Actions
794
810
  - uses: actions/checkout@v4
795
811
  with: { fetch-depth: 0 }
796
- - uses: raccioly/testguard@v0.17.0
812
+ - uses: raccioly/testguard@v0.18.1
797
813
  with: { command: gate }
798
814
 
799
815
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
800
816
  include:
801
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.17.0/packaging/gitlab/testguard.gitlab-ci.yml'
817
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/packaging/gitlab/testguard.gitlab-ci.yml'
802
818
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
803
819
  ```
804
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.17.0` — see [`action.yml`](./action.yml) |
130
+ | GitHub Action | `uses: raccioly/testguard@v0.18.1` — 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.17.0/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
132
+ | GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/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
@@ -302,7 +302,23 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
302
302
  version, its own bin script — and only then from an executable on PATH,
303
303
  which the evidence records as `runner.source: "path"`; `npx` is never
304
304
  asked, because its cache answers for packages a project does not have.
305
- Anything else goes through `--runner-cmd`. Each runner is proven against
305
+ **Node's built-in framework** uses explicit `--runner node-test` with plain
306
+ JavaScript test files and default Node collection. It uses the Node executable
307
+ running TestGuard, records `runner.source: "builtin"` and its actual version,
308
+ and installs nothing. Discovery loads test modules with test bodies filtered
309
+ out, so top-level code can run. Custom loaders, `NODE_OPTIONS`, `NODE_PATH`
310
+ and module-mocking configurations are outside this initial adapter's scope.
311
+ `auto` keeps its existing runner order. Each confirmation uses fresh isolated
312
+ processes and the same worker, timeout and confirmation rules.
313
+
314
+ To reproduce the narrow native-runner comparison, run
315
+ `node .github/scripts/benchmark-node-runner.mjs --out /absolute/receipt-dir`
316
+ from this repository after installing its pinned development dependencies.
317
+ It compares 17 identical classifier inputs and four existing faults through
318
+ complete probes, with three confirmations, fresh worker receipts and
319
+ reversed execution order. Results describe that workload and runtime only.
320
+
321
+ Other frameworks go through `--runner-cmd`. Each runner is proven against
306
322
  its own copy of the known-answer fixture.
307
323
 
308
324
  **Python** runs under `pytest` when the project's interpreter can import
@@ -769,12 +785,12 @@ detected base that does not resolve is a warning for `status` and `brief`
769
785
  # GitHub Actions
770
786
  - uses: actions/checkout@v4
771
787
  with: { fetch-depth: 0 }
772
- - uses: raccioly/testguard@v0.17.0
788
+ - uses: raccioly/testguard@v0.18.1
773
789
  with: { command: gate }
774
790
 
775
791
  # GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
776
792
  include:
777
- - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.17.0/packaging/gitlab/testguard.gitlab-ci.yml'
793
+ - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/packaging/gitlab/testguard.gitlab-ci.yml'
778
794
  inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
779
795
  ```
780
796
 
@@ -50,7 +50,7 @@ inputs:
50
50
  version:
51
51
  description: 'testguard-cli version to run'
52
52
  required: false
53
- default: '0.17.0'
53
+ default: '0.18.1'
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.17.0 · MIT</div>
147
+ <div class="meta">Technical Brief · v0.18.1 · 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 (project runners); Playwright (per file) | They share a JSON report shape; Playwright is selected per file, not per project |
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 |
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 |
@@ -21,7 +21,7 @@
21
21
  | FR-07 | Report where the suite is blind to an agent before it writes code (`brief`, `status`, MCP) | P1 |
22
22
  | FR-08 | Fail when a changed source file carries no claim and no excusing ignore entry (`gate --changed`) | P1 |
23
23
  | FR-09 | Exclude tests that mock the subject from defender discovery, and report them | P1 |
24
- | FR-10 | Support vitest and jest as project runners, and Playwright per file | P2 |
24
+ | FR-10 | Support vitest, jest and explicit native Node as project runners, plus Playwright and Python per file; native Node initially admits default JavaScript collection without custom loaders | P2 |
25
25
  | FR-11 | Propose faults mechanically for a source file, as a draft a human keeps or drops (`scaffold`) | P2 |
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 |
@@ -47,7 +47,7 @@
47
47
 
48
48
  | Criterion | Target | Status at v0.6.0 |
49
49
  |---|---|---|
50
- | Reproduces a known-answer oracle for every verdict, per runner | 3 runners | met — vitest, jest, Playwright fixtures |
50
+ | Reproduces a known-answer oracle for every verdict, per runner | Every supported engine | vitest, jest, Playwright, Python and native Node fixtures; native Node expected outcomes verified through direct Node CLI runs |
51
51
  | Self-verification gates the release | every fault killed | met — 62 faults, all killed |
52
52
  | Finds real blind spots in real code | field evidence, not synthetic | met — two independent field reports on AI-authored codebases; 8 of 9 historical bugs invisible to a 4,900-test suite; 21 of 39 faults survived a green suite on a second codebase |
53
53
  | Every field-report defect closed | 11 of 11 | met at v0.6.0 |
@@ -0,0 +1,3 @@
1
+ .baseline-counter
2
+ .probe-counter
3
+ .testguard/
@@ -0,0 +1,15 @@
1
+ # Native Node known-answer fixture
2
+
3
+ This neutral project uses only Node test/assert APIs. The intended guarantees
4
+ are recorded in its claims document. The audit defender deliberately asserts
5
+ only the row kind, so it misses exposed content. The unused module has no
6
+ importing defender. The two counter tests deliberately model an unstable
7
+ baseline and a mixed response to the fault. The unused module declared against
8
+ the policy defender tests the negative control: that test never imports it.
9
+
10
+ Do not run these intentionally faulty/flaky cases as a normal project suite.
11
+ Before accepting expected.json, apply each exact recipe manually in a scratch
12
+ copy, run its real declared defenders with the Node test CLI, inspect baseline,
13
+ fault and restored results, and record the interpretation independently of
14
+ TestGuard. A timeout, syntax error and incomplete confirmation are unproven.
15
+ The fixture requires no installed package or network access.
@@ -0,0 +1,51 @@
1
+ {
2
+ "confirmRuns": 3,
3
+ "expected": {
4
+ "NODE-ALLOW/F1": {
5
+ "verdict": "killed",
6
+ "why": "Manually applied fault: public/private assertions fail in all three native CLI runs; baseline/restored run green three times."
7
+ },
8
+ "NODE-AUDIT/F1": {
9
+ "verdict": "survived",
10
+ "why": "Manually applied exposed content: kind-only assertion remains green in all three native runs."
11
+ },
12
+ "NODE-READY/F1": {
13
+ "verdict": "timeout",
14
+ "why": "Native CLI explicitly reports testTimeoutFailure in all three faulted runs, not assertion rejection."
15
+ },
16
+ "NODE-UNUSED/F1": {
17
+ "verdict": "nocover",
18
+ "why": "No native test entry imports the unused module; no defender is declared."
19
+ },
20
+ "NODE-ANCHOR/F1": {
21
+ "verdict": "unverifiable",
22
+ "reason": "anchor-missing",
23
+ "why": "Exact find has zero occurrences in independently inspected source."
24
+ },
25
+ "NODE-ANCHOR/F2": {
26
+ "verdict": "unverifiable",
27
+ "reason": "anchor-ambiguous",
28
+ "why": "Exact return true anchor occurs twice and expects one hit."
29
+ },
30
+ "NODE-SYNTAX/F1": {
31
+ "verdict": "fault-invalid",
32
+ "reason": "replacement-does-not-compile",
33
+ "why": "Manual replacement makes native CLI fail while importing, before test-body assertions."
34
+ },
35
+ "NODE-FLAKY/F1": {
36
+ "verdict": "flaky-defender",
37
+ "reason": "defenders-not-green",
38
+ "why": "Unmodified native baseline exits 1,0,1; fault detection cannot be confirmed on that baseline."
39
+ },
40
+ "NODE-MIXED/F1": {
41
+ "verdict": "flaky-defender",
42
+ "reason": "inconsistent-probe",
43
+ "why": "Three healthy native baselines pass; faulted native runs exit 1,0,1."
44
+ },
45
+ "NODE-REACH/F1": {
46
+ "verdict": "unverifiable",
47
+ "reason": "subject-not-executed",
48
+ "why": "Manual fatal syntax in the unused target still leaves all three declared-defender runs green."
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "testguard-known-answer-node",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "node --test"
8
+ }
9
+ }
@@ -0,0 +1,15 @@
1
+ export function allowed(kind) {
2
+ return kind === 'public';
3
+ }
4
+ export function audit(row) {
5
+ return { kind: row.kind, content: null };
6
+ }
7
+ export async function ready() {
8
+ return true;
9
+ }
10
+ export function duplicateAnchor() {
11
+ return true;
12
+ }
13
+ export function value() {
14
+ return 1;
15
+ }
@@ -0,0 +1 @@
1
+ export const unused = 7;
@@ -0,0 +1,11 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
4
+ import { value } from '../src/policy.mjs';
5
+ test('deliberately flaky baseline', () => {
6
+ const file = '.baseline-counter';
7
+ const next = (existsSync(file) ? Number(readFileSync(file, 'utf8')) : 0) + 1;
8
+ writeFileSync(file, String(next));
9
+ assert.equal(next % 2, 0);
10
+ assert.equal(value(), 1);
11
+ });
@@ -0,0 +1,11 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
4
+ import { value } from '../src/policy.mjs';
5
+ test('deliberately mixed fault response', () => {
6
+ if (value() === 1) { assert.equal(value(), 1); return; }
7
+ const file = '.probe-counter';
8
+ const next = (existsSync(file) ? Number(readFileSync(file, 'utf8')) : 0) + 1;
9
+ writeFileSync(file, String(next));
10
+ assert.equal(next % 2, 0);
11
+ });
@@ -0,0 +1,15 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { allowed, audit, ready } from '../src/policy.mjs';
4
+ test('only the public kind is allowed', () => {
5
+ assert.equal(allowed('public'), true);
6
+ assert.equal(allowed('private'), false);
7
+ });
8
+ test('the audit row has its kind', () => {
9
+ assert.equal(audit({ kind: 'public', content: 'private payload' }).kind, 'public');
10
+ });
11
+ test('readiness resolves', { timeout: 100 }, async (t) => {
12
+ const hold = setInterval(() => {}, 1000);
13
+ t.signal.addEventListener('abort', () => clearInterval(hold), { once: true });
14
+ try { assert.equal(await ready(), true); } finally { clearInterval(hold); }
15
+ });