From 8910413315ecc27cdd08e99a3d557f5ee24bd1a7 Mon Sep 17 00:00:00 2001
From: m3tm3re
Date: Tue, 17 Feb 2026 19:05:45 +0100
Subject: [PATCH] feat(rules): add initial rule files for concerns, languages,
and frameworks
Concerns (6 files):
- coding-style.md (163 lines): patterns, anti-patterns, error handling, SOLID
- naming.md (105 lines): naming conventions table per language
- documentation.md (149 lines): docstrings, WHY vs WHAT, README standards
- testing.md (134 lines): AAA pattern, mocking philosophy, TDD
- git-workflow.md (118 lines): conventional commits, branch naming, PR format
- project-structure.md (82 lines): directory layout, entry points, config placement
Languages (4 files):
- python.md (224 lines): uv, ruff, pyright, pytest, pydantic, idioms, anti-patterns
- typescript.md (150 lines): strict mode, discriminated unions, satisfies, as const
- nix.md (129 lines): flake structure, module patterns, alejandra, anti-patterns
- shell.md (100 lines): set -euo pipefail, shellcheck, quoting, POSIX
Frameworks (1 file):
- n8n.md (42 lines): workflow design, node patterns, Error Trigger, security
Context budget: 975 lines (concerns + python) < 1500 limit
Refs: T6-T16 of rules-system plan
---
.sisyphus/plans/rules-system.md | 22 +--
rules/concerns/coding-style.md | 163 ++++++++++++++++++++
rules/concerns/documentation.md | 149 ++++++++++++++++++
rules/concerns/git-workflow.md | 118 +++++++++++++++
rules/concerns/naming.md | 105 +++++++++++++
rules/concerns/project-structure.md | 82 ++++++++++
rules/concerns/testing.md | 134 +++++++++++++++++
rules/frameworks/n8n.md | 42 ++++++
rules/languages/nix.md | 129 ++++++++++++++++
rules/languages/python.md | 224 ++++++++++++++++++++++++++++
rules/languages/shell.md | 100 +++++++++++++
rules/languages/typescript.md | 150 +++++++++++++++++++
12 files changed, 1407 insertions(+), 11 deletions(-)
create mode 100644 rules/concerns/coding-style.md
create mode 100644 rules/concerns/documentation.md
create mode 100644 rules/concerns/git-workflow.md
create mode 100644 rules/concerns/naming.md
create mode 100644 rules/concerns/project-structure.md
create mode 100644 rules/concerns/testing.md
create mode 100644 rules/frameworks/n8n.md
create mode 100644 rules/languages/nix.md
create mode 100644 rules/languages/python.md
create mode 100644 rules/languages/shell.md
create mode 100644 rules/languages/typescript.md
diff --git a/.sisyphus/plans/rules-system.md b/.sisyphus/plans/rules-system.md
index 0ba6274..e50f71e 100644
--- a/.sisyphus/plans/rules-system.md
+++ b/.sisyphus/plans/rules-system.md
@@ -495,7 +495,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 6. Create `rules/concerns/coding-style.md`
+- [x] 6. Create `rules/concerns/coding-style.md`
**What to do**:
- Write coding style rules: code formatting, patterns/anti-patterns, error handling, type safety, function design, DRY/SOLID
@@ -526,7 +526,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 7. Create `rules/concerns/naming.md`
+- [x] 7. Create `rules/concerns/naming.md`
**What to do**:
- Naming conventions: files, variables, functions, classes, modules, constants
@@ -549,7 +549,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 8. Create `rules/concerns/documentation.md`
+- [x] 8. Create `rules/concerns/documentation.md`
**What to do**: When to document, docstring formats, inline comment philosophy (WHY not WHAT), README standards. Under 150 lines.
**Recommended Agent Profile**: `writing`
@@ -560,7 +560,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 9. Create `rules/concerns/testing.md`
+- [x] 9. Create `rules/concerns/testing.md`
**What to do**: Arrange-act-assert, behavior vs implementation testing, mocking philosophy, coverage, TDD. Under 200 lines.
**Recommended Agent Profile**: `writing`
@@ -571,7 +571,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 10. Create `rules/concerns/git-workflow.md`
+- [x] 10. Create `rules/concerns/git-workflow.md`
**What to do**: Conventional commits, branch naming, PR descriptions, squash vs merge. Under 120 lines.
**Recommended Agent Profile**: `writing`, Skills: [`git-master`]
@@ -582,7 +582,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 11. Create `rules/concerns/project-structure.md`
+- [x] 11. Create `rules/concerns/project-structure.md`
**What to do**: Directory layout, module organization, entry points, config placement. Per-type: Python (src layout), TS (src/), Nix (modules/). Under 120 lines.
**Recommended Agent Profile**: `writing`
@@ -593,7 +593,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 12. Create `rules/languages/python.md`
+- [x] 12. Create `rules/languages/python.md`
**What to do**:
- Deep Python patterns: `uv` (pkg mgmt), `ruff` (lint/fmt), `pyright` (types), `pytest` + `hypothesis`, Pydantic for data boundaries
@@ -616,7 +616,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 13. Create `rules/languages/typescript.md`
+- [x] 13. Create `rules/languages/typescript.md`
**What to do**:
- Strict mode (`strict: true`, `noUncheckedIndexedAccess`), discriminated unions, branded types, `satisfies`, `as const`
@@ -634,7 +634,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 14. Create `rules/languages/nix.md`
+- [x] 14. Create `rules/languages/nix.md`
**What to do**:
- Flake structure, module patterns (`{ config, lib, pkgs, ... }:`), `mkIf`/`mkMerge`
@@ -655,7 +655,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 15. Create `rules/languages/shell.md`
+- [x] 15. Create `rules/languages/shell.md`
**What to do**: `set -euo pipefail`, shellcheck, quoting, local vars, POSIX portability, `#!/usr/bin/env bash`. Under 120 lines.
**Recommended Agent Profile**: `writing`
@@ -666,7 +666,7 @@ Max Concurrent: 11 (Wave 2)
---
-- [ ] 16. Create `rules/frameworks/n8n.md`
+- [x] 16. Create `rules/frameworks/n8n.md`
**What to do**: Workflow design, node patterns, naming, Error Trigger, data patterns, security. Under 120 lines.
**Recommended Agent Profile**: `writing`
diff --git a/rules/concerns/coding-style.md b/rules/concerns/coding-style.md
new file mode 100644
index 0000000..c9fc4cc
--- /dev/null
+++ b/rules/concerns/coding-style.md
@@ -0,0 +1,163 @@
+# Coding Style
+
+## Critical Rules (MUST follow)
+
+Always prioritize readability over cleverness. Never write code that requires mental gymnastics to understand.
+Always fail fast and explicitly. Never silently swallow errors or hide exceptions.
+Always keep functions under 20 lines. Never create monolithic functions that do multiple things.
+Always validate inputs at function boundaries. Never trust external data implicitly.
+
+## Formatting
+
+Prefer consistent indentation throughout the codebase. Never mix tabs and spaces.
+Prefer meaningful variable names over short abbreviations. Never use single letters except for loop counters.
+
+### Correct:
+```lang
+const maxRetryAttempts = 3;
+const connectionTimeout = 5000;
+
+for (let attempt = 1; attempt <= maxRetryAttempts; attempt++) {
+ // process attempt
+}
+```
+
+### Incorrect:
+```lang
+const m = 3;
+const t = 5000;
+
+for (let i = 1; i <= m; i++) {
+ // process attempt
+}
+```
+
+## Patterns and Anti-Patterns
+
+Never repeat yourself. Always extract duplicated logic into reusable functions.
+Prefer composition over inheritance. Never create deep inheritance hierarchies.
+Always use guard clauses to reduce nesting. Never write arrow-shaped code.
+
+### Correct:
+```lang
+def process_user(user):
+ if not user:
+ return None
+ if not user.is_active:
+ return None
+ return user.calculate_score()
+```
+
+### Incorrect:
+```lang
+def process_user(user):
+ if user:
+ if user.is_active:
+ return user.calculate_score()
+ else:
+ return None
+ else:
+ return None
+```
+
+## Error Handling
+
+Always handle specific exceptions. Never use broad catch-all exception handlers.
+Always log error context, not just the error message. Never let errors vanish without trace.
+
+### Correct:
+```lang
+try:
+ data = fetch_resource(url)
+ return parse_data(data)
+except NetworkError as e:
+ log_error(f"Network failed for {url}: {e}")
+ raise
+except ParseError as e:
+ log_error(f"Parse failed for {url}: {e}")
+ return fallback_data
+```
+
+### Incorrect:
+```lang
+try:
+ data = fetch_resource(url)
+ return parse_data(data)
+except Exception:
+ pass
+```
+
+## Type Safety
+
+Always use type annotations where supported. Never rely on implicit type coercion.
+Prefer explicit type checks over duck typing for public APIs. Never assume type behavior.
+
+### Correct:
+```lang
+function calculateTotal(price: number, quantity: number): number {
+ return price * quantity;
+}
+```
+
+### Incorrect:
+```lang
+function calculateTotal(price, quantity) {
+ return price * quantity;
+}
+```
+
+## Function Design
+
+Always write pure functions when possible. Never mutate arguments unless required.
+Always limit function parameters to 3 or fewer. Never pass objects to hide parameter complexity.
+
+### Correct:
+```lang
+def create_user(name: str, email: str) -> User:
+ return User(name=name, email=email, created_at=now())
+```
+
+### Incorrect:
+```lang
+def create_user(config: dict) -> User:
+ return User(
+ name=config['name'],
+ email=config['email'],
+ created_at=config['timestamp']
+ )
+```
+
+## SOLID Principles
+
+Never let classes depend on concrete implementations. Always depend on abstractions.
+Always ensure classes are open for extension but closed for modification. Never change working code to add features.
+Prefer many small interfaces over one large interface. Never force clients to depend on methods they don't use.
+
+### Correct:
+```lang
+class EmailSender {
+ send(message: Message): void {
+ // implementation
+ }
+}
+
+class NotificationService {
+ constructor(private sender: EmailSender) {}
+}
+```
+
+### Incorrect:
+```lang
+class NotificationService {
+ sendEmail(message: Message): void { }
+ sendSMS(message: Message): void { }
+ sendPush(message: Message): void { }
+}
+```
+
+## Critical Rules (REPEAT)
+
+Always write self-documenting code. Never rely on comments to explain complex logic.
+Always refactor when you see code smells. Never let technical debt accumulate.
+Always test edge cases explicitly. Never assume happy path only behavior.
+Never commit commented-out code. Always remove it or restore it.
diff --git a/rules/concerns/documentation.md b/rules/concerns/documentation.md
new file mode 100644
index 0000000..c076c74
--- /dev/null
+++ b/rules/concerns/documentation.md
@@ -0,0 +1,149 @@
+# Documentation Rules
+
+## When to Document
+
+**Document public APIs**. Every public function, class, method, and module needs documentation. Users need to know how to use your code.
+**Document complex logic**. Algorithms, state machines, and non-obvious implementations need explanations. Future readers will thank you.
+**Document business rules**. Encode domain knowledge directly in comments. Don't make anyone reverse-engineer requirements from code.
+**Document trade-offs**. When you choose between alternatives, explain why. Help future maintainers understand the decision context.
+**Do NOT document obvious code**. Comments like `// get user` add noise. Delete them.
+
+## Docstring Formats
+
+### Python (Google Style)
+
+```python
+def calculate_price(quantity: int, unit_price: float, discount: float = 0.0) -> float:
+ """Calculate total price after discount.
+ Args:
+ quantity: Number of items ordered.
+ unit_price: Price per item in USD.
+ discount: Decimal discount rate (0.0 to 1.0).
+ Returns:
+ Final price in USD.
+ Raises:
+ ValueError: If quantity is negative.
+ """
+```
+
+### JavaScript/TypeScript (JSDoc)
+
+```javascript
+/**
+ * Validates user input against security rules.
+ * @param {string} input - Raw user input from form.
+ * @param {Object} rules - Validation constraints.
+ * @param {number} rules.maxLength - Maximum allowed length.
+ * @returns {boolean} True if input passes all rules.
+ * @throws {ValidationError} If input violates security constraints.
+ */
+function validateInput(input, rules) {
+```
+
+### Bash
+
+```bash
+#!/usr/bin/env bash
+# Deploy application to production environment.
+#
+# Usage: ./deploy.sh [environment]
+#
+# Args:
+# environment: Target environment (staging|production). Default: staging.
+#
+# Exits:
+# 0 on success, 1 on deployment failure.
+```
+
+## Inline Comments: WHY Not WHAT
+
+**Incorrect:**
+```python
+# Iterate through all users
+for user in users:
+ # Check if user is active
+ if user.active:
+ # Increment counter
+ count += 1
+```
+
+**Correct:**
+```python
+# Count only active users to calculate monthly revenue
+for user in users:
+ if user.active:
+ count += 1
+```
+
+**Incorrect:**
+```javascript
+// Set timeout to 5000
+setTimeout(() => {
+ // Show error message
+ alert('Error');
+}, 5000);
+```
+
+**Correct:**
+```javascript
+// 5000ms delay prevents duplicate alerts during rapid retries
+setTimeout(() => {
+ alert('Error');
+}, 5000);
+```
+
+**Incorrect:**
+```bash
+# Remove temporary files
+rm -rf /tmp/app/*
+```
+
+**Correct:**
+```bash
+# Clear temp directory before batch import to prevent partial state
+rm -rf /tmp/app/*
+```
+
+**Rule:** Describe the intent and context. Never describe what the code obviously does.
+
+## README Standards
+
+Every project needs a README at the top level.
+
+**Required sections:**
+1. **What it does** - One sentence summary
+2. **Installation** - Setup commands
+3. **Usage** - Basic example
+4. **Configuration** - Environment variables and settings
+5. **Contributing** - How to contribute
+
+**Example structure:**
+
+```markdown
+# Project Name
+
+One-line description of what this project does.
+
+## Installation
+```bash
+npm install
+```
+
+## Usage
+```bash
+npm start
+```
+
+## Configuration
+
+Create `.env` file:
+```
+API_KEY=your_key_here
+```
+
+## Contributing
+
+See [CONTRIBUTING.md](./CONTRIBUTING.md).
+```
+
+**Keep READMEs focused**. Link to separate docs for complex topics. Don't make the README a tutorial.
diff --git a/rules/concerns/git-workflow.md b/rules/concerns/git-workflow.md
new file mode 100644
index 0000000..e05025e
--- /dev/null
+++ b/rules/concerns/git-workflow.md
@@ -0,0 +1,118 @@
+# Git Workflow Rules
+
+## Conventional Commits
+
+Format: `(): `
+
+### Commit Types
+
+- **feat**: New feature
+ - `feat(auth): add OAuth2 login flow`
+ - `feat(api): expose user endpoints`
+
+- **fix**: Bug fix
+ - `fix(payment): resolve timeout on Stripe calls`
+ - `fix(ui): button not clickable on mobile`
+
+- **refactor**: Code refactoring (no behavior change)
+ - `refactor(utils): extract date helpers`
+ - `refactor(api): simplify error handling`
+
+- **docs**: Documentation only
+ - `docs(readme): update installation steps`
+ - `docs(api): add endpoint examples`
+
+- **chore**: Maintenance tasks
+ - `chore(deps): update Node to 20`
+ - `chore(ci): add GitHub actions workflow`
+
+- **test**: Tests only
+ - `test(auth): add unit tests for login`
+ - `test(e2e): add checkout flow tests`
+
+- **style**: Formatting, no logic change
+ - `style: sort imports alphabetically`
+
+### Commit Rules
+
+- Subject max 72 chars
+- Imperative mood ("add", not "added")
+- No period at end
+- Reference issues: `Closes #123`
+
+## Branch Naming
+
+Pattern: `/`
+
+### Branch Types
+
+- `feature/add-user-dashboard`
+- `feature/enable-dark-mode`
+- `fix/login-redirect-loop`
+- `fix/payment-timeout-error`
+- `refactor/extract-user-service`
+- `refactor/simplify-auth-flow`
+- `hotfix/security-vulnerability`
+
+### Branch Rules
+
+- Lowercase and hyphens
+- Max 50 chars
+- Delete after merge
+
+## Pull Requests
+
+### PR Title
+
+Follow Conventional Commit format:
+- `feat: add user dashboard`
+- `fix: resolve login redirect loop`
+
+### PR Description
+
+```markdown
+## What
+Brief description
+
+## Why
+Reason for change
+
+## How
+Implementation approach
+
+## Testing
+Steps performed
+
+## Checklist
+- [ ] Tests pass
+- [ ] Code reviewed
+- [ ] Docs updated
+```
+
+## Merge Strategy
+
+### Squash Merge
+
+- Many small commits
+- One cohesive feature
+- Clean history
+
+### Merge Commit
+
+- Preserve commit history
+- Distinct milestones
+- Detailed history preferred
+
+### When to Rebase
+
+- Before opening PR
+- Resolving conflicts
+- Keeping current with main
+
+## General Rules
+
+- Pull latest from main before starting
+- Write atomic commits
+- Run tests before pushing
+- Request peer review before merge
+- Never force push to main/master
diff --git a/rules/concerns/naming.md b/rules/concerns/naming.md
new file mode 100644
index 0000000..d9c8ba8
--- /dev/null
+++ b/rules/concerns/naming.md
@@ -0,0 +1,105 @@
+# Naming Conventions
+
+Use consistent naming across all code. Follow language-specific conventions.
+
+## Language Reference
+
+| Type | Python | TypeScript | Nix | Shell |
+|------|--------|------------|-----|-------|
+| Variables | snake_case | camelCase | camelCase | UPPER_SNAKE |
+| Functions | snake_case | camelCase | camelCase | lower_case |
+| Classes | PascalCase | PascalCase | - | - |
+| Constants | UPPER_SNAKE | UPPER_SNAKE | camelCase | UPPER_SNAKE |
+| Files | snake_case | camelCase | hyphen-case | hyphen-case |
+| Modules | snake_case | camelCase | - | - |
+
+## General Rules
+
+**Files**: Use hyphen-case for documentation, snake_case for Python, camelCase for TypeScript. Names should describe content.
+
+**Variables**: Use descriptive names. Avoid single letters except loop counters. No Hungarian notation.
+
+**Functions**: Use verb-noun pattern. Name describes what it does, not how it does it.
+
+**Classes**: Use PascalCase with descriptive nouns. Avoid abbreviations.
+
+**Constants**: Use UPPER_SNAKE with descriptive names. Group related constants.
+
+## Examples
+
+Python:
+```python
+# Variables
+user_name = "alice"
+is_authenticated = True
+
+# Functions
+def get_user_data(user_id):
+ pass
+
+# Classes
+class UserProfile:
+ pass
+
+# Constants
+MAX_RETRIES = 3
+API_ENDPOINT = "https://api.example.com"
+```
+
+TypeScript:
+```typescript
+// Variables
+const userName = "alice";
+const isAuthenticated = true;
+
+// Functions
+function getUserData(userId: string): User {
+ return null;
+}
+
+// Classes
+class UserProfile {
+ private name: string;
+}
+
+// Constants
+const MAX_RETRIES = 3;
+const API_ENDPOINT = "https://api.example.com";
+```
+
+Nix:
+```nix
+# Variables
+let
+ userName = "alice";
+ isAuthenticated = true;
+in
+# ...
+```
+
+Shell:
+```bash
+# Variables
+USER_NAME="alice"
+IS_AUTHENTICATED=true
+
+# Functions
+get_user_data() {
+ echo "Getting data"
+}
+
+# Constants
+MAX_RETRIES=3
+API_ENDPOINT="https://api.example.com"
+```
+
+## File Naming
+
+Use these patterns consistently. No exceptions.
+
+- Skills: `hyphen-case`
+- Python: `snake_case.py`
+- TypeScript: `camelCase.ts` or `hyphen-case.ts`
+- Nix: `hyphen-case.nix`
+- Shell: `hyphen-case.sh`
+- Markdown: `UPPERCASE.md` or `sentence-case.md`
diff --git a/rules/concerns/project-structure.md b/rules/concerns/project-structure.md
new file mode 100644
index 0000000..19c0349
--- /dev/null
+++ b/rules/concerns/project-structure.md
@@ -0,0 +1,82 @@
+# Project Structure
+
+## Python
+
+Use src layout for all projects. Place application code in `src//`, tests in `tests/`.
+
+```
+project/
+├── src/myproject/
+│ ├── __init__.py
+│ ├── main.py # Entry point
+│ └── core/
+│ └── module.py
+├── tests/
+│ ├── __init__.py
+│ └── test_module.py
+├── pyproject.toml # Config
+├── README.md
+└── .gitignore
+```
+
+**Rules:**
+- One module per directory file
+- `__init__.py` in every package
+- Entry point in `src/myproject/main.py`
+- Config in root: `pyproject.toml`, `requirements.txt`
+
+## TypeScript
+
+Use `src/` for source, `dist/` for build output.
+
+```
+project/
+├── src/
+│ ├── index.ts # Entry point
+│ ├── core/
+│ │ └── module.ts
+│ └── types.ts
+├── tests/
+│ └── module.test.ts
+├── package.json # Config
+├── tsconfig.json
+└── README.md
+```
+
+**Rules:**
+- One module per file
+- Index exports from `src/index.ts`
+- Entry point in `src/index.ts`
+- Config in root: `package.json`, `tsconfig.json`
+
+## Nix
+
+Use `modules/` for NixOS modules, `pkgs/` for packages.
+
+```
+nix-config/
+├── modules/
+│ ├── default.nix # Module list
+│ └── my-service.nix
+├── pkgs/
+│ └── my-package/
+│ └── default.nix
+├── flake.nix # Entry point
+├── flake.lock
+└── README.md
+```
+
+**Rules:**
+- One module per file in `modules/`
+- One package per directory in `pkgs/`
+- Entry point in `flake.nix`
+- Config in root: `flake.nix`, shell.nix
+
+## General
+
+- Use hyphen-case for directories
+- Use kebab-case for file names
+- Config files in project root
+- Tests separate from source
+- Docs in root: README.md, CHANGELOG.md
+- Hidden configs: .env, .gitignore
diff --git a/rules/concerns/testing.md b/rules/concerns/testing.md
new file mode 100644
index 0000000..6ecc705
--- /dev/null
+++ b/rules/concerns/testing.md
@@ -0,0 +1,134 @@
+# Testing Rules
+
+## Arrange-Act-Assert Pattern
+
+Structure every test in three distinct phases:
+
+```python
+# Arrange: Set up the test data and conditions
+user = User(name="Alice", role="admin")
+session = create_test_session(user.id)
+
+# Act: Execute the behavior under test
+result = grant_permission(session, "read_documents")
+
+# Assert: Verify the expected outcome
+assert result.granted is True
+assert result.permissions == ["read_documents"]
+```
+
+Never mix phases. Comment each phase clearly for complex setups. Keep Act phase to one line if possible.
+
+## Behavior vs Implementation Testing
+
+Test behavior, not implementation details:
+
+```python
+# GOOD: Tests the observable behavior
+def test_user_can_login():
+ response = login("alice@example.com", "password123")
+ assert response.status_code == 200
+ assert "session_token" in response.cookies
+
+# BAD: Tests internal implementation
+def test_login_sets_database_flag():
+ login("alice@example.com", "password123")
+ user = User.get(email="alice@example.com")
+ assert user._logged_in_flag is True # Private field
+```
+
+Focus on inputs and outputs. Test public contracts. Refactor internals freely without breaking tests.
+
+## Mocking Philosophy
+
+Mock external dependencies, not internal code:
+
+```python
+# GOOD: Mock external services
+@patch("requests.post")
+def test_sends_notification_to_slack(mock_post):
+ send_notification("Build complete!")
+ mock_post.assert_called_once_with(
+ "https://slack.com/api/chat.postMessage",
+ json={"text": "Build complete!"}
+ )
+
+# BAD: Mock internal methods
+@patch("NotificationService._format_message")
+def test_notification_formatting(mock_format):
+ # Don't mock private methods
+ send_notification("Build complete!")
+```
+
+Mock when:
+- Dependency is slow (database, network, file system)
+- Dependency is unreliable (external APIs)
+- Dependency is expensive (third-party services)
+
+Don't mock when:
+- Testing the dependency itself
+- The dependency is fast and stable
+- The mock becomes more complex than real implementation
+
+## Coverage Expectations
+
+Write tests for:
+- Critical business logic (aim for 90%+)
+- Edge cases and error paths (aim for 80%+)
+- Public APIs and contracts (aim for 100%)
+
+Don't obsess over:
+- Trivial getters/setters
+- Generated code
+- One-line wrappers
+
+Coverage is a floor, not a ceiling. A test suite at 100% coverage that doesn't verify behavior is worthless.
+
+## Test-Driven Development
+
+Follow the red-green-refactor cycle:
+1. Red: Write failing test for new behavior
+2. Green: Write minimum code to pass
+3. Refactor: improve code while tests stay green
+
+Write tests first for new features. Write tests after for bug fixes. Never refactor without tests.
+
+## Test Organization
+
+Group tests by feature or behavior, not by file structure. Name tests to describe the scenario:
+
+```python
+class TestUserAuthentication:
+ def test_valid_credentials_succeeds(self):
+ pass
+
+ def test_invalid_credentials_fails(self):
+ pass
+
+ def test_locked_account_fails(self):
+ pass
+```
+
+Each test should stand alone. Avoid shared state between tests. Use fixtures or setup methods to reduce duplication.
+
+## Test Data
+
+Use realistic test data that reflects production scenarios:
+
+```python
+# GOOD: Realistic values
+user = User(
+ email="alice@example.com",
+ name="Alice Smith",
+ age=28
+)
+
+# BAD: Placeholder values
+user = User(
+ email="test@test.com",
+ name="Test User",
+ age=999
+)
+```
+
+Avoid magic strings and numbers. Use named constants for expected values that change often.
diff --git a/rules/frameworks/n8n.md b/rules/frameworks/n8n.md
new file mode 100644
index 0000000..07682f9
--- /dev/null
+++ b/rules/frameworks/n8n.md
@@ -0,0 +1,42 @@
+# n8n Workflow Automation Rules
+
+## Workflow Design
+- Start with a clear trigger: Webhook, Schedule, or Event source
+- Keep workflows under 20 nodes for maintainability
+- Group related logic with sub-workflows
+- Use the "Switch" node for conditional branching
+- Add "Wait" nodes between rate-limited API calls
+
+## Node Naming
+- Use verb-based names: `Fetch Users`, `Transform Data`, `Send Email`
+- Prefix data nodes: `Get_`, `Set_`, `Update_`
+- Prefix conditionals: `Check_`, `If_`, `When_`
+- Prefix actions: `Send_`, `Create_`, `Delete_`
+- Add version suffix to API nodes: `API_v1_Users`
+
+## Error Handling
+- Always add an Error Trigger node
+- Route errors to a "Notify Failure" branch
+- Log error details: `$json.error.message`, `$json.node.name`
+- Send alerts on critical failures
+- Add "Continue On Fail" for non-essential nodes
+
+## Data Flow
+- Use "Set" nodes to normalize output structure
+- Reference previous nodes: `{{ $json.field }}`
+- Use "Merge" node to combine multiple data sources
+- Apply "Code" node for complex transformations
+- Clean data before sending to external APIs
+
+## Credential Security
+- Store all secrets in n8n credentials manager
+- Never hardcode API keys or tokens
+- Use environment-specific credential sets
+- Rotate credentials regularly
+- Limit credential scope to minimum required permissions
+
+## Testing
+- Test each node independently with "Execute Node"
+- Verify data structure at each step
+- Mock external dependencies during development
+- Log workflow execution for debugging
diff --git a/rules/languages/nix.md b/rules/languages/nix.md
new file mode 100644
index 0000000..f4b28d7
--- /dev/null
+++ b/rules/languages/nix.md
@@ -0,0 +1,129 @@
+# Nix Code Conventions
+
+## Formatting
+
+- Use `alejandra` for formatting
+- camelCase for variables, `PascalCase` for types
+- 2 space indentation (alejandra default)
+- No trailing whitespace
+
+## Flake Structure
+
+```nix
+{
+ description = "Description here";
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+ flake-utils.url = "github:numtide/flake-utils";
+ };
+ outputs = { self, nixpkgs, flake-utils, ... }:
+ flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in {
+ packages.default = pkgs.hello;
+ devShells.default = pkgs.mkShell {
+ buildInputs = [ pkgs.hello ];
+ };
+ }
+ );
+}
+```
+
+## Module Patterns
+
+Standard module function signature:
+
+```nix
+{ config, lib, pkgs, ... }:
+{
+ options.myService.enable = lib.mkEnableOption "my service";
+ config = lib.mkIf config.myService.enable {
+ services.myService.enable = true;
+ };
+}
+```
+
+## Conditionals and Merging
+
+- Use `mkIf` for conditional config
+- Use `mkMerge` to combine multiple config sets
+- Use `mkOptionDefault` for defaults that can be overridden
+
+```nix
+config = lib.mkMerge [
+ (lib.mkIf cfg.enable { ... })
+ (lib.mkIf cfg.extraConfig { ... })
+];
+```
+
+## Anti-Patterns (AVOID)
+
+### `with pkgs;`
+Bad: Pollutes namespace, hard to trace origins
+```nix
+{ pkgs, ... }:
+{
+ packages = with pkgs; [ vim git ];
+}
+```
+
+Good: Explicit references
+```nix
+{ pkgs, ... }:
+{
+ packages = [ pkgs.vim pkgs.git ];
+}
+```
+
+### `builtins.fetchTarball`
+Use flake inputs instead. `fetchTarball` is non-reproducible.
+
+### Impure operations
+Avoid `import ` in flakes. Always use inputs.
+
+### `builtins.getAttr` / `builtins.hasAttr`
+Use `lib.attrByPath` or `lib.optionalAttrs` instead.
+
+## Home Manager Patterns
+
+```nix
+{ config, pkgs, lib, ... }:
+{
+ home.packages = with pkgs; [ ripgrep fd ];
+ programs.zsh.enable = true;
+ xdg.configFile."myapp/config".text = "...";
+}
+```
+
+## Overlays
+
+```nix
+{ config, lib, pkgs, ... }:
+let
+ myOverlay = final: prev: {
+ myPackage = prev.myPackage.overrideAttrs (old: { ... });
+ };
+in
+{
+ nixpkgs.overlays = [ myOverlay ];
+}
+```
+
+## Imports and References
+
+- Use flake inputs for dependencies
+- `lib` is always available in modules
+- Reference packages via `pkgs.packageName`
+- Use `callPackage` for complex package definitions
+
+## File Organization
+
+```
+flake.nix # Entry point
+modules/ # NixOS modules
+ services/
+ my-service.nix
+overlays/ # Package overrides
+ default.nix
+```
diff --git a/rules/languages/python.md b/rules/languages/python.md
new file mode 100644
index 0000000..523a57b
--- /dev/null
+++ b/rules/languages/python.md
@@ -0,0 +1,224 @@
+# Python Language Rules
+
+## Toolchain
+
+### Package Management (uv)
+```bash
+uv init my-project --package
+uv add numpy pandas
+uv add --dev pytest ruff pyright hypothesis
+uv run python -m pytest
+uv lock --upgrade-package numpy
+```
+
+### Linting & Formatting (ruff)
+```toml
+[tool.ruff]
+line-length = 100
+target-version = "py311"
+
+[tool.ruff.lint]
+select = ["E", "F", "W", "I", "N", "UP"]
+ignore = ["E501"]
+
+[tool.ruff.format]
+quote-style = "double"
+```
+
+### Type Checking (pyright)
+```toml
+[tool.pyright]
+typeCheckingMode = "strict"
+reportMissingTypeStubs = true
+reportUnknownMemberType = true
+```
+
+### Testing (pytest + hypothesis)
+```python
+import pytest
+from hypothesis import given, strategies as st
+
+@given(st.integers(), st.integers())
+def test_addition_commutative(a, b):
+ assert a + b == b + a
+
+@pytest.fixture
+def user_data():
+ return {"name": "Alice", "age": 30}
+
+def test_user_creation(user_data):
+ user = User(**user_data)
+ assert user.name == "Alice"
+```
+
+### Data Validation (Pydantic)
+```python
+from pydantic import BaseModel, Field, validator
+
+class User(BaseModel):
+ name: str = Field(min_length=1, max_length=100)
+ age: int = Field(ge=0, le=150)
+ email: str
+
+ @validator('email')
+ def email_must_contain_at(cls, v):
+ if '@' not in v:
+ raise ValueError('must contain @')
+ return v
+```
+
+## Idioms
+
+### Comprehensions
+```python
+# List comprehension
+squares = [x**2 for x in range(10) if x % 2 == 0]
+
+# Dict comprehension
+word_counts = {word: text.count(word) for word in unique_words}
+
+# Set comprehension
+unique_chars = {char for char in text if char.isalpha()}
+```
+
+### Context Managers
+```python
+# Built-in context managers
+with open('file.txt', 'r') as f:
+ content = f.read()
+
+# Custom context manager
+from contextlib import contextmanager
+
+@contextmanager
+def timer():
+ start = time.time()
+ yield
+ print(f"Elapsed: {time.time() - start:.2f}s")
+```
+
+### Generators
+```python
+def fibonacci():
+ a, b = 0, 1
+ while True:
+ yield a
+ a, b = b, a + b
+
+def read_lines(file_path):
+ with open(file_path) as f:
+ for line in f:
+ yield line.strip()
+```
+
+### F-strings
+```python
+name = "Alice"
+age = 30
+
+# Basic interpolation
+msg = f"Name: {name}, Age: {age}"
+
+# Expression evaluation
+msg = f"Next year: {age + 1}"
+
+# Format specs
+msg = f"Price: ${price:.2f}"
+msg = f"Hex: {0xFF:X}"
+```
+
+## Anti-Patterns
+
+### Bare Except
+```python
+# AVOID: Catches all exceptions including SystemExit
+try:
+ risky_operation()
+except:
+ pass
+
+# USE: Catch specific exceptions
+try:
+ risky_operation()
+except ValueError as e:
+ log_error(e)
+except KeyError as e:
+ log_error(e)
+```
+
+### Mutable Defaults
+```python
+# AVOID: Default argument created once
+def append_item(item, items=[]):
+ items.append(item)
+ return items
+
+# USE: None as sentinel
+def append_item(item, items=None):
+ if items is None:
+ items = []
+ items.append(item)
+ return items
+```
+
+### Global State
+```python
+# AVOID: Global mutable state
+counter = 0
+
+def increment():
+ global counter
+ counter += 1
+
+# USE: Class-based state
+class Counter:
+ def __init__(self):
+ self.count = 0
+
+ def increment(self):
+ self.count += 1
+```
+
+### Star Imports
+```python
+# AVOID: Pollutes namespace, unclear origins
+from module import *
+
+# USE: Explicit imports
+from module import specific_function, MyClass
+import module as m
+```
+
+## Project Setup
+
+### pyproject.toml Structure
+```toml
+[project]
+name = "my-project"
+version = "0.1.0"
+requires-python = ">=3.11"
+dependencies = [
+ "pydantic>=2.0",
+ "httpx>=0.25",
+]
+
+[project.optional-dependencies]
+dev = ["pytest", "ruff", "pyright", "hypothesis"]
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+```
+
+### src Layout
+```
+my-project/
+├── pyproject.toml
+└── src/
+ └── my_project/
+ ├── __init__.py
+ ├── main.py
+ └── utils/
+ ├── __init__.py
+ └── helpers.py
+```
diff --git a/rules/languages/shell.md b/rules/languages/shell.md
new file mode 100644
index 0000000..64f21e3
--- /dev/null
+++ b/rules/languages/shell.md
@@ -0,0 +1,100 @@
+# Shell Scripting Rules
+
+## Shebang
+
+Always use `#!/usr/bin/env bash` for portability. Never hardcode `/bin/bash`.
+
+```bash
+#!/usr/bin/env bash
+```
+
+## Strict Mode
+
+Enable strict mode in every script.
+
+```bash
+#!/usr/bin/env bash
+set -euo pipefail
+```
+
+- `-e`: Exit on error
+- `-u`: Error on unset variables
+- `-o pipefail`: Return exit status of last failed pipe command
+
+## Shellcheck
+
+Run shellcheck on all scripts before committing.
+
+```bash
+shellcheck script.sh
+```
+
+## Quoting
+
+Quote all variable expansions and command substitutions. Use arrays instead of word-splitting strings.
+
+```bash
+# Good
+"${var}"
+files=("file1.txt" "file2.txt")
+for f in "${files[@]}"; do
+ process "$f"
+done
+
+# Bad
+$var
+files="file1.txt file2.txt"
+for f in $files; do
+ process $f
+done
+```
+
+## Functions
+
+Define with parentheses, use `local` for variables.
+
+```bash
+my_function() {
+ local result
+ result=$(some_command)
+ echo "$result"
+}
+```
+
+## Command Substitution
+
+Use `$()` not backticks. Nests cleanly.
+
+```bash
+# Good
+output=$(ls "$dir")
+
+# Bad
+output=`ls $dir`
+```
+
+## POSIX Portability
+
+Write POSIX-compliant scripts when targeting `/bin/sh`.
+
+- Use `[[` only for bash scripts
+- Use `printf` instead of `echo -e`
+- Avoid `[[`, `((`, `&>` in sh scripts
+
+## Error Handling
+
+Use `trap` for cleanup.
+
+```bash
+cleanup() {
+ rm -f /tmp/lockfile
+}
+trap cleanup EXIT
+```
+
+## Readability
+
+- Use 2-space indentation
+- Limit lines to 80 characters
+- Add comments for non-obvious logic
+- Separate sections with blank lines
diff --git a/rules/languages/typescript.md b/rules/languages/typescript.md
new file mode 100644
index 0000000..ee7fdd7
--- /dev/null
+++ b/rules/languages/typescript.md
@@ -0,0 +1,150 @@
+# TypeScript Patterns
+
+## Strict tsconfig
+
+Always enable strict mode and key safety options:
+
+```json
+{
+ "compilerOptions": {
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true
+ }
+}
+```
+
+## Discriminated Unions
+
+Use discriminated unions for exhaustive type safety:
+
+```ts
+type Result =
+ | { success: true; data: string }
+ | { success: false; error: Error };
+
+function handleResult(result: Result): string {
+ if (result.success) {
+ return result.data;
+ }
+ throw result.error;
+}
+```
+
+## Branded Types
+
+Prevent type confusion with nominal branding:
+
+```ts
+type UserId = string & { readonly __brand: unique symbol };
+type Email = string & { readonly __brand: unique symbol };
+
+function createUserId(id: string): UserId {
+ return id as UserId;
+}
+
+function sendEmail(email: Email, userId: UserId) {}
+```
+
+## satisfies Operator
+
+Use `satisfies` for type-safe object literal inference:
+
+```ts
+const config = {
+ port: 3000,
+ host: "localhost",
+} satisfies {
+ port: number;
+ host: string;
+ debug?: boolean;
+};
+
+config.port; // number
+config.host; // string
+```
+
+## as const Assertions
+
+Freeze literal types with `as const`:
+
+```ts
+const routes = {
+ home: "/",
+ about: "/about",
+ contact: "/contact",
+} as const;
+
+type Route = typeof routes[keyof typeof routes];
+```
+
+## Modern Features
+
+```ts
+// Promise.withResolvers()
+const { promise, resolve, reject } = Promise.withResolvers();
+
+// Object.groupBy()
+const users = [
+ { name: "Alice", role: "admin" },
+ { name: "Bob", role: "user" },
+];
+const grouped = Object.groupBy(users, u => u.role);
+
+// using statement for disposables
+class Resource implements Disposable {
+ async [Symbol.asyncDispose]() {
+ await this.cleanup();
+ }
+}
+async function withResource() {
+ using r = new Resource();
+}
+```
+
+## Toolchain
+
+Prefer modern tooling:
+- Runtime: `bun` or `tsx` (no `tsc` for execution)
+- Linting: `biome` (preferred) or `eslint`
+- Formatting: `biome` (built-in) or `prettier`
+
+## Anti-Patterns
+
+Avoid these TypeScript patterns:
+
+```ts
+// NEVER use as any
+const data = response as any;
+
+// NEVER use @ts-ignore
+// @ts-ignore
+const value = unknownFunction();
+
+// NEVER use ! assertion (non-null)
+const element = document.querySelector("#foo")!;
+
+// NEVER use enum (prefer union)
+enum Status { Active, Inactive } // ❌
+
+// Prefer const object or union
+type Status = "Active" | "Inactive"; // ✅
+const Status = { Active: "Active", Inactive: "Inactive" } as const; // ✅
+```
+
+## Indexed Access Safety
+
+With `noUncheckedIndexedAccess`, handle undefined:
+
+```ts
+const arr: string[] = ["a", "b"];
+const item = arr[0]; // string | undefined
+
+const item2 = arr.at(0); // string | undefined
+
+const map = new Map();
+const value = map.get("key"); // number | undefined
+```