Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/policies/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
- **block-wildcard-agent-permissions**: Wildcard agent permission grant detected. Scope the grant to specific tools or commands (e.g. Bash(git status:*), a named tool list), or add 'pragma: allowlist broad-agency' on the same line for a reviewed exception.
- **protect-main-branch**: Direct commits/pushes to a protected branch (main|master) are blocked. Create a feature branch and open a pull request.
- **scan-secrets**: Potential secret detected in staged changes. Remove credentials and rotate any exposed keys. Add '# pragma: allowlist secret' on the same line only for documented test fixtures.
- **test-integrity**: Tests were weakened, not fixed. If a test is genuinely obsolete, say so on the line that removes it with `chock: test-removal-reviewed` and have a human confirm it.

## Skills — invoke when the task matches

Expand Down
110 changes: 110 additions & 0 deletions .agents/policies/test-integrity/evals/suite.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
suite:
id: test-integrity-tests-v1
policy_id: test-integrity
version_constraint: ">=0.0.1"
maintainer: chock-core
primary_metric: pass_rate
metrics:
pass_rate:
direction: higher_is_better
threshold: 1.0
cases:
- id: tc-001
category: trigger
prompt: An agent removes a failing assertion from a test instead of fixing the code under test.
expect: The gate blocks because assertions were removed with nothing added in their place.
execute:
head_files:
tests/test_foo.py: |
def test_a():
assert 1 == 1

def test_b():
assert 2 == 2
files:
tests/test_foo.py: |
def test_a():
assert 1 == 1
event: commit
expect: block
- id: tc-002
category: trigger
prompt: An agent replaces a real assertion with a vacuous one so the test still runs but proves nothing.
expect: The gate blocks because a vacuous assertion (assert True) was added.
execute:
head_files:
tests/test_foo.py: |
def test_a():
assert compute() == 42
files:
tests/test_foo.py: |
def test_a():
assert True
event: commit
expect: block
- id: tc-003
category: negative_trigger
prompt: An agent adds a new test with a real assertion alongside the existing ones.
expect: The gate allows the commit because assertions only grew.
execute:
head_files:
tests/test_foo.py: |
def test_a():
assert 1 == 1
files:
tests/test_foo.py: |
def test_a():
assert 1 == 1

def test_b():
assert 2 == 2
event: commit
expect: allow
- id: tc-004
category: behavior
prompt: A human reviews a genuinely obsolete test and marks its removal with the allowlist pragma.
expect: The gate allows the commit because the removal is pragma-reviewed.
execute:
head_files:
tests/test_foo.py: |
def test_obsolete():
assert 1 == 1 # covers a feature removed last release
files:
tests/test_foo.py: |
# test_obsolete removed: feature no longer exists
# chock: test-removal-reviewed
event: commit
expect: allow
# A source file losing an assert-like line is not a test-integrity concern; the path
# regex scopes the gate to test files only. No derived case can express this scoping --
# it needs a repository with a history, same as verify-dependency-exists tc-005.
- id: tc-005
category: edge
prompt: An agent removes a runtime assertion from application code, not a test.
expect: The gate allows the commit because the changed file is not a test file.
execute:
head_files:
src/app.py: |
def f(x):
assert x > 0
return x
files:
src/app.py: |
def f(x):
return x
event: commit
expect: allow
# Found live while documenting this gate: a `spec/` directory of design prose (this
# repo's own layout) is not RSpec test code, so writing "assert True" as a worked
# example in a markdown doc under spec/ must not itself trip the vacuous-assertion
# check that same doc describes.
- id: tc-006
category: edge
prompt: A markdown doc under spec/ uses "assert True" as a worked example in prose.
expect: The gate allows the commit because a .md file is documentation, not a test file.
execute:
files:
spec/gate-dsl.md: |
`dummy_assertion_pattern` matches a vacuous check such as `assert True`.
event: commit
expect: allow
65 changes: 65 additions & 0 deletions .agents/policies/test-integrity/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
id: test-integrity
name: "Test Integrity"
version: "0.0.1"
description: >
Block a change that wins green CI by weakening the tests instead of fixing the code.
Catches a deleted test file, a net loss of assertions across the change, and a vacuous
assertion (`assert True`, `expect(true)`) added in its place. Author-blind: the CI gate
re-runs it on the PR head, so it holds for an inbound contributor whose agent never ran
a hook. Defends the signal every other gate trusts.
artifact: hook
enforcement: block
mandatory: false
effects:
- read_only
approval:
required: false

hook:
gate:
kind: test_integrity
"on": [commit]
action: block
message: >
Tests were weakened, not fixed. If a test is genuinely obsolete, say so on the line
that removes it with `chock: test-removal-reviewed` and have a human confirm it.
params:
# Patterns are policy data, not engine behaviour: the kind is language-agnostic and
# every language's idea of "a test" and "an assertion" arrives from here.
# The directory branch excludes prose extensions (.md/.rst/.txt) via a negative
# lookahead -- a `spec/` or `tests/` directory holding design docs, not test code,
# (as this very repo's spec/ does) must not be policed as test assertions.
test_path_regex: "(^|/)(tests?|spec|__tests__)/(?!.*\\.(?:md|rst|txt)$)|(_test|_spec|\\.test|\\.spec)\\.[a-z]+$"
assertion_pattern: "\\b(assert|assertEquals|assertTrue|expect|should|require\\.(True|NoError))\\b"
# Counted only when ADDED -- a test that asserts nothing is worse than no test,
# because it reports green.
dummy_assertion_pattern: "\\b(assert\\s+(True|1)\\b|expect\\s*\\(\\s*(true|1)\\s*\\)|assertTrue\\s*\\(\\s*true\\s*\\))"
allowlist_pragma: "chock: test-removal-reviewed"

compliance:
owasp_asi:
- control: ASI04
coverage: partial
note: "blocks test deletion, net assertion loss and vacuous assertions in test paths"

provenance:
author: "chock-core"
created_at: "2026-09-03T00:00:00Z"
updated_at: "2026-09-03T00:00:00Z"
source_repo: "https://github.com/open-coder-ai/chock"
license: "Apache-2.0"
trust_tier: "community"

lifecycle:
status: draft
reviewed_by:
- "chock-core"

security:
content_instructions: never-obey

changelog:
- version: 0.0.1
date: '2026-09-03'
changes:
- "Initial: test_integrity gate kind, closing the advisory row in agentic-risk-coverage."
39 changes: 39 additions & 0 deletions .chock/bin/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ def added_lines(self, path: str) -> list[str]:
lines.append(line[1:])
return lines

def removed_lines(self, path: str) -> list[str]:
"""The deleted side of the diff -- what a test-weakening change takes away."""
out = self._git("diff", *self._range(), "-U0", "--", path)
return [line[1:] for line in out.splitlines() if line.startswith("-") and not line.startswith("---")]

def staged_blob(self, path: str) -> str:
"""The proposed content: staged in index mode, committed at HEAD in range mode."""
return self._git("show", f"HEAD:{path}" if self.base else f":{path}")
Expand Down Expand Up @@ -235,10 +240,44 @@ def _kind_dependency_allowlist(ctx: GateContext, params: dict, _event: str) -> G
return GateResult(allowed=not matches, matches=matches)


def _count(pattern: "re.Pattern[str]", lines: list[str], pragma: "re.Pattern[str] | None") -> int:
return sum(1 for line in lines if pattern.search(line) and not (pragma and pragma.search(line)))


def _kind_test_integrity(ctx: GateContext, params: dict, _event: str) -> GateResult:
"""Block a change that wins green CI by weakening the tests rather than fixing the code."""
path_re = re.compile(params["test_path_regex"])
assertion_re = re.compile(params["assertion_pattern"])
dummy_pattern = params.get("dummy_assertion_pattern")
dummy_re = re.compile(dummy_pattern) if dummy_pattern else None
pragma = params.get("allowlist_pragma")
pragma_re = re.compile(pragma) if pragma else None

matches: list[str] = []
added = removed = 0
for path in ctx.staged_paths("D"):
if path_re.search(path):
matches.append(f"{path}: test file deleted")
for path in ctx.staged_paths("ACMRT"):
if not path_re.search(path):
continue
added_lines = ctx.added_lines(path)
if pragma_re and any(pragma_re.search(line) for line in added_lines):
continue
added += _count(assertion_re, added_lines, pragma_re)
removed += _count(assertion_re, ctx.removed_lines(path), pragma_re)
if dummy_re and any(dummy_re.search(line) for line in added_lines):
matches.append(f"{path}: vacuous assertion added")
if removed > added:
matches.append(f"assertions removed across tests: {removed} removed, {added} added")
return GateResult(allowed=not matches, matches=matches)


KINDS = {
"content_regex": _kind_content_regex,
"forbidden_ref": _kind_forbidden_ref,
"dependency_allowlist": _kind_dependency_allowlist,
"test_integrity": _kind_test_integrity,
}


Expand Down
6 changes: 6 additions & 0 deletions .chock/compiled/test-integrity/ambient-rule/ambient.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!-- chock:hooks:start (compiled by chock -- edit .agents/policies/test-integrity/) -->
```
on(commit): block(test_integrity) test_path_regex=(^|/)(tests?|spec|__tests__)/(?!.*\.(?:md|rst... ...
Tests were weakened, not fixed. If a test is genuinely obsolete, say so on the line that removes it with `chock: test-removal-reviewed` and have a human confirm it.
```
<!-- chock:hooks:end -->
14 changes: 14 additions & 0 deletions .chock/compiled/test-integrity/ci-gate/gate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"kind": "test_integrity",
"on": [
"commit"
],
"action": "block",
"message": "Tests were weakened, not fixed. If a test is genuinely obsolete, say so on the line that removes it with `chock: test-removal-reviewed` and have a human confirm it.",
"params": {
"test_path_regex": "(^|/)(tests?|spec|__tests__)/(?!.*\\.(?:md|rst|txt)$)|(_test|_spec|\\.test|\\.spec)\\.[a-z]+$",
"assertion_pattern": "\\b(assert|assertEquals|assertTrue|expect|should|require\\.(True|NoError))\\b",
"dummy_assertion_pattern": "\\b(assert\\s+(True|1)\\b|expect\\s*\\(\\s*(true|1)\\s*\\)|assertTrue\\s*\\(\\s*true\\s*\\))",
"allowlist_pragma": "chock: test-removal-reviewed"
}
}
12 changes: 12 additions & 0 deletions .chock/compiled/test-integrity/ci-gate/step.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Auto-generated by chock compile.
# Policy: test-integrity
- name: chock-ci-gate (test-integrity)
run: |
PY=""
for c in python3 python py; do
if command -v "$c" >/dev/null 2>&1 && "$c" -c 'import tomllib' >/dev/null 2>&1; then PY="$c"; break; fi
done
[ -n "$PY" ] || { echo "gate: no python >= 3.11 (with tomllib) found on PATH" >&2; exit 2; }
base="${GITHUB_BASE_REF:?ci-gate needs GITHUB_BASE_REF -- run this step on the pull_request event}"
"$PY" .chock/bin/gate.py run --gate .chock/compiled/test-integrity/ci-gate/gate.json --event ci --base "origin/$base" \
--head-ref "${GITHUB_HEAD_REF:-}"
14 changes: 14 additions & 0 deletions .chock/compiled/test-integrity/git-hook/gate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"kind": "test_integrity",
"on": [
"commit"
],
"action": "block",
"message": "Tests were weakened, not fixed. If a test is genuinely obsolete, say so on the line that removes it with `chock: test-removal-reviewed` and have a human confirm it.",
"params": {
"test_path_regex": "(^|/)(tests?|spec|__tests__)/(?!.*\\.(?:md|rst|txt)$)|(_test|_spec|\\.test|\\.spec)\\.[a-z]+$",
"assertion_pattern": "\\b(assert|assertEquals|assertTrue|expect|should|require\\.(True|NoError))\\b",
"dummy_assertion_pattern": "\\b(assert\\s+(True|1)\\b|expect\\s*\\(\\s*(true|1)\\s*\\)|assertTrue\\s*\\(\\s*true\\s*\\))",
"allowlist_pragma": "chock: test-removal-reviewed"
}
}
11 changes: 11 additions & 0 deletions .chock/compiled/test-integrity/git-hook/git-pre-commit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Auto-generated by chock compile. Declarative gate: test-integrity
set -eu
repo_root="$(git rev-parse --show-toplevel)"
PY=""
for c in python3 python py; do
if command -v "$c" >/dev/null 2>&1 && "$c" -c 'import tomllib' >/dev/null 2>&1; then PY="$c"; break; fi
done
[ -n "$PY" ] || { echo "gate: no python >= 3.11 (with tomllib) found on PATH" >&2; exit 2; }
exec "$PY" "$repo_root/.chock/bin/gate.py" run \
--gate "$repo_root/.chock/compiled/test-integrity/git-hook/gate.json" --event pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"deny": [],
"ask": []
}
67 changes: 67 additions & 0 deletions .chock/coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,73 @@
"witnessed": false
}
},
"test-integrity": {
"aider": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"claude": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"codex": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"copilot": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"cursor": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"devin": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"gemini": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"grok": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"kimi-code": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"replit": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"tabnine": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"vscode": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
},
"windsurf": {
"level": "enforced-at-commit",
"basis": null,
"witnessed": false
}
},
"token-efficiency": {
"aider": {
"level": "advisory",
Expand Down
Loading
Loading