cohere-db 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +383 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +104 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/generate.d.ts +21 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +131 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/handoff.d.ts +16 -0
- package/dist/commands/handoff.d.ts.map +1 -0
- package/dist/commands/handoff.js +334 -0
- package/dist/commands/handoff.js.map +1 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +28 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/show.d.ts +10 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +123 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/validate.d.ts +10 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +167 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/watch.d.ts +10 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +74 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/extractors/drizzle.d.ts +63 -0
- package/dist/extractors/drizzle.d.ts.map +1 -0
- package/dist/extractors/drizzle.js +251 -0
- package/dist/extractors/drizzle.js.map +1 -0
- package/dist/extractors/firebase.d.ts +40 -0
- package/dist/extractors/firebase.d.ts.map +1 -0
- package/dist/extractors/firebase.js +192 -0
- package/dist/extractors/firebase.js.map +1 -0
- package/dist/extractors/index.d.ts +68 -0
- package/dist/extractors/index.d.ts.map +1 -0
- package/dist/extractors/index.js +346 -0
- package/dist/extractors/index.js.map +1 -0
- package/dist/extractors/mongodb.d.ts +45 -0
- package/dist/extractors/mongodb.d.ts.map +1 -0
- package/dist/extractors/mongodb.js +201 -0
- package/dist/extractors/mongodb.js.map +1 -0
- package/dist/extractors/mysql.d.ts +62 -0
- package/dist/extractors/mysql.d.ts.map +1 -0
- package/dist/extractors/mysql.js +173 -0
- package/dist/extractors/mysql.js.map +1 -0
- package/dist/extractors/postgres.d.ts +48 -0
- package/dist/extractors/postgres.d.ts.map +1 -0
- package/dist/extractors/postgres.js +141 -0
- package/dist/extractors/postgres.js.map +1 -0
- package/dist/extractors/prisma.d.ts +72 -0
- package/dist/extractors/prisma.d.ts.map +1 -0
- package/dist/extractors/prisma.js +262 -0
- package/dist/extractors/prisma.js.map +1 -0
- package/dist/extractors/sqlite.d.ts +51 -0
- package/dist/extractors/sqlite.d.ts.map +1 -0
- package/dist/extractors/sqlite.js +150 -0
- package/dist/extractors/sqlite.js.map +1 -0
- package/dist/generators/templates.d.ts +104 -0
- package/dist/generators/templates.d.ts.map +1 -0
- package/dist/generators/templates.js +1516 -0
- package/dist/generators/templates.js.map +1 -0
- package/package.json +79 -0
- package/templates/claude.md +176 -0
- package/templates/constraints.md +222 -0
- package/templates/context/session-context.example.json +48 -0
- package/templates/context/session-context.schema.json +129 -0
- package/templates/cursor.md +154 -0
- package/templates/decisions/decision-template.md +138 -0
- package/templates/edge-cases.md +164 -0
- package/templates/handoffs/handoff-template.md +132 -0
- package/templates/memory/checkpoint-patterns.md +224 -0
- package/templates/query-template.md +35 -0
- package/templates/state/CURRENT_STATE.md +117 -0
- package/templates/test-templates/edge-cases.test.md +230 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Memory & Checkpoint Patterns
|
|
2
|
+
|
|
3
|
+
> AUTO-GENERATED by cohere. Universal patterns with agent-specific notes.
|
|
4
|
+
|
|
5
|
+
## Directory Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
.ai/
|
|
9
|
+
├── memory/
|
|
10
|
+
│ ├── checkpoints/ # Session checkpoints
|
|
11
|
+
│ │ └── checkpoint-{timestamp}.json
|
|
12
|
+
│ ├── accumulated-learnings.md # Cross-session knowledge
|
|
13
|
+
│ ├── edge-cases.md # Documented edge cases
|
|
14
|
+
│ └── session-history.md # Recent session notes
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Checkpoint Pattern (Universal)
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
interface AgentCheckpoint {
|
|
23
|
+
timestamp: string;
|
|
24
|
+
sessionId: string;
|
|
25
|
+
schemaVersion: string;
|
|
26
|
+
currentTask?: TaskContext;
|
|
27
|
+
discoveredPatterns: DiscoveredPattern[];
|
|
28
|
+
pendingQueries: PendingQuery[];
|
|
29
|
+
agentMemory: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface TaskContext {
|
|
33
|
+
taskId: string;
|
|
34
|
+
status: 'in_progress' | 'paused' | 'completed';
|
|
35
|
+
lastAction?: string;
|
|
36
|
+
progress: number;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Save Checkpoint (Universal)
|
|
41
|
+
```typescript
|
|
42
|
+
import * as fs from 'fs/promises';
|
|
43
|
+
import * as path from 'path';
|
|
44
|
+
|
|
45
|
+
async function saveCheckpoint(
|
|
46
|
+
outputDir: string,
|
|
47
|
+
checkpoint: AgentCheckpoint
|
|
48
|
+
): Promise<string> {
|
|
49
|
+
const filename = `checkpoint-${Date.now()}.json`;
|
|
50
|
+
const filepath = path.join(outputDir, 'memory', 'checkpoints', filename);
|
|
51
|
+
|
|
52
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
53
|
+
await fs.writeFile(filepath, JSON.stringify(checkpoint, null, 2));
|
|
54
|
+
|
|
55
|
+
return filepath;
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Restore Checkpoint (Universal)
|
|
60
|
+
```typescript
|
|
61
|
+
async function restoreLatestCheckpoint(
|
|
62
|
+
outputDir: string
|
|
63
|
+
): Promise<AgentCheckpoint | null> {
|
|
64
|
+
const checkpointsDir = path.join(outputDir, 'memory', 'checkpoints');
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const files = await fs.readdir(checkpointsDir);
|
|
68
|
+
const checkpointFiles = files
|
|
69
|
+
.filter(f => f.startsWith('checkpoint-') && f.endsWith('.json'))
|
|
70
|
+
.sort();
|
|
71
|
+
|
|
72
|
+
if (checkpointFiles.length === 0) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const latest = checkpointFiles[checkpointFiles.length - 1];
|
|
77
|
+
const filepath = path.join(checkpointsDir, latest);
|
|
78
|
+
const content = await fs.readFile(filepath, 'utf-8');
|
|
79
|
+
|
|
80
|
+
return JSON.parse(content) as AgentCheckpoint;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
> [!NOTE|CLAUDE]
|
|
90
|
+
> **CLAUDE CODE**: Built-in memory hooks available
|
|
91
|
+
> - `claude mem save <content>` - Save to memory
|
|
92
|
+
> - `claude mem list` - List memories
|
|
93
|
+
> - Auto-loads `~/.Claude/CLAUDE.md` for user-level persistence
|
|
94
|
+
> - Checkpoints integrate with `claude session restore`
|
|
95
|
+
|
|
96
|
+
> [!NOTE|CODEX]
|
|
97
|
+
> **CODEX CRITICAL**: No auto-memory persistence
|
|
98
|
+
> - MUST explicitly save checkpoints
|
|
99
|
+
> - MUST explicitly restore at session start:
|
|
100
|
+
> ```
|
|
101
|
+
> Read the .ai/memory/checkpoints directory. Find the latest checkpoint and restore context.
|
|
102
|
+
> ```
|
|
103
|
+
> - AGENTS.md is NOT auto-loaded (see constraints.md)
|
|
104
|
+
|
|
105
|
+
> [!NOTE|ANTIGRAVITY]
|
|
106
|
+
> **ANTIGRAVITY**: Checkpoint coordination for parallel agents
|
|
107
|
+
> - Share checkpoints via shared storage
|
|
108
|
+
> - Use naming: `checkpoint-{agentId}-{timestamp}.json`
|
|
109
|
+
> - Lock files to prevent race conditions
|
|
110
|
+
|
|
111
|
+
> [!NOTE|XCODE]
|
|
112
|
+
> **XCODE 26.3**: Different persistence model
|
|
113
|
+
> - Use `ProjectDescription.plist` for project context
|
|
114
|
+
> - Use `.xcuserdatad` for user-specific state
|
|
115
|
+
> - Swift PM plugins don't persist across IDE sessions
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Session Handoff Pattern
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
interface SessionHandoff {
|
|
123
|
+
fromSession: string;
|
|
124
|
+
toSession: string;
|
|
125
|
+
timestamp: string;
|
|
126
|
+
context: {
|
|
127
|
+
schemaHash: string;
|
|
128
|
+
pendingActions: string[];
|
|
129
|
+
discoveredRelationships: string[];
|
|
130
|
+
importantConstraints: string[];
|
|
131
|
+
};
|
|
132
|
+
summary: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function createHandoff(
|
|
136
|
+
outputDir: string,
|
|
137
|
+
handoff: SessionHandoff
|
|
138
|
+
): Promise<void> {
|
|
139
|
+
const handoffPath = path.join(outputDir, 'memory', 'handoff.json');
|
|
140
|
+
|
|
141
|
+
await fs.writeFile(handoffPath, JSON.stringify(handoff, null, 2));
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Usage Examples
|
|
148
|
+
|
|
149
|
+
### Claude Code
|
|
150
|
+
```bash
|
|
151
|
+
# Auto-save to memory
|
|
152
|
+
claude mem save "Key pattern discovered: Use organization_id for tenant isolation"
|
|
153
|
+
|
|
154
|
+
# Manual checkpoint
|
|
155
|
+
npm run checkpoint:save
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Codex
|
|
159
|
+
```bash
|
|
160
|
+
# MUST be explicit
|
|
161
|
+
codex exec "node -e 'import(\"./memory/checkpoint\").then(m => m.saveCheckpoint(\".ai\", {...}))'"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Antigravity (Parallel)
|
|
165
|
+
```bash
|
|
166
|
+
# Agent A
|
|
167
|
+
checkpoint:save --agent=a --shared=/shared/memory/
|
|
168
|
+
|
|
169
|
+
# Agent B
|
|
170
|
+
checkpoint:restore --agent=b --shared=/shared/memory/
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Best Practices (All Agents)
|
|
176
|
+
|
|
177
|
+
| Practice | Why |
|
|
178
|
+
|----------|-----|
|
|
179
|
+
| Checkpoint every 5-10 min | Prevent context loss |
|
|
180
|
+
| Include schema hash | Detect schema drift |
|
|
181
|
+
| Track pending queries | Resume incomplete work |
|
|
182
|
+
| Document discoveries | Accumulate knowledge |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Cross-Session Learning
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
interface LearnedPattern {
|
|
190
|
+
timestamp: string;
|
|
191
|
+
category: 'query' | 'schema' | 'relationship' | 'edge-case';
|
|
192
|
+
description: string;
|
|
193
|
+
sqlExample?: string;
|
|
194
|
+
usageCount: number;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function accumulateLearning(
|
|
198
|
+
outputDir: string,
|
|
199
|
+
learning: LearnedPattern
|
|
200
|
+
): Promise<void> {
|
|
201
|
+
const memoryPath = path.join(outputDir, 'memory', 'accumulated-learnings.md');
|
|
202
|
+
|
|
203
|
+
const newEntry = `
|
|
204
|
+
## ${learning.timestamp}
|
|
205
|
+
|
|
206
|
+
**Category:** ${learning.category}
|
|
207
|
+
|
|
208
|
+
**Description:** ${learning.description}
|
|
209
|
+
|
|
210
|
+
${learning.sqlExample ? `**Example:**
|
|
211
|
+
\`\`\`sql
|
|
212
|
+
${learning.sqlExample}
|
|
213
|
+
\`\`\`` : ''}
|
|
214
|
+
|
|
215
|
+
**Usage Count:** ${learning.usageCount}
|
|
216
|
+
`;
|
|
217
|
+
|
|
218
|
+
const existing = await fs.readFile(memoryPath, 'utf-8').catch(() => '');
|
|
219
|
+
await fs.writeFile(memoryPath, existing + newEntry);
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
*Pattern version: 1.0.0*
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{name}}
|
|
3
|
+
description: {{description}}
|
|
4
|
+
category: {{category}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# {{name}}
|
|
8
|
+
|
|
9
|
+
{{description}}
|
|
10
|
+
|
|
11
|
+
## Query
|
|
12
|
+
|
|
13
|
+
\`\`\`sql
|
|
14
|
+
{{query}}
|
|
15
|
+
\`\`\`
|
|
16
|
+
|
|
17
|
+
## Parameters
|
|
18
|
+
|
|
19
|
+
| Name | Type | Description |
|
|
20
|
+
|------|------|-------------|
|
|
21
|
+
{{#each parameters}}
|
|
22
|
+
| `{{name}}` | `{{type}}` | {{description}} |
|
|
23
|
+
{{/each}}
|
|
24
|
+
|
|
25
|
+
## Returns
|
|
26
|
+
|
|
27
|
+
{{returns}}
|
|
28
|
+
|
|
29
|
+
## Use Case
|
|
30
|
+
|
|
31
|
+
{{useCase}}
|
|
32
|
+
|
|
33
|
+
## Notes
|
|
34
|
+
|
|
35
|
+
{{notes}}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Current State
|
|
2
|
+
|
|
3
|
+
> AUTO-GENERATED by cohere. This is the active progress snapshot.
|
|
4
|
+
|
|
5
|
+
## Session Status
|
|
6
|
+
|
|
7
|
+
| Field | Value |
|
|
8
|
+
|-------|-------|
|
|
9
|
+
| **Status** | `{status}` |
|
|
10
|
+
| **Last Updated** | `{lastUpdated}` |
|
|
11
|
+
| **Session ID** | `{sessionId}` |
|
|
12
|
+
| **Agent ID** | `{agentId}` |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Progress
|
|
17
|
+
|
|
18
|
+
### Completed Steps
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
1. ✅ Step 1 - Completed at {timestamp1}
|
|
22
|
+
2. ✅ Step 2 - Completed at {timestamp2}
|
|
23
|
+
3. ✅ Step 3 - Completed at {timestamp3}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Current Step
|
|
27
|
+
|
|
28
|
+
> **Status**: {currentStatus}
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
{currentStepDescription}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Remaining Steps
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
4. ⏳ Step 4 - Pending
|
|
38
|
+
5. ⏳ Step 5 - Pending
|
|
39
|
+
6. 📋 Step 6 - Planned
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Work Queue
|
|
45
|
+
|
|
46
|
+
### Pending Tasks
|
|
47
|
+
|
|
48
|
+
| Priority | Task | Status | Dependencies |
|
|
49
|
+
|----------|------|--------|--------------|
|
|
50
|
+
| High | Task 1 | Pending | None |
|
|
51
|
+
| Medium | Task 2 | Pending | Task 1 |
|
|
52
|
+
| Low | Task 3 | Pending | Task 1, Task 2 |
|
|
53
|
+
|
|
54
|
+
### Blocked Tasks
|
|
55
|
+
|
|
56
|
+
| Task | Blocker | Since |
|
|
57
|
+
|------|---------|-------|
|
|
58
|
+
| Task A | Dependency B | {date} |
|
|
59
|
+
| Task B | Dependency C | {date} |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Known Issues
|
|
64
|
+
|
|
65
|
+
| Issue | Severity | Workaround |
|
|
66
|
+
|-------|----------|------------|
|
|
67
|
+
| Issue 1 | Critical | Workaround A |
|
|
68
|
+
| Issue 2 | Minor | Workaround B |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Variables & State
|
|
73
|
+
|
|
74
|
+
### Active Variables
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{activeVariablesJson}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Schema Modifications
|
|
81
|
+
|
|
82
|
+
| Table | Modification | Timestamp |
|
|
83
|
+
|-------|--------------|-----------|
|
|
84
|
+
| users | Column added | {timestamp} |
|
|
85
|
+
| orders | Index created | {timestamp} |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Checkpoints
|
|
90
|
+
|
|
91
|
+
| Checkpoint | Timestamp | Status |
|
|
92
|
+
|------------|-----------|--------|
|
|
93
|
+
| checkpoint-001 | {timestamp} | Valid |
|
|
94
|
+
| checkpoint-002 | {timestamp} | Valid |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Quick Actions
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Resume from current state
|
|
102
|
+
db-ai handoff --resume {sessionId}
|
|
103
|
+
|
|
104
|
+
# Record progress
|
|
105
|
+
db-ai handoff --record
|
|
106
|
+
|
|
107
|
+
# List available sessions
|
|
108
|
+
db-ai handoff --list
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
> [!NOTE|ALL]
|
|
114
|
+
> **MULTI-AGENT**: When handoff to new agent:
|
|
115
|
+
> 1. Read this file first
|
|
116
|
+
> 2. Check `.ai/context/SESSION_CONTEXT.json` for full state
|
|
117
|
+
> 3. Review `.ai/decisions/` for context
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Edge Case Test Templates
|
|
2
|
+
|
|
3
|
+
> AUTO-GENERATED by cohere. Copy to `.ai/test-templates/` and customize.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Universal Test Patterns
|
|
8
|
+
|
|
9
|
+
These tests work across all agents (Claude, Codex, Antigravity, Xcode).
|
|
10
|
+
|
|
11
|
+
### Empty Result Set
|
|
12
|
+
```typescript
|
|
13
|
+
test('SELECT returns empty array when no rows match', async () => {
|
|
14
|
+
const result = await query(
|
|
15
|
+
'SELECT * FROM users WHERE email = $1',
|
|
16
|
+
['nonexistent@example.com']
|
|
17
|
+
);
|
|
18
|
+
expect(result).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Soft Delete Handling
|
|
23
|
+
```typescript
|
|
24
|
+
test('SELECT excludes soft-deleted records by default', async () => {
|
|
25
|
+
const result = await query(
|
|
26
|
+
'SELECT * FROM orders WHERE organization_id = $1',
|
|
27
|
+
[orgId]
|
|
28
|
+
);
|
|
29
|
+
expect(result.rows.every(r => r.deleted_at === null)).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
> [!NOTE|CLAUDE]
|
|
36
|
+
> Claude Code: Tests run via `npm test`. Use `claude test` for agent-specific testing.
|
|
37
|
+
|
|
38
|
+
> [!NOTE|CODEX]
|
|
39
|
+
> **CODEX SPECIFIC**: Codex requires explicit test file inclusion:
|
|
40
|
+
> ```
|
|
41
|
+
> I have edge case tests in .ai/test-templates/. Run them to verify.
|
|
42
|
+
> ```
|
|
43
|
+
> Tests won't run unless explicitly invoked.
|
|
44
|
+
|
|
45
|
+
> [!NOTE|ANTIGRAVITY]
|
|
46
|
+
> Antigravity: Parallel agents should run tests independently, share results via `.ai/memory/test-results/`.
|
|
47
|
+
|
|
48
|
+
> [!NOTE|XCODE]
|
|
49
|
+
> Xcode: Tests go in `Tests/` directory. Use XCTest framework.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 1. CRUD Edge Cases
|
|
54
|
+
|
|
55
|
+
### Empty Result Set
|
|
56
|
+
```typescript
|
|
57
|
+
test('SELECT returns empty array when no rows match', async () => {
|
|
58
|
+
const result = await query(
|
|
59
|
+
'SELECT * FROM users WHERE email = $1',
|
|
60
|
+
['nonexistent@example.com']
|
|
61
|
+
);
|
|
62
|
+
expect(result).toEqual([]);
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Single Row Edge Cases
|
|
67
|
+
```typescript
|
|
68
|
+
test('INSERT fails on duplicate primary key', async () => {
|
|
69
|
+
await expect(query(
|
|
70
|
+
'INSERT INTO users (id, email) VALUES ($1, $2)',
|
|
71
|
+
[existingId, 'new@example.com']
|
|
72
|
+
)).rejects.toThrow('duplicate_key');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('UPDATE modifies correct row with unique constraint', async () => {
|
|
76
|
+
const result = await query(
|
|
77
|
+
'UPDATE users SET email = $1 WHERE id = $2',
|
|
78
|
+
['updated@example.com', targetId]
|
|
79
|
+
);
|
|
80
|
+
expect(result.rowCount).toBe(1);
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 2. Foreign Key Edge Cases
|
|
87
|
+
|
|
88
|
+
### Orphaned Records
|
|
89
|
+
```typescript
|
|
90
|
+
test('SELECT with JOIN skips orphaned foreign keys', async () => {
|
|
91
|
+
const result = await query(`
|
|
92
|
+
SELECT o.*, u.email
|
|
93
|
+
FROM orders o
|
|
94
|
+
JOIN users u ON o.user_id = u.id
|
|
95
|
+
WHERE o.organization_id = $1
|
|
96
|
+
`, [orgId]);
|
|
97
|
+
// Only returns orders with valid user_id
|
|
98
|
+
expect(result.rows.every(r => r.user_id !== null)).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Cascade Delete
|
|
103
|
+
```typescript
|
|
104
|
+
test('Parent deletion cascades to children', async () => {
|
|
105
|
+
await query('DELETE FROM users WHERE id = $1', [parentId]);
|
|
106
|
+
|
|
107
|
+
const childResult = await query(
|
|
108
|
+
'SELECT * FROM orders WHERE user_id = $1',
|
|
109
|
+
[parentId]
|
|
110
|
+
);
|
|
111
|
+
expect(childResult.rows.length).toBe(0);
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 3. Concurrency Edge Cases
|
|
118
|
+
|
|
119
|
+
### Race Conditions
|
|
120
|
+
```typescript
|
|
121
|
+
test('Concurrent INSERTs with unique constraint', async () => {
|
|
122
|
+
const concurrentInserts = Array(5).fill(null).map(() =>
|
|
123
|
+
query(
|
|
124
|
+
'INSERT INTO unique_values (name) VALUES ($1) ON CONFLICT DO NOTHING',
|
|
125
|
+
['unique_name']
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const results = await Promise.all(concurrentInserts);
|
|
130
|
+
const successfulInserts = results.filter(r => r.rowCount === 1);
|
|
131
|
+
|
|
132
|
+
// Only one should succeed
|
|
133
|
+
expect(successfulInserts.length).toBe(1);
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
> [!NOTE|ANTIGRAVITY]
|
|
138
|
+
> **IMPORTANT**: Parallel agents must use transaction isolation. Test with:
|
|
139
|
+
> ```sql
|
|
140
|
+
> BEGIN ISOLATION LEVEL SERIALIZABLE;
|
|
141
|
+
> -- operations
|
|
142
|
+
> COMMIT;
|
|
143
|
+
> ```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 4. Data Type Edge Cases
|
|
148
|
+
|
|
149
|
+
### NULL Handling
|
|
150
|
+
```typescript
|
|
151
|
+
test('NULL comparison in WHERE clause', async () => {
|
|
152
|
+
const result = await query(
|
|
153
|
+
'SELECT * FROM users WHERE email IS NULL',
|
|
154
|
+
[]
|
|
155
|
+
);
|
|
156
|
+
expect(result.rows.every(r => r.email === null)).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('NULL in NOT IN with empty subquery', async () => {
|
|
160
|
+
const result = await query(
|
|
161
|
+
'SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM inactive_logins)',
|
|
162
|
+
[]
|
|
163
|
+
);
|
|
164
|
+
// Should return all users, not filtered by NULL subquery
|
|
165
|
+
expect(result.rows.length).toBeGreaterThan(0);
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 5. Large Dataset Edge Cases
|
|
172
|
+
|
|
173
|
+
### Pagination Boundaries
|
|
174
|
+
```typescript
|
|
175
|
+
test('First page returns correct results', async () => {
|
|
176
|
+
const result = await query(`
|
|
177
|
+
SELECT * FROM orders
|
|
178
|
+
WHERE organization_id = $1
|
|
179
|
+
ORDER BY created_at DESC
|
|
180
|
+
LIMIT 50 OFFSET 0
|
|
181
|
+
`, [orgId]);
|
|
182
|
+
expect(result.rows.length).toBeLessThanOrEqual(50);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('Last page handles partial results', async () => {
|
|
186
|
+
const result = await query(`
|
|
187
|
+
SELECT * FROM orders
|
|
188
|
+
WHERE organization_id = $1
|
|
189
|
+
ORDER BY created_at DESC
|
|
190
|
+
LIMIT 50 OFFSET 950
|
|
191
|
+
`, [orgId]);
|
|
192
|
+
expect(result.rows.length).toBeLessThanOrEqual(50);
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 6. Duplicate Detection Tests
|
|
199
|
+
|
|
200
|
+
### Primary Key Duplicates
|
|
201
|
+
```typescript
|
|
202
|
+
test('Detects exact duplicate records', async () => {
|
|
203
|
+
const duplicates = await query(`
|
|
204
|
+
SELECT email, COUNT(*) as cnt
|
|
205
|
+
FROM users
|
|
206
|
+
WHERE deleted_at IS NULL
|
|
207
|
+
GROUP BY email
|
|
208
|
+
HAVING COUNT(*) > 1
|
|
209
|
+
`, []);
|
|
210
|
+
expect(duplicates.rows.length).toBe(0);
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Running Tests (Universal)
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# All agents: Standard command
|
|
220
|
+
npm test -- --testPathPattern=test-templates
|
|
221
|
+
|
|
222
|
+
# Claude-specific
|
|
223
|
+
claude test run
|
|
224
|
+
|
|
225
|
+
# Codex-specific (must be explicit)
|
|
226
|
+
codex exec "npm test -- --testPathPattern=test-templates"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
*Template version: 1.0.0*
|