From 1fc281eba406baa1800404f0cf4f05c1ecc04b99 Mon Sep 17 00:00:00 2001 From: neon <18559658+neonsolstice@users.noreply.github.com> Date: Wed, 8 Jul 2026 01:34:42 +0000 Subject: [PATCH] docs: add agent guidance files --- .github/CODEOWNERS | 2 + AGENTS.md | 185 +++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 185 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 372 insertions(+) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d7ad5b89a..36c04599a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,3 @@ /.github/ @neonsolstice +/AGENTS.md @neonsolstice +/CLAUDE.md @neonsolstice diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..2182ee79f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,185 @@ +## Project + +- Book/library management app with Kobo device support. +- pnpm monorepo: `server/` (NestJS), `client/` (Vue 3), `packages/types/` (shared types). +- Requires Node >= 24, pnpm >= 9. + +**Local dev setup:** + +1. `docker compose up -d` - start PostgreSQL +2. `cp server/.env.example server/.env` - first time only +3. `pnpm install` - install all deps +4. `cd server && pnpm db:migrate` - apply migrations +5. `pnpm dev` - start server + client concurrently + +## Scale Expectations + +- BookOrbit is designed for large personal libraries: assume tens of thousands of books per user. +- High-volume paths include library browsing, search/filtering, imports, filesystem scans, metadata refresh, cover handling, Kobo sync, and bulk edits. +- Do not implement features that only work for small libraries. Avoid unbounded queries, loading entire libraries into memory, returning full collections to the frontend, N+1 query patterns, or expensive client-side processing over full result sets. +- Use user-scoped queries, pagination or batching, targeted field selection, appropriate indexes, bounded concurrency, and virtualization for long rendered lists where needed. +- Long-running bulk work should have clear progress, useful logs, bounded memory use, and resumable or idempotent behavior where practical. + +## Backend Conventions + +- NestJS 11 with Fastify (`NestFastifyApplication`). Global API prefix: `/api/v1`. +- Constructor injection via `constructor(private readonly service: SomeService)`. Use `@Inject(DB)` for non-standard providers (e.g., the Drizzle instance). +- Database: PostgreSQL via Drizzle ORM. Use `pgTable()` schema definitions and type inference via `typeof table.$inferSelect` / `$inferInsert` - never write manual type aliases for DB rows. +- Config via `@nestjs/config` with named configs (`registerAs()`). Three configs: `appConfig`, `dbConfig`, `authConfig`. Always inject the typed config, never read `process.env` directly in services. +- Validation: DTOs use `class-validator` decorators. `ValidationPipe` is global with `whitelist: true`, `forbidNonWhitelisted: true`, `transform: true`. +- Error handling: `GlobalExceptionFilter` is registered globally. Throw standard NestJS `HttpException` subclasses (e.g., `NotFoundException`, `BadRequestException`) - never throw raw `Error`. +- Module structure: one feature = one module under `server/src/modules/`. Each module has `controller`, `service`, `module`, and `dto/` subfolder. +- Shared utilities go in `server/src/common/` (filters, guards, decorators, pipes). +- Testing: Vitest + `@nestjs/testing`. All test files use `.test.ts` extension. Use `Test.createTestingModule()` for unit tests. E2E tests go in `server/test/`. Use `vi.fn()`, `vi.mock()`, `vi.spyOn()` - never `jest.*`. + +## Logging Conventions + +- Use this message format for service and integration logs: + `[event] [phase] key=value ... - short message` +- Valid phases are: + - `[start]` + - `[end]` + - `[fail]` +- Do not use dotted phase style like `fetch.start` / `fetch.end` / `fetch.fail`. +- Keep one stable `event` per operation (for example `book.refresh_metadata`, `author.bulk_refresh_metadata`). + +When to add start/end/fail logs: + +- Non-trivial operations: + - batch loops + - external API/provider calls + - multi-step DB work (fan-out queries, complex writes) + - file system operations + - orchestration/background jobs + - destructive operations (delete/merge/bulk update) + +When to avoid start/end logs: + +- Chatty hot paths (simple reads, access checks, tiny helper methods) +- very frequent per-item loops unless there is an error +- add fail logs in these paths if needed, and use threshold-based logs for slow reads + +Required fields: + +- `[start]`: primary IDs + key input flags +- `[end]`: primary IDs + `durationMs` + outcome counters/flags +- `[fail]`: primary IDs + `durationMs` + `errorClass` + `error=""` + +Key rules: + +- Keep key order stable: IDs -> inputs -> outcomes/errors +- Keep logs single-line +- Do not log secrets, tokens, raw DTO payloads, or large blobs +- Keep `error` short and sanitized + +**Always use `sanitizeLogValue()` from `server/src/common/utils/log-sanitize.utils` for any dynamic value embedded inside a quoted log field (e.g., `error="..."`, `path="..."`, `message="..."`).** Never inline `.replace(/"/g, '\\"')` or similar partial escaping - it misses backslashes and triggers CodeQL `js/incomplete-sanitization` alerts. + +Examples: + +- `[book.refresh_metadata] [start] bookId=1287 userId=42 preview=false - refresh metadata started` +- `[book.refresh_metadata] [end] bookId=1287 durationMs=914 updatedFields=7 coverDownloaded=true - refresh metadata completed` +- `[book.refresh_metadata] [fail] bookId=1287 durationMs=10023 errorClass=AbortError error="provider timeout" - refresh metadata failed` + +## Frontend Conventions + +- Vue 3 Composition API with `