testguard-cli 0.8.1__tar.gz → 0.9.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.8.1 → testguard_cli-0.9.0}/.github/workflows/release.yml +33 -5
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/workflows/scheduled-release.yml +20 -5
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/CHANGELOG.md +137 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/PKG-INFO +36 -5
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/README.md +35 -4
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/action.yml +1 -1
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/docs-canonical/REQUIREMENTS.md +5 -5
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/package-lock.json +169 -168
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/package.json +2 -2
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/pyproject.toml +1 -1
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/cli.mjs +13 -4
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/baseline.mjs +24 -3
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/init/init.mjs +15 -2
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/contention.mjs +20 -1
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/replay/replay.mjs +88 -7
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/scaffold/producers.mjs +16 -5
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/scaffold/producers.python.mjs +8 -3
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/scaffold/scaffold.mjs +25 -3
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/baseline.test.mjs +23 -0
- testguard_cli-0.9.0/test/calibration.test.mjs +158 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/contention.test.mjs +35 -5
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/init.test.mjs +55 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/probe-error.test.mjs +37 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/replay.test.mjs +4 -1
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/scaffold.python.test.mjs +25 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/scaffold.test.mjs +91 -1
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/testguard.claims.json +328 -4
- testguard_cli-0.9.0/testguard.cost-budget.json +82 -0
- testguard_cli-0.8.1/test/calibration.test.mjs +0 -73
- testguard_cli-0.8.1/testguard.cost-budget.json +0 -37
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.codex/hooks.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.docguard.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.docguardignore +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.gitattributes +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/dependabot.yml +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.gitignore +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.npmignore +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.npmrc +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/AGENTS.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/CLAUDE.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/CONTRIBUTING.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/LICENSE +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/PRIVACY.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/SECURITY.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/SUPPORT.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/bench/README.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/cli/testguard.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/docs/testguard-explained.html +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/docs-canonical/ARCHITECTURE.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/docs-canonical/ENVIRONMENT.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/docs-canonical/SECURITY.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/docs-canonical/TEST-SPEC.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/GATE-SEMANTICS.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/README.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/lib/validate.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/evidence.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/replay.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/claims/load.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/brief.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/gate.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/init.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/probe.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/commands/status.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/gate/changed.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/git.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/init/templates/SKILL.md +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/mcp/tools.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/probe.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/pyimports.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/index.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/render.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/replay/label.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/status/status.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/util/glob.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/src/util/hash.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/admit.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/brief.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/classify.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/cost.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/discover.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/gate.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/glob.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/independence.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/inject.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/progress.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/pyimports.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/release-sync.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/samples/python-report.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/status.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/test/writer.test.mjs +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/testguard.ignore.json +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.8.1 → testguard_cli-0.9.0}/vitest.config.mjs +0 -0
|
@@ -118,15 +118,43 @@ jobs:
|
|
|
118
118
|
env:
|
|
119
119
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
120
120
|
run: |
|
|
121
|
+
set -o pipefail
|
|
121
122
|
TAG="v${{ needs.detect-version.outputs.version }}"
|
|
122
123
|
if git ls-remote --tags origin | grep -q "refs/tags/$TAG$"; then
|
|
123
124
|
echo "✅ Tag $TAG already exists on remote"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
exit 0
|
|
126
|
+
fi
|
|
127
|
+
git config user.name "github-actions[bot]"
|
|
128
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
129
|
+
git tag -a "$TAG" -m "Release $TAG"
|
|
130
|
+
if git push origin "$TAG" 2>/tmp/tag-err; then
|
|
131
|
+
echo "✅ Pushed $TAG"
|
|
132
|
+
exit 0
|
|
133
|
+
fi
|
|
134
|
+
cat /tmp/tag-err
|
|
135
|
+
# A GitHub App may not PUSH a ref whose .github/workflows differ from
|
|
136
|
+
# the CURRENT default branch. That is not a property of this release:
|
|
137
|
+
# it becomes true the moment any workflow change merges to main while
|
|
138
|
+
# the release is in flight, which is exactly how v0.8.1 lost its tag
|
|
139
|
+
# ("refusing to allow a GitHub App to create or update workflow
|
|
140
|
+
# .github/workflows/release.yml without `workflows` permission")
|
|
141
|
+
# seven minutes after an unrelated workflow PR merged.
|
|
142
|
+
#
|
|
143
|
+
# So fall back to the API. It adds a ref to a commit that is ALREADY
|
|
144
|
+
# in the repository and creates no file content, so the restriction
|
|
145
|
+
# does not apply. The push is tried first on purpose: it is the path
|
|
146
|
+
# every previous release took, and this fallback only has to cover
|
|
147
|
+
# the case that currently fails outright.
|
|
148
|
+
if ! grep -q 'workflows. permission' /tmp/tag-err; then
|
|
149
|
+
echo "::error::git push of $TAG failed for a reason other than the workflow-permission restriction — see above."
|
|
150
|
+
exit 1
|
|
129
151
|
fi
|
|
152
|
+
echo "::warning::Pushing $TAG was refused because main's workflows moved during this release; creating the tag through the API instead."
|
|
153
|
+
TAG_SHA=$(gh api -X POST "repos/${GITHUB_REPOSITORY}/git/tags" -f tag="$TAG" -f message="Release $TAG" -f object="$GITHUB_SHA" -f type=commit --jq .sha) || {
|
|
154
|
+
echo "::error::Could not create the tag object for $TAG. Create it by hand: git tag -a $TAG $GITHUB_SHA -m 'Release $TAG' && git push origin $TAG"; exit 1; }
|
|
155
|
+
gh api -X POST "repos/${GITHUB_REPOSITORY}/git/refs" -f ref="refs/tags/$TAG" -f sha="$TAG_SHA" --jq .ref || {
|
|
156
|
+
echo "::error::Could not create refs/tags/$TAG from tag object $TAG_SHA. Create it by hand: git tag -a $TAG $GITHUB_SHA -m 'Release $TAG' && git push origin $TAG"; exit 1; }
|
|
157
|
+
echo "✅ Created $TAG through the API at $GITHUB_SHA"
|
|
130
158
|
- name: Release notes from CHANGELOG
|
|
131
159
|
run: |
|
|
132
160
|
VERSION="${{ needs.detect-version.outputs.version }}"
|
|
@@ -5,10 +5,11 @@ name: Scheduled Release
|
|
|
5
5
|
# tree is green, and open a release PR with auto-merge armed. Once merged, the
|
|
6
6
|
# hourly sweep in release.yml tags and publishes.
|
|
7
7
|
#
|
|
8
|
-
# GITHUB_TOKEN-created PRs do not trigger `pull_request` workflows, so
|
|
9
|
-
# the
|
|
10
|
-
#
|
|
11
|
-
#
|
|
8
|
+
# GITHUB_TOKEN-created PRs do not trigger `pull_request` workflows, so nothing
|
|
9
|
+
# would ever start the required checks and auto-merge would wait forever. This
|
|
10
|
+
# workflow therefore dispatches ci.yml on the release branch itself:
|
|
11
|
+
# `workflow_dispatch` is the one event GITHUB_TOKEN may trigger. After the
|
|
12
|
+
# three Node legs go green, auto-merge completes. No human step remains.
|
|
12
13
|
|
|
13
14
|
on:
|
|
14
15
|
schedule:
|
|
@@ -133,13 +134,27 @@ jobs:
|
|
|
133
134
|
git add -A
|
|
134
135
|
git commit -m "release: v${NEW_VERSION} — automated weekly batch"
|
|
135
136
|
git push origin "$BRANCH"
|
|
136
|
-
printf 'Automated weekly release. Version bump + CHANGELOG entry + synced version surfaces. Merging tags and publishes to npm and PyPI via release.yml.\n\
|
|
137
|
+
printf 'Automated weekly release. Version bump + CHANGELOG entry + synced version surfaces. Merging tags and publishes to npm and PyPI via release.yml.\n\nA PR opened by GITHUB_TOKEN gets no `pull_request` run, so this workflow dispatched ci.yml on `%s` itself. Auto-merge is armed; it completes once the three Node legs pass. If the checks never appear: `gh workflow run ci.yml --ref %s`.\n' "$BRANCH" "$BRANCH" > /tmp/pr-body.md
|
|
137
138
|
# A bare `gh pr create` failure here reads as a GraphQL error with no
|
|
138
139
|
# hint of the cause, which is how v0.8.1 lost its release PR after
|
|
139
140
|
# everything else in the release had succeeded.
|
|
140
141
|
if PR=$(gh pr create --base main --head "$BRANCH" --title "release: v${NEW_VERSION} — automated weekly batch" --body-file /tmp/pr-body.md 2>/tmp/pr-err); then
|
|
141
142
|
gh pr merge --auto --squash "$PR"
|
|
142
143
|
echo "Release PR: $PR"
|
|
144
|
+
# Nothing else will start the required checks: a PR opened by
|
|
145
|
+
# GITHUB_TOKEN raises no `pull_request` event, so auto-merge would
|
|
146
|
+
# sit on three checks that never run. `workflow_dispatch` is the
|
|
147
|
+
# one event GITHUB_TOKEN may trigger, and this job already holds
|
|
148
|
+
# the `actions: write` it needs. Guarded so a re-run of an
|
|
149
|
+
# already-tested branch does not queue a second identical run.
|
|
150
|
+
HEAD_SHA=$(git rev-parse HEAD)
|
|
151
|
+
RUNS=$(gh api "repos/${GITHUB_REPOSITORY}/commits/${HEAD_SHA}/check-runs" --jq '.total_count')
|
|
152
|
+
if [ "${RUNS:-0}" = "0" ]; then
|
|
153
|
+
gh workflow run ci.yml --ref "$BRANCH"
|
|
154
|
+
echo "Dispatched ci.yml on $BRANCH ($HEAD_SHA)"
|
|
155
|
+
else
|
|
156
|
+
echo "✅ $HEAD_SHA already has $RUNS check run(s)"
|
|
157
|
+
fi
|
|
143
158
|
else
|
|
144
159
|
cat /tmp/pr-err
|
|
145
160
|
if grep -q 'not permitted to create or approve pull requests' /tmp/pr-err; then
|
|
@@ -7,7 +7,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.0] - 2026-09-18
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.8.1`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- fix: the five findings from the 0.8.1 pocket-archive field report (#107)
|
|
17
|
+
- chore(deps): jest 30.5.1 -> 30.5.2 (#106)
|
|
18
|
+
- feat(release): the release PR starts its own CI (#105)
|
|
19
|
+
- fix(release): tag through the API when the push is refused (#104)
|
|
20
|
+
- chore(homebrew): sha256 for v0.8.1, from the published tarball (#103)
|
|
21
|
+
- fix(release): a blocked pull request says which setting blocks it (#102)
|
|
22
|
+
|
|
23
|
+
|
|
10
24
|
### Fixed
|
|
25
|
+
- **`scaffold` no longer dies on a zero-valued window literal, and proposes a
|
|
26
|
+
real fault for one.** The window producer weakened a literal by ×1000, and
|
|
27
|
+
×1000 of `0` is `0` — the replacement equalled its find, which the schema
|
|
28
|
+
rejects outright, so a single `expired: 0` aborted the whole file and printed
|
|
29
|
+
a raw Node stack. A 725-line source was unscaffoldable for this reason. The
|
|
30
|
+
same defect sat in the work-factor producer, where `rounds = 1` collapsed to
|
|
31
|
+
`1`, and in both Python producers, where `timeout=0` is PEP 8 spacing and so
|
|
32
|
+
the common form. The two cases are not the same, and are not fixed the same
|
|
33
|
+
way: a zero window is exactly the window worth widening, so it now widens to
|
|
34
|
+
a real value; a work factor already at 1 has nothing weaker to become, so it
|
|
35
|
+
proposes nothing rather than proposing itself. `usableProposal()` refuses a
|
|
36
|
+
no-op whatever a producer hands it, which makes the rule structural instead
|
|
37
|
+
of remembered.
|
|
38
|
+
- **`if (…) continue;` and `if (…) break;` are guards.** Only `return` and
|
|
39
|
+
`throw` were read as guard bodies, so pure selection-over-rows — the shape
|
|
40
|
+
the "pure logic, caller does the IO" advice produces — got no proposals at
|
|
41
|
+
all. Two fully-claimed files of seven such guards each returned nothing,
|
|
42
|
+
while the hand-written faults on those same lines all killed: the shape
|
|
43
|
+
`scaffold` skipped was the one demonstrably worth proposing. Both the
|
|
44
|
+
single-line and the block form, in JavaScript and Python.
|
|
45
|
+
- **An unexpected exception is named as our bug and keeps its trace.** Four
|
|
46
|
+
error classes became `error: …`; everything else reached the user as a Node
|
|
47
|
+
stack trace ending in `Node.js v24.18.0`, which reads as "your repository
|
|
48
|
+
broke the tool" and gives the operator nothing to do. It now says whose
|
|
49
|
+
defect it is and where to report it, and still prints the trace — a framed
|
|
50
|
+
error without one cannot be reported.
|
|
51
|
+
- **Every surface that describes the session-start hook is held to the code,
|
|
52
|
+
not just the README.** 0.6.0 banned "falls back to npx" in README.md and
|
|
53
|
+
added a mechanical check there. The same sentence lived in the line `init`
|
|
54
|
+
prints as it installs the hook and in the `--help` entry, and both went on
|
|
55
|
+
promising an `npx --no-install` fallback that `hookCommand()` had stopped
|
|
56
|
+
emitting — the sentence the user reads at the moment the hook is installed
|
|
57
|
+
described the network-reaching mechanism that was removed for reaching the
|
|
58
|
+
network. `TG-README-HOOK-MATCHES-THE-CODE` is now a claim about the
|
|
59
|
+
property, over all four surfaces. The fourth, `docs-canonical/SECURITY.md`,
|
|
60
|
+
was found by DocGuard while this claim was being widened from one to
|
|
61
|
+
three; it is correct today, and is checked because that is where the
|
|
62
|
+
sentence lives, not because a defect had been found there.
|
|
63
|
+
- **A test runner that has already exited is not reported as contention.**
|
|
64
|
+
`ps` is a snapshot, and a suite that finished between the snapshot and the
|
|
65
|
+
warning produced "1 test runner is already running (pid 65751)" for a process
|
|
66
|
+
the operator could not find. Liveness is re-checked immediately before
|
|
67
|
+
reporting. Detection stays best-effort: `EPERM` means running, not gone.
|
|
68
|
+
- **A release can tag itself even when `main` moves underneath it.** A GitHub
|
|
69
|
+
App may not *push* a ref whose `.github/workflows` differ from the current
|
|
70
|
+
default branch, so v0.8.1's tag was refused seven minutes after an unrelated
|
|
71
|
+
workflow PR merged — a release that was otherwise complete. `create-release`
|
|
72
|
+
still pushes first, and now falls back to creating the tag through the API,
|
|
73
|
+
which adds a ref to a commit already in the repository and so is not subject
|
|
74
|
+
to that restriction. Any other push failure is still fatal and says so.
|
|
11
75
|
- **A release that cannot open its pull request says why.** `GITHUB_TOKEN`
|
|
12
76
|
cannot call `createPullRequest` unless "Allow GitHub Actions to create and
|
|
13
77
|
approve pull requests" is enabled, and v0.8.1 hit exactly that: the branch,
|
|
@@ -19,6 +83,79 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
83
|
The failure is still a failure: the branch exists without a PR, and for the
|
|
20
84
|
formula `detect-version` keeps reporting the hash owed on every sweep.
|
|
21
85
|
|
|
86
|
+
### Changed
|
|
87
|
+
- **The calibration counts escaped bugs, not every commit that changed source
|
|
88
|
+
and a test together.** `p` is P(the suite misses it | a *bug* of this class
|
|
89
|
+
escapes), but the corpus was every commit with a source-and-test pair — which
|
|
90
|
+
in a conventional-commits repository is every feature, since a feature ships
|
|
91
|
+
with its tests as a matter of course. One run reported "12 of 12 measurable
|
|
92
|
+
bugs invisible — 100%" over five `feat:`, a `refactor:`, a `style:` and a
|
|
93
|
+
commit whose entire purpose was adding a test; only four were `fix:`. The
|
|
94
|
+
finding survived the correction — all four real fixes came back `blind` — and
|
|
95
|
+
is stronger stated over the four honestly than over the twelve loosely.
|
|
96
|
+
|
|
97
|
+
Selection is unchanged: the source-and-test pairing is still all that can be
|
|
98
|
+
judged without reading a subject line, and replaying a feature still measures
|
|
99
|
+
something. What narrows is the estimator. Where a majority of replayed
|
|
100
|
+
subjects carry a conventional type, `p` is computed over the `fix:`/`revert:`
|
|
101
|
+
records; `replay` prints both rates and the type breakdown so the narrowing
|
|
102
|
+
can be checked; and `calibration.json` records which rule produced it in
|
|
103
|
+
`source.detail`, so two calibrations from two populations can never be
|
|
104
|
+
compared by accident. A conventional repository with no `fix:` in range
|
|
105
|
+
calibrates nothing and says to widen the range, rather than silently counting
|
|
106
|
+
features as bugs.
|
|
107
|
+
- **`baseline` stops printing advice that rots, and stops repeating it.** It
|
|
108
|
+
named `.testguard/evidence.json` and `.testguard/brief.json` long after the
|
|
109
|
+
tool had grown to ten regenerated outputs, and printed them every run whether
|
|
110
|
+
or not the project already ignored them. The list now lives in one place,
|
|
111
|
+
owned by `init`, and only the outputs a project does not yet ignore are
|
|
112
|
+
printed — asked of git, so `.testguard/*` with negated exceptions, a global
|
|
113
|
+
ignore file and `.git/info/exclude` all answer correctly. `replay.json`,
|
|
114
|
+
`calibration.json`, `ci-self-evidence.json` and `.testguard/ci/` were missing
|
|
115
|
+
from the list `init` writes and have been added.
|
|
116
|
+
- **The weekly release starts its own CI, so no human step is left.** A pull
|
|
117
|
+
request opened by `GITHUB_TOKEN` raises no `pull_request` event, so the three
|
|
118
|
+
required Node legs never started and auto-merge sat on checks that would
|
|
119
|
+
never run — the workflow simply printed `gh workflow run ci.yml --ref
|
|
120
|
+
release/vX.Y.Z` and waited for someone to read it. It now dispatches that
|
|
121
|
+
run itself, as `release.yml`'s `homebrew-sha` job already did, guarded so a
|
|
122
|
+
re-run does not queue a second identical run. With
|
|
123
|
+
"Allow GitHub Actions to create and approve pull requests" enabled, a
|
|
124
|
+
release now needs no manual step at all.
|
|
125
|
+
- **The cost budget records a from-scratch release measurement.** 999 s for
|
|
126
|
+
115 faults on the v0.8.1 release commit, corroborated at 988 s on the
|
|
127
|
+
commit before it, against the unchanged 1100 s ceiling. The previous record
|
|
128
|
+
was 962 s at 96 faults.
|
|
129
|
+
|
|
130
|
+
### Performance
|
|
131
|
+
- **The self-probe cost budget is 1350 s, from 1100.** Six new claims and 14
|
|
132
|
+
new faults took the gate from 115 to 129 faults and 690 to 774 runs, and CI
|
|
133
|
+
measured 1123 s against the 1100 ceiling. The budget exists to make exactly
|
|
134
|
+
this visible rather than let it pass, so the raise is recorded with its
|
|
135
|
+
measurement and its reason in `testguard.cost-budget.json`. Two caveats are
|
|
136
|
+
written down there: the 1123 s run restored a cache under the fallback key
|
|
137
|
+
and so reused some verdicts, which makes it a floor rather than the
|
|
138
|
+
from-scratch figure this budget is defined against; and the number is
|
|
139
|
+
expected to come back down, because 44% of the gate is two shared defender
|
|
140
|
+
files re-run once per claim that names them. A third is recorded too: the
|
|
141
|
+
reported total is modelled rather than wall clock, because a reused record
|
|
142
|
+
contributes what it cost when it was last probed, so the same commit
|
|
143
|
+
measured 1123 s and then 1025 s depending on what the cache restored.
|
|
144
|
+
- **The new calibration claim costs 4.9 s instead of 51 s.** Its tests are
|
|
145
|
+
pure — no git, no fixture corpus, no scratch worktree — but they were added
|
|
146
|
+
to `replay.test.mjs`, whose scripted-corpus setup costs tens of seconds a
|
|
147
|
+
run, and a claim pays for every test in the file it names. Moved to
|
|
148
|
+
`calibration.test.mjs`, which exists for exactly this, and the claim
|
|
149
|
+
re-pointed. Same two faults, still killed 2/2.
|
|
150
|
+
|
|
151
|
+
### Documentation
|
|
152
|
+
- The README shows an ignore-file entry. It was mentioned twice without one,
|
|
153
|
+
so the field name (`pattern`, not `value`) and the fact that `expires` is a
|
|
154
|
+
full timestamp rather than a date had to be discovered from a validator
|
|
155
|
+
refusal.
|
|
156
|
+
- `--help` shows `gate --changed HEAD --include-dirty`. `--include-dirty` needs
|
|
157
|
+
a reference, and that combination is the one form a pre-commit hook wants.
|
|
158
|
+
|
|
22
159
|
## [0.8.1] - 2026-09-18
|
|
23
160
|
|
|
24
161
|
Automated weekly release — everything merged since `v0.8.0`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.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
|
|
@@ -150,9 +150,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
|
|
|
150
150
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
151
151
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
152
152
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
153
|
-
| GitHub Action | `uses: raccioly/testguard@v0.
|
|
153
|
+
| GitHub Action | `uses: raccioly/testguard@v0.9.0` — see [`action.yml`](./action.yml) |
|
|
154
154
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
155
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
155
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.9.0/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
|
|
156
156
|
|
|
157
157
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
158
158
|
less than that many days ago (`ENOVERSIONS`); install that one with
|
|
@@ -434,6 +434,37 @@ excuses nothing. Non-source files and documented never-claimed patterns
|
|
|
434
434
|
(`*.d.ts`, `*.config.*`, fixtures, mocks; `--explain` lists them) are
|
|
435
435
|
excluded and said so; `--strict` fails a change that evaluated nothing.
|
|
436
436
|
|
|
437
|
+
An ignore file is a list of `entries`. The field is `pattern`, and `reason` is
|
|
438
|
+
not optional — it is the whole point of the mechanism:
|
|
439
|
+
|
|
440
|
+
```json
|
|
441
|
+
{
|
|
442
|
+
"$schema": "./node_modules/testguard-cli/spec/schemas/ignore.schema.json",
|
|
443
|
+
"schemaVersion": 1,
|
|
444
|
+
"entries": [
|
|
445
|
+
{
|
|
446
|
+
"kind": "path",
|
|
447
|
+
"pattern": "src/commands/**",
|
|
448
|
+
"reason": "Thin wrappers: read flags, call the module, print. The logic is claimed in the modules they call.",
|
|
449
|
+
"by": "maintainer",
|
|
450
|
+
"at": "2026-09-17T00:00:00Z"
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
"kind": "path",
|
|
454
|
+
"pattern": "src/legacy/billing.ts",
|
|
455
|
+
"reason": "Scheduled for deletion in Q4; claims would outlive the file.",
|
|
456
|
+
"by": "maintainer",
|
|
457
|
+
"at": "2026-09-17T00:00:00Z",
|
|
458
|
+
"expires": "2026-12-31T00:00:00Z"
|
|
459
|
+
}
|
|
460
|
+
]
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
`kind: "claim"` entries take the same shape and excuse a *removed claim*
|
|
465
|
+
instead of an unclaimed file. An entry past its `expires` excuses nothing and
|
|
466
|
+
is reported as `EXPIRED`.
|
|
467
|
+
|
|
437
468
|
With a reference known, `status --changed <ref>` reports `unclaimed-changes`
|
|
438
469
|
**before** any evidence state and makes the claim the next action; the brief
|
|
439
470
|
lists the unclaimed files first. The claim is written before more code.
|
|
@@ -451,12 +482,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
451
482
|
# GitHub Actions
|
|
452
483
|
- uses: actions/checkout@v4
|
|
453
484
|
with: { fetch-depth: 0 }
|
|
454
|
-
- uses: raccioly/testguard@v0.
|
|
485
|
+
- uses: raccioly/testguard@v0.9.0
|
|
455
486
|
with: { command: gate }
|
|
456
487
|
|
|
457
488
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
458
489
|
include:
|
|
459
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
490
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.0/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
460
491
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
461
492
|
```
|
|
462
493
|
|
|
@@ -126,9 +126,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
|
|
|
126
126
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
127
127
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
128
128
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
129
|
-
| GitHub Action | `uses: raccioly/testguard@v0.
|
|
129
|
+
| GitHub Action | `uses: raccioly/testguard@v0.9.0` — see [`action.yml`](./action.yml) |
|
|
130
130
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
131
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
131
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.9.0/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
|
|
132
132
|
|
|
133
133
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
134
134
|
less than that many days ago (`ENOVERSIONS`); install that one with
|
|
@@ -410,6 +410,37 @@ excuses nothing. Non-source files and documented never-claimed patterns
|
|
|
410
410
|
(`*.d.ts`, `*.config.*`, fixtures, mocks; `--explain` lists them) are
|
|
411
411
|
excluded and said so; `--strict` fails a change that evaluated nothing.
|
|
412
412
|
|
|
413
|
+
An ignore file is a list of `entries`. The field is `pattern`, and `reason` is
|
|
414
|
+
not optional — it is the whole point of the mechanism:
|
|
415
|
+
|
|
416
|
+
```json
|
|
417
|
+
{
|
|
418
|
+
"$schema": "./node_modules/testguard-cli/spec/schemas/ignore.schema.json",
|
|
419
|
+
"schemaVersion": 1,
|
|
420
|
+
"entries": [
|
|
421
|
+
{
|
|
422
|
+
"kind": "path",
|
|
423
|
+
"pattern": "src/commands/**",
|
|
424
|
+
"reason": "Thin wrappers: read flags, call the module, print. The logic is claimed in the modules they call.",
|
|
425
|
+
"by": "maintainer",
|
|
426
|
+
"at": "2026-09-17T00:00:00Z"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"kind": "path",
|
|
430
|
+
"pattern": "src/legacy/billing.ts",
|
|
431
|
+
"reason": "Scheduled for deletion in Q4; claims would outlive the file.",
|
|
432
|
+
"by": "maintainer",
|
|
433
|
+
"at": "2026-09-17T00:00:00Z",
|
|
434
|
+
"expires": "2026-12-31T00:00:00Z"
|
|
435
|
+
}
|
|
436
|
+
]
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
`kind: "claim"` entries take the same shape and excuse a *removed claim*
|
|
441
|
+
instead of an unclaimed file. An entry past its `expires` excuses nothing and
|
|
442
|
+
is reported as `EXPIRED`.
|
|
443
|
+
|
|
413
444
|
With a reference known, `status --changed <ref>` reports `unclaimed-changes`
|
|
414
445
|
**before** any evidence state and makes the claim the next action; the brief
|
|
415
446
|
lists the unclaimed files first. The claim is written before more code.
|
|
@@ -427,12 +458,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
427
458
|
# GitHub Actions
|
|
428
459
|
- uses: actions/checkout@v4
|
|
429
460
|
with: { fetch-depth: 0 }
|
|
430
|
-
- uses: raccioly/testguard@v0.
|
|
461
|
+
- uses: raccioly/testguard@v0.9.0
|
|
431
462
|
with: { command: gate }
|
|
432
463
|
|
|
433
464
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
434
465
|
include:
|
|
435
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
466
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.0/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
436
467
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
437
468
|
```
|
|
438
469
|
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
| NFR-03 | The user's working tree is never damaged | scratch worktree by default; restore on signal, exception and exit; refusal to run in place over a dirty target; a restore failure is raised in every case but a scratch target that has already been deleted |
|
|
37
37
|
| NFR-04 | Every emitted document conforms to the published schema before it is written | `writeSpecDoc` refuses non-conforming output |
|
|
38
38
|
| NFR-05 | Verdicts are reproducible and attributable to a commit | evidence records `repo.head`, the snapshot when the working tree was probed, and the runner and its source |
|
|
39
|
-
| NFR-06 | A verdict is never optimistic under uncertainty | contention recorded; flaky and timeout verdicts gate; escalation cannot upgrade a verdict; a survivor is unverifiable until the defenders are shown to fail because of the subject |
|
|
39
|
+
| NFR-06 | A verdict is never optimistic under uncertainty | contention recorded and re-checked for liveness before it is reported; flaky and timeout verdicts gate; escalation cannot upgrade a verdict; a survivor is unverifiable until the defenders are shown to fail because of the subject; an unexpected exception is named as the tool's own defect rather than shown as a bare stack |
|
|
40
40
|
| NFR-07 | Cost grows with what changed, not with the size of the project | verdict reuse on unchanged inputs; blast-radius-targeted escalation; a committed cost budget gated in CI, so growth is signed for rather than discovered |
|
|
41
41
|
| NFR-08 | Portable contract | schemas are JSON Schema 2020-12, readable and implementable without this codebase |
|
|
42
42
|
|
|
@@ -95,16 +95,16 @@ fails the pull request naming the unclaimed file, and `status` reports
|
|
|
95
95
|
| FR-08 | `src/gate/changed.mjs` | `test/gate.test.mjs`, `TG-GATE-UNCLAIMED-EXITS-1` |
|
|
96
96
|
| FR-09 | `src/probe/mocks.mjs` | `test/mocks.test.mjs`, `TG-MOCKING-FILE-IS-NOT-A-DEFENDER` |
|
|
97
97
|
| FR-10 | `src/probe/runners/` | fixture acceptance per runner, `TG-RUNNER-FROM-PROJECT-FIRST` |
|
|
98
|
-
| FR-11 | `src/scaffold/producers.mjs` | `test/scaffold.test.mjs`, `TG-SCAFFOLD-ANCHORS-HIT` |
|
|
98
|
+
| FR-11 | `src/scaffold/producers.mjs` | `test/scaffold.test.mjs`, `test/scaffold.python.test.mjs`, `TG-SCAFFOLD-ANCHORS-HIT`, `TG-SCAFFOLD-NEVER-PROPOSES-A-NO-OP`, `TG-SCAFFOLD-LOOP-GUARD-IS-A-GUARD` |
|
|
99
99
|
| FR-12 | `src/admit/admit.mjs` | `test/admit.test.mjs`, `TG-ADMIT-NEEDS-ALL-KILLED` |
|
|
100
|
-
| FR-13 | `src/replay/` | `test/replay.test.mjs`, `test/calibration.test.mjs`, `TG-REPLAY-FLAKY-IS-NEVER-CAUGHT`, `TG-CALIBRATION-COUNTS-NOCOVER-AS-A-MISS` |
|
|
100
|
+
| FR-13 | `src/replay/` | `test/replay.test.mjs`, `test/calibration.test.mjs`, `TG-REPLAY-FLAKY-IS-NEVER-CAUGHT`, `TG-CALIBRATION-COUNTS-NOCOVER-AS-A-MISS`, `TG-CALIBRATION-COUNTS-ONLY-BUGS` |
|
|
101
101
|
| FR-14 | `src/mcp/` | `test/mcp.test.mjs` |
|
|
102
|
-
| NFR-01 | `src/init/init.mjs`, runner resolution | `TG-INIT-HOOK-NO-NETWORK`, `TG-README-HOOK-MATCHES-THE-CODE` |
|
|
102
|
+
| NFR-01 | `src/init/init.mjs`, runner resolution | `TG-INIT-HOOK-NO-NETWORK`, `TG-README-HOOK-MATCHES-THE-CODE` (every surface, not only the README), `TG-GITIGNORE-ADVICE-IS-ONE-LIST` |
|
|
103
103
|
| NFR-02 | `package.json` | `npm run test:install` in CI |
|
|
104
104
|
| NFR-03 | `src/probe/inject.mjs`, `src/probe/probe.mjs` | `TG-DIRTY-DEFENDERS-REFUSED`, `TG-IGNORED-DIRTY-RECORDED`, `TG-IN-PLACE-STILL-RAISES-A-MISSING-TARGET`, `TG-CONTROL-RESTORES-THE-SUBJECT` |
|
|
105
105
|
| NFR-04 | `src/evidence/writer.mjs` | `test/writer.test.mjs` |
|
|
106
106
|
| NFR-05 | `src/probe/probe.mjs` | `TG-FINGERPRINT-VERDICT`, `TG-FAULT-EDIT-VISIBLE` |
|
|
107
|
-
| NFR-06 | `src/probe/contention.mjs`, `classify.mjs` | `TG-CONTENTION-NEVER-FAILS-A-PROBE`, `TG-ESCALATION-N-RUNS`, `TG-ONE-BAD-FAULT-KEEPS-THE-EVIDENCE`, `TG-A-PRECONDITION-STILL-REFUSES-THE-RUN`, `TG-A-PROBE-ERROR-IS-NEVER-REUSED`, `TG-SURVIVOR-PROVES-THE-SUBJECT-RUNS`, `TG-CONTROL-READS-ANY-RED-AS-REACHED`, `TG-FATAL-EDIT-CANNOT-COMPILE` |
|
|
107
|
+
| NFR-06 | `src/probe/contention.mjs`, `classify.mjs` | `TG-CONTENTION-NEVER-FAILS-A-PROBE`, `TG-ESCALATION-N-RUNS`, `TG-ONE-BAD-FAULT-KEEPS-THE-EVIDENCE`, `TG-A-PRECONDITION-STILL-REFUSES-THE-RUN`, `TG-A-PROBE-ERROR-IS-NEVER-REUSED`, `TG-SURVIVOR-PROVES-THE-SUBJECT-RUNS`, `TG-CONTROL-READS-ANY-RED-AS-REACHED`, `TG-FATAL-EDIT-CANNOT-COMPILE`, `TG-CONTENTION-IGNORES-A-DEAD-PID`, `TG-CLI-NAMES-ITS-OWN-BUGS` |
|
|
108
108
|
| NFR-07 | verdict reuse, `src/probe/rank.mjs`, `src/probe/cost.mjs` | `test/probe.fixture.test.mjs` reuse case, `test/cost.test.mjs`, `TG-COST-BUDGET-ACTUALLY-GATES` |
|
|
109
109
|
| NFR-08 | `spec/` | `spec/conformance/schemas.test.mjs`, `TG-WILSON-REPRODUCES-AT-THE-DOCUMENTS-PRECISION` |
|
|
110
110
|
|