diff --git a/.gitignore b/.gitignore index 9bf0e74..1a184c7 100644 --- a/.gitignore +++ b/.gitignore @@ -47,9 +47,6 @@ Thumbs.db coverage/ .nyc_output/ -# Steering files (task management - temporary) -.steering/* -!.steering/.gitkeep # Lock files (keep package-lock.json for consistency) # yarn.lock diff --git a/.steering/20260624-execute-m1-m4-milestones/design.md b/.steering/20260624-execute-m1-m4-milestones/design.md new file mode 100644 index 0000000..e69de29 diff --git a/.steering/20260624-execute-m1-m4-milestones/requirements.md b/.steering/20260624-execute-m1-m4-milestones/requirements.md new file mode 100644 index 0000000..e69de29 diff --git a/.steering/20260624-execute-m1-m4-milestones/tasklist.md b/.steering/20260624-execute-m1-m4-milestones/tasklist.md new file mode 100644 index 0000000..e69de29 diff --git a/.steering/20260624-m1-m4-foundation/design.md b/.steering/20260624-m1-m4-foundation/design.md new file mode 100644 index 0000000..0588dab --- /dev/null +++ b/.steering/20260624-m1-m4-foundation/design.md @@ -0,0 +1,189 @@ +# Design Document + +## Architecture Overview + +Layered architecture (UI → Service → Repository → Data) per `docs/architecture.md`. Next.js 15 App Router with Node.js Runtime only (no Edge Runtime). SQLite via better-sqlite3 accessed exclusively through a custom SQL wrapper. No Prisma. + +``` +app/ (UI: Route Handlers / Server Components / SSE) + ↓ +services/ (business logic, permission checks, transactions) + ↓ +repositories/ (SQL, parameter binding, logical-delete filter) + ↓ +lib/db/ (SQLite connection, SQL wrapper, Migration) +``` + +## Component Design + +### 1. SQL Wrapper: `lib/db/sqlite.ts` + +**Responsibilities**: +- SQLite connection management (singleton getDb(), WAL, foreign_keys ON) +- query (multi-row), get (single-row), execute (insert/update/delete), transaction, close +- Error handling + +**Implementation highlights**: +- dbPath = process.env.SQLITE_PATH ?? "./data/app.db" +- SqlParams = Record | unknown[] +- All repositories use this wrapper; direct better-sqlite3 access forbidden + +### 2. Migrator: `lib/db/migrator.ts` + +**Responsibilities**: +- Create schema_migrations table +- Read .sql files from migrations dir in filename order +- Skip already-applied files (tracked in schema_migrations) +- Execute each file in a transaction; rollback on failure + +### 3. Initial Schema: `lib/db/migrations/001_initial.sql` + +16 tables: users, projects, project_members, board_threads, board_comments, chat_messages, todo_columns, todo_items, file_assets, project_notes, milestones, calendar_events, meetings, meeting_members, notifications, activity_logs + indexes. + +### 4. Entity Types: `lib/types/` + +PascalCase type files for each entity + union types (UserRole, UserStatus, ProjectStatus, ProjectMemberRole, etc.). + +### 5. UserRepository + +findById, findByEmail, create (with passwordHash), update (name/avatarUrl/role/status). + +### 6. AuthService + +register (hash password, create user), login (verify hash, issue session), logout, getCurrentUser, updateProfile. Uses bcrypt. + +### 7. Session: `lib/auth/session.ts`, `lib/auth/getCurrentUser.ts` + +Cookie-based session. getCurrentUser resolves the user from the request cookie. + +### 8. ProjectRepository / ProjectMemberRepository + +Project: findById, findByOwner, findByUser (via join), create, update, delete. +ProjectMember: findByProject, findByUser, add, remove, isMember, getRole. + +### 9. ProjectService + +createProject (owner becomes admin member), updateProject, addMember (permission check + notification hook later), removeMember, archiveProject, getDashboard skeleton. + +## Data Flow + +### Login +1. POST /api/auth/login → AuthService.login(email, password) +2. AuthService → UserRepository.findByEmail → verify bcrypt hash +3. Issue session cookie → return user + +### Project creation +1. POST /api/projects → ProjectService.createProject(actorId, input) +2. ProjectService → permission (any authenticated user) → ProjectRepository.create +3. ProjectMemberRepository.add(projectId, ownerId, 'admin') +4. Return project + +## Error Handling Strategy + +### Custom Error Classes +- ValidationError (400) - field, value +- ForbiddenError (403) +- NotFoundError (404) - resource, id +- (409 for unique constraint - handled via SQLite error code) + +### Error Handling Patterns +Route Handlers catch errors and map to HTTP status. Expected errors use custom classes; unexpected errors propagate + log. + +## Test Strategy + +### Unit Tests (Vitest) +- lib/db/sqlite.test.ts (query/get/execute/transaction, WAL, foreign keys) +- lib/db/migrator.test.ts (order, skip applied, rollback) +- repositories/UserRepository.test.ts +- services/AuthService.test.ts +- repositories/ProjectRepository.test.ts +- repositories/ProjectMemberRepository.test.ts +- services/ProjectService.test.ts + +Tests use a temp SQLite file (in-memory or tmpdir) to avoid touching real data/. + +### Integration Tests (Vitest) +- project-member-permission.test.ts (non-member cannot access project data) + +### E2E Tests (Playwright) +- auth.spec.ts (login, logout, protected screen) +- project-management.spec.ts (create, edit, member add/remove, archive) + +## Dependent Libraries + +```json +{ + "dependencies": { + "next": "15", + "react": "19", + "react-dom": "19", + "better-sqlite3": "^11", + "bcrypt": "^5", + "react-markdown": "^9", + "remark-gfm": "^4", + "rehype-sanitize": "^6" + }, + "devDependencies": { + "tailwindcss": "^3", + "@types/better-sqlite3": "^7", + "@types/bcrypt": "^5", + "@playwright/test": "^1", + "tsx": "^4" + } +} +``` + +## Directory Structure + +``` +app/ + api/auth/{register,login,logout,me}/route.ts + api/users/me/route.ts + api/projects/route.ts + api/projects/[projectId]/route.ts + api/projects/[projectId]/members/route.ts + api/projects/[projectId]/members/[userId]/route.ts + api/admin/migrations/route.ts + login/page.tsx + profile/page.tsx + dashboard/page.tsx + projects/[projectId]/{page,members,settings}.tsx + layout.tsx, globals.css +lib/ + db/{sqlite.ts, migrator.ts, run-migrations.ts, migrations/001_initial.sql} + auth/{session.ts, getCurrentUser.ts} + types/*.ts + validators/{userValidator.ts, projectValidator.ts} +repositories/{UserRepository,ProjectRepository,ProjectMemberRepository}.ts +services/{AuthService,ProjectService}.ts +components/layout/{Header,Sidebar,ProjectNav}.tsx +tests/unit/..., tests/integration/..., tests/e2e/... +``` + +## Implementation Order + +1. M1 (branch feature/m1-setup): Next.js setup, deps, config, dir structure, remove boilerplate → merge to main +2. M2 (branch feature/m2-db-foundation): SQL wrapper, migrator, schema, types, migration API, unit tests → merge to main +3. M3 (branch feature/m3-auth-user): UserRepository, AuthService, session, validators, auth API, login/profile pages, unit + e2e → merge to main +4. M4 (branch feature/m4-project-member): Project/Member repos, ProjectService, project API, dashboard/members/settings pages, unit + integration + e2e → merge to main + +## Security Considerations + +- Passwords hashed with bcrypt (never plaintext) +- All SQL parameter-bound (no string concatenation) +- Auth required on all protected routes (except /api/auth/register, /api/auth/login) +- Project access requires isMember check +- Admin features require role='system_admin' +- .env not committed; secrets via environment + +## Performance Considerations + +- SQLite WAL mode for read/write concurrency +- Pagination on list endpoints +- Singleton DB connection + +## Future Extensibility + +- Repository pattern allows adding tables without changing wrapper +- SseEvent type extensible (M8) +- NotificationService/ActivityLogService hooks prepared for M5 (ProjectService.addMember will call notification in M5) diff --git a/.steering/20260624-m1-m4-foundation/requirements.md b/.steering/20260624-m1-m4-foundation/requirements.md new file mode 100644 index 0000000..7095d08 --- /dev/null +++ b/.steering/20260624-m1-m4-foundation/requirements.md @@ -0,0 +1,102 @@ +# Requirements + +## Overview + +Implement milestones M1-M4 of the Simple Groupware project: project foundation setup (Next.js 15), DB foundation (SQL wrapper + Migration), authentication & user management, and project & member management. Each milestone is developed on a separate branch and merged to main. Unit tests and E2E tests are mandatory. + +## Background + +The repository is currently a spec-driven-development boilerplate (vitest/eslint/prettier/husky configured, but no Next.js, no react, no SQLite). It must be transformed into the foundation of the Simple Groupware: a Next.js 15 + TypeScript + SQLite(better-sqlite3) application following the layered architecture (UI → Service → Repository → Data) defined in `docs/architecture.md`. M1-M4 establish the base that all subsequent milestones (M5+) depend on. + +## Features to Implement + +### 1. M1: Project Foundation Setup +- Next.js 15 (App Router, TypeScript) project configuration +- Tailwind CSS setup +- tsconfig with `@/*` path alias, strict mode +- next.config, .env.example +- Directory structure: app/, lib/, repositories/, services/, components/, tests/, data/, backups/ +- ESLint (with Next.js plugin), Prettier, Husky, lint-staged, Vitest, Playwright setup +- CI config (GitHub Actions) +- package.json scripts: lint, format, typecheck, test, test:e2e, migrate, dev, build +- Dependencies: next, react, react-dom, better-sqlite3, bcrypt, react-markdown, remark-gfm, rehype-sanitize, tailwindcss, @types/better-sqlite3 +- Remove boilerplate src/example.ts and src/example.test.ts + +### 2. M2: DB Foundation (SQL Wrapper + Migration) +- `lib/db/sqlite.ts`: SqliteDatabase class (query/get/execute/transaction/close) + getDb() singleton (WAL, foreign_keys ON) +- `lib/db/migrator.ts`: Migrator class (filename-order execution, skip applied, 1-file-1-transaction, rollback on failure) +- `lib/db/migrations/001_initial.sql`: all 16 tables + indexes +- `lib/types/`: all Entity type definitions + enums +- `lib/db/run-migrations.ts`: migration runner script +- API: `GET /api/admin/migrations` (admin-only migration status) +- Unit tests: sqlite.test.ts, migrator.test.ts + +### 3. M3: Authentication & User Management +- UserRepository (findById/findByEmail/create/update) +- AuthService (register/login/logout/getCurrentUser/updateProfile) with bcrypt password hashing +- Role management (system_admin/project_admin/member/guest), account enable/disable +- lib/auth/ (session.ts, getCurrentUser.ts) +- lib/validators/userValidator.ts +- API: register, login, logout, me, users/me +- Login page, profile page, root layout +- Auth middleware (redirect unauthenticated to login) +- Unit tests: UserRepository, AuthService; E2E test: auth.spec.ts + +### 4. M4: Project & Member Management +- ProjectRepository, ProjectMemberRepository +- ProjectService (createProject/updateProject/addMember/removeMember/archiveProject/getDashboard) with permission checks +- projectValidator.ts +- API: projects CRUD, members CRUD +- Dashboard (project list), project overview, members page, settings page +- Layout components (Header, Sidebar, ProjectNav) +- Unit tests: ProjectRepository, ProjectMemberRepository, ProjectService; Integration test: project-member-permission; E2E test: project-management.spec.ts + +## Acceptance Criteria + +### M1 +- [ ] `npm run dev` starts the Next.js dev server +- [ ] `npm run lint`, `npm run typecheck`, `npm run build` succeed +- [ ] Directory structure matches repository-structure.md + +### M2 +- [ ] `npm run migrate` creates the initial schema +- [ ] Migrations run in filename order and skip applied ones +- [ ] Failed migration rolls back +- [ ] Unit tests pass + +### M3 +- [ ] User registration, login, logout work +- [ ] Password is hashed with bcrypt +- [ ] Profile & avatar editable +- [ ] Inactive accounts cannot log in +- [ ] Unauthenticated access to protected screens is blocked +- [ ] Unit & E2E tests pass + +### M4 +- [ ] Project create/edit/delete/archive work +- [ ] Member add/remove/role work +- [ ] Non-members get 403 +- [ ] Unit, Integration & E2E tests pass + +## Success Metrics + +- All Unit tests pass (`npm test`) +- All E2E tests pass (`npm run test:e2e`) +- Lint and typecheck clean +- Each milestone merged to main on its own branch + +## Out of Scope + +The following will not be implemented in this phase: +- M5-M15 (notifications, board, chat, SSE, todo, files, calendar, milestones, meetings, search, dashboard completion, backup) +- E2E tests for features beyond M3/M4 scope (board, chat, etc.) +- Full project dashboard content (only skeleton in M4; full content in M13) + +## Reference Documents + +- `docs/product-requirements.md` - Product Requirements Document +- `docs/functional-design.md` - Functional design document +- `docs/architecture.md` - Architecture design document +- `docs/repository-structure.md` - Repository structure document +- `docs/development-guidelines.md` - Development guidelines +- `docs/milestones.md` - Milestone & task definitions diff --git a/.steering/20260624-m1-m4-foundation/tasklist.md b/.steering/20260624-m1-m4-foundation/tasklist.md new file mode 100644 index 0000000..bf59232 --- /dev/null +++ b/.steering/20260624-m1-m4-foundation/tasklist.md @@ -0,0 +1,150 @@ +# Task List + +## 🚨 Principle of Fully Completing Tasks + +**Keep working until all tasks in this file are complete** + +### Mandatory Rules +- **Make every task `[x]`** +- "Planned as a separate task due to time constraints" is forbidden +- "Postponed because the implementation is too complex" is forbidden +- Do not finish work while leaving incomplete tasks (`[ ]`) + +### The Only Case Where Skipping a Task Is Permitted +Skipping is possible only when one of the following technical reasons applies: +- A change in the implementation approach made the feature itself unnecessary +- An architecture change replaced it with a different implementation method +- A change in dependencies made the task impossible to execute + +When skipping, always state the reason clearly: +```markdown +- [x] ~~task name~~ (unnecessary due to a change in approach: specific technical reason) +``` + +### If a Task Is Too Large +- Split the task into smaller subtasks +- Add the split subtasks to this file +- Complete the subtasks one by one + +--- + +## Phase 1: M1 - Project Foundation Setup (branch: feature/m1-setup) + +- [x] Create branch `feature/m1-setup` from main +- [x] Update package.json: name, scripts (dev/build/lint/format/typecheck/test/test:e2e/migrate), dependencies (next, react, react-dom, better-sqlite3, bcrypt, react-markdown, remark-gfm, rehype-sanitize), devDependencies (tailwindcss, @types/better-sqlite3, @types/bcrypt, @playwright/test, tsx, eslint-config-next) +- [x] Run `npm install` and confirm dependencies install successfully +- [x] Configure Next.js: create next.config.mjs (Node.js runtime), update tsconfig.json for Next.js (@/* path alias, jsx preserve, next plugin types), create app/globals.css (Tailwind directives), create tailwind.config.ts, postcss.config.mjs +- [x] Create Next.js app structure: app/layout.tsx (root layout), app/page.tsx (home redirect), app/globals.css +- [x] Create directory structure: lib/, repositories/, services/, components/, tests/unit, tests/integration, tests/e2e, data/.gitkeep, backups/.gitkeep +- [x] Remove boilerplate: delete src/example.ts, src/example.test.ts +- [x] Update vitest.config.ts (include tests/**, exclude .next), create playwright.config.ts +- [x] Update eslint.config.js (add Next.js plugin/ignores for .next), update .prettierrc if needed +- [x] Create .env.example (SQLITE_PATH, SESSION_SECRET) +- [x] Create CI config .github/workflows/ci.yml (lint, typecheck, test, build) +- [x] Commit M1, merge feature/m1-setup to main, push + +## Phase 2: M2 - DB Foundation (branch: feature/m2-db-foundation) + +- [ ] Create branch `feature/m2-db-foundation` from main +- [ ] Implement `lib/db/sqlite.ts`: SqliteDatabase class (query/get/execute/transaction/close) + getDb() singleton (WAL, foreign_keys ON) +- [ ] Implement `lib/db/migrator.ts`: Migrator class (schema_migrations table, filename-order, skip applied, 1-file-1-tx, rollback) +- [ ] Create `lib/db/migrations/001_initial.sql`: all 16 tables + indexes +- [ ] Create `lib/types/`: all Entity types + enums (User, Project, ProjectMember, BoardThread, BoardComment, ChatMessage, TodoColumn, TodoItem, FileAsset, ProjectNote, Milestone, CalendarEvent, Meeting, MeetingMember, Notification, ActivityLog, SchemaMigration + union types) +- [ ] Create `lib/db/run-migrations.ts`: migration runner script +- [ ] Implement API `GET /api/admin/migrations` (migration status, admin-only) +- [ ] Create test helper `tests/helpers/db.ts` (temp SQLite DB factory) +- [ ] Write Unit test `tests/unit/lib/db/sqlite.test.ts` +- [ ] Write Unit test `tests/unit/lib/db/migrator.test.ts` +- [ ] Run `npm run migrate` to verify schema creation +- [ ] Commit M2, merge feature/m2-db-foundation to main, push + +## Phase 3: M3 - Auth & User Management (branch: feature/m3-auth-user) + +- [ ] Create branch `feature/m3-auth-user` from main +- [ ] Create custom error classes `lib/errors.ts` (ValidationError, ForbiddenError, NotFoundError) +- [ ] Implement `lib/auth/session.ts` (session cookie read/write) and `lib/auth/getCurrentUser.ts` +- [ ] Implement `lib/validators/userValidator.ts` +- [ ] Implement `repositories/UserRepository.ts` +- [ ] Implement `services/AuthService.ts` (register/login/logout/getCurrentUser/updateProfile, bcrypt) +- [ ] Implement API routes: register, login, logout, me, users/me +- [ ] Implement auth middleware/guard (protected routes redirect to login) +- [ ] Implement `app/login/page.tsx`, `app/profile/page.tsx`, update `app/layout.tsx` +- [ ] Write Unit test `tests/unit/repositories/UserRepository.test.ts` +- [ ] Write Unit test `tests/unit/services/AuthService.test.ts` +- [ ] Write E2E test `tests/e2e/auth.spec.ts` +- [ ] Commit M3, merge feature/m3-auth-user to main, push + +## Phase 4: M4 - Project & Member Management (branch: feature/m4-project-member) + +- [ ] Create branch `feature/m4-project-member` from main +- [ ] Implement `lib/validators/projectValidator.ts` +- [ ] Implement `repositories/ProjectRepository.ts` +- [ ] Implement `repositories/ProjectMemberRepository.ts` +- [ ] Implement `services/ProjectService.ts` (createProject/updateProject/addMember/removeMember/archiveProject/getDashboard skeleton, permission checks) +- [ ] Implement API routes: projects (list/create), projects/[projectId] (detail/edit/delete), members (list/add), members/[userId] (remove) +- [ ] Implement `app/dashboard/page.tsx` (project list skeleton) +- [ ] Implement `app/projects/[projectId]/page.tsx` (overview skeleton) +- [ ] Implement `app/projects/[projectId]/members/page.tsx` +- [ ] Implement `app/projects/[projectId]/settings/page.tsx` +- [ ] Implement layout components: components/layout/Header.tsx, Sidebar.tsx, ProjectNav.tsx +- [ ] Write Unit test `tests/unit/repositories/ProjectRepository.test.ts` +- [ ] Write Unit test `tests/unit/repositories/ProjectMemberRepository.test.ts` +- [ ] Write Unit test `tests/unit/services/ProjectService.test.ts` +- [ ] Write Integration test `tests/integration/project-member-permission.test.ts` +- [ ] Write E2E test `tests/e2e/project-management.spec.ts` +- [ ] Commit M4, merge feature/m4-project-member to main, push + +## Phase 5: Quality Check and Fixes + +- [ ] Confirm that all tests pass + - [ ] `npm test` +- [ ] Confirm that there are no lint errors + - [ ] `npm run lint` +- [ ] Confirm that there are no type errors + - [ ] `npm run typecheck` +- [ ] Confirm that the build succeeds + - [ ] `npm run build` + +## Phase 6: Documentation Updates + +- [ ] Update README.md (setup instructions, scripts) +- [ ] Post-implementation retrospective (record at the bottom of this file) + +--- + +## Post-Implementation Retrospective + +### Implementation Completion Date +{YYYY-MM-DD} + +### Differences Between Plan and Actual + +**Points that differed from the plan**: +- {Technical changes not anticipated at planning time} +- {Changes in the implementation approach and the reasons} + +**Tasks that became newly necessary**: +- {Tasks added during implementation} +- {Why the addition was necessary} + +**Tasks skipped for technical reasons** (only when applicable): +- {Task name} + - Reason for skipping: {specific technical reason} + - Alternative implementation: {what it was replaced with} + +**⚠️ Note**: Do not list tasks skipped for reasons such as "time constraints" or "difficulty" here. Completing all tasks is the principle. + +### Lessons Learned + +**Technical insights**: +- {Technical knowledge gained through implementation} +- {New technologies or patterns used} + +**Process improvements**: +- {What went well in task management} +- {How the steering files were leveraged} + +### Improvement Suggestions for Next Time +- {Things to watch out for in the next feature addition} +- {More efficient implementation methods} +- {Improvements to task planning}