testguard-cli 0.13.0__tar.gz → 0.14.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.gitignore +1 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.npmignore +1 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/AGENTS.md +2 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/CHANGELOG.md +159 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/PKG-INFO +23 -10
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/README.md +22 -9
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/action.yml +1 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs/testguard-explained.html +32 -27
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs-canonical/PRIOR-ART.md +42 -16
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs-canonical/TEST-SPEC.md +2 -2
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/package-lock.json +2 -2
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/package.json +1 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/pyproject.toml +1 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/GATE-SEMANTICS.md +20 -3
- testguard_cli-0.14.1/spec/conformance/examples/evidence-persistence-signal.json +168 -0
- testguard_cli-0.14.1/spec/conformance/invalid/evidence.persistence-signal-not-a-defender.json +168 -0
- testguard_cli-0.14.1/spec/conformance/invalid/evidence.persistence-signal-on-a-non-survivor.json +139 -0
- testguard_cli-0.14.1/spec/conformance/invalid/evidence.persistence-signal-without-reason.json +167 -0
- testguard_cli-0.14.1/spec/conformance/invalid/replay.nocover-when-no-test-file-remained.json +44 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/lib/validate.mjs +20 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/evidence.schema.json +35 -2
- testguard_cli-0.14.1/spec/schemas/replay.schema.json +157 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/sweep.schema.json +5 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/probe.mjs +8 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/pyimports.mjs +9 -2
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/index.mjs +21 -2
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/replay/replay.mjs +22 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/scaffold/producers.python.mjs +74 -3
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/supply/feedback.mjs +14 -2
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/supply/persistence.mjs +20 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/supply/select.mjs +39 -6
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/sweep/sweep.mjs +30 -10
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/util/glob.mjs +5 -1
- testguard_cli-0.14.1/test/evidence-persistence-signal.test.mjs +78 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/glob.test.mjs +4 -1
- testguard_cli-0.14.1/test/probe-persistence-signal.test.mjs +107 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/pyimports.test.mjs +10 -0
- testguard_cli-0.14.1/test/replay-no-test-file-left.test.mjs +69 -0
- testguard_cli-0.14.1/test/replay-python-defenders.test.mjs +87 -0
- testguard_cli-0.14.1/test/runner-selection-blind.test.mjs +77 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/scaffold.python.test.mjs +49 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/supply-feedback.test.mjs +28 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/supply-select.test.mjs +50 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/sweep.test.mjs +27 -1
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/testguard.claims.json +400 -30
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/testguard.cost-budget.json +23 -12
- testguard_cli-0.13.0/.codex/hooks.json +0 -71
- testguard_cli-0.13.0/fixtures/known-answer-playwright/test-results/.last-run.json +0 -4
- testguard_cli-0.13.0/spec/schemas/replay.schema.json +0 -85
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.docguard.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.docguardignore +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.gitattributes +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/dependabot.yml +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/scripts/approve-held-runs.sh +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/scripts/publish-homebrew-tap.sh +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/workflows/release.yml +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/workflows/scheduled-release.yml +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.npmrc +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/CLAUDE.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/CONTRIBUTING.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/LICENSE +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/PRIVACY.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/SECURITY.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/SUPPORT.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/bench/README.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/cli/testguard.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs-canonical/ARCHITECTURE.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs-canonical/ENVIRONMENT.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs-canonical/REQUIREMENTS.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/docs-canonical/SECURITY.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/README.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/concerns.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/claims/anchors.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/claims/load.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/cli.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/baseline.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/brief.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/concerns.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/gate.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/init.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/probe.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/status.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/commands/sweep.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/gate/changed.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/git.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/init/init.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/init/templates/SKILL.md +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/mcp/tools.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/contention.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/render.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/replay/label.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/status/status.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/status/surface.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/supply/concerns.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/supply/savepath.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/src/util/hash.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/admit.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/brief.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/calibration.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/claim-selection.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/claims-anchors.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/classify.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/contention.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/cost.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/discover.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/gate.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/independence.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/init.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/inject.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/label.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/one-pager.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/probe-error.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/progress.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/release-approval.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/release-sync.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/replay-verdict.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/replay.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/samples/python-report.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/status.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/supply-concerns.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/supply-persistence.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/supply-savepath.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/surface.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/sweep.fixture.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/vitest-config.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/test/writer.test.mjs +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/testguard.ignore.json +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.13.0 → testguard_cli-0.14.1}/vitest.config.mjs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AI Agent Instructions — TestGuard
|
|
2
2
|
|
|
3
|
-
<!-- docguard:last-reviewed 2026-09-
|
|
3
|
+
<!-- docguard:last-reviewed 2026-09-22 -->
|
|
4
4
|
|
|
5
5
|
Read this before changing anything. It is short on purpose.
|
|
6
6
|
|
|
@@ -17,6 +17,7 @@ their first consumer.
|
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
npm test # unit + fixture acceptance (~15s)
|
|
20
|
+
npm ci --prefix fixtures/known-answer-playwright # once; without it the Playwright fixture acceptance silently skips
|
|
20
21
|
npm run test:spec # conformance suite only
|
|
21
22
|
npm run self:probe # TestGuard probes its own claims; must exit 0
|
|
22
23
|
npm run test:install # the packed tarball must run with production deps only
|
|
@@ -7,6 +7,165 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.14.1] - 2026-09-27
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.14.0`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- fix: four defects that made `nocover` report discovery failures as findings (#169)
|
|
17
|
+
- chore: untrack local agent tooling and a generated fixture artifact (#168)
|
|
18
|
+
- chore(homebrew): sha256 for v0.14.0 (#167)
|
|
19
|
+
- docs(brief): page 6 carries what 0.14.0 changed there (#166)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **`sweep` no longer refuses to write after a sweep that learned nothing.**
|
|
25
|
+
`learnedProductivity` named every evidence document it *read* as an ordering
|
|
26
|
+
source, while `observed` counts only `killed` and `survived` records. A
|
|
27
|
+
document whose records are all `nocover` — the ordinary shape of a first
|
|
28
|
+
sweep — therefore produced `sources: [...]` with `observed: 0`, which the
|
|
29
|
+
sweep validator correctly refuses. Because that document is written to
|
|
30
|
+
`.testguard/sweep-evidence.json`, **every later sweep failed the same way,
|
|
31
|
+
permanently**, until someone deleted a gitignored file nothing mentioned. An
|
|
32
|
+
ordering now names only the documents that contributed an observation, which
|
|
33
|
+
is what "the documents it was learned from" always meant. Measured on an
|
|
34
|
+
external corpus: this fired on 10 of 34 sweep invocations.
|
|
35
|
+
- **`replay` resolves Python importers with the Python resolver.**
|
|
36
|
+
`replay` matched test files against a reverted source file with the
|
|
37
|
+
JavaScript importer for every language. `from pkg.mod import x` carries no
|
|
38
|
+
quoted specifier, so nothing ever matched, the defender set was always empty
|
|
39
|
+
for Python, and **every Python verdict was `nocover`** — "no test imports the
|
|
40
|
+
reverted source" — however complete the suite. `discoverDefendersDetailed`
|
|
41
|
+
has always branched on the target's language; `replay` now does the same.
|
|
42
|
+
- **A runner that matches no test file is no longer selected silently.**
|
|
43
|
+
`--runner auto` tries vitest, jest, then python. A TypeScript project on
|
|
44
|
+
Playwright resolves neither JavaScript runner, so `auto` reached python
|
|
45
|
+
wherever a `python3` existed, globbed for `.py`, found none, and reported
|
|
46
|
+
every fault as `nocover`. `auto` now prefers a resolvable runner that can see
|
|
47
|
+
test files, and when none can it says so (`no runner matched a test file …
|
|
48
|
+
every verdict will be nocover`) instead of letting discovery's failure read
|
|
49
|
+
as a finding about the project. An explicitly named `--runner` is still
|
|
50
|
+
honoured; every selection now reports `testFiles`.
|
|
51
|
+
|
|
52
|
+
- **`replay` no longer reports `nocover` when it removed the only test file.**
|
|
53
|
+
Removing the fix's own test removes the whole FILE, and on a project with
|
|
54
|
+
few, large test files that also removes tests which pre-dated the fix and
|
|
55
|
+
might have caught the bug. Nothing is left to run, so nothing can be
|
|
56
|
+
concluded: the verdict is now `unverifiable` with reason
|
|
57
|
+
`the-fix-shipped-the-only-test-file`. `nocover` is a statement about the
|
|
58
|
+
PROJECT — "no test exercises this" — and it enters the calibration as a
|
|
59
|
+
miss, so the old behaviour charged a project for evidence the measurement
|
|
60
|
+
itself destroyed. The validator now refuses the mislabelled form, and
|
|
61
|
+
`GATE-SEMANTICS.md` carries it as a third rule beside de-duplication and
|
|
62
|
+
test removal.
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- **Python: a module-level dunder assignment is no longer proposed as a
|
|
67
|
+
fault.** `__version__`, `__all__` and `__author__` at module level are
|
|
68
|
+
metadata nobody writes a test for, so a survivor on one teaches the
|
|
69
|
+
survival-learned ordering to prefer a barren class — the same reasoning that
|
|
70
|
+
already sets a presentational JSX element aside. Narrow on purpose: a
|
|
71
|
+
module-level *constant* such as `DEFAULT_MAX_SIZE = 100` is still proposed,
|
|
72
|
+
because removing a real default changes a real default.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- The brief's page 6 says what 0.14.0 changed there: the evidence now names
|
|
77
|
+
the mocked layer and the assertion shape beside a save-path survivor's
|
|
78
|
+
verdict, and the fourth rule of the lower rungs — whatever a sweep does not
|
|
79
|
+
probe, it counts, presentational elements included. Re-rendered; still eight
|
|
80
|
+
sheets from eight pages.
|
|
81
|
+
|
|
82
|
+
## [0.14.0] - 2026-09-22
|
|
83
|
+
|
|
84
|
+
Automated weekly release — everything merged since `v0.13.0`.
|
|
85
|
+
|
|
86
|
+
### Changed
|
|
87
|
+
|
|
88
|
+
- fix: three defects the arid measurement found, presentational JSX set aside, and the claimspec signal bump (#164)
|
|
89
|
+
- docs: carry the 0.12 and 0.13 results into the brief and the prior-art record (#162)
|
|
90
|
+
- chore(homebrew): sha256 for v0.13.0 (#161)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
### Added
|
|
94
|
+
|
|
95
|
+
- **claimspec v1 evidence admits `persistence-payload-unasserted`** to
|
|
96
|
+
`defenders.signals`, the enum shared with every claimspec consumer. The
|
|
97
|
+
signal had lived only in the sweep document, so `probe` could not surface
|
|
98
|
+
why a save-path survivor was missed. Now a `survived` record whose fault
|
|
99
|
+
sits on the write path carries it for each resolved defender that mocks the
|
|
100
|
+
persistence layer, with the mocked `specifiers` and the file's assertion
|
|
101
|
+
`counts` (exact, partial, argument-free). The validator refuses it on any
|
|
102
|
+
other verdict, on a file that is not a resolved defender, or without its
|
|
103
|
+
reason — the same rule the sweep document already enforced on its
|
|
104
|
+
findings, and the sweep now repeats what the record says rather than
|
|
105
|
+
re-deriving it. Conformance carries a valid example and three invalid
|
|
106
|
+
cases. No consumer outside this repository validates claimspec evidence
|
|
107
|
+
today; DocGuard's adoption (docguard#420) is notified rather than broken.
|
|
108
|
+
- `sweep` sets aside a purely presentational JSX element — an icon, or a
|
|
109
|
+
static wrapper with no expression and no handler — instead of spending a
|
|
110
|
+
probe run on it, and says so: `selection.presentational` in the document, a
|
|
111
|
+
line in the report, and the remainder arithmetic now proves
|
|
112
|
+
proposed = probed + deferred + set aside. Measured on a 14-file UI diff:
|
|
113
|
+
150 of 510 proposals, 29 of 30 probe slots and 7 of 10 survivors were of
|
|
114
|
+
this shape — Google's definition of an arid node, in a syntax Google never
|
|
115
|
+
mutated. Because the ordering learns from survival, those survivors were
|
|
116
|
+
also teaching it to rank the class higher; set-aside proposals never enter
|
|
117
|
+
the evidence, so they no longer can. Google's own five arid categories were
|
|
118
|
+
measured at under 1% of proposals and are not suppressed.
|
|
119
|
+
|
|
120
|
+
### Fixed
|
|
121
|
+
|
|
122
|
+
- Python defender discovery lost the last name on an import line that ends in
|
|
123
|
+
a comment: `from pkg import cli, demo # noqa: E402` found only `cli`, and
|
|
124
|
+
`import pkg.demo # noqa` found nothing. On a real suite where `# noqa` is
|
|
125
|
+
the idiom of every src-layout test, 12 of 30 probed faults were reported
|
|
126
|
+
NOCOVER for modules whose tests import them — a gating verdict in the
|
|
127
|
+
forbidden direction. Comments are now stripped before names are read.
|
|
128
|
+
- File walking descended into `.claude/`, so a nested agent worktree under
|
|
129
|
+
`.claude/worktrees/` contributed another branch's tests as defenders of this
|
|
130
|
+
tree — files the scratch worktree cannot even contain, because the directory
|
|
131
|
+
is gitignored. `.claude` joins the tool-directory skip list, and the claim
|
|
132
|
+
that promised this now names it.
|
|
133
|
+
- The Python `statement-deleted` producer proposed on lines inside a bracket
|
|
134
|
+
opened earlier — `help="…")` inside an `add_argument(` — and none of those
|
|
135
|
+
faults compiled: 81 of 600 proposals on a real CLI (13.5%). The producer now
|
|
136
|
+
tracks bracket depth across lines (triple-quoted strings included, so a
|
|
137
|
+
docstring's parentheses cannot leave the rest of the file "inside") and
|
|
138
|
+
refuses the `pass` shapes there; dict entries and call arguments keep their
|
|
139
|
+
own producers. Re-measured: 7 of 526 (1.3%).
|
|
140
|
+
|
|
141
|
+
### Changed
|
|
142
|
+
|
|
143
|
+
- The self-probe cost budget is 1650 s, from 1440. Six new claims (85 s on
|
|
144
|
+
the Node 22 PR leg, worst 22 s, all under the 100 s per-claim ceiling) took
|
|
145
|
+
that leg from 1367 s to 1457 s, over by 17 s. The v0.13.0 release job
|
|
146
|
+
measured 1113 s from scratch, but the PR leg runs the same claims 25-30%
|
|
147
|
+
slower and is where the ceiling is checked, so 1650 restores ~13% headroom
|
|
148
|
+
over the leg that binds. The per-claim ceiling is unchanged.
|
|
149
|
+
- The technical brief carries the 0.12 and 0.13 results it was missing: the
|
|
150
|
+
whole-surface provability measurement (32 of 37 files that write are defended
|
|
151
|
+
only by tests that mock the database) replaces the sample-level number in
|
|
152
|
+
Fig. 7; the ladder's second rung is now a shipped verb with a real finding
|
|
153
|
+
(a five-line concern found an admin route whose owner/manager check can be
|
|
154
|
+
forced to `false` with no test failing) rather than a description of intent;
|
|
155
|
+
and the first rung says its ordering learns from the project's own runs.
|
|
156
|
+
Re-rendered; still eight sheets from eight pages.
|
|
157
|
+
- `docs-canonical/PRIOR-ART.md` records the arid-node measurement instead of
|
|
158
|
+
a promise: every proposal from four sweeps over two of the maintainer's
|
|
159
|
+
repositories was classified by hand against Google's five arid categories,
|
|
160
|
+
and they account for under 1% of what the producers propose. No suppressor
|
|
161
|
+
is built for them. The same pass found what IS material — presentational
|
|
162
|
+
JSX taking 29 of 30 probe slots on a UI diff, and 13.5% of Python proposals
|
|
163
|
+
that cannot compile — and lists both as open, with numbers.
|
|
164
|
+
- `docs-canonical/PRIOR-ART.md` no longer lists the feedback loop and concerns
|
|
165
|
+
as unbuilt. The two gaps that remain are restated precisely: the loop learns
|
|
166
|
+
from verdicts rather than explicit developer clicks, and concerns have no
|
|
167
|
+
model-backed producer.
|
|
168
|
+
|
|
10
169
|
## [0.13.0] - 2026-09-22
|
|
11
170
|
|
|
12
171
|
Automated weekly release — everything merged since `v0.12.0`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.1
|
|
4
4
|
Summary: Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest. Ships the Node.js CLI (requires Node.js 20+).
|
|
5
5
|
Project-URL: Homepage, https://github.com/raccioly/testguard
|
|
6
6
|
Project-URL: Documentation, https://github.com/raccioly/testguard#readme
|
|
@@ -151,9 +151,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
|
|
|
151
151
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
152
152
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
153
153
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
154
|
-
| GitHub Action | `uses: raccioly/testguard@v0.
|
|
154
|
+
| GitHub Action | `uses: raccioly/testguard@v0.14.1` — see [`action.yml`](./action.yml) |
|
|
155
155
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
156
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
156
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.14.1/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
|
|
157
157
|
|
|
158
158
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
159
159
|
less than that many days ago (`ENOVERSIONS`); install that one with
|
|
@@ -258,6 +258,10 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
|
|
|
258
258
|
static signal `mocked-never-asserted` — the cheapest blind-spot signal
|
|
259
259
|
there is, and the exact signature of one escaped bug in the field
|
|
260
260
|
reports. Silence it, visibly, with `// unasserted: <why>` above the mock.
|
|
261
|
+
Its sibling `persistence-payload-unasserted` is attached to a **survivor
|
|
262
|
+
on a write path** whose defender mocks the database: the test can prove
|
|
263
|
+
the call shape and never that the row landed, and the signal names the
|
|
264
|
+
mocked layer and the file's assertion mix (exact, partial, argument-free).
|
|
261
265
|
|
|
262
266
|
Runners: **vitest**, **jest**, **Playwright** and **Python** (`--runner
|
|
263
267
|
auto` picks the first of vitest, jest, python that resolves). A JavaScript
|
|
@@ -447,8 +451,9 @@ alarming: something was deliberately broken and not one test failed.
|
|
|
447
451
|
|
|
448
452
|
```
|
|
449
453
|
swept 13 of 14 unclaimed changed files against origin/main.
|
|
450
|
-
proposed 510 faults, probed 10 (cap 10), deferred
|
|
451
|
-
The
|
|
454
|
+
proposed 510 faults, probed 10 (cap 10), deferred 350.
|
|
455
|
+
The 350 deferred are not a verdict: raise --cap, or sweep a smaller change.
|
|
456
|
+
150 presentational elements (icons, static wrappers) set aside: nobody writes a test for those, and a survivor there would teach the ordering the wrong lesson.
|
|
452
457
|
|
|
453
458
|
9 findings — a deliberate break that no test noticed:
|
|
454
459
|
|
|
@@ -682,12 +687,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
682
687
|
# GitHub Actions
|
|
683
688
|
- uses: actions/checkout@v4
|
|
684
689
|
with: { fetch-depth: 0 }
|
|
685
|
-
- uses: raccioly/testguard@v0.
|
|
690
|
+
- uses: raccioly/testguard@v0.14.1
|
|
686
691
|
with: { command: gate }
|
|
687
692
|
|
|
688
693
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
689
694
|
include:
|
|
690
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
695
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.1/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
691
696
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
692
697
|
```
|
|
693
698
|
|
|
@@ -1002,12 +1007,20 @@ The table above is JavaScript. `scaffold` reads Python too, and proposes the
|
|
|
1002
1007
|
same fault classes in Python syntax: `if <guard>:` → `if False:`,
|
|
1003
1008
|
`return <check>` → `return True`, `verify=True` → `verify=False`, a
|
|
1004
1009
|
parameter-derived argument swapped for `None`, a key dropped from a payload
|
|
1005
|
-
`dict` or an allow-list.
|
|
1010
|
+
`dict` or an allow-list. Three differences are deliberate. A statement is
|
|
1006
1011
|
removed by replacing it with `pass`, never by deleting the line, because a
|
|
1007
1012
|
block whose only statement is gone is an `IndentationError`; and a line that
|
|
1008
|
-
leaves a bracket open (`COLOURS = {`)
|
|
1013
|
+
leaves a bracket open (`COLOURS = {`), or sits inside one opened on an earlier
|
|
1014
|
+
line (`help="…"` in an `add_argument(`), is never removed at all. Both exist
|
|
1009
1015
|
because a fault that cannot compile is a `fault-invalid` verdict — a probe run
|
|
1010
|
-
spent saying nothing about the tests.
|
|
1016
|
+
spent saying nothing about the tests. Third, a **module-level dunder
|
|
1017
|
+
assignment** (`__version__`, `__all__`, `__author__`) is not proposed. Deleting
|
|
1018
|
+
one does change behaviour, so it is not an equivalent mutant; it is something
|
|
1019
|
+
nobody writes a test for, which is the disqualification an icon already gets on
|
|
1020
|
+
the JSX side — a survivor there teaches the survival-learned ordering to prefer
|
|
1021
|
+
a barren class. The rule is narrow on purpose: a module-level *constant*
|
|
1022
|
+
(`DEFAULT_MAX_SIZE = 100`) is still proposed, because removing a real default
|
|
1023
|
+
changes a real default. The two JSX shapes (`element-removed`,
|
|
1011
1024
|
`handler-dropped`) have no Python meaning and are absent rather than faked.
|
|
1012
1025
|
|
|
1013
1026
|
Every proposal's `find` is the exact line with `expectHits`/`occurrence`
|
|
@@ -127,9 +127,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
|
|
|
127
127
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
128
128
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
129
129
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
130
|
-
| GitHub Action | `uses: raccioly/testguard@v0.
|
|
130
|
+
| GitHub Action | `uses: raccioly/testguard@v0.14.1` — see [`action.yml`](./action.yml) |
|
|
131
131
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
132
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
132
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.14.1/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
|
|
133
133
|
|
|
134
134
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
135
135
|
less than that many days ago (`ENOVERSIONS`); install that one with
|
|
@@ -234,6 +234,10 @@ npx testguard-cli admit test/x.test.ts --claim X # is this test green on HEAD
|
|
|
234
234
|
static signal `mocked-never-asserted` — the cheapest blind-spot signal
|
|
235
235
|
there is, and the exact signature of one escaped bug in the field
|
|
236
236
|
reports. Silence it, visibly, with `// unasserted: <why>` above the mock.
|
|
237
|
+
Its sibling `persistence-payload-unasserted` is attached to a **survivor
|
|
238
|
+
on a write path** whose defender mocks the database: the test can prove
|
|
239
|
+
the call shape and never that the row landed, and the signal names the
|
|
240
|
+
mocked layer and the file's assertion mix (exact, partial, argument-free).
|
|
237
241
|
|
|
238
242
|
Runners: **vitest**, **jest**, **Playwright** and **Python** (`--runner
|
|
239
243
|
auto` picks the first of vitest, jest, python that resolves). A JavaScript
|
|
@@ -423,8 +427,9 @@ alarming: something was deliberately broken and not one test failed.
|
|
|
423
427
|
|
|
424
428
|
```
|
|
425
429
|
swept 13 of 14 unclaimed changed files against origin/main.
|
|
426
|
-
proposed 510 faults, probed 10 (cap 10), deferred
|
|
427
|
-
The
|
|
430
|
+
proposed 510 faults, probed 10 (cap 10), deferred 350.
|
|
431
|
+
The 350 deferred are not a verdict: raise --cap, or sweep a smaller change.
|
|
432
|
+
150 presentational elements (icons, static wrappers) set aside: nobody writes a test for those, and a survivor there would teach the ordering the wrong lesson.
|
|
428
433
|
|
|
429
434
|
9 findings — a deliberate break that no test noticed:
|
|
430
435
|
|
|
@@ -658,12 +663,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
658
663
|
# GitHub Actions
|
|
659
664
|
- uses: actions/checkout@v4
|
|
660
665
|
with: { fetch-depth: 0 }
|
|
661
|
-
- uses: raccioly/testguard@v0.
|
|
666
|
+
- uses: raccioly/testguard@v0.14.1
|
|
662
667
|
with: { command: gate }
|
|
663
668
|
|
|
664
669
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
665
670
|
include:
|
|
666
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
671
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.1/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
667
672
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
668
673
|
```
|
|
669
674
|
|
|
@@ -978,12 +983,20 @@ The table above is JavaScript. `scaffold` reads Python too, and proposes the
|
|
|
978
983
|
same fault classes in Python syntax: `if <guard>:` → `if False:`,
|
|
979
984
|
`return <check>` → `return True`, `verify=True` → `verify=False`, a
|
|
980
985
|
parameter-derived argument swapped for `None`, a key dropped from a payload
|
|
981
|
-
`dict` or an allow-list.
|
|
986
|
+
`dict` or an allow-list. Three differences are deliberate. A statement is
|
|
982
987
|
removed by replacing it with `pass`, never by deleting the line, because a
|
|
983
988
|
block whose only statement is gone is an `IndentationError`; and a line that
|
|
984
|
-
leaves a bracket open (`COLOURS = {`)
|
|
989
|
+
leaves a bracket open (`COLOURS = {`), or sits inside one opened on an earlier
|
|
990
|
+
line (`help="…"` in an `add_argument(`), is never removed at all. Both exist
|
|
985
991
|
because a fault that cannot compile is a `fault-invalid` verdict — a probe run
|
|
986
|
-
spent saying nothing about the tests.
|
|
992
|
+
spent saying nothing about the tests. Third, a **module-level dunder
|
|
993
|
+
assignment** (`__version__`, `__all__`, `__author__`) is not proposed. Deleting
|
|
994
|
+
one does change behaviour, so it is not an equivalent mutant; it is something
|
|
995
|
+
nobody writes a test for, which is the disqualification an icon already gets on
|
|
996
|
+
the JSX side — a survivor there teaches the survival-learned ordering to prefer
|
|
997
|
+
a barren class. The rule is narrow on purpose: a module-level *constant*
|
|
998
|
+
(`DEFAULT_MAX_SIZE = 100`) is still proposed, because removing a real default
|
|
999
|
+
changes a real default. The two JSX shapes (`element-removed`,
|
|
987
1000
|
`handler-dropped`) have no Python meaning and are absent rather than faked.
|
|
988
1001
|
|
|
989
1002
|
Every proposal's `find` is the exact line with `expectHits`/`occurrence`
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
<div class="page">
|
|
145
145
|
<div class="masthead">
|
|
146
146
|
<div class="wordmark">Test<span class="mark">Guard</span></div>
|
|
147
|
-
<div class="meta">Technical Brief · v0.
|
|
147
|
+
<div class="meta">Technical Brief · v0.14.1 · MIT</div>
|
|
148
148
|
</div>
|
|
149
149
|
|
|
150
150
|
<h1>Your tests pass. That is not the same as your tests working.</h1>
|
|
@@ -678,7 +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:
|
|
681
|
+
<tr><td><code>sweep</code></td><td>The cold start: probe files with <em>no</em> claim — a diff, the write surface, or a one-line <code>concern</code>.</td></tr>
|
|
682
682
|
<tr><td class="v-good"><code>probe</code></td><td>The engine. Inject each fault, run its defenders, report what survived.</td></tr>
|
|
683
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>
|
|
684
684
|
<tr><td><code>baseline</code></td><td>Freezes today's unproven findings so only new ones gate.</td></tr>
|
|
@@ -752,10 +752,10 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
752
752
|
|
|
753
753
|
<section class="rule-top">
|
|
754
754
|
<h2>Field report C — where a save button actually fails</h2>
|
|
755
|
-
<p>Twenty-six faults
|
|
756
|
-
|
|
755
|
+
<p>Twenty-six faults on the persistence path of seven server actions in a real AI-authored
|
|
756
|
+
application: the write itself, and the fields it carries.</p>
|
|
757
757
|
<figure style="margin-top:11px">
|
|
758
|
-
<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;
|
|
758
|
+
<svg viewBox="0 0 760 150" role="img" aria-label="Save-path measurement: 12 of 26 faults survived, all of them a dropped payload field; across the whole write surface, 32 of 37 files are defended only by tests that mock the database">
|
|
759
759
|
<rect x="0" y="0" width="243" height="74" rx="4" fill="#ffffff" stroke="#e6e2d8"/>
|
|
760
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
761
|
<text class="s-sub" x="18" y="56" style="font-size:8px">survived a fully green suite</text>
|
|
@@ -767,9 +767,9 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
767
767
|
<text class="s-sub" x="276" y="68" style="font-size:8px">field — not one was anything else</text>
|
|
768
768
|
|
|
769
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:#
|
|
771
|
-
<text class="s-sub" x="534" y="56" style="font-size:8px">
|
|
772
|
-
<text class="s-sub" x="534" y="68" style="font-size:8px">
|
|
770
|
+
<text x="534" y="38" style="font-family:var(--sans);font-size:27px;font-weight:700;letter-spacing:-0.03em;fill:#b85636">32 of 37</text>
|
|
771
|
+
<text class="s-sub" x="534" y="56" style="font-size:8px">files that write are defended only</text>
|
|
772
|
+
<text class="s-sub" x="534" y="68" style="font-size:8px">by tests that mock the database</text>
|
|
773
773
|
|
|
774
774
|
<rect x="0" y="86" width="760" height="58" rx="4" fill="#fbf1ec" stroke="#e8d5cc"/>
|
|
775
775
|
<text class="s-tiny" x="16" y="104" style="fill:#b85636">The worst case — user registration</text>
|
|
@@ -778,9 +778,12 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
778
778
|
</svg>
|
|
779
779
|
<p class="figcap">Fig. 7 — The save button reports success. The suite agrees. The column is empty.</p>
|
|
780
780
|
</figure>
|
|
781
|
-
<p class="note">
|
|
782
|
-
|
|
783
|
-
|
|
781
|
+
<p class="note">The third number is the read-back answer. A test that replaced the database proves the
|
|
782
|
+
call shape and nothing beyond it — a <code>where</code> matching no rows, a rolled-back transaction, a
|
|
783
|
+
rejected constraint all pass against a mock. On this surface a clean probe is not evidence of
|
|
784
|
+
persistence; it is evidence that nothing could have measured it, and the tool says so before any verdict.
|
|
785
|
+
The evidence now names the mocked layer and the assertion shape — exact, partial or argument-free —
|
|
786
|
+
beside the survivor’s verdict.</p>
|
|
784
787
|
</section>
|
|
785
788
|
|
|
786
789
|
<section class="rule-top">
|
|
@@ -788,33 +791,33 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
788
791
|
<div class="cols-3">
|
|
789
792
|
<div>
|
|
790
793
|
<h3>Nothing written</h3>
|
|
791
|
-
<p class="note"><code>sweep</code> takes the
|
|
792
|
-
|
|
793
|
-
|
|
794
|
+
<p class="note"><code>sweep</code> takes the files the gate just called uncovered, proposes
|
|
795
|
+
faults mechanically, probes a bounded selection, and reports what nothing noticed — ordering
|
|
796
|
+
candidates by what this project’s own runs have shown survives, the feedback that took
|
|
797
|
+
Google’s productive rate from 15% to 89%.</p>
|
|
794
798
|
</div>
|
|
795
799
|
<div>
|
|
796
800
|
<h3>A sentence or ten</h3>
|
|
797
|
-
<p class="note">A <em>concern</em> — “every
|
|
798
|
-
|
|
799
|
-
|
|
801
|
+
<p class="note">A <em>concern</em> names a kind of promise and where to look. Five lines of JSON — “every admin route refuses a caller without the admin role” — found a
|
|
802
|
+
route whose owner/manager check can be forced to <code>false</code> with no test failing. One
|
|
803
|
+
sentence, twenty-four routes, no model.</p>
|
|
800
804
|
</div>
|
|
801
805
|
<div>
|
|
802
806
|
<h3>A claim</h3>
|
|
803
807
|
<p class="note">The survivors worth defending become the first claims: a statement, its faults,
|
|
804
|
-
and a reproducer already in hand
|
|
808
|
+
and a reproducer already in hand.</p>
|
|
805
809
|
</div>
|
|
806
810
|
</div>
|
|
807
|
-
<p class="note" style="margin-top:9px">
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
tool’s mistake, and a tool that fails on its own mistakes gets switched off.</p>
|
|
811
|
+
<p class="note" style="margin-top:9px">Four rules keep the lower rungs safe. A sweep <strong>never
|
|
812
|
+
writes the claims file</strong> — a sentence nobody wrote is not a claim. Its evidence <strong>never
|
|
813
|
+
replaces</strong> the canonical record. And it <strong>does not fail on its own bad guess</strong>:
|
|
814
|
+
only a survivor or an untested file exits non-zero. And whatever it does not probe, it counts —
|
|
815
|
+
past the cap, outside the concern, or presentational: an icon or a static wrapper nobody would test.</p>
|
|
813
816
|
</section>
|
|
814
817
|
|
|
815
818
|
<div class="pull">
|
|
816
|
-
<p>A sweep is weaker evidence than a probe, and says so.
|
|
817
|
-
|
|
819
|
+
<p>A sweep is weaker evidence than a probe, and says so. It is stronger than nothing,
|
|
820
|
+
which is what a repository with no claims has.</p>
|
|
818
821
|
<div class="attrib">Why the ladder has a bottom rung</div>
|
|
819
822
|
</div>
|
|
820
823
|
|
|
@@ -843,7 +846,9 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
843
846
|
A later study found reported mutants are coupled to real faults: the bugs they analysed would
|
|
844
847
|
have been caught had a mutant been surfaced.</p>
|
|
845
848
|
<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
|
|
849
|
+
selection — measured on real probed faults rather than assumed. Their arid-node suppression
|
|
850
|
+
was measured, not copied: Google's categories are under 1% of what the producers propose;
|
|
851
|
+
the arid class here is presentational JSX, now set aside and counted.</p>
|
|
847
852
|
</div>
|
|
848
853
|
<div>
|
|
849
854
|
<h3>Meta — making it specific</h3>
|
|
Binary file
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- docguard:version 1.0.0 -->
|
|
4
4
|
<!-- docguard:status approved -->
|
|
5
|
-
<!-- docguard:last-reviewed 2026-09-
|
|
5
|
+
<!-- docguard:last-reviewed 2026-09-22 -->
|
|
6
6
|
<!-- docguard:owner @raccioly -->
|
|
7
7
|
<!-- docguard:quality passive-voice off — the subject of this document is what was taken from other people's work. "Diff scoping was taken from Google" is the true sentence; rewriting every one to name us as the actor would put the borrower in the foreground of a document about the lender. -->
|
|
8
8
|
<!-- docguard:quality negation-load off — half the value here is what was deliberately NOT taken: not their mutation score, not their equivalence auto-drop, not a model judging a verdict. A record of borrowings that only lists borrowings is the half that misleads. -->
|
|
@@ -49,14 +49,16 @@ numbers are the reason several of our defaults are what they are.
|
|
|
49
49
|
| **Diff scoping** — mutate only changed lines, surfaced during code review. | `gate --changed`, and `sweep`, which proposes only for changed files. |
|
|
50
50
|
| **A hard cap on what is surfaced** — at most 7 × the number of files in the change, because past that the reader stops reading. | `capFor()` in `src/supply/select.mjs`; the remainder is reported as `deferred`, never dropped. |
|
|
51
51
|
| **Productivity-ordered selection** — order candidates by how often that operator produced a useful mutant in similar context. Developer feedback over six years took their productive rate from **15% to 89%**. | `MEASURED_PRODUCTIVITY` and `scoreOf()` in `src/supply/select.mjs`. Ours is measured on 56 probed faults from a real AI-authored codebase and shrunk toward a neutral prior, so one observation cannot dominate. |
|
|
52
|
-
| **Arid-node suppression** — do not mutate code nobody would write a test for (logging, timeouts, config flags). |
|
|
52
|
+
| **Arid-node suppression** — do not mutate code nobody would write a test for (logging, timeouts, config flags). | **Measured, not built.** On 2026-09-22 every fault the producers proposed on two of the maintainer's repositories (`sweep --changed` and `sweep --save-paths`, cap 30, 510 + 581 proposals on a Next.js app and 600 on a Python CLI) was classified by hand against Google's five categories — logging, time/sleep/deadline, config flag, memoisation, monitoring counter. They account for **2 of 510, 0 of 581 and about 4 of 600** proposals: under 1% everywhere, against the ~10% that would have justified a suppressor. The reason is structural: the producers are already shape-targeted (a guard, a payload field, a security literal), so they rarely land on a log line in the first place. What the same measurement found instead is recorded under *Known gaps*. |
|
|
53
53
|
| **Fault coupling as the justification** — their 2021 study found reported mutants are coupled to real faults: bugs they analysed would have been caught had a mutant been surfaced. | The premise behind `replay`, which calibrates fault classes against real fix commits rather than assuming the coupling. |
|
|
54
54
|
|
|
55
55
|
**Not taken.** Their mutation score, their exhaustive per-line generation, and
|
|
56
56
|
their AST-based arid heuristics (ours is a different scale and a different
|
|
57
|
-
language surface). Their selection learns
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
language surface). Their selection learns from explicit developer feedback;
|
|
58
|
+
**ours learns from the project's own probed verdicts** (v0.13.0) — every
|
|
59
|
+
evidence record already carries a fault class and a verdict, so the feedback
|
|
60
|
+
is a tally rather than a thing to collect. The combination is a Beta-Binomial
|
|
61
|
+
posterior mean, which is strictly proper; see the Laya section.
|
|
60
62
|
|
|
61
63
|
## Meta — making it specific
|
|
62
64
|
|
|
@@ -75,7 +77,7 @@ concern, and every step is gated by execution.
|
|
|
75
77
|
|
|
76
78
|
| Taken | Where it lives here |
|
|
77
79
|
|---|---|
|
|
78
|
-
| **The concern as the unit a human writes.** One plain-text sentence generates faults across a whole surface. |
|
|
80
|
+
| **The concern as the unit a human writes.** One plain-text sentence generates faults across a whole surface. | `testguard.concerns.json` and `sweep --concern` (v0.13.0). Taken WITHOUT the generator: a concern's core is a named scope plus a producer selection, which the mechanical producers already satisfy, so it ships offline. On a real application a five-line concern found an admin route whose owner/manager check can be forced to `false` with no test failing. |
|
|
79
81
|
| **An execution gate between every model step and the record.** A drafted fault must build, must survive the existing suite, and must not be equivalent before a test is generated for it; the generated test must build, pass on the original N times, and kill the fault. | The rule that nothing a model proposes enters `testguard.claims.json` without passing `probe` and a human keep — already how `scaffold` drafts work. |
|
|
80
82
|
| **Show the reviewer the fault as proof.** The engineer sees the concrete fault the test catches, so the model's output is independently checkable rather than self-referential. | Every finding names its fault, its anchor and its reproducer; `admit` reports which fault a candidate test kills. |
|
|
81
83
|
| **Learned, realistic faults rather than syntactic ones** (Beller 2021: operators learned from a corpus of real Java errors and from changes that caused operational anomalies; **>50% of 15,000 generated mutants survived** their suite). | The premise behind `replay`'s fault-class calibration, and behind the planned intent-based producer. Our built-in producers remain deterministic and line-oriented. |
|
|
@@ -148,13 +150,37 @@ difference between a metric and an audit.
|
|
|
148
150
|
|
|
149
151
|
Recorded so they are not mistaken for decisions.
|
|
150
152
|
|
|
151
|
-
- **The feedback loop
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
- **
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
- **The feedback loop learns from verdicts, not from explicit developer
|
|
154
|
+
clicks.** Google's gain came from "Please fix" / "Not useful" feedback on
|
|
155
|
+
surfaced mutants. Ours reads survival per fault class from the project's own
|
|
156
|
+
evidence — the same signal, but it cannot yet distinguish a survivor the
|
|
157
|
+
developer *acted on* from one they ignored. A keep/drop record would close
|
|
158
|
+
that.
|
|
159
|
+
- **Concerns have no model-backed producer.** The unit shipped; the ACH
|
|
160
|
+
generator that drafts intent-level faults for a concern did not, and is the
|
|
161
|
+
one piece of the rung that would touch the network.
|
|
162
|
+
- **No arid-node heuristic, and the measurement says none is needed for
|
|
163
|
+
Google's categories** (under 1% of proposals; see the Google table). Two
|
|
164
|
+
different things ARE material, measured on the same runs:
|
|
165
|
+
- **Presentational JSX.** On a 14-file UI diff, 150 of 510 proposals (29%)
|
|
166
|
+
were `element-removed` on an icon (`<X />`, `<ChevronLeft />`) or a static
|
|
167
|
+
wrapper (`<div>`, `<span>`, `<p>` with no expression and no handler). The
|
|
168
|
+
ranker spent 29 of 30 probe slots on that class and 7 of the 10 survivors
|
|
169
|
+
were of this shape — findings nobody will write a test for, which is
|
|
170
|
+
Google's definition of arid, in a syntax Google never mutated. Because the
|
|
171
|
+
ordering learns from survival, these survivors also teach it to rank the
|
|
172
|
+
class higher. A line-oriented demotion is designed; it is not built.
|
|
173
|
+
- **Python continuation lines.** 81 of 600 proposals on a Python CLI
|
|
174
|
+
(13.5%) did not compile, 77 of them a `statement-deleted` on a line that
|
|
175
|
+
sits inside a bracket opened on an earlier line. `isIncomplete()` in
|
|
176
|
+
`src/scaffold/producers.python.mjs` checks only whether the line itself
|
|
177
|
+
leaves a bracket open. Measured with the tokenizer's exact depth, a
|
|
178
|
+
running-depth guard on that class catches 74 of the 77 and drops no
|
|
179
|
+
valid proposal. Not arid — a producer that spends probe runs saying
|
|
180
|
+
nothing — but found by the same pass.
|
|
181
|
+
- **The persistence signal reached the evidence on 2026-09-22**
|
|
182
|
+
(`persistence-payload-unasserted` in `evidence.defenders.signals`, a closed
|
|
183
|
+
enum in the shared claimspec contract). No tool outside this repository
|
|
184
|
+
validated claimspec evidence at the time; DocGuard's adoption (docguard#420)
|
|
185
|
+
was notified rather than broken. Listed here because the enum is shared:
|
|
186
|
+
the next addition needs the same notice.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- docguard:version 1.0.0 -->
|
|
4
4
|
<!-- docguard:status approved -->
|
|
5
|
-
<!-- docguard:last-reviewed 2026-09-
|
|
5
|
+
<!-- docguard:last-reviewed 2026-09-22 -->
|
|
6
6
|
<!-- docguard:owner @raccioly -->
|
|
7
7
|
<!-- docguard:quality negation-load off — the governing rule is a prohibition (a test that would pass with the behaviour broken is not a test), and the verdict rules are defined by what does not count as detection. -->
|
|
8
8
|
|
|
@@ -20,7 +20,7 @@ the test that defends it, and CI fails if any injected fault survives.
|
|
|
20
20
|
| Unit | `test/*.test.mjs` | One pure function's branches, without a runner | milliseconds |
|
|
21
21
|
| Conformance | `spec/conformance/` | Every valid example validates; every must-reject document is rejected for the defect its filename names | ~1 s |
|
|
22
22
|
| Anchor preflight | `test/claims-anchors.test.mjs` | Exact anchors still locate, supported replacements parse, the check stays below one second, and CI reaches it before self-probe | <1 s |
|
|
23
|
-
| Fixture acceptance | `test/probe.fixture.test.mjs`, `test/probe.jest.test.mjs`, `test/probe.playwright.test.mjs` | The whole pipeline reproduces a known-answer oracle, one per runner | 10–50 s each |
|
|
23
|
+
| Fixture acceptance | `test/probe.fixture.test.mjs`, `test/probe.jest.test.mjs`, `test/probe.playwright.test.mjs` | The whole pipeline reproduces a known-answer oracle, one per runner. The Playwright fixture carries its own dependencies: without `npm ci --prefix fixtures/known-answer-playwright` that file skips, and a skipped acceptance is not a passing one | 10–50 s each |
|
|
24
24
|
| Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **20.2 min** cold (measured by the v0.10.1 release job on 2026-09-19 at `70e9d61`: 154 faults, 924 defender runs, 1210 s); seconds when verdicts are reused, which is why PR CI restores previous evidence from cache. Budgeted at 1440 s in `testguard.cost-budget.json` for the additional four-fault live-tap claim and **gated** in CI — cost growth is reviewed instead of disappearing into a passing log |
|
|
25
25
|
| Install smoke | `.github/scripts/install-smoke.mjs` | The **packed tarball** runs with production dependencies only | ~20 s |
|
|
26
26
|
| Runtime budget | `ci.yml` | The suite has not silently started walking the wrong tree | gate, not a test |
|