# JobForge MVP - Core Job Application Module **Version:** 1.0.0 MVP **Status:** Development Phase 1 **Date:** July 2025 **Scope:** Core job application workflow with essential features **Target:** Personal use for concept validation and testing --- ## 📋 MVP Scope & Objectives ### Core Functionality - **User Authentication**: Basic login/signup system - **Job Application Creation**: Add new applications with job description and URL - **3-Phase AI Workflow**: Research → Resume → Cover Letter generation - **Document Management**: View and edit generated documents - **Navigation Interface**: Sidebar + top bar for seamless workflow navigation ### MVP Goals - Validate core AI workflow effectiveness - Test user experience with Dash + Mantine interface - Prove concept with personal job application journey - Establish foundation for Phase 2 (post-application features) --- ## 🏗️ MVP Architecture ### System Overview ```mermaid graph TB subgraph "Frontend (Dash + Mantine)" UI[Main UI] SIDEBAR[Application Sidebar] TOPBAR[Navigation Top Bar] EDITOR[Document Editor] end subgraph "Backend API (FastAPI)" AUTH[Authentication] APP[Application Service] AI[AI Orchestrator] DOC[Document Service] end subgraph "AI Agents" RESEARCH[Research Agent] RESUME[Resume Optimizer] COVER[Cover Letter Generator] end subgraph "Data Storage" PG[(PostgreSQL + pgvector)] FILES[Document Storage] end subgraph "External AI" CLAUDE[Claude AI] OPENAI[OpenAI Embeddings] end UI --> AUTH UI --> APP UI --> DOC APP --> AI AI --> RESEARCH AI --> RESUME AI --> COVER APP --> PG DOC --> FILES RESEARCH --> CLAUDE RESUME --> CLAUDE COVER --> CLAUDE AI --> OPENAI ``` --- ## 🔐 User Authentication (MVP) ### Simple Authentication System ```python class AuthenticationService: """Basic user authentication for MVP""" async def register_user(self, email: str, password: str, name: str) -> User: """Register new user account""" async def authenticate_user(self, email: str, password: str) -> AuthResult: """Login user and return JWT token""" async def verify_token(self, token: str) -> User: """Verify JWT token and return user""" async def logout_user(self, user_id: str) -> None: """Logout user session""" ``` ### Database Schema (Users) ```sql -- Basic user table for MVP CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, full_name VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- Enable basic row level security ALTER TABLE users ENABLE ROW LEVEL SECURITY; ``` --- ## 📋 Job Application Module ### Core Application Workflow ```python class ApplicationService: """Core job application management""" async def create_application(self, user_id: str, job_data: JobApplicationData) -> Application: """Create new job application with job description and URL""" async def get_user_applications(self, user_id: str) -> List[Application]: """Get all applications for user""" async def get_application(self, user_id: str, app_id: str) -> Application: """Get specific application with documents""" async def update_application_status(self, user_id: str, app_id: str, status: str) -> None: """Update application status through workflow phases""" ``` ### Application Data Model ```python class JobApplicationData(BaseModel): """Input data for creating new application""" job_url: Optional[str] = None job_description: str company_name: str role_title: str location: Optional[str] = None priority_level: str = "medium" additional_context: Optional[str] = None class Application(BaseModel): """Core application entity""" id: str user_id: str name: str # Auto-generated: company_role_YYYY_MM_DD company_name: str role_title: str job_url: Optional[str] job_description: str status: ApplicationStatus # draft, research_complete, resume_ready, cover_letter_ready # Phase completion tracking research_completed: bool = False resume_optimized: bool = False cover_letter_generated: bool = False created_at: datetime updated_at: datetime ``` ### Database Schema (Applications) ```sql CREATE TABLE applications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, company_name VARCHAR(255) NOT NULL, role_title VARCHAR(255) NOT NULL, job_url TEXT, job_description TEXT NOT NULL, location VARCHAR(255), priority_level VARCHAR(20) DEFAULT 'medium', status VARCHAR(50) DEFAULT 'draft', -- Phase tracking research_completed BOOLEAN DEFAULT FALSE, resume_optimized BOOLEAN DEFAULT FALSE, cover_letter_generated BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ALTER TABLE applications ENABLE ROW LEVEL SECURITY; CREATE POLICY user_applications_policy ON applications FOR ALL TO application_user USING (user_id = current_setting('app.current_user_id')::UUID); ``` --- ## 🤖 AI Processing Workflow ### 3-Phase AI Orchestrator ```python class AIOrchestrator: """Orchestrates the 3-phase AI workflow""" def __init__(self, research_agent, resume_optimizer, cover_letter_generator): self.research_agent = research_agent self.resume_optimizer = resume_optimizer self.cover_letter_generator = cover_letter_generator async def execute_research_phase(self, application_id: str) -> ResearchReport: """Phase 1: Job analysis and company research""" async def execute_resume_optimization(self, application_id: str) -> OptimizedResume: """Phase 2: Resume optimization based on research""" async def execute_cover_letter_generation(self, application_id: str, user_context: str) -> CoverLetter: """Phase 3: Cover letter generation with user inputs""" ``` ### Phase 1: Research Agent ```python class ResearchAgent: """Job description analysis and company research""" async def analyze_job_description(self, job_desc: str) -> JobAnalysis: """Extract requirements, skills, and key information""" async def research_company_info(self, company_name: str) -> CompanyIntelligence: """Basic company research and insights""" async def generate_strategic_positioning(self, job_analysis: JobAnalysis) -> StrategicPositioning: """Determine optimal candidate positioning""" async def create_research_report(self, job_desc: str, company_name: str) -> ResearchReport: """Complete research phase output""" ``` ### Phase 2: Resume Optimizer ```python class ResumeOptimizer: """Resume optimization based on job requirements""" async def analyze_resume_portfolio(self, user_id: str) -> ResumePortfolio: """Load and analyze user's resume library""" async def optimize_resume_for_job(self, portfolio: ResumePortfolio, research: ResearchReport) -> OptimizedResume: """Create job-specific optimized resume""" async def validate_resume_optimization(self, resume: OptimizedResume) -> ValidationReport: """Ensure resume meets requirements and constraints""" ``` ### Phase 3: Cover Letter Generator ```python class CoverLetterGenerator: """Cover letter generation with user context""" async def analyze_writing_style(self, user_id: str) -> WritingStyle: """Analyze user's writing patterns from reference documents""" async def generate_cover_letter(self, research: ResearchReport, resume: OptimizedResume, user_context: str, writing_style: WritingStyle) -> CoverLetter: """Generate personalized cover letter""" async def validate_cover_letter(self, cover_letter: CoverLetter) -> ValidationReport: """Ensure cover letter quality and authenticity""" ``` --- ## 📄 Document Management ### Document Storage & Retrieval ```python class DocumentService: """Handle document storage and retrieval""" async def save_document(self, user_id: str, app_id: str, doc_type: str, content: str) -> None: """Save generated document (research, resume, cover letter)""" async def get_document(self, user_id: str, app_id: str, doc_type: str) -> Document: """Retrieve document for viewing/editing""" async def update_document(self, user_id: str, app_id: str, doc_type: str, content: str) -> None: """Update document after user editing""" async def get_all_documents(self, user_id: str, app_id: str) -> ApplicationDocuments: """Get all documents for an application""" ``` ### Document Models ```python class Document(BaseModel): """Base document model""" id: str application_id: str document_type: str # research_report, optimized_resume, cover_letter content: str created_at: datetime updated_at: datetime class ApplicationDocuments(BaseModel): """All documents for an application""" research_report: Optional[Document] = None optimized_resume: Optional[Document] = None cover_letter: Optional[Document] = None ``` ### Database Schema (Documents) ```sql CREATE TABLE documents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), application_id UUID REFERENCES applications(id) ON DELETE CASCADE, document_type VARCHAR(50) NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ALTER TABLE documents ENABLE ROW LEVEL SECURITY; CREATE POLICY user_documents_policy ON documents FOR ALL TO application_user USING ( application_id IN ( SELECT id FROM applications WHERE user_id = current_setting('app.current_user_id')::UUID ) ); ``` --- ## 🎨 Frontend Interface (Dash + Mantine) ### Main Application Layout ```python class JobForgeApp: """Main Dash application layout""" def create_layout(self): return dmc.MantineProvider([ dmc.AppShell([ dmc.Navbar([ ApplicationSidebar() ], width={"base": 300}), dmc.Main([ ApplicationTopBar(), MainContent() ]) ]) ]) ``` ### Application Sidebar ```python class ApplicationSidebar: """Sidebar with applications list and navigation""" def render(self, user_id: str): return dmc.Stack([ # New Application Button dmc.Button( "➕ New Application", id="new-app-btn", fullWidth=True, variant="filled" ), # Applications List dmc.Title("Applications", order=4), dmc.ScrollArea([ ApplicationCard(app) for app in self.get_user_applications(user_id) ]), # Resume Library Section dmc.Divider(), dmc.Title("Resume Library", order=4), ResumeLibrarySection() ]) class ApplicationCard: """Individual application card in sidebar""" def render(self, application: Application): return dmc.Card([ dmc.Group([ dmc.Text(application.company_name, weight=600), StatusBadge(application.status) ]), dmc.Text(application.role_title, size="sm", color="dimmed"), dmc.Text(application.created_at.strftime("%Y-%m-%d"), size="xs") ], id=f"app-card-{application.id}") ``` ### Application Top Bar Navigation ```python class ApplicationTopBar: """Top navigation bar for application phases""" def render(self, application: Application): return dmc.Group([ # Phase Navigation Buttons PhaseButton("Research", "research", application.research_completed), PhaseButton("Resume", "resume", application.resume_optimized), PhaseButton("Cover Letter", "cover_letter", application.cover_letter_generated), # Application Actions dmc.Spacer(), dmc.ActionIcon( DashIconify(icon="tabler:settings"), id="app-settings-btn" ) ]) class PhaseButton: """Navigation button for each phase""" def render(self, label: str, phase: str, completed: bool): icon = "tabler:check" if completed else "tabler:clock" color = "green" if completed else "gray" return dmc.Button([ DashIconify(icon=icon), dmc.Text(label, ml="xs") ], variant="subtle" if not completed else "filled", color=color, id=f"phase-{phase}-btn" ) ``` ### Document Editor Interface ```python class DocumentEditor: """Markdown document editor with preview""" def render(self, document: Document): return dmc.Container([ dmc.Grid([ # Editor Column dmc.Col([ dmc.Title(f"Edit {document.document_type.replace('_', ' ').title()}", order=3), dmc.Textarea( value=document.content, placeholder="Document content...", minRows=20, autosize=True, id=f"editor-{document.document_type}" ), dmc.Group([ dmc.Button("Save Changes", id="save-btn"), dmc.Button("Cancel", variant="outline", id="cancel-btn") ]) ], span=6), # Preview Column dmc.Col([ dmc.Title("Preview", order=3), dmc.Container([ dcc.Markdown(document.content, id="preview-content") ], style={"border": "1px solid #e0e0e0", "padding": "1rem", "minHeight": "500px"}) ], span=6) ]) ]) ``` --- ## 🗄️ MVP Database Schema ### Complete Database Setup ```sql -- Enable required extensions CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS vector; -- Users table CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, full_name VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- Applications table CREATE TABLE applications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, company_name VARCHAR(255) NOT NULL, role_title VARCHAR(255) NOT NULL, job_url TEXT, job_description TEXT NOT NULL, location VARCHAR(255), priority_level VARCHAR(20) DEFAULT 'medium', status VARCHAR(50) DEFAULT 'draft', research_completed BOOLEAN DEFAULT FALSE, resume_optimized BOOLEAN DEFAULT FALSE, cover_letter_generated BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- Documents table CREATE TABLE documents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), application_id UUID REFERENCES applications(id) ON DELETE CASCADE, document_type VARCHAR(50) NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), UNIQUE(application_id, document_type) ); -- Resume library table CREATE TABLE user_resumes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, content TEXT NOT NULL, focus_area VARCHAR(100), is_primary BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- Basic vector embeddings (for future enhancement) CREATE TABLE document_embeddings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), document_id UUID REFERENCES documents(id) ON DELETE CASCADE, embedding vector(1536), created_at TIMESTAMP DEFAULT NOW() ); -- Row Level Security ALTER TABLE users ENABLE ROW LEVEL SECURITY; ALTER TABLE applications ENABLE ROW LEVEL SECURITY; ALTER TABLE documents ENABLE ROW LEVEL SECURITY; ALTER TABLE user_resumes ENABLE ROW LEVEL SECURITY; -- Security policies CREATE POLICY user_own_data ON applications FOR ALL USING (user_id = current_setting('app.current_user_id')::UUID); CREATE POLICY user_own_documents ON documents FOR ALL USING ( application_id IN (SELECT id FROM applications WHERE user_id = current_setting('app.current_user_id')::UUID) ); CREATE POLICY user_own_resumes ON user_resumes FOR ALL USING (user_id = current_setting('app.current_user_id')::UUID); ``` --- ## 🚀 MVP Development Plan ### Development Phases #### **Week 1-2: Foundation Setup** - Docker development environment - PostgreSQL database with basic schema - FastAPI backend with authentication endpoints - Basic Dash + Mantine frontend structure #### **Week 3-4: Core Application Module** - Application creation and listing - Database integration with user isolation - Basic sidebar and navigation UI - Application status tracking #### **Week 5-6: AI Workflow Implementation** - Research Agent with Claude integration - Resume Optimizer with portfolio handling - Cover Letter Generator with user context - Document storage and retrieval system #### **Week 7-8: Frontend Polish & Integration** - Document editor with markdown support - Real-time status updates during AI processing - Phase navigation and progress tracking - Error handling and user feedback ### MVP Success Criteria - ✅ User can register/login securely - ✅ User can create job applications with description/URL - ✅ AI generates research report automatically - ✅ AI optimizes resume based on job requirements - ✅ AI generates cover letter with user context - ✅ User can view and edit all generated documents - ✅ Smooth navigation between application phases - ✅ Data persisted securely with user isolation --- ## 🐳 Docker Development Setup ### Development Environment ```yaml # docker-compose.yml version: '3.8' services: postgres: image: pgvector/pgvector:pg16 environment: POSTGRES_DB: jobforge_mvp POSTGRES_USER: jobforge_user POSTGRES_PASSWORD: jobforge_password ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql backend: build: context: . dockerfile: Dockerfile.backend ports: - "8000:8000" environment: - DATABASE_URL=postgresql+asyncpg://jobforge_user:jobforge_password@postgres:5432/jobforge_mvp - CLAUDE_API_KEY=${CLAUDE_API_KEY} - OPENAI_API_KEY=${OPENAI_API_KEY} volumes: - ./src:/app/src depends_on: - postgres command: uvicorn src.backend.main:app --host 0.0.0.0 --port 8000 --reload frontend: build: context: . dockerfile: Dockerfile.frontend ports: - "8501:8501" environment: - BACKEND_URL=http://backend:8000 volumes: - ./src/frontend:/app/src/frontend depends_on: - backend command: python src/frontend/main.py volumes: postgres_data: ``` --- ## 📁 MVP Project Structure ``` jobforge-mvp/ ├── docker-compose.yml ├── Dockerfile.backend ├── Dockerfile.frontend ├── requirements-backend.txt ├── requirements-frontend.txt ├── .env.example ├── database/ │ └── init.sql ├── src/ │ ├── backend/ │ │ ├── main.py │ │ ├── api/ │ │ │ ├── auth.py │ │ │ ├── applications.py │ │ │ ├── documents.py │ │ │ └── processing.py │ │ ├── services/ │ │ │ ├── auth_service.py │ │ │ ├── application_service.py │ │ │ ├── document_service.py │ │ │ └── ai_orchestrator.py │ │ ├── database/ │ │ │ ├── connection.py │ │ │ └── models.py │ │ └── models/ │ │ ├── requests.py │ │ └── responses.py │ ├── frontend/ │ │ ├── main.py │ │ ├── components/ │ │ │ ├── sidebar.py │ │ │ ├── topbar.py │ │ │ └── editor.py │ │ ├── pages/ │ │ │ ├── login.py │ │ │ ├── dashboard.py │ │ │ └── application.py │ │ └── api_client/ │ │ └── client.py │ ├── agents/ │ │ ├── research_agent.py │ │ ├── resume_optimizer.py │ │ ├── cover_letter_generator.py │ │ └── claude_client.py │ └── helpers/ │ ├── validators.py │ └── formatters.py └── user_data/ └── resumes/ ``` --- *This MVP architecture focuses on delivering the core job application workflow with essential features. It establishes the foundation for Phase 2 development while providing immediate value for personal job application management and concept validation.*