feat(01-02): codebase map templates for conventions and testing

- conventions.md: prescriptive coding standards, naming, style, error handling
- testing.md: test framework patterns, structure, mocking, coverage, commands
This commit is contained in:
Lex Christopherson
2025-12-17 09:50:30 -06:00
parent de16552e53
commit 33c832e782
2 changed files with 787 additions and 0 deletions

View File

@@ -0,0 +1,307 @@
# Coding Conventions Template
Template for `.planning/codebase/CONVENTIONS.md` - captures coding style and patterns.
**Purpose:** Document how code is written in this codebase. Prescriptive guide for Claude to match existing style.
---
## File Template
```markdown
# Coding Conventions
**Analysis Date:** [YYYY-MM-DD]
## Naming Patterns
**Files:**
- [Pattern: e.g., "kebab-case for all files"]
- [Test files: e.g., "*.test.ts alongside source"]
- [Components: e.g., "PascalCase.tsx for React components"]
**Functions:**
- [Pattern: e.g., "camelCase for all functions"]
- [Async: e.g., "no special prefix for async functions"]
- [Handlers: e.g., "handleEventName for event handlers"]
**Variables:**
- [Pattern: e.g., "camelCase for variables"]
- [Constants: e.g., "UPPER_SNAKE_CASE for constants"]
- [Private: e.g., "_prefix for private members" or "no prefix"]
**Types:**
- [Interfaces: e.g., "PascalCase, no I prefix"]
- [Types: e.g., "PascalCase for type aliases"]
- [Enums: e.g., "PascalCase for enum name, UPPER_CASE for values"]
## Code Style
**Formatting:**
- [Tool: e.g., "Prettier with config in .prettierrc"]
- [Line length: e.g., "100 characters max"]
- [Quotes: e.g., "single quotes for strings"]
- [Semicolons: e.g., "required" or "omitted"]
**Linting:**
- [Tool: e.g., "ESLint with eslint.config.js"]
- [Rules: e.g., "extends airbnb-base, no console in production"]
- [Run: e.g., "npm run lint"]
## Import Organization
**Order:**
1. [e.g., "External packages (react, express, etc.)"]
2. [e.g., "Internal modules (@/lib, @/components)"]
3. [e.g., "Relative imports (., ..)"]
4. [e.g., "Type imports (import type {})"]
**Grouping:**
- [Blank lines: e.g., "blank line between groups"]
- [Sorting: e.g., "alphabetical within each group"]
**Path Aliases:**
- [Aliases used: e.g., "@/ for src/, @components/ for src/components/"]
## Error Handling
**Patterns:**
- [Strategy: e.g., "throw errors, catch at boundaries"]
- [Custom errors: e.g., "extend Error class, named *Error"]
- [Async: e.g., "use try/catch, no .catch() chains"]
**Error Types:**
- [When to throw: e.g., "invalid input, missing dependencies"]
- [When to return: e.g., "expected failures return Result<T, E>"]
- [Logging: e.g., "log error with context before throwing"]
## Logging
**Framework:**
- [Tool: e.g., "console.log, pino, winston"]
- [Levels: e.g., "debug, info, warn, error"]
**Patterns:**
- [Format: e.g., "structured logging with context object"]
- [When: e.g., "log state transitions, external calls"]
- [Where: e.g., "log at service boundaries, not in utils"]
## Comments
**When to Comment:**
- [e.g., "explain why, not what"]
- [e.g., "document business logic, algorithms, edge cases"]
- [e.g., "avoid obvious comments like // increment counter"]
**JSDoc/TSDoc:**
- [Usage: e.g., "required for public APIs, optional for internal"]
- [Format: e.g., "use @param, @returns, @throws tags"]
**TODO Comments:**
- [Pattern: e.g., "// TODO(username): description"]
- [Tracking: e.g., "link to issue number if available"]
## Function Design
**Size:**
- [e.g., "keep under 50 lines, extract helpers"]
**Parameters:**
- [e.g., "max 3 parameters, use object for more"]
- [e.g., "destructure objects in parameter list"]
**Return Values:**
- [e.g., "explicit returns, no implicit undefined"]
- [e.g., "return early for guard clauses"]
## Module Design
**Exports:**
- [e.g., "named exports preferred, default exports for React components"]
- [e.g., "export from index.ts for public API"]
**Barrel Files:**
- [e.g., "use index.ts to re-export public API"]
- [e.g., "avoid circular dependencies"]
---
*Convention analysis: [date]*
*Update when patterns change*
```
<good_examples>
```markdown
# Coding Conventions
**Analysis Date:** 2025-01-20
## Naming Patterns
**Files:**
- kebab-case for all files (command-handler.ts, user-service.ts)
- *.test.ts alongside source files
- index.ts for barrel exports
**Functions:**
- camelCase for all functions
- No special prefix for async functions
- handleEventName for event handlers (handleClick, handleSubmit)
**Variables:**
- camelCase for variables
- UPPER_SNAKE_CASE for constants (MAX_RETRIES, API_BASE_URL)
- No underscore prefix (no private marker in TS)
**Types:**
- PascalCase for interfaces, no I prefix (User, not IUser)
- PascalCase for type aliases (UserConfig, ResponseData)
- PascalCase for enum names, UPPER_CASE for values (Status.PENDING)
## Code Style
**Formatting:**
- Prettier with .prettierrc
- 100 character line length
- Single quotes for strings
- Semicolons required
- 2 space indentation
**Linting:**
- ESLint with eslint.config.js
- Extends @typescript-eslint/recommended
- No console.log in production code (use logger)
- Run: npm run lint
## Import Organization
**Order:**
1. External packages (react, express, commander)
2. Internal modules (@/lib, @/services)
3. Relative imports (./utils, ../types)
4. Type imports (import type { User })
**Grouping:**
- Blank line between groups
- Alphabetical within each group
- Type imports last within each group
**Path Aliases:**
- @/ maps to src/
- No other aliases defined
## Error Handling
**Patterns:**
- Throw errors, catch at boundaries (route handlers, main functions)
- Extend Error class for custom errors (ValidationError, NotFoundError)
- Async functions use try/catch, no .catch() chains
**Error Types:**
- Throw on invalid input, missing dependencies, invariant violations
- Log error with context before throwing: logger.error({ err, userId }, 'Failed to process')
- Include cause in error message: new Error('Failed to X', { cause: originalError })
## Logging
**Framework:**
- pino logger instance exported from lib/logger.ts
- Levels: debug, info, warn, error (no trace)
**Patterns:**
- Structured logging with context: logger.info({ userId, action }, 'User action')
- Log at service boundaries, not in utility functions
- Log state transitions, external API calls, errors
- No console.log in committed code
## Comments
**When to Comment:**
- Explain why, not what: // Retry 3 times because API has transient failures
- Document business rules: // Users must verify email within 24 hours
- Explain non-obvious algorithms or workarounds
- Avoid obvious comments: // set count to 0
**JSDoc/TSDoc:**
- Required for public API functions
- Optional for internal functions if signature is self-explanatory
- Use @param, @returns, @throws tags
**TODO Comments:**
- Format: // TODO: description (no username, using git blame)
- Link to issue if exists: // TODO: Fix race condition (issue #123)
## Function Design
**Size:**
- Keep under 50 lines
- Extract helpers for complex logic
- One level of abstraction per function
**Parameters:**
- Max 3 parameters
- Use options object for 4+ parameters: function create(options: CreateOptions)
- Destructure in parameter list: function process({ id, name }: ProcessParams)
**Return Values:**
- Explicit return statements
- Return early for guard clauses
- Use Result<T, E> type for expected failures
## Module Design
**Exports:**
- Named exports preferred
- Default exports only for React components
- Export public API from index.ts barrel files
**Barrel Files:**
- index.ts re-exports public API
- Keep internal helpers private (don't export from index)
- Avoid circular dependencies (import from specific files if needed)
---
*Convention analysis: 2025-01-20*
*Update when patterns change*
```
</good_examples>
<guidelines>
**What belongs in CONVENTIONS.md:**
- Naming patterns observed in the codebase
- Formatting rules (Prettier config, linting rules)
- Import organization patterns
- Error handling strategy
- Logging approach
- Comment conventions
- Function and module design patterns
**What does NOT belong here:**
- Architecture decisions (that's ARCHITECTURE.md)
- Technology choices (that's STACK.md)
- Test patterns (that's TESTING.md)
- File organization (that's STRUCTURE.md)
**When filling this template:**
- Check .prettierrc, .eslintrc, or similar config files
- Examine 5-10 representative source files for patterns
- Look for consistency: if 80%+ follows a pattern, document it
- Be prescriptive: "Use X" not "Sometimes Y is used"
- Note deviations: "Legacy code uses Y, new code should use X"
- Keep under ~150 lines total
**Useful for phase planning when:**
- Writing new code (match existing style)
- Adding features (follow naming patterns)
- Refactoring (apply consistent conventions)
- Code review (check against documented patterns)
- Onboarding (understand style expectations)
**Analysis approach:**
- Scan src/ directory for file naming patterns
- Check package.json scripts for lint/format commands
- Read 5-10 files to identify function naming, error handling
- Look for config files (.prettierrc, eslint.config.js)
- Note patterns in imports, comments, function signatures
</guidelines>

View File

@@ -0,0 +1,480 @@
# Testing Patterns Template
Template for `.planning/codebase/TESTING.md` - captures test framework and patterns.
**Purpose:** Document how tests are written and run. Guide for adding tests that match existing patterns.
---
## File Template
```markdown
# Testing Patterns
**Analysis Date:** [YYYY-MM-DD]
## Test Framework
**Runner:**
- [Framework: e.g., "Jest 29.x", "Vitest 1.x"]
- [Config: e.g., "jest.config.js in project root"]
**Assertion Library:**
- [Library: e.g., "built-in expect", "chai"]
- [Matchers: e.g., "toBe, toEqual, toThrow"]
**Run Commands:**
```bash
[e.g., "npm test" or "npm run test"] # Run all tests
[e.g., "npm test -- --watch"] # Watch mode
[e.g., "npm test -- path/to/file.test.ts"] # Single file
[e.g., "npm run test:coverage"] # Coverage report
```
## Test File Organization
**Location:**
- [Pattern: e.g., "*.test.ts alongside source files"]
- [Alternative: e.g., "__tests__/ directory" or "separate tests/ tree"]
**Naming:**
- [Unit tests: e.g., "module-name.test.ts"]
- [Integration: e.g., "feature-name.integration.test.ts"]
- [E2E: e.g., "user-flow.e2e.test.ts"]
**Structure:**
```
[Show actual directory pattern, e.g.:
src/
lib/
utils.ts
utils.test.ts
services/
user-service.ts
user-service.test.ts
]
```
## Test Structure
**Suite Organization:**
```typescript
[Show actual pattern used, e.g.:
describe('ModuleName', () => {
describe('functionName', () => {
it('should handle success case', () => {
// arrange
// act
// assert
});
it('should handle error case', () => {
// test code
});
});
});
]
```
**Patterns:**
- [Setup: e.g., "beforeEach for shared setup, avoid beforeAll"]
- [Teardown: e.g., "afterEach to clean up, restore mocks"]
- [Structure: e.g., "arrange/act/assert pattern required"]
## Mocking
**Framework:**
- [Tool: e.g., "Jest built-in mocking", "Vitest vi", "Sinon"]
- [Import mocking: e.g., "vi.mock() at top of file"]
**Patterns:**
```typescript
[Show actual mocking pattern, e.g.:
// Mock external dependency
vi.mock('./external-service', () => ({
fetchData: vi.fn()
}));
// Mock in test
const mockFetch = vi.mocked(fetchData);
mockFetch.mockResolvedValue({ data: 'test' });
]
```
**What to Mock:**
- [e.g., "External APIs, file system, database"]
- [e.g., "Time/dates (use vi.useFakeTimers)"]
- [e.g., "Network calls (use mock fetch)"]
**What NOT to Mock:**
- [e.g., "Pure functions, utilities"]
- [e.g., "Internal business logic"]
## Fixtures and Factories
**Test Data:**
```typescript
[Show pattern for creating test data, e.g.:
// Factory pattern
function createTestUser(overrides?: Partial<User>): User {
return {
id: 'test-id',
name: 'Test User',
email: 'test@example.com',
...overrides
};
}
// Fixture file
// tests/fixtures/users.ts
export const mockUsers = [/* ... */];
]
```
**Location:**
- [e.g., "tests/fixtures/ for shared fixtures"]
- [e.g., "factory functions in test file or tests/factories/"]
## Coverage
**Requirements:**
- [Target: e.g., "80% line coverage", "no specific target"]
- [Enforcement: e.g., "CI blocks <80%", "coverage for awareness only"]
**Configuration:**
- [Tool: e.g., "built-in coverage via --coverage flag"]
- [Exclusions: e.g., "exclude *.test.ts, config files"]
**View Coverage:**
```bash
[e.g., "npm run test:coverage"]
[e.g., "open coverage/index.html"]
```
## Test Types
**Unit Tests:**
- [Scope: e.g., "test single function/class in isolation"]
- [Mocking: e.g., "mock all external dependencies"]
- [Speed: e.g., "must run in <1s per test"]
**Integration Tests:**
- [Scope: e.g., "test multiple modules together"]
- [Mocking: e.g., "mock external services, use real internal modules"]
- [Setup: e.g., "use test database, seed data"]
**E2E Tests:**
- [Framework: e.g., "Playwright for E2E"]
- [Scope: e.g., "test full user flows"]
- [Location: e.g., "e2e/ directory separate from unit tests"]
## Common Patterns
**Async Testing:**
```typescript
[Show pattern, e.g.:
it('should handle async operation', async () => {
const result = await asyncFunction();
expect(result).toBe('expected');
});
]
```
**Error Testing:**
```typescript
[Show pattern, e.g.:
it('should throw on invalid input', () => {
expect(() => functionCall()).toThrow('error message');
});
// Async error
it('should reject on failure', async () => {
await expect(asyncCall()).rejects.toThrow('error message');
});
]
```
**Snapshot Testing:**
- [Usage: e.g., "for React components only" or "not used"]
- [Location: e.g., "__snapshots__/ directory"]
---
*Testing analysis: [date]*
*Update when test patterns change*
```
<good_examples>
```markdown
# Testing Patterns
**Analysis Date:** 2025-01-20
## Test Framework
**Runner:**
- Vitest 1.0.4
- Config: vitest.config.ts in project root
**Assertion Library:**
- Vitest built-in expect
- Matchers: toBe, toEqual, toThrow, toMatchObject
**Run Commands:**
```bash
npm test # Run all tests
npm test -- --watch # Watch mode
npm test -- path/to/file.test.ts # Single file
npm run test:coverage # Coverage report
```
## Test File Organization
**Location:**
- *.test.ts alongside source files
- No separate tests/ directory
**Naming:**
- unit-name.test.ts for all tests
- No distinction between unit/integration in filename
**Structure:**
```
src/
lib/
parser.ts
parser.test.ts
services/
install-service.ts
install-service.test.ts
bin/
install.ts
(no test - integration tested via CLI)
```
## Test Structure
**Suite Organization:**
```typescript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
describe('ModuleName', () => {
describe('functionName', () => {
beforeEach(() => {
// reset state
});
it('should handle valid input', () => {
// arrange
const input = createTestInput();
// act
const result = functionName(input);
// assert
expect(result).toEqual(expectedOutput);
});
it('should throw on invalid input', () => {
expect(() => functionName(null)).toThrow('Invalid input');
});
});
});
```
**Patterns:**
- Use beforeEach for per-test setup, avoid beforeAll
- Use afterEach to restore mocks: vi.restoreAllMocks()
- Explicit arrange/act/assert comments in complex tests
- One assertion focus per test (but multiple expects OK)
## Mocking
**Framework:**
- Vitest built-in mocking (vi)
- Module mocking via vi.mock() at top of test file
**Patterns:**
```typescript
import { vi } from 'vitest';
import { externalFunction } from './external';
// Mock module
vi.mock('./external', () => ({
externalFunction: vi.fn()
}));
describe('test suite', () => {
it('mocks function', () => {
const mockFn = vi.mocked(externalFunction);
mockFn.mockReturnValue('mocked result');
// test code using mocked function
expect(mockFn).toHaveBeenCalledWith('expected arg');
});
});
```
**What to Mock:**
- File system operations (fs-extra)
- Child process execution (child_process.exec)
- External API calls
- Environment variables (process.env)
**What NOT to Mock:**
- Internal pure functions
- Simple utilities (string manipulation, array helpers)
- TypeScript types
## Fixtures and Factories
**Test Data:**
```typescript
// Factory functions in test file
function createTestConfig(overrides?: Partial<Config>): Config {
return {
targetDir: '/tmp/test',
global: false,
...overrides
};
}
// Shared fixtures in tests/fixtures/
// tests/fixtures/sample-command.md
export const sampleCommand = `---
description: Test command
---
Content here`;
```
**Location:**
- Factory functions: define in test file near usage
- Shared fixtures: tests/fixtures/ (for multi-file test data)
- Mock data: inline in test when simple, factory when complex
## Coverage
**Requirements:**
- No enforced coverage target
- Coverage tracked for awareness
- Focus on critical paths (parsers, service logic)
**Configuration:**
- Vitest coverage via c8 (built-in)
- Excludes: *.test.ts, bin/install.ts, config files
**View Coverage:**
```bash
npm run test:coverage
open coverage/index.html
```
## Test Types
**Unit Tests:**
- Test single function in isolation
- Mock all external dependencies (fs, child_process)
- Fast: each test <100ms
- Examples: parser.test.ts, validator.test.ts
**Integration Tests:**
- Test multiple modules together
- Mock only external boundaries (file system, process)
- Examples: install-service.test.ts (tests service + parser)
**E2E Tests:**
- Not currently used
- CLI integration tested manually
## Common Patterns
**Async Testing:**
```typescript
it('should handle async operation', async () => {
const result = await asyncFunction();
expect(result).toBe('expected');
});
```
**Error Testing:**
```typescript
it('should throw on invalid input', () => {
expect(() => parse(null)).toThrow('Cannot parse null');
});
// Async error
it('should reject on file not found', async () => {
await expect(readConfig('invalid.txt')).rejects.toThrow('ENOENT');
});
```
**File System Mocking:**
```typescript
import { vi } from 'vitest';
import * as fs from 'fs-extra';
vi.mock('fs-extra');
it('mocks file system', () => {
vi.mocked(fs.readFile).mockResolvedValue('file content');
// test code
});
```
**Snapshot Testing:**
- Not used in this codebase
- Prefer explicit assertions for clarity
---
*Testing analysis: 2025-01-20*
*Update when test patterns change*
```
</good_examples>
<guidelines>
**What belongs in TESTING.md:**
- Test framework and runner configuration
- Test file location and naming patterns
- Test structure (describe/it, beforeEach patterns)
- Mocking approach and examples
- Fixture/factory patterns
- Coverage requirements
- How to run tests (commands)
- Common testing patterns in actual code
**What does NOT belong here:**
- Specific test cases (defer to actual test files)
- Technology choices (that's STACK.md)
- CI/CD setup (that's deployment docs)
**When filling this template:**
- Check package.json scripts for test commands
- Find test config file (jest.config.js, vitest.config.ts)
- Read 3-5 existing test files to identify patterns
- Look for test utilities in tests/ or test-utils/
- Check for coverage configuration
- Document actual patterns used, not ideal patterns
**Useful for phase planning when:**
- Adding new features (write matching tests)
- Refactoring (maintain test patterns)
- Fixing bugs (add regression tests)
- Understanding verification approach
- Setting up test infrastructure
**Analysis approach:**
- Check package.json for test framework and scripts
- Read test config file for coverage, setup
- Examine test file organization (collocated vs separate)
- Review 5 test files for patterns (mocking, structure, assertions)
- Look for test utilities, fixtures, factories
- Note any test types (unit, integration, e2e)
- Document commands for running tests
</guidelines>