testguard-cli 0.10.1__tar.gz → 0.10.3__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.10.3/.github/scripts/publish-homebrew-tap.sh +68 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/workflows/release.yml +25 -13
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/workflows/scheduled-release.yml +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/AGENTS.md +4 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/CHANGELOG.md +41 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/CONTRIBUTING.md +7 -3
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/PKG-INFO +15 -7
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/README.md +14 -6
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/SECURITY.md +6 -4
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/action.yml +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/docs/testguard-explained.html +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/docs-canonical/ARCHITECTURE.md +5 -5
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/docs-canonical/ENVIRONMENT.md +12 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/docs-canonical/SECURITY.md +14 -5
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/docs-canonical/TEST-SPEC.md +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/package-lock.json +2 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/package.json +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/pyproject.toml +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/GATE-SEMANTICS.md +8 -3
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/brief.mjs +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/gate.mjs +3 -3
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/probe.mjs +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/status.mjs +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/gate/changed.mjs +55 -10
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/init/templates/SKILL.md +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/mcp/tools.mjs +1 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/gate.test.mjs +52 -2
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/release-sync.test.mjs +74 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/testguard.claims.json +135 -1
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/testguard.cost-budget.json +19 -14
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.codex/hooks.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.docguard.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.docguardignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.gitattributes +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/dependabot.yml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/scripts/approve-held-runs.sh +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.npmignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.npmrc +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/CLAUDE.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/LICENSE +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/PRIVACY.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/SUPPORT.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/bench/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/cli/testguard.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/docs-canonical/REQUIREMENTS.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/README.md +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/lib/validate.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/evidence.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/replay.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/claims/anchors.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/claims/load.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/cli.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/baseline.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/init.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/git.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/init/init.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/contention.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/probe.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/pyimports.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/index.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/render.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/replay/label.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/replay/replay.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/scaffold/producers.python.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/status/status.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/status/surface.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/util/glob.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/src/util/hash.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/admit.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/brief.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/calibration.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/claim-selection.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/claims-anchors.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/classify.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/contention.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/cost.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/discover.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/glob.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/independence.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/init.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/inject.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/label.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/one-pager.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/probe-error.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/progress.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/pyimports.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/release-approval.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/replay-verdict.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/replay.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/samples/python-report.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/scaffold.python.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/status.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/surface.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/vitest-config.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/test/writer.test.mjs +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/testguard.ignore.json +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.10.1 → testguard_cli-0.10.3}/vitest.config.mjs +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
fail() {
|
|
6
|
+
echo "::error::$*" >&2
|
|
7
|
+
exit 1
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
VERSION="${1:-}"
|
|
11
|
+
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || fail "usage: publish-homebrew-tap.sh <stable-semver>"
|
|
12
|
+
|
|
13
|
+
SOURCE_FORMULA="${HOMEBREW_TAP_SOURCE:-packaging/homebrew/testguard.rb}"
|
|
14
|
+
TAP_REMOTE="${HOMEBREW_TAP_REMOTE:-git@github.com:raccioly/homebrew-tap.git}"
|
|
15
|
+
[[ -f "$SOURCE_FORMULA" ]] || fail "source formula not found: $SOURCE_FORMULA"
|
|
16
|
+
|
|
17
|
+
FORMULA_VERSION=$(sed -nE 's#^[[:space:]]*url ".*testguard-cli-([0-9]+\.[0-9]+\.[0-9]+)\.tgz"#\1#p' "$SOURCE_FORMULA")
|
|
18
|
+
FORMULA_SHA=$(sed -nE 's/^[[:space:]]*sha256 "([0-9a-f]+)"/\1/p' "$SOURCE_FORMULA")
|
|
19
|
+
[[ "$FORMULA_VERSION" = "$VERSION" ]] || fail "formula version $FORMULA_VERSION does not match release $VERSION"
|
|
20
|
+
[[ "$FORMULA_SHA" =~ ^[0-9a-f]{64}$ ]] || fail "formula sha256 is not 64 lowercase hex characters"
|
|
21
|
+
|
|
22
|
+
TEMP_ROOT="${RUNNER_TEMP:-${TMPDIR:-/tmp}}"
|
|
23
|
+
[[ -d "$TEMP_ROOT" ]] || fail "temporary root does not exist: $TEMP_ROOT"
|
|
24
|
+
WORK_DIR=$(mktemp -d "$TEMP_ROOT/testguard-homebrew-tap.XXXXXX")
|
|
25
|
+
|
|
26
|
+
cleanup() {
|
|
27
|
+
case "$WORK_DIR" in
|
|
28
|
+
"$TEMP_ROOT"/testguard-homebrew-tap.*) rm -rf -- "$WORK_DIR" ;;
|
|
29
|
+
*) echo "::warning::refusing to remove unexpected temporary path: $WORK_DIR" >&2 ;;
|
|
30
|
+
esac
|
|
31
|
+
}
|
|
32
|
+
trap cleanup EXIT
|
|
33
|
+
|
|
34
|
+
if [[ "$TAP_REMOTE" = git@github.com:* ]]; then
|
|
35
|
+
[[ -n "${HOMEBREW_TAP_DEPLOY_KEY:-}" ]] || fail "HOMEBREW_TAP_DEPLOY_KEY is required for the GitHub tap"
|
|
36
|
+
command -v gh >/dev/null || fail "gh is required to verify GitHub's SSH host keys"
|
|
37
|
+
KEY_FILE="$WORK_DIR/deploy_key"
|
|
38
|
+
KNOWN_HOSTS="$WORK_DIR/known_hosts"
|
|
39
|
+
umask 077
|
|
40
|
+
printf '%s\n' "$HOMEBREW_TAP_DEPLOY_KEY" > "$KEY_FILE"
|
|
41
|
+
gh api meta --jq '.ssh_keys[] | "github.com " + .' > "$KNOWN_HOSTS"
|
|
42
|
+
[[ -s "$KNOWN_HOSTS" ]] || fail "GitHub API returned no SSH host keys"
|
|
43
|
+
export GIT_SSH_COMMAND="ssh -i \"$KEY_FILE\" -o IdentitiesOnly=yes -o UserKnownHostsFile=\"$KNOWN_HOSTS\" -o StrictHostKeyChecking=yes"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
TAP_DIR="$WORK_DIR/tap"
|
|
47
|
+
git clone --quiet --depth 1 "$TAP_REMOTE" "$TAP_DIR"
|
|
48
|
+
TAP_FORMULA="$TAP_DIR/Formula/testguard.rb"
|
|
49
|
+
[[ -f "$TAP_FORMULA" ]] || fail "tap is missing Formula/testguard.rb"
|
|
50
|
+
|
|
51
|
+
if cmp -s "$SOURCE_FORMULA" "$TAP_FORMULA"; then
|
|
52
|
+
echo "✅ raccioly/tap already carries TestGuard v$VERSION ($FORMULA_SHA)"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
install -m 0644 "$SOURCE_FORMULA" "$TAP_FORMULA"
|
|
57
|
+
ruby -c "$TAP_FORMULA" >/dev/null
|
|
58
|
+
git -C "$TAP_DIR" diff --check
|
|
59
|
+
git -C "$TAP_DIR" config user.name "github-actions[bot]"
|
|
60
|
+
git -C "$TAP_DIR" config user.email "github-actions[bot]@users.noreply.github.com"
|
|
61
|
+
git -C "$TAP_DIR" add Formula/testguard.rb
|
|
62
|
+
git -C "$TAP_DIR" commit --quiet -m "testguard $VERSION" -m "Published from raccioly/testguard's release workflow."
|
|
63
|
+
git -C "$TAP_DIR" push --quiet origin HEAD:main
|
|
64
|
+
git -C "$TAP_DIR" fetch --quiet origin main
|
|
65
|
+
cmp "$SOURCE_FORMULA" <(git -C "$TAP_DIR" show origin/main:Formula/testguard.rb) \
|
|
66
|
+
|| fail "live tap does not match the validated source formula after push"
|
|
67
|
+
|
|
68
|
+
echo "✅ published TestGuard v$VERSION to raccioly/tap ($FORMULA_SHA)"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
name: Auto Release —
|
|
1
|
+
name: Auto Release — GitHub, npm, PyPI, Homebrew
|
|
2
2
|
|
|
3
3
|
# TAG-DRIVEN and idempotent: a version needs releasing iff no tag v<version>
|
|
4
|
-
# exists. Re-runs never double-publish.
|
|
5
|
-
#
|
|
4
|
+
# exists. Re-runs never double-publish. npm and PyPI authenticate with OIDC
|
|
5
|
+
# Trusted Publishing. Homebrew uses one long-lived SSH deploy key scoped only
|
|
6
|
+
# to the distribution tap; it cannot access this repository or any other one.
|
|
6
7
|
#
|
|
7
8
|
# ONE-TIME SETUP (maintainer, cannot be automated):
|
|
8
9
|
# npm: the first `npm publish` of testguard-cli is manual (Trusted Publishing
|
|
@@ -12,6 +13,9 @@ name: Auto Release — Tag, GitHub Release, npm, PyPI
|
|
|
12
13
|
# PyPI: pypi.org → Your account → Publishing → add a PENDING publisher for
|
|
13
14
|
# project testguard-cli: owner raccioly, repo testguard, workflow
|
|
14
15
|
# release.yml, environment pypi. The first release creates the project.
|
|
16
|
+
# Brew: add a write deploy key named testguard-release-homebrew-tap to
|
|
17
|
+
# raccioly/homebrew-tap and store its private half here as the Actions
|
|
18
|
+
# secret HOMEBREW_TAP_DEPLOY_KEY.
|
|
15
19
|
|
|
16
20
|
on:
|
|
17
21
|
workflow_dispatch: # manual recovery
|
|
@@ -231,15 +235,17 @@ jobs:
|
|
|
231
235
|
# for byte from `npm pack` — after npm serves it (a publish is `PUT 202`;
|
|
232
236
|
# the file can take minutes to appear, so the script polls, bounded).
|
|
233
237
|
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
#
|
|
239
|
-
#
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
#
|
|
238
|
+
# The validated formula is also copied to the live raccioly/homebrew-tap
|
|
239
|
+
# repository with a write deploy key scoped only to that tap. The source
|
|
240
|
+
# copy cannot commit to main directly: branch protection requires the
|
|
241
|
+
# three CI legs on every commit and github-actions[bot] is no admin. So
|
|
242
|
+
# that one-line change lands as a bot PR with native auto-merge armed, and
|
|
243
|
+
# this job dispatches ci.yml on the branch itself — a PR opened with
|
|
244
|
+
# GITHUB_TOKEN gets no pull_request run, but workflow_dispatch is the
|
|
245
|
+
# documented exception. The PR's own runs may still appear held for
|
|
246
|
+
# first-time-contributor approval, so this job clears those holds before
|
|
247
|
+
# returning. That also means the merge triggers nothing here; the hourly
|
|
248
|
+
# sweep observes the merged source formula and finds brew_missing=false.
|
|
243
249
|
#
|
|
244
250
|
# Idempotent: an existing branch is reused, an existing open PR is left
|
|
245
251
|
# alone (CI re-dispatched only if its head has no check runs), a closed
|
|
@@ -265,9 +271,10 @@ jobs:
|
|
|
265
271
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
266
272
|
with:
|
|
267
273
|
node-version: 24
|
|
268
|
-
- name: Set sha256
|
|
274
|
+
- name: Set sha256, publish the live tap, and land the source formula PR
|
|
269
275
|
env:
|
|
270
276
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
277
|
+
HOMEBREW_TAP_DEPLOY_KEY: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}
|
|
271
278
|
run: |
|
|
272
279
|
set -euo pipefail
|
|
273
280
|
VERSION="${{ needs.detect-version.outputs.version }}"
|
|
@@ -290,6 +297,11 @@ jobs:
|
|
|
290
297
|
# other surface drifted. Nothing is pushed unless this passes.
|
|
291
298
|
node .github/scripts/sync-release-version.mjs --check --online
|
|
292
299
|
|
|
300
|
+
# Homebrew users install from raccioly/homebrew-tap, not this staged
|
|
301
|
+
# copy. Publish the exact formula that passed the online check. The
|
|
302
|
+
# helper is idempotent and fails closed if the tap cannot be updated.
|
|
303
|
+
.github/scripts/publish-homebrew-tap.sh "$VERSION"
|
|
304
|
+
|
|
293
305
|
if git diff --quiet -- packaging/homebrew/testguard.rb; then
|
|
294
306
|
echo "✅ $BRANCH already carries the published sha256"
|
|
295
307
|
else
|
|
@@ -148,7 +148,7 @@ jobs:
|
|
|
148
148
|
git add -A
|
|
149
149
|
git commit -m "release: v${NEW_VERSION} — automated weekly batch"
|
|
150
150
|
git push origin "$BRANCH"
|
|
151
|
-
printf 'Automated weekly release. Version bump + CHANGELOG entry + synced version surfaces. Merging tags and publishes to npm and
|
|
151
|
+
printf 'Automated weekly release. Version bump + CHANGELOG entry + synced version surfaces. Merging tags and publishes to GitHub, npm, PyPI, and Homebrew via release.yml.\n\nTwo things a GITHUB_TOKEN PR needs done for it, both handled by this workflow: it raises no `pull_request` event, so ci.yml was dispatched on `%s` itself; and the `pull_request` runs it does create are held at `action_required` (github-actions[bot] is permanently a first-time contributor), so those were approved too. Auto-merge is armed and completes once the three Node legs pass.\n\nIf it sits anyway: `gh workflow run ci.yml --ref %s` for missing checks, or check for runs still awaiting approval on the head sha.\n' "$BRANCH" "$BRANCH" > /tmp/pr-body.md
|
|
152
152
|
# A bare `gh pr create` failure here reads as a GraphQL error with no
|
|
153
153
|
# hint of the cause, which is how v0.8.1 lost its release PR after
|
|
154
154
|
# everything else in the release had succeeded.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AI Agent Instructions — TestGuard
|
|
2
2
|
|
|
3
|
-
<!-- docguard:last-reviewed 2026-09-
|
|
3
|
+
<!-- docguard:last-reviewed 2026-09-19 -->
|
|
4
4
|
|
|
5
5
|
Read this before changing anything. It is short on purpose.
|
|
6
6
|
|
|
@@ -63,7 +63,9 @@ node cli/testguard.mjs scaffold src/probe/classify.mjs --json # what the produ
|
|
|
63
63
|
|
|
64
64
|
Dependabot minor/patch PRs and automated release PRs auto-merge when the exact
|
|
65
65
|
CI run is green on all three Node legs. Majors and anything else wait for a
|
|
66
|
-
human.
|
|
66
|
+
human. npm and PyPI publishing is OIDC-only; Homebrew uses a dedicated SSH
|
|
67
|
+
deploy key scoped only to `raccioly/homebrew-tap`. There are no personal or
|
|
68
|
+
registry tokens in this repo.
|
|
67
69
|
|
|
68
70
|
<!-- testguard:begin -->
|
|
69
71
|
## TestGuard
|
|
@@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.10.3] - 2026-09-20
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.10.2`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- fix(gate): infer safe local change base (#145)
|
|
17
|
+
- chore(homebrew): sha256 for v0.10.2 (#144)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Bare `testguard gate` now discovers an ordinary clone's remote default
|
|
23
|
+
branch and reports how it chose the comparison. It still fails closed on
|
|
24
|
+
ambiguous Git state: the default branch is not compared to itself, and a
|
|
25
|
+
feature branch's same-name tracking ref is never accepted as its base.
|
|
26
|
+
|
|
27
|
+
## [0.10.2] - 2026-09-20
|
|
28
|
+
|
|
29
|
+
Automated weekly release — everything merged since `v0.10.1`.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- fix(release): publish the live Homebrew tap (#142)
|
|
34
|
+
- chore(homebrew): sha256 for v0.10.1 (#141)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Releases now publish the registry-verified Homebrew formula to the live
|
|
40
|
+
`raccioly/tap` repository with a dedicated, tap-scoped deploy key. The
|
|
41
|
+
release fails closed if that copy cannot be made, instead of updating only
|
|
42
|
+
the staging formula in this repository while users remain on an old version.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- The self-probe cost budget is 1440 s, from 1230. The cold v0.10.1 release
|
|
47
|
+
measured 1210 s before the four-fault live-tap claim added about 53 s; 1440
|
|
48
|
+
restores the established ~14% CI headroom. The 100 s per-claim ceiling is
|
|
49
|
+
unchanged, so an individually expensive claim still fails the gate.
|
|
50
|
+
|
|
10
51
|
## [0.10.1] - 2026-09-19
|
|
11
52
|
|
|
12
53
|
Automated weekly release — everything merged since `v0.10.0`.
|
|
@@ -79,12 +79,16 @@ on the fault.
|
|
|
79
79
|
`package.json` is the single source of truth for the version;
|
|
80
80
|
`.github/scripts/sync-release-version.mjs` propagates it. A weekly workflow
|
|
81
81
|
opens a release PR; merging it tags and publishes to npm and PyPI via OIDC
|
|
82
|
-
Trusted Publishing. There are no tokens to rotate.
|
|
82
|
+
Trusted Publishing. There are no npm or PyPI tokens to rotate. Homebrew uses a
|
|
83
|
+
dedicated SSH deploy key that can write only to `raccioly/homebrew-tap`; its
|
|
84
|
+
private half is stored as the `HOMEBREW_TAP_DEPLOY_KEY` Actions secret.
|
|
83
85
|
|
|
84
86
|
The Homebrew formula's `sha256` is the one surface that cannot be known before
|
|
85
87
|
publishing: `release.yml` sets it after `npm publish`, from the registry
|
|
86
|
-
tarball (`sync-release-version.mjs --sha256`),
|
|
87
|
-
|
|
88
|
+
tarball (`sync-release-version.mjs --sha256`), copies that verified formula
|
|
89
|
+
byte-for-byte to the live tap, and lands the source copy as a bot PR with
|
|
90
|
+
auto-merge armed — this repository's `main` is protected, so no job commits to
|
|
91
|
+
it directly.
|
|
88
92
|
`--check` (offline) refuses a placeholder hash; `--check --online` compares
|
|
89
93
|
against the tarball and is what decides, on every hourly sweep, whether the
|
|
90
94
|
formula is still owed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.3
|
|
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.10.
|
|
154
|
+
| GitHub Action | `uses: raccioly/testguard@v0.10.3` — 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.10.
|
|
156
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.3/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
|
|
@@ -484,9 +484,17 @@ state when an exact fault anchor is missing or ambiguous, and points at the
|
|
|
484
484
|
fault definition to repair. Repair preserves the fault's meaning and requires
|
|
485
485
|
a re-probe; there is deliberately no `--fix`.
|
|
486
486
|
|
|
487
|
-
**
|
|
487
|
+
**The base is detected when Git records it.** An explicit `--changed` wins,
|
|
488
|
+
then `TESTGUARD_CHANGED_REF`, GitHub Actions (`GITHUB_BASE_REF`) and GitLab
|
|
488
489
|
merge request pipelines (`CI_MERGE_REQUEST_DIFF_BASE_SHA`, then
|
|
489
|
-
`CI_MERGE_REQUEST_TARGET_BRANCH_NAME`)
|
|
490
|
+
`CI_MERGE_REQUEST_TARGET_BRANCH_NAME`). In an ordinary clone on a feature
|
|
491
|
+
branch, bare `testguard gate` uses the configured remote's symbolic default
|
|
492
|
+
branch (normally `origin/main`) and says how it chose it. If that symbolic ref
|
|
493
|
+
is absent, a configured upstream is used only when its branch name differs
|
|
494
|
+
from the current branch. A feature branch's same-name tracking ref and the
|
|
495
|
+
default branch itself are never selected: either could produce a misleading
|
|
496
|
+
empty diff. In those ambiguous cases, pass `--changed` explicitly.
|
|
497
|
+
|
|
490
498
|
The base must exist locally: GitHub — `actions/checkout` with `fetch-depth: 0`;
|
|
491
499
|
GitLab — the diff base sha needs nothing extra on a merge request pipeline,
|
|
492
500
|
the branch name needs `GIT_DEPTH: 0` or a `git fetch origin <target>`. A
|
|
@@ -497,12 +505,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
497
505
|
# GitHub Actions
|
|
498
506
|
- uses: actions/checkout@v4
|
|
499
507
|
with: { fetch-depth: 0 }
|
|
500
|
-
- uses: raccioly/testguard@v0.10.
|
|
508
|
+
- uses: raccioly/testguard@v0.10.3
|
|
501
509
|
with: { command: gate }
|
|
502
510
|
|
|
503
511
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
504
512
|
include:
|
|
505
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.
|
|
513
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.3/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
506
514
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
507
515
|
```
|
|
508
516
|
|
|
@@ -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.10.
|
|
130
|
+
| GitHub Action | `uses: raccioly/testguard@v0.10.3` — 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.10.
|
|
132
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.10.3/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
|
|
@@ -460,9 +460,17 @@ state when an exact fault anchor is missing or ambiguous, and points at the
|
|
|
460
460
|
fault definition to repair. Repair preserves the fault's meaning and requires
|
|
461
461
|
a re-probe; there is deliberately no `--fix`.
|
|
462
462
|
|
|
463
|
-
**
|
|
463
|
+
**The base is detected when Git records it.** An explicit `--changed` wins,
|
|
464
|
+
then `TESTGUARD_CHANGED_REF`, GitHub Actions (`GITHUB_BASE_REF`) and GitLab
|
|
464
465
|
merge request pipelines (`CI_MERGE_REQUEST_DIFF_BASE_SHA`, then
|
|
465
|
-
`CI_MERGE_REQUEST_TARGET_BRANCH_NAME`)
|
|
466
|
+
`CI_MERGE_REQUEST_TARGET_BRANCH_NAME`). In an ordinary clone on a feature
|
|
467
|
+
branch, bare `testguard gate` uses the configured remote's symbolic default
|
|
468
|
+
branch (normally `origin/main`) and says how it chose it. If that symbolic ref
|
|
469
|
+
is absent, a configured upstream is used only when its branch name differs
|
|
470
|
+
from the current branch. A feature branch's same-name tracking ref and the
|
|
471
|
+
default branch itself are never selected: either could produce a misleading
|
|
472
|
+
empty diff. In those ambiguous cases, pass `--changed` explicitly.
|
|
473
|
+
|
|
466
474
|
The base must exist locally: GitHub — `actions/checkout` with `fetch-depth: 0`;
|
|
467
475
|
GitLab — the diff base sha needs nothing extra on a merge request pipeline,
|
|
468
476
|
the branch name needs `GIT_DEPTH: 0` or a `git fetch origin <target>`. A
|
|
@@ -473,12 +481,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
473
481
|
# GitHub Actions
|
|
474
482
|
- uses: actions/checkout@v4
|
|
475
483
|
with: { fetch-depth: 0 }
|
|
476
|
-
- uses: raccioly/testguard@v0.10.
|
|
484
|
+
- uses: raccioly/testguard@v0.10.3
|
|
477
485
|
with: { command: gate }
|
|
478
486
|
|
|
479
487
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
480
488
|
include:
|
|
481
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.
|
|
489
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.3/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
482
490
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
483
491
|
```
|
|
484
492
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
| Version | Supported |
|
|
6
6
|
|---------|-----------|
|
|
7
|
-
| 0.
|
|
7
|
+
| 0.10.x | ✅ current |
|
|
8
8
|
|
|
9
9
|
## Reporting a vulnerability
|
|
10
10
|
|
|
@@ -27,9 +27,11 @@ TestGuard is a local CLI with a deliberately small surface:
|
|
|
27
27
|
scratch git worktrees under the system temp directory, which are removed
|
|
28
28
|
after each run. With `--in-place`, the mutated files are restored — on
|
|
29
29
|
success, on failure, and on `SIGINT`/`SIGTERM`.
|
|
30
|
-
- **Publishing uses OIDC Trusted Publishing** to npm and PyPI
|
|
31
|
-
pipeline holds no
|
|
32
|
-
generated automatically.
|
|
30
|
+
- **Publishing uses OIDC Trusted Publishing** to npm and PyPI, so the release
|
|
31
|
+
pipeline holds no registry tokens and npm provenance attestations are
|
|
32
|
+
generated automatically. Homebrew publishing uses a dedicated SSH deploy
|
|
33
|
+
key scoped only to `raccioly/homebrew-tap`; it cannot access this repository
|
|
34
|
+
or any other repository.
|
|
33
35
|
|
|
34
36
|
## The claims file is code
|
|
35
37
|
|
|
@@ -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.10.
|
|
147
|
+
<div class="meta">Technical Brief · v0.10.3 · MIT</div>
|
|
148
148
|
</div>
|
|
149
149
|
|
|
150
150
|
<h1>Your tests pass. That is not the same as your tests working.</h1>
|
|
Binary file
|
|
@@ -85,7 +85,7 @@ other tools, and it must remain portable out of this repository.
|
|
|
85
85
|
| Supported runners | vitest, jest (project runners); Playwright (per file) | They share a JSON report shape; Playwright is selected per file, not per project |
|
|
86
86
|
| Schemas | JSON Schema 2020-12 | Readable without the tool; portable to other languages |
|
|
87
87
|
| Distribution | npm, PyPI, Homebrew, GitHub Action, pre-commit, GitLab component | Meet teams in the pipeline they already run |
|
|
88
|
-
| Publishing | OIDC
|
|
88
|
+
| Publishing | OIDC for npm/PyPI; tap-scoped SSH deploy key for Homebrew | Registry credentials remain tokenless; the only stored publishing secret cannot access any repository except the tap |
|
|
89
89
|
|
|
90
90
|
## Configuration Files
|
|
91
91
|
|
|
@@ -101,7 +101,7 @@ appears here but not on disk, or on disk but not here, is drift.
|
|
|
101
101
|
| `.pre-commit-hooks.yaml` | Hook definitions **we publish for consumers** of TestGuard — a different file for a different audience | consumers' `.pre-commit-config.yaml` |
|
|
102
102
|
| `action.yml` | The GitHub Action surface; `version` input synced from `package.json` | consumers' workflows |
|
|
103
103
|
| `packaging/gitlab/testguard.gitlab-ci.yml` | The GitLab component; tag and `TESTGUARD_VERSION` synced | consumers' pipelines |
|
|
104
|
-
| `packaging/homebrew/testguard.rb` | The Homebrew formula; tarball URL synced | `raccioly/homebrew-tap` |
|
|
104
|
+
| `packaging/homebrew/testguard.rb` | The staged Homebrew formula; tarball URL and registry-derived hash synced, then copied byte-for-byte | `raccioly/homebrew-tap` |
|
|
105
105
|
| `.docguard.json`, `.docguardignore` | Which canonical documents exist and which validators run | DocGuard, in CI and pre-commit |
|
|
106
106
|
| `.npmrc`, `.npmignore`, `.gitattributes` | Packaging and line-ending hygiene | npm, git |
|
|
107
107
|
|
|
@@ -115,7 +115,7 @@ and no license server.
|
|
|
115
115
|
| `ajv` (npm, bundled) | Validate every document against the spec | none — local | Install-time only; the install smoke gate catches a missing runtime dep |
|
|
116
116
|
| `git` (system binary) | Worktree isolation, diffs, snapshots, patch ids | none — local | Precondition failure with a stated reason, exit 2 |
|
|
117
117
|
| The project's test runner | Runs the defenders | none — local | Precondition failure, exit 2; never a verdict about a claim |
|
|
118
|
-
| npm / PyPI registries | Distribution only | build time | A publish fails; the tool itself never calls them at run time |
|
|
118
|
+
| npm / PyPI registries and Homebrew tap | Distribution only | build time | A publish fails; the tool itself never calls them at run time |
|
|
119
119
|
|
|
120
120
|
## Infrastructure (IaC)
|
|
121
121
|
|
|
@@ -133,8 +133,8 @@ to every other surface and `--check` fails the build on drift.
|
|
|
133
133
|
|---|---|---|
|
|
134
134
|
| Pull request | `ci.yml` | Suite on Node 20/22/24, syntax check of every source, install-from-tarball smoke, Python wrapper import, fixture oracles for vitest and jest, Playwright job, change gate, removed-claim check, anchor/replacement preflight before self-probe |
|
|
135
135
|
| Release gate | `ci.yml` | TestGuard probes its own claims; every fault must be killed |
|
|
136
|
-
| Merge to main | `release.yml` | A version with no tag is tagged, released, and published to npm and PyPI over OIDC |
|
|
137
|
-
| Recovery | `release.yml` | Hourly sweep; each registry
|
|
136
|
+
| Merge to main | `release.yml` | A version with no tag is tagged, released, and published to npm and PyPI over OIDC; the verified formula is pushed to the live Homebrew tap with its repository-scoped deploy key |
|
|
137
|
+
| Recovery | `release.yml` | Hourly sweep; each registry and the formula hash are gated on their own state, so a partial publish is fixed by re-running |
|
|
138
138
|
|
|
139
139
|
## Diagrams
|
|
140
140
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- docguard:version 1.0.0 -->
|
|
4
4
|
<!-- docguard:status approved -->
|
|
5
|
-
<!-- docguard:last-reviewed 2026-09-
|
|
5
|
+
<!-- docguard:last-reviewed 2026-09-20 -->
|
|
6
6
|
<!-- docguard:owner @raccioly -->
|
|
7
7
|
<!-- docguard:quality negation-load off — the environment is defined by absences: no required variables, no secrets, no network, no service. -->
|
|
8
8
|
<!-- docguard:quality passive-voice off — setup steps describe what is read and written, not who reads and writes it. -->
|
|
@@ -28,7 +28,7 @@ None are required. All are optional overrides.
|
|
|
28
28
|
| Variable | Read by | Effect |
|
|
29
29
|
|---|---|---|
|
|
30
30
|
| `TESTGUARD_NODE_MODULES` | `probe` | `node_modules` to link into the scratch worktree; same as `--node-modules` |
|
|
31
|
-
| `TESTGUARD_CHANGED_REF` | `gate`, `status --changed` | The reference a change is measured against; overrides CI detection |
|
|
31
|
+
| `TESTGUARD_CHANGED_REF` | `gate`, `status --changed` | The reference a change is measured against; overrides CI and local Git detection |
|
|
32
32
|
| `GITHUB_BASE_REF` | `gate` | GitHub Actions pull requests: the base branch |
|
|
33
33
|
| `CI_MERGE_REQUEST_DIFF_BASE_SHA` | `gate` | GitLab merge request pipelines: exact diff base, needs no fetch |
|
|
34
34
|
| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | `gate` | GitLab fallback when the diff base sha is absent |
|
|
@@ -37,6 +37,16 @@ None are required. All are optional overrides.
|
|
|
37
37
|
The tool sets `PLAYWRIGHT_JSON_OUTPUT_FILE` and `FORCE_COLOR=0` on the
|
|
38
38
|
processes it spawns. It reads no secret, no token and no credential.
|
|
39
39
|
|
|
40
|
+
## Local Git Discovery
|
|
41
|
+
|
|
42
|
+
Without an explicit, environment or CI reference, TestGuard reads only local
|
|
43
|
+
Git metadata. On a feature branch it prefers the configured remote's symbolic
|
|
44
|
+
default branch, such as `origin/main`. If that ref is absent, it accepts the
|
|
45
|
+
branch's configured upstream only when the upstream branch name differs from
|
|
46
|
+
the current branch name. It never contacts the remote, never compares the
|
|
47
|
+
default branch to itself, and never treats `origin/feature` as the base of a
|
|
48
|
+
local `feature` branch; those ambiguous cases require `--changed`.
|
|
49
|
+
|
|
40
50
|
## Configuration Files
|
|
41
51
|
|
|
42
52
|
| File | Owner | Committed | Purpose |
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- docguard:version 1.0.0 -->
|
|
4
4
|
<!-- docguard:status approved -->
|
|
5
|
-
<!-- docguard:last-reviewed 2026-09-
|
|
5
|
+
<!-- docguard:last-reviewed 2026-09-19 -->
|
|
6
6
|
<!-- docguard:owner @raccioly -->
|
|
7
|
-
<!-- docguard:quality negation-load off — a security posture is a list of things that must not happen: no network, no secrets, no tokens, no telemetry. Stating them positively would weaken them. -->
|
|
7
|
+
<!-- docguard:quality negation-load off — a security posture is a list of things that must not happen: no runtime network, no CLI secrets, no registry tokens, no telemetry. Stating them positively would weaken them. -->
|
|
8
8
|
<!-- docguard:quality passive-voice off — threat and control statements name what is protected, not who protects it; the actor is the system throughout. -->
|
|
9
9
|
|
|
10
10
|
> Canonical. Code that contradicts this document is drift.
|
|
@@ -27,8 +27,14 @@ happens in CI, never on a developer machine:
|
|
|
27
27
|
| npm | OIDC Trusted Publishing | none |
|
|
28
28
|
| PyPI | OIDC Trusted Publishing (environment `pypi`) | none |
|
|
29
29
|
| GitHub releases | `GITHUB_TOKEN`, scoped to the run | none stored |
|
|
30
|
+
| Homebrew tap | SSH deploy key scoped to `raccioly/homebrew-tap` | `HOMEBREW_TAP_DEPLOY_KEY` |
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
npm and PyPI have no registry tokens to rotate or leak. Homebrew publishing
|
|
33
|
+
uses one stored credential: a dedicated Ed25519 deploy key whose public half
|
|
34
|
+
has write access only to `raccioly/homebrew-tap` and whose private half is the
|
|
35
|
+
`HOMEBREW_TAP_DEPLOY_KEY` Actions secret in this repository. It is not a
|
|
36
|
+
personal key and cannot access another repository. Rotate it by replacing both
|
|
37
|
+
the tap deploy key and the Actions secret.
|
|
32
38
|
|
|
33
39
|
## Authorization
|
|
34
40
|
|
|
@@ -53,8 +59,11 @@ guarantees are about **what the tool is allowed to touch**:
|
|
|
53
59
|
|
|
54
60
|
## Secrets Management
|
|
55
61
|
|
|
56
|
-
TestGuard neither reads nor stores secrets. It has no credential file,
|
|
57
|
-
keychain access and no environment variable holding a secret.
|
|
62
|
+
The TestGuard CLI neither reads nor stores secrets. It has no credential file,
|
|
63
|
+
no keychain access and no environment variable holding a secret. The release
|
|
64
|
+
workflow is the deliberate exception: its Homebrew step receives the
|
|
65
|
+
tap-scoped deploy key only while it copies a registry-verified formula to the
|
|
66
|
+
distribution repository. npm and PyPI publishing remain OIDC-only.
|
|
58
67
|
|
|
59
68
|
Two consequences worth stating for reviewers:
|
|
60
69
|
|
|
@@ -21,7 +21,7 @@ the test that defends it, and CI fails if any injected fault survives.
|
|
|
21
21
|
| Conformance | `spec/conformance/` | Every valid example validates; every must-reject document is rejected for the defect its filename names | ~1 s |
|
|
22
22
|
| Anchor preflight | `test/claims-anchors.test.mjs` | Exact anchors still locate, supported replacements parse, the check stays below one second, and CI reaches it before self-probe | <1 s |
|
|
23
23
|
| Fixture acceptance | `test/probe.fixture.test.mjs`, `test/probe.jest.test.mjs`, `test/probe.playwright.test.mjs` | The whole pipeline reproduces a known-answer oracle, one per runner | 10–50 s each |
|
|
24
|
-
| Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **
|
|
24
|
+
| Self-verification | `testguard.claims.json`, run by `npm run self:probe` | The tool's own invariants survive fault injection | **20.2 min** cold (measured by the v0.10.1 release job on 2026-09-19 at `70e9d61`: 154 faults, 924 defender runs, 1210 s); seconds when verdicts are reused, which is why PR CI restores previous evidence from cache. Budgeted at 1440 s in `testguard.cost-budget.json` for the additional four-fault live-tap claim and **gated** in CI — cost growth is reviewed instead of disappearing into a passing log |
|
|
25
25
|
| Install smoke | `.github/scripts/install-smoke.mjs` | The **packed tarball** runs with production dependencies only | ~20 s |
|
|
26
26
|
| Runtime budget | `ci.yml` | The suite has not silently started walking the wrong tree | gate, not a test |
|
|
27
27
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testguard-cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "testguard-cli",
|
|
9
|
-
"version": "0.10.
|
|
9
|
+
"version": "0.10.3",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"ajv": "8.20.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testguard-cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Include it from a project's .gitlab-ci.yml, pinned to a release:
|
|
4
4
|
#
|
|
5
5
|
# include:
|
|
6
|
-
# - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.
|
|
6
|
+
# - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.10.3/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
7
7
|
# inputs:
|
|
8
8
|
# dir: backend # where testguard.claims.json lives (default ".")
|
|
9
9
|
# post_note: true # post the brief as a merge-request note (needs TESTGUARD_GITLAB_TOKEN, api scope)
|
|
@@ -29,7 +29,7 @@ spec:
|
|
|
29
29
|
inputs:
|
|
30
30
|
version:
|
|
31
31
|
description: testguard-cli version to run
|
|
32
|
-
default: "0.10.
|
|
32
|
+
default: "0.10.3"
|
|
33
33
|
dir:
|
|
34
34
|
description: directory whose test runner is invoked (where testguard.claims.json lives)
|
|
35
35
|
default: "."
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
class Testguard < Formula
|
|
16
16
|
desc "Proves a test suite defends the claims a project makes"
|
|
17
17
|
homepage "https://github.com/raccioly/testguard"
|
|
18
|
-
url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.10.
|
|
19
|
-
sha256 "
|
|
18
|
+
url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.10.3.tgz"
|
|
19
|
+
sha256 "073d225733ecf5ba3da9fecc5952236a3fa22c46cb5981def3f568d08e1ca044"
|
|
20
20
|
license "MIT"
|
|
21
21
|
|
|
22
22
|
depends_on "node"
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "testguard-cli"
|
|
7
|
-
version = "0.10.
|
|
7
|
+
version = "0.10.3"
|
|
8
8
|
description = "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+)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -402,9 +402,14 @@ Rules:
|
|
|
402
402
|
4. **Never a silent pass on nothing.** A non-empty change whose files were
|
|
403
403
|
all excluded passes with an explicit "0 evaluated" line; a tool offers a
|
|
404
404
|
strict mode that fails it instead. An empty change is an honest `0`.
|
|
405
|
-
5. **The reference is
|
|
406
|
-
|
|
407
|
-
|
|
405
|
+
5. **The reference is inferred only from recorded intent.** Resolution stops
|
|
406
|
+
at the first available source: an explicit flag, `TESTGUARD_CHANGED_REF`, a
|
|
407
|
+
CI-provided base, the configured remote's symbolic default branch, then a
|
|
408
|
+
configured upstream whose branch name differs from the current branch.
|
|
409
|
+
Local discovery never compares a branch to its same-name remote-tracking
|
|
410
|
+
ref: that ref may already contain the whole change and yield a false empty
|
|
411
|
+
diff. It also declines to compare the default branch to itself. When Git
|
|
412
|
+
records no safe local base, the gate exits `3` and asks for `--changed`.
|
|
408
413
|
6. **Unclaimed changes precede every evidence state.** When the status
|
|
409
414
|
document knows a reference and finds uncovered files, its state is
|
|
410
415
|
`unclaimed-changes` and its next action is to write the claim, before any
|
|
@@ -13,7 +13,7 @@ export async function briefCommand({ projectDir, values, version }, io) {
|
|
|
13
13
|
// A detected base that does not resolve is silent here: the brief is a
|
|
14
14
|
// session-start hook's output and must never add noise; `status` and
|
|
15
15
|
// `gate` are where that warning is printed.
|
|
16
|
-
status = withChangedRef(resolveChangedRef({ explicit: values.changed }), (changedRef) => computeStatus({ projectDir, toolVersion: version, changedRef, includeDirty: values['include-dirty'], evidence: values.evidence ? evPath : undefined }));
|
|
16
|
+
status = withChangedRef(resolveChangedRef({ explicit: values.changed, projectDir }), (changedRef) => computeStatus({ projectDir, toolVersion: version, changedRef, includeDirty: values['include-dirty'], evidence: values.evidence ? evPath : undefined }));
|
|
17
17
|
} catch {
|
|
18
18
|
status = undefined; // a brief must never fail because status could not be computed
|
|
19
19
|
}
|