testguard-cli 0.12.0__tar.gz → 0.14.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.12.0 → testguard_cli-0.14.0}/CHANGELOG.md +144 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/PKG-INFO +96 -12
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/README.md +95 -11
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/action.yml +1 -1
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs/testguard-explained.html +26 -23
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs-canonical/ARCHITECTURE.md +2 -1
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs-canonical/PRIOR-ART.md +42 -16
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/package-lock.json +2 -2
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/package.json +1 -1
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/pyproject.toml +1 -1
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/GATE-SEMANTICS.md +8 -0
- testguard_cli-0.14.0/spec/conformance/examples/evidence-persistence-signal.json +168 -0
- testguard_cli-0.14.0/spec/conformance/invalid/evidence.persistence-signal-not-a-defender.json +168 -0
- testguard_cli-0.14.0/spec/conformance/invalid/evidence.persistence-signal-on-a-non-survivor.json +139 -0
- testguard_cli-0.14.0/spec/conformance/invalid/evidence.persistence-signal-without-reason.json +167 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/lib/validate.mjs +50 -3
- testguard_cli-0.14.0/spec/schemas/concerns.schema.json +41 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/evidence.schema.json +35 -2
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/sweep.schema.json +61 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/cli.mjs +7 -1
- testguard_cli-0.14.0/src/commands/concerns.mjs +32 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/sweep.mjs +52 -23
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/probe.mjs +8 -1
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/pyimports.mjs +9 -2
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/scaffold/producers.python.mjs +54 -2
- testguard_cli-0.14.0/src/supply/concerns.mjs +162 -0
- testguard_cli-0.14.0/src/supply/feedback.mjs +146 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/supply/persistence.mjs +73 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/supply/select.mjs +39 -6
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/sweep/sweep.mjs +114 -18
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/util/glob.mjs +5 -1
- testguard_cli-0.14.0/test/evidence-persistence-signal.test.mjs +78 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/glob.test.mjs +4 -1
- testguard_cli-0.14.0/test/probe-persistence-signal.test.mjs +107 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/pyimports.test.mjs +10 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/scaffold.python.test.mjs +16 -0
- testguard_cli-0.14.0/test/supply-concerns.test.mjs +124 -0
- testguard_cli-0.14.0/test/supply-feedback.test.mjs +134 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/supply-persistence.test.mjs +41 -1
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/supply-select.test.mjs +50 -1
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/sweep.test.mjs +56 -2
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/testguard.claims.json +345 -5
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/testguard.cost-budget.json +23 -12
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.codex/hooks.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.docguard.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.docguardignore +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.gitattributes +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/dependabot.yml +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/scripts/approve-held-runs.sh +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/scripts/publish-homebrew-tap.sh +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/workflows/release.yml +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/workflows/scheduled-release.yml +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.gitignore +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.npmignore +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.npmrc +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/AGENTS.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/CLAUDE.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/CONTRIBUTING.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/LICENSE +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/PRIVACY.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/SECURITY.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/SUPPORT.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/bench/README.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/cli/testguard.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs-canonical/ENVIRONMENT.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs-canonical/REQUIREMENTS.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs-canonical/SECURITY.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/docs-canonical/TEST-SPEC.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/README.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/replay.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/claims/anchors.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/claims/load.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/baseline.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/brief.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/gate.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/init.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/probe.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/commands/status.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/gate/changed.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/git.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/init/init.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/init/templates/SKILL.md +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/mcp/tools.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/contention.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/index.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/render.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/replay/label.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/replay/replay.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/status/status.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/status/surface.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/supply/savepath.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/src/util/hash.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/admit.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/brief.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/calibration.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/claim-selection.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/claims-anchors.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/classify.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/contention.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/cost.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/discover.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/gate.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/independence.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/init.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/inject.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/label.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/one-pager.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/probe-error.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/progress.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/release-approval.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/release-sync.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/replay-verdict.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/replay.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/samples/python-report.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/status.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/supply-savepath.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/surface.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/sweep.fixture.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/vitest-config.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/test/writer.test.mjs +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/testguard.ignore.json +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.12.0 → testguard_cli-0.14.0}/vitest.config.mjs +0 -0
|
@@ -7,6 +7,103 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.14.0] - 2026-09-22
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.13.0`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- fix: three defects the arid measurement found, presentational JSX set aside, and the claimspec signal bump (#164)
|
|
17
|
+
- docs: carry the 0.12 and 0.13 results into the brief and the prior-art record (#162)
|
|
18
|
+
- chore(homebrew): sha256 for v0.13.0 (#161)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **claimspec v1 evidence admits `persistence-payload-unasserted`** to
|
|
24
|
+
`defenders.signals`, the enum shared with every claimspec consumer. The
|
|
25
|
+
signal had lived only in the sweep document, so `probe` could not surface
|
|
26
|
+
why a save-path survivor was missed. Now a `survived` record whose fault
|
|
27
|
+
sits on the write path carries it for each resolved defender that mocks the
|
|
28
|
+
persistence layer, with the mocked `specifiers` and the file's assertion
|
|
29
|
+
`counts` (exact, partial, argument-free). The validator refuses it on any
|
|
30
|
+
other verdict, on a file that is not a resolved defender, or without its
|
|
31
|
+
reason — the same rule the sweep document already enforced on its
|
|
32
|
+
findings, and the sweep now repeats what the record says rather than
|
|
33
|
+
re-deriving it. Conformance carries a valid example and three invalid
|
|
34
|
+
cases. No consumer outside this repository validates claimspec evidence
|
|
35
|
+
today; DocGuard's adoption (docguard#420) is notified rather than broken.
|
|
36
|
+
- `sweep` sets aside a purely presentational JSX element — an icon, or a
|
|
37
|
+
static wrapper with no expression and no handler — instead of spending a
|
|
38
|
+
probe run on it, and says so: `selection.presentational` in the document, a
|
|
39
|
+
line in the report, and the remainder arithmetic now proves
|
|
40
|
+
proposed = probed + deferred + set aside. Measured on a 14-file UI diff:
|
|
41
|
+
150 of 510 proposals, 29 of 30 probe slots and 7 of 10 survivors were of
|
|
42
|
+
this shape — Google's definition of an arid node, in a syntax Google never
|
|
43
|
+
mutated. Because the ordering learns from survival, those survivors were
|
|
44
|
+
also teaching it to rank the class higher; set-aside proposals never enter
|
|
45
|
+
the evidence, so they no longer can. Google's own five arid categories were
|
|
46
|
+
measured at under 1% of proposals and are not suppressed.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Python defender discovery lost the last name on an import line that ends in
|
|
51
|
+
a comment: `from pkg import cli, demo # noqa: E402` found only `cli`, and
|
|
52
|
+
`import pkg.demo # noqa` found nothing. On a real suite where `# noqa` is
|
|
53
|
+
the idiom of every src-layout test, 12 of 30 probed faults were reported
|
|
54
|
+
NOCOVER for modules whose tests import them — a gating verdict in the
|
|
55
|
+
forbidden direction. Comments are now stripped before names are read.
|
|
56
|
+
- File walking descended into `.claude/`, so a nested agent worktree under
|
|
57
|
+
`.claude/worktrees/` contributed another branch's tests as defenders of this
|
|
58
|
+
tree — files the scratch worktree cannot even contain, because the directory
|
|
59
|
+
is gitignored. `.claude` joins the tool-directory skip list, and the claim
|
|
60
|
+
that promised this now names it.
|
|
61
|
+
- The Python `statement-deleted` producer proposed on lines inside a bracket
|
|
62
|
+
opened earlier — `help="…")` inside an `add_argument(` — and none of those
|
|
63
|
+
faults compiled: 81 of 600 proposals on a real CLI (13.5%). The producer now
|
|
64
|
+
tracks bracket depth across lines (triple-quoted strings included, so a
|
|
65
|
+
docstring's parentheses cannot leave the rest of the file "inside") and
|
|
66
|
+
refuses the `pass` shapes there; dict entries and call arguments keep their
|
|
67
|
+
own producers. Re-measured: 7 of 526 (1.3%).
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
|
|
71
|
+
- The self-probe cost budget is 1650 s, from 1440. Six new claims (85 s on
|
|
72
|
+
the Node 22 PR leg, worst 22 s, all under the 100 s per-claim ceiling) took
|
|
73
|
+
that leg from 1367 s to 1457 s, over by 17 s. The v0.13.0 release job
|
|
74
|
+
measured 1113 s from scratch, but the PR leg runs the same claims 25-30%
|
|
75
|
+
slower and is where the ceiling is checked, so 1650 restores ~13% headroom
|
|
76
|
+
over the leg that binds. The per-claim ceiling is unchanged.
|
|
77
|
+
- The technical brief carries the 0.12 and 0.13 results it was missing: the
|
|
78
|
+
whole-surface provability measurement (32 of 37 files that write are defended
|
|
79
|
+
only by tests that mock the database) replaces the sample-level number in
|
|
80
|
+
Fig. 7; the ladder's second rung is now a shipped verb with a real finding
|
|
81
|
+
(a five-line concern found an admin route whose owner/manager check can be
|
|
82
|
+
forced to `false` with no test failing) rather than a description of intent;
|
|
83
|
+
and the first rung says its ordering learns from the project's own runs.
|
|
84
|
+
Re-rendered; still eight sheets from eight pages.
|
|
85
|
+
- `docs-canonical/PRIOR-ART.md` records the arid-node measurement instead of
|
|
86
|
+
a promise: every proposal from four sweeps over two of the maintainer's
|
|
87
|
+
repositories was classified by hand against Google's five arid categories,
|
|
88
|
+
and they account for under 1% of what the producers propose. No suppressor
|
|
89
|
+
is built for them. The same pass found what IS material — presentational
|
|
90
|
+
JSX taking 29 of 30 probe slots on a UI diff, and 13.5% of Python proposals
|
|
91
|
+
that cannot compile — and lists both as open, with numbers.
|
|
92
|
+
- `docs-canonical/PRIOR-ART.md` no longer lists the feedback loop and concerns
|
|
93
|
+
as unbuilt. The two gaps that remain are restated precisely: the loop learns
|
|
94
|
+
from verdicts rather than explicit developer clicks, and concerns have no
|
|
95
|
+
model-backed producer.
|
|
96
|
+
|
|
97
|
+
## [0.13.0] - 2026-09-22
|
|
98
|
+
|
|
99
|
+
Automated weekly release — everything merged since `v0.12.0`.
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
- chore(homebrew): sha256 for v0.12.0 (#159)
|
|
104
|
+
- feat: persistence provability, and an ordering that learns from this project's own verdicts (#157)
|
|
105
|
+
|
|
106
|
+
|
|
10
107
|
## [0.12.0] - 2026-09-22
|
|
11
108
|
|
|
12
109
|
Automated weekly release — everything merged since `v0.11.0`.
|
|
@@ -18,6 +115,53 @@ Automated weekly release — everything merged since `v0.11.0`.
|
|
|
18
115
|
- chore(homebrew): sha256 for v0.11.0 (#153)
|
|
19
116
|
|
|
20
117
|
|
|
118
|
+
### Added
|
|
119
|
+
|
|
120
|
+
- **Concerns** — one sentence a human writes that generates faults across a
|
|
121
|
+
whole surface, the rung between writing nothing and writing a claim per
|
|
122
|
+
behaviour. A concern says *where* to look and *which shapes of break* are
|
|
123
|
+
relevant: `"every write persists the data it was given"` targets the write
|
|
124
|
+
sites and applies the payload producers. `testguard concerns` lists them,
|
|
125
|
+
`sweep --concern <ID>` aims a sweep by one, and `--save-paths` is now sugar
|
|
126
|
+
for the built-in `SAVE-PERSISTS`.
|
|
127
|
+
- Two built-ins ship, and only two: an authorization concern would need a
|
|
128
|
+
heuristic for "which files check permissions", and a guess there is the
|
|
129
|
+
non-actionable noise that gets a check switched off. A project that knows
|
|
130
|
+
its own auth layer says so with a `glob` target in three lines.
|
|
131
|
+
- A project concern with the same id **replaces** a built-in, so a team can
|
|
132
|
+
retune `SAVE-PERSISTS` for an ORM this tool does not recognise without
|
|
133
|
+
forking anything. The replacement is reported, never silent.
|
|
134
|
+
- `urn:claimspec:v1:concerns`, with rules that refuse a glob target naming no
|
|
135
|
+
globs (it would match nothing and report clean) and an empty fault-class
|
|
136
|
+
list (a typo for "every class", every time).
|
|
137
|
+
- **No model is required, deliberately.** A concern's useful core is a named
|
|
138
|
+
scope plus a producer selection, which the mechanical producers already
|
|
139
|
+
satisfy — so every verifying command stays offline and NFR-01 is unchanged.
|
|
140
|
+
An LLM can later propose faults a line-oriented producer cannot see, as an
|
|
141
|
+
additional source for a concern that already works.
|
|
142
|
+
|
|
143
|
+
### Added
|
|
144
|
+
|
|
145
|
+
- A save-path sweep now reports **what its defenders could prove**, before it
|
|
146
|
+
reports what they did: per file, whether every defender mocks the persistence
|
|
147
|
+
layer (call shape only), at least one does not (a real write is possible), or
|
|
148
|
+
there is no defender at all. When nothing is unmocked the report says so
|
|
149
|
+
outright — a clean probe there is not evidence of persistence, it is evidence
|
|
150
|
+
that nothing could have measured it. Measured on a real AI-authored
|
|
151
|
+
application: of 37 files that write to storage, 32 were defended only by
|
|
152
|
+
mocking tests.
|
|
153
|
+
- `scope.provability` on the sweep document, with a validator rule that every
|
|
154
|
+
target is classified exactly once.
|
|
155
|
+
- The candidate ordering now **learns from this project's own probed verdicts**
|
|
156
|
+
instead of only the shipped productivity prior. Every evidence record already
|
|
157
|
+
carries a `faultClass` and a `verdict`, so "how often does a fault of this
|
|
158
|
+
class survive *here*" is a tally rather than a new thing to capture. A sweep
|
|
159
|
+
persists its own evidence to `.testguard/sweep-evidence.json` — beside the
|
|
160
|
+
canonical one, never replacing it — so the next run learns from
|
|
161
|
+
machine-proposed faults, the same distribution the ranker orders. The sweep
|
|
162
|
+
document records `ordering.observed` and `ordering.sources`, because an
|
|
163
|
+
ordering nobody can trace is a number nobody should trust.
|
|
164
|
+
|
|
21
165
|
### Added
|
|
22
166
|
|
|
23
167
|
- `sweep --save-paths` — sweep every file that writes to storage instead of the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.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.14.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.14.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
|
|
@@ -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
|
|
|
@@ -475,6 +480,14 @@ Three rules make it safe to run on a repository that has never seen this tool:
|
|
|
475
480
|
is reported and never gates — a tool that fails because its own guess was bad
|
|
476
481
|
is a tool people switch off.
|
|
477
482
|
|
|
483
|
+
The ordering **learns from your own runs**. Every evidence record already
|
|
484
|
+
carries a fault class and a verdict, so "how often does a fault of this class
|
|
485
|
+
survive here" is a tally, not a new thing to collect — a sweep persists its
|
|
486
|
+
evidence and the next one reads it. A project with no evidence falls back to
|
|
487
|
+
the shipped prior and says so; one with its own records overrides it in
|
|
488
|
+
proportion to how many it has. The report names which, because an ordering
|
|
489
|
+
nobody can trace is a number nobody should trust.
|
|
490
|
+
|
|
478
491
|
The cap and the ordering are Google's: their mutation service surfaces at most
|
|
479
492
|
7 × |files| mutants per change and orders candidates on the measured
|
|
480
493
|
productivity of their operator in similar context, which took their productive
|
|
@@ -490,6 +503,60 @@ A sweep is weaker evidence than a probe, and says so. Nobody stated that the
|
|
|
490
503
|
behaviour mattered. It is stronger than nothing, which is what a repository
|
|
491
504
|
with no claims has — and the survivors worth defending become the first claims.
|
|
492
505
|
|
|
506
|
+
#### Concerns: one sentence, two hundred screens
|
|
507
|
+
|
|
508
|
+
A claim names one promise precisely, which is why a repository with two hundred
|
|
509
|
+
screens never finishes writing them. A **concern** names a *kind* of promise and
|
|
510
|
+
says where to look for it:
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
npx testguard-cli concerns # what this project can be swept by
|
|
514
|
+
npx testguard-cli sweep --concern ADMIN-GUARDS # aim a sweep at one
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
```json
|
|
518
|
+
{
|
|
519
|
+
"schemaVersion": 1,
|
|
520
|
+
"concerns": [
|
|
521
|
+
{
|
|
522
|
+
"id": "ADMIN-GUARDS",
|
|
523
|
+
"statement": "Every admin route refuses a caller without the admin role.",
|
|
524
|
+
"severity": "critical",
|
|
525
|
+
"targets": { "kind": "glob", "globs": ["src/app/api/admin/**"] },
|
|
526
|
+
"faultClasses": ["guard-removed", "condition-forced", "return-altered"]
|
|
527
|
+
}
|
|
528
|
+
]
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
Five lines, and the sweep reports:
|
|
533
|
+
|
|
534
|
+
```
|
|
535
|
+
concern ADMIN-GUARDS matches 24 files. Swept 23.
|
|
536
|
+
158 further proposals were outside this concern's fault classes.
|
|
537
|
+
|
|
538
|
+
SURVIVED src/app/api/admin/claims/[id]/route.ts
|
|
539
|
+
[line 41] Guard never triggers: `if (!membership || (membership.role !== 'owner'
|
|
540
|
+
&& membership.role !== 'manager'))` becomes `if (false)`.
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
The idea is Meta's: ACH has an engineer describe an area of concern in plain
|
|
544
|
+
text and gates every generated step by execution. What is taken here is the
|
|
545
|
+
**unit** — the concern as the thing a human writes — not their generator, and
|
|
546
|
+
**no model is required**. A concern's useful core is a named scope plus a
|
|
547
|
+
producer selection, which the mechanical producers already satisfy, so every
|
|
548
|
+
verifying command stays offline.
|
|
549
|
+
|
|
550
|
+
Two concerns ship built in, and only two. An authorization concern would need a
|
|
551
|
+
heuristic for *"which files check permissions"*, and a guess there is exactly
|
|
552
|
+
the noise that gets a check switched off — so `SAVE-PERSISTS` and
|
|
553
|
+
`CHANGED-CODE` are the defaults, and a project that knows its own auth layer
|
|
554
|
+
says so in three lines. Declaring a concern with a built-in's id replaces it,
|
|
555
|
+
and the replacement is reported rather than silent.
|
|
556
|
+
|
|
557
|
+
A concern is **not** a claim and never becomes one by itself. It says where to
|
|
558
|
+
look; the probe says what it found; a human states the sentence worth defending.
|
|
559
|
+
|
|
493
560
|
#### Sweeping the save surface instead of the diff
|
|
494
561
|
|
|
495
562
|
A diff cannot answer *"when the user clicks save, does it actually save?"* — it
|
|
@@ -518,10 +585,26 @@ denominator comes first and the document carries `writeSites` and
|
|
|
518
585
|
AI-authored application: **12 of 26 probed persistence faults survived a fully
|
|
519
586
|
green suite, and all 12 were a dropped payload field.**
|
|
520
587
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
588
|
+
It also states, before any verdict, **what the defenders could prove at all**:
|
|
589
|
+
|
|
590
|
+
```
|
|
591
|
+
of those 37 files: 32 defended only by tests that mock the persistence layer,
|
|
592
|
+
1 with an unmocked defender, 4 with no defender at all.
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
That line is the read-back answer. A test that replaced the database can prove
|
|
596
|
+
the *call shape* and nothing beyond it — a `where` that matches no rows, a
|
|
597
|
+
rolled-back transaction and a rejected constraint all pass against a mock that
|
|
598
|
+
recorded the arguments and returned a plausible object. So rather than pretend
|
|
599
|
+
to measure persistence, TestGuard states the limit, the same way `nocover` says
|
|
600
|
+
"no test imports this" instead of guessing.
|
|
601
|
+
|
|
602
|
+
When every defender mocks the layer, the report says so outright: *nothing in
|
|
603
|
+
this suite can prove a write reached storage*. A clean probe there is not
|
|
604
|
+
evidence of persistence — it is evidence that nothing could have measured it.
|
|
605
|
+
|
|
606
|
+
`unmocked` is deliberately weaker than "proves persistence": a test that does
|
|
607
|
+
not replace the database may simply never reach it. Possible is the honest word.
|
|
525
608
|
|
|
526
609
|
### Every change needs a claim
|
|
527
610
|
|
|
@@ -604,12 +687,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
604
687
|
# GitHub Actions
|
|
605
688
|
- uses: actions/checkout@v4
|
|
606
689
|
with: { fetch-depth: 0 }
|
|
607
|
-
- uses: raccioly/testguard@v0.
|
|
690
|
+
- uses: raccioly/testguard@v0.14.0
|
|
608
691
|
with: { command: gate }
|
|
609
692
|
|
|
610
693
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
611
694
|
include:
|
|
612
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
695
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.0/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
613
696
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
614
697
|
```
|
|
615
698
|
|
|
@@ -927,7 +1010,8 @@ parameter-derived argument swapped for `None`, a key dropped from a payload
|
|
|
927
1010
|
`dict` or an allow-list. Two differences are deliberate. A statement is
|
|
928
1011
|
removed by replacing it with `pass`, never by deleting the line, because a
|
|
929
1012
|
block whose only statement is gone is an `IndentationError`; and a line that
|
|
930
|
-
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
|
|
931
1015
|
because a fault that cannot compile is a `fault-invalid` verdict — a probe run
|
|
932
1016
|
spent saying nothing about the tests. The two JSX shapes (`element-removed`,
|
|
933
1017
|
`handler-dropped`) have no Python meaning and are absent rather than faked.
|
|
@@ -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.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.14.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
|
|
@@ -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
|
|
|
@@ -451,6 +456,14 @@ Three rules make it safe to run on a repository that has never seen this tool:
|
|
|
451
456
|
is reported and never gates — a tool that fails because its own guess was bad
|
|
452
457
|
is a tool people switch off.
|
|
453
458
|
|
|
459
|
+
The ordering **learns from your own runs**. Every evidence record already
|
|
460
|
+
carries a fault class and a verdict, so "how often does a fault of this class
|
|
461
|
+
survive here" is a tally, not a new thing to collect — a sweep persists its
|
|
462
|
+
evidence and the next one reads it. A project with no evidence falls back to
|
|
463
|
+
the shipped prior and says so; one with its own records overrides it in
|
|
464
|
+
proportion to how many it has. The report names which, because an ordering
|
|
465
|
+
nobody can trace is a number nobody should trust.
|
|
466
|
+
|
|
454
467
|
The cap and the ordering are Google's: their mutation service surfaces at most
|
|
455
468
|
7 × |files| mutants per change and orders candidates on the measured
|
|
456
469
|
productivity of their operator in similar context, which took their productive
|
|
@@ -466,6 +479,60 @@ A sweep is weaker evidence than a probe, and says so. Nobody stated that the
|
|
|
466
479
|
behaviour mattered. It is stronger than nothing, which is what a repository
|
|
467
480
|
with no claims has — and the survivors worth defending become the first claims.
|
|
468
481
|
|
|
482
|
+
#### Concerns: one sentence, two hundred screens
|
|
483
|
+
|
|
484
|
+
A claim names one promise precisely, which is why a repository with two hundred
|
|
485
|
+
screens never finishes writing them. A **concern** names a *kind* of promise and
|
|
486
|
+
says where to look for it:
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
npx testguard-cli concerns # what this project can be swept by
|
|
490
|
+
npx testguard-cli sweep --concern ADMIN-GUARDS # aim a sweep at one
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
```json
|
|
494
|
+
{
|
|
495
|
+
"schemaVersion": 1,
|
|
496
|
+
"concerns": [
|
|
497
|
+
{
|
|
498
|
+
"id": "ADMIN-GUARDS",
|
|
499
|
+
"statement": "Every admin route refuses a caller without the admin role.",
|
|
500
|
+
"severity": "critical",
|
|
501
|
+
"targets": { "kind": "glob", "globs": ["src/app/api/admin/**"] },
|
|
502
|
+
"faultClasses": ["guard-removed", "condition-forced", "return-altered"]
|
|
503
|
+
}
|
|
504
|
+
]
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Five lines, and the sweep reports:
|
|
509
|
+
|
|
510
|
+
```
|
|
511
|
+
concern ADMIN-GUARDS matches 24 files. Swept 23.
|
|
512
|
+
158 further proposals were outside this concern's fault classes.
|
|
513
|
+
|
|
514
|
+
SURVIVED src/app/api/admin/claims/[id]/route.ts
|
|
515
|
+
[line 41] Guard never triggers: `if (!membership || (membership.role !== 'owner'
|
|
516
|
+
&& membership.role !== 'manager'))` becomes `if (false)`.
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
The idea is Meta's: ACH has an engineer describe an area of concern in plain
|
|
520
|
+
text and gates every generated step by execution. What is taken here is the
|
|
521
|
+
**unit** — the concern as the thing a human writes — not their generator, and
|
|
522
|
+
**no model is required**. A concern's useful core is a named scope plus a
|
|
523
|
+
producer selection, which the mechanical producers already satisfy, so every
|
|
524
|
+
verifying command stays offline.
|
|
525
|
+
|
|
526
|
+
Two concerns ship built in, and only two. An authorization concern would need a
|
|
527
|
+
heuristic for *"which files check permissions"*, and a guess there is exactly
|
|
528
|
+
the noise that gets a check switched off — so `SAVE-PERSISTS` and
|
|
529
|
+
`CHANGED-CODE` are the defaults, and a project that knows its own auth layer
|
|
530
|
+
says so in three lines. Declaring a concern with a built-in's id replaces it,
|
|
531
|
+
and the replacement is reported rather than silent.
|
|
532
|
+
|
|
533
|
+
A concern is **not** a claim and never becomes one by itself. It says where to
|
|
534
|
+
look; the probe says what it found; a human states the sentence worth defending.
|
|
535
|
+
|
|
469
536
|
#### Sweeping the save surface instead of the diff
|
|
470
537
|
|
|
471
538
|
A diff cannot answer *"when the user clicks save, does it actually save?"* — it
|
|
@@ -494,10 +561,26 @@ denominator comes first and the document carries `writeSites` and
|
|
|
494
561
|
AI-authored application: **12 of 26 probed persistence faults survived a fully
|
|
495
562
|
green suite, and all 12 were a dropped payload field.**
|
|
496
563
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
564
|
+
It also states, before any verdict, **what the defenders could prove at all**:
|
|
565
|
+
|
|
566
|
+
```
|
|
567
|
+
of those 37 files: 32 defended only by tests that mock the persistence layer,
|
|
568
|
+
1 with an unmocked defender, 4 with no defender at all.
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
That line is the read-back answer. A test that replaced the database can prove
|
|
572
|
+
the *call shape* and nothing beyond it — a `where` that matches no rows, a
|
|
573
|
+
rolled-back transaction and a rejected constraint all pass against a mock that
|
|
574
|
+
recorded the arguments and returned a plausible object. So rather than pretend
|
|
575
|
+
to measure persistence, TestGuard states the limit, the same way `nocover` says
|
|
576
|
+
"no test imports this" instead of guessing.
|
|
577
|
+
|
|
578
|
+
When every defender mocks the layer, the report says so outright: *nothing in
|
|
579
|
+
this suite can prove a write reached storage*. A clean probe there is not
|
|
580
|
+
evidence of persistence — it is evidence that nothing could have measured it.
|
|
581
|
+
|
|
582
|
+
`unmocked` is deliberately weaker than "proves persistence": a test that does
|
|
583
|
+
not replace the database may simply never reach it. Possible is the honest word.
|
|
501
584
|
|
|
502
585
|
### Every change needs a claim
|
|
503
586
|
|
|
@@ -580,12 +663,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
580
663
|
# GitHub Actions
|
|
581
664
|
- uses: actions/checkout@v4
|
|
582
665
|
with: { fetch-depth: 0 }
|
|
583
|
-
- uses: raccioly/testguard@v0.
|
|
666
|
+
- uses: raccioly/testguard@v0.14.0
|
|
584
667
|
with: { command: gate }
|
|
585
668
|
|
|
586
669
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
587
670
|
include:
|
|
588
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
671
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.14.0/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
589
672
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
590
673
|
```
|
|
591
674
|
|
|
@@ -903,7 +986,8 @@ parameter-derived argument swapped for `None`, a key dropped from a payload
|
|
|
903
986
|
`dict` or an allow-list. Two differences are deliberate. A statement is
|
|
904
987
|
removed by replacing it with `pass`, never by deleting the line, because a
|
|
905
988
|
block whose only statement is gone is an `IndentationError`; and a line that
|
|
906
|
-
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
|
|
907
991
|
because a fault that cannot compile is a `fault-invalid` verdict — a probe run
|
|
908
992
|
spent saying nothing about the tests. The two JSX shapes (`element-removed`,
|
|
909
993
|
`handler-dropped`) have no Python meaning and are absent rather than faked.
|
|
@@ -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.0 · 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>
|
|
@@ -755,7 +755,7 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
755
755
|
<p>Twenty-six faults injected on the persistence path of seven server actions in a real
|
|
756
756
|
AI-authored application: the database write itself, and the fields of the payload 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,10 @@ 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.</p>
|
|
784
785
|
</section>
|
|
785
786
|
|
|
786
787
|
<section class="rule-top">
|
|
@@ -788,28 +789,28 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
788
789
|
<div class="cols-3">
|
|
789
790
|
<div>
|
|
790
791
|
<h3>Nothing written</h3>
|
|
791
|
-
<p class="note"><code>sweep</code> takes the
|
|
792
|
-
|
|
793
|
-
|
|
792
|
+
<p class="note"><code>sweep</code> takes the files the gate just called uncovered, proposes
|
|
793
|
+
faults mechanically, probes a bounded selection, and reports what nothing noticed — ordering
|
|
794
|
+
candidates by what this project’s own runs have shown survives, the feedback that took
|
|
795
|
+
Google’s productive rate from 15% to 89%, read from evidence already on disk.</p>
|
|
794
796
|
</div>
|
|
795
797
|
<div>
|
|
796
798
|
<h3>A sentence or ten</h3>
|
|
797
|
-
<p class="note">A <em>concern</em>
|
|
798
|
-
|
|
799
|
-
|
|
799
|
+
<p class="note">A <em>concern</em> names a kind of promise and where to look. Five lines of JSON
|
|
800
|
+
on the same application — “every admin route refuses a caller without the admin role” — found a
|
|
801
|
+
route whose owner/manager check can be forced to <code>false</code> with no test failing. One
|
|
802
|
+
sentence, twenty-four routes, no model.</p>
|
|
800
803
|
</div>
|
|
801
804
|
<div>
|
|
802
805
|
<h3>A claim</h3>
|
|
803
806
|
<p class="note">The survivors worth defending become the first claims: a statement, its faults,
|
|
804
|
-
and a reproducer already in hand
|
|
807
|
+
and a reproducer already in hand.</p>
|
|
805
808
|
</div>
|
|
806
809
|
</div>
|
|
807
|
-
<p class="note" style="margin-top:9px">Three rules keep the
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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>
|
|
810
|
+
<p class="note" style="margin-top:9px">Three rules keep the lower rungs safe. A sweep <strong>never
|
|
811
|
+
writes the claims file</strong> — a sentence nobody wrote is not a claim. Its evidence <strong>never
|
|
812
|
+
replaces</strong> the canonical record. And it <strong>does not fail on its own bad guess</strong>:
|
|
813
|
+
only a survivor or an untested file exits non-zero.</p>
|
|
813
814
|
</section>
|
|
814
815
|
|
|
815
816
|
<div class="pull">
|
|
@@ -843,7 +844,9 @@ field-dropped missed 4/4 p=1.00 ci [0.51, 1.00]</pre>
|
|
|
843
844
|
A later study found reported mutants are coupled to real faults: the bugs they analysed would
|
|
844
845
|
have been caught had a mutant been surfaced.</p>
|
|
845
846
|
<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
|
|
847
|
+
selection — measured on real probed faults rather than assumed. Their arid-node suppression
|
|
848
|
+
was measured, not copied: Google's categories are under 1% of what the producers propose;
|
|
849
|
+
the arid class here is presentational JSX, now set aside and counted.</p>
|
|
847
850
|
</div>
|
|
848
851
|
<div>
|
|
849
852
|
<h3>Meta — making it specific</h3>
|
|
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 |
|
|
53
|
+
| Claim supply | The concerns a sweep can be aimed by, which proposals are worth probing, what this project's own runs say about that, where it writes to storage, and why a survivor on a write path was missed | `src/supply/concerns.mjs`, `src/supply/select.mjs`, `src/supply/feedback.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` |
|
|
@@ -105,6 +105,7 @@ appears here but not on disk, or on disk but not here, is drift.
|
|
|
105
105
|
| `action.yml` | The GitHub Action surface; `version` input synced from `package.json` | consumers' workflows |
|
|
106
106
|
| `packaging/gitlab/testguard.gitlab-ci.yml` | The GitLab component; tag and `TESTGUARD_VERSION` synced | consumers' pipelines |
|
|
107
107
|
| `packaging/homebrew/testguard.rb` | The staged Homebrew formula; tarball URL and registry-derived hash synced, then copied byte-for-byte | `raccioly/homebrew-tap` |
|
|
108
|
+
| `testguard.concerns.json` | The one-sentence scopes a sweep can be aimed by; optional, with two built-ins behind it | `testguard concerns`, `sweep --concern` |
|
|
108
109
|
| `.docguard.json`, `.docguardignore` | Which canonical documents exist and which validators run | DocGuard, in CI and pre-commit |
|
|
109
110
|
| `.npmrc`, `.npmignore`, `.gitattributes` | Packaging and line-ending hygiene | npm, git |
|
|
110
111
|
|