- Implemented Smart Search Panel component for enhanced project and deliverable search functionality. - Introduced useSemanticSearch and useOllamaHealth hooks for managing search queries and AI availability. - Developed embedding-service to generate and store vector embeddings for projects and deliverables. - Created semantic-search-service to handle vector search, structural query detection, and LLM summarization. - Added support for hybrid search combining structural filters and semantic queries. - Integrated UI components for displaying search results and user interactions.
72 KiB
HP CG Production Tracker — Upgrade Plan (Phase 5+)
This document covers all planned upgrades beyond the Phase 1–4 foundation. Each feature area includes scope, implementation approach, new/modified files, and dependencies. Features are grouped by domain and ordered within each phase by priority.
Table of Contents
- Phase 5: Visual Review & Annotation System
- Phase 6: Workload & Resource Management
- Phase 7: Automation & Workflow Engine
- Phase 8: Asset Intelligence & AI Review Integration
- Phase 9: Advanced Reporting & Analytics
- Phase 10: Collaboration Enhancements
- Phase 11: Quality of Life & Polish
- Phase 12: Docker Deployment
- Data Model Changes Summary
- New API Routes Summary
- New Pages Summary
- Third-Party Libraries
Phase 5: Visual Review & Annotation System
The cornerstone upgrade. Transforms the tracker from a status management tool into a true visual production platform. Every CG deliverable ultimately produces an image — reviewing those images with precision is the core workflow.
5.1 — Image Viewer with High-Fidelity Zoom
What: Full-screen image viewer with smooth pan/zoom up to 200%+ for pixel-level inspection. Supports common CG output formats (PNG, TIFF, EXR preview, JPEG).
Why: HP's quality bar for CG renders is extremely high. Producers and artists need to inspect fine details (texture seams, aliasing, color banding) at pixel level without downloading files to a separate app.
Implementation:
- Canvas-based viewer component using
<canvas>with WebGL acceleration for large images - Zoom controls: scroll wheel, pinch gesture (touch), toolbar buttons, keyboard shortcuts (+/-)
- Zoom levels: fit-to-view, 50%, 100% (1:1 pixel), 150%, 200%, and free zoom
- Pan via click-drag when zoomed in; minimap overlay showing viewport position on full image
- Pixel coordinate display in status bar (x, y) with color value readout (RGB/Hex)
- Preload adjacent revisions for instant switching
- Support for high-DPI displays (retina) with proper pixel ratio handling
Key files:
src/components/review/image-viewer.tsx— Core canvas viewer componentsrc/components/review/zoom-controls.tsx— Zoom toolbar + keyboard handlersrc/components/review/minimap.tsx— Navigation minimap overlaysrc/hooks/use-image-viewer.ts— Pan/zoom state management hook
Dependencies: None (uses native Canvas API + WebGL)
5.2 — Pixel-Accurate Annotations
What: Draw annotations directly on images — circles, rectangles, arrows, freehand, and text labels. Each annotation is anchored to image coordinates (not screen coordinates) so they remain accurate at any zoom level. Annotations are linked to comments.
Why: "See the artifact on the left edge of the product, 3rd shelf" is ambiguous. Clicking on the exact pixel and drawing a circle around it eliminates miscommunication between producers, artists, and HP reviewers.
Implementation:
- SVG overlay layer on top of the canvas viewer (annotations render in SVG for crisp scaling, image renders in canvas for performance)
- Annotation tools: rectangle, ellipse, arrow, freehand path, text label, pin (point marker)
- Color picker for annotation stroke (default: accent red for visibility)
- Annotations stored as JSON in the database with image-space coordinates
- Each annotation is linked to a Comment record — clicking an annotation highlights the associated comment in the sidebar, and vice versa
- Annotation visibility toggle (show/hide all, show/hide per revision round)
- Undo/redo stack for annotation drawing session
Data model additions:
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 // RECTANGLE, ELLIPSE, ARROW, FREEHAND, TEXT, PIN
data Json // { x, y, width, height, points[], text, color, strokeWidth }
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
}
Key files:
src/components/review/annotation-layer.tsx— SVG overlay with tool switchingsrc/components/review/annotation-tools.tsx— Toolbar with tool selectionsrc/components/review/annotation-renderer.tsx— Renders individual annotation shapessrc/lib/services/annotation-service.ts— CRUD for annotationssrc/lib/validators/annotation.ts— Zod schemassrc/hooks/use-annotations.ts— TanStack Query hook
Dependencies: Requires 5.1 (Image Viewer)
5.3 — Side-by-Side Version Comparison
What: Compare two revisions of the same deliverable using multiple comparison modes: side-by-side, overlay with opacity slider, horizontal/vertical wipe (swipe divider), and onion skin.
Why: CG revision changes are often subtle — slightly adjusted lighting, minor geometry fixes, color grading tweaks. Direct visual comparison makes it obvious what changed between rounds.
Implementation:
- Dual-pane viewer sharing a synchronized zoom/pan state (pan one, both move)
- Comparison modes:
- Side-by-side: Two viewers, synced zoom/pan, with version labels
- Overlay: Single viewer, second image overlaid with adjustable opacity (0-100%)
- Wipe: Draggable vertical or horizontal divider revealing one image on each side
- Onion skin: Alternating between images at adjustable interval (useful for animation)
- Dropdown selectors for left/right revision (default: previous round vs. current round)
- Annotations from both versions shown with version-colored borders
- 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 orchestratorsrc/components/review/wipe-divider.tsx— Draggable split controlsrc/components/review/overlay-controls.tsx— Opacity slider + mode togglessrc/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx— Review page
Dependencies: Requires 5.1 (Image Viewer)
5.4 — Revision History Timeline & Version Control
What: A dedicated panel within the review viewer that provides full version history for every deliverable stage. Browse all revision rounds (v1, v2, v3...) with thumbnails, see comments and annotations tied to each version, track the decision history per round, and navigate between any version with a single click.
Why: The core review loop in CG production is iterative — an artist submits, a producer requests changes, the artist resubmits, and the cycle repeats multiple times. Without a clear version timeline, it's easy to lose track of what feedback was given on which round, what changed between versions, and who approved what and when. This is the connective tissue between annotations (5.2), comparison (5.3), and review sessions (5.5) — it provides the longitudinal view across all revision rounds.
Implementation:
- Collapsible sidebar panel in the review viewer (right side or bottom drawer)
- Vertical timeline layout with each revision round as a node:
- Thumbnail preview of that round's submitted image (auto-generated via 8.2)
- Round number + status badge (Submitted, Changes Requested, Approved, Rejected)
- Submitted by (artist name + avatar) and submission timestamp
- Annotation count for that round (clickable to filter annotation layer)
- Comment thread summary — first line of feedback + total comment count
- Decision record — who approved/rejected, when, with what note
- File metadata — resolution, file size, format (useful for tracking resubmission quality)
- Click any round to load that version in the image viewer with its annotations
- "Latest" badge on the most recent round; "Current" badge on the one being viewed
- Diff indicators between rounds: visual markers showing which rounds had changes requested vs. approved, building a clear progression narrative
- Keyboard navigation: up/down arrows to move through rounds, Enter to load
- Filtering: show all rounds, show only rounds with feedback, show only decision points
- Timeline integrates with comparison viewer (5.3): selecting two rounds from the timeline opens them in side-by-side/overlay/wipe mode
- Export revision history as PDF report (round-by-round summary with thumbnails, feedback notes, decisions, and timestamps) for stakeholder documentation
Comment threading per version:
- Comments on the existing deliverable stage are already threaded (Phase 3)
- This feature extends the comment display to be grouped by revision round in the timeline panel — each round's node expands to show its associated comments and annotations
- New comments can be posted against any historical round (not just the latest), enabling back-references like "the fix from v2 introduced a new issue visible at..."
- Comments from external review links (10.3) and review sessions (5.5) also appear in the timeline, attributed to the round they were made on
Annotation history per version:
- Each annotation is already linked to a
revisionId(5.2) - The timeline panel shows annotation counts per round and toggles annotation visibility per round on the image viewer
- "Show all annotations" mode overlays annotations from all rounds with color-coded borders (e.g., v1 = blue, v2 = orange, v3 = green) for seeing the full feedback history at once
- "Show resolved" toggle to distinguish between addressed and still-open annotations
Decision audit trail:
- Every approve/reject/changes-requested decision is logged with: who, when, note
- Displayed inline in the timeline at the relevant round
- Links to approval chain records (7.2) when multi-level approval is configured
- Exportable as part of the revision history PDF
No new data model required — uses existing Revision (roundNumber, status, feedbackNotes,
internalNotes, attachments, timestamps), Comment (threaded, per stage), and Annotation
(per revision) models. The timeline is a read-only aggregation view over existing data.
Key files:
src/components/review/revision-timeline.tsx— Main timeline panel componentsrc/components/review/revision-node.tsx— Individual round node with expandable detailssrc/components/review/revision-comments.tsx— Per-round comment thread displaysrc/components/review/revision-annotations-summary.tsx— Annotation count + filter togglessrc/components/review/revision-history-export.tsx— PDF export generatorsrc/hooks/use-revision-history.ts— TanStack Query hook aggregating revisions + comments + annotations- Update
src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx— Integrate timeline panel
Dependencies: Requires 5.1 (Image Viewer) + 5.2 (Annotations). Enhanced by 5.3 (Comparison) and 8.2 (Preview Generation for thumbnails).
5.5 — Review Sessions & Playlists
What: Curate a set of deliverables/revisions into a review session. Walk through them sequentially with per-item approve/request-changes/reject actions. Shareable via link.
Why: Producers regularly review batches of deliverables with HP stakeholders. A structured review session replaces scattered email threads and ensures every item gets a documented decision.
Implementation:
- Review Session entity containing ordered list of items (deliverable stage + revision)
- Session states: DRAFT -> IN_PROGRESS -> COMPLETED
- Presenter mode: full-screen, navigate with arrow keys, decision buttons prominent
- Each item shows: image viewer, annotation layer, comment sidebar, decision buttons
- Summary view: grid of thumbnails with approve/reject status badges
- Shareable link with optional expiry and access control (internal only vs. external)
- Auto-generate session from filters (e.g., "all Catalog Images in Review for Project X")
Data model additions:
model ReviewSession {
id String @id @default(cuid())
name String
status ReviewSessionStatus @default(DRAFT)
createdById String
createdBy User @relation(fields: [createdById], references: [id])
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
shareToken String? @unique
expiresAt DateTime?
items ReviewSessionItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model ReviewSessionItem {
id String @id @default(cuid())
sessionId String
session ReviewSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
deliverableStageId String
deliverableStage DeliverableStage @relation(fields: [deliverableStageId], references: [id])
revisionId String?
revision Revision? @relation(fields: [revisionId], references: [id])
sortOrder Int
decision ReviewDecision?
decisionNote String?
decidedById String?
decidedBy User? @relation(fields: [decidedById], references: [id])
decidedAt DateTime?
}
enum ReviewSessionStatus {
DRAFT
IN_PROGRESS
COMPLETED
}
enum ReviewDecision {
APPROVED
CHANGES_REQUESTED
REJECTED
}
Key files:
src/app/(app)/reviews/page.tsx— Review sessions listsrc/app/(app)/reviews/[sessionId]/page.tsx— Session presenter viewsrc/components/review/session-builder.tsx— Create/edit session with item pickersrc/components/review/session-presenter.tsx— Full-screen walkthrough modesrc/components/review/session-summary.tsx— Thumbnail grid with decisionssrc/lib/services/review-session-service.ts— Business logicsrc/hooks/use-review-sessions.ts— TanStack Query hooks- API routes:
/api/reviews/,/api/reviews/[sessionId]/,/api/reviews/[sessionId]/items/
Dependencies: Requires 5.1 + 5.2 (Image Viewer + Annotations). Enhanced by 5.4 (Revision Timeline).
5.6 — Feedback Action Items (Artist Checklist)
What: Every annotation and review comment automatically becomes an actionable to-do item on a checklist for the assigned artist. Artists see a clear, prioritized list of everything they need to address for each revision round, and can check items off as they work through them. When all items are resolved, the stage is ready for resubmission.
Why: Without this, artists must mentally parse through comment threads and scan annotations to build their own list of what needs fixing. Items get missed, context gets lost, and producers have to manually verify whether feedback was addressed. A structured checklist makes the feedback-to-fix loop explicit, trackable, and auditable.
How it works — the full feedback loop:
- Reviewer creates feedback — draws annotation or writes comment with actionable note
- System auto-creates a FeedbackItem — linked to the annotation/comment, assigned to the stage's artist(s), categorized by severity
- Artist sees checklist — organized by priority, with direct links to the annotation on the image
- Artist works through items — checks each off as addressed, optionally adds a resolution note ("Fixed — adjusted specular intensity by 15%")
- Artist submits new revision — any unchecked items carry forward as still-open on the next round with a warning
- Reviewer verifies — can see which items were marked resolved, compare before/after, and either confirm resolution or reopen
Where the checklist appears (3 locations):
1. Review Viewer — Feedback Panel (primary)
- Dedicated collapsible panel in the review viewer, alongside the revision timeline (5.4)
- Full checklist for the current revision round with:
- Checkbox + item summary (auto-generated from annotation/comment text)
- Severity indicator: Critical (red), Major (orange), Minor (yellow), Suggestion (blue)
- Thumbnail crop of the annotated region (click to zoom to that spot on the image)
- "Resolve" action with optional note field
- Status: Open, In Progress, Resolved, Verified (by reviewer), Reopened
- Filter by: status (open/resolved), severity, annotation type
- Progress bar at top: "4 of 7 items resolved"
- Grouped by revision round — see current round's items and carried-forward items from previous rounds
2. My Work Page — Feedback Summary (artist's home base)
- Each assignment card on the existing My Work page gets a feedback badge: "5 open items"
- Expandable section within each assignment showing the checklist inline
- Click any item to deep-link directly to the review viewer, zoomed to that annotation
- Aggregate counters at top of My Work page: "12 open feedback items across 3 assignments"
- Sort/filter assignments by: most feedback items, highest severity items first, oldest unresolved items
3. Stage Card on Deliverable Detail — Progress Indicator (producer view)
- Compact progress indicator on the stage card: "4/7 feedback items resolved"
- Color-coded: red if critical items are open, green if all resolved
- Hover popover showing breakdown by severity
- Producers can see at a glance which stages have outstanding feedback before approving
Auto-creation rules:
- When an annotation is created → auto-create FeedbackItem linked to that annotation
- When a comment is posted and contains actionable language → auto-create FeedbackItem (optional: reviewer can toggle "Mark as action item" checkbox when posting a comment, default ON for annotations, default OFF for comments to avoid noise from discussion threads)
- Reviewers can also manually create standalone feedback items (not tied to an annotation) for general notes like "Overall color temperature feels too warm"
- Bulk creation: reviewer can batch-create items from a review session (5.5) where each "changes requested" decision auto-generates items from that session's annotations
Severity levels:
- Critical — Must fix before resubmission (blocks approval). E.g., wrong product shown, missing required element
- Major — Should fix, significant quality issue. E.g., visible artifacts, wrong lighting
- Minor — Nice to fix, small quality issue. E.g., minor texture seam, slight color shift
- Suggestion — Optional improvement, not required for approval. E.g., "Consider slightly warmer fill light"
Data model additions:
model FeedbackItem {
id String @id @default(cuid())
deliverableStageId String
deliverableStage DeliverableStage @relation(fields: [deliverableStageId], references: [id], onDelete: Cascade)
revisionId String
revision Revision @relation(fields: [revisionId], references: [id], onDelete: Cascade)
annotationId String?
annotation Annotation? @relation(fields: [annotationId], references: [id], onDelete: SetNull)
commentId String?
comment Comment? @relation(fields: [commentId], references: [id], onDelete: SetNull)
summary String // short description of what needs to be done
severity FeedbackSeverity @default(MAJOR)
status FeedbackStatus @default(OPEN)
sortOrder Int @default(0)
assignedToId String?
assignedTo User? @relation("FeedbackAssignee", fields: [assignedToId], references: [id])
createdById String
createdBy User @relation("FeedbackCreator", fields: [createdById], references: [id])
resolvedById String?
resolvedBy User? @relation("FeedbackResolver", fields: [resolvedById], references: [id])
resolvedAt DateTime?
resolutionNote String?
verifiedById String?
verifiedBy User? @relation("FeedbackVerifier", fields: [verifiedById], references: [id])
verifiedAt DateTime?
carriedFromId String? // if carried forward from a previous round
carriedFrom FeedbackItem? @relation("FeedbackCarryForward", fields: [carriedFromId], references: [id])
carriedTo FeedbackItem[] @relation("FeedbackCarryForward")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([deliverableStageId])
@@index([revisionId])
@@index([assignedToId])
@@index([status])
@@map("feedback_items")
}
enum FeedbackSeverity {
CRITICAL
MAJOR
MINOR
SUGGESTION
}
enum FeedbackStatus {
OPEN
IN_PROGRESS
RESOLVED
VERIFIED
REOPENED
}
Key files:
src/components/review/feedback-checklist.tsx— Main checklist panel in review viewersrc/components/review/feedback-item.tsx— Individual checklist item with resolve actionsrc/components/review/feedback-progress-bar.tsx— Progress bar (4/7 resolved)src/components/review/feedback-create-dialog.tsx— Manual item creationsrc/components/my-work/feedback-summary.tsx— Inline checklist on My Work pagesrc/components/stages/feedback-indicator.tsx— Compact badge for stage cardssrc/lib/services/feedback-service.ts— CRUD, auto-creation logic, carry-forwardsrc/lib/validators/feedback.ts— Zod schemassrc/hooks/use-feedback-items.ts— TanStack Query hook- API routes:
/api/feedback/,/api/feedback/[itemId]/,/api/stages/[stageId]/feedback/
Automation integration (Phase 7):
- Auto-rule: "When all Critical + Major feedback items are resolved → notify producer that stage is ready for re-review"
- Auto-rule: "When a new revision is submitted with unresolved Critical items → warn artist and block submission" (configurable)
- Auto-rule: "When feedback item is unresolved for >3 days → escalate notification"
Dependencies: Requires 5.2 (Annotations) + 5.4 (Revision Timeline). Enhanced by 7.1 (Automation triggers). Integrates with existing My Work page and deliverable detail page.
Phase 6: Workload & Resource Management
Gives producers visibility into team capacity so they can make informed assignment decisions and prevent burnout/bottlenecks.
6.1 — Capacity Planning View
What: Visual overview of each artist's assignment load across all projects, broken down by week. Shows allocated vs. available capacity with overallocation warnings.
Implementation:
- New page:
/workload— grid with artists as rows, weeks as columns - Each cell shows number of active assignments + status breakdown (colored segments)
- Configurable capacity threshold per user (default: 5 concurrent assignments)
- Overallocation highlighted in red when assignments exceed threshold
- Click a cell to drill down into specific assignments for that artist/week
- Filter by project, stage type, date range
- Data sourced from existing StageAssignment + DeliverableStage records
Data model additions:
// Add to User model:
maxCapacity Int @default(5) // max concurrent assignments
Key files:
src/app/(app)/workload/page.tsx— Capacity planning pagesrc/components/workload/capacity-grid.tsx— Artist x Week gridsrc/components/workload/capacity-cell.tsx— Individual cell with status barssrc/components/workload/capacity-detail-popover.tsx— Drill-down assignment listsrc/lib/services/workload-service.ts— Aggregation queries- API route:
/api/workload/
6.2 — Utilization Heatmaps
What: Color-coded heatmap showing team utilization over time. Instantly spot who's overloaded, who's underutilized, and how load trends over weeks/months.
Implementation:
- Heatmap component integrated into workload page as an alternate visualization
- Color scale: light green (low load) -> yellow (moderate) -> red (overallocated)
- Toggle between absolute count and percentage of capacity
- Time range: 4-week, 8-week, 12-week, custom
- Export to image/PDF for stakeholder reports
Key files:
src/components/workload/utilization-heatmap.tsx— Heatmap visualization (recharts)src/components/workload/heatmap-legend.tsx— Color scale legend
Dependencies: Requires 6.1 (Capacity Planning data)
6.3 — Skill-Based Assignment Suggestions
What: Tag users with specialties (modeling, texturing, lighting, compositing, animation). When assigning an artist to a stage, suggest best-fit artists based on skill match, current workload, and availability.
Implementation:
- New
Skillmodel and many-to-many with User - Predefined skill set relevant to CG pipeline: Modeling, Texturing, UV Mapping, Lighting, Rendering, Compositing, Animation, Rigging, Photography, Retouching
- Each pipeline stage template maps to recommended skills
- Assignment dialog shows suggested artists sorted by: skill match -> lowest current load
- Visual indicator showing each suggestion's match score and current utilization
Data model additions:
model Skill {
id String @id @default(cuid())
name String @unique
users UserSkill[]
stages StageSkillRequirement[]
}
model UserSkill {
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
skillId String
skill Skill @relation(fields: [skillId], references: [id], onDelete: Cascade)
level SkillLevel @default(INTERMEDIATE)
@@id([userId, skillId])
}
model StageSkillRequirement {
stageTemplateId String
stageTemplate PipelineStageTemplate @relation(fields: [stageTemplateId], references: [id])
skillId String
skill Skill @relation(fields: [skillId], references: [id])
importance Int @default(1) // 1=nice-to-have, 2=important, 3=required
@@id([stageTemplateId, skillId])
}
enum SkillLevel {
JUNIOR
INTERMEDIATE
SENIOR
LEAD
}
Key files:
src/app/(app)/settings/skills/page.tsx— Skill management page (admin)src/components/assignments/skill-match-suggestions.tsx— Suggested artists listsrc/lib/services/skill-service.ts— Matching algorithm- API routes:
/api/skills/,/api/users/[userId]/skills/
Dependencies: Requires 6.1 (capacity data for load-aware suggestions)
Phase 7: Automation & Workflow Engine
Reduces manual overhead by automating repetitive status changes, notifications, and escalations based on configurable triggers.
7.1 — Trigger-Based Automations
What: Configurable "when X happens, do Y" rules that execute automatically. Eliminates manual status bumping and notification sending for predictable workflows.
Example rules:
- When all Catalog Images are APPROVED -> auto-advance Hero/Packaging/Photocomps/360/Dynamic to NOT_STARTED
- When a deliverable is 2 days past due -> notify the assigned PM
- When an artist submits a revision -> auto-set stage to IN_REVIEW
- When a review session is completed -> update all approved items' stages to APPROVED
Implementation:
- Automation rules stored in database, configurable per organization
- Rule structure: Trigger (event type + conditions) -> Actions (status change, notification, assignment)
- Event bus: when a stage/revision/assignment changes, evaluate matching rules
- Built-in rule templates for common CG pipeline workflows
- Admin UI to create, edit, enable/disable rules
- Execution log for audit trail (what fired, when, what it did)
Data model additions:
model AutomationRule {
id String @id @default(cuid())
name String
description String?
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
isEnabled Boolean @default(true)
trigger Json // { event, conditions[] }
actions Json // { type, params }[]
createdById String
createdBy User @relation(fields: [createdById], references: [id])
executions AutomationExecution[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model AutomationExecution {
id String @id @default(cuid())
ruleId String
rule AutomationRule @relation(fields: [ruleId], references: [id], onDelete: Cascade)
triggeredBy Json // the event that triggered it
result Json // what actions were taken
status ExecutionStatus
executedAt DateTime @default(now())
}
enum ExecutionStatus {
SUCCESS
PARTIAL_FAILURE
FAILURE
}
Supported trigger events:
stage.status_changed— with conditions on stage type, old/new statusrevision.submitted— new revision uploadedreview.decision_made— approve/reject in review sessiondeadline.approaching— N days before due datedeadline.passed— due date exceededassignment.created— artist assigned to stage
Supported actions:
update_stage_status— change a stage's statussend_notification— create notification for specified users/rolescreate_assignment— auto-assign a usersend_webhook— POST to external URL (future AI review integration point)
Key files:
src/app/(app)/settings/automations/page.tsx— Automation rules managementsrc/components/automations/rule-builder.tsx— Visual rule editorsrc/components/automations/execution-log.tsx— Audit log viewersrc/lib/services/automation-service.ts— Rule evaluation enginesrc/lib/automation/event-bus.ts— Event dispatchersrc/lib/automation/action-executor.ts— Action runner- API routes:
/api/automations/,/api/automations/[ruleId]/,/api/automations/[ruleId]/executions/
Note: The existing dependency engine (src/lib/pipeline/dependency-engine.ts) already
handles stage unblocking. The automation engine extends this with user-configurable rules
beyond the hardcoded pipeline dependencies.
7.2 — Multi-Level Approval Chains
What: Define approval workflows requiring sign-off from multiple stakeholders in sequence. For example: Artist Lead -> Producer -> Client Contact.
Implementation:
- Approval chain template per organization (configurable per stage type)
- Each step has: approver role/user, required or optional, auto-advance on approve
- Status tracking per step: PENDING -> APPROVED / REJECTED
- When all required steps are approved, the stage is marked APPROVED
- If any step is rejected, stage goes back to IN_PROGRESS with feedback
- Visual progress indicator showing which approval steps are complete
- Email/in-app notification at each step to the next approver
Data model additions:
model ApprovalChain {
id String @id @default(cuid())
name String
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
stageType String? // null = applies to all stages
steps ApprovalStep[]
createdAt DateTime @default(now())
}
model ApprovalStep {
id String @id @default(cuid())
chainId String
chain ApprovalChain @relation(fields: [chainId], references: [id], onDelete: Cascade)
stepOrder Int
approverRole Role? // role-based (any PRODUCER)
approverUserId String? // specific user
approverUser User? @relation(fields: [approverUserId], references: [id])
isRequired Boolean @default(true)
autoAdvance Boolean @default(true)
approvalRecords ApprovalRecord[]
}
model ApprovalRecord {
id String @id @default(cuid())
deliverableStageId String
deliverableStage DeliverableStage @relation(fields: [deliverableStageId], references: [id])
stepId String
step ApprovalStep @relation(fields: [stepId], references: [id])
decision ReviewDecision
note String?
decidedById String
decidedBy User @relation(fields: [decidedById], references: [id])
decidedAt DateTime @default(now())
}
Key files:
src/app/(app)/settings/approvals/page.tsx— Approval chain configurationsrc/components/approvals/chain-builder.tsx— Visual chain editor (drag to reorder steps)src/components/approvals/approval-progress.tsx— Step-by-step progress indicatorsrc/lib/services/approval-service.ts— Chain evaluation and advancement- API routes:
/api/approval-chains/,/api/stages/[stageId]/approve/
7.3 — Project Templates
What: Save a full project configuration (deliverable list, stage settings, default assignments, automation rules) as a reusable template. One-click project creation for repeat HP SKU types.
Why: HP product shoots often follow the same structure — same deliverable types, same pipeline, same team assignments. Templates eliminate 15-20 minutes of manual setup per project.
Implementation:
- "Save as Template" action on any existing project
- Template captures: deliverable names/types, stage configurations, default assignments (by role, not specific user), automation rules, priority defaults
- "New Project from Template" in project creation dialog
- Template library page with preview, edit, duplicate, delete
- Org-level templates (shared) + personal templates
Data model additions:
model ProjectTemplate {
id String @id @default(cuid())
name String
description String?
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
createdById String
createdBy User @relation(fields: [createdById], references: [id])
isShared Boolean @default(true)
configuration Json // full template config
deliverables ProjectTemplateDeliverable[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model ProjectTemplateDeliverable {
id String @id @default(cuid())
templateId String
template ProjectTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
name String
type String?
priority Priority @default(MEDIUM)
defaultAssignments Json? // [{ role, stageType }]
sortOrder Int
}
Key files:
src/app/(app)/templates/page.tsx— Template librarysrc/components/templates/template-card.tsx— Template preview cardsrc/components/templates/template-builder.tsx— Create/edit templatesrc/lib/services/template-service.ts— Template CRUD + instantiation- API routes:
/api/templates/,/api/templates/[templateId]/instantiate/
Phase 8: Asset Intelligence & AI Review Integration
Ensures uploaded assets meet HP's strict quality standards through automated validation and future AI-powered review. The AI Review Engine is being developed as a separate service and will connect via API.
8.1 — File Validation on Upload
What: Automatically validate uploaded files against spec requirements: resolution, color space, file format, file size, aspect ratio. Reject non-conforming files with clear error messages before they enter the review pipeline.
Implementation:
- Validation rules configurable per stage type (e.g., Catalog Images require 4000x4000px minimum, sRGB color space, PNG or TIFF)
- Server-side validation using
sharpfor image metadata extraction - Client-side pre-validation for instant feedback (file type, basic dimensions)
- Validation report: pass/fail per check with details
- Override option for producers (with reason logged) when files are intentionally non-standard
- Batch validation for multi-file uploads
Data model additions:
model AssetSpec {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
stageType String // which pipeline stage this spec applies to
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
revision Revision @relation(fields: [revisionId], references: [id], onDelete: Cascade)
specId String
spec AssetSpec @relation(fields: [specId], references: [id])
passed Boolean
results Json // [{ check, expected, actual, passed }]
overrideById String?
overrideBy User? @relation(fields: [overrideById], references: [id])
overrideReason String?
validatedAt DateTime @default(now())
}
Key files:
src/app/(app)/settings/asset-specs/page.tsx— Spec configuration (admin)src/components/upload/file-validator.tsx— Validation UI with pass/fail reportsrc/lib/services/asset-validation-service.ts— Validation enginesrc/lib/validators/asset-specs.ts— Zod schemas for spec rules- API routes:
/api/asset-specs/,/api/revisions/[revisionId]/validate/
New dependency: sharp (image processing — already commonly used in Next.js)
8.2 — Thumbnail & Preview Generation
What: Auto-generate web-optimized thumbnails and preview images from uploaded high-resolution assets. Enables fast browsing without downloading 100MB+ source files.
Implementation:
- On upload: generate 3 sizes — thumbnail (200px), preview (1200px), full (original)
- Use
sharpfor server-side image processing - Store generated previews alongside originals (same storage, different paths)
- Lazy loading with blur placeholder (LQIP — low quality image placeholder)
- Support TIFF/EXR to JPEG/WebP conversion for browser viewing
- Background job queue for processing (avoid blocking upload response)
Key files:
src/lib/services/preview-service.ts— Image processing pipelinesrc/lib/jobs/generate-previews.ts— Background processing jobsrc/components/common/optimized-image.tsx— Image component with lazy load + LQIP
8.3 — AI Review Engine Integration Point
What: API integration layer for the external AI Review Engine. When connected, the AI engine analyzes uploaded renders for quality issues (noise, artifacts, color accuracy, composition) and returns structured feedback.
Why: HP demands exceptional quality consistency. Manual review catches most issues but human reviewers have throughput limits and can miss subtle problems. The AI engine acts as a first-pass QC layer.
Implementation:
- Webhook endpoint that the AI Review Engine calls with analysis results
- Structured result format: overall score, per-check scores, flagged regions (coordinates), severity levels, suggested actions
- AI review results displayed alongside human review in the annotation viewer
- AI-flagged regions rendered as annotations with a distinct style (dashed border, AI icon)
- Configuration: which stages trigger AI review, confidence thresholds, auto-block on fail
- Dashboard widget showing AI review pass rates and common issue categories
Data model additions:
model AIReviewResult {
id String @id @default(cuid())
revisionId String
revision Revision @relation(fields: [revisionId], references: [id], onDelete: Cascade)
overallScore Float // 0-100
passed Boolean
checks Json // [{ name, score, severity, details }]
flaggedRegions Json // [{ x, y, width, height, issue, severity }]
engineVersion String
processingTimeMs Int
createdAt DateTime @default(now())
}
Key files:
src/app/api/webhooks/ai-review/route.ts— Incoming webhook from AI enginesrc/components/review/ai-review-overlay.tsx— AI annotations on image viewersrc/components/review/ai-review-summary.tsx— Score card + issue listsrc/lib/services/ai-review-service.ts— Result processing + storage- API routes:
/api/webhooks/ai-review/,/api/revisions/[revisionId]/ai-review/
Dependencies: External AI Review Engine (separate project). This phase only builds the integration layer — the engine itself is developed independently.
8.4 — AI-Powered Natural Language Search (pgvector)
What: A chat-style search panel where producers can ask questions in plain English and get back relevant projects, deliverables, and pipeline stages with direct links. For example: "Which Envy projects are running behind?" or "Show me deliverables similar to the Q3 packaging work."
Why: As the tracker grows to hundreds of projects and thousands of deliverables, finding the right information becomes harder. Traditional filters work for structured queries (status = overdue), but producers often think in terms of meaning and context. Natural language search bridges that gap without requiring producers to learn complex filter combinations.
Approach: Use PostgreSQL's pgvector extension to add vector search directly to
our existing database — no external vector database service needed. Use Ollama to
run embedding and summarization models locally — zero API costs, no data leaves the
network, and no dependency on external AI services. This keeps the architecture simple,
self-contained, and free to operate.
Implementation:
-
Database setup
- Enable
pgvectorextension on PostgreSQL (CREATE EXTENSION vector) - Add raw SQL migration for embedding columns (Prisma doesn't natively support
vector types — use
Unsupported("vector(768)")in schema, raw SQL for queries) - Add
embedding Vector(768)column toprojects,deliverables, anddeliverable_stagestables (768 dimensions fornomic-embed-textmodel)
- Enable
-
Embedding generation service
- On create/update of a project or deliverable, generate a text representation by concatenating key fields: name, description, status, priority, assignees, deliverable names, notes, business unit, code name, etc.
- Call the local Ollama API (
POST http://ollama:11434/api/embeddings) using thenomic-embed-textmodel to convert that text into a 768-dimensional vector - Store the vector in the embedding column
- One-time backfill script to generate embeddings for all existing records
- Service layer hook to regenerate embeddings when records change
-
Search API
- New endpoint:
/api/search/semantic/ - Accepts a natural language query string
- Converts the query to an embedding using the same model
- Runs cosine similarity search via pgvector:
SELECT *, embedding <=> $1 AS distance FROM projects ORDER BY distance LIMIT 10 - Hybrid routing: detect structural queries (dates, statuses, priorities) and route to standard Prisma filters; route meaning-based queries to vector search
- Results include entity type, ID, name, status, and relevance score
- New endpoint:
-
LLM summarization layer (optional enhancement)
- Pass the top search results + the user's original question to a local Ollama LLM
(
POST http://ollama:11434/api/generate) usingqwen3.5:9b - Generate a natural language summary: "There are 3 Envy projects currently behind schedule. The most critical is Envy 16 Refresh with 4 overdue deliverables..."
- Return both the AI summary and the structured result list
- Runs entirely on-premises — no project data ever leaves the network
- Pass the top search results + the user's original question to a local Ollama LLM
(
-
Frontend: Producer Search Chat
- Extend the existing
cmdkcommand palette with a "smart search" mode, or add a dedicated slide-out chat panel accessible from the top nav - Input: free-text query field
- Output: AI summary (if enabled) at the top, followed by clickable result cards for matching projects/deliverables that link directly into the tracker
- Show relevance scores and highlight why each result matched
- Conversation history within the session for follow-up questions
- Extend the existing
Data model additions:
// Add to existing models (raw SQL migration — Prisma Unsupported type)
// projects table: embedding Unsupported("vector(768)")?
// deliverables table: embedding Unsupported("vector(768)")?
model SearchLog {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id])
query String
resultCount Int
clickedId String? // which result the user opened (for relevance tuning)
createdAt DateTime @default(now())
@@index([userId])
@@map("search_logs")
}
Key files:
src/lib/services/embedding-service.ts— Generate and store embeddingssrc/lib/services/semantic-search-service.ts— Vector search + hybrid routingsrc/app/api/search/semantic/route.ts— Search API endpointsrc/components/search/smart-search-panel.tsx— Chat-style search UIsrc/hooks/use-semantic-search.ts— React Query hook for searchprisma/migrations/xxx_add_pgvector.sql— Raw SQL migration for pgvector setupscripts/backfill-embeddings.ts— One-time backfill script
New dependencies:
pgvectorPostgreSQL extension (installed on the database, not an npm package)- Ollama service (Docker container —
ollama/ollamaimage) - Ollama models:
nomic-embed-text(embeddings, ~274MB),qwen3.5:9b(summarization) — pulled automatically on first container start - Note:
qwen3.5:9bhas been downloaded to the local dev machine and is ready for testing. This is our chosen summarization model going forward. - No paid API services — everything runs locally
Practical notes:
- Zero ongoing AI costs — all models run on-premises via Ollama
- No project data ever leaves the network — important for HP production data
- Ollama exposes a simple REST API (
http://ollama:11434) — the embedding service just makes HTTP calls, no SDK needed - Embeddings are fast even on CPU (~10-50ms per record); summarization benefits from GPU but works on CPU with a few extra seconds per query
- If vector search needs ever outgrow pgvector's performance at scale, migration to a dedicated vector database like Pinecone is straightforward — the embedding generation and search API layers stay the same, only the storage backend changes
- Search logs enable future relevance tuning and usage analytics
- See Phase 12: Docker Deployment for the full containerized deployment strategy including Ollama
Phase 9: Advanced Reporting & Analytics
Builds on the existing dashboard with deeper insights for project management and stakeholder reporting.
9.1 — Velocity & Throughput Metrics
What: Track deliverables completed per week, average time per stage, and identify pipeline bottlenecks.
Implementation:
- Time tracking derived from stage status change timestamps (already captured in
updatedAt) - Metrics: throughput (deliverables completed/week), cycle time (brief intake to final approval), stage dwell time (average time spent in each stage), bottleneck detection (stages with longest average dwell)
- Trend charts over configurable time windows (4/8/12/26 weeks)
- Filterable by project, stage type, artist, priority
- Compare periods (this month vs. last month)
Key files:
src/components/dashboard/velocity-chart.tsx— Throughput trend linesrc/components/dashboard/cycle-time-chart.tsx— Average cycle time breakdownsrc/components/dashboard/bottleneck-chart.tsx— Stage dwell time comparisonsrc/lib/services/analytics-service.ts— Metric aggregation queries
9.2 — Burndown Charts
What: Per-project burndown showing remaining deliverables over time with projected completion date based on current velocity.
Implementation:
- Chart shows: ideal burndown line, actual burndown line, projection line
- Scope changes (added deliverables) shown as step-ups in the remaining count
- Confidence interval on projection based on velocity variance
- Warning when projected completion exceeds deadline
Key files:
src/components/dashboard/burndown-chart.tsx— Burndown visualizationsrc/components/dashboard/projection-engine.ts— Velocity-based projection math
9.3 — Client-Facing Dashboard (Read-Only Portal)
What: Simplified, read-only view for HP stakeholders to check project status without needing full app access. Accessible via secure share link.
Implementation:
- New route group:
src/app/(portal)/— minimal layout, no sidebar, branded header - Share link generation with token-based auth (no account required)
- Configurable visibility: which projects, which data fields are exposed
- Shows: project status overview, deliverable progress grid, latest revision thumbnails, timeline, pending decisions
- No edit capabilities — view only
- Optional password protection + expiry on share links
- Mobile-optimized layout
Data model additions:
model PortalLink {
id String @id @default(cuid())
token String @unique
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
projectIds String[] // which projects are visible
createdById String
createdBy User @relation(fields: [createdById], references: [id])
password String? // bcrypt hashed, optional
expiresAt DateTime?
lastAccessedAt DateTime?
accessCount Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
}
Key files:
src/app/(portal)/[token]/page.tsx— Portal landing pagesrc/app/(portal)/[token]/projects/[projectId]/page.tsx— Project detail viewsrc/components/portal/portal-header.tsx— Branded header with HP/Oliver logossrc/components/portal/project-overview.tsx— Status summary gridsrc/lib/services/portal-service.ts— Token validation + scoped data accesssrc/middleware.ts— Portal route auth handling- API routes:
/api/portal/,/api/portal/[token]/
9.4 — SLA Tracking
What: Define target turnaround times per stage type. Measure actual vs. target and flag SLA breaches in real-time.
Implementation:
- SLA configuration per organization per stage type (e.g., Model Prep: 3 business days, Catalog Images: 5 business days)
- Real-time SLA status: on-track (green), at-risk (yellow, >75% elapsed), breached (red)
- SLA dashboard widget showing compliance rate and breach trends
- Notification when an SLA is at risk (configurable threshold)
- Historical SLA compliance reports (filterable by project, stage, artist, date range)
- Business-hours-aware calculation (exclude weekends, optionally holidays)
Data model additions:
model SLATarget {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
stageType String
targetHours Int // business hours
warningThreshold Float @default(0.75) // warn at 75% of target
isActive Boolean @default(true)
createdAt DateTime @default(now())
}
Key files:
src/app/(app)/settings/sla/page.tsx— SLA target configurationsrc/components/dashboard/sla-compliance-chart.tsx— Compliance rate visualizationsrc/components/stages/sla-indicator.tsx— Inline SLA status badgesrc/lib/services/sla-service.ts— SLA calculation engine (business hours aware)
Phase 10: Collaboration Enhancements
Deepens the communication layer to reduce dependency on email and Slack for project-related discussions.
10.1 — Rich @Mentions with Deep Linking
What: @mention users in comments with autocomplete. Mentioned users receive notifications with deep links directly to the relevant deliverable/stage/comment.
Implementation:
@trigger in comment input opens user autocomplete dropdown- Mentions stored as structured data:
{ userId, displayName, position }within comment text - Notification created for each mentioned user with direct link to the comment
- Mentioned user's name rendered as a clickable link to their profile
- Support @mentioning roles (e.g., @producers) to notify all users with that role
Key files:
src/components/comments/mention-input.tsx— Comment input with @mention autocompletesrc/components/comments/mention-renderer.tsx— Renders mention inline with link- Update
src/lib/services/comment-service.ts— Extract mentions + create notifications
10.2 — Project Activity Feed
What: Unified chronological stream of all activity on a project: status changes, comments, uploads, assignments, approvals. Single source of truth for "what happened."
Implementation:
- Activity log entries auto-generated on every state change (already partially done via notifications — extend to a dedicated activity model)
- Filterable by: event type, user, deliverable, date range
- Compact view (one-liner per event) and expanded view (with details)
- Infinite scroll with cursor-based pagination
Data model additions:
model ActivityEntry {
id String @id @default(cuid())
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
deliverableId String?
deliverable Deliverable? @relation(fields: [deliverableId], references: [id])
stageId String?
userId String
user User @relation(fields: [userId], references: [id])
type ActivityType
summary String // human-readable: "Jane moved Catalog Images to IN_REVIEW"
metadata Json? // structured event data for rich rendering
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— Feed component with filterssrc/components/activity/activity-entry.tsx— Individual entry renderersrc/lib/services/activity-service.ts— Logging + querying- API route:
/api/projects/[projectId]/activity/
10.3 — External Review Links
What: Generate watermarked, time-limited URLs for external stakeholders (e.g., HP brand team) to view specific deliverables without creating an account.
Implementation:
- Generate unique token-based URL for specific deliverable/revision
- Watermark overlay on images showing: reviewer email/name, date, "CONFIDENTIAL"
- Configurable expiry (24h, 48h, 7 days, 30 days)
- Access logging (who viewed, when, from what IP)
- Revoke link capability
- Comment capability for external reviewers (optional, requires email verification)
Key files:
src/app/(external)/review/[token]/page.tsx— External review pagesrc/components/review/watermark-overlay.tsx— Dynamic watermark renderersrc/lib/services/external-link-service.ts— Token generation + validation- API routes:
/api/external-links/,/api/external-links/[token]/
Note: Overlaps with 5.5 Review Sessions share functionality and 9.3 Client Portal. These should share the underlying token/auth infrastructure but serve different use cases (bulk review vs. individual deliverable vs. project overview).
Phase 11: Quality of Life & Polish
Incremental UX improvements that make the daily workflow faster and more pleasant.
11.1 — Saved Filters & Custom Views
What: Let users save their current filter/sort/column configuration as a named view. Quick-switch between saved views. Share views with team.
Implementation:
- "Save View" button in table/board/timeline toolbar
- View stores: filters, sort order, column visibility, column order, grouping
- Personal views (private) and shared views (visible to org)
- Quick access via dropdown in view toolbar
- Pin favorite views for one-click access
- URL-encode view ID so saved views are linkable
Data model additions:
model SavedView {
id String @id @default(cuid())
name String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
projectId String? // null = cross-project view
viewType ViewType // TABLE, BOARD, TIMELINE
configuration Json // { filters, sort, columns, groupBy }
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— Dropdown with saved viewssrc/components/views/save-view-dialog.tsx— Save/edit view dialogsrc/lib/services/saved-view-service.ts— CRUD- API routes:
/api/views/,/api/views/[viewId]/
11.2 — Extended Command Palette Actions
What: Extend the existing Cmd+K palette with quick-actions beyond navigation: "approve and advance," "assign to me," "mark blocked," "start review session."
Implementation:
- Add action commands alongside existing search/navigation commands
- Context-aware actions: show relevant actions based on current page/selected item
- Recent commands history
- Fuzzy search across all command types
- Keyboard shortcut hints shown inline
Key files:
- Update
src/components/layout/command-palette.tsx— Add action commands src/lib/commands/action-registry.ts— Registry of available actions + handlers
11.3 — Offline Indicator & Optimistic Updates
What: Detect when connectivity drops, show indicator, queue changes locally, and sync when connection resumes. Extends existing TanStack Query optimistic updates.
Implementation:
- Network status detection via
navigator.onLine+ periodic ping - Visual indicator in topbar when offline (amber banner)
- TanStack Query already supports optimistic updates — extend to queue failed mutations
- Sync queue with retry logic when back online
- Conflict resolution: last-write-wins with toast notification if server state diverged
Key files:
src/components/layout/offline-indicator.tsx— Banner componentsrc/hooks/use-network-status.ts— Network detection hooksrc/lib/sync/mutation-queue.ts— Offline mutation queue
11.4 — Batch Upload with Auto-Matching
What: Drag-and-drop multiple files onto a project and auto-match to deliverables by
file naming convention (e.g., SKU-12345_catalog_v2.png matches Catalog Images deliverable).
Implementation:
- Drop zone on project detail page accepting multiple files
- Naming convention parser: configurable regex patterns per organization
- Preview table showing: file name, matched deliverable, matched stage, confidence
- Manual override for mismatches or unmatched files
- Bulk upload with progress indicator
- Auto-create revisions for matched files
Key files:
src/components/upload/batch-upload-zone.tsx— Drag-and-drop areasrc/components/upload/file-matcher.tsx— Auto-match preview tablesrc/lib/services/file-matching-service.ts— Naming convention parser + matchersrc/lib/validators/naming-convention.ts— Configurable pattern schemas
Phase 12: Docker Deployment
Containerize the entire application stack for consistent, one-command deployment to any server. Eliminates "works on my machine" issues, simplifies onboarding, and makes the Ollama AI layer a natural part of the infrastructure rather than a separate install.
12.1 — Docker Compose Stack
What: A docker-compose.yml that defines the complete application as three services:
the Next.js app, PostgreSQL with pgvector, and Ollama with pre-configured models. One
docker compose up starts everything.
Architecture:
┌─────────────────────────────────────────────────────┐
│ docker-compose.yml │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────────┐ │
│ │ app │ │ db │ │ ollama │ │
│ │ Next.js │ │ PostgreSQL │ │ nomic-embed │ │
│ │ Port 3000 │ │ + pgvector│ │ qwen3.5:9b │ │
│ │ │──│ Port 5432 │ │ Port 11434 │ │
│ │ │ │ │ │ │ │
│ └────────────┘ └────────────┘ └────────────────┘ │
│ │ │ │ │
│ [app-network] [db-volume] [ollama-volume] │
└─────────────────────────────────────────────────────┘
Services:
| Service | Image | Purpose |
|---|---|---|
app |
Custom (Dockerfile) | Next.js production build, serves the tracker |
db |
pgvector/pgvector:pg17 |
PostgreSQL 17 with pgvector extension pre-installed |
ollama |
ollama/ollama:latest |
Local AI model server for embeddings and summarization |
Implementation:
-
Dockerfile(Next.js app)- Multi-stage build:
node:20-alpinefor deps + build, minimal final image - Stage 1: Install dependencies (
npm ci) - Stage 2: Build the Next.js app (
npm run build) - Stage 3: Production image with only
next startand built output - Runs
prisma generateduring build,prisma migrate deployon startup - Final image size target: ~200-300MB
- Multi-stage build:
-
docker-compose.yml- Three services (
app,db,ollama) on a shared internal network appdepends ondbandollamawith health checksdbusespgvector/pgvector:pg17image with pgvector ready out of the boxollamauses official image with a startup script to pull models on first run- Named volumes for database data (
pgdata) and Ollama models (ollama-models) - Environment variables sourced from
.envfile - Only
appexposes a port to the host (3000);dbandollamaare internal only
- Three services (
-
docker/ollama-entrypoint.sh(model bootstrap script)- Starts the Ollama server
- Checks if required models are already pulled (cached in volume)
- If not, pulls
nomic-embed-textandqwen3.5:9bautomatically - Subsequent starts skip the pull — models persist in the Docker volume
-
docker/db-init.sql(database initialization)CREATE EXTENSION IF NOT EXISTS vector;— ensures pgvector is enabled- Runs automatically on first database creation via PostgreSQL's init script mechanism
-
.env.example(deployment template)# Database DATABASE_URL=postgresql://postgres:your_password@db:5432/hp_prod_tracker POSTGRES_PASSWORD=your_password POSTGRES_DB=hp_prod_tracker # NextAuth NEXTAUTH_URL=http://your-server:3000 NEXTAUTH_SECRET=generate-a-random-secret # Ollama (internal — no need to change) OLLAMA_HOST=http://ollama:11434 OLLAMA_EMBED_MODEL=nomic-embed-text OLLAMA_LLM_MODEL=qwen3.5:9b
Key files:
Dockerfile— Multi-stage Next.js production builddocker-compose.yml— Full stack orchestrationdocker/ollama-entrypoint.sh— Model bootstrap scriptdocker/db-init.sql— pgvector extension initialization.env.example— Environment variable template with documentation.dockerignore— Exclude node_modules, .next, .git, etc.
12.2 — Health Checks & Startup Orchestration
What: Ensure services start in the correct order and the app only accepts traffic once all dependencies are healthy.
Implementation:
dbhealth check:pg_isreadycommand — app waits until database accepts connectionsollamahealth check:curl http://localhost:11434/api/tags— confirms Ollama is running and responsiveappstartup script: runsprisma migrate deployfirst (applies any pending migrations), then starts Next.js- Docker Compose
depends_onwithcondition: service_healthyensures correct order: db starts first, then ollama, then app - Restart policy:
restart: unless-stoppedon all services for automatic recovery
12.3 — Production Deployment Workflow
What: Documented step-by-step process for deploying to a server.
Deployment steps:
# 1. Clone the repository
git clone <repo-url> hp-prod-tracker
cd hp-prod-tracker
# 2. Configure environment
cp .env.example .env
# Edit .env with production values (database password, NextAuth secret, server URL)
# 3. Start everything
docker compose up -d
# 4. First run: wait for Ollama to download models (~5GB, one-time)
docker compose logs -f ollama # Watch progress, Ctrl+C when done
# 5. Seed the database (if fresh install)
docker compose exec app npx prisma db seed
# 6. Verify
curl http://localhost:3000 # Should return the app
Updating the application:
git pull
docker compose up -d --build # Rebuilds only the app container
# Prisma migrations run automatically on startup
GPU support for Ollama (optional):
- Install
nvidia-container-toolkiton the host - Add
deploy.resources.reservations.devicesto the ollama service in compose - Significantly speeds up LLM summarization; embeddings are fast regardless
- CPU-only is fully functional — GPU is a performance optimization, not a requirement
Backup strategy:
- Database:
docker compose exec db pg_dump -U postgres hp_prod_tracker > backup.sql - Ollama models: cached in volume, re-pulled automatically if lost — no backup needed
- Application: stateless — the Docker image is rebuilt from source on each deploy
12.4 — Development Environment with Docker
What: A docker-compose.dev.yml override for local development that mounts source
code and enables hot reloading while keeping the database and Ollama in containers.
Implementation:
- Override file extends the production compose with dev-specific settings
appservice: mounts./srcas a volume, runsnext devinstead ofnext startdbservice: exposes port 5432 to host for Prisma Studio / direct accessollamaservice: same as production (models don't need hot reload)- Developers can choose: run everything in Docker, or run only
db+ollamain Docker and run the Next.js app natively withnpm run dev
Usage:
# Full Docker development
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
# Or: only infrastructure in Docker, app runs natively
docker compose up db ollama
npm run dev
Key files:
docker-compose.dev.yml— Development overridesdocker/dev-entrypoint.sh— Dev startup script (skip build, run dev server)
Data Model Changes Summary
| Phase | New Models | Modified Models |
|---|---|---|
| 5 | Annotation, ReviewSession, ReviewSessionItem, FeedbackItem | Comment (add annotations + feedback relations) |
| 6 | Skill, UserSkill, StageSkillRequirement | User (add maxCapacity, skills) |
| 7 | AutomationRule, AutomationExecution, ApprovalChain, ApprovalStep, ApprovalRecord, ProjectTemplate, ProjectTemplateDeliverable | — |
| 8 | AssetSpec, AssetValidationResult, AIReviewResult, SearchLog | Revision (add validation/AI relations), Project + Deliverable (add embedding columns) |
| 9 | PortalLink, SLATarget | — |
| 10 | ActivityEntry | — |
| 11 | SavedView | — |
Total new models: 22
New API Routes Summary
| Phase | Routes |
|---|---|
| 5 | /api/annotations/, /api/reviews/, /api/reviews/[id]/items/, /api/feedback/, /api/feedback/[id]/, /api/stages/[id]/feedback/ |
| 6 | /api/workload/, /api/skills/, /api/users/[id]/skills/ |
| 7 | /api/automations/, /api/automations/[id]/executions/, /api/approval-chains/, /api/stages/[id]/approve/, /api/templates/, /api/templates/[id]/instantiate/ |
| 8 | /api/asset-specs/, /api/revisions/[id]/validate/, /api/webhooks/ai-review/, /api/revisions/[id]/ai-review/, /api/search/semantic/ |
| 9 | /api/portal/, /api/portal/[token]/, /api/analytics/velocity/, /api/analytics/sla/ |
| 10 | /api/projects/[id]/activity/, /api/external-links/ |
| 11 | /api/views/ |
Total new API routes: ~26
New Pages Summary
| Phase | Pages |
|---|---|
| 5 | Review page (per deliverable), Review sessions list, Session presenter |
| 6 | Workload/capacity page, Skills management (settings) |
| 7 | Automations management (settings), Approval chains (settings), Template library |
| 8 | Asset specs (settings), Smart search panel (chat UI) |
| 9 | Client portal (external), SLA configuration (settings) |
| 10 | Activity feed (per project), External review page |
| 11 | — (enhancements to existing pages) |
Total new pages: ~13
Third-Party Libraries
| Library | Purpose | Phase |
|---|---|---|
sharp |
Server-side image processing (validation, thumbnails, previews) | 8 |
fabric.js or native Canvas/SVG |
Annotation drawing on canvas (evaluate during 5.2) | 5 |
bcryptjs |
Password hashing for portal links | 9 |
Philosophy: Minimize new dependencies. Most features build on the existing stack (React, TanStack Query, shadcn/ui, recharts, Prisma). Canvas/SVG APIs are native browser capabilities. Only add libraries when they provide substantial value over a custom solution.
Implementation Priority & Dependencies
Phase 5 (Visual Review) ─── standalone, highest impact
|
|-- 5.1 Image Viewer <-- foundation for everything
|-- 5.2 Annotations <-- requires 5.1
|-- 5.3 Comparison <-- requires 5.1
|-- 5.4 Revision History Timeline <-- requires 5.1 + 5.2, enhanced by 5.3
|-- 5.5 Review Sessions <-- requires 5.1 + 5.2, enhanced by 5.4
+-- 5.6 Feedback Action Items <-- requires 5.2 + 5.4, integrates with My Work
Phase 6 (Workload) ─── standalone, high impact for producers
|
|-- 6.1 Capacity Grid <-- foundation
|-- 6.2 Heatmaps <-- requires 6.1
+-- 6.3 Skill Matching <-- requires 6.1
Phase 7 (Automation) ─── standalone, reduces manual work
|
|-- 7.1 Trigger Rules <-- foundation
|-- 7.2 Approval Chains <-- standalone within phase
+-- 7.3 Templates <-- standalone within phase
Phase 8 (Asset Intelligence) ─── requires Phase 5 for full value
|
|-- 8.1 File Validation <-- standalone
|-- 8.2 Preview Generation <-- standalone
|-- 8.3 AI Integration <-- requires external engine + 8.1 + 8.2
+-- 8.4 Semantic Search <-- standalone, requires pgvector extension
Phase 9 (Reporting) ─── benefits from Phase 6 + 7 data
Phase 10 (Collaboration) ─── benefits from Phase 5
Phase 11 (QoL) ─── standalone incremental improvements, can be interleaved
Phase 12 (Docker) ─── can be done at any time, benefits from 8.4 for Ollama
|
|-- 12.1 Docker Compose Stack <-- foundation
|-- 12.2 Health Checks <-- requires 12.1
|-- 12.3 Production Workflow <-- requires 12.1 + 12.2
+-- 12.4 Dev Environment <-- requires 12.1
Estimated Scope
| Phase | New Models | New Routes | New Pages | Components |
|---|---|---|---|---|
| 5 | 5 | 6 | 3 | ~32 |
| 6 | 3 | 3 | 2 | ~8 |
| 7 | 6 | 6 | 3 | ~10 |
| 8 | 4 | 5 | 2 | ~13 |
| 9 | 2 | 4 | 2 | ~8 |
| 10 | 1 | 2 | 2 | ~6 |
| 11 | 1 | 1 | 0 | ~8 |
| 12 | 0 | 0 | 0 | 0 (infra only) |
| Total | 22 | ~27 | ~14 | ~85 |
Document version: 1.1 — Created 2026-03-01, updated 2026-03-06 Updates: Added 8.4 (AI semantic search with Ollama + pgvector), Phase 12 (Docker deployment) To be updated as features are refined and priorities shift.