Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Python quality checks: ruff, pytest, mypy, bandit in deterministic order.
.claude/skills/notque-python-quality-gate/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 102% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 109% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 109% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 62% | 0% |
Run four quality tools in deterministic order -- ruff, pytest, mypy, bandit -- and produce a structured pass/fail report with severity-categorized issues and auto-fix commands.
| Signal | Load These Files | Why | |---|---|---| | writing the quality gate report | report-template.md | Loads detailed guidance from report-template.md. | | invoking ruff/mypy/pytest; severity classification and pass/fail thresholds | tool-commands.md | Loads detailed guidance from tool-commands.md. |
Step 1: Read CLAUDE.md and detect project configuration.
Read and follow the repository's CLAUDE.md before any execution. Then detect project configuration:
bashls -la pyproject.toml setup.py setup.cfg mypy.ini .python-version 2>/dev/null
Identify Python version target, ruff config, pytest config, mypy config from pyproject.toml. Only validate code -- never add tools, features, or flexibility not requested.
Step 2: Detect source and test directories.
bashls -d src/ app/ lib/ 2>/dev/null || echo "Source: current directory" ls -d tests/ test/ 2>/dev/null || echo "Tests: not found"
Step 3: Verify tool availability.
bashruff --version pytest --version mypy --version || echo "mypy not installed (optional)" bandit --version || echo "bandit not installed (optional)"
If ruff or pytest are missing, STOP. These are required:
ERROR: Required tool not found: {tool_name}
Install with: pip install ruff pytest pytest-covDo not install missing tools automatically unless the user explicitly requests it. Do not modify pyproject.toml or configuration files unless explicitly asked.
Gate: ruff and pytest available. Project structure identified. Proceed only when gate passes.
Run all checks in fixed order, capturing full output for each. Show complete command output with exact file paths and line numbers -- never summarize or paraphrase tool output, because summarization hides the details engineers need to locate and fix issues.
Step 1: Ruff linting.
bashruff check . --output-format=grouped
Step 2: Ruff formatting check.
bashruff format --check .
Step 3: Type checking with mypy (if installed).
bashmypy . --ignore-missing-imports --show-error-codes
Skip and note in report if mypy is not installed. Even if tests pass, still run mypy when available -- tests check behavior while types check contracts, and passing one does not make the other redundant.
Step 4: Run test suite.
bashpytest -v --tb=short --cov=src --cov-report=term-missing
If no tests directory exists, skip and note in report. Never skip tests to make the gate pass -- tests verify functionality, and skipping them hides broken code. Only skip optional tools (mypy, bandit) if genuinely unavailable, not to manufacture a passing status.
Step 5: Security scanning with bandit (if installed).
bashbandit -r src/ -ll --format=screen
Skip and note in report if bandit is not installed. Linting passing does not mean code is correct -- linting finds style issues, not logic or security bugs. Run every available tool.
Gate: All available tools have been run. Full output captured. Proceed to analysis.
Step 1: Categorize issues by severity.
See references/tool-commands.md for complete severity classification tables.
Summary of severity levels:
Always prioritize critical issues over style fixes -- critical issues (F errors, test failures) break functionality while style issues do not. Fix critical first, high second; use auto-fix for bulk style cleanup only after critical issues are resolved.
Step 2: Count auto-fixable issues.
bashruff check . --statistics
Issues marked with [*] are auto-fixable. Show suggested auto-fix commands for these issues so users know what can be fixed automatically.
Step 3: Determine overall status.
FAIL if:
PASS otherwise. Exit with non-zero status if any critical check fails.
Gate: All issues categorized. Pass/fail determined. Proceed to report.
Format a structured markdown report. See references/report-template.md for the full template.
The report MUST include:
Report facts -- show raw command output rather than describing it. No self-congratulation ("great job", "looking good"). Generate the full report even when only style issues are found, because style issues can hide real problems in noise and a full severity-prioritized report surfaces them.
Print the complete report to stdout. Never summarize or truncate. If --output {file} flag was provided, also write report to file. Remove any intermediate temporary files at completion -- keep the final report only if the user requested file output.
Gate: Report generated and displayed. Task complete.
Auto-fix modifies files in place -- never run it without explicit user confirmation. Running ruff --fix blindly can change code semantics (import removal, reformatting), so always run check-only first, review issues, confirm auto-fix intent, then verify changes.
When user explicitly requests auto-fix:
bashruff check . --fix ruff format .
After auto-fix, show the diff so changes can be reviewed, then re-run the quality gate to verify:
bashgit diff
User says: "Run quality checks before I merge this PR" Actions:
Result: Structured report with actionable fix commands
User says: "Check code quality on the payments module" Actions:
Result: FAILED status with prioritized fix list, auto-fix commands for 5 medium issues
Cause: Ruff is not installed in the current environment Solution: Install with pip install ruff. Do not proceed without ruff -- exit with status 2.
Cause: pytest found test failures Solution: This is expected behavior, not a tool error. Parse output, include failure details in report, mark overall status as FAILED, continue with remaining checks.
Cause: Running from wrong directory or not a Python project Solution: Verify location with ls pyproject.toml src/ tests/. Run from project root.
Cause: Stale or corrupted .mypy_cache directory Solution: Clear cache with rm -rf .mypy_cache and retry. If mypy continues to fail, skip type checking and note in report.
${CLAUDE_SKILL_DIR}/references/tool-commands.md: Severity classifications, expected output formats, CLI flags${CLAUDE_SKILL_DIR}/references/report-template.md: Full structured report template${CLAUDE_SKILL_DIR}/references/pyproject-template.toml: Complete ruff, pytest, mypy, bandit configuration| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 17,615 | 8,037 | -54% | 1 | 1 | 0% | 3,090 | 2,395 | -22% | 0 | 0 | — |
case-02 | fail→fail | 20,480 | 14,558 | -29% | 1 | 1 | 0% | 2,709 | 2,324 | -14% | 0 | 0 | — |
case-03 | fail→fail | 16,063 | 6,653 | -59% | 1 | 1 | 0% | 2,778 | 2,473 | -11% | 0 | 0 | — |
case-04 | fail→pass | 11,984 | 3,856 | -68% | 1 | 1 | 0% | 1,834 | 2,730 | +49% | 0 | 0 | — |
case-05 | fail→pass | 8,285 | 4,169 | -50% | 1 | 1 | 0% | 1,368 | 2,766 | +102% | 0 | 0 | — |
case-06 | fail→pass | 9,470 | 7,607 | -20% | 1 | 1 | 0% | 1,621 | 3,390 | +109% | 0 | 0 | — |
case-07 | fail→pass | 7,652 | 2,882 | -62% | 1 | 1 | 0% | 1,213 | 2,534 | +109% | 0 | 0 | — |
case-08 | pass→pass | 11,220 | 4,043 | -64% | 1 | 1 | 0% | 1,631 | 2,789 | +71% | 0 | 0 | — |
case-09 | fail→pass | 12,322 | 5,849 | -53% | 1 | 1 | 0% | 1,862 | 3,008 | +62% | 0 | 0 | — |
case-10 | pass→pass | 6,561 | 4,150 | -37% | 1 | 1 | 0% | 1,005 | 2,733 | +172% | 0 | 0 | — |
case-11 | fail→pass | 12,553 | 7,182 | -43% | 1 | 1 | 0% | 2,047 | 3,216 | +57% | 0 | 0 | — |
case-12 | fail→pass | 8,507 | 3,497 | -59% | 1 | 1 | 0% | 1,239 | 2,575 | +108% | 0 | 0 | — |
case-13 | fail→fail | 10,723 | 4,510 | -58% | 1 | 1 | 0% | 1,771 | 2,802 | +58% | 0 | 0 | — |
case-14 | fail→pass | 10,098 | 3,432 | -66% | 1 | 1 | 0% | 1,731 | 2,512 | +45% | 0 | 0 | — |
case-15 | fail→pass | 6,403 | 2,580 | -60% | 1 | 1 | 0% | 1,004 | 2,449 | +144% | 0 | 0 | — |
case-16 | pass→pass | 5,225 | 1,876 | -64% | 1 | 1 | 0% | 754 | 2,318 | +207% | 0 | 0 | — |
case-17 | pass→pass | 10,143 | 1,556 | -85% | 1 | 1 | 0% | 1,473 | 2,270 | +54% | 0 | 0 | — |
case-18 | pass→pass | 9,564 | 3,032 | -68% | 1 | 1 | 0% | 1,596 | 2,492 | +56% | 0 | 0 | — |
case-19 | fail→pass | 12,951 | 5,962 | -54% | 1 | 1 | 0% | 2,163 | 3,025 | +40% | 0 | 0 | — |
case-20 | pass→pass | 7,041 | 8,180 | +16% | 1 | 1 | 0% | 1,302 | 3,438 | +164% | 0 | 0 | — |
case-21 | pass→fail | 7,711 | 6,314 | -18% | 1 | 1 | 0% | 1,535 | 2,483 | +62% | 0 | 0 | — |
case-22 | pass→fail | 4,164 | 5,838 | +40% | 1 | 1 | 0% | 773 | 2,298 | +197% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 17 counted toward the lift figure. The other 5 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +36 percentage points is the difference between those two pass rates over the 17 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.