From 14e4ab59cbd260bc018db31d7858ea6a77eb4a36 Mon Sep 17 00:00:00 2001 From: Seth Raphael Date: Sat, 14 Mar 2026 09:14:29 -0700 Subject: [PATCH] docs: add CLAUDE.md with Convex performance rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Encodes the patterns learned from bandwidth optimization so AI coding assistants get them right from the start — digest owner fields over users table reads, compound indexes over JS filtering, one-shot fetches for public pages, change detection in triggers. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..c1838d04 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,36 @@ +# ClawHub — Project Rules + +## Convex Performance Rules + +- For public listing/browse pages, use `ConvexHttpClient.query()` (one-shot fetch), + not `useQuery`/`usePaginatedQuery` (reactive subscription). Reserve reactive + queries for data the user needs to see update in real time. +- Denormalize hot read paths into a single lightweight "digest" table. Every + `ctx.db.get()` join adds a table to the reactive invalidation scope. +- When a `skillSearchDigest` row is available, use `digestToOwnerInfo(digest)` + to resolve owner data. NEVER call `ctx.db.get(ownerUserId)` when digest + owner fields (`ownerHandle`, `ownerName`, `ownerDisplayName`, `ownerImage`) + are already present. Reading from `users` adds the entire table to the + reactive read set and wastes bandwidth. +- Use `convex-helpers` Triggers to sync denormalized tables automatically. + Always add change detection — skip the write if no fields actually changed. +- Use compound indexes instead of JS filtering. If you're filtering docs after + the query, you're scanning documents you'll throw away. +- For search results scored by computed values (vector + lexical + popularity), + fetch all results once and paginate client-side. Don't re-run the full search + pipeline on "load more." +- Backfills on reactively-subscribed tables need `delayMs` between batches. +- Mutations that read >8 MB should use the Action → Query → Mutation pattern + to split reads across transactions. + +## Convex Conventions + +- All mutations import from `convex/functions.ts` (not `convex/_generated/server`) + to get trigger wrapping. Type imports still come from `convex/_generated/server`. +- NEVER use `--typecheck=disable` on `npx convex deploy`. +- Use `npx convex dev --once` to push functions once (not long-running watcher). + +## Testing + +- Tests use `._handler` to call mutation handlers directly with mock `db` objects. +- Mock `db` objects MUST include `normalizeId: vi.fn()` for trigger wrapper compatibility.