feat: Phase 1 foundation - schema, auth, realtime, shell
- Rewrote Drizzle schema: 20 tables with enums, relations, indexes - Generated migration with DROP TABLE records (v1 EAV removal) - Added passkey auth routes (register/login) - Added requireWorkspaceAccess helper - Added seedDefaultData for Personal workspace + welcome note - Updated SSE endpoint for v2 entities + workspace_id filtering - Created recordActivity helper (insert + pg_notify) - Updated sidebar: Graph replaces Reports, removed Analytics - Updated command palette for v2 entities - Created AGENTS.md with locked contract - Created llm-wiki scaffold (5 stubs) - Added inline AGENT INSTRUCTION comments to all 50 API route files - Fixed globals.css border-border class conflict - Updated database.ts stub for v1 compatibility
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# API Patterns
|
||||
|
||||
## Route Structure
|
||||
|
||||
All routes are workspace-scoped:
|
||||
|
||||
```
|
||||
GET /api/domains/:domainId/tasks — List tasks
|
||||
POST /api/domains/:domainId/tasks — Create task
|
||||
GET /api/domains/:domainId/tasks/:id — Get task
|
||||
PATCH /api/domains/:domainId/tasks/:id — Update task
|
||||
DELETE /api/domains/:domainId/tasks/:id — Soft-delete task
|
||||
```
|
||||
|
||||
Same pattern for: habits, projects, notes, domains, tags, sections, webhooks, activity-feed.
|
||||
|
||||
## Query Parameters
|
||||
|
||||
| Param | Purpose | Example |
|
||||
|-------|---------|---------|
|
||||
| `?limit=&offset=` | Pagination | `?limit=20&offset=0` |
|
||||
| `?sort=` | Sorting | `?sort=-created` (descending) |
|
||||
| `?status=` | Filter by status | `?status=todo,in_progress` |
|
||||
| `?priority=` | Filter by priority | `?priority=high,urgent` |
|
||||
| `?tag=` | Filter by tag | `?tag=meeting` |
|
||||
| `?domain=` | Filter by domain | `?domain=uuid` |
|
||||
| `?search=` | Full-text search | `?search=deploy` |
|
||||
|
||||
## Error Format
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Title is required",
|
||||
"details": { "field": "title" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Standard codes: `VALIDATION_ERROR`, `NOT_FOUND`, `UNAUTHORIZED`, `FORBIDDEN`, `CONFLICT`, `INTERNAL_ERROR`
|
||||
|
||||
## Write Pattern
|
||||
|
||||
Every write API route MUST:
|
||||
1. Drizzle write (INSERT/UPDATE/DELETE)
|
||||
2. Insert activity feed entry via `recordActivity()`
|
||||
3. `pg.notify('project_e_events', payload)` — handled by `recordActivity()`
|
||||
@@ -0,0 +1,42 @@
|
||||
# Architecture
|
||||
|
||||
## System Overview
|
||||
|
||||
Project E is a full-stack personal productivity OS with realtime collaboration, AI agent integration, and keyboard-driven UI.
|
||||
|
||||
## Three-Layer Architecture
|
||||
|
||||
```
|
||||
Frontend (Next.js React) → Next.js API Routes (business logic) → PostgreSQL (data store)
|
||||
│ │
|
||||
│ ├── NOTIFY (on write)
|
||||
│ │
|
||||
└── SSE (EventSource) ←────────┘
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
1. User action → API route → Drizzle write → `pg.notify()` → SSE endpoint → Zustand store → UI update
|
||||
2. External agent → MCP tool call → API route → Drizzle write → `pg.notify()` → SSE → UI update
|
||||
3. Worker → webhook delivery / recurring spawn → API route → Drizzle write → `pg.notify()` → SSE → UI update
|
||||
|
||||
## Container Layout
|
||||
|
||||
- **project-e-web** — Next.js 15 (App Router), frontend + REST API + MCP + SSE
|
||||
- **project-e-db** — PostgreSQL 16, all data + LISTEN/NOTIFY
|
||||
- **project-e-worker** — Node.js worker, webhook delivery + recurring spawning + AI dispatch
|
||||
|
||||
## Monorepo Structure
|
||||
|
||||
```
|
||||
ProjectE/
|
||||
├── apps/
|
||||
│ ├── web/ # Next.js 15 app
|
||||
│ └── worker/ # Node.js worker process
|
||||
├── packages/
|
||||
│ ├── db/ # Drizzle schema + connection
|
||||
│ └── shared/ # Shared types, schemas, constants
|
||||
├── docker-compose.yml
|
||||
├── AGENTS.md
|
||||
└── llm-wiki/
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
# Conventions
|
||||
|
||||
## Naming
|
||||
|
||||
- Tables: snake_case, plural (e.g., `activity_feed`, `task_tags`)
|
||||
- Columns: snake_case (e.g., `domain_id`, `created_at`)
|
||||
- TypeScript: camelCase (e.g., `domainId`, `createdAt`)
|
||||
- API routes: kebab-case (e.g., `/api/domains/[domainId]/tasks`)
|
||||
- Files: kebab-case (e.g., `auth-config.ts`, `command-palette.tsx`)
|
||||
|
||||
## File Structure
|
||||
|
||||
- Schema: `packages/db/src/schema.ts` (single file for all tables)
|
||||
- API routes: `apps/web/app/api/[entity]/route.ts`
|
||||
- Components: `apps/web/components/[entity]/[component].tsx`
|
||||
- Stores: `apps/web/lib/stores/use-[store-name]-store.ts`
|
||||
- Lib: `apps/web/lib/[module].ts`
|
||||
|
||||
## Commit Format
|
||||
|
||||
```
|
||||
type: short description
|
||||
|
||||
- Bullet points for details
|
||||
```
|
||||
|
||||
Types: `feat`, `fix`, `refactor`, `docs`, `chore`, `test`
|
||||
|
||||
## Build
|
||||
|
||||
- Run `npm run build --workspace=apps/web` before committing
|
||||
- Do NOT commit build artifacts (`.next/`, `dist/`, `.turbo/`)
|
||||
@@ -0,0 +1,33 @@
|
||||
# Data Model
|
||||
|
||||
## Entity Relationship Overview
|
||||
|
||||
- **domains** — Workspaces (parent_id=null) and sub-groups
|
||||
- **tasks** — Rich task model with subtasks, dependencies, time tracking
|
||||
- **habits** — Habit tracking with streaks, mood, reminders
|
||||
- **projects** — Projects with sections (milestones)
|
||||
- **notes** — Markdown notes with wikilinks and backlinks
|
||||
- **tags** — Universal tags with entity scoping and hierarchy
|
||||
- **activity_feed** — Append-only audit log
|
||||
- **scheduled_jobs** — Recurring task/habit spawning
|
||||
- **jobs** — Worker queue
|
||||
- **webhooks** — Registered webhook endpoints
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
- Soft-delete via `deleted_at` on all user data tables
|
||||
- UUID primary keys with `gen_random_uuid()`
|
||||
- Text enums via `pgEnum` for status/priority/type fields
|
||||
- JSONB for flexible data (custom_fields, changes, payload)
|
||||
- All entities scoped to a workspace via `domain_id` or `workspace_id`
|
||||
|
||||
## Junction Tables
|
||||
|
||||
- task_tags, habit_tags, note_tags, project_tags — many-to-many entity↔tag
|
||||
- task_dependencies — task dependency graph
|
||||
- note_links — wikilink/backlink connections
|
||||
- note_entity_links — cross-entity linking (note → task/habit/project)
|
||||
|
||||
## Indexes
|
||||
|
||||
Every FK column and frequently filtered column has an index. See `packages/db/src/schema.ts` for the full list.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Realtime System
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
API Route (write)
|
||||
│
|
||||
├── Drizzle INSERT/UPDATE/DELETE
|
||||
├── Activity feed INSERT (via recordActivity)
|
||||
└── pg.notify('project_e_events', payload) (via recordActivity)
|
||||
│
|
||||
▼
|
||||
SSE Endpoint (/api/realtime)
|
||||
│
|
||||
├── LISTEN project_e_events
|
||||
├── Filter by workspace_id
|
||||
└── Push to EventSource
|
||||
│
|
||||
▼
|
||||
Zustand Store (entity slices)
|
||||
│
|
||||
├── Receives event: { type, action, id, workspace_id }
|
||||
├── Re-fetches affected entity
|
||||
└── Components re-render
|
||||
```
|
||||
|
||||
## SSE Event Format
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "task",
|
||||
"action": "created",
|
||||
"id": "uuid",
|
||||
"workspace_id": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## Connection
|
||||
|
||||
- Endpoint: `GET /api/realtime?workspace_id=uuid`
|
||||
- Content-Type: `text/event-stream`
|
||||
- Heartbeat: `:ping` every 30 seconds
|
||||
- Reconnection: Client-side exponential backoff
|
||||
|
||||
## Trigger Pattern
|
||||
|
||||
Every write API route calls `recordActivity()` which handles both the activity feed insert and the pg_notify call.
|
||||
Reference in New Issue
Block a user