writing-ratchet-tests
Write ratchet tests to prevent accumulation of code anti-patterns. Use when asked to create a "ratchet test" for tracking and preventing specific code patterns (e.g., TODO comments, inline imports, broad exception handling).
Best use case
writing-ratchet-tests is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Write ratchet tests to prevent accumulation of code anti-patterns. Use when asked to create a "ratchet test" for tracking and preventing specific code patterns (e.g., TODO comments, inline imports, broad exception handling).
Teams using writing-ratchet-tests should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/writing-ratchet-tests/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How writing-ratchet-tests Compares
| Feature / Agent | writing-ratchet-tests | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Write ratchet tests to prevent accumulation of code anti-patterns. Use when asked to create a "ratchet test" for tracking and preventing specific code patterns (e.g., TODO comments, inline imports, broad exception handling).
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
SKILL.md Source
# Writing Ratchet Tests
This skill provides guidelines for writing ratchet tests that prevent accumulation of code anti-patterns in a project.
## What are Ratchet Tests?
Ratchet tests are a testing pattern that:
- Track the current count of a specific anti-pattern in the codebase
- Prevent that count from increasing (using inline-snapshot)
- Allow the count to decrease (improvement is always allowed)
- Provide clear, actionable feedback when violations increase
Common use cases:
- TODO comments
- Inline imports
- Use of eval() or exec()
- Broad exception handling (bare except, except Exception)
- Any other code pattern you want to gradually eliminate
## Architecture
The ratchet system has three layers:
1. **Rule definitions** in `libs/imbue_common/imbue/imbue_common/ratchet_testing/common_ratchets.py` -- `RegexRatchetRule` and `RatchetRuleInfo` objects
2. **Wrapper functions** in `libs/imbue_common/imbue/imbue_common/ratchet_testing/standard_ratchet_checks.py` -- one function per ratchet rule
3. **Test functions** in each project's `test_ratchets.py` -- call the wrapper with a snapshot count
All projects must have the same set of test functions (enforced by `test_meta_ratchets.py`).
## Adding a New Common Ratchet
### Step 1: Define the Rule
Add a `RegexRatchetRule` to `common_ratchets.py`:
```python
PREVENT_MY_PATTERN = RegexRatchetRule(
rule_name="my pattern usages",
rule_description="Explain why this pattern is problematic and what to do instead",
pattern_string=r"my_regex_pattern",
is_multiline=False, # set True if using ^ or $ anchors
)
```
Or a `RatchetRuleInfo` for AST-based checks (add the detection function to `ratchets.py`).
### Step 2: Add a Wrapper Function
Add to `standard_ratchet_checks.py` (the single source of truth for which ratchets exist):
```python
def check_my_pattern(source_dir: Path, max_count: int) -> None:
assert_ratchet(PREVENT_MY_PATTERN, source_dir, max_count)
```
Remember to import the rule at the top of the file. The function name determines the test name: `check_foo` becomes `test_prevent_foo`.
### Step 3: Sync and Set Counts
```bash
uv run python scripts/sync_common_ratchets.py
uv run pytest --inline-snapshot=update -k test_ratchets
```
The sync script reads `standard_ratchet_checks.py`, generates `test_prevent_my_pattern` in all 26 `test_ratchets.py` files with `snapshot(0)`, then the pytest command sets the actual violation counts per project.
## Adding a Project-Specific Ratchet
Not all ratchets belong in every project. If a ratchet only applies to one project (e.g., a project-specific API convention), add it to a separate file -- NOT to `test_ratchets.py` (which must define the same test function names across all projects, enforced by `test_meta_ratchets.py`).
Create a project-specific ratchet test file (e.g., `test_project_ratchets.py`) using the core API directly:
```python
from pathlib import Path
from inline_snapshot import snapshot
from imbue.imbue_common.ratchet_testing.core import FileExtension
from imbue.imbue_common.ratchet_testing.core import RegexPattern
from imbue.imbue_common.ratchet_testing.core import check_regex_ratchet
from imbue.imbue_common.ratchet_testing.core import format_ratchet_failure_message
_DIR = Path(__file__).parent.parent.parent
def test_prevent_my_project_pattern() -> None:
pattern = RegexPattern(r"my_regex_pattern", multiline=False)
chunks = check_regex_ratchet(_DIR, FileExtension(".py"), pattern)
assert len(chunks) <= snapshot(), format_ratchet_failure_message(
rule_name="my project pattern",
rule_description="Why this is problematic and what to do instead",
chunks=chunks,
)
```
Run with `--inline-snapshot=create` to set the initial count, then verify it passes normally.
## Important Rules
- **Never add per-project code quality ratchets to `test_meta_ratchets.py`**. Meta ratchets are for repo-wide structural checks only.
- If a ratchet applies to all projects, use the common ratchet workflow (sync script). If it only applies to one project, use a separate test file with the core API.
- Keep test function names descriptive: `test_prevent_<anti_pattern>`
- Provide clear `rule_description` that explains WHY the pattern is bad and WHAT to do instead
- Never blindly update snapshots -- investigate why a count increased
## Troubleshooting
**Pattern not matching expected violations:**
- Check if you need `multiline=True` for patterns using `^` or `$`
- Verify the regex is correct using a regex tester
- Check that the file extension is correct
- Ensure violations are in git-tracked files (git blame only works on committed code)
**Test fails after running:**
- This is expected if the current count is higher than the snapshot
- Always fix the violations if a ratchet test fails--it's because you messed something up. NEVER run with `--inline-snapshot=fix`
- Never just blindly update snapshots - investigate why the count increased
**Snapshot shows 0 but violations exist:**
- The regex pattern might be incorrect
- Try running with `multiline=True` if using `^` or `$` anchorsRelated Skills
writing-specs
Write high quality specifications or design docs for a program. Use any time you are asked to write, improve, or update specs / design docs (e.g., files in a `specs/` folder).
writing-docs
Write high quality, user-facing documentation. Use any time you need to write, improve, or update a significant amount of user-facing documentation (e.g., files in a "docs/" folder or README file).
sync-tutorial-to-e2e-tests
Match tutorial script blocks to e2e pytest functions and add missing tests
wait-for-agent
Wait for another agent to enter WAITING state, then execute follow-up instructions
update-issues-in-repo
Convert a file containing identified issues into a tracked file in current_tasks/. Use after running identify-* commands to create a local record of current issues.
triage-backlog
Interactively triage the user's local engineering backlog file into GitHub issues. Use when the user wants to process their raw thought notes / ticket backlog into proper GitHub issues.
think-of-something-to-fix
Come up with good ideas about what to fix. Use when you have to fix something, but you're not sure what.
minds-dev-iterate
Set up and iterate on the minds app stack (desktop client, workspace server, mngr, forever-claude-template) with a running Docker agent
message-agent
Send a message to another mngr agent. Use when you need to communicate with a peer agent.
identify-style-issues
Identify divergences from the style guide in the $1 library
identify-outdated-docstrings
Identify outdated docstrings in the $1 library
identify-inconsistencies
Identify inconsistencies in the $1 library