Files
solopm-server/plans/16-phases.md
T
2026-08-14 11:29:11 -06:00

9.6 KiB
Raw Blame History

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, 14-design-system.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 :8080; /healthz reports db ok — not verified: no docker CLI available in this environment. Functionally equivalent path verified instead: go run ./cmd/solopm serve against the real dockerized postgres/meilisearch returned SPA 200 and /healthz {"database":"ok","meilisearch":"ok","smtp":"absent"}. Someone with docker access should run the literal command once to close this out.
  • make migrate-up && make migrate-down cycles cleanly
  • Shell passes the design-system conformity checklist

Phase 1 — Auth & users

Spec files: 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 12 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 · 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 · 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 · 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 (+ 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 · 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 · 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 · 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 · 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 · 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