This skill should be used when the user asks to "strictify a repo", "add code quality enforcement", "make this repo strict", "add prek hooks", "add type checking", "enforce code quality", "set up linting", or runs the /strictify command.
التثبيت
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
This skill should be used when the user asks to "strictify a repo", "add code quality enforcement", "make this repo strict", "add prek hooks", "add type checking", "enforce code quality", "set up linting", or runs the /strictify command.
Strictify
Overview
This skill enforces taste programmatically across 22 categories of Python code quality. It analyzes an existing repository (or bootstraps a new one), proposes strict-but-pragmatic defaults across static analysis, type safety, testing, architecture, and ongoing enforcement, then applies approved changes. Every rule exists because it improves code quality, not because a linter supports it.
The approach is inspired by the "AI Is Forcing Us to Write Good Code" thesis and OpenAI's "Harness Engineering" insight: AI agents amplify whatever quality level a codebase already has. The only guardrails are the ones that get set and enforced. The tooling, abstractions, and feedback loops that keep a codebase coherent are the primary leverage point. Agent legibility -- making code navigable by both humans and AI agents -- is a first-class goal alongside human readability.
Philosophy
Enforce taste, not arbitrary strictness -- every rule exists because it improves code quality
Fit before force -- these categories are a menu of good patterns, not a checklist to apply wholesale. Not every pattern suits every repo, so first read the target and judge whether each directive even makes sense for its size, stack, domain, and maturity; skip or soften what does not earn its place (no architectural-layer lint rules for a 200-line script, no third-party-call caching for an offline library, no per-worktree isolation for a repo with no shared services). Then bias strict on what remains.
Bias for strict, but check in -- propose aggressive defaults, let the user veto
Self-reinforcing -- hookify rules capture new taste preferences during normal work
Parse, don't validate -- coerce at the boundary, carry proof through types
Agent legibility -- make code navigable by both humans and AI agents
Detect and fill gaps -- works on both existing and new projects, merging strictness into whatever is already there
Phase 1: Analyze
Scan the target repo to understand its current state. Check all of the following:
pyproject.toml -- existence and current tool configs (ruff, mypy, pytest, coverage, vulture, deptry sections)
prek.toml -- existence and current hooks
Legacy hook config -- if .pre-commit-config.yaml or .pre-commit-config.yml exists, plan its one-way migration to native prek.toml; strictify does not retain the legacy runner or config
Package layout -- src/ layout vs flat layout; identify the package name
Python version -- from pyproject.tomlrequires-python, .python-version, or python3 --version
Documentation state -- README, docs/, inline comments quality
Phase 2: Propose
First, use the Phase 1 analysis to filter to the categories that actually fit this repo (see Fit before force), setting aside the rest with a brief reason. Then present the remaining findings grouped by the 6 category groups below. For each category, show: current state -> proposed change. Ask the user to veto any categories they do not want. Within the relevant set, default is to apply everything -- the user opts OUT, not in.
Static Analysis & Type Safety (categories 1-6)
Prek hook framework -- install prek if missing and use native prek.toml; this is a hard cut, with no fallback runner. Migrate a legacy YAML hook config instead of leaving both systems in place. Read references/prek-config.md for the full template.
Ruff -- curated anti-slop lint rules (core E/W/F/I, B, UP, C4, SIM, RUF, complexity C90, selected annotation/docstring checks, plus high-signal families for async, exceptions, logging, performance, security, pytest, pathlib, suppressions, private access, debugger/print bans, executable scripts, and import/package boundaries) and format config. Do not enable ALL, top-level preview mode, or unsafe fixes. Exact preview lint rules may be enabled only with lint-scoped preview mode and explicit-preview-rules = true. Read references/pyproject-strict.md for exact settings. Ruff owns cyclomatic-complexity enforcement through C901; do not add a second complexity tool.
mypy -- strict = true with pragmatic exceptions for the project's frameworks. Read references/pyproject-strict.md for strict mypy config and framework overrides.
Beartype -- add dependency, insert beartype_this_package() in package __init__.py. Read references/beartype-setup.md for integration patterns and common issues.
Semantic typing -- recorded as a principle in the CONVENTIONS.md design doc (Phase 3): give domain concepts (user IDs, amounts, slugs) a distinct NewType/TypeAlias instead of a bare primitive. Deciding which primitives carry domain meaning is a judgment call, so it lives in the conventions doc for the agent to apply, not a regex hook.
Parse-don't-validate -- recorded as a principle in the CONVENTIONS.md design doc (Phase 3): coerce unstructured data into constrained types (Pydantic models, frozen dataclasses, NewType) at the boundary and carry proof through types, instead of re-validating downstream. Includes the Pydantic-validator caveat -- a @field_validator that doesn't change the static type is a check-and-discard, not a parse.
Code Health (categories 7-10)
Vulture -- dead code detection with sensible ignore list. Read references/pyproject-strict.md for min_confidence and ignore settings.
Dependency and package integrity -- when the repo has reliable dependency metadata, add deptry to catch missing, unused, transitive, and misplaced development dependencies. Validate pyproject.toml when the configured schemas cover the selected tools, but never weaken valid tool configuration to appease a stale third-party schema. For a publishable Python distribution, add check-sdist; skip it for applications and non-packaged repos. For uv-managed repos, set exclude-newer = "3 days" in [tool.uv] -- a dependency cooldown that avoids supply-chain security breaches by refusing freshly published releases until the ecosystem has had time to catch and yank malicious uploads. Read references/pyproject-strict.md and references/prek-config.md for the conditional configuration.
Pyupgrade + flynt -- modernize syntax to the project's target Python version. Automates f-string conversion and syntax upgrades.
Coverage enforcement -- collect branch coverage for the actual package, enforce fail_under = 100 from [tool.coverage.report], and render missing lines as an explicit todo list. Add exclusions with exclude_also so Coverage.py's defaults remain intact. Read references/pyproject-strict.md for the full configuration.
Fast test infrastructure -- pytest-xdist parallel execution, test timeouts, --failed-first for fast feedback. Read references/pyproject-strict.md for pytest addopts config. Separately, only if the tests make real third-party/external calls (HTTP APIs, SDKs, network services -- the usual source of slow, flaky suites): propose a record-replay layer (vcrpy/pytest-recording, respx for httpx, or responses for requests) that records real responses once and replays them on later runs. A recorded response assumes the third party is a pure function of the request, so pair it with a CI job that re-runs the suite without the recordings after PR approval, to catch where that assumption breaks.
Architecture & Organization (categories 13-16)
Filesystem discipline -- file length limits (400 lines). Hookify rule warning on utils.py/helpers.py/misc.py creation. The problem is not shared code -- it is anonymous shared code. If a shared utility is needed, name it after what it does.
Architecture codemap -- create docs/ARCHITECTURE.md: a short bird's-eye map that tells a newcomer (human or agent) where things live, not how they work. Include a one-paragraph statement of the problem the codebase solves, a codemap of the coarse-grained modules/packages and how they relate, and the load-bearing architectural invariants -- including things deliberately absent (e.g. "the domain layer never imports Django"). Name important files, modules, and types explicitly so they are greppable. Keep it short and do not link to specific lines (links rot); it is a mental model, not an index. This is the primary agent-legibility artifact -- valuable for every project regardless of size. Revisit it a couple of times a year rather than syncing it to every change.
Architectural layer enforcement -- analyze the project's domain structure and propose dependency-direction rules. For a Django project: models -> services -> views -> urls. For a CLI tool: parsing -> domain -> output. For a data pipeline: extract -> transform -> load. Figure out the appropriate layers for the target project, create custom lint rules enforcing valid dependency edges, and record the layers and their invariants in the docs/ARCHITECTURE.md codemap (category 14). Scale to project size: lightweight or no lint rules for small projects (the category-14 codemap still applies), more rigid for larger ones.
Quality grades -- create docs/QUALITY.md scorecard grading each module/domain on coverage, type safety, complexity, and test health. Assess the current state, produce initial grades, and include guidance on how to maintain and update the scorecard over time.
Environment & Infrastructure (categories 17-18)
Ephemeral environment -- the goal is a single command that stands up a fresh, ready-to-work dev environment -- create a git worktree, copy local-only config (.env, credentials, editor settings), install dependencies, hand off to the agent -- fast enough (seconds, not minutes) to make concurrent agents in separate worktrees practical. Build the version that fits the target: for a uv project with no services, a thin new-feature <name> script wrapping git worktree add + uv sync + .env copy; for a heavier stack, whatever else it needs to boot. Adapt the essence (one command, ephemeral, automated) to the repo rather than shipping a fixed script.
Per-worktree isolation -- worktrees must not collide when several run at once. The rule: any state at a fixed shared location must be keyed per-worktree -- ports, database/schema names, caches on hardcoded paths (/tmp/myapp-cache, ~/.cache/myapp), and shared service instances (redis db numbers, queue names). Derive each from the worktree (an offset or hash of its name) via environment variables, or concurrent worktrees clobber each other. The exception is content-addressed global caches (~/.cache/uv, pip wheels): keyed by content hash, so sharing them is safe -- leave them alone. A project with no shared state may just need uv run to work from any worktree; a complex one should template the isolating env vars into the category-17 setup command so isolation is automatic, not manual. With containers, isolation may mean per-worktree Docker compose project names or volumes.
Ongoing Enforcement (categories 19-22)
Custom hooks -- exception handling (check_exception_handling.py), print/logging bans (check_print_statements.py), timeless comments (check_timeless_comments.py), future annotations (fix_future_annotations.py), and tests-verify-public-behaviour (check_private_test_imports.py, which forbids tests from importing leading-underscore first-party symbols so they exercise the public surface instead of internal shape). Read each script from scripts/ to understand behavior and adapt to the target repo.
Hygiene hooks -- use prek's native built-ins for trailing whitespace, end-of-file fixes, JSON/TOML/YAML validation, large files, merge conflicts, private keys, case-conflicting paths, broken/destroyed symlinks, byte-order markers, mixed line endings, and executable/shebang consistency; pair them with detect-secrets for entropy-based secret scanning. Out of scope: personal/prod strings (internal hostnames, real usernames, prod URLs). Any mechanism for these either commits the pattern (defeating the point) or requires per-user config strictify cannot bootstrap -- users who care should add a local hook that reads patterns from a gitignored file.
Doc gardening -- detect stale documentation that does not reflect actual code behavior. Set up infrastructure appropriate to the project's maturity: a prek hook, a CI job, or guidance for a recurring agent task that scans for drift and opens fix-up PRs. Pair with the keep code and docs coupled principle in the CONVENTIONS.md design doc (Phase 3): leave NOTE: back-pointers at code sites whose values are documented elsewhere, so the two don't drift.
Taste enforcer -- hookify rule that captures ongoing user preferences. When the user expresses a coding preference, determine whether it can be codified as a prek hook script, a hookify rule, or a pyproject.toml setting, then create or update the enforcement mechanism.
Phase 3: Apply
For each approved category, perform the following. Read the referenced files before writing any config.
Configuration merging
Merge into pyproject.toml -- read references/pyproject-strict.md for strict tool configurations. Merge sections: never remove existing settings, only add or tighten. Create pyproject.toml if it does not exist.
Merge into prek.toml -- read references/prek-config.md for the complete native template. Add missing repositories and hooks. Create the file if it does not exist. If a legacy YAML hook config exists, migrate its behavior into prek.toml and remove the legacy file; never leave two competing hook configs.
Scripts and assets
Copy and adapt scripts -- read each script from scripts/ (check_exception_handling.py, check_print_statements.py, check_file_length.py, check_timeless_comments.py, check_private_test_imports.py, fix_future_annotations.py). Adapt paths and package names to the target repo. Write to scripts/prek_hooks/ in the target repo. check_private_test_imports.py auto-detects first-party packages from the target's layout, so it needs no per-repo edit (pass --package only to override).
Beartype integration -- read references/beartype-setup.md. Modify the package __init__.py to insert beartype_this_package().
Hookify rules -- copy from assets/ (taste-enforcer, no-junk-drawers) to the target repo's .claude/ directory. Only these two are shipped as hooks: a prompt-keyword trigger and a filename match, both mechanical and low-false-positive. The judgment-based design principles that used to be hookify rules now live in the CONVENTIONS.md design doc (see Infrastructure setup below).
Design conventions doc -- copy assets/CONVENTIONS.md-EXAMPLE to the target as CONVENTIONS.md, then adapt it: trim principles that do not fit, sharpen examples to use the repo's real types, add repo-specific conventions. It seeds judgment-based principles too nuanced for a regex hook -- composition over inheritance, parse-don't-validate, semantic types, and code/doc coupling. Append a pointer line to the repo's CLAUDE.md/AGENTS.md (e.g. "See CONVENTIONS.md for design principles") so agents load it. This is an agent-legibility artifact alongside docs/ARCHITECTURE.md and docs/QUALITY.md.
Dev dependencies
Detect the package manager and run the appropriate install command:
Architecture codemap: create docs/ARCHITECTURE.md -- a short bird's-eye problem statement, a codemap of the coarse-grained modules and how they relate, and the load-bearing invariants (including deliberate absences). Name entities so they are greppable; do not link to specific lines.
Architectural layers: if the project warrants it, add dependency-direction lint rules and record the layers in the codemap.
Quality scorecard: create docs/QUALITY.md with initial grades per module.
Doc gardening: set up stale-docs detection appropriate to project maturity; put docs/ARCHITECTURE.md on a "revisit a couple times a year" cadence rather than gating every change on it.
Per-worktree: configure if applicable (ports, DBs, caches).
Never remove user settings -- only add or tighten. The sole format-migration
exception is a legacy YAML hook config: translate all of its behavior into
prek.toml, verify the native config, then remove the obsolete file so there is
one hook runner and one source of truth.
Present diff -- show current state -> proposed change for every modification
User veto -- the user can reject any category before application
Bias strict -- default is to apply everything; the user opts out, not in
Resources
Detailed configs, scripts, and assets live in the skill's bundled resources. Read these before writing any configuration to the target repo.
Reference Files
references/pyproject-strict.md -- strict tool configurations for ruff, mypy, pytest, coverage, vulture, and conditional deptry sections in pyproject.toml
references/prek-config.md -- complete native prek.toml template with built-in, remote, conditional integrity, and local hook definitions
references/beartype-setup.md -- beartype integration guide: beartype_this_package() snippet, BeartypeConf options, common issues, and install commands per package manager
Scripts
Custom prek hook scripts in scripts/. All scripts accept filenames as arguments, report violations as {file}:{line}: {message} -- {remediation} (agent-readable), exit nonzero on failure, and support # allow: {hook-name} per-line exemptions.
scripts/check_exception_handling.py -- detects bare except:, swallowed exceptions, exception handlers with only pass
scripts/check_print_statements.py -- bans print() in production code, detects unstructured logging patterns
scripts/check_file_length.py -- enforces max 400 logical lines per file
scripts/check_timeless_comments.py -- detects temporal keywords in comments (legacy, new, old, TODO, FIXME, HACK, temporary)
scripts/check_private_test_imports.py -- forbids tests from importing leading-underscore first-party symbols; auto-detects first-party packages, supports --package overrides and a # allow: private-test-imports carve-out
scripts/fix_future_annotations.py -- ensures from __future__ import annotations is placed correctly; runs as a fixer
Assets
Files in assets/. Copy these to the target repo.
assets/hookify.taste-enforcer.md -- hookify rule (prompt event) that captures user taste preferences and codifies them as hooks, rules, or config. Copy to .claude/.
assets/hookify.no-junk-drawers.md -- hookify rule (file event) that warns on junk-drawer module names (utils.py, helpers.py, misc.py) by filename match. Copy to .claude/.
assets/CONVENTIONS.md-EXAMPLE -- template for the CONVENTIONS.md design-conventions doc: judgment-based principles (composition over inheritance, parse-don't-validate, semantic types, code/doc coupling) that an agent reads and applies with judgment rather than a regex. Copy to the repo root as CONVENTIONS.md, adapt per repo, and reference from CLAUDE.md/AGENTS.md.
assets/agents.red-green-tdd.md -- red/green TDD agent directive. Copy to .claude/.