doing-a-simple-two-sta…
Use when analyzing a large corpus of text, code, or data that exceeds a single agent's effective context - orchestrates parallel…
Use when writing database access code, creating schemas, or managing transactions with PostgreSQL - enforces transaction safety with TX_ naming, read-write separation, type safety for UUIDs/JSONB, and snake_case conventions to prevent data corruption and type errors
Claude Code
$ npx -y skills add ed3dai/ed3d-plugins --skill howto-develop-with-postgres --agent claude-code
Codex
$ npx skills add ed3dai/ed3d-plugins --skill howto-develop-with-postgres -a codex -y
Cursor
$ npx skills add ed3dai/ed3d-plugins --skill howto-develop-with-postgres -a cursor -y
Gemini CLI
$ npx skills add ed3dai/ed3d-plugins --skill howto-develop-with-postgres -a gemini-cli -y
GitHub Copilot
$ npx skills add ed3dai/ed3d-plugins --skill howto-develop-with-postgres -a github-copilot -y
OpenCode
$ npx skills add ed3dai/ed3d-plugins --skill howto-develop-with-postgres -a opencode -y
Windsurf
$ npx skills add ed3dai/ed3d-plugins --skill howto-develop-with-postgres -a windsurf -y
name: howto-develop-with-postgres description: Use when writing database access code, creating schemas, or managing transactions with PostgreSQL - enforces transaction safety with TX_ naming, read-write separation, type safety for UUIDs/JSONB, and snake_case conventions to prevent data corruption and type errors user-invocable: false
Enforce transaction safety, type safety, and naming conventions to prevent data corruption and runtime errors.
**Core principles:**
**For TypeScript/Drizzle implementations:** See [typescript-drizzle.md](./typescript-drizzle.md) for concrete patterns.
**Methods that START transactions:**
**Methods that PARTICIPATE in transactions:**
// GOOD: Starts transaction, has TX_ prefix, no executor parameter
async TX_createUserWithProfile(userData: UserData, profileData: ProfileData): Promise<User> {
return this.db.transaction(async (tx) => {
const user = await this.createUser(userData, tx);
await this.createProfile(user.id, profileData, tx);
return user;
});
}
// GOOD: Participates in transaction, no TX_ prefix, takes executor
async createUser(userData: UserData, executor: Drizzle = this.db): Promise<User> {
return executor.insert(USERS).values(userData).returning();
}
// BAD: Starts transaction but missing TX_ prefix
async createUserWithProfile(userData: UserData, profileData: ProfileData): Promise<User> {
return this.db.transaction(async (tx) => { /* ... */ });
}
// BAD: Has TX_ prefix but takes executor parameter (allows nesting)
async TX_createUser(userData: UserData, executor: Drizzle = this.db): Promise<User> {
return executor.transaction(async (tx) => { /* ... */ });
}**What DOES NOT count as "starting a transaction":**
**Default: ULID stored as UUID**
**Exceptions (context-dependent):**
**Rule:** If unsure whether data will be user-visible, use ULID.
**Use exact decimal types (numeric/decimal) for monetary values:**
**Why:** Floating-point types accumulate rounding errors. Exact decimal types prevent financial discrepancies.
**ALWAYS type JSONB columns in your ORM/schema:**
**Why:** Prevents runtime errors from accessing undefined properties or wrong types.
**Maintain separate client types at compile time:**
**Why:** Prevents accidental writes to replica, enforces deliberate mutation choices.
**All database objects use snake_case:**
**Application code:** Map to idiomatic case (camelCase in TypeScript, etc.)
**Standard mixins:**
**Proactive indexing:**
**Default isolation (Read Committed) for most operations.**
**Use stricter isolation when:**
**Always use generate + migrate workflow:** 1. Change schema in code 2. Generate migration file 3. Review migration SQL 4. Apply migration to database
**Never use auto-push workflow in production.**
| Mistake | Reality | Fix | |---------|---------|-----| | "This is one operation, doesn't need transaction" | Multi-step operations without transactions cause partial updates and data corruption | Wrap in transaction with TX_ prefix | | "Single atomic operation needs TX_ prefix" | TX_ is for explicit transaction blocks, not atomic operations | No TX_ for single INSERT/UPDATE/DELETE | | "UUID is just a string" | Type confusion causes runtime errors (wrong ID formats, failed lookups) | Use strict UUID type in language | | "I'll type JSONB later when schema stabilizes" | Untyped JSONB leads to undefined property access and type errors | Type immediately with known fields or Record<string, unknown> | | "Read client vs write client doesn't matter" | Using wrong client bypasses separation, allows accidental mutations | Use read-
Ed's repo of Claude Code plugins, centered around a research-plan-implement workflow. Only a tiny bit cursed. If you're lucky.
Use when analyzing a large corpus of text, code, or data that exceeds a single agent's effective context - orchestrates parallel…
Use when creating a new Claude Code plugin or setting up plugin structure - provides complete file organization, manifest format,…
Use when creating specialized subagents for Claude Code plugins or the Task tool - covers description writing for…
Use when creating, releasing, or maintaining a Claude Code Plugin Marketplace - covers marketplace.json schema, version…
Use when completing development phases or branches to identify and update CLAUDE.md or AGENTS.md files that may have become stale…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.