hp-prod-tracker/ROADMAP.md
Leivur Djurhuus 4149b2cf40 Switch from db push to versioned Prisma migrations
Replace 2 stale migration files with a single baseline migration
capturing the full 40+ model schema. The database was freshly reset
via clean-slate, making this the ideal time to establish migration
history. Dockerfile now runs prisma migrate deploy before app start.
Updated SETUP.md and ROADMAP.md to reference prisma migrate dev
instead of db push.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:45:36 -05:00

61 KiB
Raw Permalink Blame History

HP CG Production Tracker — Roadmap

Single source of truth for project status and remaining work. Previous planning documents (IMPLEMENTATION_PLAN.md, UPGRADE_PLAN.md) are archived in docs/archive/.

Last updated: 2026-03-18


Table of Contents

  1. What's Built
  2. Remaining Work — Priority Order
  3. Data Model Status
  4. Architecture Reference

What's Built

Core Foundation (Phases 14)

Feature Status
Project scaffold (Next.js, TS, Tailwind, Prisma, Auth.js) Complete
Design system (Oliver Agency palette, Montserrat + Inter, light/dark) Complete
Auth — SSO (Google + Microsoft Entra ID) + dev bypass Complete
Project CRUD + deliverable CRUD with auto-pipeline Complete
User management + role assignment Complete
Dependency engine + stage state machine Complete
Artist assignments + My Work page Complete
Table view (TanStack Table, filters, sort, column visibility) Complete
Board view (Kanban drag-and-drop) Complete
Timeline view (Gantt with dependencies) Complete
Dashboard (KPIs, charts, overdue alerts) Complete
Revision tracking (rounds, feedback notes, history) Complete
Threaded comment system Complete
Notification system (in-app bell + full page) Complete
Excel import (bulk upload from Master CG Tracker) Complete
Excel export Complete
Deadline tracking (approaching/overdue + notifications) Complete
Command palette (Cmd+K) Complete
Bulk operations (multi-select → batch status/assignment/priority) Complete
Loading skeletons, error boundaries, empty states Complete
Responsive design + mobile sidebar Complete
Accessibility (skip-to-content, ARIA, focus-visible) Complete
Stage date override + scheduling features Complete

Beyond Phase 4 (implemented opportunistically)

Feature Notes
Workload capacity grid Artist × Week grid with status bars, overallocation warnings, 4/8/12-week projections
Workload utilization heatmap Color-coded heatmap on workload page (green → yellow → red by load). Added in c8f88c6.
Calendar view Monthly calendar with event pills, filters, day detail. Not in original plan.
Calendar heatmap Heatmap overlay on calendar grid with toggle and bar visualization. Added in bac6d4c. Not in original plan.
Automation rule engine Full trigger/action engine with event bus, rule evaluator, action executor, execution log
Semantic search (pgvector + Ollama) Embedding service, hybrid search, pgvector columns on Project + Deliverable
AI Chat panel SSE streaming chat with tool calls (search_entities), entity cards, project/deliverable context. Not in original plan.
Weekly executive report Full report page at /reports/weekly/[date] with KPI strip, completed, deadlines, at-risk, upcoming sections
Skill data model Skill, UserSkill, StageSkillRequirement in schema with seed data — no assignment UI yet

Phase 2: Multi-Brand Ready (Dynamic Pipelines)

Feature Status
WP1 — RBAC Foundation Complete
Permission enum (20 perms) + OrgRolePermission model
requireAuth(permission?) replaces getAuthSession() across all 28 API routes
Default permissions per role (ADMIN/PRODUCER/ARTIST) with org-level overrides
Permission matrix settings page (/settings/permissions)
WP2 — Org Hardening Complete
Denormalized organizationId on Deliverable + DeliverableStage
assertOrgAccess() on all project/deliverable/stage API routes
Backfill script (scripts/backfill-org-ids.ts)
WP3 — Dynamic Pipeline Schema Complete
PipelineTemplate, PipelineStageDefinition, PipelineStageDependencyV2 models
pipelineTemplateId on Project, stageDefinitionId on DeliverableStage
Stage resolver normalizes old/new definitions
deliverable-service.ts creates stages from project's dynamic pipeline
Migration script (scripts/migrate-to-dynamic-pipelines.ts)
WP4 — Pipeline Template CRUD Complete
Full service: list, get, create, update, archive, duplicate
Stage CRUD: add, update, remove, reorder
Dependency management with cycle detection
Pipeline validation (cycles, orphans, root stages)
8 API routes under /api/pipelines/
WP5 — Pipeline Builder UI Complete
@xyflow/react dependency graph visualization
Pipeline list page (/settings/pipelines) with create, duplicate, archive
Pipeline editor page with two-panel layout (stage list + dependency graph)
Stage edit sheet, validation banner, custom edge with delete
WP6 — Invite System & Org Onboarding Complete
Invitation model with token-based accept flow
Invitation service: create, list, revoke, accept
Team settings page (/settings/team) with member list + invite form
WP7 — Custom Fields & Notification Rules Complete
CustomFieldDefinition model + customFields JSON on Project/Deliverable
NotificationRule model (org-scoped event + conditions + channels)
CRUD services, validators, hooks, API routes for both
Custom fields settings page (/settings/fields)
Notification rules settings page (/settings/notifications)

Remaining Work — Priority Order


A. Visual Review Tool

The highest-impact remaining feature. CG production review is fundamentally visual — this is a single, unified review tool built in stages, each independently useful. No throwaway prototypes.

The review tool lives at its own dedicated page (/projects/[projectId]/deliverables/[deliverableId]/review) and is also accessible from the stage detail sheet via a "Review" button. It's the primary interface for inspecting renders, comparing revisions, annotating feedback, and making approve/reject decisions.

Infrastructure built so far:

  • Review page at /projects/[projectId]/deliverables/[deliverableId]/review with image viewer, upload, gallery
  • Canvas-based image viewer with zoom/pan/minimap, retina support
  • Image upload API with PNG alpha compositing + TIFF conversion (sharp)
  • Revision.attachments JSON stores { referenceImage, currentImage } with metadata
  • Comparison viewer with 4 modes: side-by-side, wipe, overlay, toggle
  • SVG annotation layer with 7 tools (rect, ellipse, arrow, freehand, text, pin, screenshot paste)
  • Annotation model in Prisma schema (use prisma migrate dev to create migration)
  • Annotation API: GET/POST /api/revisions/[id]/annotations, PATCH/DELETE /api/revisions/[id]/annotations/[id]
  • Annotations linked to comments (transactional create), undo/redo stack
  • Screenshot paste: Cmd+V pastes clipboard image as draggable/resizable callout
  • "Review" button on stage cards in deliverable detail page
  • Review sessions: session builder, presenter mode, summary grid, decision recording
  • ReviewSession + ReviewSessionItem models in schema
  • Video upload with HLS streaming (A7.1) + custom video player (A7.2)
  • Video annotation layer with auto-pause, timestamped annotations, timeline markers (A7.3 in progress)
  • Annotation.timestampSeconds + Annotation.frameThumbnailUrl fields in schema
  • Feedback item cards show timestamp badges for video annotations
  • Revisions auto-poll when video status is "processing"
  • next.config.ts configured with proxyClientMaxBodySize: "500mb" for video uploads

New dependency for all stages: sharp (server-side PNG alpha compositing + image processing)


A1 — Review Viewer & Image Upload [x]

The foundation. A dedicated review page with a high-fidelity image viewer and the upload infrastructure to get images into the system.

What gets built:

  • Review page at /projects/[projectId]/deliverables/[deliverableId]/review — full-width layout with image viewer as the centerpiece, toolbar at top, panels on the sides
  • Image viewer — canvas-based with WebGL acceleration for large CG renders (4000×4000px+)
    • Zoom: scroll wheel, pinch gesture, keyboard (+/-), toolbar presets (Fit, 50%, 100%, 150%, 200%)
    • Pan: click-drag when zoomed, minimap overlay showing viewport position
    • Pixel info: coordinate display (x, y) + color readout (RGB/Hex) in status bar
    • High-DPI (retina) support with proper pixel ratio handling
  • Image upload — drag-and-drop zone + click-to-browse for reference and current render per revision round
    • Supports PNG, TIFF, JPEG, WebP up to 50MB
    • PNG alpha compositing on upload — flatten transparent PNGs onto white (#FFF) background server-side using sharp, so CG renders with semi-transparent drop shadows display correctly in all comparison and overlay modes
    • Store flattened version for viewing + optionally keep original transparent PNG for download
    • Extend Revision.attachments JSON: { referenceImage?: { url, filename, size, uploadedAt, originalUrl? }, currentImage?: { ... } }
  • Image gallery — thumbnail strip of all uploaded images across revision rounds, with round number labels. Click any to load it in the viewer.
  • "Review" button on stage cards in the deliverable detail page — opens the review page for that stage

New API endpoints:

  • POST /api/stages/[stageId]/revisions/[revisionId]/upload — multipart form upload (type: "reference" | "current")
  • DELETE /api/stages/[stageId]/revisions/[revisionId]/upload — remove an uploaded image

Key files:

  • src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx — Review page
  • src/components/review/image-viewer.tsx — Core canvas viewer
  • src/components/review/zoom-controls.tsx — Zoom toolbar + keyboard handler
  • src/components/review/minimap.tsx — Navigation minimap overlay
  • src/components/review/image-upload-zone.tsx — Drag-and-drop upload
  • src/components/review/image-gallery.tsx — Thumbnail strip across rounds
  • src/hooks/use-image-viewer.ts — Pan/zoom state management
  • src/lib/services/upload-service.ts — File storage + PNG alpha compositing

A2 — Version Comparison [x]

Compare two revisions of the same deliverable stage. This is the daily workhorse — producers and artists check what changed between rounds.

What gets built:

  • Comparison modes (toolbar toggle in the review page):
    • Side-by-side — dual panes with synced zoom/pan, version labels
    • A-B Slider (wipe) — draggable vertical/horizontal divider revealing one image on each side
    • Overlay — second image overlaid with adjustable opacity (0100%)
    • Toggle — click or press Space to crossfade between images (default on narrow screens)
  • Revision selectors — dropdowns for left/right revision (default: previous round vs. current)
  • Synced navigation — zoom/pan one pane, both move together across all modes
  • Keyboard shortcuts — 1/2/3/4 to switch modes, left/right arrows to cycle revisions

Key files:

  • src/components/review/comparison-viewer.tsx — Dual-pane orchestrator
  • src/components/review/wipe-divider.tsx — Draggable split control
  • src/components/review/overlay-controls.tsx — Opacity slider + mode toggles

Dependencies: Requires A1


A3 — Annotations [x]

Draw pixel-accurate annotations directly on images. Each annotation is anchored to image coordinates so it stays accurate at any zoom level, and is linked to a comment for context.

What gets built:

  • SVG overlay layer on top of the canvas viewer (annotations in SVG for crisp scaling, image in canvas for performance)
  • Annotation tools — toolbar with: rectangle, ellipse, arrow, freehand path, text label, pin (point marker)
  • Screenshot paste callouts — Cmd+V / Ctrl+V pastes a clipboard image directly onto the review image as a floating, repositionable callout. Use case: paste a screenshot of a Maya attribute editor panel, a Photoshop layer setting, or a cropped reference to show the artist exactly what you mean. Callouts can be:
    • Moved — drag to reposition anywhere on the image
    • Resized — corner handles to scale up/down
    • Bordered — automatic contrast border so the callout stands out against the render
    • Linked to a comment — same as other annotations, the pasted screenshot becomes part of the feedback thread
    • Stored as a small image (uploaded to server, URL in annotation data) anchored to image coordinates like any other annotation
  • Color picker for annotation stroke (default: accent red for visibility against CG renders)
  • Image-coordinate anchoring — annotations stored in image space, rendered correctly at any zoom
  • Comment linking — each annotation creates or attaches to a Comment record; clicking an annotation highlights its comment in the sidebar, and vice versa
  • Visibility controls — show/hide all, show/hide per revision round, show resolved vs. open
  • Undo/redo for drawing sessions

New data model:

model Annotation {
  id           String         @id @default(cuid())
  commentId    String
  comment      Comment        @relation(fields: [commentId], references: [id], onDelete: Cascade)
  revisionId   String
  revision     Revision       @relation(fields: [revisionId], references: [id], onDelete: Cascade)
  type         AnnotationType
  data         Json           // { x, y, width, height, points[], text, color, strokeWidth, imageUrl? }
  imageX       Float          // anchor point in image coordinates
  imageY       Float
  createdById  String
  createdBy    User           @relation(fields: [createdById], references: [id])
  createdAt    DateTime       @default(now())
}

enum AnnotationType { RECTANGLE  ELLIPSE  ARROW  FREEHAND  TEXT  PIN  SCREENSHOT }

Key files:

  • src/components/review/annotation-layer.tsx — SVG overlay with tool switching
  • src/components/review/annotation-tools.tsx — Toolbar with tool selection
  • src/components/review/annotation-renderer.tsx — Renders individual shapes
  • src/components/review/screenshot-callout.tsx — Pasted screenshot with drag/resize handles
  • src/lib/services/annotation-service.ts — CRUD
  • src/lib/validators/annotation.ts — Zod schemas
  • src/hooks/use-annotations.ts — TanStack Query hook

Dependencies: Requires A1


A4 — Revision History Timeline [x]

A collapsible sidebar panel in the review page showing the full version history for a deliverable stage. The connective tissue between annotations, comparison, and feedback — provides the longitudinal view across all rounds.

What gets built:

  • Vertical timeline in a collapsible right-side panel, each revision round as a node:
    • Thumbnail preview of that round's submitted image
    • Round number + status badge (Submitted, Changes Requested, Approved)
    • Submitted by (name + avatar) + timestamp
    • Annotation count (clickable to filter annotation layer to that round)
    • Comment thread summary — first line + total count
    • Decision record — who approved/rejected, when, with what note
  • Click any round to load that version in the viewer with its annotations
  • Keyboard navigation — up/down arrows to move through rounds
  • Comparison integration — select two rounds from the timeline to open them in comparison mode (A2)
  • Filtering — show all rounds, show only rounds with feedback, show only decision points

No new data model — read-only aggregation over existing Revision, Comment, and Annotation records.

Key files:

  • src/components/review/revision-timeline.tsx — Main timeline panel
  • src/components/review/revision-node.tsx — Individual round node
  • src/components/review/revision-comments.tsx — Per-round comment display
  • src/components/review/revision-annotations-summary.tsx — Annotation count + filters
  • src/hooks/use-revision-history.ts — TanStack Query hook aggregating revisions + comments + annotations

Dependencies: Requires A1. Enhanced by A3 (annotation counts and filtering).


A5 — Feedback Checklist (Artist Action Items) [x]

Every annotation and actionable comment becomes a structured to-do item on a checklist for the assigned artist. Closes the feedback-to-fix loop.

The feedback loop:

  1. Reviewer draws annotation or posts actionable comment
  2. System auto-creates a FeedbackItem linked to the annotation/comment
  3. Artist sees checklist — action items with direct links to annotations on the image
  4. Artist works through items — checks each off with optional resolution note
  5. Artist submits new revision — unchecked action items carry forward with a warning
  6. Reviewer verifies — can confirm resolution or reopen

Two item types (simplified from 4-level severity):

  • Action Item (default) — something the artist needs to fix. Has checkbox, can be resolved/verified.
  • Info Callout — context or reference that doesn't require action (e.g., "FYI the client prefers warmer tones"). No checkbox. Can be toggled from action item and vice versa.

Where the checklist appears (3 locations):

  1. Review page — Feedback Panel (primary): full checklist with action items first, then info callouts. Progress bar counts only action items. Filter by type and status.
  2. My Work page — feedback badge per assignment ("5 open items")
  3. Stage card on deliverable page — compact badge ("4/7 resolved") for action items

New data model:

model FeedbackItem {
  id                 String           @id @default(cuid())
  deliverableStageId String
  deliverableStage   DeliverableStage @relation(...)
  revisionId         String
  revision           Revision         @relation(...)
  annotationId       String?
  annotation         Annotation?      @relation(...)
  commentId          String?
  comment            Comment?         @relation(...)
  summary            String
  isActionItem       Boolean          @default(true)
  status             FeedbackStatus   @default(OPEN)
  sortOrder          Int              @default(0)
  assignedToId       String?
  createdById        String
  resolvedById       String?
  resolvedAt         DateTime?
  resolutionNote     String?
  verifiedById       String?
  verifiedAt         DateTime?
  carriedFromId      String?          // carried forward from prior round
  createdAt          DateTime         @default(now())
  updatedAt          DateTime         @updatedAt
  @@map("feedback_items")
}

enum FeedbackStatus { OPEN  IN_PROGRESS  RESOLVED  VERIFIED  REOPENED }

Key files:

  • src/components/review/feedback-checklist.tsx — Main checklist panel
  • src/components/review/feedback-item.tsx — Individual item with resolve action
  • src/components/review/feedback-progress-bar.tsx — Progress bar
  • src/components/my-work/feedback-summary.tsx — Inline checklist on My Work
  • src/components/stages/feedback-indicator.tsx — Compact badge for stage cards
  • src/lib/services/feedback-service.ts — CRUD, auto-creation, carry-forward logic
  • src/hooks/use-feedback-items.ts

Dependencies: Requires A3 (annotations to generate items from) + A4 (timeline for round context)


A6 — Review Sessions & Playlists [x]

Curate a batch of deliverables into a structured review session. Walk through them sequentially in presenter mode with per-item decisions. The formal review workflow for HP stakeholder reviews.

What gets built:

  • Session builder — pick deliverables/stages to include, drag to reorder, auto-generate from filters ("all Catalog Images in Review for Project X")
  • Presenter mode — full-screen, navigate with arrow keys, image viewer with annotations, comment sidebar, prominent approve/request-changes/reject buttons per item
  • Summary view — thumbnail grid with decision status badges, overall session progress
  • Session states — DRAFT → IN_PROGRESS → COMPLETED
  • Shareable link with optional expiry and access control

New data model:

model ReviewSession {
  id             String              @id @default(cuid())
  name           String
  status         ReviewSessionStatus @default(DRAFT)
  createdById    String
  organizationId String
  shareToken     String?             @unique
  expiresAt      DateTime?
  items          ReviewSessionItem[]
  createdAt      DateTime            @default(now())
  updatedAt      DateTime            @updatedAt
}

model ReviewSessionItem {
  id                 String          @id @default(cuid())
  sessionId          String
  deliverableStageId String
  revisionId         String?
  sortOrder          Int
  decision           ReviewDecision?
  decisionNote       String?
  decidedById        String?
  decidedAt          DateTime?
}

enum ReviewSessionStatus { DRAFT  IN_PROGRESS  COMPLETED }
enum ReviewDecision      { APPROVED  CHANGES_REQUESTED  REJECTED }

Infrastructure built so far:

  • Review sessions list page at /reviews with create/delete, status filtering
  • Session detail page at /reviews/[sessionId] with builder (list) and summary (grid) views
  • Session builder with auto-fill from project (filter by stage status), reorder, remove items
  • Presenter mode with full-screen walkthrough, image viewer, annotation overlay (read-only), keyboard shortcuts (A=approve, C=changes, arrows=navigate, Esc=exit), fullscreen toggle
  • Session summary with thumbnail grid, decision badges, stats strip
  • ReviewSession + ReviewSessionItem models in Prisma schema (decision stored as string, no enum coupling)
  • Full service layer, API routes (CRUD + add-items/remove/reorder/decide/generate), TanStack Query hooks
  • "Reviews" added to sidebar navigation
  • AnnotationLayer extended with readOnly prop for presenter mode
  • No shareable links (deferred to B3)
  • No pipeline advancement on approve (manual "client approved" comes later)

Key files:

  • src/app/(app)/reviews/page.tsx — Session list
  • src/app/(app)/reviews/[sessionId]/page.tsx — Session detail (builder/summary/presenter)
  • src/components/review/create-session-dialog.tsx — Create session dialog
  • src/components/review/session-builder.tsx — Item list with auto-fill
  • src/components/review/session-presenter.tsx — Full-screen walkthrough
  • src/components/review/session-summary.tsx — Thumbnail grid with decisions
  • src/lib/services/review-session-service.ts — All business logic
  • src/lib/validators/review-session.ts — Zod schemas
  • src/hooks/use-review-sessions.ts — TanStack Query hooks
  • src/app/api/reviews/route.ts — List/create API
  • src/app/api/reviews/[sessionId]/route.ts — CRUD + actions API

Dependencies: Requires A1 + A3


A7 — Video Review with Timestamped Annotations

Extend the review tool to support video. Artists submit MP4 renders; reviewers scrub to a frame, pause, and draw spatial annotations (reusing the existing annotation tools) tied to that timestamp. Optional reference video for side-by-side comparison. Self-hosted transcoding via FFmpeg — no external services.

Reference implementation: lawn (pingdotgg/lawn) — React + Convex + Mux video reviewer with HLS streaming, timestamped text comments, and timeline markers.

Learnings from Lawn (reviewed 2026-03-18):

  • Lawn is a time-only comment system — no spatial annotations or drawing on video frames. Our drawing-on-frame annotation feature (SVG overlay + annotation tools) goes beyond Lawn's scope.
  • Timeline marker grouping: Lawn deduplicates markers within 1% of the timeline to prevent visual clutter. Markers are green (resolved) / orange (unresolved) with an active ring when playback is within 1.5s.
  • Player architecture: Custom player on raw <video> + HLS.js (no third-party player SDK). Uses forwardRef + useImperativeHandle to expose seekTo(time) for sidebar→player communication. All playback state is local useState.
  • Dual playback sources: Shows raw original immediately while transcoded video processes, with a quality switcher. User is never blocked waiting for encoding.
  • Controls auto-hide: Controls fade after 2.5s during playback, re-shown on mouse move.
  • Key patterns adopted: HLS-first streaming, timeline markers with dedup + color coding, comment-to-player seek via timestamp badges, graceful fallback when transcoding unavailable.
  • Key patterns NOT adopted: Mux (we self-host with FFmpeg), Convex real-time DB (we use Prisma + TanStack Query), Clerk auth (we use Auth.js).

New dependency: ffmpeg (added to Docker image for HLS transcoding, frame extraction, and metadata parsing)

The video feature is broken into sub-stages, each independently useful:


A7.1 — FFmpeg in Docker + Video Upload with HLS Streaming [x]

Add FFmpeg to the Docker environment, extend the upload system to accept video files, and transcode to HLS for fast, seekable streaming from day 1. Inspired by the lawn-video-reviewer reference: prioritize playback speed and responsiveness so artists and producers actually want to use the platform.

Architecture decision: Videos are stored outside /public in a mounted volume (/data/uploads) and served via a custom API route with proper Range, Cache-Control, and MIME headers. This avoids baking large files into the container image and enables proper streaming. HLS transcoding (MP4 → .m3u8 + .ts segments) runs async after upload so the user isn't blocked.

What gets built:

  • Dockerfile update — install ffmpeg in both builder and runner stages (apk add --no-cache ffmpeg on Alpine)
  • docker-compose.yml — add /data/uploads volume mount for persistent media storage
  • Upload service extension — accept MP4 files up to 500MB alongside existing image types
    • New type values: "video" and "referenceVideo" (in addition to "reference", "current", "screenshot")
    • Validate MIME type (video/mp4) and file size (500MB for video, 50MB for images)
    • Streaming write to disk (no full file buffering in memory)
    • Store raw MP4 in /data/uploads/revisions/[revisionId]/video_[timestamp].mp4
  • HLS transcoding (async) — after raw MP4 is stored:
    1. Extract metadata via ffprobe (duration, resolution, fps, codec) — fast, reads headers only
    2. Extract thumbnail JPEG (poster frame at 1s)
    3. Transcode to HLS: ffmpeg/data/uploads/revisions/[id]/hls/index.m3u8 + segment_NNN.ts
    4. HLS settings: 6s segments, single quality tier matching source resolution, fast preset
    5. Update revision.attachments with status: "ready" and hlsUrl when complete
    6. On failure: set status: "failed", keep raw MP4 as fallback
  • Streaming API routeGET /api/uploads/[...path]
    • Serves files from /data/uploads/ (not /public)
    • Proper MIME types: .m3u8application/vnd.apple.mpegurl, .tsvideo/mp2t, .mp4video/mp4
    • Range header support for MP4 fallback seeking
    • Cache-Control headers for segment caching
  • Thumbnail extractionffmpeg extracts poster frame (at 1s or first keyframe) as JPEG
  • Video metadata extractionffprobe reads duration, resolution, codec, frame rate
  • Extend Revision.attachments JSON:
    {
      "referenceImage": { ... },
      "currentImage": { ... },
      "video": {
        "url": "/api/uploads/revisions/.../video_xxx.mp4",
        "hlsUrl": "/api/uploads/revisions/.../hls/index.m3u8",
        "status": "processing | ready | failed",
        "thumbnailUrl": "/api/uploads/revisions/.../video_xxx_thumb.jpg",
        "filename": "video_xxx.mp4",
        "size": 52428800,
        "width": 1920,
        "height": 1080,
        "duration": 12.5,
        "fps": 24,
        "codec": "h264",
        "uploadedAt": "2026-03-16T..."
      },
      "referenceVideo": { ... }
    }
    

Upload flow:

Artist uploads MP4
  → Stream-write raw file to /data/uploads/revisions/[id]/video_[ts].mp4
  → Extract metadata via ffprobe (instant — reads headers only)
  → Extract thumbnail JPEG (poster frame at 1s)
  → Save to revision.attachments with status: "processing"
  → Return 201 immediately (artist sees thumbnail + "Processing..." indicator)
  → Async: FFmpeg transcodes to HLS segments
  → On complete: update revision.attachments with hlsUrl + status: "ready"

Why HLS over raw MP4 serving (lawn learning):

  • Instant playback — browser starts streaming the first segment immediately, no full download
  • Smooth seeking — jumps to nearest segment boundary without buffering the whole file
  • Works in all browsers — hls.js for Chrome/Firefox, native HLS for Safari
  • Future-proof — adaptive bitrate (multiple quality tiers) can be added later without changing the player

New API endpoints:

  • POST /api/stages/[stageId]/revisions/[revisionId]/upload — extend existing endpoint to handle type: "video" | "referenceVideo"
  • DELETE /api/stages/[stageId]/revisions/[revisionId]/upload?type=video — remove uploaded video + HLS segments
  • GET /api/uploads/[...path] — streaming file server for all uploaded media

Key files (new/modified):

  • Dockerfile — add ffmpeg installation
  • docker-compose.yml — add /data/uploads volume mount
  • src/lib/services/upload-service.ts — extend with video handling, streaming writes
  • src/lib/services/video-service.ts — FFmpeg/ffprobe wrapper (HLS transcoding, thumbnail extraction, metadata parsing)
  • src/app/api/uploads/[...path]/route.ts — streaming file server with Range support
  • src/components/review/video-upload-zone.tsx — drag-and-drop for video files (reuse image-upload-zone.tsx pattern)

Dependencies: Requires A1 (upload infrastructure)


A7.2 — Video Player Component [x]

A custom video player built for frame-accurate review, embedded in the existing review page alongside the image viewer.

What gets built:

  • Video player component — HTML5 <video> element with custom controls (no native browser chrome)
    • Play/pause (Space or K)
    • Seek bar / scrub timeline with hover preview
    • Frame-by-frame stepping (← / → arrow keys, step by 1/fps seconds)
    • Playback speed control (0.25x, 0.5x, 1x, 1.5x, 2x)
    • Volume control + mute (M)
    • Fullscreen toggle (F)
    • Current time / duration display (timecode format: HH:MM:SS:FF)
    • Loop toggle
  • Review page integration — detect whether the revision has a video or image attachment and render the appropriate viewer
    • Tab or toggle to switch between image viewer and video player when both exist
    • Gallery strip shows video thumbnails alongside image thumbnails
  • Keyboard shortcuts — consistent with lawn's patterns:
    • Space/K = play/pause
    • J/L = skip back/forward 5s
    • ← / → = frame step (when paused)
    • , / . = frame step alternative (matching common NLEs)
    • F = fullscreen
    • M = mute
    • [ / ] = playback speed down/up

Key files:

  • src/components/review/video-player.tsx — Core player component with custom controls
  • src/components/review/video-controls.tsx — Play, seek, speed, volume, fullscreen controls
  • src/components/review/video-timeline.tsx — Scrub bar with hover time preview
  • src/components/review/video-frame-display.tsx — Timecode display (HH:MM:SS:FF)
  • src/hooks/use-video-player.ts — Player state management (current time, playing, speed, volume)

Dependencies: Requires A7.1 (video upload)


A7.3 — Timestamped Video Annotations [~]

The core differentiator: pause a video at any frame, draw spatial annotations on that frame using the existing annotation tools, and have those annotations tied to both a timestamp and image coordinates.

What gets built:

  • Frame capture — when the user activates an annotation tool while the video is playing:
    1. Video auto-pauses
    2. Current frame is captured to a canvas (via drawImage from <video>)
    3. SVG annotation layer overlays the paused frame (reuse existing annotation-layer.tsx)
    4. User draws annotation using existing tools (rectangle, ellipse, arrow, freehand, text, pin)
    5. On submit, annotation is saved with timestampSeconds + spatial coordinates
  • Server-side frame extraction fallback — API endpoint to extract a specific frame via FFmpeg for cases where client-side capture fails (e.g., CORS, codec issues):
    • GET /api/stages/[stageId]/revisions/[revisionId]/frame?t=5.25 → returns JPEG of frame at 5.25s
  • Timeline annotation markers — colored dots/ticks on the video scrub bar showing where annotations exist
    • Orange = unresolved, green = resolved (matches lawn's pattern)
    • Click a marker to seek to that timestamp and show the annotation
    • Cluster nearby markers when zoomed out
  • Annotation visibility by time — annotations fade in/out based on current playback position
    • When playing: briefly flash annotations as playhead crosses their timestamp
    • When paused: show annotations for the current frame (±0.5s window configurable)
    • Toggle: "show all annotations" mode shows all regardless of timestamp
  • Comment linking — each video annotation creates a Comment (same as image annotations) with the timestamp embedded
    • Comment sidebar shows timestamp badge (clickable to seek)
    • Comments sorted by timestamp (not creation time)

Data model extension:

// Extend existing Annotation model — add optional timestamp field
model Annotation {
  // ... existing fields ...
  timestampSeconds  Float?   // null for image annotations, set for video annotations
  frameThumbnailUrl String?  // cached frame thumbnail for the annotation's moment
}

New API endpoints:

  • GET /api/stages/[stageId]/revisions/[revisionId]/frame?t=5.25 — extract frame at timestamp via FFmpeg
  • Existing annotation CRUD endpoints gain timestampSeconds field

Key files (new/modified):

  • src/components/review/video-annotation-layer.tsx — Orchestrates frame capture + annotation overlay on paused video
  • src/components/review/video-timeline-markers.tsx — Annotation markers on scrub bar
  • src/components/review/annotation-layer.tsx — extend to accept optional timestamp context
  • src/components/review/comment-list.tsx — extend to show timestamp badges, sort by time
  • src/lib/services/video-service.ts — add frame extraction endpoint
  • src/lib/services/annotation-service.ts — extend to handle timestampSeconds
  • prisma/schema.prisma — add timestampSeconds and frameThumbnailUrl to Annotation

Dependencies: Requires A7.2 (video player) + A3 (annotation system)


A7.4 — Video Comparison (Reference vs. Current) [ ]

Side-by-side video comparison for reviewing changes between a reference render and the current submission.

What gets built:

  • Dual video player — two synced <video> elements playing in lockstep
    • Synced playback: play/pause/seek one, both follow
    • Synced frame stepping
    • Independent volume controls
    • Labels: "Reference" / "Current"
  • Comparison modes (reuse concepts from image comparison):
    • Side-by-side — two players next to each other (default)
    • Toggle — press Space to swap which video is visible (single player area)
  • Wipe mode (stretch goal) — CSS clip-path or canvas compositing to do A/B wipe on video
    • More complex due to dual video decoding — defer if performance is an issue
  • Revision selectors — dropdowns to pick which revision's video goes on each side
  • Annotations shared across comparison — annotations from either video visible, with source label

Key files:

  • src/components/review/video-comparison.tsx — Dual synced player orchestrator
  • src/components/review/synced-video-player.tsx — Player that accepts external time/play state
  • src/components/review/video-comparison-toolbar.tsx — Mode switcher
  • src/hooks/use-synced-video.ts — Shared playback state management

Dependencies: Requires A7.2 (video player). Reference video upload from A7.1.


A7.5 — Video in Review Sessions [ ]

Extend the existing review session presenter (A6) to handle video deliverables alongside images.

What gets built:

  • Presenter mode video support — when a ReviewSessionItem's revision has a video attachment, render the video player instead of the image viewer
    • All video controls available (play, pause, seek, frame-step, speed)
    • Annotations visible on the timeline and as overlays when paused
    • Decision buttons (Approve / Changes Requested) work the same as for images
  • Mixed sessions — sessions can contain both image and video items; presenter mode switches viewer type per item
  • Session summary — video items show poster frame thumbnail (extracted in A7.1) in the summary grid

Key files (modified):

  • src/components/review/session-presenter.tsx — add video player branch
  • src/components/review/session-summary.tsx — video thumbnail support
  • src/components/review/session-builder.tsx — show video icon for video items

Dependencies: Requires A6 (review sessions) + A7.2 (video player)


A7.6 — Feedback Checklist for Video Annotations [ ]

Extend the feedback checklist (A5) to work with timestamped video annotations.

What gets built:

  • Timestamp in feedback items — each feedback item created from a video annotation shows the timestamp
  • Click-to-seek — clicking a video feedback item in the checklist seeks the player to that timestamp and highlights the annotation
  • Frame thumbnail in checklist — small thumbnail of the annotated frame next to each feedback item for quick visual context
  • Carry-forward for video — when a new revision is submitted, unresolved video feedback items carry forward with their timestamps (timestamps may shift if the video changes length — flag these for manual review)

Key files (modified):

  • src/components/review/feedback-checklist.tsx — add timestamp display + seek action
  • src/components/review/feedback-item-card.tsx — frame thumbnail + timestamp badge
  • src/lib/services/feedback-service.ts — carry-forward logic for video annotations

Dependencies: Requires A5 (feedback checklist) + A7.3 (timestamped annotations)


Implementation Order & Dependency Graph
A7.1  FFmpeg + Video Upload
  │
  ▼
A7.2  Video Player Component
  │
  ├──────────────┐
  ▼              ▼
A7.3  Timestamped    A7.4  Video
      Annotations          Comparison
  │              │
  ▼              ▼
A7.6  Feedback   A7.5  Review
      Checklist        Sessions

Estimated build order:

  1. A7.1 — Foundation. Small scope, mostly backend. Do this first.
  2. A7.2 — Core player. Can start immediately after A7.1.
  3. A7.3 — The main feature. Requires the player + existing annotation system.
  4. A7.4 — Comparison. Can be built in parallel with A7.3 if desired.
  5. A7.5 + A7.6 — Integration with existing features. Do last.

B. Collaboration Enhancements


B1 — Rich @Mentions with Deep Linking

What: @ trigger in comment input opens user autocomplete. Mentioned users get a notification with a direct link to the comment.

Key files:

  • src/components/comments/mention-input.tsx
  • src/components/comments/mention-renderer.tsx
  • Update src/lib/services/comment-service.ts — extract mentions + create notifications

B2 — Project Activity Feed

What: Unified chronological stream of all activity on a project — status changes, comments, uploads, assignments, approvals.

New data model:

model ActivityEntry {
  id            String       @id @default(cuid())
  projectId     String
  deliverableId String?
  stageId       String?
  userId        String
  type          ActivityType
  summary       String
  metadata      Json?
  createdAt     DateTime     @default(now())
}

enum ActivityType {
  STATUS_CHANGE  COMMENT_ADDED  REVISION_SUBMITTED  ASSIGNMENT_CHANGED
  APPROVAL_DECISION  FILE_UPLOADED  DEADLINE_CHANGED  PRIORITY_CHANGED
  PROJECT_CREATED  DELIVERABLE_CREATED
}

Key files:

  • src/components/activity/activity-feed.tsx
  • src/components/activity/activity-entry.tsx
  • src/lib/services/activity-service.ts
  • src/app/api/projects/[projectId]/activity/route.ts

What: Token-based URLs for external HP stakeholders to view specific deliverables without an account. Optional watermark (reviewer name, date, CONFIDENTIAL), configurable expiry, access logging, revoke capability.

Key files:

  • src/app/(external)/review/[token]/page.tsx
  • src/components/review/watermark-overlay.tsx
  • src/lib/services/external-link-service.ts

C. Reporting Completions


C1 — Velocity & Throughput Metrics

What: Deliverables completed per week, average time per stage, bottleneck detection. Trend charts over 4/8/12/26-week windows.

Key files:

  • src/components/dashboard/velocity-chart.tsx
  • src/components/dashboard/cycle-time-chart.tsx
  • src/components/dashboard/bottleneck-chart.tsx
  • src/lib/services/analytics-service.ts

C2 — Burndown Charts

What: Per-project burndown with ideal line, actual line, and velocity-based projection with confidence interval. Warning when projection exceeds deadline.

Key files:

  • src/components/dashboard/burndown-chart.tsx
  • src/components/dashboard/projection-engine.ts

C3 — Client-Facing Dashboard (Read-Only Portal)

What: Simplified read-only view for HP stakeholders via secure share link. No account required. Configurable visibility, optional password + expiry.

New data model:

model PortalLink {
  id             String    @id @default(cuid())
  token          String    @unique
  organizationId String
  projectIds     String[]
  createdById    String
  password       String?
  expiresAt      DateTime?
  lastAccessedAt DateTime?
  accessCount    Int       @default(0)
  isActive       Boolean   @default(true)
  createdAt      DateTime  @default(now())
}

Key files:

  • src/app/(portal)/[token]/page.tsx
  • src/app/(portal)/[token]/projects/[projectId]/page.tsx
  • src/components/portal/portal-header.tsx
  • src/lib/services/portal-service.ts

New dependency: bcryptjs (password hashing for portal links)


C4 — SLA Tracking

What: Target turnaround times per stage type. Real-time on-track / at-risk / breached status. Business-hours-aware calculation. Compliance dashboard widget.

New data model:

model SLATarget {
  id               String  @id @default(cuid())
  organizationId   String
  stageType        String
  targetHours      Int
  warningThreshold Float   @default(0.75)
  isActive         Boolean @default(true)
  createdAt        DateTime @default(now())
}

Key files:

  • src/app/(app)/settings/sla/page.tsx
  • src/components/dashboard/sla-compliance-chart.tsx
  • src/components/stages/sla-indicator.tsx
  • src/lib/services/sla-service.ts

D. Automation Completions

The automation rule engine (Phase 7.1) is fully built. These features extend it.


D1 — Automation Engine Activation

The automation engine backend is fully built (event bus, rule engine, action executor, execution logging, REST API). What's missing: event emission isn't wired into the service layer, deadline events have no scheduler, and there's no admin UI. This feature activates the engine end-to-end.

Three sub-stages, each independently useful:


D1.1 — Wire Event Emission into Service Layer [x]

Connect the existing emit helpers to actual service mutations so automation rules fire in production.

What gets wired:

  • stage-service.tsemitStageStatusChanged() after updateStageStatus and bulkUpdateStageStatuses transactions commit. Extend Prisma queries to include deliverable.project for event payload context.
  • revision-service.tsemitRevisionSubmitted() after createRevision. Add stage context query (deliverable + project + org) since the function only receives stageId.
  • assignment-service.tsemitAssignmentCreated() after assignUserToStage and bulkAssignArtists. Extend stage query to include deliverable/project context.
  • Handler registration — ensure automation-service.ts (which self-registers on import) is imported before events fire.

Critical constraint: Events must fire AFTER transaction commit, not inside it. If emitted inside $transaction and it rolls back, the event would have already fired.

Key files (modified):

  • src/lib/services/stage-service.ts
  • src/lib/services/revision-service.ts
  • src/lib/services/assignment-service.ts
  • src/lib/automation/event-bus.ts (no changes, but used by all above)

No new dependencies. No new data models.


D1.2 — Deadline Scheduler [x]

Implement the trigger mechanism for deadline.approaching and deadline.passed events. The existing deadline-service.ts already has checkDeadlines() — this adds event emission and a scheduler.

What gets built:

  • emitDeadlineEvents() in deadline-service.ts — calls checkDeadlines(), emits automation events per approaching/overdue item
  • 24-hour deduplication — before executing a matched deadline rule, check AutomationExecution for recent successful execution of the same rule (prevents repeated firing on every scheduler run)
  • Cron API route at /api/cron/deadlines — protected by CRON_SECRET env var, callable by external scheduler or manual testing
  • Next.js instrumentation filesetInterval hourly in the Node.js server process as primary trigger mechanism

Key files (new):

  • src/app/api/cron/deadlines/route.ts
  • instrumentation.ts

Key files (modified):

  • src/lib/services/deadline-service.ts
  • src/lib/services/automation-service.ts (dedup logic)

Dependencies: Requires D1.1


D1.3 — Automation Rules Admin UI [x]

Settings page at /settings/automations for producers and admins to create, edit, and monitor automation rules.

What gets built:

  • Rule list — card per rule with name, enabled <Switch>, trigger event badge, action type badges, condition count, execution count, edit/delete actions
  • Rule builder dialog — form with:
    • Trigger event picker (5 event types)
    • Condition builder (dynamic rows, contextual field dropdowns per event type, operator select, value input; AND logic)
    • Action configurator (dynamic rows, type selector with contextual params per action type)
    • send_webhook includes Teams Adaptive Card auto-formatting when URL contains webhook.office.com
  • Execution log — dense table of recent executions across all rules with status badges (SUCCESS/PARTIAL_FAILURE/FAILURE), expandable detail rows
  • TanStack Query hooks for all CRUD + execution history
  • New API endpoint GET /api/automations/executions for org-wide execution log

Key files (new):

  • src/app/(app)/settings/automations/page.tsx
  • src/hooks/use-automations.ts
  • src/lib/automation/field-options.ts (event field definitions + status enums for condition builder)
  • src/app/api/automations/executions/route.ts

Key files (modified):

  • src/app/(app)/settings/page.tsx (add entry to settings index)
  • src/lib/automation/action-executor.ts (Teams Adaptive Card formatting in webhook action)
  • src/lib/services/automation-service.ts (add listAllExecutions())

Dependencies: Requires D1.1. Enhanced by D1.2 (deadline rules only useful once scheduler exists).


D2 — Multi-Level Approval Chains

What: Approval workflows requiring sign-off from multiple stakeholders in sequence. Each step: approver role/user, required/optional, auto-advance on approve. Visual progress indicator.

New data model:

model ApprovalChain {
  id             String         @id @default(cuid())
  name           String
  organizationId String
  stageType      String?
  steps          ApprovalStep[]
  createdAt      DateTime       @default(now())
}

model ApprovalStep {
  id             String          @id @default(cuid())
  chainId        String
  stepOrder      Int
  approverRole   Role?
  approverUserId String?
  isRequired     Boolean         @default(true)
  autoAdvance    Boolean         @default(true)
  approvalRecords ApprovalRecord[]
}

model ApprovalRecord {
  id                 String           @id @default(cuid())
  deliverableStageId String
  stepId             String
  decision           ReviewDecision
  note               String?
  decidedById        String
  decidedAt          DateTime         @default(now())
}

Key files:

  • src/app/(app)/settings/approvals/page.tsx
  • src/components/approvals/chain-builder.tsx
  • src/components/approvals/approval-progress.tsx
  • src/lib/services/approval-service.ts

D3 — Project Templates

What: Save a full project configuration (deliverables, stage settings, default assignments) as a reusable template. One-click project creation for repeat HP SKU types.

New data model:

model ProjectTemplate {
  id             String                     @id @default(cuid())
  name           String
  description    String?
  organizationId String
  createdById    String
  isShared       Boolean                    @default(true)
  configuration  Json
  deliverables   ProjectTemplateDeliverable[]
  createdAt      DateTime                   @default(now())
  updatedAt      DateTime                   @updatedAt
}

model ProjectTemplateDeliverable {
  id                 String          @id @default(cuid())
  templateId         String
  name               String
  type               String?
  priority           Priority        @default(MEDIUM)
  defaultAssignments Json?
  sortOrder          Int
}

Key files:

  • src/app/(app)/templates/page.tsx
  • src/components/templates/template-card.tsx
  • src/components/templates/template-builder.tsx
  • src/lib/services/template-service.ts

E. Asset Intelligence


E1 — File Validation on Upload

What: Automatically validate uploaded files against spec requirements (resolution, color space, format, file size, aspect ratio). Reject non-conforming files with clear error messages. Override option for producers.

New data model:

model AssetSpec {
  id                String                  @id @default(cuid())
  organizationId    String
  stageType         String
  name              String
  rules             Json    // { minWidth, minHeight, maxFileSize, allowedFormats[], colorSpace, dpi }
  isActive          Boolean @default(true)
  validationResults AssetValidationResult[]
  createdAt         DateTime @default(now())
  updatedAt         DateTime @updatedAt
}

model AssetValidationResult {
  id             String    @id @default(cuid())
  revisionId     String
  specId         String
  passed         Boolean
  results        Json      // [{ check, expected, actual, passed }]
  overrideById   String?
  overrideReason String?
  validatedAt    DateTime  @default(now())
}

Key files:

  • src/app/(app)/settings/asset-specs/page.tsx
  • src/components/upload/file-validator.tsx
  • src/lib/services/asset-validation-service.ts

New dependency: sharp (already needed for A1 PNG compositing)


E2 — Thumbnail & Preview Generation

What: Auto-generate web-optimized thumbnails (200px), previews (1200px), and full copies from uploaded high-res assets. Background processing queue.

Key files:

  • src/lib/services/preview-service.ts
  • src/lib/jobs/generate-previews.ts
  • src/components/common/optimized-image.tsx

E3 — AI Review Engine Integration Point

What: Webhook endpoint for an external AI Review Engine to post structured quality analysis results. AI-flagged regions rendered as dashed-border annotations in the viewer. Configuration per stage type.

New data model:

model AIReviewResult {
  id               String   @id @default(cuid())
  revisionId       String
  overallScore     Float
  passed           Boolean
  checks           Json
  flaggedRegions   Json
  engineVersion    String
  processingTimeMs Int
  createdAt        DateTime @default(now())
}

Key files:

  • src/app/api/webhooks/ai-review/route.ts
  • src/components/review/ai-review-overlay.tsx
  • src/components/review/ai-review-summary.tsx
  • src/lib/services/ai-review-service.ts

F. Quality of Life


F1 — Saved Filters & Custom Views

What: Save current filter/sort/column config as a named view. Personal and shared views. URL-encodable for link sharing.

New data model:

model SavedView {
  id             String   @id @default(cuid())
  name           String
  userId         String
  organizationId String
  projectId      String?
  viewType       ViewType
  configuration  Json
  isShared       Boolean  @default(false)
  isPinned       Boolean  @default(false)
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt
}

enum ViewType { TABLE  BOARD  TIMELINE }

Key files:

  • src/components/views/saved-view-picker.tsx
  • src/components/views/save-view-dialog.tsx
  • src/lib/services/saved-view-service.ts

F2 — Skill-Based Assignment UI

What: The Skill / UserSkill / StageSkillRequirement data models already exist. This adds the UI: skill tags on user profiles, skill requirements on stage templates, and best-fit artist suggestions in the assignment dialog (sorted by skill match + lowest current load).

Key files:

  • src/app/(app)/settings/skills/page.tsx
  • src/components/assignments/skill-match-suggestions.tsx
  • src/lib/services/skill-service.ts — matching algorithm

F3 — Batch Upload with Auto-Matching

What: Drop multiple files onto a project; auto-match to deliverables by file naming convention (configurable regex). Preview table shows match confidence, manual override available.

Key files:

  • src/components/upload/batch-upload-zone.tsx
  • src/components/upload/file-matcher.tsx
  • src/lib/services/file-matching-service.ts

F4 — Extended Command Palette Actions

What: Add quick-action commands to the existing Cmd+K palette: "approve and advance," "assign to me," "mark blocked," "start review session." Context-aware based on current page.

Key files:

  • Update src/components/layout/command-palette.tsx
  • src/lib/commands/action-registry.ts

G. Docker Deployment


G1 — Docker Compose Stack

What: docker-compose.yml with three services — Next.js app, PostgreSQL + pgvector, Ollama with pre-configured models. One docker compose up starts everything.

Note: Dockerfile and docker-compose.yml already exist in the repo root — review and complete as needed. The Dockerfile must include ffmpeg (required by A7 — Video Review).

Services:

Service Image Purpose
app Custom Dockerfile Next.js production build
db pgvector/pgvector:pg17 PostgreSQL with pgvector
ollama ollama/ollama:latest Local AI (nomic-embed-text + qwen3.5:9b)

Key files:

  • Dockerfile — multi-stage Next.js build (review existing)
  • docker-compose.yml — full stack (review existing)
  • docker/ollama-entrypoint.sh — model bootstrap script
  • docker/db-init.sqlCREATE EXTENSION IF NOT EXISTS vector;
  • .env.example — environment template

G2 — Health Checks & Startup Orchestration

What: pg_isready health check on db, Ollama API health check, prisma migrate deploy runs on app startup before next start. depends_on: condition: service_healthy ensures correct start order.


G3 — Dev Environment Override

What: docker-compose.dev.yml that mounts source code and runs next dev for hot reloading, while keeping database and Ollama in containers.

Key files:

  • docker-compose.dev.yml

Data Model Status

Models Currently in Schema

Model Status
Organization, User, Account, Session
PipelineStageTemplate, PipelineStageDependency (legacy global, kept for backward compat)
PipelineTemplate, PipelineStageDefinition, PipelineStageDependencyV2 (Phase 2 — org-scoped dynamic pipelines)
OrgRolePermission (Phase 2 — RBAC)
Invitation (Phase 2 — invite system)
CustomFieldDefinition (Phase 2 — custom fields)
NotificationRule (Phase 2 — notification rules)
Project, Deliverable, DeliverableStage (with pgvector, customFields JSON, org denormalization)
StageAssignment, Revision, Comment
Notification
AutomationRule, AutomationExecution
Skill, UserSkill, StageSkillRequirement
SearchLog
ChatMessage

Models Needed (not yet in schema)

Model Feature
Annotation (+ timestampSeconds, frameThumbnailUrl fields) A3, A7.3
FeedbackItem A5
ReviewSession, ReviewSessionItem A6
ApprovalChain, ApprovalStep, ApprovalRecord D2
ProjectTemplate, ProjectTemplateDeliverable D3
AssetSpec, AssetValidationResult E1
AIReviewResult E3
PortalLink C3
SLATarget C4
ActivityEntry B2
SavedView F1
Revision.attachments extended with video + referenceVideo A7.1

Architecture Reference

src/
├── app/
│   ├── (auth)/login/          # SSO login
│   ├── (app)/                 # Authenticated routes
│   │   ├── dashboard/
│   │   ├── projects/[projectId]/
│   │   │   ├── table/ board/ timeline/
│   │   │   └── deliverables/[deliverableId]/
│   │   ├── my-work/
│   │   ├── workload/          # ✅ Capacity grid + heatmap
│   │   ├── calendar/          # ✅ Calendar view
│   │   ├── reports/weekly/    # ✅ Weekly executive report
│   │   ├── reviews/           # A7 Review sessions
│   │   ├── templates/         # D3 Project templates
│   │   └── settings/
│   │       ├── permissions/   # ✅ Phase 2 RBAC matrix
│   │       ├── pipelines/     # ✅ Phase 2 Pipeline builder
│   │       ├── team/          # ✅ Phase 2 Team + invitations
│   │       ├── fields/        # ✅ Phase 2 Custom fields
│   │       ├── notifications/ # ✅ Phase 2 Notification rules
│   │       └── skills/        # ✅ Skills management
│   ├── (portal)/[token]/      # C3 Client portal (external)
│   ├── (external)/review/     # B3 External review links
│   └── api/
├── components/
│   ├── ui/                    # shadcn/ui primitives
│   ├── layout/                # Sidebar, topbar, command palette
│   ├── views/                 # Table, Board, Timeline, Dashboard
│   ├── workload/              # ✅ Capacity grid + heatmap components
│   ├── calendar/              # ✅ Calendar components
│   ├── reports/weekly/        # ✅ Weekly report components
│   ├── pipeline-builder/       # ✅ Phase 2 Pipeline graph + stage list
│   ├── revisions/             # A1 Image comparison (to build)
│   ├── review/                # A2-A7 Full review viewer (image + video)
│   ├── automations/           # D1 Automation UI (to build)
│   └── search/                # ✅ Smart search panel
├── lib/
│   ├── pipeline/              # ✅ Dependency engine + stage machine + stage resolver
│   ├── rbac/                  # ✅ Phase 2 permissions + org scope + requireAuth
│   ├── automation/            # ✅ Event bus + rule engine + executor
│   ├── services/              # Business logic per entity
│   └── validators/            # Zod schemas
├── hooks/                     # TanStack Query hooks
└── stores/                    # Zustand (UI state, filters)

Key Patterns

  • Thin API routes → delegate to lib/services/ for all business logic
  • Service layer → Prisma queries + business rules (testable, reusable)
  • URL-synced filters via nuqs — views are shareable/bookmarkable
  • Server Components for initial fetch, TanStack Query for mutations/cache
  • npx prisma migrate dev for schema changes (versioned migrations in prisma/migrations/)

Local Dev

# Start DB
brew services start postgresql@17

# Start app (must source nvm)
export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && npm run dev

Third-Party Libraries Needed for Remaining Work

Library Purpose Feature
@xyflow/react Pipeline dependency graph visualization Phase 2 WP5 (installed)
sharp PNG alpha compositing + image validation + thumbnail generation A1, E1, E2
bcryptjs Password hashing for portal links C3
@react-pdf/renderer PDF export for weekly report download button C — weekly report enhancement
ffmpeg (system) Video frame extraction, metadata parsing, thumbnail generation A7

Document version: 1.1 — Updated 2026-03-14 (Phase 2: Dynamic Pipelines complete) To be updated as features are built and priorities shift.