| name | enforce |
| description | Deterministic quality enforcement. Researches the repo, creates strict linter configs, fixes ALL violations, enforces structural limits (files <400 LOC, functions <80 LOC) across the ENTIRE codebase. Use when asked to "/lint", "/enforce", "make this repo clean", "set up linting", "enforce quality", or "add strict linting". |
Deterministic Quality Enforcement (/lint)
Autonomous quality enforcement. Researches the repo, creates or upgrades strict linter configs IN THE REPO, then iterates until every file passes linting AND structural limits. Unlike _linter.py (passive stop-time gate using --config flags), this skill permanently configures the repo so CI, hooks, and every developer enforce the same rules.
Progress Output
[1/8] Activating autonomous mode
[2/8] Researching repo stack and existing config
[3/8] Creating strict linter configurations
[4/8] Running initial lint pass — capturing baseline
[5/8] Fixing batch N/M — [category] violations
[6/8] Enforcing structural limits (ALL files)
[7/8] Re-running full lint suite — verifying zero errors
[8/8] Writing completion checkpoint
Step [5/8] repeats per batch. Steps [5-7] may loop on regressions. Always emit [1/8] and [8/8].
Architecture
RESEARCH repo → CONFIGURE linters → FIX all violations → ENFORCE structural limits → VERIFY zero errors → loop until clean
The key difference from other skills: /lint makes permanent, deterministic changes to the repo's quality infrastructure. After this skill runs, ruff check . and npm run lint enforce the same strict rules everywhere — CI, pre-commit, every developer's editor.
Autonomous Execution
THIS WORKFLOW IS 100% AUTONOMOUS. YOU MUST:
- NEVER ask for confirmation — No "Should I add this rule?"
- Auto-fix all violations — Apply fixes without asking
- Auto-commit and push — Commit after each logical batch, push after final pass
- Create/update config files — This skill WRITES linter configs INTO the repo
- Enforce structural limits on ALL files — Not just changed files. Every code file.
- Fill out checkpoint honestly — The stop hook validates your booleans
Only stop when zero violations remain.
Triggers
/lint (primary), /enforce (alias)
/lint [scope] (scoped to directory/file)
- "set up linting", "make this repo clean", "enforce quality", "configure linters"
Scope Arguments
| Invocation | Behavior |
|---|
/lint | Full repo — research, configure, fix everything |
/lint src/ | Research full repo, fix only that directory |
/lint --config-only | Research and configure — don't fix violations |
Phase 0: Activation
mkdir -p .claude && cat > .claude/autonomous-state.json << 'EOF'
{
"mode": "enforce",
"started_at": "TIMESTAMP",
"iteration": 1,
"coordinator": true
}
EOF
cp .claude/autonomous-state.json ~/.claude/autonomous-state.json
Phase 1: Research
Goal: Understand the repo's stack, existing quality infrastructure, and gap to "deterministically clean."
1.1 Stack Detection
Read filesystem markers:
| Marker | Stack |
|---|
pyproject.toml, setup.py, requirements.txt | Python |
package.json | JavaScript |
tsconfig.json | TypeScript |
1.2 Existing Config Inventory
Python: Check for [tool.ruff] in pyproject.toml, ruff.toml, .ruff.toml. Also check for legacy configs: .pylintrc, .flake8, [tool.mypy].
JS/TS: Check for eslint.config.mjs/.js (flat config), .eslintrc.* (legacy — migrate), biome.json/biome.jsonc (alternative linter). Check tsconfig.json for strict flag.
1.3 Config Gap Analysis
Compare existing rules against the strict reference:
Python strict ruleset:
F, E, W, B, UP, C4, SIM, C90, I, RUF, PERF, FURB, PIE, T20, ERA
JS/TS strict ruleset:
eslint:recommended + unicorn/recommended + max-lines:400 + max-lines-per-function:80
typescript-eslint/recommended (if TS)
The delta between what the repo has and what it should have is the work.
1.4 Full File Census
Scan ALL code files for structural violations. Use Bash to count lines:
find . -name "*.py" -not -path "*/node_modules/*" -not -path "*/.venv/*" -not -path "*/__pycache__/*" -not -path "*/migrations/*" -not -path "*/.git/*" -exec wc -l {} + | sort -rn | head -20
find . \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/.next/*" -not -path "*/.git/*" -exec wc -l {} + | sort -rn | head -20
Flag: files >400 LOC (MUST split), functions >80 LOC (MUST extract).
Phase 2: Configure
Goal: Create or upgrade linter configs IN THE REPO.
2.1 Python Configuration
Create or upgrade [tool.ruff] in pyproject.toml:
[tool.ruff]
line-length = 100
target-version = "py39"
[tool.ruff.lint]
select = [
"F",
"E",
"W",
"B",
"UP",
"C4",
"SIM",
"C90",
"I",
"RUF",
"PERF",
"FURB",
"PIE",
"T20",
"ERA",
]
ignore = [
"E501",
]
fixable = ["ALL"]
[tool.ruff.lint.mccabe]
max-complexity = 10
[tool.ruff.lint.per-file-ignores]
= [, ]
= [, ]
= []
= []
If legacy linter configs exist (pylintrc, flake8): Migrate rules to ruff, remove old configs.
If ruff config already exists: Upgrade by adding missing rule groups. Never remove existing rules.
2.2 JavaScript/TypeScript Configuration
Create eslint.config.mjs (flat config):
import js from "@eslint/js";
let unicornConfig = {};
try {
const unicorn = await import("eslint-plugin-unicorn");
unicornConfig = unicorn.default.configs["recommended"];
} catch {}
export default [
js.configs.recommended,
...(unicornConfig ? [unicornConfig] : []),
{
rules: {
"max-lines": ["error", { max: 400, skipBlankLines: true, skipComments: true }],
"max-lines-per-function": ["error", { max: 80, skipBlankLines: true, skipComments: true, IIFEs: true }],
"no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"no-var": "error",
"prefer-const": "error",
"no-console": "warn",
"eqeqeq": ["error", "always"],
...(unicornConfig ? {
"unicorn/no-null": "off",
: ,
: ,
: ,
: ,
: ,
: ,
} : {}),
},
},
{
: [
, , , , ,
, , , ,
, , ,
],
},
];
If the project uses TypeScript, also add typescript-eslint:
import tseslint from "typescript-eslint";
Install missing devDependencies (use the project's package manager):
npm install -D eslint @eslint/js eslint-plugin-unicorn
Add lint scripts to package.json if missing:
{ "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix" } }
2.3 TypeScript Strict Mode
If tsconfig.json exists, ensure strict options:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"forceConsistentCasingInFileNames": true
}
}
If enabling strict produces >100 type errors: Enable incrementally — add // @ts-expect-error TODOs for existing violations and track as a separate fix batch.
2.4 Commit Configuration
git add pyproject.toml eslint.config.mjs tsconfig.json package.json
git commit -m "enforce: configure strict linting rules"
Phase 3: Baseline Capture
Run all configured linters, capture full error output, count violations by category:
ruff check . 2>&1
npx eslint . 2>&1
npx tsc --noEmit 2>&1
Store baseline counts in .claude/autonomous-state.json for progress tracking.
Phase 4: Iterative Fix Loop
Fix Order (mandatory — minimizes regressions)
- Auto-fixable linter errors (cheapest, highest volume)
- Manual linter errors (require code understanding)
- Type errors (may cascade — fix in dependency order)
- Structural: oversized files (>400 LOC — split into modules)
- Structural: oversized functions (>80 LOC — extract helpers)
- Dead code removal (unused imports, commented-out code, unreachable branches)
Batch 1: Auto-Fix Pass
ruff check --fix . && ruff format .
npx eslint . --fix
Commit: enforce: auto-fix linter violations
Batch 2: Manual Linter Fixes
Fix remaining violations via Edit tool. Group by file. Run linters after each group to catch cascades.
Commit: enforce: fix manual linter violations
Batch 3: Type Error Fixes
Fix tsc --noEmit errors. Replace any with proper types. Add missing return types. Fix null safety.
Commit: enforce: fix type errors
Batch 4: Split Oversized Files
For each file >400 LOC:
- Analyze responsibilities — identify distinct concerns
- Plan the split — each new module <300 lines (leave headroom)
- Extract modules — move related functions/classes
- Update imports — fix all import paths across codebase
- Verify — linters + tests pass
| File Type | Split Strategy |
|---|
| Python module with multiple classes | One class per file |
| Python module with utility functions | Group by concern |
| React component file | Separate components, hooks, utils |
| Route handler file | One route group per file |
| Config/settings file | Skip (allowlisted) |
| Test file | Skip (relaxed limits) |
Commit: enforce: split [filename] into focused modules
Batch 5: Extract Oversized Functions
For each function >80 LOC:
| Pattern | Extraction |
|---|
| Sequential steps | Extract each step as a named function |
| Nested conditionals | Extract branches as predicate + handler |
| Loop body >20 lines | Extract loop body |
| Data transformation pipeline | Extract each transform step |
Commit: enforce: extract oversized functions
Batch 6: Dead Code Removal
ruff check --select F401,F841,ERA . --fix
Commit: enforce: remove dead code
Parallel Batch Execution
If batches touch disjoint file sets, launch as parallel Task() agents:
Task(subagent_type="general-purpose", description="Split oversized files",
prompt="Split these files to <400 LOC: [list]. Run linters after. Commit.")
Task(subagent_type="general-purpose", description="Extract oversized functions",
prompt="Extract functions >80 LOC in: [list]. Run linters after. Commit.")
If overlapping: fix serially.
Phase 5: Full Verification
5.1 Linter Verification (Zero Tolerance)
ruff check .
npx eslint .
npx tsc --noEmit
If any fails → return to Phase 4.
5.2 Structural Verification (ALL Files)
Scan EVERY code file (not just changed files). Apply exclusions from _linter.py:
Excluded directories: node_modules, .venv, dist, build, .next, pycache, migrations, .git, .claude
Excluded files: *.min.js, *.d.ts, .generated., lock files, conftest.py, settings.py, *.config.js/ts
Relaxed limits for test directories (tests/, test/, tests/, spec/, e2e/): 600 LOC files, 120 LOC functions.
| Check | Limit | Applies To |
|---|
| File length | 400 lines | All code files (.py, .js, .jsx, .ts, .tsx) |
| Function length | 80 lines | All functions/methods |
| Cyclomatic complexity | 10 | Via ruff C90 / ESLint complexity rule |
5.3 Test Suite
pytest 2>/dev/null || python -m pytest 2>/dev/null
npm test 2>/dev/null
If tests fail from our changes → fix the regression. If pre-existing → document in checkpoint.
5.4 Regression Loop
If Phase 5 finds new violations → return to Phase 4. Max 3 regression loops, then document remaining issues.
Phase 6: Completion
Completion Checkpoint
{
"self_report": {
"is_job_complete": true,
"code_changes_made": true,
"linters_pass": true,
"category": "quality"
},
"reflection": {
"what_was_done": "Configured ruff + ESLint strict rules, fixed N violations across M files, enforced <400 LOC / <80 LOC on all code files",
"what_remains": "none",
"key_insight": "Reusable lesson about this repo's quality patterns (>50 chars)",
"search_terms": ["lint", "ruff", "eslint", "quality", "enforcement"]
},
"verification"
Exit Conditions
| Condition | Result |
|---|
All linters pass, all structural checks pass, what_remains: "none" | SUCCESS |
| Any linter errors remain | BLOCKED — continue fixing |
| Any structural violations remain | BLOCKED — continue fixing |
| Tests failing from our changes | BLOCKED — fix regressions |
Cleanup
rm -f ~/.claude/autonomous-state.json .claude/autonomous-state.json
Structural Limits Reference
| Metric | Limit | Relaxed For |
|---|
| File length | 400 lines | Test dirs (600), config files (exempt) |
| Function length | 80 lines | Test functions (120) |
| Cyclomatic complexity | 10 | — |
Code files: .py, .js, .jsx, .ts, .tsx, .mjs, .cjs
Exempt: Config files, generated files, lock files, minified files.
Quality Hierarchy
/lint (enforce) = Install the guardrails (one-time setup + full sweep)
_linter.py = Check the guardrails at stop-time (passive gate, every session)
/melt step 4 = Pass through the guardrails during a build (gate within task)
/burndown = Fix what guardrails don't catch (architecture, slop, semantics)
/lint makes the repo self-enforcing. After it runs, _linter.py uses the project's own strict configs instead of toolkit fallbacks. Every future /melt session, CI run, and developer inherits the strict rules. The quality investment compounds.
Comparison
| Aspect | _linter.py | /burndown | /lint (enforce) |
|---|
| When | Stop hook (passive) | On-demand (debt) | On-demand (quality infra) |
| Config | --config flags (ephemeral) | Uses _linter.py | Writes configs INTO repo |
| Structural | Changed files only | Changed files only | ALL files in repo |
| Output | Pass/fail gate | Code fixes | Config + fixes (permanent) |
| Post-run | Same as before | Code improved | Repo self-enforcing |
Skill Fluidity
Use techniques from any skill inline. Need to understand a complex file before splitting? Use /heavy patterns. Discover a bug while fixing types? Debug inline. Your autonomous state and checkpoint remain governed by /enforce.