Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/hwangtaehyun-v11-py-test-runner
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: testing
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions: not checked yet.
What's inside
- Skill name
V11-py-test-runner- Description
- None
- License
- MIT
- Instructions
- 157 lines in SKILL.md · view on GitHub
Agent Skills spec issues
- missing YAML frontmatter
- name: missing
- description: missing
Files (1)
- SKILL.md
SKILL.md (first lines)
# V11 — py-test-runner
> **Owner**: `hooks/validators/py_test_runner.py`
> **Tier**: 2 (PostToolUse) only. Stop is a no-op — V21 (`py_pytest`) handles project-wide pytest with the smart-mode gate (`stop.run_pytest`).
> **File patterns**: `**/*.py`
## Rules
| Rule ID | Severity | When |
|---|---|---|
| `V11-TEST-FAIL` | error | The test corresponding to the edited Python file failed (single-file pytest run). |
| `V11-NO-TEST` | warning | A non-test source file was edited and no `test_<name>.py` / `<name>_test.py` exists nearby. |
| `V11-REPEATED-FAIL` | warning | Same `<file>::<test>` failed ≥ N times in a row (default 3, shared with V09/V10 via `FeedbackTracker`). |
## Why this verifier exists
Python test execution differs from Go's package-scoped `go test` in one important way: pytest **collects** all tests by default, so naively running pytest on every Edit re-collects the whole tree. V11 narrows the scope to "tests that exercise the file just edited" so Tier 2 latency stays under a few seconds.
Three regression patterns this catches:
1. **Refactor a function signature without updating tests.** Module imports work; the test crashes at call time.
2. **Add a new module without tests.** Common in hot prototype phases. V11-NO-TEST surfaces the gap as warning.
3. **Test breaks repeatedly across iterations.** Same as V09/V10 — points to spec/test bug, not implementation bug.
## Design rationale
- **Per-test-file invocation, not per-package.** pytest doesn't have Go's package abstraction; it has files (and conftest scopes). V11 maps `src/foo.py` → `tests/test_foo.py` → `pytest tests/test_foo.py -x -q`.
- **Stop is no-op (V21 covers it).** V21's smart-mode is the right place for the project-wide pytest run; doing it again here is redundant.
- **`-x` (stop on first failure) is intentional.** Tier 2's job is to give Claude one fixable signal per Edit, not a full failure report. The first failure is what matters; V21 (Stop) gets the whole picture.
## How it checks (implementation)
Lives in `hooks/validators/py_test_runner.py`.
### `validate_file(ctx, file_path)`
```python
def validate_file(self, ctx, file_path):
py_root = self._find_python_root(ctx)
if not py_root or not file_path.endswith(".py"):
return []
if self._is_excluded(file_path):
return []
threshold = ctx.config.thresholds.test_runner.repeated_failure_count
findings: list[Finding] = []
if self._is_test_file(file_path):
findings.extend(self._run_test_file(py_root, file_path, threshold))
else:
test_file = self._resolve_test_file(py_root, file_path)
if test_file:
findings.extend(self._run_test_file(py_root, test_file, threshold))
else:
findings.extend(self._check_test_exists(file_path))
return findings
```
### `_resolve_test_file(py_root, file_path)`
```python
# pytest convention: tests/test_<name>.py | <name>_test.py | tests/<dir>/test_<name>.py
src = Path(file_path)
name = src.stem
candidates = [
py_root / "tests" / f"test_{name}.py",
py_root / "tests" / src.parent.name / f"test_{name}.py",
src.with_name(f"test_{name}.py"),
src.with_name(f"{name}_test.py"),
]
return next((c for c in candidates if c.exists()), None)
```
### `_run_test_file(py_root, test_file, threshold)` — V11-TEST-FAIL / V11-REPEATED-FAIL
```python
result = subprocess.run(
[self._python_bin(py_root), "-m", "pytest", "-x", "-q", "--tb=line", str(test_file)],
cwd=str(py_root),
capture_output=True, text=True, timeout=60,
)
if result.returncode == 0:
return []
# Parse "FAILED <file>::<test>"
FAIL = re.compile(r'^FAILED\s+(\S+)::(\S+)')
tracker = FeedbackTracker(ctx)
for line in (result.stdout + result.stderr).splitlines():
if (m := FAIL.match(line)):
sig = f"V11::{m.group(1)}::{m.group(2)}"
streak = tracker.record_failure(sig)
yield Finding(
severity="error",
rule="V11-REPEATED-FAIL" if streak >= threshold else "V11-TEST-FAIL",
file=m.group(1),
message=m.group(2),
...
)
```
### `_check_test_exists(file_path)` — V11-NO-TEST
```python
src = Path(file_path)
# Skip files that don't really need tests (heuristic):
non_blank = [
line for line in src.read_text().splitlines()
if line.strip() and not line.strip().startswith(("import ", "from "))
]
if len(non_blank) < 5:
return [] # tiny / re-export / __init__-style — exempt
if not self._resolve_test_file(self.find_root, file_path):
yield Finding(severity="warning", rule="V11-NO-TEST", ...)
```
### Could be more effective
- **`pytest --collect-only --quiet` for symbol-level coverage.** Currently V11 only checks "does a test file exist?". Collecting tests and matching `test_<func>` to the source's exported symbols would catch the "test file exists but doesn't test the new function" case.
- **Coverage delta on edited file.** `pytest --cov=<file>` + delta vs baseline → "coverage of `src/foo.py` dropped". `coverage.py` overhead is non-trivial — Tier 2 budget might not allow.
- **`pytest-testmon` integration.** testmon already runs only tests affected by the edit. V11 currently re-runs by file convention; testmon would be more accurate. Trade-off: extra dep + cache state to manage.From HwangTaehyun/verifiers, MIT.
More from HwangTaehyun/verifiers
- 0.251score
V18-mock-data-guard
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V20-hasura-graphql
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V01-env-config
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V02-graphql-gen
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V03-proto-connect
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V04-hasura-migration
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V06-go-quality
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V07-ts-quality
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V08-security
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V09-go-test-runner
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V10-ts-test-runner
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9 - 0.251score
V12-commit-discipline
HwangTaehyun
No description from the publisher yet.
SkillCFreshness: CClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Apr 29, 2026 (6 months ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/HwangTaehyun/verifiers
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Cite this page
V11-py-test-runner skill. Agent Mart. https://agentmart.dev/skills/hwangtaehyun-v11-py-test-runner (last updated 2026-10-24)