testguard-cli 0.11.0__tar.gz → 0.12.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.12.0}/.docguard.json +2 -1
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/CHANGELOG.md +51 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/PKG-INFO +64 -5
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/README.md +63 -4
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/action.yml +1 -1
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/docs/testguard-explained.html +172 -11
- testguard_cli-0.12.0/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/docs-canonical/ARCHITECTURE.md +1 -1
- testguard_cli-0.12.0/docs-canonical/PRIOR-ART.md +160 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/package-lock.json +2 -2
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/package.json +1 -1
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/pyproject.toml +1 -1
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/lib/validate.mjs +12 -0
- testguard_cli-0.12.0/spec/schemas/sweep.schema.json +310 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/cli.mjs +5 -1
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/sweep.mjs +9 -3
- testguard_cli-0.12.0/src/supply/savepath.mjs +216 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/sweep/sweep.mjs +32 -4
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/util/glob.mjs +8 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/glob.test.mjs +5 -2
- testguard_cli-0.12.0/test/supply-savepath.test.mjs +165 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/sweep.test.mjs +40 -3
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/testguard.claims.json +99 -1
- {testguard_cli-0.11.0 → testguard_cli-0.12.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 → testguard_cli-0.12.0}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.codex/hooks.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.docguardignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.gitattributes +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/dependabot.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/scripts/approve-held-runs.sh +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/scripts/publish-homebrew-tap.sh +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/workflows/release.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/workflows/scheduled-release.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.npmignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.npmrc +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/AGENTS.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/CLAUDE.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/CONTRIBUTING.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/LICENSE +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/PRIVACY.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/SECURITY.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/SUPPORT.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/bench/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/cli/testguard.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/docs-canonical/ENVIRONMENT.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/docs-canonical/REQUIREMENTS.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/docs-canonical/SECURITY.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/docs-canonical/TEST-SPEC.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/GATE-SEMANTICS.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/README.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/evidence.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/replay.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/claims/anchors.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/claims/load.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/baseline.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/brief.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/gate.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/init.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/probe.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/commands/status.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/gate/changed.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/git.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/init/init.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/init/templates/SKILL.md +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/mcp/tools.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/contention.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/probe.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/pyimports.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/index.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/render.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/replay/label.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/replay/replay.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/scaffold/producers.python.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/status/status.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/status/surface.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/supply/persistence.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/supply/select.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/src/util/hash.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/admit.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/brief.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/calibration.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/claim-selection.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/claims-anchors.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/classify.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/contention.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/cost.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/discover.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/gate.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/independence.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/init.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/inject.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/label.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/one-pager.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/probe-error.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/progress.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/pyimports.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/release-approval.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/release-sync.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/replay-verdict.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/replay.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/samples/python-report.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/scaffold.python.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/status.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/supply-persistence.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/supply-select.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/surface.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/sweep.fixture.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/vitest-config.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/test/writer.test.mjs +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/testguard.ignore.json +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.11.0 → testguard_cli-0.12.0}/vitest.config.mjs +0 -0
|
@@ -7,6 +7,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.12.0] - 2026-09-22
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.11.0`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- feat(sweep): --save-paths, and the denominator a save report is read against (#156)
|
|
17
|
+
- docs: make the brief describe what the tool actually does, and record the prior art (#155)
|
|
18
|
+
- chore(homebrew): sha256 for v0.11.0 (#153)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- `sweep --save-paths` — sweep every file that writes to storage instead of the
|
|
24
|
+
diff, and report the surface as the denominator the findings are read
|
|
25
|
+
against. A diff cannot answer how many save paths exist, and a clean report
|
|
26
|
+
over an unstated denominator is a sample of unknown size. The sweep document
|
|
27
|
+
gains `scope.mode`, `scope.writeSites` and `scope.payloadFields`, and the
|
|
28
|
+
validator refuses a save-paths sweep that omits its surface.
|
|
29
|
+
- `src/supply/savepath.mjs` — the write-site enumerator: which calls put data
|
|
30
|
+
into storage, and which payload keys sit on their own line (the ones a
|
|
31
|
+
`field-dropped` fault can anchor to). Measured on a real AI-authored
|
|
32
|
+
application: 119 writes across 37 files carrying 226 payload fields.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- `walk()` skipped `dist` and `coverage` but not `.next`, `build`, `out`,
|
|
37
|
+
`.svelte-kit`, `.turbo` and their siblings, while the claim above it already
|
|
38
|
+
promised that a build's test files cannot be picked up. A scan of a real
|
|
39
|
+
Next.js application counted 104 files instead of 32, because `.next/` carries
|
|
40
|
+
compiled copies of the application — its writes and its tests. The claim was
|
|
41
|
+
right; the list was short.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- `docs-canonical/PRIOR-ART.md` — the canonical record of what was taken from
|
|
46
|
+
Google's and Meta's mutation-testing programmes, what was deliberately not
|
|
47
|
+
taken, the measured reason no model decides a verdict, and the known gaps in
|
|
48
|
+
what was borrowed. Registered with DocGuard.
|
|
49
|
+
- README prior-art section pointing at it.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- The technical brief now describes what the tool actually does. A new
|
|
54
|
+
*Starting from zero* page carries Field report C (the persistence-path
|
|
55
|
+
measurement: 12 of 26 faults survived a green suite, all of them a dropped
|
|
56
|
+
payload field) and the claim-supply ladder; prior art names Google and Meta
|
|
57
|
+
and what each solved; the decision-model result is stated as a measurement
|
|
58
|
+
rather than a design assertion; and a *what it deliberately is not* section
|
|
59
|
+
closes it. `sweep` added to the verb table. Six pages to eight, re-rendered.
|
|
60
|
+
|
|
10
61
|
## [0.11.0] - 2026-09-21
|
|
11
62
|
|
|
12
63
|
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.12.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.12.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.12.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
|
|
@@ -490,6 +490,39 @@ A sweep is weaker evidence than a probe, and says so. Nobody stated that the
|
|
|
490
490
|
behaviour mattered. It is stronger than nothing, which is what a repository
|
|
491
491
|
with no claims has — and the survivors worth defending become the first claims.
|
|
492
492
|
|
|
493
|
+
#### Sweeping the save surface instead of the diff
|
|
494
|
+
|
|
495
|
+
A diff cannot answer *"when the user clicks save, does it actually save?"* — it
|
|
496
|
+
only knows what changed today. `--save-paths` points the same sweep at every
|
|
497
|
+
file that writes to storage:
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
npx testguard-cli sweep --save-paths --cap 20
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
```
|
|
504
|
+
119 writes to storage across 37 files, carrying 226 payload fields. Swept 36.
|
|
505
|
+
proposed 762 faults, probed 20 (cap 20), deferred 742.
|
|
506
|
+
|
|
507
|
+
SURVIVED src/app/actions/register.ts [write path]
|
|
508
|
+
[line 59] Field dropped: `password_hash` is no longer written.
|
|
509
|
+
src/tests/actions/register.test.ts mocks @/lib/prisma; 1 exact, 8 partial,
|
|
510
|
+
31 argument-free call assertions in the file. A field dropped from the write
|
|
511
|
+
payload fails only against an assertion that names that field exactly.
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
The first line is the point. A report that lists findings without saying how
|
|
515
|
+
much surface it looked at invites you to assume the rest is fine, so the
|
|
516
|
+
denominator comes first and the document carries `writeSites` and
|
|
517
|
+
`payloadFields` whether or not anything survived. Measured on a real
|
|
518
|
+
AI-authored application: **12 of 26 probed persistence faults survived a fully
|
|
519
|
+
green suite, and all 12 were a dropped payload field.**
|
|
520
|
+
|
|
521
|
+
This is a *sample* of the surface, and says so. It does not yet prove a write
|
|
522
|
+
reached storage — only that a test would notice if the payload changed.
|
|
523
|
+
Read-back oracles are the next rung
|
|
524
|
+
([#154](https://github.com/raccioly/testguard/issues/154)).
|
|
525
|
+
|
|
493
526
|
### Every change needs a claim
|
|
494
527
|
|
|
495
528
|
`probe` can only verify claims that exist. Every escaped defect in the field
|
|
@@ -571,12 +604,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
571
604
|
# GitHub Actions
|
|
572
605
|
- uses: actions/checkout@v4
|
|
573
606
|
with: { fetch-depth: 0 }
|
|
574
|
-
- uses: raccioly/testguard@v0.
|
|
607
|
+
- uses: raccioly/testguard@v0.12.0
|
|
575
608
|
with: { command: gate }
|
|
576
609
|
|
|
577
610
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
578
611
|
include:
|
|
579
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
612
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.12.0/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
580
613
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
581
614
|
```
|
|
582
615
|
|
|
@@ -604,6 +637,32 @@ talks to the network; only the job posts, and only when told to.
|
|
|
604
637
|
toward green. The one pass is `killed`; everything else gates.
|
|
605
638
|
- **One exact-pinned runtime dependency** (`ajv`), Node ≥ 20, MIT.
|
|
606
639
|
|
|
640
|
+
### Prior art, and what was taken from where
|
|
641
|
+
|
|
642
|
+
Breaking code to test the tests is mutation testing, and it dates to the 1970s.
|
|
643
|
+
Two industrial programmes solved the parts that make it usable, and this tool
|
|
644
|
+
takes from both.
|
|
645
|
+
|
|
646
|
+
**Google** made it affordable: mutate only *changed* lines, cap what is
|
|
647
|
+
surfaced at seven per file, and order candidates by the measured productivity
|
|
648
|
+
of the operator in similar context — developer feedback took their productive
|
|
649
|
+
rate from 15% to 89%
|
|
650
|
+
([Petrović et al., 2021](https://arxiv.org/abs/2102.11378)). `sweep`'s diff
|
|
651
|
+
scope, its cap and its ordering are theirs; the productivity prior here is
|
|
652
|
+
measured on real probed faults rather than assumed.
|
|
653
|
+
|
|
654
|
+
**Meta** made it specific: an engineer writes a *concern* in plain text, a
|
|
655
|
+
model drafts faults for it, and every step is gated by execution before a
|
|
656
|
+
human ever sees it — 73% of the resulting tests were accepted
|
|
657
|
+
([Foster et al., FSE 2025](https://arxiv.org/abs/2501.12862)). The rule that
|
|
658
|
+
nothing a model proposes reaches the claims file without passing `probe` and a
|
|
659
|
+
human keep is theirs.
|
|
660
|
+
|
|
661
|
+
What neither does is bind a fault to a **stated promise**, which is what this
|
|
662
|
+
tool is for. The full record — including what was deliberately *not* taken, the
|
|
663
|
+
measured reason no model decides a verdict, and the gaps in what we borrowed —
|
|
664
|
+
is in [`docs-canonical/PRIOR-ART.md`](docs-canonical/PRIOR-ART.md).
|
|
665
|
+
|
|
607
666
|
### What TestGuard is not
|
|
608
667
|
|
|
609
668
|
- **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.12.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.12.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
|
|
@@ -466,6 +466,39 @@ A sweep is weaker evidence than a probe, and says so. Nobody stated that the
|
|
|
466
466
|
behaviour mattered. It is stronger than nothing, which is what a repository
|
|
467
467
|
with no claims has — and the survivors worth defending become the first claims.
|
|
468
468
|
|
|
469
|
+
#### Sweeping the save surface instead of the diff
|
|
470
|
+
|
|
471
|
+
A diff cannot answer *"when the user clicks save, does it actually save?"* — it
|
|
472
|
+
only knows what changed today. `--save-paths` points the same sweep at every
|
|
473
|
+
file that writes to storage:
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
npx testguard-cli sweep --save-paths --cap 20
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
119 writes to storage across 37 files, carrying 226 payload fields. Swept 36.
|
|
481
|
+
proposed 762 faults, probed 20 (cap 20), deferred 742.
|
|
482
|
+
|
|
483
|
+
SURVIVED src/app/actions/register.ts [write path]
|
|
484
|
+
[line 59] Field dropped: `password_hash` is no longer written.
|
|
485
|
+
src/tests/actions/register.test.ts mocks @/lib/prisma; 1 exact, 8 partial,
|
|
486
|
+
31 argument-free call assertions in the file. A field dropped from the write
|
|
487
|
+
payload fails only against an assertion that names that field exactly.
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
The first line is the point. A report that lists findings without saying how
|
|
491
|
+
much surface it looked at invites you to assume the rest is fine, so the
|
|
492
|
+
denominator comes first and the document carries `writeSites` and
|
|
493
|
+
`payloadFields` whether or not anything survived. Measured on a real
|
|
494
|
+
AI-authored application: **12 of 26 probed persistence faults survived a fully
|
|
495
|
+
green suite, and all 12 were a dropped payload field.**
|
|
496
|
+
|
|
497
|
+
This is a *sample* of the surface, and says so. It does not yet prove a write
|
|
498
|
+
reached storage — only that a test would notice if the payload changed.
|
|
499
|
+
Read-back oracles are the next rung
|
|
500
|
+
([#154](https://github.com/raccioly/testguard/issues/154)).
|
|
501
|
+
|
|
469
502
|
### Every change needs a claim
|
|
470
503
|
|
|
471
504
|
`probe` can only verify claims that exist. Every escaped defect in the field
|
|
@@ -547,12 +580,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
547
580
|
# GitHub Actions
|
|
548
581
|
- uses: actions/checkout@v4
|
|
549
582
|
with: { fetch-depth: 0 }
|
|
550
|
-
- uses: raccioly/testguard@v0.
|
|
583
|
+
- uses: raccioly/testguard@v0.12.0
|
|
551
584
|
with: { command: gate }
|
|
552
585
|
|
|
553
586
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
554
587
|
include:
|
|
555
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
588
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.12.0/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
556
589
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
557
590
|
```
|
|
558
591
|
|
|
@@ -580,6 +613,32 @@ talks to the network; only the job posts, and only when told to.
|
|
|
580
613
|
toward green. The one pass is `killed`; everything else gates.
|
|
581
614
|
- **One exact-pinned runtime dependency** (`ajv`), Node ≥ 20, MIT.
|
|
582
615
|
|
|
616
|
+
### Prior art, and what was taken from where
|
|
617
|
+
|
|
618
|
+
Breaking code to test the tests is mutation testing, and it dates to the 1970s.
|
|
619
|
+
Two industrial programmes solved the parts that make it usable, and this tool
|
|
620
|
+
takes from both.
|
|
621
|
+
|
|
622
|
+
**Google** made it affordable: mutate only *changed* lines, cap what is
|
|
623
|
+
surfaced at seven per file, and order candidates by the measured productivity
|
|
624
|
+
of the operator in similar context — developer feedback took their productive
|
|
625
|
+
rate from 15% to 89%
|
|
626
|
+
([Petrović et al., 2021](https://arxiv.org/abs/2102.11378)). `sweep`'s diff
|
|
627
|
+
scope, its cap and its ordering are theirs; the productivity prior here is
|
|
628
|
+
measured on real probed faults rather than assumed.
|
|
629
|
+
|
|
630
|
+
**Meta** made it specific: an engineer writes a *concern* in plain text, a
|
|
631
|
+
model drafts faults for it, and every step is gated by execution before a
|
|
632
|
+
human ever sees it — 73% of the resulting tests were accepted
|
|
633
|
+
([Foster et al., FSE 2025](https://arxiv.org/abs/2501.12862)). The rule that
|
|
634
|
+
nothing a model proposes reaches the claims file without passing `probe` and a
|
|
635
|
+
human keep is theirs.
|
|
636
|
+
|
|
637
|
+
What neither does is bind a fault to a **stated promise**, which is what this
|
|
638
|
+
tool is for. The full record — including what was deliberately *not* taken, the
|
|
639
|
+
measured reason no model decides a verdict, and the gaps in what we borrowed —
|
|
640
|
+
is in [`docs-canonical/PRIOR-ART.md`](docs-canonical/PRIOR-ART.md).
|
|
641
|
+
|
|
583
642
|
### What TestGuard is not
|
|
584
643
|
|
|
585
644
|
- **Not a test generator.** It judges a test the agent wrote (`admit`); the
|
|
@@ -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.
|
|
147
|
+
<div class="meta">Technical Brief · v0.12.0 · MIT</div>
|
|
148
148
|
</div>
|
|
149
149
|
|
|
150
150
|
<h1>Your tests pass. That is not the same as your tests working.</h1>
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
<div class="attrib">The design constraint everything else follows from</div>
|
|
210
210
|
</div>
|
|
211
211
|
|
|
212
|
-
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 1 of
|
|
212
|
+
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 1 of 8 · The idea</span></div>
|
|
213
213
|
</div>
|
|
214
214
|
|
|
215
215
|
<!-- ═══════════════ PAGE — THE EVIDENCE ═══════════════ -->
|
|
@@ -368,7 +368,7 @@
|
|
|
368
368
|
<div class="attrib">Why the question has to be asked adversarially</div>
|
|
369
369
|
</div>
|
|
370
370
|
|
|
371
|
-
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 2 of
|
|
371
|
+
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 2 of 8 · Evidence</span></div>
|
|
372
372
|
</div>
|
|
373
373
|
|
|
374
374
|
<!-- ═══════════════ PAGE 2 — HOW A RUN WORKS ═══════════════ -->
|
|
@@ -541,7 +541,7 @@
|
|
|
541
541
|
</div>
|
|
542
542
|
</section>
|
|
543
543
|
|
|
544
|
-
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 3 of
|
|
544
|
+
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 3 of 8 · Anatomy</span></div>
|
|
545
545
|
</div>
|
|
546
546
|
|
|
547
547
|
<!-- ═══════════════ PAGE 3 — VERDICTS & ARCHITECTURE ═══════════════ -->
|
|
@@ -658,7 +658,7 @@
|
|
|
658
658
|
</div>
|
|
659
659
|
</section>
|
|
660
660
|
|
|
661
|
-
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 4 of
|
|
661
|
+
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 4 of 8 · Verdicts & architecture</span></div>
|
|
662
662
|
</div>
|
|
663
663
|
|
|
664
664
|
<!-- ═══════════════ PAGE 5 — THE LOOP & CALIBRATION ═══════════════ -->
|
|
@@ -678,6 +678,7 @@
|
|
|
678
678
|
<tr><td><code>init</code></td><td>Installs the agent layer — skill, session-start hook, AGENTS.md section — at the git root.</td></tr>
|
|
679
679
|
<tr><td><code>claims</code></td><td>Lists the claims and reports drift against the <code>@claim</code> annotations in the code.</td></tr>
|
|
680
680
|
<tr><td><code>scaffold</code></td><td>Proposes faults mechanically for one file, as a draft claims document. A starting point, not an answer.</td></tr>
|
|
681
|
+
<tr><td><code>sweep</code></td><td>The cold start: propose and probe faults for changed files that carry <em>no</em> claim.</td></tr>
|
|
681
682
|
<tr><td class="v-good"><code>probe</code></td><td>The engine. Inject each fault, run its defenders, report what survived.</td></tr>
|
|
682
683
|
<tr><td><code>admit</code></td><td>The two-gate rule as one verb: green on HEAD <em>and</em> failing on every fault of the claim. Otherwise the test is not a defender.</td></tr>
|
|
683
684
|
<tr><td><code>baseline</code></td><td>Freezes today's unproven findings so only new ones gate.</td></tr>
|
|
@@ -728,20 +729,135 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
728
729
|
</div>
|
|
729
730
|
</section>
|
|
730
731
|
|
|
731
|
-
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 5 of
|
|
732
|
+
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 5 of 8 · The loop & calibration</span></div>
|
|
732
733
|
</div>
|
|
733
734
|
|
|
734
|
-
|
|
735
|
+
|
|
736
|
+
<!-- ═══════════════ PAGE 6 — STARTING FROM ZERO ═══════════════ -->
|
|
735
737
|
<div class="page">
|
|
736
738
|
<div class="masthead">
|
|
737
739
|
<div class="wordmark">Test<span class="mark">Guard</span></div>
|
|
738
|
-
<div class="meta">06 —
|
|
740
|
+
<div class="meta">06 — Starting from zero</div>
|
|
739
741
|
</div>
|
|
740
742
|
|
|
741
743
|
<section style="margin-top:17px">
|
|
742
|
-
<h2>
|
|
744
|
+
<h2>The bottleneck was never verification</h2>
|
|
745
|
+
<p class="lede">A verifier with no claim about a feature is silent about it by construction. Every
|
|
746
|
+
escaped defect in the field reports was a <strong>claim gap</strong> — not a probe that missed.</p>
|
|
747
|
+
<p>So the scarce thing is not the engine. It is the <em>oracle</em>: a sentence about what must be
|
|
748
|
+
true, written by someone who is not the code. <code>gate</code> names the changed files that carry no
|
|
749
|
+
claim and stops there, correctly — stating a claim is a human act. But a team adopting this reads
|
|
750
|
+
that list, has nothing to compare it against, and closes the tab.</p>
|
|
751
|
+
</section>
|
|
752
|
+
|
|
753
|
+
<section class="rule-top">
|
|
754
|
+
<h2>Field report C — where a save button actually fails</h2>
|
|
755
|
+
<p>Twenty-six faults injected on the persistence path of seven server actions in a real
|
|
756
|
+
AI-authored application: the database write itself, and the fields of the payload it carries.</p>
|
|
757
|
+
<figure style="margin-top:11px">
|
|
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; 10 of 10 tests mock the database and none read back">
|
|
759
|
+
<rect x="0" y="0" width="243" height="74" rx="4" fill="#ffffff" stroke="#e6e2d8"/>
|
|
760
|
+
<text x="18" y="38" style="font-family:var(--sans);font-size:27px;font-weight:700;letter-spacing:-0.03em;fill:#b85636">12 of 26</text>
|
|
761
|
+
<text class="s-sub" x="18" y="56" style="font-size:8px">survived a fully green suite</text>
|
|
762
|
+
<text class="s-sub" x="18" y="68" style="font-size:8px">46% of the persistence path</text>
|
|
763
|
+
|
|
764
|
+
<rect x="258" y="0" width="243" height="74" rx="4" fill="#ffffff" stroke="#e6e2d8"/>
|
|
765
|
+
<text x="276" y="38" style="font-family:var(--sans);font-size:27px;font-weight:700;letter-spacing:-0.03em;fill:#191917">12 of 12</text>
|
|
766
|
+
<text class="s-sub" x="276" y="56" style="font-size:8px">survivors were a dropped payload</text>
|
|
767
|
+
<text class="s-sub" x="276" y="68" style="font-size:8px">field — not one was anything else</text>
|
|
768
|
+
|
|
769
|
+
<rect x="516" y="0" width="244" height="74" rx="4" fill="#ffffff" stroke="#e6e2d8"/>
|
|
770
|
+
<text x="534" y="38" style="font-family:var(--sans);font-size:27px;font-weight:700;letter-spacing:-0.03em;fill:#191917">10 of 10</text>
|
|
771
|
+
<text class="s-sub" x="534" y="56" style="font-size:8px">tests mock the database; none</text>
|
|
772
|
+
<text class="s-sub" x="534" y="68" style="font-size:8px">read the record back</text>
|
|
773
|
+
|
|
774
|
+
<rect x="0" y="86" width="760" height="58" rx="4" fill="#fbf1ec" stroke="#e8d5cc"/>
|
|
775
|
+
<text class="s-tiny" x="16" y="104" style="fill:#b85636">The worst case — user registration</text>
|
|
776
|
+
<text class="s-sub" x="16" y="120" style="font-size:7.8px">Each of email, username, password_hash, terms_accepted_at and terms_version can be removed from the row that is</text>
|
|
777
|
+
<text class="s-sub" x="16" y="133" style="font-size:7.8px">written, and no test fails. The tests assert that the write was called. They never assert what it was called with.</text>
|
|
778
|
+
</svg>
|
|
779
|
+
<p class="figcap">Fig. 7 — The save button reports success. The suite agrees. The column is empty.</p>
|
|
780
|
+
</figure>
|
|
781
|
+
<p class="note">This is the page-one exhibit again, measured rather than recounted: an assertion that
|
|
782
|
+
names the call but not the payload cannot fail when a field goes missing. It is invisible to coverage,
|
|
783
|
+
invisible to a mutation score, and invisible to the test's own author.</p>
|
|
784
|
+
</section>
|
|
785
|
+
|
|
786
|
+
<section class="rule-top">
|
|
787
|
+
<h2>A ladder, so a repository with no claims is not stuck</h2>
|
|
788
|
+
<div class="cols-3">
|
|
789
|
+
<div>
|
|
790
|
+
<h3>Nothing written</h3>
|
|
791
|
+
<p class="note"><code>sweep</code> takes the changed files the gate just called uncovered,
|
|
792
|
+
proposes faults with the mechanical producers, probes a bounded selection, and reports what
|
|
793
|
+
nothing noticed. A fault that survives needs no claim to be alarming.</p>
|
|
794
|
+
</div>
|
|
795
|
+
<div>
|
|
796
|
+
<h3>A sentence or ten</h3>
|
|
797
|
+
<p class="note">A <em>concern</em> — “every save persists its data” — is one sentence that applies
|
|
798
|
+
across two hundred screens. It is human-anchored and machine-instantiated, and every proposal it
|
|
799
|
+
produces still has to survive an execution gate.</p>
|
|
800
|
+
</div>
|
|
801
|
+
<div>
|
|
802
|
+
<h3>A claim</h3>
|
|
803
|
+
<p class="note">The survivors worth defending become the first claims: a statement, its faults,
|
|
804
|
+
and a reproducer already in hand. The ladder ends where the rest of this brief begins.</p>
|
|
805
|
+
</div>
|
|
806
|
+
</div>
|
|
807
|
+
<p class="note" style="margin-top:9px">Three rules keep the bottom rung safe on a repository that has
|
|
808
|
+
never seen this tool. A sweep <strong>never writes the claims file</strong> — a sentence nobody wrote
|
|
809
|
+
is not a claim. Its evidence <strong>never replaces</strong> the canonical evidence, which is the
|
|
810
|
+
record of what the project actually claims. And it <strong>does not fail on its own bad guess</strong>:
|
|
811
|
+
only a survivor or an untested file exits non-zero, because a proposal that would not compile is the
|
|
812
|
+
tool’s mistake, and a tool that fails on its own mistakes gets switched off.</p>
|
|
813
|
+
</section>
|
|
814
|
+
|
|
815
|
+
<div class="pull">
|
|
816
|
+
<p>A sweep is weaker evidence than a probe, and says so. Nobody stated that the behaviour mattered.
|
|
817
|
+
It is stronger than nothing, which is what a repository with no claims has.</p>
|
|
818
|
+
<div class="attrib">Why the ladder has a bottom rung</div>
|
|
819
|
+
</div>
|
|
820
|
+
|
|
821
|
+
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 6 of 8 · Starting from zero</span></div>
|
|
822
|
+
</div>
|
|
823
|
+
|
|
824
|
+
<!-- ═══════════════ PAGE 7 — PRIOR ART & DIFFERENCE ═══════════════ -->
|
|
825
|
+
<div class="page">
|
|
826
|
+
<div class="masthead">
|
|
827
|
+
<div class="wordmark">Test<span class="mark">Guard</span></div>
|
|
828
|
+
<div class="meta">07 — Prior art</div>
|
|
829
|
+
</div>
|
|
830
|
+
|
|
831
|
+
<section style="margin-top:17px">
|
|
832
|
+
<h2>What is not new, and who solved which part</h2>
|
|
743
833
|
<p>Breaking code to test your tests is <strong>mutation testing</strong>, and it dates to the 1970s.
|
|
744
|
-
Stryker, PIT, mutmut and Cosmic Ray all do it. Anyone claiming that part is novel is selling something
|
|
834
|
+
Stryker, PIT, mutmut and Cosmic Ray all do it. Anyone claiming that part is novel is selling something.
|
|
835
|
+
Two industrial programmes solved the parts that make it usable, and this tool takes both.</p>
|
|
836
|
+
<div class="cols-2" style="margin-top:11px">
|
|
837
|
+
<div>
|
|
838
|
+
<h3>Google — making it affordable</h3>
|
|
839
|
+
<p class="note">Mutating two billion lines is infeasible, so Petrović et al. mutate only
|
|
840
|
+
<em>changed</em> lines, suppress “arid” nodes nobody would test, cap what is surfaced at seven
|
|
841
|
+
per file, and order candidates by the measured productivity of the operator in similar context.
|
|
842
|
+
Developer feedback over six years took their productive rate from <strong>15% to 89%</strong>.
|
|
843
|
+
A later study found reported mutants are coupled to real faults: the bugs they analysed would
|
|
844
|
+
have been caught had a mutant been surfaced.</p>
|
|
845
|
+
<p class="note"><em>Taken here:</em> the diff scope, the cap, and a productivity-ordered
|
|
846
|
+
selection — measured on real probed faults rather than assumed.</p>
|
|
847
|
+
</div>
|
|
848
|
+
<div>
|
|
849
|
+
<h3>Meta — making it specific</h3>
|
|
850
|
+
<p class="note">ACH inverts the generator: an engineer writes a <em>concern</em> in plain text,
|
|
851
|
+
an LLM drafts faults for that concern, and each one must build, survive the existing suite, and
|
|
852
|
+
not be equivalent before a test is generated to kill it. Engineers accepted <strong>73%</strong>
|
|
853
|
+
of the resulting tests. Nothing is trusted because a model said it — every step is gated by
|
|
854
|
+
execution, and the mutant is shown to the reviewer as proof the test catches something real.</p>
|
|
855
|
+
<p class="note"><em>Taken here:</em> the concern as the unit a human writes, and an execution
|
|
856
|
+
gate between every model step and the record.</p>
|
|
857
|
+
</div>
|
|
858
|
+
</div>
|
|
859
|
+
<p class="note" style="margin-top:9px">What neither does is bind a fault to a <em>stated promise</em>.
|
|
860
|
+
That is the remaining gap, and it is the next section.</p>
|
|
745
861
|
</section>
|
|
746
862
|
|
|
747
863
|
<section class="rule-top">
|
|
@@ -812,6 +928,51 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
812
928
|
<strong>audit</strong>, and it is the whole design.</p>
|
|
813
929
|
</section>
|
|
814
930
|
|
|
931
|
+
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 7 of 8 · Prior art</span></div>
|
|
932
|
+
</div>
|
|
933
|
+
|
|
934
|
+
<!-- ═══════════════ PAGE 8 — DIFFERENCE ═══════════════ -->
|
|
935
|
+
<div class="page">
|
|
936
|
+
<div class="masthead">
|
|
937
|
+
<div class="wordmark">Test<span class="mark">Guard</span></div>
|
|
938
|
+
<div class="meta">08 — Difference</div>
|
|
939
|
+
</div>
|
|
940
|
+
|
|
941
|
+
<section style="margin-top:17px">
|
|
942
|
+
<h2>And one thing that was measured, not assumed</h2>
|
|
943
|
+
<p class="note">“No model decides a verdict” is a design rule elsewhere in this brief. It is also a
|
|
944
|
+
result. A calibrated commercial decision model was shadow-evaluated against real probed verdicts on
|
|
945
|
+
an AI-authored codebase: asked whether a given test would catch a given fault, it scored
|
|
946
|
+
<strong>AUC 0.813</strong> — and its errors ran in the forbidden direction, confidently reporting
|
|
947
|
+
that a test would catch a fault that in fact survived. On hand-built fixtures the same model looked
|
|
948
|
+
far better, which is the second finding: a synthetic fixture flatters any judge, because its blind
|
|
949
|
+
spots were authored to be legible. Execution remains the only thing permitted to say
|
|
950
|
+
<code>killed</code>.</p>
|
|
951
|
+
</section>
|
|
952
|
+
|
|
953
|
+
<section class="rule-top">
|
|
954
|
+
<h2>What it deliberately is not</h2>
|
|
955
|
+
<div class="cols-2">
|
|
956
|
+
<div><ul class="ticks">
|
|
957
|
+
<li><strong>Not a test generator.</strong> It judges a test the agent wrote; the generating half
|
|
958
|
+
stays with the agent, where the market is putting it.</li>
|
|
959
|
+
<li><strong>Not a coverage tool.</strong> A line executed says nothing about whether an assertion
|
|
960
|
+
would notice.</li>
|
|
961
|
+
</ul></div>
|
|
962
|
+
<div><ul class="ticks">
|
|
963
|
+
<li><strong>Not a mutation-score dashboard.</strong> No blanket mutants, no single number, no
|
|
964
|
+
threshold to game.</li>
|
|
965
|
+
<li><strong>Not a self-healing runner.</strong> A defender that adapts itself to a change is a
|
|
966
|
+
defender that did not observe it.</li>
|
|
967
|
+
</ul></div>
|
|
968
|
+
</div>
|
|
969
|
+
<p class="note" style="margin-top:9px">And one gap it names rather than hides: a claim that
|
|
970
|
+
<em>disappeared</em> is invisible to all of the above — <code>probe</code> verifies the claims that
|
|
971
|
+
exist, and deleting one is cheaper than weakening it. So removal is reported too, against any
|
|
972
|
+
reference, with the verdict the claim last had. Removal is allowed; claims can be wrong, superseded
|
|
973
|
+
or split. It is never silent.</p>
|
|
974
|
+
</section>
|
|
975
|
+
|
|
815
976
|
<section class="rule-top">
|
|
816
977
|
<h2>Three more things that follow from it</h2>
|
|
817
978
|
<div class="cols-3">
|
|
@@ -840,7 +1001,7 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
840
1001
|
<div class="attrib">github.com/raccioly/testguard · npm testguard-cli · MIT</div>
|
|
841
1002
|
</div>
|
|
842
1003
|
|
|
843
|
-
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page
|
|
1004
|
+
<div class="footer"><span>TestGuard — Technical Brief</span><span>Page 8 of 8 · Difference</span></div>
|
|
844
1005
|
</div>
|
|
845
1006
|
|
|
846
1007
|
</body>
|
|
Binary file
|
|
@@ -50,7 +50,7 @@ code were wrong, would anything fail?*
|
|
|
50
50
|
| Progress | How a running probe reports itself, per reader: tty, plain, ndjson | `src/probe/progress.mjs` |
|
|
51
51
|
| Change gate | Claim coverage of a diff | `src/gate/changed.mjs` |
|
|
52
52
|
| Sweep | The cold start: propose faults for changed files that carry no claim, probe a bounded selection, report what nothing noticed | `src/sweep/sweep.mjs` |
|
|
53
|
-
| Claim supply | Which proposals are worth probing, and why a survivor on a write path was missed | `src/supply/select.mjs`, `src/supply/persistence.mjs` |
|
|
53
|
+
| Claim supply | Which proposals are worth probing, where this project writes to storage, and why a survivor on a write path was missed | `src/supply/select.mjs`, `src/supply/savepath.mjs`, `src/supply/persistence.mjs` |
|
|
54
54
|
| Baseline | Freeze debt; gate the delta; re-stamp | `src/baseline/baseline.mjs` |
|
|
55
55
|
| Status | The single state machine every rendering derives from | `src/status/status.mjs` |
|
|
56
56
|
| Brief | The agent's session-start blind-spot block | `src/brief/brief.mjs` |
|