# 16 — Phased Roadmap Eleven phases, each independently buildable and verifiable. A build agent starting a phase should read: `00`, `01`, `02`, the files listed for the phase, and nothing else. **Check off tasks and acceptance criteria here (and the phase box in `00-overview.md`) as they are completed and verified.** Dependency graph: `0 → 1 → 2 → 3 → 4 → 5 → 6 → {7, 8} → 9 → 10` (7 and 8 are parallel-safe after 6). ## Phase 0 — Foundation Spec files: [01-architecture.md](01-architecture.md), [14-design-system.md](14-design-system.md), [15-deployment.md](15-deployment.md) - [ ] Repo skeleton per architecture layout; Go module `solopm.com/solopm-server` - [ ] chi server + middleware chain (minus auth resolver), slog, request logging, graceful shutdown - [ ] Config loader with full env var set + `.env.example` - [ ] golang-migrate wired (embedded migrations) + migration 0001 (extensions + enums); sqlc configured and generating - [ ] Vue app scaffolded (Vite, TS, Pinia, Router, Tailwind) with design system copied to `web/design-system/` and wired; AppShell (sidebar/navbar) renders - [ ] `go:embed` SPA serving with deep-link fallback; Vite dev proxy - [ ] docker-compose (app + postgres + meilisearch + mailhog dev profile); Makefile targets - [ ] `/healthz` Acceptance: - [ ] `docker compose up` serves the SPA shell at `:4080`; `/healthz` reports db ok - [ ] `make migrate-up && make migrate-down` cycles cleanly - [ ] Shell passes the design-system conformity checklist ## Phase 1 — Auth & users Spec files: [05-auth.md](05-auth.md) · Depends: 0 - [ ] Migrations: users (+seeds), oauth_accounts, sessions, api_tokens - [ ] Register/login/logout with scs sessions; first-user-becomes-admin; rate limiting - [ ] GitHub + Gitea OAuth (linking rules incl. verified-email guard) - [ ] PAT create/list/revoke + bearer middleware - [ ] `/me` endpoints, `/users` picker, admin user endpoints - [ ] Frontend: login/register/oauth-callback pages, auth store + guard, settings→profile/security/tokens tabs, admin users page Acceptance (scriptable with curl): - [ ] Register → is_admin true; second register → false - [ ] Cookie-authed and PAT-authed `GET /me` both succeed; revoked PAT → 401 - [ ] system/zombie users absent from `/users`; login as ids 1–2 impossible - [ ] OAuth flow against a real or mocked provider produces a linked account (verified-email path unit-tested) ## Phase 2 — Event backbone & audit Spec files: [03-domain-events.md](03-domain-events.md) · Depends: 1 - [ ] Migrations: audit_log, events, jobs - [ ] `events.Emit` (audit + outbox in-tx), pg_notify, dispatcher goroutine, job worker pool with backoff/dead states - [ ] `auth.*` and `user.*` events emitting through the pipeline - [ ] Admin jobs inspection endpoint Acceptance: - [ ] Login writes an audit row and a processed event row - [ ] `kill -9` during a pending job → job retried after restart (integration test) - [ ] A job failing 8 times lands in `dead` and is visible via admin endpoint ## Phase 3 — Projects core Spec files: [06-projects.md](06-projects.md) · Depends: 2 - [ ] Migrations: projects, project_members, project_dependencies, project_favorites, labels - [ ] Project CRUD + status/completed_at automation + owner transfer + membership + favorites + label CRUD/seeding + dependencies with cycle detection - [ ] `RequireProjectMember`/`RequireProjectOwner` middleware; non-member 404s - [ ] Activity feed endpoints (project + dashboard) - [ ] All mutations emit events per taxonomy Acceptance: - [ ] Non-member GET on another's project → 404 - [ ] Dependency cycle attempt → 409 `cycle_detected` - [ ] Every mutation appears in `/projects/{id}/activity` with correct actor - [ ] Sole-owner cannot be removed/deleted without transfer (409) ## Phase 4 — Issues & epics Spec files: [07-issues-epics.md](07-issues-epics.md) · Depends: 3 - [ ] Migrations: epics, issues, issue_blockers, issue_labels, issue_watchers - [ ] Issue CRUD, numbering, sub-issues, blockers + cycles, labels, points, completed_at automation - [ ] board_rank helpers (`Between/Before/After`) with unit tests; `?view=board`; list filters/sort - [ ] Epic CRUD, attach/detach/reorder issues, computed progress (counts + points) Acceptance: - [ ] Two projects both mint `#1`; 20 parallel creates in one project yield 20 unique consecutive numbers - [ ] Status→done sets completed_at; back to in_progress clears it - [ ] Blocker cycle → 409; epic progress matches a hand-computed fixture - [ ] Board view returns rank-ordered groups; a move lands between its neighbors ## Phase 5 — Views (frontend core) Spec files: [13-frontend.md](13-frontend.md) (+ [14-design-system.md](14-design-system.md)) · Depends: 4 - [ ] Dashboard, project list/overview, project settings (general/members/labels tabs) - [ ] Issue table with FilterBar + URL-synced state - [ ] Kanban board with optimistic drag-and-drop - [ ] Issue detail page (all sidebar fields editable, sub-issues, blockers) - [ ] Epic list/detail pages - [ ] Stores: projects, issues, board, epics; badges/pickers/dialog components Acceptance: - [ ] Drag card between columns → persists after reload (status + position) - [ ] Filters/sort survive refresh via URL; shareable link reproduces the view - [ ] Failed PATCH rolls back the optimistic move with a toast - [ ] New pages pass the conformity checklist ## Phase 6 — Collaboration Spec files: [08-collaboration.md](08-collaboration.md) · Depends: 5 - [ ] Migrations: comments, links, attachments, wiki_pages - [ ] Comments CRUD + resolve; polymorphic links/attachments + `/files/` auth-checked serving; wiki CRUD with slug rules + conflict warning - [ ] Server-side mention/#ref extraction into event payloads; watcher auto-watch/mute semantics - [ ] Frontend: MarkdownEditor/MarkdownView (mention + #ref autocomplete/rendering, paste-to-attach), comments UI with resolve, attachments/links UI, wiki pages, WatchButton + watcher list Acceptance: - [ ] `@user` in a comment produces a mention entry in the event payload; `#3` renders as a link to issue 3 - [ ] Resolving collapses the thread; unresolve restores it - [ ] Upload lands at the specified disk path; non-member GET on `/files/…` → 404; oversized upload → 413 - [ ] Unwatch (mute) then comment again → no re-auto-watch ## Phase 7 — Notifications & real-time Spec files: [09-notifications-realtime.md](09-notifications-realtime.md) · Depends: 6 - [ ] Migration: notifications - [ ] Fan-out consumer (rules + precedence + dedupe), inbox/bell endpoints - [ ] SSE hub + `/stream` + heartbeats; frontend sse.ts + realtime store + in-place updates - [ ] SMTP sender, instant emails, daily digest job, email_mode setting UI Acceptance: - [ ] Two-browser test: assign in A → B's bell increments and B's board card moves within 2s, no reload - [ ] Actor never notified of own action; `mentioned` wins precedence over `commented` - [ ] MailHog shows instant email; digest job groups a day of notifications into one email - [ ] Muted watcher gets neither bell nor email ## Phase 8 — Search (parallel-safe with 7) Spec files: [10-search.md](10-search.md) · Depends: 6 - [ ] Meilisearch client, index bootstrap, event→sync mapping, `search_sync` job handler - [ ] `/search` endpoint with membership filtering; reindex CLI + admin endpoint - [ ] CommandPalette (Cmd+K) + `/search` page Acceptance: - [ ] Created issue findable in < 5s; soft-deleted issue disappears from results - [ ] Non-member's content never appears (integration test with two users) - [ ] `solopm reindex` rebuilds equal counts from Postgres truth - [ ] Palette opens with Cmd+K, arrows navigate, Enter routes ## Phase 9 — Integrations Spec files: [11-integrations.md](11-integrations.md) · Depends: 7 - [ ] Migrations: incoming_webhook_receipts, outgoing_webhooks, outgoing_webhook_deliveries - [ ] Incoming endpoint: HMAC verify, receipts, repo routing, closes-#N automation (system user, via_webhook), replay - [ ] Outgoing: CRUD, signed delivery jobs, delivery log, test endpoint, SSRF guard - [ ] Export JSON + CSV streaming - [ ] Frontend: settings→webhooks tab (secret, receipts, outgoing CRUD + deliveries), export tab Acceptance: - [ ] Replayed GitHub PR-merged fixture with valid signature closes `#N`; audit shows system user with via_webhook=true; bad signature → 401 with receipt logged invalid - [ ] Duplicate delivery id → 200 without reprocessing - [ ] Outgoing delivery retries against a flaky test receiver, then records success with correct `X-SoloPM-Signature` - [ ] JSON export contains every entity family; CSV opens with correct columns ## Phase 10 — Retention & hardening Spec files: [12-retention.md](12-retention.md) · Depends: 9 - [ ] Cull job + CLI + admin endpoint with dry-run; cascade scopes incl. disk files; zombie reassignment; auxiliary retention (notifications/events/jobs/receipts/deliveries) - [ ] Backup/restore docs + drill; systemd unit example; reverse-proxy doc - [ ] Final pass: rate limits verified, error envelope consistency, `/healthz` complete, README quickstart Acceptance: - [ ] Dry-run reports the exact would-delete set with zero deletions - [ ] Culling a project removes all its rows **and** files (orphan-check test passes) - [ ] Culled user's comments render as "Deleted User"; audit keeps original id - [ ] Fresh-clone `docker compose up` walkthrough from the README works end-to-end