diff --git a/AGENTS.md b/AGENTS.md
index 058c272..7c4f626 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -17,6 +17,106 @@ EOF
These credentials should be used for all automated testing (Magnitude, Playwright) and manual testing when needed. Do not attempt to authenticate without using these credentials.
+**Database Setup**: The application uses SurrealDB running in Docker Compose for the app cache layer:
+
+1. Start the database services:
+ ```bash
+ docker compose up -d
+ ```
+
+2. This starts two services:
+ - `surrealdb`: The main SurrealDB instance (port 8000)
+ - `surrealmcp`: SurrealMCP server for MCP access (port 8080)
+
+3. Start the Next.js development server:
+ ```bash
+ pnpm dev
+ ```
+
+4. To stop the services:
+ ```bash
+ docker compose down
+ ```
+
+5. Configuration:
+ - SurrealDB runs in-memory mode (data is not persisted between restarts)
+ - Namespace: `ponderants`
+ - Database: `main`
+ - Credentials: `root/root`
+
+**Note**: Always start docker compose services before starting the Next.js dev server to ensure the database is available.
+
+**Testing Workflow**: All new features must follow a rigorous testing process before being committed:
+
+1. **Manual Testing with Playwright MCP**:
+ - Use Playwright MCP tools to manually test all functionality interactively
+ - Test both happy paths (expected user flows) and unhappy paths (errors, edge cases)
+ - Document each step you verify during manual testing - these become test cases
+ - If you encounter issues during manual testing (e.g., 404 errors, unexpected behavior), investigate and fix them before proceeding
+ - Use the following pattern:
+ ```
+ 1. Navigate to the feature
+ 2. Perform user actions (clicks, typing, etc.)
+ 3. Verify expected outcomes
+ 4. Test error scenarios
+ 5. Verify cleanup/state updates
+ ```
+
+2. **Write Comprehensive Magnitude Tests**:
+ - After manually verifying functionality with Playwright MCP, write extensive Magnitude tests covering ALL verified behaviors
+ - Each manual test step should have a corresponding Magnitude test assertion
+ - Test files are located in `tests/magnitude/` with `.mag.ts` extension
+ - Use the test credentials from .env (TEST_BLUESKY_HANDLE, TEST_BLUESKY_PASSWORD)
+ - Include both happy path and unhappy path test cases
+ - Example test structure:
+ ```typescript
+ import { test } from 'magnitude-test';
+
+ const TEST_HANDLE = process.env.TEST_BLUESKY_HANDLE;
+ const TEST_PASSWORD = process.env.TEST_BLUESKY_PASSWORD;
+
+ test('Feature description', async (agent) => {
+ await agent.act('Navigate to /page');
+ await agent.act('Perform user action');
+ await agent.check('Verify expected outcome');
+ });
+ ```
+
+3. **Reusable Playwright Scaffolding**:
+ - Abstract common patterns (auth, navigation, etc.) into helper files in `tests/playwright/helpers/`
+ - These helpers should be usable both during manual Playwright MCP testing AND by Magnitude tests
+ - Examples: `tests/playwright/helpers/chat.ts`, `tests/playwright/helpers/galaxy.ts`, `tests/playwright/helpers/node.ts`
+ - For auth setup, use Playwright's global setup pattern (see https://playwright.dev/docs/test-global-setup-teardown)
+ - Current auth setup: `tests/playwright/auth.setup.ts`
+
+4. **Generating Playwright Code**:
+ - Use https://playwright.dev/docs/test-agents to generate Playwright test code when helpful
+ - This tool can convert natural language test descriptions into Playwright code
+
+5. **Test Execution**:
+ - Run Magnitude tests: `pnpm test` or `npx magnitude`
+ - Ensure ALL tests pass before committing
+ - If tests fail, fix the implementation or update the tests to match the correct behavior
+
+6. **Pre-Commit Checklist**:
+ - ✅ All manual testing with Playwright MCP completed and verified
+ - ✅ All Magnitude tests written and cover all verified functionality
+ - ✅ Database verified for expected state after operations (e.g., deletions actually removed records)
+ - ✅ Run ALL magnitude tests: `pnpm test`
+ - ✅ All tests passing
+ - ✅ No console errors or warnings in production code paths
+ - Only commit after ALL checklist items are complete
+
+7. **Documentation**:
+ - Document test coverage in `tests/README.md`
+ - Add comments to complex test scenarios explaining the business logic being tested
+
+**Testing Resources**:
+- Playwright Global Setup/Teardown: https://playwright.dev/docs/test-global-setup-teardown
+- Playwright Test Agents: https://playwright.dev/docs/test-agents
+- Magnitude.run Documentation: https://magnitude.run/docs
+- Project Test README: `tests/README.md`
+
You are an expert-level, full-stack AI coding agent. Your task is to implement
the "Ponderants" application. Product Vision: Ponderants is an AI-powered
thought partner that interviews a user to capture, structure, and visualize
diff --git a/app/api/nodes/[id]/route.ts b/app/api/nodes/[id]/route.ts
index 0e34c70..26ce4e4 100644
--- a/app/api/nodes/[id]/route.ts
+++ b/app/api/nodes/[id]/route.ts
@@ -50,9 +50,13 @@ export async function DELETE(
// 1. Fetch the node from SurrealDB to verify ownership and get atp_uri
const db = await connectToDB();
+ // Parse the ID to extract table and record ID parts
+ // Format: "node:e9e38d09-c0f4-4834-a6ba-c92dfa4c0910" or "node:⟨e9e38d09-c0f4-4834-a6ba-c92dfa4c0910⟩"
+ const cleanId = id.replace(/^node:/, '').replace(/[⟨⟩]/g, '');
+
const nodeResult = await db.query<[Array<{ id: string; user_did: string; atp_uri: string }>]>(
- 'SELECT id, user_did, atp_uri FROM node WHERE id = $nodeId',
- { nodeId: id }
+ 'SELECT id, user_did, atp_uri FROM node WHERE id = type::thing($table, $recordId)',
+ { table: 'node', recordId: cleanId }
);
const node = nodeResult[0]?.[0];
@@ -114,7 +118,11 @@ export async function DELETE(
// 4. Delete from SurrealDB cache (only after successful ATproto deletion)
try {
- await db.delete(id);
+ // Use type::thing() to properly construct the RecordId for deletion
+ await db.query('DELETE FROM type::thing($table, $recordId)', {
+ table: 'node',
+ recordId: cleanId,
+ });
console.log('[DELETE /api/nodes/[id]] ✓ Deleted node from SurrealDB cache');
} catch (error) {
console.warn('[DELETE /api/nodes/[id]] ⚠ SurrealDB cache deletion failed (non-critical):', error);
diff --git a/components/DeleteNodeModal.tsx b/components/DeleteNodeModal.tsx
new file mode 100644
index 0000000..210d126
--- /dev/null
+++ b/components/DeleteNodeModal.tsx
@@ -0,0 +1,58 @@
+'use client';
+
+import { Modal, Stack, Text, Group, Button } from '@mantine/core';
+import { IconTrash } from '@tabler/icons-react';
+
+interface DeleteNodeModalProps {
+ opened: boolean;
+ onClose: () => void;
+ onConfirm: () => void;
+ nodeTitle: string | null;
+ isDeleting: boolean;
+}
+
+export function DeleteNodeModal({
+ opened,
+ onClose,
+ onConfirm,
+ nodeTitle,
+ isDeleting,
+}: DeleteNodeModalProps) {
+ return (
+
+
+
+ Are you sure you want to delete "{nodeTitle}"? This will:
+
+
+ • Remove the post from Bluesky
+ • Delete the node from your galaxy
+ This action cannot be undone.
+
+
+
+ }
+ >
+ Delete Permanently
+
+
+
+
+ );
+}
diff --git a/components/ThoughtGalaxy.tsx b/components/ThoughtGalaxy.tsx
index f1983a0..985dd31 100644
--- a/components/ThoughtGalaxy.tsx
+++ b/components/ThoughtGalaxy.tsx
@@ -7,9 +7,10 @@ import {
Text,
} from '@react-three/drei';
import { Suspense, useEffect, useRef, useState } from 'react';
-import { Stack, Text as MantineText, Paper, Title, Box, CloseButton, Group, Anchor, useComputedColorScheme, Button, Modal } from '@mantine/core';
+import { Stack, Text as MantineText, Paper, Title, Box, CloseButton, Group, Anchor, useComputedColorScheme, Button } from '@mantine/core';
import { IconTrash } from '@tabler/icons-react';
import { notifications } from '@mantine/notifications';
+import { DeleteNodeModal } from './DeleteNodeModal';
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
import * as THREE from 'three';
@@ -482,41 +483,13 @@ export function ThoughtGalaxy() {
)}
{/* Delete confirmation modal */}
- setDeleteConfirmOpen(false)}
- title="Delete Node"
- centered
- zIndex={1001}
- >
-
-
- Are you sure you want to delete this node? This will:
-
-
- • Remove the post from Bluesky
- • Delete the node from your galaxy
- This action cannot be undone.
-
-
-
- }
- >
- Delete Permanently
-
-
-
-
+ onConfirm={handleDeleteNode}
+ nodeTitle={selectedNode?.title || null}
+ isDeleting={isDeleting}
+ />