Skip to content

ADR-008: EmProps Open Interface UI Integration into Monorepo

Date: 2025-10-18 Status: 🤔 Proposed Decision Makers: Architecture Team Related ADRs: None


Executive Summary

This ADR proposes integrating the emprops-open-interface (Next.js UI application) from its standalone repository into the emp-job-queue Turborepo monorepo. The integration consolidates the entire EmProps stack - from job queue and ComfyUI workers to the user-facing studio interface - into a single, unified codebase.

Current State:

  • Job Queue & Workers - Fully integrated in monorepo (apps/api, apps/worker, apps/monitor)
  • Machine Services - ComfyUI workers and connectors in monorepo (apps/machines)
  • User Interface - Separate repo at /Users/the_dusky/code/emprops/core-services/emprops-open-interface

Target State:

  • Complete Stack - All components in single monorepo for unified development, deployment, and versioning

Key Decision: Should we migrate emprops-open-interface into the monorepo as apps/emprops-studio or apps/ui?


Table of Contents

  1. Context
  2. Problem Statement
  3. Decision
  4. Technical Architecture
  5. Implementation Strategy
  6. Dependency Analysis
  7. Migration Challenges
  8. Consequences
  9. Alternatives Considered
  10. Related Documentation

Context

Current System Architecture

The EmProps platform consists of three main components currently split across repositories:

┌─────────────────────────────────────────────────────────────┐
│ MONOREPO: emerge-turbo (emp-job-queue)                      │
│                                                              │
│  apps/api          → Job broker API (Express + Redis)       │
│  apps/worker       → Job workers (ComfyUI connectors)       │
│  apps/monitor      → Real-time monitoring UI (Next.js)      │
│  apps/machines     → ComfyUI container deployments          │
│  packages/core     → Shared types & Redis functions         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ SEPARATE REPO: emprops-open-interface                       │
│                                                              │
│  Studio UI (Next.js 14) → User-facing creative studio       │
│  - Workflow builder (Studio V2)                             │
│  - Asset management                                          │
│  - NFT collection publishing                                 │
│  - User authentication & profiles                            │
│  - Credit/payment system                                     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Why They're Currently Separate

Historical reasons:

  1. Different teams/ownership (platform vs. UI)
  2. Different release cycles (backend stability vs. UI iteration)
  3. Different hosting platforms (Railway vs. Vercel)
  4. Different package managers (pnpm vs. yarn)

Why Integration Makes Sense Now

Technical benefits:

  1. Shared Types - Direct TypeScript type sharing for job definitions, API contracts
  2. Unified API Client - Studio UI can import @emp/core types directly
  3. Consistent Tooling - Single pnpm workspace, unified linting/formatting
  4. Atomic Commits - API + UI changes in single commit with type safety
  5. Simplified CI/CD - One pipeline, consistent deployment process

Developer Experience benefits:

  1. Single Repository - No context switching between repos
  2. Type Safety - Compile-time errors for API/UI mismatches
  3. Faster Iteration - Test full stack locally with single pnpm dev command
  4. Better Documentation - All docs in one place

Problem Statement

Current Pain Points

1. Type Duplication

typescript
// emprops-open-interface/types/job.ts (duplicated)
interface GenerationRequest {
  workflowId: string;
  parameters: Record<string, any>;
}

// packages/core/src/types/job.ts (source of truth)
export interface JobRequest {
  workflowId: string;
  params: Record<string, unknown>;
}

2. API Contract Sync Issues

  • UI makes assumptions about API responses
  • Breaking changes require coordinated PRs across repos
  • No compile-time validation of API/UI integration

3. Development Workflow Friction

  • Must run multiple repos simultaneously for full-stack development
  • Changes to job queue require manual type updates in UI repo
  • Testing requires coordinating versions across repositories

4. Deployment Complexity

  • Separate CI/CD pipelines
  • Versioning coordination challenges
  • Environment variable duplication

Decision

PROPOSED: Migrate emprops-open-interface into the monorepo as apps/emprops-studio

Key Technical Decisions

1. Directory Structure

apps/
  studio/          ← emprops-open-interface migrated here
    pages/
    components/
    hooks/
    clients/       ← Will use @emp/core types
    public/
    package.json   ← Updated dependencies

2. Package Management

  • Convert from Yarn → pnpm to match monorepo
  • Leverage workspace: packages for shared code
  • Preserve existing dependencies where possible

3. API Client Integration

typescript
// apps/emprops-studio/clients/job-api-client.ts
import { JobRequest, JobStatus } from '@emp/core';  // ← Direct import

export class JobApiClient {
  async submitJob(req: JobRequest): Promise<JobStatus> {
    // Type-safe API calls
  }
}

4. Build & Development

json
// apps/emprops-studio/package.json
{
  "scripts": {
    "dev": "next dev --port 3001",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "@emp/core": "workspace:*",  // ← Monorepo dependency
    "next": "^14.2.3",
    // ... existing dependencies
  }
}

5. Environment Configuration

  • Preserve existing .env structure for Studio
  • Consolidate shared env vars (API URLs, Redis connection)
  • Use monorepo env management patterns from other apps

Technical Architecture

emprops-open-interface Tech Stack (Current)

Framework & Core:

  • Next.js 14.2.3 - React framework with App Router support
  • TypeScript 5.0.4 - Type safety
  • React 18.3.1 - UI library

State Management:

  • Jotai 2.1.0 - Atomic state management
  • SWR 2.2.5 - Data fetching and caching

UI & Styling:

  • TailwindCSS 3.3.2 - Utility-first CSS
  • Radix UI - Headless component primitives
  • Framer Motion 11.3.28 - Animations
  • Lucide React 0.396.0 - Icons

Blockchain Integration:

  • Dynamic Labs - Web3 authentication
  • Ethers.js 6.7.1 - Ethereum interactions
  • Taquito - Tezos blockchain client
  • Viem 2.28.4 - Ethereum utilities

Custom Packages (CRITICAL):

  • @stakeordie/emprops-clients 0.1.6 - EmProps API clients
  • @stakeordie/emprops-ui 0.4.23 - Shared UI components
  • @stakeordie/sk8kit-ui-react 0.3.31 - UI component library

Analytics & Monitoring:

  • PostHog - Product analytics
  • Google Tag Manager - Marketing analytics
  • OpenTelemetry - Observability

Integration Points

1. API Connections

typescript
// Current: apps/emprops-studio will connect to apps/api
Environment Variables:
- NEXT_PUBLIC_OPEN_API_URL=http://localhost:3000  // apps/api
- NEXT_PUBLIC_PLATFORM_API_URL=https://api.emprops.ai/api

2. WebSocket Connections

typescript
// apps/emprops-studio will connect to apps/api WebSocket for real-time updates
- Job progress updates
- Machine status changes
- Generation completion events

3. Type Sharing

typescript
// apps/emprops-studio/clients/job-client.ts
import {
  JobRequest,
  JobStatus,
  WorkflowDefinition
} from '@emp/core';

// Direct type safety with job queue

Implementation Strategy

Phase 1: Pre-Migration Setup ✅

1.1 Copy codebase to analysis directory

bash
mkdir -p .refcode
cp -r /path/to/emprops-open-interface .refcode/

1.2 Analyze dependencies

  • Identify conflicts with existing monorepo packages
  • Map custom packages (@stakeordie/*)
  • Document environment variables

1.3 Create ADR (this document)

  • Technical architecture analysis
  • Migration strategy
  • Risk assessment

Phase 2: Dependency Resolution 🔄

2.1 Convert package manager

bash
# Remove Yarn artifacts
rm yarn.lock
rm -rf node_modules

# Initialize pnpm
pnpm install

2.2 Update package.json

json
{
  "name": "@emp/emprops-studio",
  "dependencies": {
    "@emp/core": "workspace:*",
    // Keep existing Next.js dependencies
    // Keep @stakeordie/* packages (external)
  }
}

2.3 Resolve dependency conflicts

  • Check TypeScript version compatibility (core uses 5.x, studio uses 5.0.4) ✅
  • Verify React version consistency (18.3.1) ✅
  • Test build with monorepo structure

Phase 3: Code Migration 🔄

3.1 Create apps/emprops-studio directory

bash
mkdir -p apps/emprops-studio

3.2 Move source code

bash
# Move all source directories
cp -r .refcode/emprops-open-interface/{pages,components,hooks,lib,public,styles,utils} apps/emprops-studio/

# Move configuration files
cp .refcode/emprops-open-interface/{next.config.js,tailwind.config.js,tsconfig.json} apps/emprops-studio/

3.3 Update import paths

typescript
// OLD: import { Something } from '@/clients/emprops-api-client'
// NEW: import { JobRequest } from '@emp/core'

// Search and replace for core type imports

3.4 Configure TypeScript

json
// apps/emprops-studio/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "paths": {
      "@/*": ["./"],
      "@emp/core": ["../../packages/core/src"]
    }
  }
}

Phase 4: Environment Configuration 🔄

4.1 Consolidate environment variables

bash
# apps/emprops-studio/.env.local
NEXT_PUBLIC_OPEN_API_URL=http://localhost:3000  # Local apps/api
NEXT_PUBLIC_CDN_URL=https://cdn.emprops.ai
# ... preserve existing Studio env vars

4.2 Update API client connections

typescript
// apps/emprops-studio/clients/job-api-client.ts
const apiUrl = process.env.NEXT_PUBLIC_OPEN_API_URL || 'http://localhost:3000';

Phase 5: Testing & Validation 🔄

5.1 Local development

bash
# Terminal 1: Start API
pnpm dev:api

# Terminal 2: Start Studio
pnpm dev:studio

# Verify: Full-stack workflow works

5.2 Type safety validation

bash
# Verify TypeScript compilation
pnpm typecheck

# Verify no type errors in Studio
cd apps/emprops-studio && pnpm typecheck

5.3 Build validation

bash
# Verify production build
pnpm build

# Verify Studio builds successfully
cd apps/emprops-studio && pnpm build

Phase 6: CI/CD Integration 🔄

6.1 Update GitHub Actions

yaml
# .github/workflows/ci.yml
jobs:
  build-and-test:
    steps:
      - name: Build Studio
        run: pnpm build --filter=@emp/emprops-studio

6.2 Update deployment scripts

bash
# scripts/deploy-studio.sh
pnpm build --filter=@emp/emprops-studio
# Deploy to Vercel or Railway

Dependency Analysis

Critical External Dependencies

Custom Packages (Must Preserve):

json
{
  "@stakeordie/emprops-clients": "^0.1.6",   // EmProps API clients
  "@stakeordie/emprops-ui": "^0.4.23",       // Shared UI components
  "@stakeordie/sk8kit-ui-react": "0.3.31"    // UI component library
}

These are external packages NOT in monorepo - must remain as npm dependencies

Monorepo Package Integration

New Dependencies (From Monorepo):

json
{
  "@emp/core": "workspace:*"  // Job types, Redis functions, shared utilities
}

Potential Conflicts

1. TypeScript Version

  • Monorepo: TypeScript 5.x
  • Studio: TypeScript 5.0.4
  • Resolution: ✅ Compatible - no conflict

2. React Version

  • Monorepo: React 18.3.1
  • Studio: React 18.3.1
  • Resolution: ✅ Exact match - no conflict

3. Next.js Version

  • Monitor app: Next.js 15.x
  • Studio: Next.js 14.2.3
  • Resolution: ⚠️ Different versions - Studio stays on 14.x until ready to upgrade

4. Package Manager

  • Monorepo: pnpm (workspace protocol)
  • Studio: yarn (currently)
  • Resolution: 🔄 Convert Studio to pnpm

Migration Challenges

1. Custom Package Compatibility

Challenge: Studio depends on @stakeordie/* packages not in monorepo

Solution:

  • Keep as external npm dependencies
  • Document version requirements
  • Consider future migration to monorepo if source available

2. API Client Type Mismatches

Challenge: Studio API client types may not match @emp/core

Solution:

typescript
// Create adapter layer if needed
// apps/emprops-studio/clients/api-adapter.ts
import { JobRequest as CoreJobRequest } from '@emp/core';
import { GenerationRequest as StudioRequest } from './types';

export function toJobRequest(req: StudioRequest): CoreJobRequest {
  return {
    workflowId: req.workflowId,
    params: req.parameters,
    // ... type mapping
  };
}

3. Environment Variable Management

Challenge: Studio has complex env configuration (.env, .env.local.dev, .env.staging, .env.prod)

Solution:

  • Preserve Studio's env structure
  • Use monorepo's encrypted env pattern for secrets
  • Document env vars in CRITICAL_ENV_VARS.md

4. Build Process Differences

Challenge: Studio has prebuild step (scripts/fetchContent.js)

Solution:

json
// apps/emprops-studio/package.json
{
  "scripts": {
    "prebuild": "node ./scripts/fetchContent.js || true",
    "build": "next build"
  }
}

5. Asset/CDN Management

Challenge: Studio expects CDN for static assets

Solution:

  • Preserve NEXT_PUBLIC_CDN_URL environment variable
  • Keep existing asset structure
  • Document CDN requirements

Consequences

Benefits ✅

1. Type Safety

  • Compile-time validation of API/UI integration
  • No more type duplication/drift
  • Refactoring across stack with confidence

2. Developer Experience

  • Single git clone for full stack
  • Unified development commands
  • Consistent tooling (ESLint, Prettier, TypeScript)

3. Deployment Simplification

  • Single CI/CD pipeline
  • Coordinated releases
  • Consistent versioning

4. Code Reuse

  • Share utilities between apps
  • Common UI components
  • Unified error handling patterns

5. Documentation

  • All docs in one place
  • Clear architecture visibility
  • Easier onboarding for new developers

Drawbacks ⚠️

1. Larger Repository

  • More code to clone/maintain
  • Longer CI/CD times (mitigated by Turborepo caching)

2. Migration Effort

  • Initial setup time
  • Risk of breaking existing Studio functionality
  • Need comprehensive testing

3. Deployment Coupling

  • Can't deploy Studio independently (actually can with Turbo filters)
  • Version coordination required

4. Package Manager Change

  • Convert from Yarn → pnpm
  • Team must learn pnpm workspace patterns

Alternatives Considered

Alternative 1: Keep Separate Repositories

Approach: Maintain current split architecture

Pros:

  • No migration effort
  • Independent deployment cycles
  • Smaller repository size

Cons:

  • Type duplication continues
  • API/UI integration errors at runtime only
  • Development workflow friction
  • Deployment coordination complexity

Decision: ❌ Rejected - Benefits of integration outweigh migration cost

Alternative 2: Git Submodule

Approach: Add emprops-open-interface as git submodule

Pros:

  • Keep separate git history
  • Can still have independent releases

Cons:

  • Submodules are notoriously difficult to manage
  • Still can't share types directly
  • Developer experience worse than monorepo
  • Deployment still complex

Decision: ❌ Rejected - Submodules add complexity without benefits

Alternative 3: Separate npm Package

Approach: Publish Studio as npm package, import in monorepo

Pros:

  • Studio stays independent
  • Clear versioning

Cons:

  • Can't share types (npm package boundary)
  • Additional build/publish step
  • Defeats purpose of monorepo

Decision: ❌ Rejected - Doesn't solve type sharing problem


Monorepo Documentation

EmProps Open Interface Documentation

  • .refcode/emprops-open-interface/CLAUDE.md - UI architecture guide
  • .refcode/emprops-open-interface/STUDIO_V2_REFACTOR_PLAN.md - Studio V2 architecture
  • None yet - this is the first UI integration ADR

Next Steps

  1. Review & Approval - Architecture team review this ADR
  2. Create Implementation Plan - Detailed task breakdown with estimates
  3. Proof of Concept - Test migration with minimal subset of Studio
  4. Full Migration - Execute phases 2-6
  5. Documentation Update - Update CLAUDE.md with Studio development patterns
  6. Team Training - Onboard team on monorepo Studio development

Decision Log

DateDecisionRationale
2025-10-18Proposed integration as apps/emprops-studioFull-stack type safety, unified development
TBDApproved/RejectedAfter architecture review

Released under the MIT License.