Run a structured quality control audit on any codebase. Use when asked to QC, audit, review, or check code quality for a project. Supports Python, TypeScript, GDScript, and general projects. Produces a standardized report with PASS/WARN/FAIL verdict, covering tests, imports, type checking, static analysis, smoke tests, and documentation. Also use when asked to compare QC results over time.
Scanned 9/7/2026
Install to Claude Code
npx -y skills add modbender/skill-library-mcp --skill code-qc --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Code Qc?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/modbender-code-qc)More formats (shields.io, HTML) on the badges page.
---
name: code-qc
description: Run a structured quality control audit on any codebase. Use when asked to QC, audit, review, or check code quality for a project. Supports Python, TypeScript, GDScript, and general projects. Produces a standardized report with PASS/WARN/FAIL verdict, covering tests, imports, type checking, static analysis, smoke tests, and documentation. Also use when asked to compare QC results over time.
---
# Code QC
Structured quality control audit for codebases. Delegates static analysis to proper tools (ruff, eslint, gdlint) and focuses on what AI adds: semantic understanding, cross-module consistency, and dynamic smoke test generation.
## Quick Start
1. Detect project type (read the profile for that language)
2. Load `.qc-config.yaml` if present (for custom thresholds/exclusions)
3. Run the 8-phase audit (or subset with `--quick`)
4. Generate report with verdict
5. Save baseline for future comparison
## Configuration (`.qc-config.yaml`)
Optional project-level config for monorepos and custom settings:
```yaml
# .qc-config.yaml
thresholds:
test_failure_rate: 0.05 # >5% = FAIL, 0-5% = WARN, 0% = PASS
lint_errors_max: 0 # Max lint errors before FAIL
lint_warnings_max: 50 # Max warnings before WARN
type_errors_max: 0 # Max type errors before FAIL (strict by default)
exclude:
dirs: [vendor, third_party, generated]
files: ["*_generated.py", "*.pb.go"]
changed_only: false # Only check git-changed files (CI mode)
fail_fast: false # Stop on first failure
quick_mode: false # Only run Phase 1, 3, 3.5, 6
languages:
python:
min_coverage: 80
ignore_rules: [T201] # Allow print in this project
typescript:
strict_mode: true # Require tsconfig strict: true
ignore_rules: [] # eslint rules to ignore
gdscript:
godot_version: "4.2"
```
## Execution Modes
| Mode | Phases Run | Use Case |
|------|------------|----------|
| Full (default) | All 8 phases | Thorough audit |
| `--quick` | 1, 3, 3.5, 6 | Fast sanity check |
| `--changed-only` | All, filtered | CI on pull requests |
| `--fail-fast` | All, stops early | Find first issue fast |
| `--fix` | 3 with autofix | Apply automatic fixes |
## Phase Overview
| # | Phase | What | Tools |
|---|-------|------|-------|
| 1 | Test Suite | Run existing tests + coverage | pytest --cov / jest --coverage |
| 2 | Import Integrity | Verify all modules load | `scripts/import_check.py` |
| 3 | Static Analysis | Lint with proper tools | ruff / eslint / gdlint |
| 3.5 | Type Checking | Static type verification | mypy / tsc --noEmit / (N/A for GDScript) |
| 4 | Smoke Tests | Verify business logic works | AI-generated per project |
| 5 | UI/Frontend | Verify UI components load | Framework-specific |
| 6 | File Consistency | Syntax + git state | `scripts/syntax_check.py` + git |
| 7 | Documentation | Docstrings + docs accuracy | `scripts/docstring_check.py` |
## Phase Details
### Phase 1: Test Suite
Run the project's test suite with coverage. Auto-detect the test runner:
```
pytest.ini / pyproject.toml [tool.pytest] → pytest --cov
package.json scripts.test → npm test (or npx vitest --coverage)
Cargo.toml → cargo test
project.godot → (GUT if present, else manual)
```
**Record:** total, passed, failed, errors, skipped, duration, coverage %.
**Verdict contribution:**
- No tests found → **SKIP** (not FAIL; project may be config-only)
- Failure rate = 0% → **PASS**
- Failure rate ≤ threshold (default 5%) → **WARN**
- Failure rate > threshold → **FAIL**
**Coverage reporting (Python):**
```bash
pytest --cov=<package> --cov-report=term-missing --cov-report=json
```
### Phase 2: Import Integrity (Python/GDScript)
**Python:** Run `scripts/import_check.py` against the project root.
**GDScript:** Verify scene/preload references are valid (see gdscript-profile.md).
#### Critical vs Optional Import Classification
Use these heuristics to classify import failures:
| Pattern | Classification | Rationale |
|---------|---------------|-----------|
| `__init__.py`, `main.py`, `app.py`, `cli.py` | **Critical** | Core entry points |
| Module in `src/`, `lib/`, or top-level package | **Critical** | Core functionality |
| `*_test.py`, `test_*.py`, `conftest.py` | **Optional** | Test infrastructure |
| Modules in `examples/`, `scripts/`, `tools/` | **Optional** | Auxiliary code |
| Import error mentions `cuml`, `triton`, `tensorrt` | **Optional** | Hardware-specific |
| Import error mentions missing system lib | **Optional** | Environment-specific |
| Dependency in `[project.optional-dependencies]` | **Optional** | Declared optional |
### Phase 3: Static Analysis
**Do NOT use grep.** Use the language's standard linter.
#### Standard Mode
```bash
# Python
ruff check --select E722,T201,B006,F401,F841,UP,I --statistics <project>
# TypeScript
npx eslint . --format json
# GDScript
gdlint <project>
```
#### Fix Mode (`--fix`)
When `--fix` is specified, apply automatic corrections:
```bash
# Python — safe auto-fixes
ruff check --fix --select E,F,I,UP <project>
ruff format <project>
# TypeScript
npx eslint . --fix
# GDScript
gdformat <project>
```
**Important:** After `--fix`, re-run the check to report remaining issues that couldn't be auto-fixed.
### Phase 3.5: Type Checking (NEW)
Run static type analysis before proceeding to runtime checks.
**Python:**
```bash
mypy <package> --ignore-missing-imports --no-error-summary
# or if pyproject.toml has [tool.pyright]:
pyright <package>
```
**TypeScript:**
```bash
npx tsc --noEmit
```
**GDScript:** Godot 4 has built-in static typing but no standalone checker. Estimate type coverage manually:
```bash
# Find untyped declarations
grep -rn "var \w\+ =" --include="*.gd" . # Untyped variables
grep -rn "func \w\+(" --include="*.gd" . | grep -v ":" # Untyped functions
```
Use the `estimate_type_coverage()` function from `gdscript-profile.md` to calculate coverage per file:
```python
# From gdscript-profile.md
def estimate_type_coverage(gd_file: str) -> float:
"""Count typed vs untyped declarations."""
# See full implementation in gdscript-profile.md
```
Also check for `@warning_ignore` annotations which may hide type issues.
**Record:** Total errors, categorized by severity.
### Phase 4: Smoke Tests (Business Logic)
Test **backend/core functionality** — NOT UI components (that's Phase 5).
**API Discovery Heuristics:**
1. **Entry points:** Look for `main()`, `cli()`, `app`, `create_app()`, `__main__.py`
2. **Service layer:** Find classes/modules named `*Service`, `*Manager`, `*Handler`
3. **Public API:** Check `__all__` exports in `__init__.py`
4. **FastAPI/Flask:** Find route decorators (`@app.get`, `@router.post`)
5. **CLI:** Find typer/click `@app.command()` decorators
6. **SDK:** Look for client classes, public methods without `_` prefix
**For each discovered API, generate a minimal test:**
```python
def smoke_test_user_service():
"""Test UserService basic CRUD."""
from myproject.services.user import UserService
svc = UserService(db=":memory:")
user = svc.create(name="test")
assert user.id is not None
fetched = svc.get(user.id)
assert fetched.name == "test"
return "PASS"
```
**Guidelines:**
- Import + instantiate + call one method with minimal valid input
- Use in-memory/temp resources (`:memory:`, `tempdir`)
- Each test < 5 seconds
- Catch exceptions, report clearly
### Phase 5: UI/Frontend Verification
Test **UI components** separately from business logic.
| Framework | Test Method |
|-----------|-------------|
| **Gradio** | `from project.ui import create_ui` (no `launch()`) |
| **Streamlit** | `streamlit run app.py --headless` exits cleanly |
| **PyQt/PySide** | Set `QT_QPA_PLATFORM=offscreen`, import widget modules |
| **React** | `npm run build` succeeds |
| **Vue** | `npm run build` succeeds |
| **Godot** | Scene files parse without error, required scripts exist |
| **CLI** | `--help` on all subcommands returns 0 |
**Boundary:** Phase 4 tests "does the logic work?" Phase 5 tests "does the UI render?"
### Phase 6: File Consistency
Run `scripts/syntax_check.py` — compiles all source files to verify no syntax errors.
> **Note:** Phase 2 (Import Integrity) tests *runtime* import behavior including initialization code. Phase 6 tests *static* syntax correctness. Both are needed: a file can have valid syntax but fail to import (e.g., missing dependency), or vice versa (syntax error in a module that's never imported).
Check git state:
```bash
git status --short # Should be clean (or report uncommitted changes)
git diff --check # No conflict markers
```
### Phase 7: Documentation
Run `scripts/docstring_check.py` (now checks `__init__.py` by default).
Also verify:
- README exists and is non-empty
- Key docs (CHANGELOG, CONTRIBUTING) exist if referenced
- No stale TODO markers in docs claiming completion
## Verdict Logic
```
# Calculate test failure rate
failure_rate = test_failures / total_tests
# Default thresholds (override in .qc-config.yaml)
FAIL_THRESHOLD = 0.05 # 5%
WARN_THRESHOLD = 0.00 # 0%
TYPE_ERRORS_MAX = 0 # Default: strict (any type error = FAIL)
# Verdict determination
if any([
failure_rate > FAIL_THRESHOLD,
critical_import_failure,
type_check_errors > thresholds.type_errors_max, # Configurable threshold
lint_errors > thresholds.lint_errors_max,
]):
verdict = "FAIL"
elif any([
0 < failure_rate <= FAIL_THRESHOLD,
optional_import_failures > 0,
lint_warnings > thresholds.lint_warnings_max,
missing_docstrings > 0,
smoke_test_failures > 0,
]):
verdict = "PASS WITH WARNINGS"
else:
verdict = "PASS"
```
## Baseline Comparison
Save results to `.qc-baseline.json`:
```json
{
"timestamp": "2026-02-15T15:00:00Z",
"commit": "abc123",
"verdict": "PASS WITH WARNINGS",
"config": {
"mode": "full",
"thresholds": {"test_failure_rate": 0.05}
},
"phases": {
"tests": {"total": 134, "passed": 134, "failed": 0, "coverage": 87.5},
"imports": {"total": 50, "failed": 0, "optional_failed": 1, "critical_failed": 0},
"types": {"errors": 0, "warnings": 5},
"lint": {"errors": 0, "warnings": 12, "fixed": 8},
"smoke": {"total": 14, "passed": 14},
"docs": {"missing_docstrings": 3}
}
}
```
On subsequent runs, report delta:
```
Tests: 134 → 140 (+6 ✅)
Coverage: 87% → 91% (+4% ✅)
Type errors: 0 → 0 (✅)
Lint warnings: 12 → 5 (-7 ✅)
```
## Report Output
Generate in 3 formats:
1. **Markdown** (`qc-report.md`) — full detailed report for humans
2. **JSON** (`.qc-baseline.json`) — machine-readable for CI/comparison
3. **Summary** (chat message) — 10-line digest for Discord/Slack
### Summary Format Example
```
📊 QC Report: my-project @ abc123
Verdict: ✅ PASS WITH WARNINGS
Tests: 134/134 passed (100%) | Coverage: 87%
Types: 0 errors
Lint: 0 errors, 12 warnings
Imports: 50/50 (1 optional failed)
Smoke: 14/14 passed
⚠️ Warnings:
- 3 missing docstrings
- 12 lint warnings (run with --fix)
```
## Language-Specific Profiles
Read the appropriate profile before running:
- **Python**: `references/python-profile.md`
- **TypeScript**: `references/typescript-profile.md`
- **GDScript**: `references/gdscript-profile.md`
- **General** (any language): `references/general-profile.md`
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!