Workspace App β Implementation Plan & Work Breakdown
Document Type: Living Implementation Plan
Context: Solo developer, hobby-to-business trajectory, AI-assisted with full code review
Based on: Production-Grade Architecture Handover (May 2026)
Reading This Document
Tasks are grouped into phases. Each phase is a shippable milestone β by the end of it, something real and working exists that you can use, demo, or build on top of. Phases are sequenced to minimize re-work and to teach progressively harder concepts.
Complexity tags:
π’ Beginner-friendly β follow-the-docs territory
π‘ Intermediate β requires understanding the βwhyβ
π΄ Advanced β architectural weight-bearing, get it right first time
Learning tags:
π¦ Monorepo / toolchain
π Auth / Security
ποΈ Database / Drizzle
π§© Next.js patterns
π¨ UI / Mantine
βοΈ Editor / Tiptap
π€ AI / Embeddings
π± Mobile / PWA
π₯οΈ CLI / API
π³ Billing / SaaS
Phase 0 β Foundation & Tooling β COMPLETE
Milestone: A working monorepo that builds, lints, and runs locally. The skeleton that every future phase lives inside.
Learning payoff: Turborepo, pnpm workspaces, TypeScript strict mode, project conventions.
Completed: May 2026
| # | Task | Complexity | Learning | Status |
|---|---|---|---|---|
| 0.1 | Initialize pnpm monorepo with pnpm init and workspace config | π’ | π¦ | β |
| 0.2 | Set up Turborepo with turbo.json β define build, dev, lint, typecheck pipelines | π’ | π¦ | β |
| 0.3 | Create apps/web as a Next.js 16 App Router project with TypeScript strict | π’ | π§© π¦ | β |
| 0.4 | Create apps/cli as a bare TypeScript package (empty for now, will be wired up later) | π’ | π¦ | β |
| 0.5 | Create packages/core β empty package with correct package.json and tsconfig | π’ | π¦ | β |
| 0.6 | Create packages/ui β empty package, will house shared Mantine components | π’ | π¦ | β |
| 0.7 | Create packages/features-registry β empty, will house ALL_FEATURES manifest | π’ | π¦ | β |
| 0.8 | Configure shared tsconfig.base.json at root; extend it in all packages | π‘ | π¦ | β |
| 0.9 | Add ESLint + Prettier with boundary enforcement; wire into Turborepo lint pipeline | π‘ | π¦ | β |
| 0.10 | Add .env.example template and document all required environment variables (see Β§20.1) | π’ | Β | β |
| 0.11 | Set up Vercel project, link repo, configure environment variables in dashboard | π’ | Β | β |
| 0.12 | Confirm turbo dev starts apps/web correctly from the monorepo root | π’ | π¦ | β |
| 0.13 | Add a root README.md documenting how to run, build, and add packages | π’ | Β | β |
Phase 0 Exit Criteria: β All met.
pnpm turbo buildcompletes without errors or warningspnpm turbo devstartsapps/webatlocalhost:3000pnpm turbo lintandpnpm turbo typecheckpass across all 5 packages- Dependency boundary enforcement active β
packages/*βapps/*blocked by ESLint - Deployed to Vercel at
https://sidekick-six-bay.vercel.app
Implementation notes:
- Next.js 16 was current at time of implementation (plan said 15 β updated in place)
- pnpm 11.0.8 via Corepack (plan said pnpm@8 β Corepack handles version enforcement)
apps/web/eslint.config.mjsgenerated by scaffolder was removed β rooteslint.config.jshandles all packagesapps/web/pnpm-lock.yamlgenerated by scaffolder was removed β root lockfile manages the workspacepackages/features-registrynotpackages/feature-registryβ corrected after initial scaffoldturbo.jsonbuild.envarray added to prevent stale Vercel cache when env vars change- Repo made public intentionally β see architecture handover Β§21
Phase 1 β Supabase & Auth Shell β COMPLETE
Milestone: A real login screen that works. Protected routes. A profile in the database. The security skeleton everything else hangs on.
Learning payoff: Supabase auth, cookie sessions, Next.js middleware, server vs browser client separation.
Completed: May 2026
| # | Task | Complexity | Learning | Status |
|---|---|---|---|---|
| 1.1 | Create Supabase project; enable email/password auth | π’ | π | β |
| 1.2 | Install Supabase client packages in packages/core | π’ | π | β |
| 1.3 | Implement createBrowserClient() helper in packages/core/supabase/browser.ts | π‘ | π | β |
| 1.4 | Implement createServerClient() helper in packages/core/supabase/server.ts | π‘ | π | β |
| 1.5 | Implement admin/service-role client in packages/core/supabase/admin.ts β server only | π΄ | π | β |
| 1.6 | Write profiles table schema in packages/core using Drizzle; add id, email, createdAt | π‘ | ποΈ | β |
| 1.7 | Set up drizzle.config.ts in packages/core; configure migrations folder | π‘ | ποΈ | β |
| 1.8 | Add a root pnpm db:migrate script that discovers and runs all package migrations in order | π΄ | ποΈ π¦ | β |
| 1.9 | Enable RLS on profiles; add the canonical user-owns-rows policy | π΄ | π ποΈ | β |
| 1.10 | Implement withRLS(userId, fn) helper in packages/core/db/rls.ts | π΄ | π ποΈ | β |
| 1.11 | Implement Next.js proxy in apps/web β session refresh, redirect unauthenticated users, exclude /api/* | π΄ | π§© π | β |
| 1.12 | Build login page UI with Mantine form components | π’ | π¨ | β |
| 1.13 | Build sign-up page UI with email/password | π’ | π¨ | β |
| 1.14 | Add Mantine provider, Notifications, and PostCSS config (see Β§20.4) | π‘ | π¨ | β |
| 1.15 | Implement post-login redirect to /dashboard | π’ | π§© | β |
| 1.16 | Build a minimal dashboard shell layout (sidebar navigation placeholder, header) | π’ | π§© π¨ | β |
| 1.17 | Implement sign-out functionality | π’ | π | β |
| 1.18 | Verify session persists across page reloads; verify redirect works for unauthenticated users | π’ | Β | β |
Phase 1 Exit Criteria: β All met.
- You can sign up, log in, and see a dashboard
- Unauthenticated access to
/dashboardredirects to login - RLS is enabled on
profiles
Implementation notes:
- Next.js 16 renamed middleware β proxy. File:
middleware.tsβproxy.ts. Export:export function middlewareβexport function proxy. Theconfigexport is unchanged. This affects how session refresh and route protection work at the edge. - Profile creation via Postgres trigger, not API route. Trigger
on_auth_user_createdonauth.userscallscreate_profile_for_new_user(). The function must referencepublic.profiles(fully qualified) because triggers run in theauthschema context. This approach handles all auth providers (email, OAuth, magic link) without per-provider app-level code, and cannot fail silently after auth succeeds. - CSS modules only. No inline styles. No Mantine style props.
packages/eslint-plugin-sidekickwith ano-mantine-style-propsrule enforces this. Mantine behavioral props (withBorder,shadow,navbar=) are allowed; pure style props (h,px,fw,c,mt,size,color,justify,gap) are banned. - 4 Supabase clients. A fourth client,
createProxyClient(request, response), was added for the Edge runtime. It reads cookies from the incoming request/response directly and never importsnext/headers(which is Node.js-only). Used exclusively inproxy.ts. packages/copyadded. Centralized string copy shared across all apps. Never hardcode user-visible strings in source files β import frompackages/copyinstead.useNavigationhook added. Always callsrouter.push()+router.refresh()together. Prevents forgetting the refresh step after auth actions that change server-rendered state.export const dynamic = 'force-dynamic'required. Must be set on all route groups that touch Supabase (e.g.(app)/layout.tsx,(auth)/layout.tsx). Prevents Next.js from pre-rendering server-rendered auth-dependent routes at build time.dotenv-clipattern for env loading..env.locallives at the repo root only. All scripts that need env vars prefix withdotenv -e ../../.env.local --. Node.js--env-fileflag is blocked as a security measure by Node.js itself.- GraphQL + Relay deferred to post-MVP. REST is sufficient for MVP. Relay + App Router friction is unresolved upstream.
withApiGuardmaps cleanly to REST. Can add GraphQL later without a full rewrite. - API versioning (/api/v1/) deferred to post-MVP. Current routes are at
/api/. Versioning adds URL complexity for no current benefit β MVP has one client and breaking changes can be coordinated directly. DATABASE_DIRECT_URL(port 5432) for migrations only;DATABASE_URL(port 6543) for runtime.DATABASE_DIRECT_URLis NOT needed in Vercel β migrations run locally, never on Vercel.- Supabase renamed keys in 2025.
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYreplaces the oldNEXT_PUBLIC_SUPABASE_ANON_KEY.SUPABASE_SECRET_KEYreplaces the oldSUPABASE_SERVICE_ROLE_KEY.
Phase 2 β Core Infrastructure (API Guard, Feature System)
Milestone: The architectural backbone is live.
withApiGuardis implemented and tested. The feature registry exists. You could add any feature safely from here.
Learning payoff: Middleware patterns, centralized auth, feature flags, the βwhyβ behind the architecture.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 2.1 | Implement resolveApiCaller(req) in packages/core/api/auth.ts β handles both cookie sessions and Bearer API keys | π΄ | π π§© |
| 2.2 | Implement withApiGuard(handler, opts) in packages/core/api/guard.ts (see Β§10.2 canonical implementation) | π΄ | π π§© |
| 2.3 | Wire withRLS inside withApiGuard | π΄ | π ποΈ |
| 2.4 | Add basic request logging inside withApiGuard (method, path, userId, latency) | π‘ | π§© |
| 2.5 | Add auth failure logging inside withApiGuard | π‘ | π§© |
| 2.6 | Define FeatureManifest type in packages/features-registry | π‘ | π¦ |
| 2.7 | Implement ALL_FEATURES array in packages/features-registry/index.ts β start with an empty array | π‘ | π¦ |
| 2.8 | Create user_feature_entitlements table in packages/core with userId, featureSlug, RLS policy | π΄ | ποΈ π |
| 2.9 | Implement getEnabledFeatures(userId) in packages/core β reads from entitlements table | π‘ | ποΈ |
| 2.10 | Write a seed script to enable all features for your own user account during development | π’ | ποΈ |
| 2.11 | Create a test API route /api/health using withApiGuard to verify the full guard chain works | π‘ | π§© π |
| 2.12 | Verify 401 is returned when unauthenticated; 403 when a feature is disabled | π’ | Β |
Phase 2 Exit Criteria: withApiGuard is implemented and the /api/health route correctly returns 401/403 in the right conditions. The feature system can enable/disable features per user.
Phase 3 β First Feature: Notes (End-to-End Vertical)
Milestone: A fully working Notes feature β create, read, update, soft-delete. This is your βproof of architectureβ feature. Every future feature follows this exact same pattern.
Learning payoff: The complete feature loop: schema β migration β API β repository β UI. This is the template youβll reuse for every other feature.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 3.1 | Create packages/feature-notes with its own package.json, tsconfig, and Drizzle config | π‘ | π¦ ποΈ |
| 3.2 | Define notes table schema in packages/feature-notes/schema.ts β include id (client UUID), userId, title, content, createdAt, updatedAt, deletedAt, embeddingStatus | π΄ | ποΈ |
| 3.3 | Run migration; verify table appears in Supabase dashboard | π’ | ποΈ |
| 3.4 | Enable RLS on notes; add canonical user-owns-rows policy | π΄ | π ποΈ |
| 3.5 | Register notes feature in packages/features-registry/ALL_FEATURES | π’ | π¦ |
| 3.6 | Implement NotesRepository in packages/feature-notes/repository.ts β list(), getById(), create(), update(), softDelete() | π΄ | ποΈ |
| 3.7 | Implement GET /api/notes route using withApiGuard + feature guard + notes:read scope | π΄ | π§© π |
| 3.8 | Implement POST /api/notes route β accept client-generated UUID | π΄ | π§© π |
| 3.9 | Implement GET /api/notes/[id] route | π‘ | π§© |
| 3.10 | Implement PATCH /api/notes/[id] route | π‘ | π§© |
| 3.11 | Implement DELETE /api/notes/[id] route β soft delete only, set deletedAt | π‘ | π§© ποΈ |
| 3.12 | Build Notes list page at /notes β server component fetching notes via repository | π‘ | π§© π¨ |
| 3.13 | Build Note detail/edit page at /notes/[id] | π‘ | π§© π¨ |
| 3.14 | Build βNew Noteβ flow with client-generated UUID | π‘ | π§© π¨ |
| 3.15 | Wire up soft-delete in the UI with confirmation | π’ | π¨ |
| 3.16 | Enable the notes feature for your own user account via seed script | π’ | Β |
| 3.17 | Manually test the full CRUD loop via both UI and direct API calls (use curl or Postman) | π’ | Β |
Phase 3 Exit Criteria: Notes can be created, edited, listed, and soft-deleted through the UI. The API layer enforces auth and feature entitlement. All queries filter where(isNull(notes.deletedAt)).
Phase 4 β Rich Text Editor (Writing Feature)
Milestone: A proper writing experience with Tiptap. Notes and Writing share the editor component. This is where the app starts feeling real.
Learning payoff: Tiptap configuration, rich text as JSON storage, markdown export, editor extensions.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 4.1 | Install Tiptap dependencies in packages/ui | π’ | βοΈ |
| 4.2 | Build a <RichTextEditor> component in packages/ui using @mantine/tiptap | π‘ | βοΈ π¨ |
| 4.3 | Configure extensions: Bold, Italic, Heading, BulletList, OrderedList, Code, Link, Image | π‘ | βοΈ |
| 4.4 | Implement JSON storage β editor outputs editor.getJSON() for storage | π΄ | βοΈ |
| 4.5 | Implement markdown export β editor.storage.markdown.getMarkdown() for embedding pipeline | π΄ | βοΈ |
| 4.6 | Make editor mobile-friendly (touch targets, mobile toolbar) | π‘ | βοΈ π± |
| 4.7 | Swap plain textarea in Notes editor for <RichTextEditor> | π’ | Β |
| 4.8 | Create packages/feature-writing with its own schema | π‘ | π¦ ποΈ |
| 4.9 | Define documents table β similar shape to notes but with type (essay, journal, draft) | π‘ | ποΈ |
| 4.10 | Implement full API routes for Writing feature (same pattern as Notes) | π‘ | π§© |
| 4.11 | Register writing feature in feature registry | π’ | Β |
| 4.12 | Build Writing section in the app shell (/writing) | π‘ | π§© π¨ |
Phase 4 Exit Criteria: The rich text editor is shared, reusable, stores JSON, exports markdown. Notes and Writing both use it.
Phase 5 β Content Features (Bookmarks, Recipes, Budget)
Milestone: The app is a multi-feature productivity tool. Three more features built using the established pattern.
Learning payoff: Repetition builds fluency. Schema design for varied content types. Simpler after Notes.
Each sub-feature follows the same pattern: schema β migration β RLS β feature registry β API routes β repository β UI pages.
5A β Bookmarks
| # | Task | Complexity |
|---|---|---|
| 5A.1 | Create packages/feature-bookmarks; define bookmarks schema (url, title, description, tags, favicon) | π‘ |
| 5A.2 | Migration, RLS, feature registry entry | π‘ |
| 5A.3 | API routes: list, create, update, soft-delete | π‘ |
| 5A.4 | Implement BookmarksRepository | π‘ |
| 5A.5 | UI: Bookmarks list with search/filter by tag | π‘ |
| 5A.6 | UI: Add bookmark form (URL, auto-fetch title/description via server action) | π‘ |
5B β Recipes
| # | Task | Complexity |
|---|---|---|
| 5B.1 | Create packages/feature-recipes; define recipes schema (title, ingredients, steps, tags, servings) | π‘ |
| 5B.2 | Migration, RLS, feature registry entry | π‘ |
| 5B.3 | API routes: list, create, update, soft-delete | π‘ |
| 5B.4 | Implement RecipesRepository | π‘ |
| 5B.5 | UI: Recipe list with search | π‘ |
| 5B.6 | UI: Recipe detail with structured ingredients and steps | π‘ |
5C β Budget
| # | Task | Complexity |
|---|---|---|
| 5C.1 | Create packages/feature-budget; define transactions schema (amount, category, date, notes) | π‘ |
| 5C.2 | Migration, RLS, feature registry entry | π‘ |
| 5C.3 | API routes: list (with date range filter), create, update, soft-delete | π‘ |
| 5C.4 | Implement BudgetRepository | π‘ |
| 5C.5 | UI: Transaction list with monthly grouping | π‘ |
| 5C.6 | UI: Simple spending summary by category | π‘ |
Phase 5 Exit Criteria: All three content features are live, follow the same architectural patterns, and are protected by the feature entitlement system.
Phase 6 β AI Layer (Embeddings + RAG + Chat)
Milestone: Your content is semantically searchable. An AI chat interface can answer questions about your notes, bookmarks, and recipes. This is where the app becomes genuinely powerful.
Learning payoff: pgvector, HNSW indexes, semantic chunking, the Vercel AI SDK, streaming responses, RAG pipeline design.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 6.1 | Enable pgvector extension in Supabase | π’ | π€ |
| 6.2 | Add embedding vector column to notes, documents, bookmarks, recipes tables | π‘ | π€ ποΈ |
| 6.3 | Create HNSW indexes on each embedding column | π΄ | π€ ποΈ |
| 6.4 | Implement generateEmbedding(text) in packages/core/ai/embed.ts using OpenAI text-embedding-3-small | π‘ | π€ |
| 6.5 | Implement semantic chunking strategy β split tiptap JSON β markdown β chunks with overlap | π΄ | π€ βοΈ |
| 6.6 | Implement async background embedding job with retry (2 retries, exponential backoff) using waitUntil() | π΄ | π€ π§© |
| 6.7 | Wire embedding generation into Notes create/update API routes β non-blocking, sets embeddingStatus | π΄ | π€ π |
| 6.8 | Implement match_content() PostgreSQL function for vector similarity search | π΄ | π€ ποΈ |
| 6.9 | Create packages/feature-ai-chat with its own schema (chat_sessions, chat_messages) | π‘ | π¦ ποΈ |
| 6.10 | Migration, RLS, feature registry entry for AI Chat | π‘ | Β |
| 6.11 | Implement POST /api/ai/chat streaming route using Vercel AI SDK + Anthropic Claude | π΄ | π€ π§© |
| 6.12 | Implement RAG context retrieval in the chat handler β retrieve top-k relevant chunks before calling LLM | π΄ | π€ |
| 6.13 | Build AI Chat UI in packages/feature-ai-chat β streaming message display, input, loading states | π‘ | π¨ π§© |
| 6.14 | Add /chat page to app shell | π’ | Β |
| 6.15 | Implement embeddingStatus monitoring β a simple admin view showing failed embeddings | π‘ | π€ |
| 6.16 | Add retry trigger API endpoint for failed embeddings | π‘ | π€ |
Phase 6 Exit Criteria: You can type a question in the chat, it retrieves relevant chunks from your notes/recipes/bookmarks, and streams a contextual answer from Claude. Failed embeddings are visible and retriable.
Phase 7 β PWA & iOS Shell
Milestone: The app is installable on your iPhone. It works as a PWA in the browser and as a side-loaded Capacitor app.
Learning payoff: Service workers, PWA manifest, Capacitor, mobile UX constraints.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 7.1 | Install and configure Serwist for service worker support in apps/web | π‘ | π± |
| 7.2 | Create manifest.json β app name, icons, display mode, theme color | π’ | π± |
| 7.3 | Configure offline asset caching strategy in the service worker | π‘ | π± |
| 7.4 | Test PWA install flow in Chrome DevTools and on iPhone via Safari | π’ | π± |
| 7.5 | Audit mobile UI β touch targets, viewport, safe areas, keyboard behavior | π‘ | π± π¨ |
| 7.6 | Initialize Capacitor in apps/web | π‘ | π± |
| 7.7 | Configure Capacitor to point at the hosted Vercel Next.js URL | π‘ | π± |
| 7.8 | Build and side-load the iOS app onto your iPhone using Xcode | π‘ | π± |
| 7.9 | Test core flows (login, notes, chat) on device | π’ | Β |
| 7.10 | Handle iOS safe area insets in the layout | π‘ | π± |
Phase 7 Exit Criteria: App is installable as a PWA from Safari, side-loadable as an iOS app via Capacitor. Core features work on-device.
Phase 8 β API Keys & CLI
Milestone: You can use your own app from the terminal. API keys are manageable from the UI. The CLI is a real working tool.
Learning payoff: CLI tooling (commander.js or similar), API key security patterns, Bearer auth flows, streaming in a terminal context.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 8.1 | Create api_keys table in packages/core (see Β§8.3 canonical schema) | π‘ | ποΈ π |
| 8.2 | Migration + RLS for api_keys | π‘ | ποΈ π |
| 8.3 | Implement API key generation β secure random bytes β raw key returned once β SHA-256 hash stored | π΄ | π |
| 8.4 | Wire Bearer API key lookup into resolveApiCaller() in packages/core | π΄ | π |
| 8.5 | Implement POST /api/api-keys β create key, return raw key once | π΄ | π π§© |
| 8.6 | Implement GET /api/api-keys β list keys (no raw key values, show label/scopes/last-used) | π‘ | π§© |
| 8.7 | Implement DELETE /api/api-keys/[id] β revoke key by setting revokedAt | π‘ | π§© |
| 8.8 | Update withApiGuard to track lastUsedAt on successful API key auth | π‘ | π |
| 8.9 | Build API key management UI β list keys, create key (show raw key once), revoke | π‘ | π¨ |
| 8.10 | Build apps/cli as a Node.js CLI tool β authenticate with API key from env/config file | π‘ | π₯οΈ |
| 8.11 | Implement cli notes list command | π‘ | π₯οΈ |
| 8.12 | Implement cli notes create command (from stdin or file) | π‘ | π₯οΈ |
| 8.13 | Implement cli chat command with streaming output to terminal | π΄ | π₯οΈ π€ |
| 8.14 | Document CLI usage in README | π’ | Β |
Phase 8 Exit Criteria: You can generate an API key in the UI, set it as an env var, and run cli notes list and cli chat from your terminal.
Phase 9 β Observability & Hardening
Milestone: The app is reliable enough for friends and family. You have visibility into whatβs failing. Error handling is consistent.
Learning payoff: Structured logging, error boundaries, rate limiting, the operational side of running a web service.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 9.1 | Add structured request logging to withApiGuard β method, path, userId, latency, status | π‘ | π§© |
| 9.2 | Add auth failure logging β track 401/403 patterns | π‘ | π |
| 9.3 | Add failed embedding logging with enough context to retry | π‘ | π€ |
| 9.4 | Add Next.js error boundaries to all main UI sections | π‘ | π§© |
| 9.5 | Add global API error response normalization β consistent { error, code } shape | π‘ | π§© |
| 9.6 | Review and audit all API routes β confirm every route uses withApiGuard | π’ | π |
| 9.7 | Audit all queries β confirm where(isNull(table.deletedAt)) on syncable tables | π’ | ποΈ |
| 9.8 | Add basic rate limiting on auth routes and AI chat endpoint | π‘ | π |
| 9.9 | Run a manual penetration test of your own app β try to access another userβs data | π΄ | π |
| 9.10 | Add input validation (zod) on all API route handlers | π‘ | π§© |
Phase 9 Exit Criteria: Logs are structured and useful. All routes are guarded. No RLS gaps. Input validation is consistent across the API.
Phase 10 β Dogfooding (Friends & Family Access)
Milestone: You can invite others to use the app. They have their own isolated data. You have a basic way to manage who has access.
Learning payoff: Multi-user ops, invite flows, feature management for different users.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 10.1 | Build an invite flow β generate invite link that pre-approves sign-up | π΄ | π π§© |
| 10.2 | Build a simple admin page (your user only) to list users and manage feature entitlements | π‘ | π¨ π |
| 10.3 | Add isAdmin flag to profiles table; gate admin pages behind it | π‘ | π |
| 10.4 | Enable specific features for invited users from the admin panel | π‘ | Β |
| 10.5 | Test full sign-up and feature access flow from a fresh incognito session | π’ | Β |
| 10.6 | Collect feedback from dogfood users; create a prioritized bug list | π’ | Β |
Phase 10 Exit Criteria: You can invite someone, they can sign up, they have access only to the features you enabled for them, and their data is fully isolated from yours.
Phase 11 β Billing & SaaS Readiness (Optional Path)
Milestone: The app can charge for access. Feature entitlement is tied to subscription tier. The foundation for a real business offering.
Learning payoff: Stripe integration, webhook handling, subscription state management, SaaS architecture patterns.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 11.1 | Create Stripe account; configure products and price tiers | π’ | π³ |
| 11.2 | Add subscriptions table β userId, stripeCustomerId, stripePriceId, status, currentPeriodEnd | π‘ | ποΈ π³ |
| 11.3 | Implement Stripe checkout session creation in POST /api/billing/checkout | π‘ | π³ π§© |
| 11.4 | Implement Stripe webhook handler β sync subscription state into subscriptions table | π΄ | π³ π |
| 11.5 | Update getEnabledFeatures() to resolve features from subscription tier as well as manual entitlements | π΄ | π³ ποΈ |
| 11.6 | Build billing settings page β current plan, upgrade CTA, portal link | π‘ | π¨ π³ |
| 11.7 | Implement Stripe customer portal redirect for managing subscriptions | π‘ | π³ |
| 11.8 | Add a landing page or marketing page explaining features per tier | π’ | π¨ |
| 11.9 | End-to-end test: sign up β subscribe β gain feature access β cancel β lose access | π‘ | Β |
Phase 11 Exit Criteria: A new user who subscribes to a paid plan automatically gains access to paid features. A cancelled user loses access at period end.
Phase 12 β Bots, Workflows & Agents (Future)
Milestone: The app can execute automations on your behalf. External agents can interact with your data via the public API.
Learning payoff: Agent interoperability, webhook-driven automation, workflow design patterns.
| # | Task | Complexity | Learning |
|---|---|---|---|
| 12.1 | Design a workflows schema β trigger type, actions, enabled flag | π΄ | ποΈ |
| 12.2 | Implement a simple recurring workflow β e.g., weekly digest of bookmarks emailed to self | π‘ | π§© |
| 12.3 | Add webhook ingest endpoint β external services can push events to the app | π΄ | π π§© |
| 12.4 | Document the public API surface for agents β OpenAPI spec or equivalent | π‘ | π₯οΈ |
| 12.5 | Evaluate Inngest for durable workflows if complexity warrants it | π‘ | Β |
| 12.6 | Add agent-friendly scoped API keys for automation use cases | π‘ | π |
Recommended Learning Order
If youβre new to some of the tech in this stack, hereβs the minimum viable reading before each phase:
| Before Phase | Read / Watch |
|---|---|
| Phase 0 | Turborepo docs getting started; pnpm workspaces |
| Phase 1 | Supabase Auth docs; Next.js App Router docs (routing, middleware, server components) |
| Phase 2 | Next.js Route Handlers; how middleware chains work |
| Phase 3 | Drizzle ORM quickstart; PostgreSQL RLS basics |
| Phase 4 | Tiptap getting started; @mantine/tiptap docs |
| Phase 6 | pgvector README; Vercel AI SDK docs; Anthropic API docs |
| Phase 7 | Serwist docs; Capacitor iOS quickstart |
| Phase 8 | Node.js CLI patterns (commander.js); API key security best practices |
| Phase 11 | Stripe docs: Checkout, webhooks, customer portal |
Architectural Invariants to Review After Every Phase
Before moving to the next phase, verify:
- Every new API route uses
withApiGuard() - No
packages/*imports fromapps/* - Every new user-owned table has RLS enabled with the canonical policy
- Every new table with user content uses
withRLS()via the guard, not inline - All mutations go through the repository layer
- Syncable tables include
createdAt,updatedAt,deletedAt - Soft deletes only β no hard deletes on user data
- All queries on syncable tables filter
where(isNull(table.deletedAt)) - Content tables in the embedding pipeline have
embeddingStatus
Rough Effort Estimates (Solo, AI-assisted, Learning pace)
| Phase | Estimated Sessions | Notes |
|---|---|---|
| Phase 0 | 2β3 sessions | Mostly config, fast with AI help |
| Phase 1 | 3β5 sessions | Auth has depth; worth going slow |
| Phase 2 | 2β4 sessions | Conceptually dense; revisit often |
| Phase 3 | 4β6 sessions | The learning payoff is highest here |
| Phase 4 | 2β4 sessions | Tiptap is well-documented |
| Phase 5 | 4β8 sessions | Repetition; gets faster each sub-feature |
| Phase 6 | 6β10 sessions | Most technically complex phase |
| Phase 7 | 2β4 sessions | Mostly config and testing |
| Phase 8 | 3β5 sessions | CLI is fun; API key crypto needs care |
| Phase 9 | 2β3 sessions | Audit work; methodical |
| Phase 10 | 2β3 sessions | Satisfying milestone |
| Phase 11 | 4β6 sessions | Stripe webhooks need careful testing |
| Phase 12 | Open-ended | Exploratory; do when ready |
Sessions are loosely defined as focused 2β3 hour working blocks. Estimates assume youβre reviewing every line and asking questions β thatβs the point.