testguard-cli 0.11.0__tar.gz → 0.13.0__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.11.0 → testguard_cli-0.13.0}/.docguard.json +2 -1
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/CHANGELOG.md +108 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/PKG-INFO +142 -5
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/README.md +141 -4
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/action.yml +1 -1
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs/testguard-explained.html +172 -11
- testguard_cli-0.13.0/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/ARCHITECTURE.md +2 -1
- testguard_cli-0.13.0/docs-canonical/PRIOR-ART.md +160 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/package-lock.json +2 -2
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/package.json +1 -1
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/pyproject.toml +1 -1
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/lib/validate.mjs +49 -1
- testguard_cli-0.13.0/spec/schemas/concerns.schema.json +41 -0
- testguard_cli-0.13.0/spec/schemas/sweep.schema.json +366 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/cli.mjs +12 -2
- testguard_cli-0.13.0/src/commands/concerns.mjs +32 -0
- testguard_cli-0.13.0/src/commands/sweep.mjs +109 -0
- testguard_cli-0.13.0/src/supply/concerns.mjs +162 -0
- testguard_cli-0.13.0/src/supply/feedback.mjs +146 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/supply/persistence.mjs +53 -0
- testguard_cli-0.13.0/src/supply/savepath.mjs +216 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/sweep/sweep.mjs +111 -7
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/util/glob.mjs +8 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/glob.test.mjs +5 -2
- testguard_cli-0.13.0/test/supply-concerns.test.mjs +124 -0
- testguard_cli-0.13.0/test/supply-feedback.test.mjs +134 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/supply-persistence.test.mjs +41 -1
- testguard_cli-0.13.0/test/supply-savepath.test.mjs +165 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/sweep.test.mjs +68 -3
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard.claims.json +229 -1
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard.cost-budget.json +1 -1
- testguard_cli-0.11.0/docs/testguard-explained.pdf +0 -0
- testguard_cli-0.11.0/spec/schemas/sweep.schema.json +0 -127
- testguard_cli-0.11.0/src/commands/sweep.mjs +0 -74
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.codex/hooks.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.docguardignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.gitattributes +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/dependabot.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/approve-held-runs.sh +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/publish-homebrew-tap.sh +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/release.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/scheduled-release.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.npmignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.npmrc +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/AGENTS.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/CLAUDE.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/CONTRIBUTING.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/LICENSE +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/PRIVACY.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/SECURITY.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/SUPPORT.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/bench/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/cli/testguard.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/ENVIRONMENT.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/REQUIREMENTS.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/SECURITY.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/docs-canonical/TEST-SPEC.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/GATE-SEMANTICS.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/evidence.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/replay.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/claims/anchors.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/claims/load.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/baseline.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/brief.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/gate.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/init.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/probe.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/commands/status.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/gate/changed.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/git.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/init/init.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/init/templates/SKILL.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/mcp/tools.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/contention.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/probe.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/pyimports.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/index.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/render.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/replay/label.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/replay/replay.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/scaffold/producers.python.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/status/status.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/status/surface.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/supply/select.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/src/util/hash.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/admit.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/brief.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/calibration.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/claim-selection.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/claims-anchors.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/classify.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/contention.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/cost.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/discover.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/gate.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/independence.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/init.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/inject.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/label.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/one-pager.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe-error.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/progress.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/pyimports.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/release-approval.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/release-sync.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/replay-verdict.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/replay.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/samples/python-report.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/scaffold.python.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/status.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/supply-select.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/surface.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/sweep.fixture.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/vitest-config.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/test/writer.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard.ignore.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.13.0}/vitest.config.mjs +0 -0
|
@@ -7,6 +7,114 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.13.0] - 2026-09-22
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.12.0`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- chore(homebrew): sha256 for v0.12.0 (#159)
|
|
17
|
+
- feat: persistence provability, and an ordering that learns from this project's own verdicts (#157)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## [0.12.0] - 2026-09-22
|
|
21
|
+
|
|
22
|
+
Automated weekly release — everything merged since `v0.11.0`.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- feat(sweep): --save-paths, and the denominator a save report is read against (#156)
|
|
27
|
+
- docs: make the brief describe what the tool actually does, and record the prior art (#155)
|
|
28
|
+
- chore(homebrew): sha256 for v0.11.0 (#153)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **Concerns** — one sentence a human writes that generates faults across a
|
|
34
|
+
whole surface, the rung between writing nothing and writing a claim per
|
|
35
|
+
behaviour. A concern says *where* to look and *which shapes of break* are
|
|
36
|
+
relevant: `"every write persists the data it was given"` targets the write
|
|
37
|
+
sites and applies the payload producers. `testguard concerns` lists them,
|
|
38
|
+
`sweep --concern <ID>` aims a sweep by one, and `--save-paths` is now sugar
|
|
39
|
+
for the built-in `SAVE-PERSISTS`.
|
|
40
|
+
- Two built-ins ship, and only two: an authorization concern would need a
|
|
41
|
+
heuristic for "which files check permissions", and a guess there is the
|
|
42
|
+
non-actionable noise that gets a check switched off. A project that knows
|
|
43
|
+
its own auth layer says so with a `glob` target in three lines.
|
|
44
|
+
- A project concern with the same id **replaces** a built-in, so a team can
|
|
45
|
+
retune `SAVE-PERSISTS` for an ORM this tool does not recognise without
|
|
46
|
+
forking anything. The replacement is reported, never silent.
|
|
47
|
+
- `urn:claimspec:v1:concerns`, with rules that refuse a glob target naming no
|
|
48
|
+
globs (it would match nothing and report clean) and an empty fault-class
|
|
49
|
+
list (a typo for "every class", every time).
|
|
50
|
+
- **No model is required, deliberately.** A concern's useful core is a named
|
|
51
|
+
scope plus a producer selection, which the mechanical producers already
|
|
52
|
+
satisfy — so every verifying command stays offline and NFR-01 is unchanged.
|
|
53
|
+
An LLM can later propose faults a line-oriented producer cannot see, as an
|
|
54
|
+
additional source for a concern that already works.
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
|
|
58
|
+
- A save-path sweep now reports **what its defenders could prove**, before it
|
|
59
|
+
reports what they did: per file, whether every defender mocks the persistence
|
|
60
|
+
layer (call shape only), at least one does not (a real write is possible), or
|
|
61
|
+
there is no defender at all. When nothing is unmocked the report says so
|
|
62
|
+
outright — a clean probe there is not evidence of persistence, it is evidence
|
|
63
|
+
that nothing could have measured it. Measured on a real AI-authored
|
|
64
|
+
application: of 37 files that write to storage, 32 were defended only by
|
|
65
|
+
mocking tests.
|
|
66
|
+
- `scope.provability` on the sweep document, with a validator rule that every
|
|
67
|
+
target is classified exactly once.
|
|
68
|
+
- The candidate ordering now **learns from this project's own probed verdicts**
|
|
69
|
+
instead of only the shipped productivity prior. Every evidence record already
|
|
70
|
+
carries a `faultClass` and a `verdict`, so "how often does a fault of this
|
|
71
|
+
class survive *here*" is a tally rather than a new thing to capture. A sweep
|
|
72
|
+
persists its own evidence to `.testguard/sweep-evidence.json` — beside the
|
|
73
|
+
canonical one, never replacing it — so the next run learns from
|
|
74
|
+
machine-proposed faults, the same distribution the ranker orders. The sweep
|
|
75
|
+
document records `ordering.observed` and `ordering.sources`, because an
|
|
76
|
+
ordering nobody can trace is a number nobody should trust.
|
|
77
|
+
|
|
78
|
+
### Added
|
|
79
|
+
|
|
80
|
+
- `sweep --save-paths` — sweep every file that writes to storage instead of the
|
|
81
|
+
diff, and report the surface as the denominator the findings are read
|
|
82
|
+
against. A diff cannot answer how many save paths exist, and a clean report
|
|
83
|
+
over an unstated denominator is a sample of unknown size. The sweep document
|
|
84
|
+
gains `scope.mode`, `scope.writeSites` and `scope.payloadFields`, and the
|
|
85
|
+
validator refuses a save-paths sweep that omits its surface.
|
|
86
|
+
- `src/supply/savepath.mjs` — the write-site enumerator: which calls put data
|
|
87
|
+
into storage, and which payload keys sit on their own line (the ones a
|
|
88
|
+
`field-dropped` fault can anchor to). Measured on a real AI-authored
|
|
89
|
+
application: 119 writes across 37 files carrying 226 payload fields.
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
|
|
93
|
+
- `walk()` skipped `dist` and `coverage` but not `.next`, `build`, `out`,
|
|
94
|
+
`.svelte-kit`, `.turbo` and their siblings, while the claim above it already
|
|
95
|
+
promised that a build's test files cannot be picked up. A scan of a real
|
|
96
|
+
Next.js application counted 104 files instead of 32, because `.next/` carries
|
|
97
|
+
compiled copies of the application — its writes and its tests. The claim was
|
|
98
|
+
right; the list was short.
|
|
99
|
+
|
|
100
|
+
### Added
|
|
101
|
+
|
|
102
|
+
- `docs-canonical/PRIOR-ART.md` — the canonical record of what was taken from
|
|
103
|
+
Google's and Meta's mutation-testing programmes, what was deliberately not
|
|
104
|
+
taken, the measured reason no model decides a verdict, and the known gaps in
|
|
105
|
+
what was borrowed. Registered with DocGuard.
|
|
106
|
+
- README prior-art section pointing at it.
|
|
107
|
+
|
|
108
|
+
### Changed
|
|
109
|
+
|
|
110
|
+
- The technical brief now describes what the tool actually does. A new
|
|
111
|
+
*Starting from zero* page carries Field report C (the persistence-path
|
|
112
|
+
measurement: 12 of 26 faults survived a green suite, all of them a dropped
|
|
113
|
+
payload field) and the claim-supply ladder; prior art names Google and Meta
|
|
114
|
+
and what each solved; the decision-model result is stated as a measurement
|
|
115
|
+
rather than a design assertion; and a *what it deliberately is not* section
|
|
116
|
+
closes it. `sweep` added to the verb table. Six pages to eight, re-rendered.
|
|
117
|
+
|
|
10
118
|
## [0.11.0] - 2026-09-21
|
|
11
119
|
|
|
12
120
|
Automated weekly release — everything merged since `v0.10.4`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
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.
|
|
154
|
+
| GitHub Action | `uses: raccioly/testguard@v0.13.0` — 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.
|
|
156
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.13.0/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
|
|
@@ -475,6 +475,14 @@ Three rules make it safe to run on a repository that has never seen this tool:
|
|
|
475
475
|
is reported and never gates — a tool that fails because its own guess was bad
|
|
476
476
|
is a tool people switch off.
|
|
477
477
|
|
|
478
|
+
The ordering **learns from your own runs**. Every evidence record already
|
|
479
|
+
carries a fault class and a verdict, so "how often does a fault of this class
|
|
480
|
+
survive here" is a tally, not a new thing to collect — a sweep persists its
|
|
481
|
+
evidence and the next one reads it. A project with no evidence falls back to
|
|
482
|
+
the shipped prior and says so; one with its own records overrides it in
|
|
483
|
+
proportion to how many it has. The report names which, because an ordering
|
|
484
|
+
nobody can trace is a number nobody should trust.
|
|
485
|
+
|
|
478
486
|
The cap and the ordering are Google's: their mutation service surfaces at most
|
|
479
487
|
7 × |files| mutants per change and orders candidates on the measured
|
|
480
488
|
productivity of their operator in similar context, which took their productive
|
|
@@ -490,6 +498,109 @@ A sweep is weaker evidence than a probe, and says so. Nobody stated that the
|
|
|
490
498
|
behaviour mattered. It is stronger than nothing, which is what a repository
|
|
491
499
|
with no claims has — and the survivors worth defending become the first claims.
|
|
492
500
|
|
|
501
|
+
#### Concerns: one sentence, two hundred screens
|
|
502
|
+
|
|
503
|
+
A claim names one promise precisely, which is why a repository with two hundred
|
|
504
|
+
screens never finishes writing them. A **concern** names a *kind* of promise and
|
|
505
|
+
says where to look for it:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
npx testguard-cli concerns # what this project can be swept by
|
|
509
|
+
npx testguard-cli sweep --concern ADMIN-GUARDS # aim a sweep at one
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
```json
|
|
513
|
+
{
|
|
514
|
+
"schemaVersion": 1,
|
|
515
|
+
"concerns": [
|
|
516
|
+
{
|
|
517
|
+
"id": "ADMIN-GUARDS",
|
|
518
|
+
"statement": "Every admin route refuses a caller without the admin role.",
|
|
519
|
+
"severity": "critical",
|
|
520
|
+
"targets": { "kind": "glob", "globs": ["src/app/api/admin/**"] },
|
|
521
|
+
"faultClasses": ["guard-removed", "condition-forced", "return-altered"]
|
|
522
|
+
}
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Five lines, and the sweep reports:
|
|
528
|
+
|
|
529
|
+
```
|
|
530
|
+
concern ADMIN-GUARDS matches 24 files. Swept 23.
|
|
531
|
+
158 further proposals were outside this concern's fault classes.
|
|
532
|
+
|
|
533
|
+
SURVIVED src/app/api/admin/claims/[id]/route.ts
|
|
534
|
+
[line 41] Guard never triggers: `if (!membership || (membership.role !== 'owner'
|
|
535
|
+
&& membership.role !== 'manager'))` becomes `if (false)`.
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
The idea is Meta's: ACH has an engineer describe an area of concern in plain
|
|
539
|
+
text and gates every generated step by execution. What is taken here is the
|
|
540
|
+
**unit** — the concern as the thing a human writes — not their generator, and
|
|
541
|
+
**no model is required**. A concern's useful core is a named scope plus a
|
|
542
|
+
producer selection, which the mechanical producers already satisfy, so every
|
|
543
|
+
verifying command stays offline.
|
|
544
|
+
|
|
545
|
+
Two concerns ship built in, and only two. An authorization concern would need a
|
|
546
|
+
heuristic for *"which files check permissions"*, and a guess there is exactly
|
|
547
|
+
the noise that gets a check switched off — so `SAVE-PERSISTS` and
|
|
548
|
+
`CHANGED-CODE` are the defaults, and a project that knows its own auth layer
|
|
549
|
+
says so in three lines. Declaring a concern with a built-in's id replaces it,
|
|
550
|
+
and the replacement is reported rather than silent.
|
|
551
|
+
|
|
552
|
+
A concern is **not** a claim and never becomes one by itself. It says where to
|
|
553
|
+
look; the probe says what it found; a human states the sentence worth defending.
|
|
554
|
+
|
|
555
|
+
#### Sweeping the save surface instead of the diff
|
|
556
|
+
|
|
557
|
+
A diff cannot answer *"when the user clicks save, does it actually save?"* — it
|
|
558
|
+
only knows what changed today. `--save-paths` points the same sweep at every
|
|
559
|
+
file that writes to storage:
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
npx testguard-cli sweep --save-paths --cap 20
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
```
|
|
566
|
+
119 writes to storage across 37 files, carrying 226 payload fields. Swept 36.
|
|
567
|
+
proposed 762 faults, probed 20 (cap 20), deferred 742.
|
|
568
|
+
|
|
569
|
+
SURVIVED src/app/actions/register.ts [write path]
|
|
570
|
+
[line 59] Field dropped: `password_hash` is no longer written.
|
|
571
|
+
src/tests/actions/register.test.ts mocks @/lib/prisma; 1 exact, 8 partial,
|
|
572
|
+
31 argument-free call assertions in the file. A field dropped from the write
|
|
573
|
+
payload fails only against an assertion that names that field exactly.
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
The first line is the point. A report that lists findings without saying how
|
|
577
|
+
much surface it looked at invites you to assume the rest is fine, so the
|
|
578
|
+
denominator comes first and the document carries `writeSites` and
|
|
579
|
+
`payloadFields` whether or not anything survived. Measured on a real
|
|
580
|
+
AI-authored application: **12 of 26 probed persistence faults survived a fully
|
|
581
|
+
green suite, and all 12 were a dropped payload field.**
|
|
582
|
+
|
|
583
|
+
It also states, before any verdict, **what the defenders could prove at all**:
|
|
584
|
+
|
|
585
|
+
```
|
|
586
|
+
of those 37 files: 32 defended only by tests that mock the persistence layer,
|
|
587
|
+
1 with an unmocked defender, 4 with no defender at all.
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
That line is the read-back answer. A test that replaced the database can prove
|
|
591
|
+
the *call shape* and nothing beyond it — a `where` that matches no rows, a
|
|
592
|
+
rolled-back transaction and a rejected constraint all pass against a mock that
|
|
593
|
+
recorded the arguments and returned a plausible object. So rather than pretend
|
|
594
|
+
to measure persistence, TestGuard states the limit, the same way `nocover` says
|
|
595
|
+
"no test imports this" instead of guessing.
|
|
596
|
+
|
|
597
|
+
When every defender mocks the layer, the report says so outright: *nothing in
|
|
598
|
+
this suite can prove a write reached storage*. A clean probe there is not
|
|
599
|
+
evidence of persistence — it is evidence that nothing could have measured it.
|
|
600
|
+
|
|
601
|
+
`unmocked` is deliberately weaker than "proves persistence": a test that does
|
|
602
|
+
not replace the database may simply never reach it. Possible is the honest word.
|
|
603
|
+
|
|
493
604
|
### Every change needs a claim
|
|
494
605
|
|
|
495
606
|
`probe` can only verify claims that exist. Every escaped defect in the field
|
|
@@ -571,12 +682,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
571
682
|
# GitHub Actions
|
|
572
683
|
- uses: actions/checkout@v4
|
|
573
684
|
with: { fetch-depth: 0 }
|
|
574
|
-
- uses: raccioly/testguard@v0.
|
|
685
|
+
- uses: raccioly/testguard@v0.13.0
|
|
575
686
|
with: { command: gate }
|
|
576
687
|
|
|
577
688
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
578
689
|
include:
|
|
579
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
690
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.13.0/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
580
691
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
581
692
|
```
|
|
582
693
|
|
|
@@ -604,6 +715,32 @@ talks to the network; only the job posts, and only when told to.
|
|
|
604
715
|
toward green. The one pass is `killed`; everything else gates.
|
|
605
716
|
- **One exact-pinned runtime dependency** (`ajv`), Node ≥ 20, MIT.
|
|
606
717
|
|
|
718
|
+
### Prior art, and what was taken from where
|
|
719
|
+
|
|
720
|
+
Breaking code to test the tests is mutation testing, and it dates to the 1970s.
|
|
721
|
+
Two industrial programmes solved the parts that make it usable, and this tool
|
|
722
|
+
takes from both.
|
|
723
|
+
|
|
724
|
+
**Google** made it affordable: mutate only *changed* lines, cap what is
|
|
725
|
+
surfaced at seven per file, and order candidates by the measured productivity
|
|
726
|
+
of the operator in similar context — developer feedback took their productive
|
|
727
|
+
rate from 15% to 89%
|
|
728
|
+
([Petrović et al., 2021](https://arxiv.org/abs/2102.11378)). `sweep`'s diff
|
|
729
|
+
scope, its cap and its ordering are theirs; the productivity prior here is
|
|
730
|
+
measured on real probed faults rather than assumed.
|
|
731
|
+
|
|
732
|
+
**Meta** made it specific: an engineer writes a *concern* in plain text, a
|
|
733
|
+
model drafts faults for it, and every step is gated by execution before a
|
|
734
|
+
human ever sees it — 73% of the resulting tests were accepted
|
|
735
|
+
([Foster et al., FSE 2025](https://arxiv.org/abs/2501.12862)). The rule that
|
|
736
|
+
nothing a model proposes reaches the claims file without passing `probe` and a
|
|
737
|
+
human keep is theirs.
|
|
738
|
+
|
|
739
|
+
What neither does is bind a fault to a **stated promise**, which is what this
|
|
740
|
+
tool is for. The full record — including what was deliberately *not* taken, the
|
|
741
|
+
measured reason no model decides a verdict, and the gaps in what we borrowed —
|
|
742
|
+
is in [`docs-canonical/PRIOR-ART.md`](docs-canonical/PRIOR-ART.md).
|
|
743
|
+
|
|
607
744
|
### What TestGuard is not
|
|
608
745
|
|
|
609
746
|
- **Not a test generator.** It judges a test the agent wrote (`admit`); the
|
|
@@ -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.
|
|
130
|
+
| GitHub Action | `uses: raccioly/testguard@v0.13.0` — 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.
|
|
132
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.13.0/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
|
|
@@ -451,6 +451,14 @@ Three rules make it safe to run on a repository that has never seen this tool:
|
|
|
451
451
|
is reported and never gates — a tool that fails because its own guess was bad
|
|
452
452
|
is a tool people switch off.
|
|
453
453
|
|
|
454
|
+
The ordering **learns from your own runs**. Every evidence record already
|
|
455
|
+
carries a fault class and a verdict, so "how often does a fault of this class
|
|
456
|
+
survive here" is a tally, not a new thing to collect — a sweep persists its
|
|
457
|
+
evidence and the next one reads it. A project with no evidence falls back to
|
|
458
|
+
the shipped prior and says so; one with its own records overrides it in
|
|
459
|
+
proportion to how many it has. The report names which, because an ordering
|
|
460
|
+
nobody can trace is a number nobody should trust.
|
|
461
|
+
|
|
454
462
|
The cap and the ordering are Google's: their mutation service surfaces at most
|
|
455
463
|
7 × |files| mutants per change and orders candidates on the measured
|
|
456
464
|
productivity of their operator in similar context, which took their productive
|
|
@@ -466,6 +474,109 @@ A sweep is weaker evidence than a probe, and says so. Nobody stated that the
|
|
|
466
474
|
behaviour mattered. It is stronger than nothing, which is what a repository
|
|
467
475
|
with no claims has — and the survivors worth defending become the first claims.
|
|
468
476
|
|
|
477
|
+
#### Concerns: one sentence, two hundred screens
|
|
478
|
+
|
|
479
|
+
A claim names one promise precisely, which is why a repository with two hundred
|
|
480
|
+
screens never finishes writing them. A **concern** names a *kind* of promise and
|
|
481
|
+
says where to look for it:
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
npx testguard-cli concerns # what this project can be swept by
|
|
485
|
+
npx testguard-cli sweep --concern ADMIN-GUARDS # aim a sweep at one
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
```json
|
|
489
|
+
{
|
|
490
|
+
"schemaVersion": 1,
|
|
491
|
+
"concerns": [
|
|
492
|
+
{
|
|
493
|
+
"id": "ADMIN-GUARDS",
|
|
494
|
+
"statement": "Every admin route refuses a caller without the admin role.",
|
|
495
|
+
"severity": "critical",
|
|
496
|
+
"targets": { "kind": "glob", "globs": ["src/app/api/admin/**"] },
|
|
497
|
+
"faultClasses": ["guard-removed", "condition-forced", "return-altered"]
|
|
498
|
+
}
|
|
499
|
+
]
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Five lines, and the sweep reports:
|
|
504
|
+
|
|
505
|
+
```
|
|
506
|
+
concern ADMIN-GUARDS matches 24 files. Swept 23.
|
|
507
|
+
158 further proposals were outside this concern's fault classes.
|
|
508
|
+
|
|
509
|
+
SURVIVED src/app/api/admin/claims/[id]/route.ts
|
|
510
|
+
[line 41] Guard never triggers: `if (!membership || (membership.role !== 'owner'
|
|
511
|
+
&& membership.role !== 'manager'))` becomes `if (false)`.
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
The idea is Meta's: ACH has an engineer describe an area of concern in plain
|
|
515
|
+
text and gates every generated step by execution. What is taken here is the
|
|
516
|
+
**unit** — the concern as the thing a human writes — not their generator, and
|
|
517
|
+
**no model is required**. A concern's useful core is a named scope plus a
|
|
518
|
+
producer selection, which the mechanical producers already satisfy, so every
|
|
519
|
+
verifying command stays offline.
|
|
520
|
+
|
|
521
|
+
Two concerns ship built in, and only two. An authorization concern would need a
|
|
522
|
+
heuristic for *"which files check permissions"*, and a guess there is exactly
|
|
523
|
+
the noise that gets a check switched off — so `SAVE-PERSISTS` and
|
|
524
|
+
`CHANGED-CODE` are the defaults, and a project that knows its own auth layer
|
|
525
|
+
says so in three lines. Declaring a concern with a built-in's id replaces it,
|
|
526
|
+
and the replacement is reported rather than silent.
|
|
527
|
+
|
|
528
|
+
A concern is **not** a claim and never becomes one by itself. It says where to
|
|
529
|
+
look; the probe says what it found; a human states the sentence worth defending.
|
|
530
|
+
|
|
531
|
+
#### Sweeping the save surface instead of the diff
|
|
532
|
+
|
|
533
|
+
A diff cannot answer *"when the user clicks save, does it actually save?"* — it
|
|
534
|
+
only knows what changed today. `--save-paths` points the same sweep at every
|
|
535
|
+
file that writes to storage:
|
|
536
|
+
|
|
537
|
+
```bash
|
|
538
|
+
npx testguard-cli sweep --save-paths --cap 20
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
```
|
|
542
|
+
119 writes to storage across 37 files, carrying 226 payload fields. Swept 36.
|
|
543
|
+
proposed 762 faults, probed 20 (cap 20), deferred 742.
|
|
544
|
+
|
|
545
|
+
SURVIVED src/app/actions/register.ts [write path]
|
|
546
|
+
[line 59] Field dropped: `password_hash` is no longer written.
|
|
547
|
+
src/tests/actions/register.test.ts mocks @/lib/prisma; 1 exact, 8 partial,
|
|
548
|
+
31 argument-free call assertions in the file. A field dropped from the write
|
|
549
|
+
payload fails only against an assertion that names that field exactly.
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
The first line is the point. A report that lists findings without saying how
|
|
553
|
+
much surface it looked at invites you to assume the rest is fine, so the
|
|
554
|
+
denominator comes first and the document carries `writeSites` and
|
|
555
|
+
`payloadFields` whether or not anything survived. Measured on a real
|
|
556
|
+
AI-authored application: **12 of 26 probed persistence faults survived a fully
|
|
557
|
+
green suite, and all 12 were a dropped payload field.**
|
|
558
|
+
|
|
559
|
+
It also states, before any verdict, **what the defenders could prove at all**:
|
|
560
|
+
|
|
561
|
+
```
|
|
562
|
+
of those 37 files: 32 defended only by tests that mock the persistence layer,
|
|
563
|
+
1 with an unmocked defender, 4 with no defender at all.
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
That line is the read-back answer. A test that replaced the database can prove
|
|
567
|
+
the *call shape* and nothing beyond it — a `where` that matches no rows, a
|
|
568
|
+
rolled-back transaction and a rejected constraint all pass against a mock that
|
|
569
|
+
recorded the arguments and returned a plausible object. So rather than pretend
|
|
570
|
+
to measure persistence, TestGuard states the limit, the same way `nocover` says
|
|
571
|
+
"no test imports this" instead of guessing.
|
|
572
|
+
|
|
573
|
+
When every defender mocks the layer, the report says so outright: *nothing in
|
|
574
|
+
this suite can prove a write reached storage*. A clean probe there is not
|
|
575
|
+
evidence of persistence — it is evidence that nothing could have measured it.
|
|
576
|
+
|
|
577
|
+
`unmocked` is deliberately weaker than "proves persistence": a test that does
|
|
578
|
+
not replace the database may simply never reach it. Possible is the honest word.
|
|
579
|
+
|
|
469
580
|
### Every change needs a claim
|
|
470
581
|
|
|
471
582
|
`probe` can only verify claims that exist. Every escaped defect in the field
|
|
@@ -547,12 +658,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
547
658
|
# GitHub Actions
|
|
548
659
|
- uses: actions/checkout@v4
|
|
549
660
|
with: { fetch-depth: 0 }
|
|
550
|
-
- uses: raccioly/testguard@v0.
|
|
661
|
+
- uses: raccioly/testguard@v0.13.0
|
|
551
662
|
with: { command: gate }
|
|
552
663
|
|
|
553
664
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
554
665
|
include:
|
|
555
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
666
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.13.0/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
556
667
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
557
668
|
```
|
|
558
669
|
|
|
@@ -580,6 +691,32 @@ talks to the network; only the job posts, and only when told to.
|
|
|
580
691
|
toward green. The one pass is `killed`; everything else gates.
|
|
581
692
|
- **One exact-pinned runtime dependency** (`ajv`), Node ≥ 20, MIT.
|
|
582
693
|
|
|
694
|
+
### Prior art, and what was taken from where
|
|
695
|
+
|
|
696
|
+
Breaking code to test the tests is mutation testing, and it dates to the 1970s.
|
|
697
|
+
Two industrial programmes solved the parts that make it usable, and this tool
|
|
698
|
+
takes from both.
|
|
699
|
+
|
|
700
|
+
**Google** made it affordable: mutate only *changed* lines, cap what is
|
|
701
|
+
surfaced at seven per file, and order candidates by the measured productivity
|
|
702
|
+
of the operator in similar context — developer feedback took their productive
|
|
703
|
+
rate from 15% to 89%
|
|
704
|
+
([Petrović et al., 2021](https://arxiv.org/abs/2102.11378)). `sweep`'s diff
|
|
705
|
+
scope, its cap and its ordering are theirs; the productivity prior here is
|
|
706
|
+
measured on real probed faults rather than assumed.
|
|
707
|
+
|
|
708
|
+
**Meta** made it specific: an engineer writes a *concern* in plain text, a
|
|
709
|
+
model drafts faults for it, and every step is gated by execution before a
|
|
710
|
+
human ever sees it — 73% of the resulting tests were accepted
|
|
711
|
+
([Foster et al., FSE 2025](https://arxiv.org/abs/2501.12862)). The rule that
|
|
712
|
+
nothing a model proposes reaches the claims file without passing `probe` and a
|
|
713
|
+
human keep is theirs.
|
|
714
|
+
|
|
715
|
+
What neither does is bind a fault to a **stated promise**, which is what this
|
|
716
|
+
tool is for. The full record — including what was deliberately *not* taken, the
|
|
717
|
+
measured reason no model decides a verdict, and the gaps in what we borrowed —
|
|
718
|
+
is in [`docs-canonical/PRIOR-ART.md`](docs-canonical/PRIOR-ART.md).
|
|
719
|
+
|
|
583
720
|
### What TestGuard is not
|
|
584
721
|
|
|
585
722
|
- **Not a test generator.** It judges a test the agent wrote (`admit`); the
|