Foreman docs

Changelog

What shipped in Foreman, in reverse chronological order.

2026-05-10

Phase 9 — Polish

  • Custom not-found.tsx and root error.tsx boundary (no more Next.js stack-trace pages in production).
  • loading.tsx skeletons on the project board, task detail, and notifications inbox so cold loads have a frame instead of a flash of blank.
  • Signed-in users hitting / now redirect straight to /projects. Logged-out home is a small "invite-only" lead-in.

Phase 8 — Notifications

  • notifications table. RLS: users read/update/delete only their own; only the server (service role) inserts.
  • notifyUser / notifyMany helpers in src/lib/notifications.ts insert the in-app row and send a Resend email in parallel. Email failure doesn't break the action.
  • createComment notifies the assignee + all prior commenters (excluding self).
  • updateTask notifies the new assignee whenever assignee_id changes (excluding self-assignment).
  • New <NotificationBell /> in the project layout, <UserRealtime /> listens to inserts on notifications for live unread-count updates.
  • New /notifications page: full inbox, mark read / mark all read / dismiss.
  • Email templates inline (text-only for now). React Email templates and @mention-driven mentions land in a follow-up alongside TipTap.

Phase 7 — Attachments

  • Supabase Storage bucket task-attachments. Path convention: {project_id}/{task_id}/{uuid}-{filename}.
  • Storage RLS parses project_id from the path and gates by project membership for read, by editor/admin role for upload/delete.
  • Metadata table attachments mirrors object info; same RLS shape.
  • Task detail page shows the attachment list with image thumbnails for image MIME types and a generic file pill for everything else. Server-side signed URLs (1-hour TTL).
  • Upload via a hidden <input type="file"> in <AttachmentList />. Direct browser-to-Storage upload, then metadata insert. If the metadata insert fails the storage object is rolled back.

Phase 6 — Subtasks

  • One-level subtask UI on the task detail page: list with checkbox toggle, quick-add input, link to each subtask's own detail page.
  • createSubtask server action inherits parent's project + the project's first non-done status.
  • toggleTaskDone flips between the first non-done and first done status (per project).
  • Schema already supported subtasks via parent_task_id; depth still capped at one via the existing trigger.

Phase 5 — Realtime

  • Added tasks, comments, activity, statuses, labels, task_labels, project_members, invitations to the supabase_realtime publication.
  • <ProjectRealtime /> (mounted in the project layout) subscribes to project-scoped Postgres changes and triggers router.refresh(). RLS ensures contractors only receive events for projects they're in.
  • <TaskRealtime /> does the same for comments + activity scoped to a single task on the detail page.
  • Refreshes are debounced to one per 150ms so a burst of changes (e.g. drag-drop) doesn't stampede the server.

Phase 4 — Comments + activity log

  • Schema: comments and activity tables.
  • Activity is auto-logged via a Postgres trigger on tasks (created, title_changed, status_changed, assignee_changed, priority_changed, due_date_changed). Clients can read but cannot insert directly — only the trigger writes.
  • createComment, updateComment, deleteComment server actions.
  • New combined timeline on the task detail page: comments and activity events, ordered by time. Comment composer at the bottom, ⌘+Enter submits.

Phase 3b — Board drag-and-drop + inline task edit

  • Board now drag-and-drop via @dnd-kit/core + @dnd-kit/sortable. Drag a card within a column to reorder, drag across columns to change status. Optimistic UI via useOptimistic; server reconciles on the next render.
  • Position math: fractional indexing — drop between two cards averages their positions, drop at top/bottom shifts by ±1024. No full-column re-shuffle on every move.
  • moveTask, updateTask, deleteTask server actions added.
  • Task detail page is now editable: Edit / Save / Cancel / Delete. Form covers title, description, status, assignee, priority, due date.

Phase 3 — Tasks core (read + create slice)

  • Schema: statuses, labels, tasks (with one-level subtask depth enforced), task_labels.
  • Default statuses (To-do / Doing / Review / Done) auto-seeded on every project create.
  • Helper: project_role(uuid) returns the caller's role in a project (admin / editor / viewer / null).
  • RLS policies — viewers read-only on tasks/task_labels, editors and admins can create/update/delete.
  • Project shell: layout.tsx provides project name + tab nav (Board / List / Settings — Settings admin-only). Active tab highlighting via usePathname.
  • Board view (kanban, static — drag-and-drop ships in 3b). List view (sortable table). Task detail page.
  • Server action: createTask with fractional-position calculation (end-of-column on insert).
  • Invite/cancel/remove member flows now live inside the project shell rather than as standalone pages.

Phase 2 — Projects + invitations + RLS

  • Schema: projects, project_members, invitations with RLS on each.
  • Helper: is_project_member(uuid) — used everywhere RLS gates project access.
  • accept_invitation(token) SQL function (SECURITY DEFINER) does the email-match check and inserts the membership atomically.
  • Admin can create projects, invite users by email, cancel pending invitations, and remove members.
  • Pages: /projects, /projects/new, /projects/[id], /projects/[id]/settings, /invite/[token].
  • Resend wired for invitation emails (foreman@hrefcreative.com, reply-to support@hrefcreative.com). Falls back to a manually-shareable URL if the email send fails.
  • Home page detects auth state and shows "Open Foreman" / "Account" when signed in.

Permanent local ports + Google OAuth wired

  • Foreman dev server permanently on 3030 (was using fallback 3003 because HQ holds 3000).
  • Supabase moved off the CLI defaults onto a Foreman-specific block: API 54400, DB 54402, Studio 54403, Inbucket 54404, Pooler 54409, Analytics 54407. Done in supabase/config.toml so it's permanent across supabase starts.
  • Google OAuth provider enabled in supabase/config.toml via env() substitutions. Credentials live in a gitignored .env at the project root.

Phase 1 — Auth + profiles

  • profiles table with auto-create trigger on auth.users insert (admin emails hardcoded for bootstrap).
  • RLS helpers: is_admin(), plus self-select / self-update policies on profiles.
  • Supabase clients for server (cookies-based) and browser, plus shared session-refresh proxy.
  • Magic link + Google OAuth login flows via Server Actions.
  • Pages: /login, /account, plus /auth/callback exchange route.
  • Proxy (formerly middleware in Next ≤15) gates everything except /, /login, /auth/*, /docs/*.

Phase 0 — Foundation

  • Project scaffold: Next.js 16 (App Router), Tailwind v4, shadcn/ui (neutral, Base UI primitives), Fumadocs at /docs/*, Supabase CLI initialized.
  • Design doc committed at docs/plans/2026-05-10-foreman-design.md.
  • HQ project + phase lists created (G_XpbGT2J6Gx3J_3mXsT3 under ws-href).
  • GitHub repo published at github.com/amarasa/foreman (private).

On this page