- Be extremely concise. Sacrifice grammar for concision.
- Avoid unnecessary words and tangential points. Stay on the question.
- When asking the user one or more questions, enumerate them (1., 2., 3.) so each can be answered individually. For decision points, list options under each question:
1. Which approach? - Option A: ... - Option B: ... 2. Proceed? (yes/no) - Favor simple real code examples over long written explanations.
- When discussing data, use schemas to illustrate shapes and types rigorously.
- When discussing functionality, use function signatures and diagrams to illustrate flow.
- Diagrams by surface:
- Persistent docs (markdown files, Obsidian, READMEs, PRs, issues): mermaid — renders to SVG
- Conversation / terminal / inline explanations: ASCII — renders natively, no viewer required
- Never put mermaid in a chat reply where the user will read it as raw text.
- ASCII conventions:
Boxes: [Client] ──REST──> [API] ──SQL──> [(DB)] Sequence: Client → API: POST /login API → DB: validate DB → API: user API → Client: {token} Tree: root ├── child-a │ └── grandchild └── child-b
- Always plan solution and confirm before executing.
- Design code interfaces and confirm BEFORE writing code.
- Database migrations / schema changes last — defer until implementation complete.
Do not proactively add "defense in depth," "belt and suspenders," fallbacks, retries, redundant validation, or backward-compatibility shims unless I explicitly ask for them. Build exactly what was requested — nothing more.
- No try/catch around code that can't fail, or that should fail loudly.
- No fallback values for inputs that should never be missing — let it throw.
- No coercing null/undefined to a semantically meaningful default (e.g.
value ?? 0,count ?? 0,?? '',?? []). When 0 (or'',[]) is a real value in the domain — numerical analysis, counts, ratios, aggregations — substituting it for absent data fabricates ground truth. Propagatenullor throw; let the caller decide. - No "just in case" null checks layered on top of types/schemas that already guarantee shape.
- No compat aliases, deprecated re-exports, or dual code paths for "old callers" — change the code.
If you think hardening is genuinely warranted, ask first with a one-line justification. Default is: do less.
- Gitleaks runs as a pre-commit hook on all repos. Treat it as a hard gate, not advisory.
- If gitleaks blocks a commit: investigate the finding, never bypass with
--no-verify. - New repos should have
.gitleaks.tomland apre-commithook (.husky/pre-commitor.git/hooks/pre-commit) installed.
- Ask permission for sensitive files: Never read
.env,.env.*, config files with secrets, or credential files without explicit user permission. - Respect privacy boundaries: Ask before accessing files that may contain API keys, passwords, or personal data.
- Safe exploration: Use file listings and search tools to understand project structure before requesting access to sensitive files.
- Be careful not to introduce command injection, XSS, SQL injection, or other OWASP Top 10 vulnerabilities.
- Validate untrusted input at I/O boundaries with schemas (see Schemas section).
- How things work: Logic, flow, and purpose of code
- Why decisions were made: Architectural choices and trade-offs
- Configuration details: Required settings, env vars, constants
- Usage examples: How to use APIs, components, utilities
- Security considerations: Auth, authorization, data protection
- Performance optimizations: Caching, polling intervals, efficiency
- Error handling: Error types, codes, recovery strategies
- Type definitions: Complex types and their purposes
- Business logic: Domain-specific rules and workflows
- Removal notes: Don't mention what was deleted, removed, or replaced
- Historical changes: Don't reference previous implementations
- Refactoring notes: Don't explain what used to exist
- Cleanup comments: Don't note that something was "cleaned up" or "simplified"
- Migration notes: Don't reference old patterns or deprecated methods
// Removed the old caching mechanism in favor of context state
// This used to call getLinkedPRsForIssue but now uses context
// Deleted the redundant API call
// No longer needed after refactoring// Fetches PR data from the context state which is updated by polling
// Uses JWT tokens for secure state management during OAuth flows
// Polls every 30 seconds to detect PR changes
// Returns error with code so caller can handle appropriatelyWhen explaining flow, architecture, or state machines, favor diagrams over multi-paragraph prose.
In persistent docs — use mermaid:
sequenceDiagram
Client->>API: POST /login
API->>DB: validate creds
DB-->>API: user record
API->>API: sign JWT
API-->>Client: { token }
In conversation / terminal — use ASCII:
Client → API: POST /login
API → DB: validate creds
DB → API: user record
API → API: sign JWT
API → Client: { token }
- Write code that explains what it does, not what it replaced.
- Focus on current implementation details.
- Document the "what" and "why" of the current solution.
- Keep comments concise and relevant to present code.
- Always use TypeScript with Bun (
bun run script.ts), not Python.
- Define the most strict and restrictive type possible.
- Avoid optional/nullable (
?,| null,| undefined) unless absence is a valid, expected state:- Function parameters and return types
- Class constructor parameters
- Object properties and interfaces
- Type definitions and DTOs
- No re-exports: Don't re-export items from a file where the same thing is imported. Files should import directly from source.
- Type Derivation: Derive types from schemas/source of truth rather than duplicating.
Use LSP tool to understand code without reading entire files:
hover— get function/variable typesgoToDefinition— find where symbols are definedfindReferences— find all usagesincomingCalls/outgoingCalls— trace call chains
- Read files before theorizing: Always read actual file contents to trace the code path and validate theories.
- Evidence-based debugging: Do not propose theories about bugs without examining the relevant code files.
- Follow the execution path: Trace through actual code rather than making assumptions.
- Always search for dependencies: Before removing or refactoring, find all callers, imports, and references.
- Don't assume visibility: Never assume you've seen all potential uses of a function, variable, or component.
- Verify safe removal: Use grep/search tools to confirm code is truly unused before deleting.
- Check indirect dependencies: Dynamic imports, string references, configuration-based usage.
- When removing a file, always use the
trashcommand, notrm. Ask before deleting files.
Semi-functional — practical patterns, not dogma.
Core Principles:
- Immutability first: Favor immutable data structures over mutations.
- Dependency injection: Pass dependencies via function signatures, not global state.
- Encapsulation: Make class members private/readonly wherever possible.
- Pure functions: Prefer pure functions but don't sacrifice practicality.
- Functional transformations: Use
.filter/.map/.reduceover imperative loops with mutations. - Reducers: Use for complex state updates.
- React: Avoid global mutable state. Use context or props.
// ❌ Mutation
function updateUser(user: User, changes: Partial<User>) {
user.name = changes.name || user.name;
return user;
}
// ✅ Immutable
function updateUser(user: User, changes: Partial<User>): User {
return { ...user, ...changes };
}// ❌ Global state
let globalConfig = { apiUrl: 'https://api.example.com' };
function fetchUser(id: string) {
return fetch(`${globalConfig.apiUrl}/users/${id}`);
}
// ✅ Inject via signature
function fetchUser(id: string, config: { apiUrl: string }) {
return fetch(`${config.apiUrl}/users/${id}`);
}
// ✅ Or factory for complex deps
function createUserService(config: { apiUrl: string }) {
return {
fetchUser: (id: string) => fetch(`${config.apiUrl}/users/${id}`),
updateUser: (id: string, data: User) =>
fetch(`${config.apiUrl}/users/${id}`, { method: 'PUT', body: JSON.stringify(data) })
};
}class UserRepository {
private readonly db: Database;
private readonly logger: Logger;
constructor(db: Database, logger: Logger) {
this.db = db;
this.logger = logger;
}
async findById(id: string): Promise<User | null> {
return this.db.query('SELECT * FROM users WHERE id = ?', [id]);
}
private validateUser(user: User): boolean {
return user.email.includes('@');
}
}- Prefer async/await: Use async versions of file system and database operations.
- Non-blocking: Avoid
existsSync,mkdirSync,readFileSync. - Consistent patterns: Use async/await consistently across the codebase.
// ❌ Sync
import { existsSync, mkdirSync } from 'fs';
function ensureDir(path: string) {
if (!existsSync(path)) mkdirSync(path, { recursive: true });
}
// ✅ Async
import { access, mkdir } from 'fs/promises';
async function ensureDir(path: string) {
try { await access(path); }
catch { await mkdir(path, { recursive: true }); }
}Optional chaining (?.) masks type uncertainty and defers error handling. Validate and narrow types upfront using schemas:
// ❌ Optional chaining hides type problems
function processUser(user: User | undefined) {
const name = user?.profile?.name ?? 'Unknown';
const email = user?.email?.toLowerCase();
// If user is undefined, the error is hidden until later.
}
// ✅ Validate and narrow types explicitly
const UserSchema = z.object({
profile: z.object({ name: z.string() }),
email: z.string().email()
});
function processUser(rawUser: unknown) {
const user = UserSchema.parse(rawUser); // throws if invalid — fail fast
const name = user.profile.name;
const email = user.email.toLowerCase();
}Define rigorous schemas and parse data at all I/O boundaries.
import { z } from 'zod';
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
isActive: z.boolean(),
createdAt: z.string().datetime()
});
type User = z.infer<typeof UserSchema>;
async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const rawData = await response.json();
return UserSchema.parse(rawData);
}const ConfigSchema = z.object({
database: z.object({
host: z.string(),
port: z.number(),
ssl: z.boolean().default(false)
}),
features: z.array(z.string())
});
type Config = z.infer<typeof ConfigSchema>;
async function loadConfig(filePath: string): Promise<Config> {
const rawContent = await readFile(filePath, 'utf-8');
return ConfigSchema.parse(JSON.parse(rawContent));
}const EnvSchema = z.object({
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(1),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(['development', 'production', 'test'])
});
type Environment = z.infer<typeof EnvSchema>;
const env: Environment = EnvSchema.parse(process.env);- Describe current system architecture.
- Explain data flows and component interactions — prefer mermaid diagrams.
- Document API contracts and interfaces.
- Focus on how the system works now.
Example component diagram:
flowchart LR
Client[Web Client] -->|REST| API[API Server]
API -->|SQL| DB[(Postgres)]
API -->|pub/sub| Queue[Redis Queue]
Queue --> Worker[Worker]
Worker -->|SQL| DB
- Mobile-first: Design for mobile screens first, enhance for larger.
- Touch targets: Minimum 44x44px (h-11) for interactive elements.
- Don't reference removed test cases.
- Keep commits small.
- Describe what the commit adds or fixes.
- Focus on the change's purpose and impact.
- Use present tense ("Add JWT authentication" not "Removed direct token usage").
- Explain what the PR accomplishes in a simple, concist, bulleted list
- Include any configuration changes needed.