.claude/skills/convex-development-general/SKILL.md
Applies general rules for Convex development, emphasizing schema design, validator usage, index-first query patterns, function registration, and correct handling of system fields.
npx skillsauth add oimiragieo/agent-studio convex-development-generalInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Schema and Validators
defineTable(v.object({...})) in convex/schema.ts.v.id("tableName") for cross-document references — never plain v.string()._id and _creationTime from schema definitions — they are auto-generated system fields.Function Registration
query({ args: {}, returns: v.null(), handler: async (ctx, args) => {...} }).args) and return (returns) validators; if nothing is returned, use returns: v.null().internalQuery/internalMutation/internalAction for private functions — never expose internal logic via public API.httpAction with httpRouter for HTTP endpoints in convex/http.ts.Index-First Query Patterns
.withIndex("by_field", (q) => q.eq("field", value)) over .filter((q) => q.eq(q.field("field"), value)).schema.ts using .index("name", ["field1", "field2"]) on defineTable..withSearchIndex for full-text search patterns..collect() on large tables — use .paginate(opts) or .take(n).Queries vs Mutations vs Actions
query: read-only, reactive (subscriptions), runs in V8 sandbox.mutation: database writes, transactional, runs in V8 sandbox.action: can call external APIs / run Node.js, NOT transactional — minimize direct db access.ctx.runQuery/ctx.runMutation for cross-function calls; avoid action-to-mutation loops that split transactions.Await All Promises
await ctx.db.patch(...), await ctx.scheduler.runAfter(...), etc.no-floating-promises ESLint rule to catch un-awaited Convex calls.Real-Time Subscriptions
useQuery hooks auto-subscribe and re-render on data changes — no manual onSnapshot wiring needed.export default defineSchema({ messages: defineTable({ channel: v.id("channels"), // cross-doc ref body: v.string(), user: v.id("users"), // _id and _creationTime are auto-added — do NOT include them }) .index("by_channel", ["channel"]) .index("by_channel_user", ["channel", "user"]), });
// convex/messages.ts — correct query with index + return validator import { query } from "./_generated/server"; import { v } from "convex/values";
export const getByChannel = query({ args: { channelId: v.id("channels") }, returns: v.array(v.object({ _id: v.id("messages"), body: v.string() })), handler: async (ctx, args) => { return await ctx.db .query("messages") .withIndex("by_channel", (q) => q.eq("channel", args.channelId)) .take(50); // bounded — never unbounded .collect() in production }, });
// convex/messages.ts — internal mutation (not exposed publicly) import { internalMutation } from "./_generated/server";
export const deleteOldMessages = internalMutation({ args: { before: v.number() }, returns: v.null(), handler: async (ctx, args) => { const old = await ctx.db .query("messages") .withIndex("by_channel", (q) => q.lt("_creationTime", args.before)) .take(100); await Promise.all(old.map((msg) => ctx.db.delete(msg._id))); }, });
</examples>
## Iron Laws
1. **ALWAYS** define document schemas using Convex `v` validators — never rely on raw TypeScript types alone for runtime-enforced schema correctness.
2. **NEVER** manually include `_id` or `_creationTime` fields in schema definitions — they are automatically generated system fields and specifying them causes runtime errors.
3. **ALWAYS** use `v.id("tableName")` for cross-document references — never store foreign keys as plain strings, which bypasses Convex's referential integrity tools.
4. **NEVER** perform direct database mutations from client-side code — all mutations must be defined as Convex mutation functions in the `convex/` directory.
5. **ALWAYS** add `.withIndex(...)` for filtered queries on non-trivial tables — never use `.filter()` as a substitute for a missing index on production data, and never use `.collect()` without a bound (`take(n)` or `.paginate()`) on large tables.
## Anti-Patterns
| Anti-Pattern | Why It Fails | Correct Approach |
| --- | --- | --- |
| Using plain TypeScript interfaces as schema definitions | TypeScript types are compile-time only; Convex `v` validators enforce runtime shape and generate type-safe accessors | Define all table schemas with `defineTable(v.object({...}))` |
| Adding `_id` or `_creationTime` to defineTable schemas | Convex rejects schemas that include system fields, causing runtime initialization errors | Omit system fields; access them via `doc._id` and `doc._creationTime` after query |
| Storing cross-document references as plain `v.string()` | Loses Convex's cross-reference validation and type inference for joined queries | Use `v.id("tableName")` so Convex validates the reference type |
| Running `.collect()` on large tables without pagination | Returns all documents, causing memory spikes and timeouts on large datasets | Use `.paginate(opts)` or `.take(100)` with cursor-based pagination |
| Writing to the database from React client code directly | Bypasses access control, validation, and audit trail; creates untraceable mutations | All writes must go through a Convex `mutation` function in `convex/` |
| Using `.filter()` instead of `.withIndex()` for field-based lookups | `.filter()` performs a full table scan; identical performance to filtering in-code but misses index speed-up | Define a schema index and use `.withIndex(name, q => q.eq(...))` |
## Memory Protocol (MANDATORY)
**Before starting:**
```bash
cat .claude/context/memory/learnings.md
After completing: Record any new patterns or exceptions discovered.
ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.
tools
Comprehensive biosignal processing toolkit for analyzing physiological data including ECG, EEG, EDA, RSP, PPG, EMG, and EOG signals. Use this skill when processing cardiovascular signals, brain activity, electrodermal responses, respiratory patterns, muscle activity, or eye movements. Applicable for heart rate variability analysis, event-related potentials, complexity measures, autonomic nervous system assessment, psychophysiology research, and multi-modal physiological signal integration.
tools
Comprehensive toolkit for creating, analyzing, and visualizing complex networks and graphs in Python. Use when working with network/graph data structures, analyzing relationships between entities, computing graph algorithms (shortest paths, centrality, clustering), detecting communities, generating synthetic networks, or visualizing network topologies. Applicable to social networks, biological networks, transportation systems, citation networks, and any domain involving pairwise relationships.
data-ai
Molecular featurization for ML (100+ featurizers). ECFP, MACCS, descriptors, pretrained models (ChemBERTa), convert SMILES to features, for QSAR and molecular ML.
development
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.