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