testguard-cli 0.18.0__tar.gz → 0.18.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/CHANGELOG.md +21 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/PKG-INFO +5 -5
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/README.md +4 -4
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/action.yml +1 -1
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs/testguard-explained.html +1 -1
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs/testguard-explained.pdf +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/package-lock.json +2 -2
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/package.json +1 -1
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/packaging/gitlab/testguard.gitlab-ci.yml +2 -2
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/packaging/homebrew/testguard.rb +2 -2
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/pyproject.toml +1 -1
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/discovery.mjs +10 -1
- testguard_cli-0.18.1/test/probe.node.test.mjs +200 -0
- testguard_cli-0.18.1/test/runner-node-admission.test.mjs +35 -0
- testguard_cli-0.18.1/test/runner-report-allocation.test.mjs +81 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/testguard.claims.json +32 -1
- testguard_cli-0.18.0/test/probe.node.test.mjs +0 -93
- testguard_cli-0.18.0/test/runner-node.test.mjs +0 -127
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.agents/skills/testguard/SKILL.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.docguard.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.docguardignore +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.gitattributes +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/dependabot.yml +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/approve-held-runs.sh +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/benchmark-node-runner.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/build-one-pager.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/check-cost-budget.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/check-node-fixture.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/check-python-fixture.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/publish-homebrew-tap.sh +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/workflows/release.yml +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/workflows/scheduled-release.yml +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.gitignore +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.npmignore +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.npmrc +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.pre-commit-config.yaml +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/AGENTS.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/CLAUDE.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/CONTRIBUTING.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/DRIFT-LOG.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/LICENSE +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/PRIVACY.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/SECURITY.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/SUPPORT.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/bench/README.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/cli/testguard.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs-canonical/ARCHITECTURE.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs-canonical/DATA-MODEL.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs-canonical/ENVIRONMENT.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs-canonical/PRIOR-ART.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs-canonical/REQUIREMENTS.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs-canonical/SECURITY.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/docs-canonical/TEST-SPEC.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/test/export-mocked.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/.gitignore +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/README.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/expected.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/jest.config.js +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/package.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/src/export.js +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/src/redact.js +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/test/export-mocked.test.js +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/test/flaky.test.js +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/test/redact.test.js +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-jest/testguard.claims.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/.gitignore +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/README.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/expected.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/package.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/src/policy.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/src/unused.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/test/flaky.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/test/mixed.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/test/policy.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-node/testguard.claims.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/.gitignore +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/README.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/e2e/flaky.spec.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/e2e/toggle.spec.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/expected.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/package-lock.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/package.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/playwright.config.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/src/toggle.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/test/toggle.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/testguard.claims.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-playwright/vitest.config.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/.gitignore +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/README.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/__init__.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/export.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/orphan.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/pipeline.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/demo/redact.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/expected.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/testguard.claims.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/tests/test_export_mocked.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/tests/test_flaky.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/tests/test_patched.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/fixtures/known-answer-python/tests/test_redact.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/GATE-SEMANTICS.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/README.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/annotations.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/authoring-input.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/draft-append.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/annotations.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/authoring-input.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/brief.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/calibration-websec.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/claims-methods.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-disturbed-run.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-negative-control.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-persistence-signal.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-provisional.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-python.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-resources.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence-scan-method.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/evidence.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/gate.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/replay.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/status-resource-cost.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/examples/status.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.backoff-cell-not-wilson.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.breakdown-does-not-sum-to-n.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.ci-not-wilson.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.compound-key-arity-mismatch.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.fallback-without-basis.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.p-not-positives-over-n.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.fault-injection-without-a-substitution.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.method-detail-on-a-specified-method.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.annotated-signal-without-reason.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.attribute-signal-not-a-defender.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.attribute-signal-without-attributes.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.byrunner-file-not-resolved.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.contention-without-runners.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.discovered-defender-mocks-subject.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.fault-injection-without-its-evidence.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.ignored-dirty-on-snapshot.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.method-detail-on-an-injecting-run.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.negative-control-not-reached-but-survived.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.negative-control-reached-on-a-kill.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.persistence-signal-not-a-defender.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.persistence-signal-on-a-non-survivor.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.persistence-signal-without-reason.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.probe-error-without-message.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.resource-accounting.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.restore-skipped-in-place.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.serial-worker-conflict.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.single-run-without-provisional.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.target-not-imported-but-killed.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/gate.counts-disagree.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/gate.exit-zero-with-uncovered.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/gate.ignore-cover-without-pattern.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/replay.blind-with-a-failing-run.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/replay.duplicate-patch-id.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/replay.nocover-when-no-test-file-remained.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.claim-without-file.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.clean-with-new-findings.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.invalid-anchor-hidden.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.partial-scope-counts-disagree.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.resource-accounting.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.surface-counts-disagree.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.unclaimed-hidden-behind-unproven.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/invalid/status.write-test-without-target.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/origin-policy.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/origin-summaries.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/origins.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/conformance/schemas.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/lib/authoring-input.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/lib/origins.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/lib/validate.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/lib/wilson.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/annotations.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/authoring-input.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/brief.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/concerns.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/evidence.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/gate.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/replay.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/status.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/spec/schemas/sweep.schema.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/admit/admit.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/brief/brief.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/claims/anchors.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/claims/annotation-placement.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/claims/load.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/claims/removed.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/cli.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/command-budget.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/admit.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/annotations.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/baseline.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/brief.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/concerns.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/gate.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/init.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/mcp.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/probe.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/replay.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/status.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/commands/sweep.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/gate/changed.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/git.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/init/init.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/init/templates/SKILL.md +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/mcp/server.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/mcp/tools.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/attribution.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/contention.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/cost.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/imports.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/independence.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/mocks.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/origin-policy.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/policy-freshness.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/probe.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/progress.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/pyimports.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/index.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/jest.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/lifecycle.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/node/entry.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/node/report.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/node/runner.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/node-test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/playwright.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/python/_testguard_pytest_plugin.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/python/_testguard_report.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/python/_testguard_unittest_main.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/python.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/shared.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/runners/vitest.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/universe.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/render.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/replay/label.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/replay/replay.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/admission.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/append.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/apply-append.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/fix-input.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/fix-paths.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/intent.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/open-append.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/plan-append.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/plan-intent-input.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/prepare-append.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/producers.python.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/request.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/status/status.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/status/surface.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/supply/concerns.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/supply/feedback.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/supply/persistence.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/supply/savepath.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/supply/select.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/sweep/pool.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/sweep/sweep.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/util/glob.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/src/util/hash.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/admit-decision.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/admit.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/anchor-admission.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/annotation-advisory-limits.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/annotation-advisory.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/annotation-apply.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/annotation-cli.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/annotation-placement.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/attribution.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/brief.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/calibration.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/claim-selection.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/claims-anchors.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/claims-since.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/classify.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/cli-unexpected-error.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/command-budget-write-boundaries.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/command-budget.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/contention.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/cost.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/discover.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/discovery-config.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/draft-admission.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/draft-append-apply.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/draft-append-cli.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/draft-append-open.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/draft-append-plan.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/draft-append-prepare.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/draft-append.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/evidence-persistence-signal.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/fault-defender-orchestration.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/fault-defenders.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/fix-input.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/fix-inventory.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/fix-nested.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/fix-paths.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/fix-projects.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/gate-ref.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/gate.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/gitlab-template.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/glob.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/helpers/gate-project.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/helpers/probe-assertions.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/helpers/release-project.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/imports.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/independence.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/init.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/inject.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/intent-authoring.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/intent-document.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/intent-input-cli.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/intent-input-plan.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/intent-input-request.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/intent-project.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/label.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/mcp-next.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/mcp-protocol.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/mcp.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/mocks.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/native-universe-hash.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/native-universe.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/negative-control-capabilities.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/negative-control.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/one-pager.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/origin-policy-admission.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/origin-policy-cli.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/origin-policy-projections.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/origin-policy-request.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/origin-policy.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/origin-reporting.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/origin-summary.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/policy-freshness.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-control.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-dirty-admission.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-dirty-paths.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-error-boundary.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-error.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-isolation.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-native-manifest.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-persistence-signal.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe-records.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe.fixture.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe.jest.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe.playwright.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/probe.python.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/progress.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/pyimports.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/release-approval.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/release-sync.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/release-tap.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/release-version.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/replay-mixed-runners.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/replay-no-test-file-left.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/replay-python-defenders.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/replay-selection.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/replay-verdict.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/replay.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/resource-safety.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/resource-timing.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-argv.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-discovery.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-node-controller.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-node-report.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-output-bounds.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-playwright.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-project-resolution.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-python.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-report.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-selection-blind.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/runner-worker-argv.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/samples/playwright-report.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/samples/python-report-collect-error.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/samples/python-report.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/scaffold-admitted-source.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/scaffold-placeholder.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/scaffold.python.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/status.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/supply-concerns.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/supply-feedback.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/supply-persistence.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/supply-savepath.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/supply-select.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/surface.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/sweep-pool.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/sweep.fixture.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/sweep.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/vitest-config.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/worker-policy.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/test/writer.test.mjs +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/testguard.cost-budget.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/testguard.ignore.json +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.18.0 → testguard_cli-0.18.1}/vitest.config.mjs +0 -0
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.18.1] - 2026-10-01
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.18.0`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- test: consolidate native runtime acceptance checks
|
|
17
|
+
- perf: isolate native admission mutation defenders
|
|
18
|
+
- perf: size bounded report buffers to observed files
|
|
19
|
+
- chore(homebrew): sha256 for v0.18.0 (#194)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Runner report and discovery-config reads reserve memory for the observed
|
|
25
|
+
file size instead of the maximum allowed size, retaining the hard byte
|
|
26
|
+
ceiling and checking file growth before parsing.
|
|
27
|
+
- Native-runner admission self-probes use focused defenders, preserving the
|
|
28
|
+
fault recipes and full runner acceptance coverage while avoiding unrelated
|
|
29
|
+
timeout and process-cleanup checks for each admission confirmation.
|
|
30
|
+
|
|
10
31
|
## [0.18.0] - 2026-10-01
|
|
11
32
|
|
|
12
33
|
Automated weekly release — everything merged since `v0.17.0`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.18.
|
|
3
|
+
Version: 0.18.1
|
|
4
4
|
Summary: Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest. Ships the Node.js CLI (requires Node.js 20+).
|
|
5
5
|
Project-URL: Homepage, https://github.com/raccioly/testguard
|
|
6
6
|
Project-URL: Documentation, https://github.com/raccioly/testguard#readme
|
|
@@ -151,9 +151,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
|
|
|
151
151
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
152
152
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
153
153
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
154
|
-
| GitHub Action | `uses: raccioly/testguard@v0.18.
|
|
154
|
+
| GitHub Action | `uses: raccioly/testguard@v0.18.1` — see [`action.yml`](./action.yml) |
|
|
155
155
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
156
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.18.
|
|
156
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
|
|
157
157
|
|
|
158
158
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
159
159
|
less than that many days ago (`ENOVERSIONS`); install that one with
|
|
@@ -809,12 +809,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
809
809
|
# GitHub Actions
|
|
810
810
|
- uses: actions/checkout@v4
|
|
811
811
|
with: { fetch-depth: 0 }
|
|
812
|
-
- uses: raccioly/testguard@v0.18.
|
|
812
|
+
- uses: raccioly/testguard@v0.18.1
|
|
813
813
|
with: { command: gate }
|
|
814
814
|
|
|
815
815
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
816
816
|
include:
|
|
817
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.
|
|
817
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
818
818
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
819
819
|
```
|
|
820
820
|
|
|
@@ -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.18.
|
|
130
|
+
| GitHub Action | `uses: raccioly/testguard@v0.18.1` — see [`action.yml`](./action.yml) |
|
|
131
131
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
132
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.18.
|
|
132
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/packaging/gitlab/testguard.gitlab-ci.yml` with `inputs:` — see [`packaging/gitlab/`](./packaging/gitlab/testguard.gitlab-ci.yml) |
|
|
133
133
|
|
|
134
134
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
135
135
|
less than that many days ago (`ENOVERSIONS`); install that one with
|
|
@@ -785,12 +785,12 @@ detected base that does not resolve is a warning for `status` and `brief`
|
|
|
785
785
|
# GitHub Actions
|
|
786
786
|
- uses: actions/checkout@v4
|
|
787
787
|
with: { fetch-depth: 0 }
|
|
788
|
-
- uses: raccioly/testguard@v0.18.
|
|
788
|
+
- uses: raccioly/testguard@v0.18.1
|
|
789
789
|
with: { command: gate }
|
|
790
790
|
|
|
791
791
|
# GitLab CI — the component-shaped template: gate + probe, brief as an artifact and, opted in, as a merge-request note
|
|
792
792
|
include:
|
|
793
|
-
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.
|
|
793
|
+
- remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/packaging/gitlab/testguard.gitlab-ci.yml'
|
|
794
794
|
inputs: { dir: backend, post_note: true } # post_note needs TESTGUARD_GITLAB_TOKEN (api scope); one note, updated in place
|
|
795
795
|
```
|
|
796
796
|
|
|
@@ -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.18.
|
|
147
|
+
<div class="meta">Technical Brief · v0.18.1 · 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.18.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "testguard-cli",
|
|
9
|
-
"version": "0.18.
|
|
9
|
+
"version": "0.18.1",
|
|
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.18.
|
|
3
|
+
"version": "0.18.1",
|
|
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.18.
|
|
6
|
+
# - remote: 'https://raw.githubusercontent.com/raccioly/testguard/v0.18.1/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.18.
|
|
32
|
+
default: "0.18.1"
|
|
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.18.
|
|
19
|
-
sha256 "
|
|
18
|
+
url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.18.1.tgz"
|
|
19
|
+
sha256 "b722d8656f634198a483ff50db7891c21cfccae3aa136faaf9436fc79df33d7d"
|
|
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.18.
|
|
7
|
+
version = "0.18.1"
|
|
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" }
|
|
@@ -130,12 +130,21 @@ const readBoundedFile = (path, maxBytes, label, { optional = false } = {}) => {
|
|
|
130
130
|
const stat = fstatSync(descriptor);
|
|
131
131
|
if (!stat.isFile()) throw new DiscoveryError(`${label} is not a regular file`);
|
|
132
132
|
if (stat.size > maxBytes) throw new DiscoveryError(`${label} exceeded ${maxBytes} bytes`);
|
|
133
|
-
|
|
133
|
+
// Reserve the observed file size, not the allowance for the largest report.
|
|
134
|
+
// The extra byte detects growth; a full buffer grows within the same hard
|
|
135
|
+
// ceiling so a concurrently appended suffix can never be silently dropped.
|
|
136
|
+
let bytes = Buffer.alloc(stat.size + 1);
|
|
134
137
|
let length = 0;
|
|
135
138
|
while (length < bytes.length) {
|
|
136
139
|
const read = readSync(descriptor, bytes, length, bytes.length - length, null);
|
|
137
140
|
if (read === 0) break;
|
|
138
141
|
length += read;
|
|
142
|
+
if (length > maxBytes) throw new DiscoveryError(`${label} exceeded ${maxBytes} bytes`);
|
|
143
|
+
if (length === bytes.length) {
|
|
144
|
+
const grown = Buffer.alloc(Math.min(maxBytes + 1, Math.max(4096, bytes.length * 2)));
|
|
145
|
+
bytes.copy(grown, 0, 0, length);
|
|
146
|
+
bytes = grown;
|
|
147
|
+
}
|
|
139
148
|
}
|
|
140
149
|
if (length > maxBytes) throw new DiscoveryError(`${label} exceeded ${maxBytes} bytes`);
|
|
141
150
|
return bytes.subarray(0, length);
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// @req FR-10
|
|
2
|
+
// @req FR-16
|
|
3
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
4
|
+
import { cpSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync, existsSync, symlinkSync } from 'node:fs';
|
|
5
|
+
import { join, dirname } from 'node:path';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
9
|
+
import { probe } from '../src/probe/probe.mjs';
|
|
10
|
+
import { loadClaims } from '../src/claims/load.mjs';
|
|
11
|
+
import { validate } from '../spec/lib/validate.mjs';
|
|
12
|
+
import { run, discoverTests } from '../src/probe/runners/node-test.mjs';
|
|
13
|
+
import { selectRunner } from '../src/probe/runners/index.mjs';
|
|
14
|
+
|
|
15
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
16
|
+
const FIXTURE = join(ROOT, 'fixtures/known-answer-node');
|
|
17
|
+
const expected = JSON.parse(readFileSync(join(FIXTURE, 'expected.json'), 'utf8'));
|
|
18
|
+
describe('native Node known-answer proof', () => {
|
|
19
|
+
let dir, claims, evidence;
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
dir = mkdtempSync(join(tmpdir(), 'tg-node-fixture-'));
|
|
22
|
+
cpSync(FIXTURE, dir, { recursive: true });
|
|
23
|
+
for (const args of [['init','-q'],['add','.'],['-c','user.name=fixture','-c','user.email=fixture@example.invalid','commit','-qm','fixture']]) execFileSync('git',args,{cwd:dir,timeout:5000});
|
|
24
|
+
claims = loadClaims(join(dir,'testguard.claims.json'));
|
|
25
|
+
evidence = await probe({ projectDir:dir, claims, runnerName:'node-test', confirmRuns:3, budgetMs:5000, mode:'worktree', toolVersion:'test' });
|
|
26
|
+
}, 30_000);
|
|
27
|
+
afterAll(() => { if(dir)rmSync(dir,{recursive:true,force:true}); });
|
|
28
|
+
it('reproduces every independently verified verdict and reason', () => {
|
|
29
|
+
const actual = Object.fromEntries(evidence.records.map((r) => [`${r.claim.id}/${r.subject.id}`, { verdict:r.verdict, reason:r.detail.reason }]));
|
|
30
|
+
for (const [id, oracle] of Object.entries(expected.expected)) {
|
|
31
|
+
expect(actual[id]?.verdict,id).toBe(oracle.verdict);
|
|
32
|
+
if(oracle.reason)expect(actual[id]?.reason,id).toBe(oracle.reason);
|
|
33
|
+
}
|
|
34
|
+
expect(Object.keys(actual)).toHaveLength(Object.keys(expected.expected).length);
|
|
35
|
+
});
|
|
36
|
+
it('emits conforming builtin-runtime evidence with N/N green baselines and assertion-only kills', () => {
|
|
37
|
+
expect(validate('evidence',evidence).errors).toEqual([]);
|
|
38
|
+
expect(evidence.run.runner).toEqual({name:'node-test',version:process.versions.node,source:'builtin'});
|
|
39
|
+
for (const r of evidence.records.filter((r)=>r.verdict==='killed'||r.verdict==='survived')) {
|
|
40
|
+
expect(r.detail.baselineRuns).toHaveLength(3);expect(r.detail.probeRuns).toHaveLength(3);
|
|
41
|
+
expect(r.detail.baselineRuns.every((b)=>b.outcome==='pass')).toBe(true);
|
|
42
|
+
if(r.verdict==='killed')expect(r.detail.probeRuns.every((p)=>p.outcome==='fail'&&p.assertionFailures>0)).toBe(true);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
it('restores every target and removes its scratch worktree after failures/timeouts', () => {
|
|
46
|
+
expect(execFileSync('git',['status','--porcelain'],{cwd:dir,encoding:'utf8'})).toBe('');
|
|
47
|
+
expect(execFileSync('git',['worktree','list','--porcelain'],{cwd:dir,encoding:'utf8'}).match(/^worktree /gm)).toHaveLength(1);
|
|
48
|
+
expect(readFileSync(join(dir,'src/policy.mjs'),'utf8')).toBe(readFileSync(join(FIXTURE,'src/policy.mjs'),'utf8'));
|
|
49
|
+
});
|
|
50
|
+
it('reuses matching Node evidence and remeasures across framework identity or worker policy', async () => {
|
|
51
|
+
const subset = {...claims,claims:claims.claims.filter((c)=>c.id==='NODE-ALLOW')};
|
|
52
|
+
const options = {projectDir:dir,claims:subset,runnerName:'node-test',confirmRuns:3,budgetMs:5000,toolVersion:'test',previous:evidence};
|
|
53
|
+
const reused = await probe(options);
|
|
54
|
+
expect(reused.records[0].reusedFrom).toBe(evidence.run.id);
|
|
55
|
+
const changed = await probe({...options,workers:2,serial:false});
|
|
56
|
+
expect(changed.records[0].reusedFrom).toBeUndefined();
|
|
57
|
+
expect(changed.records[0].verdict).toBe('killed');
|
|
58
|
+
const foreign = structuredClone(evidence);foreign.run.runner={name:'vitest',version:'5.0.2'};
|
|
59
|
+
foreign.records.forEach((r)=>{r.inputs.testUniverseHash='0'.repeat(64);});
|
|
60
|
+
const remeasured = await probe({...options,previous:foreign});
|
|
61
|
+
expect(remeasured.records[0].reusedFrom).toBeUndefined();
|
|
62
|
+
expect(remeasured.records[0].verdict).toBe('killed');
|
|
63
|
+
}, 15_000);
|
|
64
|
+
it('cancels the real CLI during a live fault, restoring targets without publishing partial evidence', async () => {
|
|
65
|
+
const scratch = mkdtempSync(join(tmpdir(), 'tg-node-cancel-'));
|
|
66
|
+
const sentinel = spawn(process.execPath, ['-e', 'setInterval(()=>{},1000)'], { stdio: 'ignore' });
|
|
67
|
+
let child;
|
|
68
|
+
try {
|
|
69
|
+
cpSync(FIXTURE, scratch, { recursive: true });
|
|
70
|
+
const source = readFileSync(join(scratch, 'src/policy.mjs'), 'utf8');
|
|
71
|
+
writeFileSync(join(scratch, 'test/policy.test.mjs'), `import { test } from 'node:test'; import { writeFileSync } from 'node:fs'; import { ready } from '../src/policy.mjs';
|
|
72
|
+
test('live fault',async()=>{if(!await ready()){writeFileSync('live.json',JSON.stringify({pid:process.pid}));while(true){}}});`);
|
|
73
|
+
const subset = { ...claims, claims: claims.claims.filter((c) => c.id === 'NODE-READY') };
|
|
74
|
+
subset.claims[0].faults[0].replace = 'export async function ready() {\n return false;';
|
|
75
|
+
writeFileSync(join(scratch, 'testguard.claims.json'), JSON.stringify(subset));
|
|
76
|
+
for (const args of [['init','-q'],['add','.'],['-c','user.name=fixture','-c','user.email=fixture@example.invalid','commit','-qm','fixture']]) execFileSync('git',args,{cwd:scratch,timeout:5000});
|
|
77
|
+
child = spawn(process.execPath, [join(ROOT, 'cli/testguard.mjs'), 'probe', scratch, '--runner', 'node-test', '--in-place', '--budget', '30000', '--quiet'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
78
|
+
let diagnostic = ''; for (const stream of [child.stdout, child.stderr]) stream.on('data', (bytes) => { diagnostic = (diagnostic + bytes).slice(-4000); });
|
|
79
|
+
const closed = new Promise((resolve) => child.once('close', (code, signal) => resolve({ code, signal })));
|
|
80
|
+
const deadline = Date.now() + 10_000;
|
|
81
|
+
while (!existsSync(join(scratch, 'live.json')) && child.exitCode === null && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 20));
|
|
82
|
+
expect(existsSync(join(scratch, 'live.json')), diagnostic).toBe(true);
|
|
83
|
+
const worker = JSON.parse(readFileSync(join(scratch, 'live.json'), 'utf8')).pid;
|
|
84
|
+
child.kill('SIGINT');
|
|
85
|
+
expect(await closed).toEqual({ code: 130, signal: null });
|
|
86
|
+
expect(readFileSync(join(scratch, 'src/policy.mjs'), 'utf8')).toBe(source);
|
|
87
|
+
expect(existsSync(join(scratch, '.testguard/evidence.json'))).toBe(false);
|
|
88
|
+
expect(() => process.kill(worker, 0)).toThrow();
|
|
89
|
+
expect(() => process.kill(sentinel.pid, 0)).not.toThrow();
|
|
90
|
+
} finally {
|
|
91
|
+
child?.kill('SIGKILL'); sentinel.kill('SIGKILL');
|
|
92
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
}, 15_000);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Runtime acceptance remains independent of the focused self-probe defenders.
|
|
98
|
+
describe('native Node collection and runtime acceptance', () => {
|
|
99
|
+
const owned = [];
|
|
100
|
+
const project = (files) => {
|
|
101
|
+
const dir = mkdtempSync(join(tmpdir(), 'tg-node-test-')); owned.push(dir);
|
|
102
|
+
writeFileSync(join(dir, 'package.json'), '{"type":"module"}');
|
|
103
|
+
for (const [file, source] of Object.entries(files)) {
|
|
104
|
+
mkdirSync(dirname(join(dir, file)), { recursive: true }); writeFileSync(join(dir, file), source);
|
|
105
|
+
}
|
|
106
|
+
return dir;
|
|
107
|
+
};
|
|
108
|
+
const body = (code) => `import { test } from 'node:test'; import assert from 'node:assert/strict';\n${code}\n`;
|
|
109
|
+
afterEach(() => { for (const dir of owned.splice(0)) rmSync(dir, { recursive: true, force: true }); });
|
|
110
|
+
const execute = (dir, files = ['test/a.test.mjs'], extra = {}) => run({ projectDir: dir, files, budgetMs: 4000, ...extra });
|
|
111
|
+
const discover = (dir, extra = {}) => discoverTests({ projectDir: dir, version: process.versions.node, ...extra });
|
|
112
|
+
const alive = (pid) => {
|
|
113
|
+
try { process.kill(pid,0);return process.platform==='win32'||!execFileSync('ps',['-p',String(pid),'-o','stat='],{encoding:'utf8',timeout:1000}).trim().startsWith('Z'); } catch {return false;}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
it('refuses an explicitly selected empty universe', async () => {
|
|
117
|
+
const selected = await selectRunner({ projectDir: project({}), name: 'node-test' });
|
|
118
|
+
expect(selected.error).toMatch(/no test files/);
|
|
119
|
+
});
|
|
120
|
+
it('does not turn a top-level assertion or syntax/import error into a test-body kill', async () => {
|
|
121
|
+
for (const source of ["import assert from 'node:assert/strict'; assert.equal(1,2);", '(', "import './missing.mjs';"]) {
|
|
122
|
+
const dir = project({ 'test/a.test.mjs': source });
|
|
123
|
+
const result = await execute(dir);
|
|
124
|
+
expect(result.run.outcome).toBe('error'); expect(result.run.assertionFailures).toBe(0);
|
|
125
|
+
await expect(discover(dir)).rejects.toThrow();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
it('detects a genuine test-body exception and preserves its test identity', async () => {
|
|
129
|
+
const dir = project({ 'test/a.test.mjs': body("test('exception', () => { throw new Error('bad behavior'); });") });
|
|
130
|
+
const result = await execute(dir);
|
|
131
|
+
expect(result.run.outcome).toBe('fail'); expect(result.run.assertionFailures).toBe(1);
|
|
132
|
+
expect(result.failedTests).toEqual(['test/a.test.mjs::exception']);
|
|
133
|
+
});
|
|
134
|
+
it('does not double count a failing suite or accept skipped-only defenders', async () => {
|
|
135
|
+
const dir = project({ 'test/a.test.mjs': "import {describe,it} from 'node:test'; import assert from 'node:assert/strict'; describe('suite',()=>{it('failure',()=>assert.equal(1,2));it.skip('skip',()=>{});});" });
|
|
136
|
+
const result = await execute(dir);
|
|
137
|
+
expect(result.run.tests).toEqual({ total: 1, passed: 0, failed: 1 }); expect(result.run.assertionFailures).toBe(1);
|
|
138
|
+
writeFileSync(join(dir,'test/a.test.mjs'),body("test.skip('skip',()=>{});test.todo('todo');"));
|
|
139
|
+
expect((await execute(dir)).run.outcome).toBe('error');
|
|
140
|
+
});
|
|
141
|
+
it('recognizes native test timeouts and hard command timeouts', async () => {
|
|
142
|
+
const dir = project({ 'test/a.test.mjs': body("test('hangs',{timeout:40},async(t)=>{const h=setInterval(()=>{},1000);t.signal.addEventListener('abort',()=>clearInterval(h),{once:true});try{await new Promise(()=>{});}finally{clearInterval(h);}});") });
|
|
143
|
+
const result = await execute(dir);
|
|
144
|
+
expect(result.timeouts).toBeGreaterThan(0); expect(result.run.assertionFailures).toBe(0);
|
|
145
|
+
expect(result.run.outcome).toBe('fail');
|
|
146
|
+
writeFileSync(join(dir,'test/a.test.mjs'),body("test('loop',()=>{while(true){}});"));
|
|
147
|
+
const hard = await execute(dir,undefined,{budgetMs:400});
|
|
148
|
+
expect(hard.run.outcome).toBe('timeout'); expect(hard.run.assertionFailures).toBe(0);
|
|
149
|
+
});
|
|
150
|
+
it('refuses a hook failure, incomplete process exit and a file with no tests', async () => {
|
|
151
|
+
for (const source of ["import {before,test} from 'node:test';before(()=>{throw new Error('setup');});test('case',()=>{});", body("test('first',()=>{});test('unfinished',()=>process.exit(0));"), 'export const value=1;']) {
|
|
152
|
+
const result = await execute(project({ 'test/a.test.mjs': source }));
|
|
153
|
+
expect(result.run.outcome).toBe('error'); expect(result.run.assertionFailures).toBe(0);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('binds configuration edits to the native universe and rejects symlink escapes', async () => {
|
|
158
|
+
const dir = project({ 'test/a.test.mjs': body("test('case',()=>{});") });
|
|
159
|
+
const before = await discover(dir); writeFileSync(join(dir,'package.json'),'{"type":"module","description":"changed"}');
|
|
160
|
+
expect((await discover(dir)).testUniverseHash).not.toBe(before.testUniverseHash);
|
|
161
|
+
const external=project({'external.mjs':body("test('outside',()=>{});")});
|
|
162
|
+
symlinkSync(join(external,'external.mjs'),join(dir,'test','escape.mjs'));
|
|
163
|
+
await expect(discover(dir)).rejects.toThrow(/outside/);
|
|
164
|
+
});
|
|
165
|
+
it('executes selected entries through a symlinked project root with canonical attribution', async () => {
|
|
166
|
+
const dir = project({ 'test/a.test.mjs': body("test('through alias',()=>assert.equal(1,1));") });
|
|
167
|
+
const aliases = project({}); const alias = join(aliases, 'linked'); symlinkSync(dir, alias, 'dir');
|
|
168
|
+
expect((await discover(alias)).files).toEqual(['test/a.test.mjs']);
|
|
169
|
+
expect((await execute(alias)).run).toMatchObject({ outcome: 'pass', tests: { total: 1, passed: 1, failed: 0 } });
|
|
170
|
+
});
|
|
171
|
+
it('bounds native collection output and command time', async () => {
|
|
172
|
+
const dir = project({ 'test/a.test.mjs': body("test('case',()=>{});") });
|
|
173
|
+
await expect(discover(dir,{maxOutputBytes:1})).rejects.toThrow(/exceeded/);
|
|
174
|
+
writeFileSync(join(dir,'test/a.test.mjs'),'while(true){}');
|
|
175
|
+
await expect(discover(dir,{timeoutMs:300})).rejects.toThrow(/timeout/);
|
|
176
|
+
});
|
|
177
|
+
it('retains process isolation and serial worker policy', async () => {
|
|
178
|
+
const dir = project({ 'test/a.test.mjs': body("test('isolated',()=>{globalThis.nativeSentinel=1;assert.equal(1,1);});"),
|
|
179
|
+
'test/b.test.mjs':body("test('separate',()=>assert.equal(globalThis.nativeSentinel,undefined));") });
|
|
180
|
+
expect((await execute(dir,['test/a.test.mjs','test/b.test.mjs'],{workers:2})).run.tests.passed).toBe(2);
|
|
181
|
+
expect((await execute(dir,['test/a.test.mjs','test/b.test.mjs'],{workers:4,serial:true})).run.tests.passed).toBe(2);
|
|
182
|
+
expect(existsSync(join(dir,'node_modules'))).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('cleans its worker descendants on timeout and abnormal exit while preserving an unrelated sentinel', async () => {
|
|
186
|
+
const sentinel=spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{stdio:'ignore'});
|
|
187
|
+
const source = body("import {spawn} from 'node:child_process';import {writeFileSync} from 'node:fs';test('child',()=>{const c=spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{stdio:'ignore'});writeFileSync('owned.json',JSON.stringify([process.pid,c.pid]));while(true){}});");
|
|
188
|
+
const dir=project({'test/a.test.mjs':source});
|
|
189
|
+
try {
|
|
190
|
+
expect((await execute(dir,undefined,{budgetMs:800})).run.outcome).toBe('timeout');
|
|
191
|
+
const pids=JSON.parse(readFileSync(join(dir,'owned.json'),'utf8'));
|
|
192
|
+
for(const pid of pids)expect(alive(pid)).toBe(false);
|
|
193
|
+
expect(alive(sentinel.pid)).toBe(true);
|
|
194
|
+
writeFileSync(join(dir,'test/a.test.mjs'),source.replace('while(true){}','process.exit(0);'));
|
|
195
|
+
expect((await execute(dir)).run.outcome).toBe('error');
|
|
196
|
+
for(const pid of JSON.parse(readFileSync(join(dir,'owned.json'),'utf8')))expect(alive(pid)).toBe(false);
|
|
197
|
+
expect(alive(sentinel.pid)).toBe(true);
|
|
198
|
+
} finally {sentinel.kill('SIGKILL');}
|
|
199
|
+
}, 10_000);
|
|
200
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// @req FR-10
|
|
2
|
+
// Focused admission defenders; runner-node.test.mjs retains runtime acceptance.
|
|
3
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
4
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { check, run } from '../src/probe/runners/node-test.mjs';
|
|
8
|
+
import { selectRunner } from '../src/probe/runners/index.mjs';
|
|
9
|
+
|
|
10
|
+
const dirs = [];
|
|
11
|
+
const project = () => {
|
|
12
|
+
const dir = mkdtempSync(join(tmpdir(), 'tg-node-admission-'));
|
|
13
|
+
dirs.push(dir);
|
|
14
|
+
mkdirSync(join(dir, 'test'));
|
|
15
|
+
writeFileSync(join(dir, 'package.json'), '{"type":"module"}');
|
|
16
|
+
writeFileSync(join(dir, 'test/a.test.mjs'), "import { test } from 'node:test'; import assert from 'node:assert/strict'; test('real', () => assert.equal(2+2,4));\n");
|
|
17
|
+
return dir;
|
|
18
|
+
};
|
|
19
|
+
afterEach(() => { for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }); });
|
|
20
|
+
|
|
21
|
+
describe('native Node admission', () => {
|
|
22
|
+
it('runs without node_modules and records the actual built-in runtime', async () => {
|
|
23
|
+
expect(await check()).toEqual({ ok: true, version: process.versions.node, source: 'builtin' });
|
|
24
|
+
const dir = project();
|
|
25
|
+
const selected = await selectRunner({ projectDir: dir, name: 'node-test' });
|
|
26
|
+
expect(selected.runner.name).toBe('node-test'); expect(selected.testFiles).toBe(1);
|
|
27
|
+
expect((await run({ projectDir: dir, files: ['test/a.test.mjs'], budgetMs: 4000 })).run.outcome).toBe('pass');
|
|
28
|
+
});
|
|
29
|
+
it('refuses non-default loading and never silently routes a Node project through auto', async () => {
|
|
30
|
+
const original = process.env.NODE_OPTIONS; process.env.NODE_OPTIONS = '--import=unsupported';
|
|
31
|
+
try { expect((await check()).ok).toBe(false); } finally { if(original===undefined)delete process.env.NODE_OPTIONS;else process.env.NODE_OPTIONS=original; }
|
|
32
|
+
const selected = await selectRunner({ projectDir: project(), name: 'auto', budgetMs: 2000 });
|
|
33
|
+
expect(selected.runner?.name).not.toBe('node-test');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// @req FR-10
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { appendFileSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { readBoundedJsonFile } from '../src/probe/runners/discovery.mjs';
|
|
7
|
+
|
|
8
|
+
const race = vi.hoisted(() => ({ afterStat: undefined }));
|
|
9
|
+
vi.mock('node:fs', async (importOriginal) => {
|
|
10
|
+
const fs = await importOriginal();
|
|
11
|
+
return { ...fs, fstatSync: (...args) => {
|
|
12
|
+
const stat = fs.fstatSync(...args);
|
|
13
|
+
const change = race.afterStat;
|
|
14
|
+
race.afterStat = undefined;
|
|
15
|
+
change?.();
|
|
16
|
+
return stat;
|
|
17
|
+
} };
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const dirs = [];
|
|
21
|
+
const reportFile = (text) => {
|
|
22
|
+
const dir = mkdtempSync(join(tmpdir(), 'tg-report-allocation-'));
|
|
23
|
+
dirs.push(dir);
|
|
24
|
+
const path = join(dir, 'report.json');
|
|
25
|
+
writeFileSync(path, text);
|
|
26
|
+
return path;
|
|
27
|
+
};
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
race.afterStat = undefined;
|
|
30
|
+
vi.restoreAllMocks();
|
|
31
|
+
for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('bounded report allocation', () => {
|
|
35
|
+
it('can read a tiny report under a large byte ceiling without reserving the ceiling', () => {
|
|
36
|
+
const path = reportFile('{"cases":[]}');
|
|
37
|
+
const alloc = Buffer.alloc;
|
|
38
|
+
const sizes = [];
|
|
39
|
+
vi.spyOn(Buffer, 'alloc').mockImplementation((size, ...args) => {
|
|
40
|
+
sizes.push(size);
|
|
41
|
+
if (size > 1024) throw new Error('tiny report exceeded its memory allowance');
|
|
42
|
+
return alloc(size, ...args);
|
|
43
|
+
});
|
|
44
|
+
expect(readBoundedJsonFile(path, 1024 * 1024 * 1024)).toEqual({ cases: [] });
|
|
45
|
+
expect(sizes.length).toBeGreaterThan(0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('reads growth after the size snapshot instead of accepting a valid truncated prefix', () => {
|
|
49
|
+
const path = reportFile('{}');
|
|
50
|
+
race.afterStat = () => appendFileSync(path, ' x');
|
|
51
|
+
expect(() => readBoundedJsonFile(path, 32)).toThrow(/not valid JSON/);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('can grow repeatedly to complete a report within the byte ceiling', () => {
|
|
55
|
+
const text = JSON.stringify({ text: 'x'.repeat(20_000) });
|
|
56
|
+
const path = reportFile(text.slice(0, 2));
|
|
57
|
+
race.afterStat = () => appendFileSync(path, text.slice(2));
|
|
58
|
+
expect(readBoundedJsonFile(path, Buffer.byteLength(text))).toEqual({ text: 'x'.repeat(20_000) });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('rejects growth beyond the ceiling even when its initial prefix was valid JSON', () => {
|
|
62
|
+
const path = reportFile('{}');
|
|
63
|
+
race.afterStat = () => appendFileSync(path, ' '.repeat(100));
|
|
64
|
+
expect(() => readBoundedJsonFile(path, 16)).toThrow(/exceeded 16 bytes/);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('keeps exact-byte, empty, UTF-8 and regular-file boundaries', () => {
|
|
68
|
+
const text = '{"text":"é"}';
|
|
69
|
+
const path = reportFile(text);
|
|
70
|
+
expect(readBoundedJsonFile(path, Buffer.byteLength(text))).toEqual({ text: 'é' });
|
|
71
|
+
expect(() => readBoundedJsonFile(path, Buffer.byteLength(text) - 1)).toThrow(/exceeded/);
|
|
72
|
+
writeFileSync(path, '');
|
|
73
|
+
expect(() => readBoundedJsonFile(path)).toThrow(/not valid JSON/);
|
|
74
|
+
writeFileSync(path, Buffer.from([0xff]));
|
|
75
|
+
expect(() => readBoundedJsonFile(path)).toThrow(/UTF-8/);
|
|
76
|
+
const link = join(dirs.at(-1), 'link.json');
|
|
77
|
+
symlinkSync(path, link);
|
|
78
|
+
expect(() => readBoundedJsonFile(link)).toThrow(/not readable/);
|
|
79
|
+
expect(() => readBoundedJsonFile(dirs.at(-1))).toThrow(/not a regular file/);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
"$schema": "./spec/schemas/claims.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
4
|
"claims": [
|
|
5
|
+
{
|
|
6
|
+
"id": "TG-REPORT-READ-ALLOCATION-TRACKS-FILE",
|
|
7
|
+
"statement": "Bounded runner-report reads reserve memory for observed file bytes rather than the maximum allowance, and still read a file that grows within the allowance without accepting a truncated prefix.",
|
|
8
|
+
"source": {
|
|
9
|
+
"kind": "manual",
|
|
10
|
+
"ref": "test/runner-report-allocation.test.mjs"
|
|
11
|
+
},
|
|
12
|
+
"severity": "high",
|
|
13
|
+
"producedBy": { "producer": "agent", "by": "codex" },
|
|
14
|
+
"defendedBy": ["test/runner-report-allocation.test.mjs"],
|
|
15
|
+
"faults": [
|
|
16
|
+
{
|
|
17
|
+
"id": "F1",
|
|
18
|
+
"description": "Reserve the maximum report allowance for every tiny file.",
|
|
19
|
+
"faultClass": "other",
|
|
20
|
+
"file": "src/probe/runners/discovery.mjs",
|
|
21
|
+
"find": " let bytes = Buffer.alloc(stat.size + 1);",
|
|
22
|
+
"replace": " let bytes = Buffer.alloc(maxBytes + 1);",
|
|
23
|
+
"producedBy": { "producer": "agent", "by": "codex" }
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "F2",
|
|
27
|
+
"description": "Stop reading when the initial size-based buffer fills.",
|
|
28
|
+
"faultClass": "guard-removed",
|
|
29
|
+
"file": "src/probe/runners/discovery.mjs",
|
|
30
|
+
"find": " if (length === bytes.length) {",
|
|
31
|
+
"replace": " if (false) {",
|
|
32
|
+
"producedBy": { "producer": "agent", "by": "codex" }
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
5
36
|
{
|
|
6
37
|
"id": "TG-NODE-ENTRY-SELECTION-AND-LOADING",
|
|
7
38
|
"statement": "Explicit native runs select files within the admitted canonical project root and refuse inherited alternative module loading before launching.",
|
|
@@ -15,7 +46,7 @@
|
|
|
15
46
|
"by": "codex"
|
|
16
47
|
},
|
|
17
48
|
"defendedBy": [
|
|
18
|
-
"test/runner-node.test.mjs"
|
|
49
|
+
"test/runner-node-admission.test.mjs"
|
|
19
50
|
],
|
|
20
51
|
"faults": [
|
|
21
52
|
{
|