---
phase: 02-data-layer
plan: 02
type: execute
wave: 2
depends_on: ["02-01"]
files_modified:
  - src/db/repositories/initiative-repository.ts
  - src/db/repositories/phase-repository.ts
  - src/db/repositories/plan-repository.ts
  - src/db/repositories/task-repository.ts
  - src/db/repositories/drizzle/initiative.ts
  - src/db/repositories/drizzle/phase.ts
  - src/db/repositories/drizzle/plan.ts
  - src/db/repositories/drizzle/task.ts
  - src/db/repositories/drizzle/index.ts
  - src/db/repositories/index.ts
  - src/db/index.ts
  - src/db/repositories/drizzle/*.test.ts
autonomous: true
---

<objective>
Create repository layer following hexagonal architecture with separate repositories per aggregate.

Purpose: Each aggregate (Initiative, Phase, Plan, Task) gets its own repository port + adapter. Clean boundaries, single responsibility, proper DDD.
Output: 4 repository interfaces, 4 Drizzle adapters, tests for each.
</objective>

<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-data-layer/02-01-SUMMARY.md
@src/events/types.ts
@src/events/bus.ts
@src/db/schema.ts
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create repository port interfaces</name>
  <files>src/db/repositories/initiative-repository.ts, src/db/repositories/phase-repository.ts, src/db/repositories/plan-repository.ts, src/db/repositories/task-repository.ts, src/db/repositories/index.ts</files>
  <action>
    Create separate repository PORT interfaces for each aggregate. Each repository only knows about its own aggregate.

    src/db/repositories/initiative-repository.ts:
    ```typescript
    export interface InitiativeRepository {
      create(data: Omit<NewInitiative, 'id' | 'createdAt' | 'updatedAt'>): Promise<Initiative>;
      findById(id: string): Promise<Initiative | null>;
      findAll(): Promise<Initiative[]>;
      update(id: string, data: Partial<...>): Promise<Initiative>;
      delete(id: string): Promise<void>;
    }
    ```

    src/db/repositories/phase-repository.ts:
    ```typescript
    export interface PhaseRepository {
      create(data: Omit<NewPhase, 'id' | 'createdAt' | 'updatedAt'>): Promise<Phase>;
      findById(id: string): Promise<Phase | null>;
      findByInitiativeId(initiativeId: string): Promise<Phase[]>;
      update(id: string, data: Partial<...>): Promise<Phase>;
      delete(id: string): Promise<void>;
    }
    ```

    src/db/repositories/plan-repository.ts:
    ```typescript
    export interface PlanRepository {
      create(data: Omit<NewPlan, 'id' | 'createdAt' | 'updatedAt'>): Promise<Plan>;
      findById(id: string): Promise<Plan | null>;
      findByPhaseId(phaseId: string): Promise<Plan[]>;
      update(id: string, data: Partial<...>): Promise<Plan>;
      delete(id: string): Promise<void>;
    }
    ```

    src/db/repositories/task-repository.ts:
    ```typescript
    export interface TaskRepository {
      create(data: Omit<NewTask, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task>;
      findById(id: string): Promise<Task | null>;
      findByPlanId(planId: string): Promise<Task[]>;
      update(id: string, data: Partial<...>): Promise<Task>;
      delete(id: string): Promise<void>;
    }
    ```

    src/db/repositories/index.ts - re-export all interfaces.

    Pattern: These are PORTS. Implementations are ADAPTERS. Same as EventBus in Phase 1.1.
    Import types from ../schema.js. Use Promise for all operations.
  </action>
  <verify>npm run build succeeds - interfaces compile correctly</verify>
  <done>4 separate repository interfaces exported, one per aggregate</done>
</task>

<task type="auto">
  <name>Task 2: Create Drizzle repository adapters</name>
  <files>src/db/repositories/drizzle/initiative.ts, src/db/repositories/drizzle/phase.ts, src/db/repositories/drizzle/plan.ts, src/db/repositories/drizzle/task.ts, src/db/repositories/drizzle/index.ts</files>
  <action>
    Implement each repository interface with a Drizzle adapter.

    Each adapter class:
    - Constructor takes DrizzleDatabase instance (dependency injection)
    - Implements its repository interface
    - Uses nanoid for generating IDs
    - Sets createdAt/updatedAt timestamps automatically
    - Orders results appropriately (phases by number, tasks by order)

    src/db/repositories/drizzle/initiative.ts:
    - DrizzleInitiativeRepository implements InitiativeRepository
    - CRUD operations on initiatives table only

    src/db/repositories/drizzle/phase.ts:
    - DrizzlePhaseRepository implements PhaseRepository
    - findByInitiativeId orders by number
    - Foreign key to initiative enforced by SQLite

    src/db/repositories/drizzle/plan.ts:
    - DrizzlePlanRepository implements PlanRepository
    - findByPhaseId orders by number
    - Foreign key to phase enforced by SQLite

    src/db/repositories/drizzle/task.ts:
    - DrizzleTaskRepository implements TaskRepository
    - findByPlanId orders by order field
    - Foreign key to plan enforced by SQLite

    src/db/repositories/drizzle/index.ts - re-export all adapters.

    Install nanoid if not present.
    Throw on update/delete if not found.
  </action>
  <verify>npm run build succeeds - no type errors in implementations</verify>
  <done>4 Drizzle adapters implement their respective interfaces</done>
</task>

<task type="auto">
  <name>Task 3: Write repository tests</name>
  <files>src/db/repositories/drizzle/initiative.test.ts, src/db/repositories/drizzle/phase.test.ts, src/db/repositories/drizzle/plan.test.ts, src/db/repositories/drizzle/task.test.ts</files>
  <action>
    Write tests for each Drizzle repository adapter using Vitest.

    Test setup (shared helper or per-file):
    - Use in-memory database (':memory:')
    - Push schema before each test (drizzle-kit push or direct SQL)
    - Fresh repository instance per test

    initiative.test.ts:
    - create generates id, sets timestamps
    - findById returns null for non-existent
    - findAll returns empty array initially
    - update changes fields and updatedAt
    - delete removes initiative (cascade tested separately)

    phase.test.ts:
    - create with valid initiativeId
    - create with invalid initiativeId throws (FK constraint)
    - findByInitiativeId returns only matching, ordered by number
    - update/delete work correctly

    plan.test.ts:
    - create with valid phaseId
    - create with invalid phaseId throws
    - findByPhaseId returns only matching, ordered by number
    - update/delete work correctly

    task.test.ts:
    - create with valid planId
    - create with invalid planId throws
    - findByPlanId returns only matching, ordered by order
    - update status changes work
    - delete works correctly

    Use describe/it pattern. Test behavior, not implementation.
  </action>
  <verify>npm test -- src/db/repositories/drizzle passes all tests</verify>
  <done>All repository adapters have test coverage</done>
</task>

<task type="auto">
  <name>Task 4: Update exports and test cascade deletes</name>
  <files>src/db/index.ts, src/db/repositories/drizzle/cascade.test.ts</files>
  <action>
    Update main db index to export repositories and add cascade delete test.

    src/db/index.ts:
    - Keep existing createDatabase export
    - Add: export * from './repositories/index.js'
    - Add: export * from './repositories/drizzle/index.js'

    src/db/repositories/drizzle/cascade.test.ts:
    - Test full hierarchy creation (initiative -> phase -> plan -> task)
    - Delete initiative, verify all children removed
    - Delete phase, verify plans and tasks removed
    - Delete plan, verify tasks removed
    - This tests SQLite foreign key cascade behavior through the adapters

    These tests ensure the cascade delete configured in schema.ts works correctly when using the repository layer.
  </action>
  <verify>npm test -- src/db/repositories passes all tests including cascade</verify>
  <done>Exports updated, cascade deletes proven working through repository layer</done>
</task>

</tasks>

<verification>
Before declaring plan complete:
- [ ] `npm run build` succeeds without errors
- [ ] `npm test -- src/db/repositories` passes all tests
- [ ] All 4 repository interfaces importable from src/db/index.ts
- [ ] All 4 adapters importable from src/db/index.ts
- [ ] Cascade deletes work through hierarchy
</verification>

<success_criteria>
- 4 separate repository port interfaces (one per aggregate)
- 4 Drizzle adapter implementations
- Each aggregate's repository only knows about its own table
- Tests prove CRUD operations work
- Cascade deletes verified
- No TypeScript errors
- All tests pass
</success_criteria>

<output>
After completion, create `.planning/phases/02-data-layer/02-02-SUMMARY.md`
</output>
