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 <noreply@anthropic.com>
This commit is contained in:
@@ -31,12 +31,11 @@ Decimal phases appear between their surrounding integers in numeric order.
|
|||||||
3. Repository layer provides typed CRUD operations for entries
|
3. Repository layer provides typed CRUD operations for entries
|
||||||
4. Filesystem storage directory structure exists for future images
|
4. Filesystem storage directory structure exists for future images
|
||||||
5. Development server starts and serves a basic page
|
5. Development server starts and serves a basic page
|
||||||
**Plans**: TBD
|
**Plans**: 2 plans
|
||||||
|
|
||||||
Plans:
|
Plans:
|
||||||
- [ ] 01-01: TBD
|
- [ ] 01-01-PLAN.md — SvelteKit project setup with Drizzle schema and unified entries table
|
||||||
- [ ] 01-02: TBD
|
- [ ] 01-02-PLAN.md — Repository layer with typed CRUD and verification page
|
||||||
- [ ] 01-03: TBD
|
|
||||||
|
|
||||||
### Phase 2: Core CRUD
|
### Phase 2: Core CRUD
|
||||||
**Goal**: Users can create, manage, and view entries with a responsive, accessible UI
|
**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 |
|
| Phase | Plans Complete | Status | Completed |
|
||||||
|-------|----------------|--------|-----------|
|
|-------|----------------|--------|-----------|
|
||||||
| 1. Foundation | 0/3 | Not started | - |
|
| 1. Foundation | 0/2 | Planned | - |
|
||||||
| 2. Core CRUD | 0/4 | Not started | - |
|
| 2. Core CRUD | 0/4 | Not started | - |
|
||||||
| 3. Images | 0/3 | Not started | - |
|
| 3. Images | 0/3 | Not started | - |
|
||||||
| 4. Tags & Organization | 0/3 | Not started | - |
|
| 4. Tags & Organization | 0/3 | Not started | - |
|
||||||
|
|||||||
226
.planning/phases/01-foundation/01-01-PLAN.md
Normal file
226
.planning/phases/01-foundation/01-01-PLAN.md
Normal file
@@ -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"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
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.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@/home/tho/.claude/get-shit-done/workflows/execute-plan.md
|
||||||
|
@/home/tho/.claude/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/research/STACK.md
|
||||||
|
@.planning/research/ARCHITECTURE.md
|
||||||
|
@.planning/phases/01-foundation/01-CONTEXT.md
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Create SvelteKit project with dependencies</name>
|
||||||
|
<files>
|
||||||
|
package.json
|
||||||
|
tsconfig.json
|
||||||
|
svelte.config.js
|
||||||
|
vite.config.ts
|
||||||
|
src/app.html
|
||||||
|
src/app.css
|
||||||
|
src/routes/+page.svelte
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
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
|
||||||
|
<h1 class="text-2xl font-bold text-blue-600">TaskPlanner</h1>
|
||||||
|
<p class="text-gray-600">Foundation setup complete.</p>
|
||||||
|
```
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
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.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
SvelteKit dev server runs, Tailwind processes classes, all dependencies installed.
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Configure Drizzle schema with entries table</name>
|
||||||
|
<files>
|
||||||
|
src/lib/server/db/schema.ts
|
||||||
|
src/lib/server/db/index.ts
|
||||||
|
drizzle.config.ts
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
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.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
Drizzle schema defines entries table with id, title, content, type, status, pinned, dueDate, createdAt, updatedAt.
|
||||||
|
Database file created with schema applied. WAL mode enabled.
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
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
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- 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
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/01-foundation/01-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
356
.planning/phases/01-foundation/01-02-PLAN.md
Normal file
356
.planning/phases/01-foundation/01-02-PLAN.md
Normal file
@@ -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"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
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.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@/home/tho/.claude/get-shit-done/workflows/execute-plan.md
|
||||||
|
@/home/tho/.claude/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.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
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Create EntryRepository with typed CRUD operations</name>
|
||||||
|
<files>
|
||||||
|
src/lib/server/db/repository.ts
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
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<NewEntry, 'id' | 'createdAt' | 'updatedAt'>): Entry;
|
||||||
|
getById(id: string): Entry | undefined;
|
||||||
|
getAll(options?: { limit?: number; offset?: number }): Entry[];
|
||||||
|
update(id: string, updates: Partial<Omit<Entry, 'id' | 'createdAt'>>): Entry | undefined;
|
||||||
|
delete(id: string): boolean;
|
||||||
|
count(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SQLiteEntryRepository implements EntryRepository {
|
||||||
|
create(entry: Omit<NewEntry, 'id' | 'createdAt' | 'updatedAt'>): 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<Omit<Entry, 'id' | 'createdAt'>>): 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
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
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`.
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
EntryRepository interface and implementation exist with create, getById, getAll, update, delete, count methods.
|
||||||
|
All methods are type-safe with Drizzle schema types.
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Create data directory, server hooks, and verification page</name>
|
||||||
|
<files>
|
||||||
|
data/.gitkeep
|
||||||
|
data/attachments/.gitkeep
|
||||||
|
src/hooks.server.ts
|
||||||
|
src/routes/+page.server.ts
|
||||||
|
src/routes/+page.svelte
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
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
|
||||||
|
<script lang="ts">
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main class="min-h-screen bg-gray-50 p-8">
|
||||||
|
<div class="max-w-2xl mx-auto">
|
||||||
|
<h1 class="text-3xl font-bold text-gray-900 mb-2">TaskPlanner</h1>
|
||||||
|
<p class="text-gray-600 mb-8">Foundation Phase Complete</p>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow p-6 mb-6">
|
||||||
|
<h2 class="text-lg font-semibold text-gray-800 mb-4">System Status</h2>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-green-500"></span>
|
||||||
|
<span class="text-gray-700">Database: {data.dbStatus}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-green-500"></span>
|
||||||
|
<span class="text-gray-700">Entries in database: {data.entryCount}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-green-500"></span>
|
||||||
|
<span class="text-gray-700">Repository layer: operational</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if data.recentEntries.length > 0}
|
||||||
|
<div class="bg-white rounded-lg shadow p-6">
|
||||||
|
<h2 class="text-lg font-semibold text-gray-800 mb-4">Recent Entries</h2>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
{#each data.recentEntries as entry}
|
||||||
|
<li class="p-3 bg-gray-50 rounded">
|
||||||
|
<div class="font-medium text-gray-900">{entry.title || '(no title)'}</div>
|
||||||
|
<div class="text-sm text-gray-600">{entry.content}</div>
|
||||||
|
<div class="text-xs text-gray-400 mt-1">
|
||||||
|
Type: {entry.type} | Created: {new Date(entry.createdAt).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
```
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
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
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
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.
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
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
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- 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
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`
|
||||||
|
</output>
|
||||||
Reference in New Issue
Block a user