testguard-cli 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- testguard_cli-0.3.0/.codex/hooks.json +71 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/AGENTS.md +1 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/CHANGELOG.md +39 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/PKG-INFO +31 -5
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/README.md +30 -4
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/action.yml +1 -1
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/package-lock.json +2 -2
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/package.json +1 -1
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/packaging/homebrew/testguard.rb +1 -1
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/pyproject.toml +1 -1
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/GATE-SEMANTICS.md +11 -1
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/README.md +2 -1
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/examples/brief.json +6 -1
- testguard_cli-0.3.0/spec/conformance/examples/evidence-provisional.json +276 -0
- testguard_cli-0.3.0/spec/conformance/examples/evidence.json +540 -0
- testguard_cli-0.3.0/spec/conformance/examples/status.json +60 -0
- testguard_cli-0.2.0/spec/conformance/examples/evidence.json → testguard_cli-0.3.0/spec/conformance/invalid/evidence.provisional-flag-on-confirmed-run.json +2 -1
- testguard_cli-0.3.0/spec/conformance/invalid/evidence.single-run-without-provisional.json +271 -0
- testguard_cli-0.3.0/spec/conformance/invalid/status.clean-with-new-findings.json +60 -0
- testguard_cli-0.3.0/spec/conformance/invalid/status.write-test-without-target.json +54 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/schemas.test.mjs +6 -4
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/lib/validate.mjs +11 -1
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/schemas/brief.schema.json +11 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/schemas/evidence.schema.json +9 -1
- testguard_cli-0.3.0/spec/schemas/status.schema.json +110 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/brief/brief.mjs +5 -3
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/cli.mjs +12 -2
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/commands/baseline.mjs +11 -2
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/commands/brief.mjs +10 -3
- testguard_cli-0.3.0/src/commands/init.mjs +15 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/commands/probe.mjs +18 -7
- testguard_cli-0.3.0/src/commands/status.mjs +10 -0
- testguard_cli-0.3.0/src/init/init.mjs +78 -0
- testguard_cli-0.3.0/src/init/templates/SKILL.md +63 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/probe/probe.mjs +2 -1
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/render.mjs +7 -5
- testguard_cli-0.3.0/src/status/status.mjs +163 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/brief.test.mjs +15 -0
- testguard_cli-0.3.0/test/init.test.mjs +49 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/probe.fixture.test.mjs +46 -0
- testguard_cli-0.3.0/test/status.test.mjs +118 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/testguard.claims.json +32 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.gitattributes +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/dependabot.yml +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/scripts/install-smoke.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/scripts/sync-release-version.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/workflows/auto-merge.yml +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/workflows/ci.yml +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/workflows/release.yml +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/workflows/scheduled-release.yml +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.github/workflows/supply-chain.yml +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.gitignore +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.npmignore +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.npmrc +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/.pre-commit-hooks.yaml +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/CLAUDE.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/CODE_OF_CONDUCT.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/CONTRIBUTING.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/LICENSE +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/PRIVACY.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/SECURITY.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/SUPPORT.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/bench/README.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/cli/testguard.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/.gitignore +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/README.md +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/expected.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/package.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/src/export.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/src/redact.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/test/flaky.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/test/redact.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/testguard.claims.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/fixtures/known-answer/vitest.config.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/examples/baseline.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/examples/calibration.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/examples/claims.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/examples/ignore.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/baseline.bad-fingerprint-key.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/baseline.zero-count.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/brief.text-without-heading.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/brief.unknown-verdict-key.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/calibration.p-outside-ci.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/calibration.positives-exceed-n.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/claims.absolute-path.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/claims.duplicate-fault-id.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/claims.missing-provenance.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/claims.noop-fault.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/claims.occurrence-exceeds-hits.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/claims.parent-traversal.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.flaky-defender-without-flakiness.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.killed-by-non-assertion.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.killed-on-red-baseline.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.killed-without-n-runs.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.rank-extra-property.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.undeclared-killers-without-reason.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.unknown-verdict.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.unverifiable-without-reason.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/evidence.wrong-fingerprint.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/ignore.missing-reason.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/conformance/invalid/ignore.short-reason.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/lib/fingerprint.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/schemas/baseline.schema.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/schemas/calibration.schema.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/schemas/claims.schema.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/schemas/common.schema.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/spec/schemas/ignore.schema.json +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/baseline/baseline.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/claims/annotations.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/claims/load.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/commands/claims.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/commands/scaffold.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/evidence/writer.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/git.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/probe/classify.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/probe/discover.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/probe/inject.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/probe/rank.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/probe/runner-vitest.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/probe/worktree.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/scaffold/producers.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/scaffold/scaffold.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/util/glob.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/src/util/hash.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/annotations.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/baseline.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/classify.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/discover.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/glob.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/inject.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/probe-preconditions.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/rank-aliases.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/runner-command.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/runner-vitest.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/scaffold.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/snapshot.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/worktree.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/test/writer.test.mjs +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/testguard_cli/__init__.py +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/testguard_cli/wrapper.py +0 -0
- {testguard_cli-0.2.0 → testguard_cli-0.3.0}/vitest.config.mjs +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Read",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/pre-read.js\"",
|
|
10
|
+
"timeout": 5
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/pre-write.js\"",
|
|
20
|
+
"timeout": 5
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"PostToolUse": [
|
|
26
|
+
{
|
|
27
|
+
"matcher": "Read",
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/post-read.js\"",
|
|
32
|
+
"timeout": 5
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
38
|
+
"hooks": [
|
|
39
|
+
{
|
|
40
|
+
"type": "command",
|
|
41
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/post-write.js\"",
|
|
42
|
+
"timeout": 10
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"SessionStart": [
|
|
48
|
+
{
|
|
49
|
+
"matcher": "",
|
|
50
|
+
"hooks": [
|
|
51
|
+
{
|
|
52
|
+
"type": "command",
|
|
53
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/session-start.js\"",
|
|
54
|
+
"timeout": 5
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"Stop": [
|
|
60
|
+
{
|
|
61
|
+
"hooks": [
|
|
62
|
+
{
|
|
63
|
+
"type": "command",
|
|
64
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/stop.js\"",
|
|
65
|
+
"timeout": 10
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -31,6 +31,7 @@ node cli/testguard.mjs scaffold src/probe/classify.mjs --json # what the produ
|
|
|
31
31
|
- `fixtures/known-answer/expected.json` — the oracle. Never edit it to match output.
|
|
32
32
|
- `testguard.claims.json` — claims about this codebase, probed in CI.
|
|
33
33
|
- `src/scaffold/producers.mjs` — the five fault shapes. Deterministic line heuristics; no AST, no LLM. A new shape needs a synthetic-file test and a README row.
|
|
34
|
+
- `src/status/status.mjs` — the state machine every rendering derives from. A new state or action is a spec change (`status.schema.json`) and a skill-template change (`src/init/templates/SKILL.md`) in the same PR.
|
|
34
35
|
|
|
35
36
|
## Rules
|
|
36
37
|
|
|
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-09-17
|
|
11
|
+
|
|
12
|
+
The agent operating layer. TestGuard is meant to be run by AI agents; this
|
|
13
|
+
release gives them one source of truth, an installable loop, and makes the
|
|
14
|
+
one cheap way to game it visible.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`testguard status --json`** — the machine entry point. A new spec kind
|
|
19
|
+
(`status.schema.json`): `state` (no-claims · unprobed · evidence-stale ·
|
|
20
|
+
provisional-only · unproven · clean) and the one `next` action, computed
|
|
21
|
+
from claims, evidence, baseline and the working tree. Exit 0 clean, 1
|
|
22
|
+
unproven/stale, 2 nothing to probe yet.
|
|
23
|
+
- **`testguard init`** — installs `.claude/skills/testguard/SKILL.md`, the
|
|
24
|
+
`brief --text` SessionStart hook (merged into an existing
|
|
25
|
+
`.claude/settings.json`), an `AGENTS.md` section and the `.gitignore`
|
|
26
|
+
lines. Idempotent; `--force` replaces the skill.
|
|
27
|
+
- **Fault edits are visible.** Evidence records `subject.contentHash`;
|
|
28
|
+
`status` lists `changedFaults` (a fault edited after it was probed, with
|
|
29
|
+
its previous verdict) and makes `review-fault-change` the next action when
|
|
30
|
+
the edited fault had survived. Gate rule 8. Self-claim
|
|
31
|
+
`TG-FAULT-EDIT-VISIBLE`.
|
|
32
|
+
- `--json` on every command; `probe` and `baseline` emit the status document
|
|
33
|
+
plus their result. `brief` carries `next` and prints it.
|
|
34
|
+
|
|
35
|
+
## [0.2.1] - 2026-09-17
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **Provisional verdicts** (#5). A run with `--confirm` below 3 declares
|
|
40
|
+
`run.provisional: true` — a spec field whose equivalence with
|
|
41
|
+
`confirmRuns < 3` the validator enforces both ways. Provisional verdicts
|
|
42
|
+
print with a `?`, the summary is prefixed `PROVISIONAL`, a warning is
|
|
43
|
+
printed on stderr, evidence is written to `.testguard/evidence-provisional.json`
|
|
44
|
+
so the canonical file only ever holds confirmed runs, `brief` warns at the
|
|
45
|
+
top, and `baseline` refuses provisional evidence unless
|
|
46
|
+
`--allow-provisional`. A confirmed prior verdict is never reused by a
|
|
47
|
+
provisional run, nor the reverse.
|
|
48
|
+
|
|
10
49
|
## [0.2.0] - 2026-09-17
|
|
11
50
|
|
|
12
51
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: testguard-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Proves a test suite actually defends the claims a project makes: injects the faults those claims forbid and reports every one the tests miss. Python wrapper for 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
|
|
@@ -76,7 +76,7 @@ tests were written against the survivors, 39/39 were killed.
|
|
|
76
76
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
77
77
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
78
78
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
79
|
-
| GitHub Action | `uses: raccioly/testguard@v0.
|
|
79
|
+
| GitHub Action | `uses: raccioly/testguard@v0.3.0` — see [`action.yml`](./action.yml) |
|
|
80
80
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
81
81
|
|
|
82
82
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
@@ -86,6 +86,8 @@ less than that many days ago (`ENOVERSIONS`); install that one with
|
|
|
86
86
|
## How it works
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
+
npx testguard-cli init # install the agent layer: skill, session-start hook, AGENTS.md section
|
|
90
|
+
npx testguard-cli status --json # where the project is and the ONE next action — the machine entry point
|
|
89
91
|
npx testguard-cli claims # what does this project claim, and is every claim probeable?
|
|
90
92
|
npx testguard-cli probe # try to falsify each claim; report what the tests missed
|
|
91
93
|
npx testguard-cli baseline # freeze today's unproven findings; from now on only new ones gate
|
|
@@ -136,7 +138,10 @@ npx testguard-cli scaffold src/x.ts # propose faults for a file, as a draft to
|
|
|
136
138
|
Practical loop: first pass `--no-escalate` (escalation re-runs the whole
|
|
137
139
|
suite N times per survivor); iterate on one claim with `--claim <ID>` and
|
|
138
140
|
either `--include-dirty` or `--in-place` (only fault target files must be
|
|
139
|
-
clean there; test files may be dirty)
|
|
141
|
+
clean there; test files may be dirty), optionally `--confirm 1` for a fast
|
|
142
|
+
**provisional** signal — verdicts print with a `?`, evidence goes to
|
|
143
|
+
`evidence-provisional.json`, and `baseline` refuses it; final pass with
|
|
144
|
+
defaults. By default
|
|
140
145
|
the stream shows only unproven faults plus a killed count — `--verbose`
|
|
141
146
|
shows every fault. A custom
|
|
142
147
|
runner (`pnpm --filter`, a specific config) goes in
|
|
@@ -160,6 +165,27 @@ npx testguard-cli scaffold src/x.ts # propose faults for a file, as a draft to
|
|
|
160
165
|
`--text` prints only, and exits 0 silently when there is no evidence yet,
|
|
161
166
|
so the hook can never break a session.
|
|
162
167
|
|
|
168
|
+
### Built for agents to run
|
|
169
|
+
|
|
170
|
+
TestGuard is meant to be driven by an AI agent, not typed by a person. Three
|
|
171
|
+
things make that safe:
|
|
172
|
+
|
|
173
|
+
- **One source of truth.** `testguard status --json` computes `state` and the
|
|
174
|
+
one `next` action from the claims file, the evidence, the baseline and the
|
|
175
|
+
working tree. Every human rendering — the CLI text, the session-start
|
|
176
|
+
brief, the skill — derives from it, so they cannot disagree. Every command
|
|
177
|
+
accepts `--json`.
|
|
178
|
+
- **An installable operating loop.** `testguard init` writes
|
|
179
|
+
`.claude/skills/testguard/SKILL.md` (state → action, verdict → the only
|
|
180
|
+
acceptable fix, the two-gate rule for any test the agent writes), the
|
|
181
|
+
`brief --text` session-start hook, an `AGENTS.md` section and the
|
|
182
|
+
`.gitignore` lines. Idempotent.
|
|
183
|
+
- **Gaming is visible.** The cheapest way to make a survivor disappear is to
|
|
184
|
+
weaken its fault, not to write a test. Evidence records every fault's
|
|
185
|
+
content hash; `status` lists any fault edited after it survived, with its
|
|
186
|
+
previous verdict, and makes reviewing that edit the next action. Editing a
|
|
187
|
+
claim is allowed — claims can be wrong — but it is never invisible.
|
|
188
|
+
|
|
163
189
|
### Authoring faults mechanically
|
|
164
190
|
|
|
165
191
|
Writing faults by hand means reading the code to find exact anchors. Two
|
|
@@ -213,8 +239,8 @@ through every verdict.
|
|
|
213
239
|
|
|
214
240
|
## Status
|
|
215
241
|
|
|
216
|
-
**v0.
|
|
217
|
-
mechanical scaffold
|
|
242
|
+
**v0.3.** Seven commands, vitest runner, hand-authored faults plus a
|
|
243
|
+
mechanical scaffold, and an agent operating layer (`status`, `init`). The contract
|
|
218
244
|
spine — six JSON Schemas shared with the other Guard tools — is under
|
|
219
245
|
[`spec/`](spec/). One exact-pinned runtime dependency (`ajv`, for schema validation); Node ≥ 20.
|
|
220
246
|
|
|
@@ -52,7 +52,7 @@ tests were written against the survivors, 39/39 were killed.
|
|
|
52
52
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
53
53
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
54
54
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
55
|
-
| GitHub Action | `uses: raccioly/testguard@v0.
|
|
55
|
+
| GitHub Action | `uses: raccioly/testguard@v0.3.0` — see [`action.yml`](./action.yml) |
|
|
56
56
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-probe` |
|
|
57
57
|
|
|
58
58
|
Projects that set `min-release-age` in `.npmrc` cannot see a version published
|
|
@@ -62,6 +62,8 @@ less than that many days ago (`ENOVERSIONS`); install that one with
|
|
|
62
62
|
## How it works
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
+
npx testguard-cli init # install the agent layer: skill, session-start hook, AGENTS.md section
|
|
66
|
+
npx testguard-cli status --json # where the project is and the ONE next action — the machine entry point
|
|
65
67
|
npx testguard-cli claims # what does this project claim, and is every claim probeable?
|
|
66
68
|
npx testguard-cli probe # try to falsify each claim; report what the tests missed
|
|
67
69
|
npx testguard-cli baseline # freeze today's unproven findings; from now on only new ones gate
|
|
@@ -112,7 +114,10 @@ npx testguard-cli scaffold src/x.ts # propose faults for a file, as a draft to
|
|
|
112
114
|
Practical loop: first pass `--no-escalate` (escalation re-runs the whole
|
|
113
115
|
suite N times per survivor); iterate on one claim with `--claim <ID>` and
|
|
114
116
|
either `--include-dirty` or `--in-place` (only fault target files must be
|
|
115
|
-
clean there; test files may be dirty)
|
|
117
|
+
clean there; test files may be dirty), optionally `--confirm 1` for a fast
|
|
118
|
+
**provisional** signal — verdicts print with a `?`, evidence goes to
|
|
119
|
+
`evidence-provisional.json`, and `baseline` refuses it; final pass with
|
|
120
|
+
defaults. By default
|
|
116
121
|
the stream shows only unproven faults plus a killed count — `--verbose`
|
|
117
122
|
shows every fault. A custom
|
|
118
123
|
runner (`pnpm --filter`, a specific config) goes in
|
|
@@ -136,6 +141,27 @@ npx testguard-cli scaffold src/x.ts # propose faults for a file, as a draft to
|
|
|
136
141
|
`--text` prints only, and exits 0 silently when there is no evidence yet,
|
|
137
142
|
so the hook can never break a session.
|
|
138
143
|
|
|
144
|
+
### Built for agents to run
|
|
145
|
+
|
|
146
|
+
TestGuard is meant to be driven by an AI agent, not typed by a person. Three
|
|
147
|
+
things make that safe:
|
|
148
|
+
|
|
149
|
+
- **One source of truth.** `testguard status --json` computes `state` and the
|
|
150
|
+
one `next` action from the claims file, the evidence, the baseline and the
|
|
151
|
+
working tree. Every human rendering — the CLI text, the session-start
|
|
152
|
+
brief, the skill — derives from it, so they cannot disagree. Every command
|
|
153
|
+
accepts `--json`.
|
|
154
|
+
- **An installable operating loop.** `testguard init` writes
|
|
155
|
+
`.claude/skills/testguard/SKILL.md` (state → action, verdict → the only
|
|
156
|
+
acceptable fix, the two-gate rule for any test the agent writes), the
|
|
157
|
+
`brief --text` session-start hook, an `AGENTS.md` section and the
|
|
158
|
+
`.gitignore` lines. Idempotent.
|
|
159
|
+
- **Gaming is visible.** The cheapest way to make a survivor disappear is to
|
|
160
|
+
weaken its fault, not to write a test. Evidence records every fault's
|
|
161
|
+
content hash; `status` lists any fault edited after it survived, with its
|
|
162
|
+
previous verdict, and makes reviewing that edit the next action. Editing a
|
|
163
|
+
claim is allowed — claims can be wrong — but it is never invisible.
|
|
164
|
+
|
|
139
165
|
### Authoring faults mechanically
|
|
140
166
|
|
|
141
167
|
Writing faults by hand means reading the code to find exact anchors. Two
|
|
@@ -189,8 +215,8 @@ through every verdict.
|
|
|
189
215
|
|
|
190
216
|
## Status
|
|
191
217
|
|
|
192
|
-
**v0.
|
|
193
|
-
mechanical scaffold
|
|
218
|
+
**v0.3.** Seven commands, vitest runner, hand-authored faults plus a
|
|
219
|
+
mechanical scaffold, and an agent operating layer (`status`, `init`). The contract
|
|
194
220
|
spine — six JSON Schemas shared with the other Guard tools — is under
|
|
195
221
|
[`spec/`](spec/). One exact-pinned runtime dependency (`ajv`, for schema validation); Node ≥ 20.
|
|
196
222
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testguard-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "testguard-cli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.3.0",
|
|
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.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Proves a test suite defends the claims a project makes: injects the faults those claims forbid and reports every one the tests fail to detect.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
class Testguard < Formula
|
|
14
14
|
desc "Proves a test suite defends the claims a project makes"
|
|
15
15
|
homepage "https://github.com/raccioly/testguard"
|
|
16
|
-
url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.
|
|
16
|
+
url "https://registry.npmjs.org/testguard-cli/-/testguard-cli-0.3.0.tgz"
|
|
17
17
|
sha256 "385d69f9d3c153b934d9c1cb6a2c754eb9b221b0a8c0ba8b805858384d2d4678"
|
|
18
18
|
license "MIT"
|
|
19
19
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "testguard-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Proves a test suite actually defends the claims a project makes: injects the faults those claims forbid and reports every one the tests miss. Python wrapper for the Node.js CLI (requires Node.js 20+)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -22,7 +22,11 @@ Rules that follow from the table:
|
|
|
22
22
|
1. **Green baseline first.** Defenders run N times unmodified before any fault
|
|
23
23
|
is applied. Anything short of N/N pass is `flaky-defender` and stops there.
|
|
24
24
|
2. **Confirm over N runs.** `killed` and `survived` both require exactly N
|
|
25
|
-
probe runs, all agreeing. Default N is 3.
|
|
25
|
+
probe runs, all agreeing. Default N is 3. **Fewer than three runs is
|
|
26
|
+
provisional**: the evidence declares `run.provisional: true`, every
|
|
27
|
+
rendering marks the verdicts as unconfirmed, and a provisional run is
|
|
28
|
+
never frozen into a baseline. Provisional runs exist for the fix loop —
|
|
29
|
+
a fast signal while writing a test — not for a gate.
|
|
26
30
|
3. **Only a test body rejecting the behaviour kills.** A test that fails by
|
|
27
31
|
assertion — or by an exception the fault provoked inside it — counts. A
|
|
28
32
|
timeout does not, and a suite that fails to load does not: neither is
|
|
@@ -46,6 +50,12 @@ Rules that follow from the table:
|
|
|
46
50
|
snapshot the working tree — otherwise the answer looks right and is not.
|
|
47
51
|
7. **Never a single global score.** Output is per claim, ranked. Blindness is
|
|
48
52
|
concentrated, and one number hides where.
|
|
53
|
+
8. **A changed fault is a finding.** Editing a claim is legitimate — claims
|
|
54
|
+
can be wrong — but the cheapest way to make a survivor disappear without
|
|
55
|
+
writing a test is to weaken its fault. Evidence records each fault's
|
|
56
|
+
content hash; the status document lists every fault whose content
|
|
57
|
+
changed since it was probed, with its previous verdict. The change is
|
|
58
|
+
allowed; it is never invisible.
|
|
49
59
|
|
|
50
60
|
## Baseline and delta
|
|
51
61
|
|
|
@@ -20,7 +20,8 @@ The pattern all such tools run:
|
|
|
20
20
|
| `baseline` | [`schemas/baseline.schema.json`](schemas/baseline.schema.json) | Frozen fingerprints of existing debt. Gate only what is new. |
|
|
21
21
|
| `ignore` | [`schemas/ignore.schema.json`](schemas/ignore.schema.json) | Reviewable scoping. Every entry has a reason. |
|
|
22
22
|
| `calibration` | [`schemas/calibration.schema.json`](schemas/calibration.schema.json) | P(finding is real) per bucket, with a Wilson interval and the sample size behind it. |
|
|
23
|
-
| `brief` | [`schemas/brief.schema.json`](schemas/brief.schema.json) | What to tell an agent before it writes code — ranked, capped, never a single score. |
|
|
23
|
+
| `brief` | [`schemas/brief.schema.json`](schemas/brief.schema.json) | What to tell an agent before it writes code — ranked, capped, never a single score, and carrying the one next action. |
|
|
24
|
+
| `status` | [`schemas/status.schema.json`](schemas/status.schema.json) | Where the project is and what happens next — the single machine-readable truth every human rendering derives from. Surfaces faults whose content changed since they were probed. |
|
|
24
25
|
|
|
25
26
|
Shared definitions (verdicts, fault classes, provenance, annotations) live in
|
|
26
27
|
[`schemas/common.schema.json`](schemas/common.schema.json). Gate behaviour —
|
|
@@ -44,5 +44,10 @@
|
|
|
44
44
|
"isNew": true
|
|
45
45
|
}
|
|
46
46
|
],
|
|
47
|
-
"text": "## TEST BLINDSPOT CONTEXT\n\nSource: testguard 0.1.0 @ deadbeefcafe. 2 claims; 1 killed, 1 survived, 1 unverifiable, 1 nocover, 1 flaky-defender. 1 new since baseline.\n\n1. [NEW] REDACT-002 unverifiable (high) src/redact.mjs — Anchor not found; re-author the fault.\n2. REDACT-001 SURVIVED (critical) src/redact.mjs — test/redact.test.mjs asserts with objectContaining and omits the `content` key.\n"
|
|
47
|
+
"text": "## TEST BLINDSPOT CONTEXT\n\nSource: testguard 0.1.0 @ deadbeefcafe. 2 claims; 1 killed, 1 survived, 1 unverifiable, 1 nocover, 1 flaky-defender. 1 new since baseline.\n\n1. [NEW] REDACT-002 unverifiable (high) src/redact.mjs — Anchor not found; re-author the fault.\n2. REDACT-001 SURVIVED (critical) src/redact.mjs — test/redact.test.mjs asserts with objectContaining and omits the `content` key.\n",
|
|
48
|
+
"next": {
|
|
49
|
+
"action": "write-test",
|
|
50
|
+
"command": "write a test in test/redact.test.mjs that fails on REDACT-001/F1 and passes on HEAD, then: testguard probe --claim REDACT-001 --include-dirty",
|
|
51
|
+
"why": "REDACT-001/F1 (critical) survived: the audit row can carry the raw input and no test notices."
|
|
52
|
+
}
|
|
48
53
|
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"tool": {
|
|
4
|
+
"name": "testguard",
|
|
5
|
+
"version": "0.1.0"
|
|
6
|
+
},
|
|
7
|
+
"run": {
|
|
8
|
+
"id": "run-provisional",
|
|
9
|
+
"startedAt": "2026-09-17T02:00:00Z",
|
|
10
|
+
"finishedAt": "2026-09-17T02:00:41Z",
|
|
11
|
+
"repo": {
|
|
12
|
+
"head": "deadbeefcafe",
|
|
13
|
+
"dirty": false,
|
|
14
|
+
"snapshot": "c0ffeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
15
|
+
},
|
|
16
|
+
"runner": {
|
|
17
|
+
"name": "vitest",
|
|
18
|
+
"version": "5.0.1"
|
|
19
|
+
},
|
|
20
|
+
"confirmRuns": 1,
|
|
21
|
+
"mode": "worktree",
|
|
22
|
+
"provisional": true
|
|
23
|
+
},
|
|
24
|
+
"records": [
|
|
25
|
+
{
|
|
26
|
+
"fingerprint": "01876384493be6809da455cb1a1c4bc92e7564e9898506d790b9589200502163",
|
|
27
|
+
"claim": {
|
|
28
|
+
"id": "REDACT-001",
|
|
29
|
+
"statement": "The audit record written for a redacted message never contains the original secret.",
|
|
30
|
+
"severity": "critical",
|
|
31
|
+
"source": {
|
|
32
|
+
"kind": "spec",
|
|
33
|
+
"ref": "docs/spec.md#audit-records"
|
|
34
|
+
},
|
|
35
|
+
"producedBy": {
|
|
36
|
+
"producer": "human",
|
|
37
|
+
"by": "maintainer",
|
|
38
|
+
"at": "2026-09-17T02:00:00Z"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"subject": {
|
|
42
|
+
"kind": "fault",
|
|
43
|
+
"id": "F1",
|
|
44
|
+
"description": "Audit row is written with the raw input.",
|
|
45
|
+
"file": "src/redact.mjs",
|
|
46
|
+
"faultClass": "variable-swap",
|
|
47
|
+
"producedBy": {
|
|
48
|
+
"producer": "human",
|
|
49
|
+
"by": "maintainer",
|
|
50
|
+
"at": "2026-09-17T02:00:00Z"
|
|
51
|
+
},
|
|
52
|
+
"contentHash": "f232f31d9e6eecc8e952f0da1fb0878c5060e1a4dc19724874b041a4ab265aaf"
|
|
53
|
+
},
|
|
54
|
+
"verdict": "survived",
|
|
55
|
+
"detail": {
|
|
56
|
+
"baselineRuns": [
|
|
57
|
+
{
|
|
58
|
+
"outcome": "pass",
|
|
59
|
+
"tests": {
|
|
60
|
+
"total": 4,
|
|
61
|
+
"passed": 4,
|
|
62
|
+
"failed": 0
|
|
63
|
+
},
|
|
64
|
+
"assertionFailures": 0,
|
|
65
|
+
"durationMs": 310
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"probeRuns": [
|
|
69
|
+
{
|
|
70
|
+
"outcome": "pass",
|
|
71
|
+
"tests": {
|
|
72
|
+
"total": 4,
|
|
73
|
+
"passed": 4,
|
|
74
|
+
"failed": 0
|
|
75
|
+
},
|
|
76
|
+
"assertionFailures": 0,
|
|
77
|
+
"durationMs": 310
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"escalated": true,
|
|
81
|
+
"escalationRuns": [
|
|
82
|
+
{
|
|
83
|
+
"outcome": "pass",
|
|
84
|
+
"tests": {
|
|
85
|
+
"total": 4,
|
|
86
|
+
"passed": 4,
|
|
87
|
+
"failed": 0
|
|
88
|
+
},
|
|
89
|
+
"assertionFailures": 0,
|
|
90
|
+
"durationMs": 310
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"defenders": {
|
|
95
|
+
"requested": [
|
|
96
|
+
"test/redact.test.mjs"
|
|
97
|
+
],
|
|
98
|
+
"resolved": [
|
|
99
|
+
"test/redact.test.mjs"
|
|
100
|
+
],
|
|
101
|
+
"nocover": false,
|
|
102
|
+
"discovered": false
|
|
103
|
+
},
|
|
104
|
+
"inputs": {
|
|
105
|
+
"targetHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
106
|
+
"defenderHashes": {
|
|
107
|
+
"test/redact.test.mjs": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"rank": {
|
|
111
|
+
"score": 9.5,
|
|
112
|
+
"blastRadius": 7,
|
|
113
|
+
"tier": "critical"
|
|
114
|
+
},
|
|
115
|
+
"annotations": [
|
|
116
|
+
{
|
|
117
|
+
"kind": "tracked",
|
|
118
|
+
"text": "Assertion uses objectContaining without the content key.",
|
|
119
|
+
"by": "maintainer",
|
|
120
|
+
"at": "2026-09-17T02:00:00Z",
|
|
121
|
+
"ref": "issue#12"
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"fingerprint": "10d526ffb9535c8d99c39dcea29a8d6fa86c3370a2a5061de5320e028ba79b1f",
|
|
127
|
+
"claim": {
|
|
128
|
+
"id": "REDACT-001",
|
|
129
|
+
"statement": "The audit record written for a redacted message never contains the original secret.",
|
|
130
|
+
"severity": "critical",
|
|
131
|
+
"source": {
|
|
132
|
+
"kind": "spec",
|
|
133
|
+
"ref": "docs/spec.md#audit-records"
|
|
134
|
+
},
|
|
135
|
+
"producedBy": {
|
|
136
|
+
"producer": "human",
|
|
137
|
+
"by": "maintainer",
|
|
138
|
+
"at": "2026-09-17T02:00:00Z"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"subject": {
|
|
142
|
+
"kind": "fault",
|
|
143
|
+
"id": "F2",
|
|
144
|
+
"description": "Redaction skipped.",
|
|
145
|
+
"file": "src/redact.mjs",
|
|
146
|
+
"faultClass": "statement-deleted",
|
|
147
|
+
"contentHash": "f232f31d9e6eecc8e952f0da1fb0878c5060e1a4dc19724874b041a4ab265aaf"
|
|
148
|
+
},
|
|
149
|
+
"verdict": "killed",
|
|
150
|
+
"detail": {
|
|
151
|
+
"baselineRuns": [
|
|
152
|
+
{
|
|
153
|
+
"outcome": "pass",
|
|
154
|
+
"tests": {
|
|
155
|
+
"total": 4,
|
|
156
|
+
"passed": 4,
|
|
157
|
+
"failed": 0
|
|
158
|
+
},
|
|
159
|
+
"assertionFailures": 0,
|
|
160
|
+
"durationMs": 310
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
"probeRuns": [
|
|
164
|
+
{
|
|
165
|
+
"outcome": "fail",
|
|
166
|
+
"tests": {
|
|
167
|
+
"total": 4,
|
|
168
|
+
"passed": 3,
|
|
169
|
+
"failed": 1
|
|
170
|
+
},
|
|
171
|
+
"assertionFailures": 1,
|
|
172
|
+
"durationMs": 322
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
"defenders": {
|
|
177
|
+
"requested": [
|
|
178
|
+
"test/redact.test.mjs"
|
|
179
|
+
],
|
|
180
|
+
"resolved": [
|
|
181
|
+
"test/redact.test.mjs"
|
|
182
|
+
],
|
|
183
|
+
"nocover": false
|
|
184
|
+
},
|
|
185
|
+
"inputs": {
|
|
186
|
+
"targetHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
187
|
+
"defenderHashes": {
|
|
188
|
+
"test/redact.test.mjs": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"fingerprint": "0a0767ff8b124883e0bc042094c8494027b3d6d45bb8f4e40eda0930214a8970",
|
|
194
|
+
"claim": {
|
|
195
|
+
"id": "REDACT-002",
|
|
196
|
+
"statement": "A rule with an invalid pattern is skipped; it never aborts the scan.",
|
|
197
|
+
"severity": "high",
|
|
198
|
+
"source": {
|
|
199
|
+
"kind": "comment",
|
|
200
|
+
"ref": "src/redact.mjs:41"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"subject": {
|
|
204
|
+
"kind": "fault",
|
|
205
|
+
"id": "F1",
|
|
206
|
+
"description": "Invalid pattern rethrows.",
|
|
207
|
+
"file": "src/redact.mjs",
|
|
208
|
+
"faultClass": "exception-swallowed",
|
|
209
|
+
"contentHash": "f232f31d9e6eecc8e952f0da1fb0878c5060e1a4dc19724874b041a4ab265aaf"
|
|
210
|
+
},
|
|
211
|
+
"verdict": "unverifiable",
|
|
212
|
+
"detail": {
|
|
213
|
+
"reason": "anchor-missing",
|
|
214
|
+
"baselineRuns": [],
|
|
215
|
+
"probeRuns": [],
|
|
216
|
+
"anchor": {
|
|
217
|
+
"hits": 0,
|
|
218
|
+
"expected": 1
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"defenders": {
|
|
222
|
+
"requested": [
|
|
223
|
+
"test/redact.test.mjs"
|
|
224
|
+
],
|
|
225
|
+
"resolved": [
|
|
226
|
+
"test/redact.test.mjs"
|
|
227
|
+
],
|
|
228
|
+
"nocover": false
|
|
229
|
+
},
|
|
230
|
+
"inputs": {
|
|
231
|
+
"targetHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
232
|
+
"defenderHashes": {
|
|
233
|
+
"test/redact.test.mjs": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"fingerprint": "81cb777d4cba8dd856ad3f0626fe58186906e5c18a01425d82957adaa963cb28",
|
|
239
|
+
"claim": {
|
|
240
|
+
"id": "REDACT-002",
|
|
241
|
+
"statement": "A rule with an invalid pattern is skipped; it never aborts the scan.",
|
|
242
|
+
"severity": "high",
|
|
243
|
+
"source": {
|
|
244
|
+
"kind": "comment",
|
|
245
|
+
"ref": "src/redact.mjs:41"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
"subject": {
|
|
249
|
+
"kind": "fault",
|
|
250
|
+
"id": "F1",
|
|
251
|
+
"description": "Invalid pattern rethrows.",
|
|
252
|
+
"file": "src/redact.mjs",
|
|
253
|
+
"faultClass": "exception-swallowed",
|
|
254
|
+
"contentHash": "f232f31d9e6eecc8e952f0da1fb0878c5060e1a4dc19724874b041a4ab265aaf"
|
|
255
|
+
},
|
|
256
|
+
"verdict": "nocover",
|
|
257
|
+
"detail": {
|
|
258
|
+
"baselineRuns": [],
|
|
259
|
+
"probeRuns": []
|
|
260
|
+
},
|
|
261
|
+
"defenders": {
|
|
262
|
+
"requested": [
|
|
263
|
+
"test/rules.test.mjs"
|
|
264
|
+
],
|
|
265
|
+
"resolved": [],
|
|
266
|
+
"nocover": true
|
|
267
|
+
},
|
|
268
|
+
"inputs": {
|
|
269
|
+
"targetHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
270
|
+
"defenderHashes": {
|
|
271
|
+
"test/redact.test.mjs": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
]
|
|
276
|
+
}
|