wip: [01-stabilize] paused at task 1/1 - OCR Hallucination Immune logic via Semantic delta window and fret-isolation

This commit is contained in:
2026-03-29 22:08:40 +09:00
parent aca7bf592a
commit 2507de45d3
4289 changed files with 732689 additions and 28672 deletions

View File

@@ -0,0 +1,106 @@
# Rules
## Structure
Rules are organized into a **common** layer plus **language-specific** directories:
```
rules/
├── common/ # Language-agnostic principles (always install)
│ ├── coding-style.md
│ ├── git-workflow.md
│ ├── testing.md
│ ├── performance.md
│ ├── patterns.md
│ ├── hooks.md
│ ├── agents.md
│ └── security.md
├── typescript/ # TypeScript/JavaScript specific
├── python/ # Python specific
├── golang/ # Go specific
├── swift/ # Swift specific
└── php/ # PHP specific
```
- **common/** contains universal principles — no language-specific code examples.
- **Language directories** extend the common rules with framework-specific patterns, tools, and code examples. Each file references its common counterpart.
## Installation
### Option 1: Install Script (Recommended)
```bash
# Install common + one or more language-specific rule sets
./install.sh typescript
./install.sh python
./install.sh golang
./install.sh swift
./install.sh php
# Install multiple languages at once
./install.sh typescript python
```
### Option 2: Manual Installation
> **Important:** Copy entire directories — do NOT flatten with `/*`.
> Common and language-specific directories contain files with the same names.
> Flattening them into one directory causes language-specific files to overwrite
> common rules, and breaks the relative `../common/` references used by
> language-specific files.
```bash
# Install common rules (required for all projects)
cp -r rules/common ~/.claude/rules/common
# Install language-specific rules based on your project's tech stack
cp -r rules/typescript ~/.claude/rules/typescript
cp -r rules/python ~/.claude/rules/python
cp -r rules/golang ~/.claude/rules/golang
cp -r rules/swift ~/.claude/rules/swift
cp -r rules/php ~/.claude/rules/php
# Attention ! ! ! Configure according to your actual project requirements; the configuration here is for reference only.
```
## Rules vs Skills
- **Rules** define standards, conventions, and checklists that apply broadly (e.g., "80% test coverage", "no hardcoded secrets").
- **Skills** (`skills/` directory) provide deep, actionable reference material for specific tasks (e.g., `python-patterns`, `golang-testing`).
Language-specific rule files reference relevant skills where appropriate. Rules tell you *what* to do; skills tell you *how* to do it.
## Adding a New Language
To add support for a new language (e.g., `rust/`):
1. Create a `rules/rust/` directory
2. Add files that extend the common rules:
- `coding-style.md` — formatting tools, idioms, error handling patterns
- `testing.md` — test framework, coverage tools, test organization
- `patterns.md` — language-specific design patterns
- `hooks.md` — PostToolUse hooks for formatters, linters, type checkers
- `security.md` — secret management, security scanning tools
3. Each file should start with:
```
> This file extends [common/xxx.md](../common/xxx.md) with <Language> specific content.
```
4. Reference existing skills if available, or create new ones under `skills/`.
## Rule Priority
When language-specific rules and common rules conflict, **language-specific rules take precedence** (specific overrides general). This follows the standard layered configuration pattern (similar to CSS specificity or `.gitignore` precedence).
- `rules/common/` defines universal defaults applicable to all projects.
- `rules/golang/`, `rules/python/`, `rules/swift/`, `rules/php/`, `rules/typescript/`, etc. override those defaults where language idioms differ.
### Example
`common/coding-style.md` recommends immutability as a default principle. A language-specific `golang/coding-style.md` can override this:
> Idiomatic Go uses pointer receivers for struct mutation — see [common/coding-style.md](../common/coding-style.md) for the general principle, but Go-idiomatic mutation is preferred here.
### Common rules with override notes
Rules in `rules/common/` that may be overridden by language-specific files are marked with:
> **Language note**: This rule may be overridden by language-specific rules for languages where this pattern is not idiomatic.

View File

@@ -0,0 +1,50 @@
# Agent Orchestration
## Available Agents
Located in `~/.claude/agents/`:
| Agent | Purpose | When to Use |
|-------|---------|-------------|
| planner | Implementation planning | Complex features, refactoring |
| architect | System design | Architectural decisions |
| tdd-guide | Test-driven development | New features, bug fixes |
| code-reviewer | Code review | After writing code |
| security-reviewer | Security analysis | Before commits |
| build-error-resolver | Fix build errors | When build fails |
| e2e-runner | E2E testing | Critical user flows |
| refactor-cleaner | Dead code cleanup | Code maintenance |
| doc-updater | Documentation | Updating docs |
| rust-reviewer | Rust code review | Rust projects |
## Immediate Agent Usage
No user prompt needed:
1. Complex feature requests - Use **planner** agent
2. Code just written/modified - Use **code-reviewer** agent
3. Bug fix or new feature - Use **tdd-guide** agent
4. Architectural decision - Use **architect** agent
## Parallel Task Execution
ALWAYS use parallel Task execution for independent operations:
```markdown
# GOOD: Parallel execution
Launch 3 agents in parallel:
1. Agent 1: Security analysis of auth module
2. Agent 2: Performance review of cache system
3. Agent 3: Type checking of utilities
# BAD: Sequential when unnecessary
First agent 1, then agent 2, then agent 3
```
## Multi-Perspective Analysis
For complex problems, use split role sub-agents:
- Factual reviewer
- Senior engineer
- Security expert
- Consistency reviewer
- Redundancy checker

View File

@@ -0,0 +1,48 @@
# Coding Style
## Immutability (CRITICAL)
ALWAYS create new objects, NEVER mutate existing ones:
```
// Pseudocode
WRONG: modify(original, field, value) → changes original in-place
CORRECT: update(original, field, value) → returns new copy with change
```
Rationale: Immutable data prevents hidden side effects, makes debugging easier, and enables safe concurrency.
## File Organization
MANY SMALL FILES > FEW LARGE FILES:
- High cohesion, low coupling
- 200-400 lines typical, 800 max
- Extract utilities from large modules
- Organize by feature/domain, not by type
## Error Handling
ALWAYS handle errors comprehensively:
- Handle errors explicitly at every level
- Provide user-friendly error messages in UI-facing code
- Log detailed error context on the server side
- Never silently swallow errors
## Input Validation
ALWAYS validate at system boundaries:
- Validate all user input before processing
- Use schema-based validation where available
- Fail fast with clear error messages
- Never trust external data (API responses, user input, file content)
## Code Quality Checklist
Before marking work complete:
- [ ] Code is readable and well-named
- [ ] Functions are small (<50 lines)
- [ ] Files are focused (<800 lines)
- [ ] No deep nesting (>4 levels)
- [ ] Proper error handling
- [ ] No hardcoded values (use constants or config)
- [ ] No mutation (immutable patterns used)

View File

@@ -0,0 +1,38 @@
# Development Workflow
> This file extends [common/git-workflow.md](./git-workflow.md) with the full feature development process that happens before git operations.
The Feature Implementation Workflow describes the development pipeline: research, planning, TDD, code review, and then committing to git.
## Feature Implementation Workflow
0. **Research & Reuse** _(mandatory before any new implementation)_
- **GitHub code search first:** Run `gh search repos` and `gh search code` to find existing implementations, templates, and patterns before writing anything new.
- **Library docs second:** Use Context7 or primary vendor docs to confirm API behavior, package usage, and version-specific details before implementing.
- **Exa only when the first two are insufficient:** Use Exa for broader web research or discovery after GitHub search and primary docs.
- **Check package registries:** Search npm, PyPI, crates.io, and other registries before writing utility code. Prefer battle-tested libraries over hand-rolled solutions.
- **Search for adaptable implementations:** Look for open-source projects that solve 80%+ of the problem and can be forked, ported, or wrapped.
- Prefer adopting or porting a proven approach over writing net-new code when it meets the requirement.
1. **Plan First**
- Use **planner** agent to create implementation plan
- Generate planning docs before coding: PRD, architecture, system_design, tech_doc, task_list
- Identify dependencies and risks
- Break down into phases
2. **TDD Approach**
- Use **tdd-guide** agent
- Write tests first (RED)
- Implement to pass tests (GREEN)
- Refactor (IMPROVE)
- Verify 80%+ coverage
3. **Code Review**
- Use **code-reviewer** agent immediately after writing code
- Address CRITICAL and HIGH issues
- Fix MEDIUM issues when possible
4. **Commit & Push**
- Detailed commit messages
- Follow conventional commits format
- See [git-workflow.md](./git-workflow.md) for commit message format and PR process

View File

@@ -0,0 +1,24 @@
# Git Workflow
## Commit Message Format
```
<type>: <description>
<optional body>
```
Types: feat, fix, refactor, docs, test, chore, perf, ci
Note: Attribution disabled globally via ~/.claude/settings.json.
## Pull Request Workflow
When creating PRs:
1. Analyze full commit history (not just latest commit)
2. Use `git diff [base-branch]...HEAD` to see all changes
3. Draft comprehensive PR summary
4. Include test plan with TODOs
5. Push with `-u` flag if new branch
> For the full development process (planning, TDD, code review) before git operations,
> see [development-workflow.md](./development-workflow.md).

View File

@@ -0,0 +1,30 @@
# Hooks System
## Hook Types
- **PreToolUse**: Before tool execution (validation, parameter modification)
- **PostToolUse**: After tool execution (auto-format, checks)
- **Stop**: When session ends (final verification)
## Auto-Accept Permissions
Use with caution:
- Enable for trusted, well-defined plans
- Disable for exploratory work
- Never use dangerously-skip-permissions flag
- Configure `allowedTools` in `~/.claude.json` instead
## TodoWrite Best Practices
Use TodoWrite tool to:
- Track progress on multi-step tasks
- Verify understanding of instructions
- Enable real-time steering
- Show granular implementation steps
Todo list reveals:
- Out of order steps
- Missing items
- Extra unnecessary items
- Wrong granularity
- Misinterpreted requirements

View File

@@ -0,0 +1,31 @@
# Common Patterns
## Skeleton Projects
When implementing new functionality:
1. Search for battle-tested skeleton projects
2. Use parallel agents to evaluate options:
- Security assessment
- Extensibility analysis
- Relevance scoring
- Implementation planning
3. Clone best match as foundation
4. Iterate within proven structure
## Design Patterns
### Repository Pattern
Encapsulate data access behind a consistent interface:
- Define standard operations: findAll, findById, create, update, delete
- Concrete implementations handle storage details (database, API, file, etc.)
- Business logic depends on the abstract interface, not the storage mechanism
- Enables easy swapping of data sources and simplifies testing with mocks
### API Response Format
Use a consistent envelope for all API responses:
- Include a success/status indicator
- Include the data payload (nullable on error)
- Include an error message field (nullable on success)
- Include metadata for paginated responses (total, page, limit)

View File

@@ -0,0 +1,55 @@
# Performance Optimization
## Model Selection Strategy
**Haiku 4.5** (90% of Sonnet capability, 3x cost savings):
- Lightweight agents with frequent invocation
- Pair programming and code generation
- Worker agents in multi-agent systems
**Sonnet 4.6** (Best coding model):
- Main development work
- Orchestrating multi-agent workflows
- Complex coding tasks
**Opus 4.5** (Deepest reasoning):
- Complex architectural decisions
- Maximum reasoning requirements
- Research and analysis tasks
## Context Window Management
Avoid last 20% of context window for:
- Large-scale refactoring
- Feature implementation spanning multiple files
- Debugging complex interactions
Lower context sensitivity tasks:
- Single-file edits
- Independent utility creation
- Documentation updates
- Simple bug fixes
## Extended Thinking + Plan Mode
Extended thinking is enabled by default, reserving up to 31,999 tokens for internal reasoning.
Control extended thinking via:
- **Toggle**: Option+T (macOS) / Alt+T (Windows/Linux)
- **Config**: Set `alwaysThinkingEnabled` in `~/.claude/settings.json`
- **Budget cap**: `export MAX_THINKING_TOKENS=10000`
- **Verbose mode**: Ctrl+O to see thinking output
For complex tasks requiring deep reasoning:
1. Ensure extended thinking is enabled (on by default)
2. Enable **Plan Mode** for structured approach
3. Use multiple critique rounds for thorough analysis
4. Use split role sub-agents for diverse perspectives
## Build Troubleshooting
If build fails:
1. Use **build-error-resolver** agent
2. Analyze error messages
3. Fix incrementally
4. Verify after each fix

View File

@@ -0,0 +1,29 @@
# Security Guidelines
## Mandatory Security Checks
Before ANY commit:
- [ ] No hardcoded secrets (API keys, passwords, tokens)
- [ ] All user inputs validated
- [ ] SQL injection prevention (parameterized queries)
- [ ] XSS prevention (sanitized HTML)
- [ ] CSRF protection enabled
- [ ] Authentication/authorization verified
- [ ] Rate limiting on all endpoints
- [ ] Error messages don't leak sensitive data
## Secret Management
- NEVER hardcode secrets in source code
- ALWAYS use environment variables or a secret manager
- Validate that required secrets are present at startup
- Rotate any secrets that may have been exposed
## Security Response Protocol
If security issue found:
1. STOP immediately
2. Use **security-reviewer** agent
3. Fix CRITICAL issues before continuing
4. Rotate any exposed secrets
5. Review entire codebase for similar issues

View File

@@ -0,0 +1,29 @@
# Testing Requirements
## Minimum Test Coverage: 80%
Test Types (ALL required):
1. **Unit Tests** - Individual functions, utilities, components
2. **Integration Tests** - API endpoints, database operations
3. **E2E Tests** - Critical user flows (framework chosen per language)
## Test-Driven Development
MANDATORY workflow:
1. Write test first (RED)
2. Run test - it should FAIL
3. Write minimal implementation (GREEN)
4. Run test - it should PASS
5. Refactor (IMPROVE)
6. Verify coverage (80%+)
## Troubleshooting Test Failures
1. Use **tdd-guide** agent
2. Check test isolation
3. Verify mocks are correct
4. Fix implementation, not tests (unless tests are wrong)
## Agent Support
- **tdd-guide** - Use PROACTIVELY for new features, enforces write-tests-first

View File

@@ -0,0 +1,44 @@
---
paths:
- "**/*.cpp"
- "**/*.hpp"
- "**/*.cc"
- "**/*.hh"
- "**/*.cxx"
- "**/*.h"
- "**/CMakeLists.txt"
---
# C++ Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with C++ specific content.
## Modern C++ (C++17/20/23)
- Prefer **modern C++ features** over C-style constructs
- Use `auto` when the type is obvious from context
- Use `constexpr` for compile-time constants
- Use structured bindings: `auto [key, value] = map_entry;`
## Resource Management
- **RAII everywhere** — no manual `new`/`delete`
- Use `std::unique_ptr` for exclusive ownership
- Use `std::shared_ptr` only when shared ownership is truly needed
- Use `std::make_unique` / `std::make_shared` over raw `new`
## Naming Conventions
- Types/Classes: `PascalCase`
- Functions/Methods: `snake_case` or `camelCase` (follow project convention)
- Constants: `kPascalCase` or `UPPER_SNAKE_CASE`
- Namespaces: `lowercase`
- Member variables: `snake_case_` (trailing underscore) or `m_` prefix
## Formatting
- Use **clang-format** — no style debates
- Run `clang-format -i <file>` before committing
## Reference
See skill: `cpp-coding-standards` for comprehensive C++ coding standards and guidelines.

View File

@@ -0,0 +1,39 @@
---
paths:
- "**/*.cpp"
- "**/*.hpp"
- "**/*.cc"
- "**/*.hh"
- "**/*.cxx"
- "**/*.h"
- "**/CMakeLists.txt"
---
# C++ Hooks
> This file extends [common/hooks.md](../common/hooks.md) with C++ specific content.
## Build Hooks
Run these checks before committing C++ changes:
```bash
# Format check
clang-format --dry-run --Werror src/*.cpp src/*.hpp
# Static analysis
clang-tidy src/*.cpp -- -std=c++17
# Build
cmake --build build
# Tests
ctest --test-dir build --output-on-failure
```
## Recommended CI Pipeline
1. **clang-format** — formatting check
2. **clang-tidy** — static analysis
3. **cppcheck** — additional analysis
4. **cmake build** — compilation
5. **ctest** — test execution with sanitizers

View File

@@ -0,0 +1,51 @@
---
paths:
- "**/*.cpp"
- "**/*.hpp"
- "**/*.cc"
- "**/*.hh"
- "**/*.cxx"
- "**/*.h"
- "**/CMakeLists.txt"
---
# C++ Patterns
> This file extends [common/patterns.md](../common/patterns.md) with C++ specific content.
## RAII (Resource Acquisition Is Initialization)
Tie resource lifetime to object lifetime:
```cpp
class FileHandle {
public:
explicit FileHandle(const std::string& path) : file_(std::fopen(path.c_str(), "r")) {}
~FileHandle() { if (file_) std::fclose(file_); }
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
private:
std::FILE* file_;
};
```
## Rule of Five/Zero
- **Rule of Zero**: Prefer classes that need no custom destructor, copy/move constructors, or assignments
- **Rule of Five**: If you define any of destructor/copy-ctor/copy-assign/move-ctor/move-assign, define all five
## Value Semantics
- Pass small/trivial types by value
- Pass large types by `const&`
- Return by value (rely on RVO/NRVO)
- Use move semantics for sink parameters
## Error Handling
- Use exceptions for exceptional conditions
- Use `std::optional` for values that may not exist
- Use `std::expected` (C++23) or result types for expected failures
## Reference
See skill: `cpp-coding-standards` for comprehensive C++ patterns and anti-patterns.

View File

@@ -0,0 +1,51 @@
---
paths:
- "**/*.cpp"
- "**/*.hpp"
- "**/*.cc"
- "**/*.hh"
- "**/*.cxx"
- "**/*.h"
- "**/CMakeLists.txt"
---
# C++ Security
> This file extends [common/security.md](../common/security.md) with C++ specific content.
## Memory Safety
- Never use raw `new`/`delete` — use smart pointers
- Never use C-style arrays — use `std::array` or `std::vector`
- Never use `malloc`/`free` — use C++ allocation
- Avoid `reinterpret_cast` unless absolutely necessary
## Buffer Overflows
- Use `std::string` over `char*`
- Use `.at()` for bounds-checked access when safety matters
- Never use `strcpy`, `strcat`, `sprintf` — use `std::string` or `fmt::format`
## Undefined Behavior
- Always initialize variables
- Avoid signed integer overflow
- Never dereference null or dangling pointers
- Use sanitizers in CI:
```bash
cmake -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined" ..
```
## Static Analysis
- Use **clang-tidy** for automated checks:
```bash
clang-tidy --checks='*' src/*.cpp
```
- Use **cppcheck** for additional analysis:
```bash
cppcheck --enable=all src/
```
## Reference
See skill: `cpp-coding-standards` for detailed security guidelines.

View File

@@ -0,0 +1,44 @@
---
paths:
- "**/*.cpp"
- "**/*.hpp"
- "**/*.cc"
- "**/*.hh"
- "**/*.cxx"
- "**/*.h"
- "**/CMakeLists.txt"
---
# C++ Testing
> This file extends [common/testing.md](../common/testing.md) with C++ specific content.
## Framework
Use **GoogleTest** (gtest/gmock) with **CMake/CTest**.
## Running Tests
```bash
cmake --build build && ctest --test-dir build --output-on-failure
```
## Coverage
```bash
cmake -DCMAKE_CXX_FLAGS="--coverage" -DCMAKE_EXE_LINKER_FLAGS="--coverage" ..
cmake --build .
ctest --output-on-failure
lcov --capture --directory . --output-file coverage.info
```
## Sanitizers
Always run tests with sanitizers in CI:
```bash
cmake -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined" ..
```
## Reference
See skill: `cpp-testing` for detailed C++ testing patterns, TDD workflow, and GoogleTest/GMock usage.

View File

@@ -0,0 +1,72 @@
---
paths:
- "**/*.cs"
- "**/*.csx"
---
# C# Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with C#-specific content.
## Standards
- Follow current .NET conventions and enable nullable reference types
- Prefer explicit access modifiers on public and internal APIs
- Keep files aligned with the primary type they define
## Types and Models
- Prefer `record` or `record struct` for immutable value-like models
- Use `class` for entities or types with identity and lifecycle
- Use `interface` for service boundaries and abstractions
- Avoid `dynamic` in application code; prefer generics or explicit models
```csharp
public sealed record UserDto(Guid Id, string Email);
public interface IUserRepository
{
Task<UserDto?> FindByIdAsync(Guid id, CancellationToken cancellationToken);
}
```
## Immutability
- Prefer `init` setters, constructor parameters, and immutable collections for shared state
- Do not mutate input models in-place when producing updated state
```csharp
public sealed record UserProfile(string Name, string Email);
public static UserProfile Rename(UserProfile profile, string name) =>
profile with { Name = name };
```
## Async and Error Handling
- Prefer `async`/`await` over blocking calls like `.Result` or `.Wait()`
- Pass `CancellationToken` through public async APIs
- Throw specific exceptions and log with structured properties
```csharp
public async Task<Order> LoadOrderAsync(
Guid orderId,
CancellationToken cancellationToken)
{
try
{
return await repository.FindAsync(orderId, cancellationToken)
?? throw new InvalidOperationException($"Order {orderId} was not found.");
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to load order {OrderId}", orderId);
throw;
}
}
```
## Formatting
- Use `dotnet format` for formatting and analyzer fixes
- Keep `using` directives organized and remove unused imports
- Prefer expression-bodied members only when they stay readable

View File

@@ -0,0 +1,25 @@
---
paths:
- "**/*.cs"
- "**/*.csx"
- "**/*.csproj"
- "**/*.sln"
- "**/Directory.Build.props"
- "**/Directory.Build.targets"
---
# C# Hooks
> This file extends [common/hooks.md](../common/hooks.md) with C#-specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **dotnet format**: Auto-format edited C# files and apply analyzer fixes
- **dotnet build**: Verify the solution or project still compiles after edits
- **dotnet test --no-build**: Re-run the nearest relevant test project after behavior changes
## Stop Hooks
- Run a final `dotnet build` before ending a session with broad C# changes
- Warn on modified `appsettings*.json` files so secrets do not get committed

View File

@@ -0,0 +1,50 @@
---
paths:
- "**/*.cs"
- "**/*.csx"
---
# C# Patterns
> This file extends [common/patterns.md](../common/patterns.md) with C#-specific content.
## API Response Pattern
```csharp
public sealed record ApiResponse<T>(
bool Success,
T? Data = default,
string? Error = null,
object? Meta = null);
```
## Repository Pattern
```csharp
public interface IRepository<T>
{
Task<IReadOnlyList<T>> FindAllAsync(CancellationToken cancellationToken);
Task<T?> FindByIdAsync(Guid id, CancellationToken cancellationToken);
Task<T> CreateAsync(T entity, CancellationToken cancellationToken);
Task<T> UpdateAsync(T entity, CancellationToken cancellationToken);
Task DeleteAsync(Guid id, CancellationToken cancellationToken);
}
```
## Options Pattern
Use strongly typed options for config instead of reading raw strings throughout the codebase.
```csharp
public sealed class PaymentsOptions
{
public const string SectionName = "Payments";
public required string BaseUrl { get; init; }
public required string ApiKeySecretName { get; init; }
}
```
## Dependency Injection
- Depend on interfaces at service boundaries
- Keep constructors focused; if a service needs too many dependencies, split responsibilities
- Register lifetimes intentionally: singleton for stateless/shared services, scoped for request data, transient for lightweight pure workers

View File

@@ -0,0 +1,58 @@
---
paths:
- "**/*.cs"
- "**/*.csx"
- "**/*.csproj"
- "**/appsettings*.json"
---
# C# Security
> This file extends [common/security.md](../common/security.md) with C#-specific content.
## Secret Management
- Never hardcode API keys, tokens, or connection strings in source code
- Use environment variables, user secrets for local development, and a secret manager in production
- Keep `appsettings.*.json` free of real credentials
```csharp
// BAD
const string ApiKey = "sk-live-123";
// GOOD
var apiKey = builder.Configuration["OpenAI:ApiKey"]
?? throw new InvalidOperationException("OpenAI:ApiKey is not configured.");
```
## SQL Injection Prevention
- Always use parameterized queries with ADO.NET, Dapper, or EF Core
- Never concatenate user input into SQL strings
- Validate sort fields and filter operators before using dynamic query composition
```csharp
const string sql = "SELECT * FROM Orders WHERE CustomerId = @customerId";
await connection.QueryAsync<Order>(sql, new { customerId });
```
## Input Validation
- Validate DTOs at the application boundary
- Use data annotations, FluentValidation, or explicit guard clauses
- Reject invalid model state before running business logic
## Authentication and Authorization
- Prefer framework auth handlers instead of custom token parsing
- Enforce authorization policies at endpoint or handler boundaries
- Never log raw tokens, passwords, or PII
## Error Handling
- Return safe client-facing messages
- Log detailed exceptions with structured context server-side
- Do not expose stack traces, SQL text, or filesystem paths in API responses
## References
See skill: `security-review` for broader application security review checklists.

View File

@@ -0,0 +1,46 @@
---
paths:
- "**/*.cs"
- "**/*.csx"
- "**/*.csproj"
---
# C# Testing
> This file extends [common/testing.md](../common/testing.md) with C#-specific content.
## Test Framework
- Prefer **xUnit** for unit and integration tests
- Use **FluentAssertions** for readable assertions
- Use **Moq** or **NSubstitute** for mocking dependencies
- Use **Testcontainers** when integration tests need real infrastructure
## Test Organization
- Mirror `src/` structure under `tests/`
- Separate unit, integration, and end-to-end coverage clearly
- Name tests by behavior, not implementation details
```csharp
public sealed class OrderServiceTests
{
[Fact]
public async Task FindByIdAsync_ReturnsOrder_WhenOrderExists()
{
// Arrange
// Act
// Assert
}
}
```
## ASP.NET Core Integration Tests
- Use `WebApplicationFactory<TEntryPoint>` for API integration coverage
- Test auth, validation, and serialization through HTTP, not by bypassing middleware
## Coverage
- Target 80%+ line coverage
- Focus coverage on domain logic, validation, auth, and failure paths
- Run `dotnet test` in CI with coverage collection enabled where available

View File

@@ -0,0 +1,32 @@
---
paths:
- "**/*.go"
- "**/go.mod"
- "**/go.sum"
---
# Go Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with Go specific content.
## Formatting
- **gofmt** and **goimports** are mandatory — no style debates
## Design Principles
- Accept interfaces, return structs
- Keep interfaces small (1-3 methods)
## Error Handling
Always wrap errors with context:
```go
if err != nil {
return fmt.Errorf("failed to create user: %w", err)
}
```
## Reference
See skill: `golang-patterns` for comprehensive Go idioms and patterns.

View File

@@ -0,0 +1,17 @@
---
paths:
- "**/*.go"
- "**/go.mod"
- "**/go.sum"
---
# Go Hooks
> This file extends [common/hooks.md](../common/hooks.md) with Go specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **gofmt/goimports**: Auto-format `.go` files after edit
- **go vet**: Run static analysis after editing `.go` files
- **staticcheck**: Run extended static checks on modified packages

View File

@@ -0,0 +1,45 @@
---
paths:
- "**/*.go"
- "**/go.mod"
- "**/go.sum"
---
# Go Patterns
> This file extends [common/patterns.md](../common/patterns.md) with Go specific content.
## Functional Options
```go
type Option func(*Server)
func WithPort(port int) Option {
return func(s *Server) { s.port = port }
}
func NewServer(opts ...Option) *Server {
s := &Server{port: 8080}
for _, opt := range opts {
opt(s)
}
return s
}
```
## Small Interfaces
Define interfaces where they are used, not where they are implemented.
## Dependency Injection
Use constructor functions to inject dependencies:
```go
func NewUserService(repo UserRepository, logger Logger) *UserService {
return &UserService{repo: repo, logger: logger}
}
```
## Reference
See skill: `golang-patterns` for comprehensive Go patterns including concurrency, error handling, and package organization.

View File

@@ -0,0 +1,34 @@
---
paths:
- "**/*.go"
- "**/go.mod"
- "**/go.sum"
---
# Go Security
> This file extends [common/security.md](../common/security.md) with Go specific content.
## Secret Management
```go
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("OPENAI_API_KEY not configured")
}
```
## Security Scanning
- Use **gosec** for static security analysis:
```bash
gosec ./...
```
## Context & Timeouts
Always use `context.Context` for timeout control:
```go
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
```

View File

@@ -0,0 +1,31 @@
---
paths:
- "**/*.go"
- "**/go.mod"
- "**/go.sum"
---
# Go Testing
> This file extends [common/testing.md](../common/testing.md) with Go specific content.
## Framework
Use the standard `go test` with **table-driven tests**.
## Race Detection
Always run with the `-race` flag:
```bash
go test -race ./...
```
## Coverage
```bash
go test -cover ./...
```
## Reference
See skill: `golang-testing` for detailed Go testing patterns and helpers.

View File

@@ -0,0 +1,114 @@
---
paths:
- "**/*.java"
---
# Java Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with Java-specific content.
## Formatting
- **google-java-format** or **Checkstyle** (Google or Sun style) for enforcement
- One public top-level type per file
- Consistent indent: 2 or 4 spaces (match project standard)
- Member order: constants, fields, constructors, public methods, protected, private
## Immutability
- Prefer `record` for value types (Java 16+)
- Mark fields `final` by default — use mutable state only when required
- Return defensive copies from public APIs: `List.copyOf()`, `Map.copyOf()`, `Set.copyOf()`
- Copy-on-write: return new instances rather than mutating existing ones
```java
// GOOD — immutable value type
public record OrderSummary(Long id, String customerName, BigDecimal total) {}
// GOOD — final fields, no setters
public class Order {
private final Long id;
private final List<LineItem> items;
public List<LineItem> getItems() {
return List.copyOf(items);
}
}
```
## Naming
Follow standard Java conventions:
- `PascalCase` for classes, interfaces, records, enums
- `camelCase` for methods, fields, parameters, local variables
- `SCREAMING_SNAKE_CASE` for `static final` constants
- Packages: all lowercase, reverse domain (`com.example.app.service`)
## Modern Java Features
Use modern language features where they improve clarity:
- **Records** for DTOs and value types (Java 16+)
- **Sealed classes** for closed type hierarchies (Java 17+)
- **Pattern matching** with `instanceof` — no explicit cast (Java 16+)
- **Text blocks** for multi-line strings — SQL, JSON templates (Java 15+)
- **Switch expressions** with arrow syntax (Java 14+)
- **Pattern matching in switch** — exhaustive sealed type handling (Java 21+)
```java
// Pattern matching instanceof
if (shape instanceof Circle c) {
return Math.PI * c.radius() * c.radius();
}
// Sealed type hierarchy
public sealed interface PaymentMethod permits CreditCard, BankTransfer, Wallet {}
// Switch expression
String label = switch (status) {
case ACTIVE -> "Active";
case SUSPENDED -> "Suspended";
case CLOSED -> "Closed";
};
```
## Optional Usage
- Return `Optional<T>` from finder methods that may have no result
- Use `map()`, `flatMap()`, `orElseThrow()` — never call `get()` without `isPresent()`
- Never use `Optional` as a field type or method parameter
```java
// GOOD
return repository.findById(id)
.map(ResponseDto::from)
.orElseThrow(() -> new OrderNotFoundException(id));
// BAD — Optional as parameter
public void process(Optional<String> name) {}
```
## Error Handling
- Prefer unchecked exceptions for domain errors
- Create domain-specific exceptions extending `RuntimeException`
- Avoid broad `catch (Exception e)` unless at top-level handlers
- Include context in exception messages
```java
public class OrderNotFoundException extends RuntimeException {
public OrderNotFoundException(Long id) {
super("Order not found: id=" + id);
}
}
```
## Streams
- Use streams for transformations; keep pipelines short (3-4 operations max)
- Prefer method references when readable: `.map(Order::getTotal)`
- Avoid side effects in stream operations
- For complex logic, prefer a loop over a convoluted stream pipeline
## References
See skill: `java-coding-standards` for full coding standards with examples.
See skill: `jpa-patterns` for JPA/Hibernate entity design patterns.

View File

@@ -0,0 +1,18 @@
---
paths:
- "**/*.java"
- "**/pom.xml"
- "**/build.gradle"
- "**/build.gradle.kts"
---
# Java Hooks
> This file extends [common/hooks.md](../common/hooks.md) with Java-specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **google-java-format**: Auto-format `.java` files after edit
- **checkstyle**: Run style checks after editing Java files
- **./mvnw compile** or **./gradlew compileJava**: Verify compilation after changes

View File

@@ -0,0 +1,146 @@
---
paths:
- "**/*.java"
---
# Java Patterns
> This file extends [common/patterns.md](../common/patterns.md) with Java-specific content.
## Repository Pattern
Encapsulate data access behind an interface:
```java
public interface OrderRepository {
Optional<Order> findById(Long id);
List<Order> findAll();
Order save(Order order);
void deleteById(Long id);
}
```
Concrete implementations handle storage details (JPA, JDBC, in-memory for tests).
## Service Layer
Business logic in service classes; keep controllers and repositories thin:
```java
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentGateway paymentGateway;
public OrderService(OrderRepository orderRepository, PaymentGateway paymentGateway) {
this.orderRepository = orderRepository;
this.paymentGateway = paymentGateway;
}
public OrderSummary placeOrder(CreateOrderRequest request) {
var order = Order.from(request);
paymentGateway.charge(order.total());
var saved = orderRepository.save(order);
return OrderSummary.from(saved);
}
}
```
## Constructor Injection
Always use constructor injection — never field injection:
```java
// GOOD — constructor injection (testable, immutable)
public class NotificationService {
private final EmailSender emailSender;
public NotificationService(EmailSender emailSender) {
this.emailSender = emailSender;
}
}
// BAD — field injection (untestable without reflection, requires framework magic)
public class NotificationService {
@Inject // or @Autowired
private EmailSender emailSender;
}
```
## DTO Mapping
Use records for DTOs. Map at service/controller boundaries:
```java
public record OrderResponse(Long id, String customer, BigDecimal total) {
public static OrderResponse from(Order order) {
return new OrderResponse(order.getId(), order.getCustomerName(), order.getTotal());
}
}
```
## Builder Pattern
Use for objects with many optional parameters:
```java
public class SearchCriteria {
private final String query;
private final int page;
private final int size;
private final String sortBy;
private SearchCriteria(Builder builder) {
this.query = builder.query;
this.page = builder.page;
this.size = builder.size;
this.sortBy = builder.sortBy;
}
public static class Builder {
private String query = "";
private int page = 0;
private int size = 20;
private String sortBy = "id";
public Builder query(String query) { this.query = query; return this; }
public Builder page(int page) { this.page = page; return this; }
public Builder size(int size) { this.size = size; return this; }
public Builder sortBy(String sortBy) { this.sortBy = sortBy; return this; }
public SearchCriteria build() { return new SearchCriteria(this); }
}
}
```
## Sealed Types for Domain Models
```java
public sealed interface PaymentResult permits PaymentSuccess, PaymentFailure {
record PaymentSuccess(String transactionId, BigDecimal amount) implements PaymentResult {}
record PaymentFailure(String errorCode, String message) implements PaymentResult {}
}
// Exhaustive handling (Java 21+)
String message = switch (result) {
case PaymentSuccess s -> "Paid: " + s.transactionId();
case PaymentFailure f -> "Failed: " + f.errorCode();
};
```
## API Response Envelope
Consistent API responses:
```java
public record ApiResponse<T>(boolean success, T data, String error) {
public static <T> ApiResponse<T> ok(T data) {
return new ApiResponse<>(true, data, null);
}
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(false, null, message);
}
}
```
## References
See skill: `springboot-patterns` for Spring Boot architecture patterns.
See skill: `jpa-patterns` for entity design and query optimization.

View File

@@ -0,0 +1,100 @@
---
paths:
- "**/*.java"
---
# Java Security
> This file extends [common/security.md](../common/security.md) with Java-specific content.
## Secrets Management
- Never hardcode API keys, tokens, or credentials in source code
- Use environment variables: `System.getenv("API_KEY")`
- Use a secret manager (Vault, AWS Secrets Manager) for production secrets
- Keep local config files with secrets in `.gitignore`
```java
// BAD
private static final String API_KEY = "sk-abc123...";
// GOOD — environment variable
String apiKey = System.getenv("PAYMENT_API_KEY");
Objects.requireNonNull(apiKey, "PAYMENT_API_KEY must be set");
```
## SQL Injection Prevention
- Always use parameterized queries — never concatenate user input into SQL
- Use `PreparedStatement` or your framework's parameterized query API
- Validate and sanitize any input used in native queries
```java
// BAD — SQL injection via string concatenation
Statement stmt = conn.createStatement();
String sql = "SELECT * FROM orders WHERE name = '" + name + "'";
stmt.executeQuery(sql);
// GOOD — PreparedStatement with parameterized query
PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE name = ?");
ps.setString(1, name);
// GOOD — JDBC template
jdbcTemplate.query("SELECT * FROM orders WHERE name = ?", mapper, name);
```
## Input Validation
- Validate all user input at system boundaries before processing
- Use Bean Validation (`@NotNull`, `@NotBlank`, `@Size`) on DTOs when using a validation framework
- Sanitize file paths and user-provided strings before use
- Reject input that fails validation with clear error messages
```java
// Validate manually in plain Java
public Order createOrder(String customerName, BigDecimal amount) {
if (customerName == null || customerName.isBlank()) {
throw new IllegalArgumentException("Customer name is required");
}
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
return new Order(customerName, amount);
}
```
## Authentication and Authorization
- Never implement custom auth crypto — use established libraries
- Store passwords with bcrypt or Argon2, never MD5/SHA1
- Enforce authorization checks at service boundaries
- Clear sensitive data from logs — never log passwords, tokens, or PII
## Dependency Security
- Run `mvn dependency:tree` or `./gradlew dependencies` to audit transitive dependencies
- Use OWASP Dependency-Check or Snyk to scan for known CVEs
- Keep dependencies updated — set up Dependabot or Renovate
## Error Messages
- Never expose stack traces, internal paths, or SQL errors in API responses
- Map exceptions to safe, generic client messages at handler boundaries
- Log detailed errors server-side; return generic messages to clients
```java
// Log the detail, return a generic message
try {
return orderService.findById(id);
} catch (OrderNotFoundException ex) {
log.warn("Order not found: id={}", id);
return ApiResponse.error("Resource not found"); // generic, no internals
} catch (Exception ex) {
log.error("Unexpected error processing order id={}", id, ex);
return ApiResponse.error("Internal server error"); // never expose ex.getMessage()
}
```
## References
See skill: `springboot-security` for Spring Security authentication and authorization patterns.
See skill: `security-review` for general security checklists.

View File

@@ -0,0 +1,131 @@
---
paths:
- "**/*.java"
---
# Java Testing
> This file extends [common/testing.md](../common/testing.md) with Java-specific content.
## Test Framework
- **JUnit 5** (`@Test`, `@ParameterizedTest`, `@Nested`, `@DisplayName`)
- **AssertJ** for fluent assertions (`assertThat(result).isEqualTo(expected)`)
- **Mockito** for mocking dependencies
- **Testcontainers** for integration tests requiring databases or services
## Test Organization
```
src/test/java/com/example/app/
service/ # Unit tests for service layer
controller/ # Web layer / API tests
repository/ # Data access tests
integration/ # Cross-layer integration tests
```
Mirror the `src/main/java` package structure in `src/test/java`.
## Unit Test Pattern
```java
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
private OrderService orderService;
@BeforeEach
void setUp() {
orderService = new OrderService(orderRepository);
}
@Test
@DisplayName("findById returns order when exists")
void findById_existingOrder_returnsOrder() {
var order = new Order(1L, "Alice", BigDecimal.TEN);
when(orderRepository.findById(1L)).thenReturn(Optional.of(order));
var result = orderService.findById(1L);
assertThat(result.customerName()).isEqualTo("Alice");
verify(orderRepository).findById(1L);
}
@Test
@DisplayName("findById throws when order not found")
void findById_missingOrder_throws() {
when(orderRepository.findById(99L)).thenReturn(Optional.empty());
assertThatThrownBy(() -> orderService.findById(99L))
.isInstanceOf(OrderNotFoundException.class)
.hasMessageContaining("99");
}
}
```
## Parameterized Tests
```java
@ParameterizedTest
@CsvSource({
"100.00, 10, 90.00",
"50.00, 0, 50.00",
"200.00, 25, 150.00"
})
@DisplayName("discount applied correctly")
void applyDiscount(BigDecimal price, int pct, BigDecimal expected) {
assertThat(PricingUtils.discount(price, pct)).isEqualByComparingTo(expected);
}
```
## Integration Tests
Use Testcontainers for real database integration:
```java
@Testcontainers
class OrderRepositoryIT {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
private OrderRepository repository;
@BeforeEach
void setUp() {
var dataSource = new PGSimpleDataSource();
dataSource.setUrl(postgres.getJdbcUrl());
dataSource.setUser(postgres.getUsername());
dataSource.setPassword(postgres.getPassword());
repository = new JdbcOrderRepository(dataSource);
}
@Test
void save_and_findById() {
var saved = repository.save(new Order(null, "Bob", BigDecimal.ONE));
var found = repository.findById(saved.getId());
assertThat(found).isPresent();
}
}
```
For Spring Boot integration tests, see skill: `springboot-tdd`.
## Test Naming
Use descriptive names with `@DisplayName`:
- `methodName_scenario_expectedBehavior()` for method names
- `@DisplayName("human-readable description")` for reports
## Coverage
- Target 80%+ line coverage
- Use JaCoCo for coverage reporting
- Focus on service and domain logic — skip trivial getters/config classes
## References
See skill: `springboot-tdd` for Spring Boot TDD patterns with MockMvc and Testcontainers.
See skill: `java-coding-standards` for testing expectations.

View File

@@ -0,0 +1,86 @@
---
paths:
- "**/*.kt"
- "**/*.kts"
---
# Kotlin Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with Kotlin-specific content.
## Formatting
- **ktlint** or **Detekt** for style enforcement
- Official Kotlin code style (`kotlin.code.style=official` in `gradle.properties`)
## Immutability
- Prefer `val` over `var` — default to `val` and only use `var` when mutation is required
- Use `data class` for value types; use immutable collections (`List`, `Map`, `Set`) in public APIs
- Copy-on-write for state updates: `state.copy(field = newValue)`
## Naming
Follow Kotlin conventions:
- `camelCase` for functions and properties
- `PascalCase` for classes, interfaces, objects, and type aliases
- `SCREAMING_SNAKE_CASE` for constants (`const val` or `@JvmStatic`)
- Prefix interfaces with behavior, not `I`: `Clickable` not `IClickable`
## Null Safety
- Never use `!!` — prefer `?.`, `?:`, `requireNotNull()`, or `checkNotNull()`
- Use `?.let {}` for scoped null-safe operations
- Return nullable types from functions that can legitimately have no result
```kotlin
// BAD
val name = user!!.name
// GOOD
val name = user?.name ?: "Unknown"
val name = requireNotNull(user) { "User must be set before accessing name" }.name
```
## Sealed Types
Use sealed classes/interfaces to model closed state hierarchies:
```kotlin
sealed interface UiState<out T> {
data object Loading : UiState<Nothing>
data class Success<T>(val data: T) : UiState<T>
data class Error(val message: String) : UiState<Nothing>
}
```
Always use exhaustive `when` with sealed types — no `else` branch.
## Extension Functions
Use extension functions for utility operations, but keep them discoverable:
- Place in a file named after the receiver type (`StringExt.kt`, `FlowExt.kt`)
- Keep scope limited — don't add extensions to `Any` or overly generic types
## Scope Functions
Use the right scope function:
- `let` — null check + transform: `user?.let { greet(it) }`
- `run` — compute a result using receiver: `service.run { fetch(config) }`
- `apply` — configure an object: `builder.apply { timeout = 30 }`
- `also` — side effects: `result.also { log(it) }`
- Avoid deep nesting of scope functions (max 2 levels)
## Error Handling
- Use `Result<T>` or custom sealed types
- Use `runCatching {}` for wrapping throwable code
- Never catch `CancellationException` — always rethrow it
- Avoid `try-catch` for control flow
```kotlin
// BAD — using exceptions for control flow
val user = try { repository.getUser(id) } catch (e: NotFoundException) { null }
// GOOD — nullable return
val user: User? = repository.findUser(id)
```

View File

@@ -0,0 +1,17 @@
---
paths:
- "**/*.kt"
- "**/*.kts"
- "**/build.gradle.kts"
---
# Kotlin Hooks
> This file extends [common/hooks.md](../common/hooks.md) with Kotlin-specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **ktfmt/ktlint**: Auto-format `.kt` and `.kts` files after edit
- **detekt**: Run static analysis after editing Kotlin files
- **./gradlew build**: Verify compilation after changes

View File

@@ -0,0 +1,146 @@
---
paths:
- "**/*.kt"
- "**/*.kts"
---
# Kotlin Patterns
> This file extends [common/patterns.md](../common/patterns.md) with Kotlin and Android/KMP-specific content.
## Dependency Injection
Prefer constructor injection. Use Koin (KMP) or Hilt (Android-only):
```kotlin
// Koin — declare modules
val dataModule = module {
single<ItemRepository> { ItemRepositoryImpl(get(), get()) }
factory { GetItemsUseCase(get()) }
viewModelOf(::ItemListViewModel)
}
// Hilt — annotations
@HiltViewModel
class ItemListViewModel @Inject constructor(
private val getItems: GetItemsUseCase
) : ViewModel()
```
## ViewModel Pattern
Single state object, event sink, one-way data flow:
```kotlin
data class ScreenState(
val items: List<Item> = emptyList(),
val isLoading: Boolean = false
)
class ScreenViewModel(private val useCase: GetItemsUseCase) : ViewModel() {
private val _state = MutableStateFlow(ScreenState())
val state = _state.asStateFlow()
fun onEvent(event: ScreenEvent) {
when (event) {
is ScreenEvent.Load -> load()
is ScreenEvent.Delete -> delete(event.id)
}
}
}
```
## Repository Pattern
- `suspend` functions return `Result<T>` or custom error type
- `Flow` for reactive streams
- Coordinate local + remote data sources
```kotlin
interface ItemRepository {
suspend fun getById(id: String): Result<Item>
suspend fun getAll(): Result<List<Item>>
fun observeAll(): Flow<List<Item>>
}
```
## UseCase Pattern
Single responsibility, `operator fun invoke`:
```kotlin
class GetItemUseCase(private val repository: ItemRepository) {
suspend operator fun invoke(id: String): Result<Item> {
return repository.getById(id)
}
}
class GetItemsUseCase(private val repository: ItemRepository) {
suspend operator fun invoke(): Result<List<Item>> {
return repository.getAll()
}
}
```
## expect/actual (KMP)
Use for platform-specific implementations:
```kotlin
// commonMain
expect fun platformName(): String
expect class SecureStorage {
fun save(key: String, value: String)
fun get(key: String): String?
}
// androidMain
actual fun platformName(): String = "Android"
actual class SecureStorage {
actual fun save(key: String, value: String) { /* EncryptedSharedPreferences */ }
actual fun get(key: String): String? = null /* ... */
}
// iosMain
actual fun platformName(): String = "iOS"
actual class SecureStorage {
actual fun save(key: String, value: String) { /* Keychain */ }
actual fun get(key: String): String? = null /* ... */
}
```
## Coroutine Patterns
- Use `viewModelScope` in ViewModels, `coroutineScope` for structured child work
- Use `stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), initialValue)` for StateFlow from cold Flows
- Use `supervisorScope` when child failures should be independent
## Builder Pattern with DSL
```kotlin
class HttpClientConfig {
var baseUrl: String = ""
var timeout: Long = 30_000
private val interceptors = mutableListOf<Interceptor>()
fun interceptor(block: () -> Interceptor) {
interceptors.add(block())
}
}
fun httpClient(block: HttpClientConfig.() -> Unit): HttpClient {
val config = HttpClientConfig().apply(block)
return HttpClient(config)
}
// Usage
val client = httpClient {
baseUrl = "https://api.example.com"
timeout = 15_000
interceptor { AuthInterceptor(tokenProvider) }
}
```
## References
See skill: `kotlin-coroutines-flows` for detailed coroutine patterns.
See skill: `android-clean-architecture` for module and layer patterns.

View File

@@ -0,0 +1,82 @@
---
paths:
- "**/*.kt"
- "**/*.kts"
---
# Kotlin Security
> This file extends [common/security.md](../common/security.md) with Kotlin and Android/KMP-specific content.
## Secrets Management
- Never hardcode API keys, tokens, or credentials in source code
- Use `local.properties` (git-ignored) for local development secrets
- Use `BuildConfig` fields generated from CI secrets for release builds
- Use `EncryptedSharedPreferences` (Android) or Keychain (iOS) for runtime secret storage
```kotlin
// BAD
val apiKey = "sk-abc123..."
// GOOD — from BuildConfig (generated at build time)
val apiKey = BuildConfig.API_KEY
// GOOD — from secure storage at runtime
val token = secureStorage.get("auth_token")
```
## Network Security
- Use HTTPS exclusively — configure `network_security_config.xml` to block cleartext
- Pin certificates for sensitive endpoints using OkHttp `CertificatePinner` or Ktor equivalent
- Set timeouts on all HTTP clients — never leave defaults (which may be infinite)
- Validate and sanitize all server responses before use
```xml
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<base-config cleartextTrafficPermitted="false" />
</network-security-config>
```
## Input Validation
- Validate all user input before processing or sending to API
- Use parameterized queries for Room/SQLDelight — never concatenate user input into SQL
- Sanitize file paths from user input to prevent path traversal
```kotlin
// BAD — SQL injection
@Query("SELECT * FROM items WHERE name = '$input'")
// GOOD — parameterized
@Query("SELECT * FROM items WHERE name = :input")
fun findByName(input: String): List<ItemEntity>
```
## Data Protection
- Use `EncryptedSharedPreferences` for sensitive key-value data on Android
- Use `@Serializable` with explicit field names — don't leak internal property names
- Clear sensitive data from memory when no longer needed
- Use `@Keep` or ProGuard rules for serialized classes to prevent name mangling
## Authentication
- Store tokens in secure storage, not in plain SharedPreferences
- Implement token refresh with proper 401/403 handling
- Clear all auth state on logout (tokens, cached user data, cookies)
- Use biometric authentication (`BiometricPrompt`) for sensitive operations
## ProGuard / R8
- Keep rules for all serialized models (`@Serializable`, Gson, Moshi)
- Keep rules for reflection-based libraries (Koin, Retrofit)
- Test release builds — obfuscation can break serialization silently
## WebView Security
- Disable JavaScript unless explicitly needed: `settings.javaScriptEnabled = false`
- Validate URLs before loading in WebView
- Never expose `@JavascriptInterface` methods that access sensitive data
- Use `WebViewClient.shouldOverrideUrlLoading()` to control navigation

View File

@@ -0,0 +1,128 @@
---
paths:
- "**/*.kt"
- "**/*.kts"
---
# Kotlin Testing
> This file extends [common/testing.md](../common/testing.md) with Kotlin and Android/KMP-specific content.
## Test Framework
- **kotlin.test** for multiplatform (KMP) — `@Test`, `assertEquals`, `assertTrue`
- **JUnit 4/5** for Android-specific tests
- **Turbine** for testing Flows and StateFlow
- **kotlinx-coroutines-test** for coroutine testing (`runTest`, `TestDispatcher`)
## ViewModel Testing with Turbine
```kotlin
@Test
fun `loading state emitted then data`() = runTest {
val repo = FakeItemRepository()
repo.addItem(testItem)
val viewModel = ItemListViewModel(GetItemsUseCase(repo))
viewModel.state.test {
assertEquals(ItemListState(), awaitItem()) // initial state
viewModel.onEvent(ItemListEvent.Load)
assertTrue(awaitItem().isLoading) // loading
assertEquals(listOf(testItem), awaitItem().items) // loaded
}
}
```
## Fakes Over Mocks
Prefer hand-written fakes over mocking frameworks:
```kotlin
class FakeItemRepository : ItemRepository {
private val items = mutableListOf<Item>()
var fetchError: Throwable? = null
override suspend fun getAll(): Result<List<Item>> {
fetchError?.let { return Result.failure(it) }
return Result.success(items.toList())
}
override fun observeAll(): Flow<List<Item>> = flowOf(items.toList())
fun addItem(item: Item) { items.add(item) }
}
```
## Coroutine Testing
```kotlin
@Test
fun `parallel operations complete`() = runTest {
val repo = FakeRepository()
val result = loadDashboard(repo)
advanceUntilIdle()
assertNotNull(result.items)
assertNotNull(result.stats)
}
```
Use `runTest` — it auto-advances virtual time and provides `TestScope`.
## Ktor MockEngine
```kotlin
val mockEngine = MockEngine { request ->
when (request.url.encodedPath) {
"/api/items" -> respond(
content = Json.encodeToString(testItems),
headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString())
)
else -> respondError(HttpStatusCode.NotFound)
}
}
val client = HttpClient(mockEngine) {
install(ContentNegotiation) { json() }
}
```
## Room/SQLDelight Testing
- Room: Use `Room.inMemoryDatabaseBuilder()` for in-memory testing
- SQLDelight: Use `JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)` for JVM tests
```kotlin
@Test
fun `insert and query items`() = runTest {
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
Database.Schema.create(driver)
val db = Database(driver)
db.itemQueries.insert("1", "Sample Item", "description")
val items = db.itemQueries.getAll().executeAsList()
assertEquals(1, items.size)
}
```
## Test Naming
Use backtick-quoted descriptive names:
```kotlin
@Test
fun `search with empty query returns all items`() = runTest { }
@Test
fun `delete item emits updated list without deleted item`() = runTest { }
```
## Test Organization
```
src/
├── commonTest/kotlin/ # Shared tests (ViewModel, UseCase, Repository)
├── androidUnitTest/kotlin/ # Android unit tests (JUnit)
├── androidInstrumentedTest/kotlin/ # Instrumented tests (Room, UI)
└── iosTest/kotlin/ # iOS-specific tests
```
Minimum test coverage: ViewModel + UseCase for every feature.

View File

@@ -0,0 +1,46 @@
---
paths:
- "**/*.pl"
- "**/*.pm"
- "**/*.t"
- "**/*.psgi"
- "**/*.cgi"
---
# Perl Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with Perl-specific content.
## Standards
- Always `use v5.36` (enables `strict`, `warnings`, `say`, subroutine signatures)
- Use subroutine signatures — never unpack `@_` manually
- Prefer `say` over `print` with explicit newlines
## Immutability
- Use **Moo** with `is => 'ro'` and `Types::Standard` for all attributes
- Never use blessed hashrefs directly — always use Moo/Moose accessors
- **OO override note**: Moo `has` attributes with `builder` or `default` are acceptable for computed read-only values
## Formatting
Use **perltidy** with these settings:
```
-i=4 # 4-space indent
-l=100 # 100 char line length
-ce # cuddled else
-bar # opening brace always right
```
## Linting
Use **perlcritic** at severity 3 with themes: `core`, `pbp`, `security`.
```bash
perlcritic --severity 3 --theme 'core || pbp || security' lib/
```
## Reference
See skill: `perl-patterns` for comprehensive modern Perl idioms and best practices.

View File

@@ -0,0 +1,22 @@
---
paths:
- "**/*.pl"
- "**/*.pm"
- "**/*.t"
- "**/*.psgi"
- "**/*.cgi"
---
# Perl Hooks
> This file extends [common/hooks.md](../common/hooks.md) with Perl-specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **perltidy**: Auto-format `.pl` and `.pm` files after edit
- **perlcritic**: Run lint check after editing `.pm` files
## Warnings
- Warn about `print` in non-script `.pm` files — use `say` or a logging module (e.g., `Log::Any`)

View File

@@ -0,0 +1,76 @@
---
paths:
- "**/*.pl"
- "**/*.pm"
- "**/*.t"
- "**/*.psgi"
- "**/*.cgi"
---
# Perl Patterns
> This file extends [common/patterns.md](../common/patterns.md) with Perl-specific content.
## Repository Pattern
Use **DBI** or **DBIx::Class** behind an interface:
```perl
package MyApp::Repo::User;
use Moo;
has dbh => (is => 'ro', required => 1);
sub find_by_id ($self, $id) {
my $sth = $self->dbh->prepare('SELECT * FROM users WHERE id = ?');
$sth->execute($id);
return $sth->fetchrow_hashref;
}
```
## DTOs / Value Objects
Use **Moo** classes with **Types::Standard** (equivalent to Python dataclasses):
```perl
package MyApp::DTO::User;
use Moo;
use Types::Standard qw(Str Int);
has name => (is => 'ro', isa => Str, required => 1);
has email => (is => 'ro', isa => Str, required => 1);
has age => (is => 'ro', isa => Int);
```
## Resource Management
- Always use **three-arg open** with `autodie`
- Use **Path::Tiny** for file operations
```perl
use autodie;
use Path::Tiny;
my $content = path('config.json')->slurp_utf8;
```
## Module Interface
Use `Exporter 'import'` with `@EXPORT_OK` — never `@EXPORT`:
```perl
use Exporter 'import';
our @EXPORT_OK = qw(parse_config validate_input);
```
## Dependency Management
Use **cpanfile** + **carton** for reproducible installs:
```bash
carton install
carton exec prove -lr t/
```
## Reference
See skill: `perl-patterns` for comprehensive modern Perl patterns and idioms.

View File

@@ -0,0 +1,69 @@
---
paths:
- "**/*.pl"
- "**/*.pm"
- "**/*.t"
- "**/*.psgi"
- "**/*.cgi"
---
# Perl Security
> This file extends [common/security.md](../common/security.md) with Perl-specific content.
## Taint Mode
- Use `-T` flag on all CGI/web-facing scripts
- Sanitize `%ENV` (`$ENV{PATH}`, `$ENV{CDPATH}`, etc.) before any external command
## Input Validation
- Use allowlist regex for untainting — never `/(.*)/s`
- Validate all user input with explicit patterns:
```perl
if ($input =~ /\A([a-zA-Z0-9_-]+)\z/) {
my $clean = $1;
}
```
## File I/O
- **Three-arg open only** — never two-arg open
- Prevent path traversal with `Cwd::realpath`:
```perl
use Cwd 'realpath';
my $safe_path = realpath($user_path);
die "Path traversal" unless $safe_path =~ m{\A/allowed/directory/};
```
## Process Execution
- Use **list-form `system()`** — never single-string form
- Use **IPC::Run3** for capturing output
- Never use backticks with variable interpolation
```perl
system('grep', '-r', $pattern, $directory); # safe
```
## SQL Injection Prevention
Always use DBI placeholders — never interpolate into SQL:
```perl
my $sth = $dbh->prepare('SELECT * FROM users WHERE email = ?');
$sth->execute($email);
```
## Security Scanning
Run **perlcritic** with the security theme at severity 4+:
```bash
perlcritic --severity 4 --theme security lib/
```
## Reference
See skill: `perl-security` for comprehensive Perl security patterns, taint mode, and safe I/O.

View File

@@ -0,0 +1,54 @@
---
paths:
- "**/*.pl"
- "**/*.pm"
- "**/*.t"
- "**/*.psgi"
- "**/*.cgi"
---
# Perl Testing
> This file extends [common/testing.md](../common/testing.md) with Perl-specific content.
## Framework
Use **Test2::V0** for new projects (not Test::More):
```perl
use Test2::V0;
is($result, 42, 'answer is correct');
done_testing;
```
## Runner
```bash
prove -l t/ # adds lib/ to @INC
prove -lr -j8 t/ # recursive, 8 parallel jobs
```
Always use `-l` to ensure `lib/` is on `@INC`.
## Coverage
Use **Devel::Cover** — target 80%+:
```bash
cover -test
```
## Mocking
- **Test::MockModule** — mock methods on existing modules
- **Test::MockObject** — create test doubles from scratch
## Pitfalls
- Always end test files with `done_testing`
- Never forget the `-l` flag with `prove`
## Reference
See skill: `perl-testing` for detailed Perl TDD patterns with Test2::V0, prove, and Devel::Cover.

View File

@@ -0,0 +1,40 @@
---
paths:
- "**/*.php"
- "**/composer.json"
---
# PHP Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with PHP specific content.
## Standards
- Follow **PSR-12** formatting and naming conventions.
- Prefer `declare(strict_types=1);` in application code.
- Use scalar type hints, return types, and typed properties everywhere new code permits.
## Immutability
- Prefer immutable DTOs and value objects for data crossing service boundaries.
- Use `readonly` properties or immutable constructors for request/response payloads where possible.
- Keep arrays for simple maps; promote business-critical structures into explicit classes.
## Formatting
- Use **PHP-CS-Fixer** or **Laravel Pint** for formatting.
- Use **PHPStan** or **Psalm** for static analysis.
- Keep Composer scripts checked in so the same commands run locally and in CI.
## Imports
- Add `use` statements for all referenced classes, interfaces, and traits.
- Avoid relying on the global namespace unless the project explicitly prefers fully qualified names.
## Error Handling
- Throw exceptions for exceptional states; avoid returning `false`/`null` as hidden error channels in new code.
- Convert framework/request input into validated DTOs before it reaches domain logic.
## Reference
See skill: `backend-patterns` for broader service/repository layering guidance.

View File

@@ -0,0 +1,24 @@
---
paths:
- "**/*.php"
- "**/composer.json"
- "**/phpstan.neon"
- "**/phpstan.neon.dist"
- "**/psalm.xml"
---
# PHP Hooks
> This file extends [common/hooks.md](../common/hooks.md) with PHP specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **Pint / PHP-CS-Fixer**: Auto-format edited `.php` files.
- **PHPStan / Psalm**: Run static analysis after PHP edits in typed codebases.
- **PHPUnit / Pest**: Run targeted tests for touched files or modules when edits affect behavior.
## Warnings
- Warn on `var_dump`, `dd`, `dump`, or `die()` left in edited files.
- Warn when edited PHP files add raw SQL or disable CSRF/session protections.

View File

@@ -0,0 +1,33 @@
---
paths:
- "**/*.php"
- "**/composer.json"
---
# PHP Patterns
> This file extends [common/patterns.md](../common/patterns.md) with PHP specific content.
## Thin Controllers, Explicit Services
- Keep controllers focused on transport: auth, validation, serialization, status codes.
- Move business rules into application/domain services that are easy to test without HTTP bootstrapping.
## DTOs and Value Objects
- Replace shape-heavy associative arrays with DTOs for requests, commands, and external API payloads.
- Use value objects for money, identifiers, date ranges, and other constrained concepts.
## Dependency Injection
- Depend on interfaces or narrow service contracts, not framework globals.
- Pass collaborators through constructors so services are testable without service-locator lookups.
## Boundaries
- Isolate ORM models from domain decisions when the model layer is doing more than persistence.
- Wrap third-party SDKs behind small adapters so the rest of the codebase depends on your contract, not theirs.
## Reference
See skill: `api-design` for endpoint conventions and response-shape guidance.
See skill: `laravel-patterns` for Laravel-specific architecture guidance.

View File

@@ -0,0 +1,37 @@
---
paths:
- "**/*.php"
- "**/composer.lock"
- "**/composer.json"
---
# PHP Security
> This file extends [common/security.md](../common/security.md) with PHP specific content.
## Input and Output
- Validate request input at the framework boundary (`FormRequest`, Symfony Validator, or explicit DTO validation).
- Escape output in templates by default; treat raw HTML rendering as an exception that must be justified.
- Never trust query params, cookies, headers, or uploaded file metadata without validation.
## Database Safety
- Use prepared statements (`PDO`, Doctrine, Eloquent query builder) for all dynamic queries.
- Avoid string-building SQL in controllers/views.
- Scope ORM mass-assignment carefully and whitelist writable fields.
## Secrets and Dependencies
- Load secrets from environment variables or a secret manager, never from committed config files.
- Run `composer audit` in CI and review new package maintainer trust before adding dependencies.
- Pin major versions deliberately and remove abandoned packages quickly.
## Auth and Session Safety
- Use `password_hash()` / `password_verify()` for password storage.
- Regenerate session identifiers after authentication and privilege changes.
- Enforce CSRF protection on state-changing web requests.
## Reference
See skill: `laravel-security` for Laravel-specific security guidance.

View File

@@ -0,0 +1,39 @@
---
paths:
- "**/*.php"
- "**/phpunit.xml"
- "**/phpunit.xml.dist"
- "**/composer.json"
---
# PHP Testing
> This file extends [common/testing.md](../common/testing.md) with PHP specific content.
## Framework
Use **PHPUnit** as the default test framework. If **Pest** is configured in the project, prefer Pest for new tests and avoid mixing frameworks.
## Coverage
```bash
vendor/bin/phpunit --coverage-text
# or
vendor/bin/pest --coverage
```
Prefer **pcov** or **Xdebug** in CI, and keep coverage thresholds in CI rather than as tribal knowledge.
## Test Organization
- Separate fast unit tests from framework/database integration tests.
- Use factory/builders for fixtures instead of large hand-written arrays.
- Keep HTTP/controller tests focused on transport and validation; move business rules into service-level tests.
## Inertia
If the project uses Inertia.js, prefer `assertInertia` with `AssertableInertia` to verify component names and props instead of raw JSON assertions.
## Reference
See skill: `tdd-workflow` for the repo-wide RED -> GREEN -> REFACTOR loop.
See skill: `laravel-tdd` for Laravel-specific testing patterns (PHPUnit and Pest).

View File

@@ -0,0 +1,42 @@
---
paths:
- "**/*.py"
- "**/*.pyi"
---
# Python Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with Python specific content.
## Standards
- Follow **PEP 8** conventions
- Use **type annotations** on all function signatures
## Immutability
Prefer immutable data structures:
```python
from dataclasses import dataclass
@dataclass(frozen=True)
class User:
name: str
email: str
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
```
## Formatting
- **black** for code formatting
- **isort** for import sorting
- **ruff** for linting
## Reference
See skill: `python-patterns` for comprehensive Python idioms and patterns.

View File

@@ -0,0 +1,19 @@
---
paths:
- "**/*.py"
- "**/*.pyi"
---
# Python Hooks
> This file extends [common/hooks.md](../common/hooks.md) with Python specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **black/ruff**: Auto-format `.py` files after edit
- **mypy/pyright**: Run type checking after editing `.py` files
## Warnings
- Warn about `print()` statements in edited files (use `logging` module instead)

View File

@@ -0,0 +1,39 @@
---
paths:
- "**/*.py"
- "**/*.pyi"
---
# Python Patterns
> This file extends [common/patterns.md](../common/patterns.md) with Python specific content.
## Protocol (Duck Typing)
```python
from typing import Protocol
class Repository(Protocol):
def find_by_id(self, id: str) -> dict | None: ...
def save(self, entity: dict) -> dict: ...
```
## Dataclasses as DTOs
```python
from dataclasses import dataclass
@dataclass
class CreateUserRequest:
name: str
email: str
age: int | None = None
```
## Context Managers & Generators
- Use context managers (`with` statement) for resource management
- Use generators for lazy evaluation and memory-efficient iteration
## Reference
See skill: `python-patterns` for comprehensive patterns including decorators, concurrency, and package organization.

View File

@@ -0,0 +1,30 @@
---
paths:
- "**/*.py"
- "**/*.pyi"
---
# Python Security
> This file extends [common/security.md](../common/security.md) with Python specific content.
## Secret Management
```python
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.environ["OPENAI_API_KEY"] # Raises KeyError if missing
```
## Security Scanning
- Use **bandit** for static security analysis:
```bash
bandit -r src/
```
## Reference
See skill: `django-security` for Django-specific security guidelines (if applicable).

View File

@@ -0,0 +1,38 @@
---
paths:
- "**/*.py"
- "**/*.pyi"
---
# Python Testing
> This file extends [common/testing.md](../common/testing.md) with Python specific content.
## Framework
Use **pytest** as the testing framework.
## Coverage
```bash
pytest --cov=src --cov-report=term-missing
```
## Test Organization
Use `pytest.mark` for test categorization:
```python
import pytest
@pytest.mark.unit
def test_calculate_total():
...
@pytest.mark.integration
def test_database_connection():
...
```
## Reference
See skill: `python-testing` for detailed pytest patterns and fixtures.

View File

@@ -0,0 +1,151 @@
---
paths:
- "**/*.rs"
---
# Rust Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with Rust-specific content.
## Formatting
- **rustfmt** for enforcement — always run `cargo fmt` before committing
- **clippy** for lints — `cargo clippy -- -D warnings` (treat warnings as errors)
- 4-space indent (rustfmt default)
- Max line width: 100 characters (rustfmt default)
## Immutability
Rust variables are immutable by default — embrace this:
- Use `let` by default; only use `let mut` when mutation is required
- Prefer returning new values over mutating in place
- Use `Cow<'_, T>` when a function may or may not need to allocate
```rust
use std::borrow::Cow;
// GOOD — immutable by default, new value returned
fn normalize(input: &str) -> Cow<'_, str> {
if input.contains(' ') {
Cow::Owned(input.replace(' ', "_"))
} else {
Cow::Borrowed(input)
}
}
// BAD — unnecessary mutation
fn normalize_bad(input: &mut String) {
*input = input.replace(' ', "_");
}
```
## Naming
Follow standard Rust conventions:
- `snake_case` for functions, methods, variables, modules, crates
- `PascalCase` (UpperCamelCase) for types, traits, enums, type parameters
- `SCREAMING_SNAKE_CASE` for constants and statics
- Lifetimes: short lowercase (`'a`, `'de`) — descriptive names for complex cases (`'input`)
## Ownership and Borrowing
- Borrow (`&T`) by default; take ownership only when you need to store or consume
- Never clone to satisfy the borrow checker without understanding the root cause
- Accept `&str` over `String`, `&[T]` over `Vec<T>` in function parameters
- Use `impl Into<String>` for constructors that need to own a `String`
```rust
// GOOD — borrows when ownership isn't needed
fn word_count(text: &str) -> usize {
text.split_whitespace().count()
}
// GOOD — takes ownership in constructor via Into
fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
// BAD — takes String when &str suffices
fn word_count_bad(text: String) -> usize {
text.split_whitespace().count()
}
```
## Error Handling
- Use `Result<T, E>` and `?` for propagation — never `unwrap()` in production code
- **Libraries**: define typed errors with `thiserror`
- **Applications**: use `anyhow` for flexible error context
- Add context with `.with_context(|| format!("failed to ..."))?`
- Reserve `unwrap()` / `expect()` for tests and truly unreachable states
```rust
// GOOD — library error with thiserror
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("failed to read config: {0}")]
Io(#[from] std::io::Error),
#[error("invalid config format: {0}")]
Parse(String),
}
// GOOD — application error with anyhow
use anyhow::Context;
fn load_config(path: &str) -> anyhow::Result<Config> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("failed to read {path}"))?;
toml::from_str(&content)
.with_context(|| format!("failed to parse {path}"))
}
```
## Iterators Over Loops
Prefer iterator chains for transformations; use loops for complex control flow:
```rust
// GOOD — declarative and composable
let active_emails: Vec<&str> = users.iter()
.filter(|u| u.is_active)
.map(|u| u.email.as_str())
.collect();
// GOOD — loop for complex logic with early returns
for user in &users {
if let Some(verified) = verify_email(&user.email)? {
send_welcome(&verified)?;
}
}
```
## Module Organization
Organize by domain, not by type:
```text
src/
├── main.rs
├── lib.rs
├── auth/ # Domain module
│ ├── mod.rs
│ ├── token.rs
│ └── middleware.rs
├── orders/ # Domain module
│ ├── mod.rs
│ ├── model.rs
│ └── service.rs
└── db/ # Infrastructure
├── mod.rs
└── pool.rs
```
## Visibility
- Default to private; use `pub(crate)` for internal sharing
- Only mark `pub` what is part of the crate's public API
- Re-export public API from `lib.rs`
## References
See skill: `rust-patterns` for comprehensive Rust idioms and patterns.

View File

@@ -0,0 +1,16 @@
---
paths:
- "**/*.rs"
- "**/Cargo.toml"
---
# Rust Hooks
> This file extends [common/hooks.md](../common/hooks.md) with Rust-specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **cargo fmt**: Auto-format `.rs` files after edit
- **cargo clippy**: Run lint checks after editing Rust files
- **cargo check**: Verify compilation after changes (faster than `cargo build`)

View File

@@ -0,0 +1,168 @@
---
paths:
- "**/*.rs"
---
# Rust Patterns
> This file extends [common/patterns.md](../common/patterns.md) with Rust-specific content.
## Repository Pattern with Traits
Encapsulate data access behind a trait:
```rust
pub trait OrderRepository: Send + Sync {
fn find_by_id(&self, id: u64) -> Result<Option<Order>, StorageError>;
fn find_all(&self) -> Result<Vec<Order>, StorageError>;
fn save(&self, order: &Order) -> Result<Order, StorageError>;
fn delete(&self, id: u64) -> Result<(), StorageError>;
}
```
Concrete implementations handle storage details (Postgres, SQLite, in-memory for tests).
## Service Layer
Business logic in service structs; inject dependencies via constructor:
```rust
pub struct OrderService {
repo: Box<dyn OrderRepository>,
payment: Box<dyn PaymentGateway>,
}
impl OrderService {
pub fn new(repo: Box<dyn OrderRepository>, payment: Box<dyn PaymentGateway>) -> Self {
Self { repo, payment }
}
pub fn place_order(&self, request: CreateOrderRequest) -> anyhow::Result<OrderSummary> {
let order = Order::from(request);
self.payment.charge(order.total())?;
let saved = self.repo.save(&order)?;
Ok(OrderSummary::from(saved))
}
}
```
## Newtype Pattern for Type Safety
Prevent argument mix-ups with distinct wrapper types:
```rust
struct UserId(u64);
struct OrderId(u64);
fn get_order(user: UserId, order: OrderId) -> anyhow::Result<Order> {
// Can't accidentally swap user and order IDs at call sites
todo!()
}
```
## Enum State Machines
Model states as enums — make illegal states unrepresentable:
```rust
enum ConnectionState {
Disconnected,
Connecting { attempt: u32 },
Connected { session_id: String },
Failed { reason: String, retries: u32 },
}
fn handle(state: &ConnectionState) {
match state {
ConnectionState::Disconnected => connect(),
ConnectionState::Connecting { attempt } if *attempt > 3 => abort(),
ConnectionState::Connecting { .. } => wait(),
ConnectionState::Connected { session_id } => use_session(session_id),
ConnectionState::Failed { retries, .. } if *retries < 5 => retry(),
ConnectionState::Failed { reason, .. } => log_failure(reason),
}
}
```
Always match exhaustively — no wildcard `_` for business-critical enums.
## Builder Pattern
Use for structs with many optional parameters:
```rust
pub struct ServerConfig {
host: String,
port: u16,
max_connections: usize,
}
impl ServerConfig {
pub fn builder(host: impl Into<String>, port: u16) -> ServerConfigBuilder {
ServerConfigBuilder {
host: host.into(),
port,
max_connections: 100,
}
}
}
pub struct ServerConfigBuilder {
host: String,
port: u16,
max_connections: usize,
}
impl ServerConfigBuilder {
pub fn max_connections(mut self, n: usize) -> Self {
self.max_connections = n;
self
}
pub fn build(self) -> ServerConfig {
ServerConfig {
host: self.host,
port: self.port,
max_connections: self.max_connections,
}
}
}
```
## Sealed Traits for Extensibility Control
Use a private module to seal a trait, preventing external implementations:
```rust
mod private {
pub trait Sealed {}
}
pub trait Format: private::Sealed {
fn encode(&self, data: &[u8]) -> Vec<u8>;
}
pub struct Json;
impl private::Sealed for Json {}
impl Format for Json {
fn encode(&self, data: &[u8]) -> Vec<u8> { todo!() }
}
```
## API Response Envelope
Consistent API responses using a generic enum:
```rust
#[derive(Debug, serde::Serialize)]
#[serde(tag = "status")]
pub enum ApiResponse<T: serde::Serialize> {
#[serde(rename = "ok")]
Ok { data: T },
#[serde(rename = "error")]
Error { message: String },
}
```
## References
See skill: `rust-patterns` for comprehensive patterns including ownership, traits, generics, concurrency, and async.

View File

@@ -0,0 +1,141 @@
---
paths:
- "**/*.rs"
---
# Rust Security
> This file extends [common/security.md](../common/security.md) with Rust-specific content.
## Secrets Management
- Never hardcode API keys, tokens, or credentials in source code
- Use environment variables: `std::env::var("API_KEY")`
- Fail fast if required secrets are missing at startup
- Keep `.env` files in `.gitignore`
```rust
// BAD
const API_KEY: &str = "sk-abc123...";
// GOOD — environment variable with early validation
fn load_api_key() -> anyhow::Result<String> {
std::env::var("PAYMENT_API_KEY")
.context("PAYMENT_API_KEY must be set")
}
```
## SQL Injection Prevention
- Always use parameterized queries — never format user input into SQL strings
- Use query builder or ORM (sqlx, diesel, sea-orm) with bind parameters
```rust
// BAD — SQL injection via format string
let query = format!("SELECT * FROM users WHERE name = '{name}'");
sqlx::query(&query).fetch_one(&pool).await?;
// GOOD — parameterized query with sqlx
// Placeholder syntax varies by backend: Postgres: $1 | MySQL: ? | SQLite: $1
sqlx::query("SELECT * FROM users WHERE name = $1")
.bind(&name)
.fetch_one(&pool)
.await?;
```
## Input Validation
- Validate all user input at system boundaries before processing
- Use the type system to enforce invariants (newtype pattern)
- Parse, don't validate — convert unstructured data to typed structs at the boundary
- Reject invalid input with clear error messages
```rust
// Parse, don't validate — invalid states are unrepresentable
pub struct Email(String);
impl Email {
pub fn parse(input: &str) -> Result<Self, ValidationError> {
let trimmed = input.trim();
let at_pos = trimmed.find('@')
.filter(|&p| p > 0 && p < trimmed.len() - 1)
.ok_or_else(|| ValidationError::InvalidEmail(input.to_string()))?;
let domain = &trimmed[at_pos + 1..];
if trimmed.len() > 254 || !domain.contains('.') {
return Err(ValidationError::InvalidEmail(input.to_string()));
}
// For production use, prefer a validated email crate (e.g., `email_address`)
Ok(Self(trimmed.to_string()))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
```
## Unsafe Code
- Minimize `unsafe` blocks — prefer safe abstractions
- Every `unsafe` block must have a `// SAFETY:` comment explaining the invariant
- Never use `unsafe` to bypass the borrow checker for convenience
- Audit all `unsafe` code during review — it is a red flag without justification
- Prefer `safe` FFI wrappers around C libraries
```rust
// GOOD — safety comment documents ALL required invariants
let widget: &Widget = {
// SAFETY: `ptr` is non-null, aligned, points to an initialized Widget,
// and no mutable references or mutations exist for its lifetime.
unsafe { &*ptr }
};
// BAD — no safety justification
unsafe { &*ptr }
```
## Dependency Security
- Run `cargo audit` to scan for known CVEs in dependencies
- Run `cargo deny check` for license and advisory compliance
- Use `cargo tree` to audit transitive dependencies
- Keep dependencies updated — set up Dependabot or Renovate
- Minimize dependency count — evaluate before adding new crates
```bash
# Security audit
cargo audit
# Deny advisories, duplicate versions, and restricted licenses
cargo deny check
# Inspect dependency tree
cargo tree
cargo tree -d # Show duplicates only
```
## Error Messages
- Never expose internal paths, stack traces, or database errors in API responses
- Log detailed errors server-side; return generic messages to clients
- Use `tracing` or `log` for structured server-side logging
```rust
// Map errors to appropriate status codes and generic messages
// (Example uses axum; adapt the response type to your framework)
match order_service.find_by_id(id) {
Ok(order) => Ok((StatusCode::OK, Json(order))),
Err(ServiceError::NotFound(_)) => {
tracing::info!(order_id = id, "order not found");
Err((StatusCode::NOT_FOUND, "Resource not found"))
}
Err(e) => {
tracing::error!(order_id = id, error = %e, "unexpected error");
Err((StatusCode::INTERNAL_SERVER_ERROR, "Internal server error"))
}
}
```
## References
See skill: `rust-patterns` for unsafe code guidelines and ownership patterns.
See skill: `security-review` for general security checklists.

View File

@@ -0,0 +1,154 @@
---
paths:
- "**/*.rs"
---
# Rust Testing
> This file extends [common/testing.md](../common/testing.md) with Rust-specific content.
## Test Framework
- **`#[test]`** with `#[cfg(test)]` modules for unit tests
- **rstest** for parameterized tests and fixtures
- **proptest** for property-based testing
- **mockall** for trait-based mocking
- **`#[tokio::test]`** for async tests
## Test Organization
```text
my_crate/
├── src/
│ ├── lib.rs # Unit tests in #[cfg(test)] modules
│ ├── auth/
│ │ └── mod.rs # #[cfg(test)] mod tests { ... }
│ └── orders/
│ └── service.rs # #[cfg(test)] mod tests { ... }
├── tests/ # Integration tests (each file = separate binary)
│ ├── api_test.rs
│ ├── db_test.rs
│ └── common/ # Shared test utilities
│ └── mod.rs
└── benches/ # Criterion benchmarks
└── benchmark.rs
```
Unit tests go inside `#[cfg(test)]` modules in the same file. Integration tests go in `tests/`.
## Unit Test Pattern
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn creates_user_with_valid_email() {
let user = User::new("Alice", "alice@example.com").unwrap();
assert_eq!(user.name, "Alice");
}
#[test]
fn rejects_invalid_email() {
let result = User::new("Bob", "not-an-email");
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("invalid email"));
}
}
```
## Parameterized Tests
```rust
use rstest::rstest;
#[rstest]
#[case("hello", 5)]
#[case("", 0)]
#[case("rust", 4)]
fn test_string_length(#[case] input: &str, #[case] expected: usize) {
assert_eq!(input.len(), expected);
}
```
## Async Tests
```rust
#[tokio::test]
async fn fetches_data_successfully() {
let client = TestClient::new().await;
let result = client.get("/data").await;
assert!(result.is_ok());
}
```
## Mocking with mockall
Define traits in production code; generate mocks in test modules:
```rust
// Production trait — pub so integration tests can import it
pub trait UserRepository {
fn find_by_id(&self, id: u64) -> Option<User>;
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::eq;
mockall::mock! {
pub Repo {}
impl UserRepository for Repo {
fn find_by_id(&self, id: u64) -> Option<User>;
}
}
#[test]
fn service_returns_user_when_found() {
let mut mock = MockRepo::new();
mock.expect_find_by_id()
.with(eq(42))
.times(1)
.returning(|_| Some(User { id: 42, name: "Alice".into() }));
let service = UserService::new(Box::new(mock));
let user = service.get_user(42).unwrap();
assert_eq!(user.name, "Alice");
}
}
```
## Test Naming
Use descriptive names that explain the scenario:
- `creates_user_with_valid_email()`
- `rejects_order_when_insufficient_stock()`
- `returns_none_when_not_found()`
## Coverage
- Target 80%+ line coverage
- Use **cargo-llvm-cov** for coverage reporting
- Focus on business logic — exclude generated code and FFI bindings
```bash
cargo llvm-cov # Summary
cargo llvm-cov --html # HTML report
cargo llvm-cov --fail-under-lines 80 # Fail if below threshold
```
## Testing Commands
```bash
cargo test # Run all tests
cargo test -- --nocapture # Show println output
cargo test test_name # Run tests matching pattern
cargo test --lib # Unit tests only
cargo test --test api_test # Specific integration test (tests/api_test.rs)
cargo test --doc # Doc tests only
```
## References
See skill: `rust-testing` for comprehensive testing patterns including property-based testing, fixtures, and benchmarking with Criterion.

View File

@@ -0,0 +1,47 @@
---
paths:
- "**/*.swift"
- "**/Package.swift"
---
# Swift Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with Swift specific content.
## Formatting
- **SwiftFormat** for auto-formatting, **SwiftLint** for style enforcement
- `swift-format` is bundled with Xcode 16+ as an alternative
## Immutability
- Prefer `let` over `var` — define everything as `let` and only change to `var` if the compiler requires it
- Use `struct` with value semantics by default; use `class` only when identity or reference semantics are needed
## Naming
Follow [Apple API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/):
- Clarity at the point of use — omit needless words
- Name methods and properties for their roles, not their types
- Use `static let` for constants over global constants
## Error Handling
Use typed throws (Swift 6+) and pattern matching:
```swift
func load(id: String) throws(LoadError) -> Item {
guard let data = try? read(from: path) else {
throw .fileNotFound(id)
}
return try decode(data)
}
```
## Concurrency
Enable Swift 6 strict concurrency checking. Prefer:
- `Sendable` value types for data crossing isolation boundaries
- Actors for shared mutable state
- Structured concurrency (`async let`, `TaskGroup`) over unstructured `Task {}`

View File

@@ -0,0 +1,20 @@
---
paths:
- "**/*.swift"
- "**/Package.swift"
---
# Swift Hooks
> This file extends [common/hooks.md](../common/hooks.md) with Swift specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **SwiftFormat**: Auto-format `.swift` files after edit
- **SwiftLint**: Run lint checks after editing `.swift` files
- **swift build**: Type-check modified packages after edit
## Warning
Flag `print()` statements — use `os.Logger` or structured logging instead for production code.

View File

@@ -0,0 +1,66 @@
---
paths:
- "**/*.swift"
- "**/Package.swift"
---
# Swift Patterns
> This file extends [common/patterns.md](../common/patterns.md) with Swift specific content.
## Protocol-Oriented Design
Define small, focused protocols. Use protocol extensions for shared defaults:
```swift
protocol Repository: Sendable {
associatedtype Item: Identifiable & Sendable
func find(by id: Item.ID) async throws -> Item?
func save(_ item: Item) async throws
}
```
## Value Types
- Use structs for data transfer objects and models
- Use enums with associated values to model distinct states:
```swift
enum LoadState<T: Sendable>: Sendable {
case idle
case loading
case loaded(T)
case failed(Error)
}
```
## Actor Pattern
Use actors for shared mutable state instead of locks or dispatch queues:
```swift
actor Cache<Key: Hashable & Sendable, Value: Sendable> {
private var storage: [Key: Value] = [:]
func get(_ key: Key) -> Value? { storage[key] }
func set(_ key: Key, value: Value) { storage[key] = value }
}
```
## Dependency Injection
Inject protocols with default parameters — production uses defaults, tests inject mocks:
```swift
struct UserService {
private let repository: any UserRepository
init(repository: any UserRepository = DefaultUserRepository()) {
self.repository = repository
}
}
```
## References
See skill: `swift-actor-persistence` for actor-based persistence patterns.
See skill: `swift-protocol-di-testing` for protocol-based DI and testing.

View File

@@ -0,0 +1,33 @@
---
paths:
- "**/*.swift"
- "**/Package.swift"
---
# Swift Security
> This file extends [common/security.md](../common/security.md) with Swift specific content.
## Secret Management
- Use **Keychain Services** for sensitive data (tokens, passwords, keys) — never `UserDefaults`
- Use environment variables or `.xcconfig` files for build-time secrets
- Never hardcode secrets in source — decompilation tools extract them trivially
```swift
let apiKey = ProcessInfo.processInfo.environment["API_KEY"]
guard let apiKey, !apiKey.isEmpty else {
fatalError("API_KEY not configured")
}
```
## Transport Security
- App Transport Security (ATS) is enforced by default — do not disable it
- Use certificate pinning for critical endpoints
- Validate all server certificates
## Input Validation
- Sanitize all user input before display to prevent injection
- Use `URL(string:)` with validation rather than force-unwrapping
- Validate data from external sources (APIs, deep links, pasteboard) before processing

View File

@@ -0,0 +1,45 @@
---
paths:
- "**/*.swift"
- "**/Package.swift"
---
# Swift Testing
> This file extends [common/testing.md](../common/testing.md) with Swift specific content.
## Framework
Use **Swift Testing** (`import Testing`) for new tests. Use `@Test` and `#expect`:
```swift
@Test("User creation validates email")
func userCreationValidatesEmail() throws {
#expect(throws: ValidationError.invalidEmail) {
try User(email: "not-an-email")
}
}
```
## Test Isolation
Each test gets a fresh instance — set up in `init`, tear down in `deinit`. No shared mutable state between tests.
## Parameterized Tests
```swift
@Test("Validates formats", arguments: ["json", "xml", "csv"])
func validatesFormat(format: String) throws {
let parser = try Parser(format: format)
#expect(parser.isValid)
}
```
## Coverage
```bash
swift test --enable-code-coverage
```
## Reference
See skill: `swift-protocol-di-testing` for protocol-based dependency injection and mock patterns with Swift Testing.

View File

@@ -0,0 +1,199 @@
---
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.js"
- "**/*.jsx"
---
# TypeScript/JavaScript Coding Style
> This file extends [common/coding-style.md](../common/coding-style.md) with TypeScript/JavaScript specific content.
## Types and Interfaces
Use types to make public APIs, shared models, and component props explicit, readable, and reusable.
### Public APIs
- Add parameter and return types to exported functions, shared utilities, and public class methods
- Let TypeScript infer obvious local variable types
- Extract repeated inline object shapes into named types or interfaces
```typescript
// WRONG: Exported function without explicit types
export function formatUser(user) {
return `${user.firstName} ${user.lastName}`
}
// CORRECT: Explicit types on public APIs
interface User {
firstName: string
lastName: string
}
export function formatUser(user: User): string {
return `${user.firstName} ${user.lastName}`
}
```
### Interfaces vs. Type Aliases
- Use `interface` for object shapes that may be extended or implemented
- Use `type` for unions, intersections, tuples, mapped types, and utility types
- Prefer string literal unions over `enum` unless an `enum` is required for interoperability
```typescript
interface User {
id: string
email: string
}
type UserRole = 'admin' | 'member'
type UserWithRole = User & {
role: UserRole
}
```
### Avoid `any`
- Avoid `any` in application code
- Use `unknown` for external or untrusted input, then narrow it safely
- Use generics when a value's type depends on the caller
```typescript
// WRONG: any removes type safety
function getErrorMessage(error: any) {
return error.message
}
// CORRECT: unknown forces safe narrowing
function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message
}
return 'Unexpected error'
}
```
### React Props
- Define component props with a named `interface` or `type`
- Type callback props explicitly
- Do not use `React.FC` unless there is a specific reason to do so
```typescript
interface User {
id: string
email: string
}
interface UserCardProps {
user: User
onSelect: (id: string) => void
}
function UserCard({ user, onSelect }: UserCardProps) {
return <button onClick={() => onSelect(user.id)}>{user.email}</button>
}
```
### JavaScript Files
- In `.js` and `.jsx` files, use JSDoc when types improve clarity and a TypeScript migration is not practical
- Keep JSDoc aligned with runtime behavior
```javascript
/**
* @param {{ firstName: string, lastName: string }} user
* @returns {string}
*/
export function formatUser(user) {
return `${user.firstName} ${user.lastName}`
}
```
## Immutability
Use spread operator for immutable updates:
```typescript
interface User {
id: string
name: string
}
// WRONG: Mutation
function updateUser(user: User, name: string): User {
user.name = name // MUTATION!
return user
}
// CORRECT: Immutability
function updateUser(user: Readonly<User>, name: string): User {
return {
...user,
name
}
}
```
## Error Handling
Use async/await with try-catch and narrow unknown errors safely:
```typescript
interface User {
id: string
email: string
}
declare function riskyOperation(userId: string): Promise<User>
function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message
}
return 'Unexpected error'
}
const logger = {
error: (message: string, error: unknown) => {
// Replace with your production logger (for example, pino or winston).
}
}
async function loadUser(userId: string): Promise<User> {
try {
const result = await riskyOperation(userId)
return result
} catch (error: unknown) {
logger.error('Operation failed', error)
throw new Error(getErrorMessage(error))
}
}
```
## Input Validation
Use Zod for schema-based validation and infer types from the schema:
```typescript
import { z } from 'zod'
const userSchema = z.object({
email: z.string().email(),
age: z.number().int().min(0).max(150)
})
type UserInput = z.infer<typeof userSchema>
const validated: UserInput = userSchema.parse(input)
```
## Console.log
- No `console.log` statements in production code
- Use proper logging libraries instead
- See hooks for automatic detection

View File

@@ -0,0 +1,22 @@
---
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.js"
- "**/*.jsx"
---
# TypeScript/JavaScript Hooks
> This file extends [common/hooks.md](../common/hooks.md) with TypeScript/JavaScript specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **Prettier**: Auto-format JS/TS files after edit
- **TypeScript check**: Run `tsc` after editing `.ts`/`.tsx` files
- **console.log warning**: Warn about `console.log` in edited files
## Stop Hooks
- **console.log audit**: Check all modified files for `console.log` before session ends

View File

@@ -0,0 +1,52 @@
---
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.js"
- "**/*.jsx"
---
# TypeScript/JavaScript Patterns
> This file extends [common/patterns.md](../common/patterns.md) with TypeScript/JavaScript specific content.
## API Response Format
```typescript
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
meta?: {
total: number
page: number
limit: number
}
}
```
## Custom Hooks Pattern
```typescript
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const handler = setTimeout(() => setDebouncedValue(value), delay)
return () => clearTimeout(handler)
}, [value, delay])
return debouncedValue
}
```
## Repository Pattern
```typescript
interface Repository<T> {
findAll(filters?: Filters): Promise<T[]>
findById(id: string): Promise<T | null>
create(data: CreateDto): Promise<T>
update(id: string, data: UpdateDto): Promise<T>
delete(id: string): Promise<void>
}
```

View File

@@ -0,0 +1,28 @@
---
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.js"
- "**/*.jsx"
---
# TypeScript/JavaScript Security
> This file extends [common/security.md](../common/security.md) with TypeScript/JavaScript specific content.
## Secret Management
```typescript
// NEVER: Hardcoded secrets
const apiKey = "sk-proj-xxxxx"
// ALWAYS: Environment variables
const apiKey = process.env.OPENAI_API_KEY
if (!apiKey) {
throw new Error('OPENAI_API_KEY not configured')
}
```
## Agent Support
- Use **security-reviewer** skill for comprehensive security audits

View File

@@ -0,0 +1,18 @@
---
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.js"
- "**/*.jsx"
---
# TypeScript/JavaScript Testing
> This file extends [common/testing.md](../common/testing.md) with TypeScript/JavaScript specific content.
## E2E Testing
Use **Playwright** as the E2E testing framework for critical user flows.
## Agent Support
- **e2e-runner** - Playwright E2E testing specialist