testguard-cli 0.9.2__tar.gz → 0.9.4__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.9.4/.github/scripts/approve-held-runs.sh +64 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/workflows/release.yml +5 -2
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/workflows/scheduled-release.yml +4 -50
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/CHANGELOG.md +33 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/PKG-INFO +5 -5
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/README.md +4 -4
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/action.yml +1 -1
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/docs/testguard-explained.html +1 -1
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/package-lock.json +2 -2
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/package.json +1 -1
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/pyproject.toml +1 -1
- testguard_cli-0.9.4/test/release-approval.test.mjs +78 -0
- testguard_cli-0.9.4/test/vitest-config.test.mjs +11 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/testguard.claims.json +100 -0
- testguard_cli-0.9.4/vitest.config.mjs +11 -0
- testguard_cli-0.9.2/vitest.config.mjs +0 -9
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.codex/hooks.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.docguard.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.docguardignore +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.gitattributes +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/dependabot.yml +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.gitignore +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.npmignore +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.npmrc +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/AGENTS.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/CLAUDE.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/CONTRIBUTING.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/LICENSE +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/PRIVACY.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/SECURITY.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/SUPPORT.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/bench/README.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/cli/testguard.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/docs-canonical/ARCHITECTURE.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/docs-canonical/ENVIRONMENT.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/docs-canonical/REQUIREMENTS.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/docs-canonical/SECURITY.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/docs-canonical/TEST-SPEC.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/test-results/.last-run.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/GATE-SEMANTICS.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/README.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/lib/validate.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/evidence.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/replay.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/claims/anchors.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/claims/load.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/cli.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/baseline.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/brief.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/gate.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/init.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/probe.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/commands/status.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/gate/changed.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/git.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/init/init.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/init/templates/SKILL.md +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/mcp/tools.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/contention.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/probe.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/pyimports.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/index.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/render.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/replay/label.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/replay/replay.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/scaffold/producers.python.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/status/status.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/util/glob.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/src/util/hash.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/admit.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/brief.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/calibration.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/claims-anchors.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/classify.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/contention.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/cost.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/discover.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/gate.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/glob.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/independence.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/init.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/inject.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/label.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/one-pager.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/probe-error.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/progress.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/pyimports.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/release-sync.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/replay-verdict.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/replay.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/samples/python-report.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/scaffold.python.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/status.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/test/writer.test.mjs +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/testguard.cost-budget.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/testguard.ignore.json +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.9.2 → testguard_cli-0.9.4}/testguard_cli/wrapper.py +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Clear GitHub's first-time-contributor hold from workflow runs created for a
|
|
4
|
+
# GITHUB_TOKEN-authored pull request. The repository requires that approval,
|
|
5
|
+
# and github-actions[bot] can never graduate from it because it does not author
|
|
6
|
+
# the squash commit that lands on main.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
HEAD_SHA="${1:?usage: approve-held-runs.sh HEAD_SHA PR_URL}"
|
|
11
|
+
PR_URL="${2:?usage: approve-held-runs.sh HEAD_SHA PR_URL}"
|
|
12
|
+
: "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}"
|
|
13
|
+
|
|
14
|
+
POLL_SECONDS="${TESTGUARD_HELD_RUN_POLL_SECONDS:-10}"
|
|
15
|
+
approved=0
|
|
16
|
+
|
|
17
|
+
held_ids() {
|
|
18
|
+
gh api "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${HEAD_SHA}" \
|
|
19
|
+
--jq '[.workflow_runs[] | select(.status == "action_required" or .conclusion == "action_required") | .id] | .[]'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
held_urls() {
|
|
23
|
+
gh api "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${HEAD_SHA}" \
|
|
24
|
+
--jq '[.workflow_runs[] | select(.status == "action_required" or .conclusion == "action_required") | .html_url] | .[]'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
echo "Clearing held workflow runs on $HEAD_SHA"
|
|
28
|
+
for attempt in 1 2 3 4 5 6; do
|
|
29
|
+
# Fail closed. Treating an API failure as "no held runs" would report that
|
|
30
|
+
# auto-merge can proceed while leaving the PR queued forever.
|
|
31
|
+
if ! HELD=$(held_ids); then
|
|
32
|
+
echo "::error::Could not inspect workflow approval holds for $HEAD_SHA; auto-merge on $PR_URL may remain blocked."
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
for id in $HELD; do
|
|
37
|
+
if gh api -X POST "repos/${GITHUB_REPOSITORY}/actions/runs/${id}/approve" >/dev/null 2>&1; then
|
|
38
|
+
echo " approved run $id"
|
|
39
|
+
approved=$((approved + 1))
|
|
40
|
+
else
|
|
41
|
+
# A run can leave action_required between the query and the POST. The
|
|
42
|
+
# final query decides whether that race is harmless.
|
|
43
|
+
echo " run $id needed no approval"
|
|
44
|
+
fi
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
# Runs appear asynchronously. Once one was approved, the first empty query
|
|
48
|
+
# is terminal; when this repository gate does not apply, three empty polls
|
|
49
|
+
# are enough to establish that without paying the full minute.
|
|
50
|
+
if [ -z "$HELD" ] && { [ "$approved" -gt 0 ] || [ "$attempt" -ge 3 ]; }; then
|
|
51
|
+
break
|
|
52
|
+
fi
|
|
53
|
+
if [ "$attempt" -lt 6 ]; then sleep "$POLL_SECONDS"; fi
|
|
54
|
+
done
|
|
55
|
+
|
|
56
|
+
if ! STILL=$(held_urls); then
|
|
57
|
+
echo "::error::Could not verify workflow approval holds for $HEAD_SHA; auto-merge on $PR_URL may remain blocked."
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
if [ -n "$STILL" ]; then
|
|
61
|
+
echo "::error::Workflow runs are still held for approval, so auto-merge on $PR_URL will wait forever. Approve them, or re-run this workflow: $STILL"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
echo "✅ no run is held for approval; auto-merge can complete"
|
|
@@ -236,8 +236,10 @@ jobs:
|
|
|
236
236
|
# change lands as a bot PR with native auto-merge armed, and this job
|
|
237
237
|
# dispatches ci.yml on the branch itself — a PR opened with GITHUB_TOKEN
|
|
238
238
|
# gets no pull_request run, but workflow_dispatch is the documented
|
|
239
|
-
# exception.
|
|
240
|
-
#
|
|
239
|
+
# exception. The PR's own runs may still appear held for first-time-
|
|
240
|
+
# contributor approval, so this job clears those holds before returning.
|
|
241
|
+
# That also means the merge triggers nothing here; the hourly sweep
|
|
242
|
+
# observes the merged formula and finds brew_missing=false.
|
|
241
243
|
#
|
|
242
244
|
# Idempotent: an existing branch is reused, an existing open PR is left
|
|
243
245
|
# alone (CI re-dispatched only if its head has no check runs), a closed
|
|
@@ -334,3 +336,4 @@ jobs:
|
|
|
334
336
|
else
|
|
335
337
|
echo "✅ $HEAD_SHA already has $RUNS check run(s)"
|
|
336
338
|
fi
|
|
339
|
+
.github/scripts/approve-held-runs.sh "$HEAD_SHA" "$PR"
|
|
@@ -170,56 +170,10 @@ jobs:
|
|
|
170
170
|
echo "✅ $HEAD_SHA already has $RUNS check run(s)"
|
|
171
171
|
fi
|
|
172
172
|
|
|
173
|
-
#
|
|
174
|
-
#
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
# PERMANENTLY one — it never authors a merged commit, so it can
|
|
178
|
-
# never graduate. Those held runs leave check suites QUEUED on the
|
|
179
|
-
# head sha forever, the status rollup never reaches a terminal
|
|
180
|
-
# state, and auto-merge waits on it even though the required
|
|
181
|
-
# contexts (test 20/22/24) are already green from the dispatched
|
|
182
|
-
# run above. v0.9.0 sat here until a human clicked Approve.
|
|
183
|
-
#
|
|
184
|
-
# GITHUB_TOKEN may clear it: `actions: write` carries the approve
|
|
185
|
-
# permission, measured 201 on a bot PR's own held run. It may not
|
|
186
|
-
# approve a REVIEW, which is a different gate and still ours alone.
|
|
187
|
-
#
|
|
188
|
-
# Runs appear asynchronously, so poll. Approving an already-running
|
|
189
|
-
# run answers 403 "not waiting for approval" — success, not failure.
|
|
190
|
-
echo "Clearing held workflow runs on $HEAD_SHA"
|
|
191
|
-
approved=0
|
|
192
|
-
for attempt in 1 2 3 4 5 6; do
|
|
193
|
-
HELD=$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${HEAD_SHA}" \
|
|
194
|
-
--jq '[.workflow_runs[] | select(.status == "action_required" or .conclusion == "action_required") | .id] | .[]' 2>/dev/null || true)
|
|
195
|
-
for id in $HELD; do
|
|
196
|
-
if gh api -X POST "repos/${GITHUB_REPOSITORY}/actions/runs/${id}/approve" >/dev/null 2>&1; then
|
|
197
|
-
echo " approved run $id"
|
|
198
|
-
approved=$((approved + 1))
|
|
199
|
-
else
|
|
200
|
-
echo " run $id needed no approval"
|
|
201
|
-
fi
|
|
202
|
-
done
|
|
203
|
-
# Stop as soon as the picture is settled, rather than always
|
|
204
|
-
# paying the full poll: nothing held after something was
|
|
205
|
-
# approved means we are done, and nothing held by the third
|
|
206
|
-
# look means the gate is not applying to this run at all.
|
|
207
|
-
if [ -z "$HELD" ] && { [ "$approved" -gt 0 ] || [ "$attempt" -ge 3 ]; }; then
|
|
208
|
-
break
|
|
209
|
-
fi
|
|
210
|
-
sleep 10
|
|
211
|
-
done
|
|
212
|
-
|
|
213
|
-
# Never leave this silent: a run still held here means auto-merge
|
|
214
|
-
# will wait forever, which is the failure this whole block exists
|
|
215
|
-
# to remove. Say so, and name what to click.
|
|
216
|
-
STILL=$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs?head_sha=${HEAD_SHA}" \
|
|
217
|
-
--jq '[.workflow_runs[] | select(.status == "action_required" or .conclusion == "action_required") | .html_url] | .[]' 2>/dev/null || true)
|
|
218
|
-
if [ -n "$STILL" ]; then
|
|
219
|
-
echo "::error::Workflow runs are still held for approval, so auto-merge on $PR will wait forever. Approve them, or re-run this workflow: $STILL"
|
|
220
|
-
exit 1
|
|
221
|
-
fi
|
|
222
|
-
echo "✅ no run is held for approval; auto-merge can complete"
|
|
173
|
+
# Clear the first-time-contributor holds on this bot-created PR's
|
|
174
|
+
# workflow runs. The Homebrew PR path uses the same helper so the
|
|
175
|
+
# two release automations cannot drift again.
|
|
176
|
+
.github/scripts/approve-held-runs.sh "$HEAD_SHA" "$PR"
|
|
223
177
|
else
|
|
224
178
|
cat /tmp/pr-err
|
|
225
179
|
if grep -q 'not permitted to create or approve pull requests' /tmp/pr-err; then
|
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.4] - 2026-09-19
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.9.3`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- fix(test): ignore nested agent worktrees (#133)
|
|
17
|
+
- chore(homebrew): sha256 for v0.9.3 (#131)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- The canonical `npm test` command no longer collects suites from live nested
|
|
23
|
+
worktrees under `.claude/`. A parked checkout can remain safely available
|
|
24
|
+
without its unfinished tests or fixtures contaminating the main tree's gate.
|
|
25
|
+
|
|
26
|
+
## [0.9.3] - 2026-09-19
|
|
27
|
+
|
|
28
|
+
Automated weekly release — everything merged since `v0.9.2`.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- fix(release): clear held Homebrew PR runs (#129)
|
|
33
|
+
- chore(homebrew): sha256 for v0.9.2 (#128)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- Bot-created Homebrew checksum PRs now clear GitHub's first-time-contributor
|
|
39
|
+
workflow holds themselves, using the same fail-closed helper as scheduled
|
|
40
|
+
release PRs. A release no longer finishes publishing while leaving its final
|
|
41
|
+
formula update waiting for a manual Approve click.
|
|
42
|
+
|
|
10
43
|
## [0.9.2] - 2026-09-19
|
|
11
44
|
|
|
12
45
|
Automated weekly release — everything merged since `v0.9.1`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
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.9.
|
|
154
|
+
| GitHub Action | `uses: raccioly/testguard@v0.9.4` — 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.9.
|
|
156
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.9.4/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
|
|
@@ -493,12 +493,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
493
493
|
# GitHub Actions
|
|
494
494
|
- uses: actions/checkout@v4
|
|
495
495
|
with: { fetch-depth: 0 }
|
|
496
|
-
- uses: raccioly/testguard@v0.9.
|
|
496
|
+
- uses: raccioly/testguard@v0.9.4
|
|
497
497
|
with: { command: gate }
|
|
498
498
|
|
|
499
499
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
500
500
|
include:
|
|
501
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.
|
|
501
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.4/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
502
502
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
503
503
|
```
|
|
504
504
|
|
|
@@ -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.9.
|
|
130
|
+
| GitHub Action | `uses: raccioly/testguard@v0.9.4` — 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.9.
|
|
132
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.9.4/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
|
|
@@ -469,12 +469,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
469
469
|
# GitHub Actions
|
|
470
470
|
- uses: actions/checkout@v4
|
|
471
471
|
with: { fetch-depth: 0 }
|
|
472
|
-
- uses: raccioly/testguard@v0.9.
|
|
472
|
+
- uses: raccioly/testguard@v0.9.4
|
|
473
473
|
with: { command: gate }
|
|
474
474
|
|
|
475
475
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
476
476
|
include:
|
|
477
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.
|
|
477
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.4/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
478
478
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
479
479
|
```
|
|
480
480
|
|
|
@@ -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.9.
|
|
147
|
+
<div class="meta">Technical Brief · v0.9.4 · 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
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testguard-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "testguard-cli",
|
|
9
|
-
"version": "0.9.
|
|
9
|
+
"version": "0.9.4",
|
|
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.9.
|
|
3
|
+
"version": "0.9.4",
|
|
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.9.
|
|
6
|
+
# - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.9.4/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.9.
|
|
32
|
+
default: "0.9.4"
|
|
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.9.
|
|
19
|
-
sha256 "
|
|
18
|
+
url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.9.4.tgz"
|
|
19
|
+
sha256 "3ab202d339b014ea5d8a11cc143bd1bdef6ea08120a513029f7537c288ba3195"
|
|
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.9.
|
|
7
|
+
version = "0.9.4"
|
|
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" }
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { chmodSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
const ROOT = new URL('..', import.meta.url).pathname;
|
|
8
|
+
const SCRIPT = join(ROOT, '.github', 'scripts', 'approve-held-runs.sh');
|
|
9
|
+
|
|
10
|
+
const run = (scenario) => {
|
|
11
|
+
const dir = mkdtempSync(join(tmpdir(), 'tg-held-runs-'));
|
|
12
|
+
const state = join(dir, 'state');
|
|
13
|
+
const gh = join(dir, 'gh');
|
|
14
|
+
writeFileSync(gh, `#!/bin/sh
|
|
15
|
+
set -eu
|
|
16
|
+
case "$*" in
|
|
17
|
+
*'/approve'*)
|
|
18
|
+
printf 'approved\\n' >> "$FAKE_GH_STATE"
|
|
19
|
+
[ "$FAKE_GH_SCENARIO" != 'stuck' ]
|
|
20
|
+
;;
|
|
21
|
+
*'.html_url'*)
|
|
22
|
+
if [ "$FAKE_GH_SCENARIO" = 'stuck' ]; then
|
|
23
|
+
printf 'https://github.example/runs/42\\n'
|
|
24
|
+
fi
|
|
25
|
+
;;
|
|
26
|
+
*'.id'*)
|
|
27
|
+
if [ "$FAKE_GH_SCENARIO" = 'query-error' ]; then exit 2; fi
|
|
28
|
+
if [ "$FAKE_GH_SCENARIO" = 'stuck' ] || { [ "$FAKE_GH_SCENARIO" = 'held-clears' ] && [ ! -s "$FAKE_GH_STATE" ]; }; then
|
|
29
|
+
printf '42\\n'
|
|
30
|
+
fi
|
|
31
|
+
;;
|
|
32
|
+
*) exit 64 ;;
|
|
33
|
+
esac
|
|
34
|
+
`);
|
|
35
|
+
chmodSync(gh, 0o755);
|
|
36
|
+
const result = spawnSync('/bin/bash', [SCRIPT, 'abc1234', 'https://github.example/pull/7'], {
|
|
37
|
+
encoding: 'utf8',
|
|
38
|
+
env: {
|
|
39
|
+
...process.env,
|
|
40
|
+
PATH: `${dir}:/usr/bin:/bin`,
|
|
41
|
+
GITHUB_REPOSITORY: 'owner/repo',
|
|
42
|
+
FAKE_GH_SCENARIO: scenario,
|
|
43
|
+
FAKE_GH_STATE: state,
|
|
44
|
+
TESTGUARD_HELD_RUN_POLL_SECONDS: '0',
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
return { ...result, approvals: readFileSync(state, { encoding: 'utf8', flag: 'a+' }) };
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
describe('release workflow approval holds', () => {
|
|
51
|
+
it('both bot-created PR paths invoke the same approval helper', () => {
|
|
52
|
+
for (const workflow of ['scheduled-release.yml', 'release.yml']) {
|
|
53
|
+
const text = readFileSync(join(ROOT, '.github', 'workflows', workflow), 'utf8');
|
|
54
|
+
expect(text).toContain('.github/scripts/approve-held-runs.sh "$HEAD_SHA" "$PR"');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('approves a held run and verifies that the hold cleared', () => {
|
|
59
|
+
const result = run('held-clears');
|
|
60
|
+
expect(result.status, result.stderr).toBe(0);
|
|
61
|
+
expect(result.approvals).toBe('approved\n');
|
|
62
|
+
expect(result.stdout).toMatch(/approved run 42/);
|
|
63
|
+
expect(result.stdout).toMatch(/no run is held for approval/);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('fails closed when GitHub cannot report the approval state', () => {
|
|
67
|
+
const result = run('query-error');
|
|
68
|
+
expect(result.status).toBe(1);
|
|
69
|
+
expect(result.stdout).toMatch(/Could not inspect workflow approval holds/);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('fails with the held run URL when approval never clears', () => {
|
|
73
|
+
const result = run('stuck');
|
|
74
|
+
expect(result.status).toBe(1);
|
|
75
|
+
expect(result.stdout).toMatch(/will wait forever/);
|
|
76
|
+
expect(result.stdout).toMatch(/https:\/\/github\.example\/runs\/42/);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import config from '../vitest.config.mjs';
|
|
3
|
+
|
|
4
|
+
describe('the root test boundary', () => {
|
|
5
|
+
it('never collects fixtures or agent-managed nested worktrees', () => {
|
|
6
|
+
expect(config.test.exclude).toEqual(expect.arrayContaining([
|
|
7
|
+
'fixtures/**',
|
|
8
|
+
'.claude/**',
|
|
9
|
+
]));
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -3777,6 +3777,106 @@
|
|
|
3777
3777
|
"preflight",
|
|
3778
3778
|
"cost"
|
|
3779
3779
|
]
|
|
3780
|
+
},
|
|
3781
|
+
{
|
|
3782
|
+
"id": "TG-RELEASE-BOT-CLEARS-HELD-RUNS",
|
|
3783
|
+
"statement": "Every GITHUB_TOKEN-authored release pull request clears first-time-contributor workflow holds before its creating workflow succeeds. Both the scheduled version PR and the post-publish Homebrew checksum PR use one fail-closed helper, so an API failure or a hold that remains visible is an error rather than a silently queued auto-merge.",
|
|
3784
|
+
"source": {
|
|
3785
|
+
"kind": "manual",
|
|
3786
|
+
"ref": "v0.9.2 Homebrew release field report"
|
|
3787
|
+
},
|
|
3788
|
+
"severity": "high",
|
|
3789
|
+
"producedBy": {
|
|
3790
|
+
"producer": "human",
|
|
3791
|
+
"by": "maintainer",
|
|
3792
|
+
"at": "2026-09-19T00:00:00Z"
|
|
3793
|
+
},
|
|
3794
|
+
"defendedBy": [
|
|
3795
|
+
"test/release-approval.test.mjs"
|
|
3796
|
+
],
|
|
3797
|
+
"faults": [
|
|
3798
|
+
{
|
|
3799
|
+
"id": "F1",
|
|
3800
|
+
"description": "The post-publish Homebrew PR skips the approval helper and can remain queued after the release workflow reports success.",
|
|
3801
|
+
"faultClass": "statement-deleted",
|
|
3802
|
+
"file": ".github/workflows/release.yml",
|
|
3803
|
+
"find": " .github/scripts/approve-held-runs.sh \"$HEAD_SHA\" \"$PR\"",
|
|
3804
|
+
"replace": " echo \"approval hold check skipped\"",
|
|
3805
|
+
"producedBy": {
|
|
3806
|
+
"producer": "human",
|
|
3807
|
+
"by": "maintainer",
|
|
3808
|
+
"at": "2026-09-19T00:00:00Z"
|
|
3809
|
+
}
|
|
3810
|
+
},
|
|
3811
|
+
{
|
|
3812
|
+
"id": "F2",
|
|
3813
|
+
"description": "The scheduled release PR stops using the shared approval helper, allowing the two bot-created PR paths to drift again.",
|
|
3814
|
+
"faultClass": "statement-deleted",
|
|
3815
|
+
"file": ".github/workflows/scheduled-release.yml",
|
|
3816
|
+
"find": " .github/scripts/approve-held-runs.sh \"$HEAD_SHA\" \"$PR\"",
|
|
3817
|
+
"replace": " echo \"approval hold check skipped\"",
|
|
3818
|
+
"producedBy": {
|
|
3819
|
+
"producer": "human",
|
|
3820
|
+
"by": "maintainer",
|
|
3821
|
+
"at": "2026-09-19T00:00:00Z"
|
|
3822
|
+
}
|
|
3823
|
+
},
|
|
3824
|
+
{
|
|
3825
|
+
"id": "F3",
|
|
3826
|
+
"description": "A failed GitHub API query is treated as an empty hold list, so the helper reports success without knowing whether auto-merge is blocked.",
|
|
3827
|
+
"faultClass": "condition-forced",
|
|
3828
|
+
"file": ".github/scripts/approve-held-runs.sh",
|
|
3829
|
+
"find": " if ! HELD=$(held_ids); then",
|
|
3830
|
+
"replace": " if HELD=$(held_ids); then",
|
|
3831
|
+
"producedBy": {
|
|
3832
|
+
"producer": "human",
|
|
3833
|
+
"by": "maintainer",
|
|
3834
|
+
"at": "2026-09-19T00:00:00Z"
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
],
|
|
3838
|
+
"tags": [
|
|
3839
|
+
"release",
|
|
3840
|
+
"automation",
|
|
3841
|
+
"github-actions"
|
|
3842
|
+
]
|
|
3843
|
+
},
|
|
3844
|
+
{
|
|
3845
|
+
"id": "TG-ROOT-TEST-IGNORES-NESTED-WORKTREES",
|
|
3846
|
+
"statement": "The root Vitest gate describes only this checkout: it never collects fixtures or agent-managed nested worktrees under .claude, so parked unmerged work cannot create false failures in npm test.",
|
|
3847
|
+
"source": {
|
|
3848
|
+
"kind": "manual",
|
|
3849
|
+
"ref": "https://github.com/raccioly/testguard/issues/132"
|
|
3850
|
+
},
|
|
3851
|
+
"severity": "high",
|
|
3852
|
+
"producedBy": {
|
|
3853
|
+
"producer": "human",
|
|
3854
|
+
"by": "maintainer",
|
|
3855
|
+
"at": "2026-09-19T00:00:00Z"
|
|
3856
|
+
},
|
|
3857
|
+
"defendedBy": [
|
|
3858
|
+
"test/vitest-config.test.mjs"
|
|
3859
|
+
],
|
|
3860
|
+
"faults": [
|
|
3861
|
+
{
|
|
3862
|
+
"id": "F1",
|
|
3863
|
+
"description": "The root test boundary stops excluding agent-managed nested worktrees, so npm test can collect an unrelated checkout.",
|
|
3864
|
+
"faultClass": "field-dropped",
|
|
3865
|
+
"file": "vitest.config.mjs",
|
|
3866
|
+
"find": " exclude: [...configDefaults.exclude, 'fixtures/**', '.claude/**'],",
|
|
3867
|
+
"replace": " exclude: [...configDefaults.exclude, 'fixtures/**'],",
|
|
3868
|
+
"producedBy": {
|
|
3869
|
+
"producer": "human",
|
|
3870
|
+
"by": "maintainer",
|
|
3871
|
+
"at": "2026-09-19T00:00:00Z"
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
],
|
|
3875
|
+
"tags": [
|
|
3876
|
+
"tests",
|
|
3877
|
+
"worktrees",
|
|
3878
|
+
"isolation"
|
|
3879
|
+
]
|
|
3780
3880
|
}
|
|
3781
3881
|
]
|
|
3782
3882
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineConfig, configDefaults } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
// fixtures/ contains a deliberately flaky suite; it is driven by the
|
|
6
|
+
// tool's own tests, never collected directly. Agent-managed worktrees are
|
|
7
|
+
// separate checkouts too: collecting them makes this tree's result depend
|
|
8
|
+
// on unrelated, potentially unfinished work.
|
|
9
|
+
exclude: [...configDefaults.exclude, 'fixtures/**', '.claude/**'],
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { defineConfig, configDefaults } from 'vitest/config';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
// fixtures/ contains a deliberately flaky suite; it is driven by the
|
|
6
|
-
// tool's own tests, never collected directly.
|
|
7
|
-
exclude: [...configDefaults.exclude, 'fixtures/**'],
|
|
8
|
-
},
|
|
9
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|