From 92e0d5188a3602ce180d1cac36928853077d5324 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 29 Jan 2026 04:26:48 +0100 Subject: [PATCH] docs(01): create phase 1 foundation plans Phase 01: Foundation - 2 plans in 2 waves - Wave 1: Project setup with SvelteKit + Drizzle schema - Wave 2: Repository layer + verification page - Ready for execution Co-Authored-By: Claude Opus 4.5 --- .planning/ROADMAP.md | 9 +- .planning/phases/01-foundation/01-01-PLAN.md | 226 ++++++++++++ .planning/phases/01-foundation/01-02-PLAN.md | 356 +++++++++++++++++++ 3 files changed, 586 insertions(+), 5 deletions(-) create mode 100644 .planning/phases/01-foundation/01-01-PLAN.md create mode 100644 .planning/phases/01-foundation/01-02-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index e0a846e..0952d19 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -31,12 +31,11 @@ Decimal phases appear between their surrounding integers in numeric order. 3. Repository layer provides typed CRUD operations for entries 4. Filesystem storage directory structure exists for future images 5. Development server starts and serves a basic page -**Plans**: TBD +**Plans**: 2 plans Plans: -- [ ] 01-01: TBD -- [ ] 01-02: TBD -- [ ] 01-03: TBD +- [ ] 01-01-PLAN.md — SvelteKit project setup with Drizzle schema and unified entries table +- [ ] 01-02-PLAN.md — Repository layer with typed CRUD and verification page ### Phase 2: Core CRUD **Goal**: Users can create, manage, and view entries with a responsive, accessible UI @@ -135,7 +134,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 1. Foundation | 0/3 | Not started | - | +| 1. Foundation | 0/2 | Planned | - | | 2. Core CRUD | 0/4 | Not started | - | | 3. Images | 0/3 | Not started | - | | 4. Tags & Organization | 0/3 | Not started | - | diff --git a/.planning/phases/01-foundation/01-01-PLAN.md b/.planning/phases/01-foundation/01-01-PLAN.md new file mode 100644 index 0000000..427e332 --- /dev/null +++ b/.planning/phases/01-foundation/01-01-PLAN.md @@ -0,0 +1,226 @@ +--- +phase: 01-foundation +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - package.json + - tsconfig.json + - svelte.config.js + - vite.config.ts + - src/app.css + - src/lib/server/db/schema.ts + - src/lib/server/db/index.ts + - drizzle.config.ts +autonomous: true + +must_haves: + truths: + - "npm run dev starts without errors" + - "TypeScript compilation succeeds" + - "Tailwind CSS classes are processed" + - "Database schema defines unified entries table with type discriminator" + artifacts: + - path: "package.json" + provides: "Project dependencies" + contains: "better-sqlite3" + - path: "src/lib/server/db/schema.ts" + provides: "Drizzle schema with entries table" + contains: "entries" + - path: "src/lib/server/db/index.ts" + provides: "Database connection and initialization" + exports: ["db"] + key_links: + - from: "src/lib/server/db/index.ts" + to: "src/lib/server/db/schema.ts" + via: "import schema" + pattern: "import.*schema" +--- + + +Create the SvelteKit project with all core dependencies and establish the database schema using Drizzle ORM. + +Purpose: Establishes the foundation for all subsequent development - project structure, tooling, and data model. +Output: Working SvelteKit project with Drizzle schema defining the unified entries table. + + + +@/home/tho/.claude/get-shit-done/workflows/execute-plan.md +@/home/tho/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/research/STACK.md +@.planning/research/ARCHITECTURE.md +@.planning/phases/01-foundation/01-CONTEXT.md + + + + + + Task 1: Create SvelteKit project with dependencies + + package.json + tsconfig.json + svelte.config.js + vite.config.ts + src/app.html + src/app.css + src/routes/+page.svelte + + +Create a new SvelteKit project using `npx sv create` with the following options: +- Template: SvelteKit minimal +- TypeScript: Yes +- Tailwind CSS: Yes (v4 with @tailwindcss/vite) +- ESLint + Prettier: Yes + +After project creation, install additional dependencies: +```bash +npm install better-sqlite3 drizzle-orm nanoid zod +npm install -D drizzle-kit @types/better-sqlite3 +``` + +Configure Tailwind in `src/app.css`: +```css +@import 'tailwindcss'; +``` + +Verify `vite.config.ts` includes Tailwind plugin (sv create should handle this). + +Update `src/routes/+page.svelte` to include a simple Tailwind-styled element to verify CSS processing: +```svelte +

TaskPlanner

+

Foundation setup complete.

+``` +
+ +Run `npm run dev` - server should start on localhost:5173 without errors. +Visit the page - should see styled "TaskPlanner" heading. +Run `npm run check` - TypeScript should pass. + + +SvelteKit dev server runs, Tailwind processes classes, all dependencies installed. + +
+ + + Task 2: Configure Drizzle schema with entries table + + src/lib/server/db/schema.ts + src/lib/server/db/index.ts + drizzle.config.ts + + +Create `src/lib/server/db/schema.ts` with the unified entries table: + +```typescript +import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; + +// Entry types: 'task' (actionable) or 'thought' (reference) +export const entries = sqliteTable('entries', { + id: text('id').primaryKey(), // nanoid + title: text('title'), + content: text('content').notNull(), + type: text('type', { enum: ['task', 'thought'] }).notNull().default('thought'), + status: text('status', { enum: ['open', 'done', 'archived'] }).default('open'), // for tasks + pinned: integer('pinned', { mode: 'boolean' }).default(false), + dueDate: text('due_date'), // ISO date string, nullable + createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()), + updatedAt: text('updated_at').notNull().$defaultFn(() => new Date().toISOString()), +}); + +// Type inference for TypeScript +export type Entry = typeof entries.$inferSelect; +export type NewEntry = typeof entries.$inferInsert; +``` + +Create `src/lib/server/db/index.ts` for database connection: + +```typescript +import Database from 'better-sqlite3'; +import { drizzle } from 'drizzle-orm/better-sqlite3'; +import * as schema from './schema'; +import { existsSync, mkdirSync } from 'fs'; +import { dirname } from 'path'; + +const DB_PATH = process.env.DATABASE_PATH || './data/taskplaner.db'; + +// Ensure data directory exists +const dbDir = dirname(DB_PATH); +if (!existsSync(dbDir)) { + mkdirSync(dbDir, { recursive: true }); +} + +const sqlite = new Database(DB_PATH); + +// Enable WAL mode for better concurrent read performance +sqlite.pragma('journal_mode = WAL'); + +export const db = drizzle(sqlite, { schema }); + +export { schema }; +``` + +Create `drizzle.config.ts` for migrations: + +```typescript +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + schema: './src/lib/server/db/schema.ts', + out: './drizzle', + dialect: 'sqlite', + dbCredentials: { + url: process.env.DATABASE_PATH || './data/taskplaner.db', + }, +}); +``` + +Add npm scripts to `package.json`: +```json +{ + "scripts": { + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:push": "drizzle-kit push", + "db:studio": "drizzle-kit studio" + } +} +``` + + +Run `npm run db:push` - should create database file at `./data/taskplaner.db` with entries table. +Run `sqlite3 ./data/taskplaner.db ".schema"` - should show entries table with all columns. +Import db in a test: create a simple test file that imports and logs `db` without errors. + + +Drizzle schema defines entries table with id, title, content, type, status, pinned, dueDate, createdAt, updatedAt. +Database file created with schema applied. WAL mode enabled. + + + +
+ + +1. `npm run dev` starts without errors +2. `npm run check` passes TypeScript validation +3. `./data/taskplaner.db` exists after `npm run db:push` +4. Database has entries table with correct schema +5. Tailwind classes render correctly on the test page + + + +- SvelteKit project created with TypeScript and Tailwind 4 +- All dependencies installed (better-sqlite3, drizzle-orm, nanoid, zod) +- Drizzle schema defines unified entries table with type discriminator +- Database initializes with WAL mode +- Dev server starts and serves styled page + + + +After completion, create `.planning/phases/01-foundation/01-01-SUMMARY.md` + diff --git a/.planning/phases/01-foundation/01-02-PLAN.md b/.planning/phases/01-foundation/01-02-PLAN.md new file mode 100644 index 0000000..11432a5 --- /dev/null +++ b/.planning/phases/01-foundation/01-02-PLAN.md @@ -0,0 +1,356 @@ +--- +phase: 01-foundation +plan: 02 +type: execute +wave: 2 +depends_on: ["01-01"] +files_modified: + - src/lib/server/db/repository.ts + - src/hooks.server.ts + - src/routes/+page.server.ts + - src/routes/+page.svelte + - data/.gitkeep +autonomous: true + +must_haves: + truths: + - "Repository provides create, read, update, delete operations for entries" + - "Database initializes automatically on first server request" + - "Data directory structure exists with .gitkeep" + - "Basic page shows database connection status" + artifacts: + - path: "src/lib/server/db/repository.ts" + provides: "EntryRepository with typed CRUD operations" + exports: ["entryRepository", "EntryRepository"] + - path: "src/hooks.server.ts" + provides: "Server hooks for database initialization" + contains: "handle" + - path: "data/.gitkeep" + provides: "Data directory placeholder for git" + key_links: + - from: "src/lib/server/db/repository.ts" + to: "src/lib/server/db/index.ts" + via: "import db" + pattern: "import.*db.*from" + - from: "src/hooks.server.ts" + to: "src/lib/server/db/index.ts" + via: "import db for init" + pattern: "import.*db" + - from: "src/routes/+page.server.ts" + to: "src/lib/server/db/repository.ts" + via: "import repository" + pattern: "import.*repository" +--- + + +Create the repository layer for typed CRUD operations and wire up database initialization with a verification page. + +Purpose: Provides the data access layer that all future features will use, and verifies the foundation works end-to-end. +Output: Working repository with CRUD operations, auto-initializing database, and a verification page proving everything connects. + + + +@/home/tho/.claude/get-shit-done/workflows/execute-plan.md +@/home/tho/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/phases/01-foundation/01-01-SUMMARY.md +@src/lib/server/db/schema.ts +@src/lib/server/db/index.ts + + + + + + Task 1: Create EntryRepository with typed CRUD operations + + src/lib/server/db/repository.ts + + +Create `src/lib/server/db/repository.ts` implementing the repository pattern: + +```typescript +import { eq, desc } from 'drizzle-orm'; +import { db } from './index'; +import { entries, type Entry, type NewEntry } from './schema'; +import { nanoid } from 'nanoid'; + +export interface EntryRepository { + create(entry: Omit): Entry; + getById(id: string): Entry | undefined; + getAll(options?: { limit?: number; offset?: number }): Entry[]; + update(id: string, updates: Partial>): Entry | undefined; + delete(id: string): boolean; + count(): number; +} + +class SQLiteEntryRepository implements EntryRepository { + create(entry: Omit): Entry { + const now = new Date().toISOString(); + const newEntry: NewEntry = { + ...entry, + id: nanoid(), + createdAt: now, + updatedAt: now, + }; + + db.insert(entries).values(newEntry).run(); + return this.getById(newEntry.id)!; + } + + getById(id: string): Entry | undefined { + return db.select().from(entries).where(eq(entries.id, id)).get(); + } + + getAll(options: { limit?: number; offset?: number } = {}): Entry[] { + const { limit = 100, offset = 0 } = options; + return db + .select() + .from(entries) + .orderBy(desc(entries.createdAt)) + .limit(limit) + .offset(offset) + .all(); + } + + update(id: string, updates: Partial>): Entry | undefined { + const existing = this.getById(id); + if (!existing) return undefined; + + db.update(entries) + .set({ + ...updates, + updatedAt: new Date().toISOString(), + }) + .where(eq(entries.id, id)) + .run(); + + return this.getById(id); + } + + delete(id: string): boolean { + const existing = this.getById(id); + if (!existing) return false; + + db.delete(entries).where(eq(entries.id, id)).run(); + return true; + } + + count(): number { + const result = db.select().from(entries).all(); + return result.length; + } +} + +// Singleton instance +export const entryRepository: EntryRepository = new SQLiteEntryRepository(); +``` + +This provides: +- Type-safe CRUD operations using Drizzle ORM +- Automatic ID generation with nanoid +- Automatic timestamp management (createdAt, updatedAt) +- Pagination support via limit/offset +- Singleton pattern for consistent access + + +Create a quick test by importing the repository: +```typescript +import { entryRepository } from './repository'; +const entry = entryRepository.create({ content: 'Test', type: 'thought' }); +console.log(entry); // Should have id, createdAt, updatedAt +const retrieved = entryRepository.getById(entry.id); +console.log(retrieved); // Should match entry +entryRepository.delete(entry.id); +``` +TypeScript compilation should pass with `npm run check`. + + +EntryRepository interface and implementation exist with create, getById, getAll, update, delete, count methods. +All methods are type-safe with Drizzle schema types. + + + + + Task 2: Create data directory, server hooks, and verification page + + data/.gitkeep + data/attachments/.gitkeep + src/hooks.server.ts + src/routes/+page.server.ts + src/routes/+page.svelte + + +1. Create data directory structure for persistence: + +```bash +mkdir -p data/attachments +touch data/.gitkeep +touch data/attachments/.gitkeep +``` + +Add to `.gitignore`: +``` +# Data directory (persistent data) +data/*.db +data/*.db-wal +data/*.db-shm +data/attachments/* +!data/.gitkeep +!data/attachments/.gitkeep +``` + +2. Create `src/hooks.server.ts` for database initialization: + +```typescript +import type { Handle } from '@sveltejs/kit'; +import { db } from '$lib/server/db'; +import { entries } from '$lib/server/db/schema'; +import { sql } from 'drizzle-orm'; + +// Ensure database tables exist on first request +let initialized = false; + +export const handle: Handle = async ({ event, resolve }) => { + if (!initialized) { + // Run a simple query to ensure connection works + // Drizzle with push handles schema creation, but this verifies connectivity + try { + db.select().from(entries).limit(1).all(); + initialized = true; + console.log('Database initialized successfully'); + } catch (error) { + console.error('Database initialization failed:', error); + // Don't block - let the error propagate to the request + } + } + + return resolve(event); +}; +``` + +3. Create `src/routes/+page.server.ts` for verification data: + +```typescript +import type { PageServerLoad } from './$types'; +import { entryRepository } from '$lib/server/db/repository'; + +export const load: PageServerLoad = async () => { + const count = entryRepository.count(); + + // Create a test entry if none exist (for verification) + let testEntry = null; + if (count === 0) { + testEntry = entryRepository.create({ + title: 'Foundation Test', + content: 'This entry was created to verify the foundation is working.', + type: 'thought', + }); + } + + const entries = entryRepository.getAll({ limit: 5 }); + + return { + dbStatus: 'connected', + entryCount: testEntry ? count + 1 : count, + recentEntries: entries, + }; +}; +``` + +4. Update `src/routes/+page.svelte` to show verification status: + +```svelte + + +
+
+

TaskPlanner

+

Foundation Phase Complete

+ +
+

System Status

+ +
+
+ + Database: {data.dbStatus} +
+
+ + Entries in database: {data.entryCount} +
+
+ + Repository layer: operational +
+
+
+ + {#if data.recentEntries.length > 0} +
+

Recent Entries

+
    + {#each data.recentEntries as entry} +
  • +
    {entry.title || '(no title)'}
    +
    {entry.content}
    +
    + Type: {entry.type} | Created: {new Date(entry.createdAt).toLocaleString()} +
    +
  • + {/each} +
+
+ {/if} +
+
+``` +
+ +1. `ls data/` - should show .gitkeep and attachments/ +2. `npm run dev` - server starts without errors +3. Visit http://localhost:5173 - page shows: + - "Database: connected" + - "Entries in database: 1" (or more) + - "Repository layer: operational" + - The test entry displayed in "Recent Entries" +4. Check `data/taskplaner.db` exists +5. Run `npm run check` - no TypeScript errors + + +Data directory structure created with .gitkeep files. +Server hooks initialize database on first request. +Verification page displays database status and entry count. +Full end-to-end data flow works: hooks -> repository -> page. + +
+ +
+ + +1. Repository CRUD operations work (create, read, update, delete) +2. Database auto-initializes on first server request +3. Data directory exists with attachments subdirectory +4. Verification page shows green status for all systems +5. Test entry appears in recent entries list +6. `npm run check` passes +7. `npm run dev` serves the page without errors + + + +- EntryRepository provides typed create, getById, getAll, update, delete, count operations +- Database initializes automatically via server hooks +- Data directory structure exists (data/, data/attachments/) +- Verification page confirms database connected, entry count, repository operational +- Test entry created and displayed proving end-to-end data flow + + + +After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md` +