# Code Style GuideFollow these conventions when writing code.---## Core Philosophy- **Less is more.** Minimal code, minimal abstraction, minimal API surface. Delete dead code. Every export earns its place.- **Iterate toward simplicity.** Build, evaluate, rebuild until you reach the most minimal abstraction. Don't abstract prematurely.- **Write for the next reader.** Code should be readable without comments. If you need a comment, rewrite the code.- **Correctness above all.** Never return `undefined` to avoid an error. Never swallow failures. If a case isn't planned for, throw.- **Ship, then polish.** Get it working first, iterate later. "Working" means clean enough to read tomorrow.---## Language & Runtime- TypeScript everywhere- Next.js App Router for apps, pure Node.js for libraries- pnpm, pnpm workspaces + Turborepo for monorepos- commander.js/tsx for clis- tsup for library bundling- Biome for linting/formatting- vitest/jest for testing---## NamingNaming is critical. Names _are_ the API.### Files- **kebab-case** for all files: `activity-logs.ts`, `query-properties-tool.ts`- Test files co-located: `searches.test.ts` alongside `searches.ts`### Code- **PascalCase**: types, Zod schemas, React components, error classes- **camelCase**: functions, variables, module-level constants- **SCREAMING_SNAKE_CASE**: true constants (enums, string literals)- Prefix domain types with the domain name: `[Domain]User`, `[Domain]Search`- Error classes: `[Domain][Entity][Problem]Error`### Data Access FunctionsThese conventions are strict:| Prefix | Returns | On missing ||--------|---------|------------|| `get` | Single item | **Throws** || `find` | Single item or `null` | Returns `null` || `findMany` / `find*s` | Array (possibly empty) | Returns `[]` || `query` | Paginated results | Returns empty page || `create` | Created item | — || `update` | Updated item | Throws if not found || `upsert` | Created or updated item | — || `insert` | Inserted item | — || `softDelete` | void | — |Names should be short. Compound names with `by`/`and` are fine when needed: `findEntityWithRelationByOwnerAndId`.---## Type System### Zod as Single Source of TruthType and schema share the same name. The TypeScript type is derived from the Zod schema:```tsexport type MyFilter = z.infer<typeof MyFilter>;export const MyFilter = z.object({ types: z.array(MyType).optional(), priceRange: z.tuple([z.number().nullable(), z.number().nullable()]).optional(),});```### Drizzle Schema Types```tsexport type MyEntity = InferSelectModel<typeof myEntities>;```### Discriminated Unions for Events```tsexport const MyEvents = z.discriminatedUnion("type", [ z.object({ type: z.literal("entity-created"), entity: MyEntity }), z.object({ type: z.literal("entity-updated"), entityId: z.number() }),]);```### Entity IDsUse indexed type access, not bare primitives:```tsfunction getEntityById(id: MyEntity["id"]): Promise<MyEntity>```### Interface vs Type- **Interfaces** for contracts that external code implements (pluggable systems)- **Types** (via Zod `z.infer`) for data shapes and domain models---## Architecture### Factory Functions over ClassesPrefer `create*` factory functions returning interface-typed objects. Private state via closures:```tsexport function createThing(options: ThingOptions): Thing { let { store } = options; return { doWork() { ... }, configure(opts) { ... }, };}```---## API DesignDesign the public API first. Start in `lib/[domain]/` with a clear, minimal exported surface. Only export what consumers need.Exported functions are the primary unit of testing. If the public API is well-tested, internals can change freely.### Error Handling**Throw early, catch at boundaries.**- Base domain error class extending `Error`- Specific subclasses for each failure, all one-liners:```tsexport class EntityNotFoundError extends DomainError {}export class InsufficientCreditError extends DomainError {}```- `get*` functions throw when entities are missing- Catch at API/action boundaries with `instanceof` checks- Server actions wrap in `createAction()` returning `{ error: string }`---## Database (Drizzle ORM)- `pgTable` definitions with epoch-based timestamps- `createdAt` with `.default(sql\`CURRENT_TIMESTAMP\`).notNull()`- Soft deletion via nullable `deletedAt`- JSONB typed with `.$type<T>()`### Query Patterns- **CTEs as module-level variables** for reusable filtered views (e.g. undeleted records)- **Dynamic WHERE**: build a `conditions[]` array, apply with `and(...conditions)`- **Pagination**: `page`/`pageSize` params with defaults (`1`, `100`)- **Sort as tuples**: `[key, "ASC" | "DESC"][]`- **Destructure single results**: `const [result] = await db.insert(...).returning()`- **Raw SQL** via `sql` tag for complex queries (PostGIS, JSONB, etc.)---## Library Design### Pluggable ArchitectureDefine behaviour as interfaces, provide multiple implementations:```tsexport interface Store { get(id: string): Promise<unknown | undefined>; set(id: string, value: unknown): Promise<void>;}export function createMemoryStore(): Store { ... } // Dev/testexport function createRedisStore(redis): Store { ... } // Production```### Optional Methods for Optimizations```tsexport interface Store { getAll?(): Promise<Record<string, unknown>>; // Optional bulk fetch get(id: string): Promise<unknown | undefined>; // Required fallback}```### Event Callback HooksOptional callbacks invoked with `?.()`:```tsexport interface Events<T> { onCreated?(entity: T): Promise<void>; onUpdated?(entity: T): Promise<void>;}await events.onCreated?.(entity);```### Barrel ExportsExplicit re-exports in `index.ts`. Separate `export type` for type-only exports.---## TestingTest infrastructure comes first. Invest in making tests easy to write before writing tests. If tests are hard to write, the API is wrong.### Philosophy- Test the public API, not implementation details- Pragmatic coverage — test where bugs are likely, skip the obvious- Test cases should be minimal — no setup in the test body- Mock at API boundaries with strong types, never at low levels like `fetch`- Tests inform API design### Style- `describe` with function reference in app code: `describe(findEntities, () => {})`- `describe` with string in library code: `describe("module", () => {})`- Simple test names: `it("works")`, `it("throws on missing")`- Inline snapshots for complex output- Helper generators: `generateEntity({ overrides })`- Reusable setup in `beforeEach` — never duplicated- Cleanup: `afterEach(() => { jest.clearAllMocks() })`---## React & Frontend- Named function exports (no default exports for components)- Props typed inline, not as separate interfaces- `"use client"` only when needed; server components by default- `useState` for local state, `useSWR` for data fetching- shadcn/ui + Tailwind + Lucide icons- Small focused components; helpers at bottom of file, unexported unless needed- Custom hooks co-located when single-use- For anything requiring browser automation or testing, use the `agent-browser` skill.---## DependenciesReach for trusted dependencies first. For anything new: research, review code quality/maintenance/security. If it doesn't meet the bar, build it yourself.**Trusted stack:** TypeScript, Next.js, React, Zod, Drizzle, Vercel AI SDK, Vercel Workflow, Vercel Sandbox, D3, pnpm, tsup, Biome, Jest, shadcn/ui, Tailwind, Lucide, Radix UI, Stripe, SWR, Terraform, Turso, Neon, Supabase---## Infrastructure & Deployment### Platform- Vercel for deployment. Serverless architecture by default.- Choose infrastructure that fits the serverless model. Not rigid — pick the right tool for the job.### Database Strategy- **Non-trivial projects**: start with Postgres on Neon or Supabase.- **Simple/prototype projects**: SQLite locally, Turso in production. Flag when hitting limitations (concurrent writes, complex queries, JSONB) as a signal to migrate to Postgres.- Drizzle ORM + Drizzle Kit always.### AI & Workflow- Vercel AI SDK (`ai-sdk.dev`) for AI features- Vercel Workflow (`useworkflow.dev`) for durable workflows- Vercel Sandbox for sandboxed code execution### Infrastructure as Code- **Always prefer IaC over manual setup.** Terraform is the tool of choice.- Before provisioning anything on a third-party service, check for a Terraform provider first.- Provision secrets via Terraform where possible.- State: per-project local state.- Terraform files live in `<projectDir>/infra`.- Use the 1Password Terraform provider for secrets at plan/apply time. App runtime secrets via environment variables.### Secrets- All tokens, passwords, and credentials are managed via **1Password** and the `op` CLI.- Never hardcode secrets. Never commit them.- Use `op://VaultName/ItemName/fieldName` URI format everywhere — Terraform providers, `.env` files, scripts.- Read a secret: `op read "op://Vault/Item/field"`- Inject into a process: `op run --env-file=.env -- <command>` (replaces `op://` refs at runtime)- In Terraform: use the 1Password provider to reference `op://` URIs directly.- If `op` fails due to authentication, ask for a service token.### Project Bootstrap- All projects live in `~/code`. Scaffold new projects there.- `pnpm create next-app --ts --tailwind --biome --app --turbopack --use-pnpm --yes ~/code/<dir>` to scaffold (creates git repo).- Push to GitHub and link to Vercel on explicit instruction.---## Project Structure### Application (Next.js)```lib/ [domain]/ schema.ts # Drizzle tables types.ts # Zod schemas + types errors.ts # Error classes db.ts # DB connection [entity].ts # Data access [entity].test.ts # Tests functions/ # Event handlers (default exports) tools/ # AI tool definitions adapters/ # External service adaptersapp/ (group)/ page.tsx layout.tsx actions.ts # Server actions urls.ts # URL formatters api/ components/ui/ # shadcn/ui```### Library (Monorepo)```packages/ [core]/src/ index.ts # Barrel exports types.ts [module].ts [module].test.ts testing.ts # Test utilities (exported) [integration]/src/ index.ts # Single-file implementationexamples/ [example-app]/```---## Creative Coding & Visual DesignFor creative/visual projects.### Visual Identity- Dark backgrounds, D3 `interpolatePlasma` as primary color scheme- `mix-blend-mode: screen` for luminous light-on-dark effects- Inverted text (white bg + black text + screen blend) for emphasis instead of bold- Animated gradient titles via `background-clip: text`, slow (10-30s), never distracting- Subtlety — effects should be felt, not noticed- hackerman CLI style, retro futurism### Principles1. Audio/music vocabulary — `lfo`, `hz`, `sample` — animation as sound2. D3 color scales as identity, not arbitrary hex3. `function*` generators for progressive reveal — show the process4. Time as modular arithmetic for seamless looping5. Parametric everything — sketches are parameter spaces6. Trail/accumulation aesthetics — depth from simplicity# Work Style- **Think from first principles.** Before reaching for a pattern, library, or default — ask what the problem actually requires. Decompose new problem spaces into their atomic parts visibly before proposing solutions. Reason from fundamentals, not convention.- Default to the stated technology preferences (Next.js, Vercel, Drizzle, etc.) — they exist for good reason. But if first-principles thinking reveals a better fit, surface it. Prefer the default, challenge it when the fundamentals demand it.- Be precise. No filler, no hedging, no restating the question, no summarizing what was just done. Say what matters, skip what doesn't.- Code output follows the same rule — no placeholder comments, no explanatory prose where the code speaks for itself.- Be autonomous. Make decisions, trust your judgment, keep moving. Only pause when making a large leap or a big assumption — that's when to ask for input.- Give meaningful pushback. Say "I don't think this is a good idea for X reasons" when warranted.- At the end of each plan, interview me in detail using the AskUserQuestionTool about literally anything: technical implementation, UI & UX, concerns, tradeoffs, etc. but make sure the questions are not obvious.## One-Way DoorsBefore committing to a decision that's hard to reverse, explicitly name it as a **one-way door** and confirm before proceeding. This includes:- Architectural patterns that propagate across the codebase- Third-party service or framework lock-in- Data model choices that touch many consumers- Public API contractsPrefer the reversible approach when the cost difference is small. Not every decision needs a gate — routine database migrations and incremental changes are fine. The point is awareness: flag the doors that are hard to walk back through.## Scope & Adjacent Work- Fix trivial adjacent issues (<5 lines) inline and mention what you did.- For anything bigger, add it to the task list and stay on task.## Tests as Feedback- Tests are the primary feedback tool. Always write tests for meaningful changes — don't reach for one-off scripts or manual verification.- Use tests to validate your own work before surfacing results. Write them early, run them often.## Error Recovery- When hitting build/test/type errors mid-task, attempt 2–3 fix cycles before surfacing the issue. Use judgment — if the fix is obvious, keep going; if you're circling, stop and report.## Git Workflow- One commit per completed task with a descriptive message.- Use feature branches. Branch naming: `<type>/<short-description>` (e.g. `feat/add-auth`, `fix/connection-pooling`).- Don't commit partial or broken work.# Agent Work LogLog completed work with `aw task "description" --category <category>`## What to LogLog meaningful completions: features, bug fixes, refactors, config changes, tests, docs, research findings.**Don't log**: file reading, trivial edits, planning, incomplete work.## Categoriesfeature | bugfix | refactor | docs | config | test | perf | infra | security | research## Examples```bashaw task "Implemented JWT authentication with refresh tokens" --category featureaw task "Fixed race condition in database connection pooling" --category bugfixaw task "Refactored API routes to use middleware pattern" --category refactor```<!-- End Agent Work Log -->