Jobber/orchestrator
Shaheer Sarfaraz 05f1c62de2
Auto save project selection bug (#134)
* fix(tailoring): remove auto-sync effect causing race conditions

Remove the problematic useEffect that was syncing incoming job data
automatically. The effect caused race conditions where user edits were
overwritten after auto-save completed. Now, state only resets when the
job ID changes (user switches to a different job). User edits persist
until explicitly saved.

Fixes #133

* fix(tailoring): remove auto-save from tailor mode

Remove the 1500ms auto-save timeout that was causing race conditions
with the state sync. Users must now explicitly save changes via the
Save Selection button or finalize to persist changes.

* refactor(tailoring): remove draft status state and UI

Remove the draftStatus state and related UI elements that showed
saving/saved/unsaved status. With auto-save removed, this status
indicator is no longer needed. Users now explicitly save via buttons.

* test(tailoring): remove auto-save test

Remove the test that verified auto-save behavior since auto-save
has been removed from the tailor mode. Users now explicitly save
via the Finalize button.

* refactor(tailoring): remove dead focus tracking code

Remove the activeField state and all related focus/blur tracking that
was orphaned after removing auto-sync. The focus tracking was only
used to prevent the auto-sync effect from running while editing.

Changes:
- Remove TailoringActiveField type export
- Remove activeField state and setActiveField from useTailoringDraft
- Remove handleFieldBlur callback from TailoringWorkspace
- Remove onFieldFocus/onFieldBlur props and handlers from TailoringSections

39 lines of dead code removed.

* docs(tailoring): clarify save behavior comment

Update comment to distinguish between editor mode (Save Selection
button) and tailor mode (only persists on finalize). Addresses
review feedback.

* docs(tailoring): clarify useEffect dependencies

Add note explaining why 'job' is included in dependencies despite
the effect being guarded by job.id check. Addresses review feedback
about dependency array clarity.

* fix(tailoring): sync server-normalized values after save

Update persistCurrent and saveChanges to use the returned job from
api.updateJob and call applyIncomingDraft. This ensures local state
stays in sync with server-normalized values (e.g., trimmed fields).

Also removes unused markCurrentAsSaved dependency.

* refactor(tailoring): simplify draft sync effect

Remove unused save snapshot helpers and stop exposing them from the
hook. Track the latest job in a ref and only sync drafts when the job
id changes to avoid unnecessary effect runs while keeping data
correctness.

Addresses review feedback on dependency churn and dead API surface.
2026-02-11 16:18:17 +00:00
..
2026-01-15 19:22:22 +00:00
2025-12-11 22:31:59 +00:00
2025-12-14 16:48:07 +00:00
2026-01-16 15:40:12 +00:00
2026-02-10 22:13:05 +00:00
2025-12-14 16:48:07 +00:00
2026-02-05 19:17:14 +00:00

Job Ops Orchestrator

A unified orchestrator for the job application pipeline. Discovers jobs, scores them for suitability, generates tailored resumes, and provides a UI to manage applications.

Architecture

orchestrator/
├── src/
│   ├── server/           # Express backend
│   │   ├── api/          # REST API routes
│   │   ├── db/           # SQLite + Drizzle ORM
│   │   ├── pipeline/     # Orchestration logic
│   │   ├── repositories/ # Data access layer
│   │   └── services/     # Integrations (crawler, AI, PDF)
│   ├── client/           # React frontend
│   │   ├── api/          # API client
│   │   ├── components/   # UI components
│   │   └── styles/       # CSS design system
│   └── shared/           # Shared types
├── data/                 # SQLite DB + generated PDFs (gitignored)
└── public/               # Static assets

Setup

  1. Install dependencies:

    cd orchestrator
    npm install
    
  2. Set up environment:

    cp .env.example .env
    # The app is self-configuring. You can add keys via the UI Onboarding.
    

    After the server starts, use the onboarding modal to connect OpenRouter, link your v4.rxresu.me account, and select a template resume.

    OpenRouter is the default LLM provider, but LM Studio, Ollama, OpenAI, and Gemini are also supported.

    Use LLM_API_KEY / llmApiKey to configure providers that require an API key.

  3. Initialize database:

    npm run db:migrate
    
  4. Start development server:

    npm run dev
    

    This starts:

    • Backend API at http://localhost:3001
    • Frontend at http://localhost:5173

API Endpoints

Jobs

Method Endpoint Description
GET /api/jobs List all jobs (filter with ?status=ready,discovered)
GET /api/jobs/:id Get single job
PATCH /api/jobs/:id Update job
POST /api/jobs/:id/process Generate resume for job
POST /api/jobs/:id/apply Mark as applied
POST /api/jobs/:id/skip Mark as skipped

Pipeline

Method Endpoint Description
GET /api/pipeline/status Get pipeline status
GET /api/pipeline/runs Get recent pipeline runs
POST /api/pipeline/run Trigger pipeline manually
POST /api/webhook/trigger Webhook for n8n (use WEBHOOK_SECRET)

Daily Flow

  1. 17:00 - n8n triggers pipeline:

    • Calls POST /api/webhook/trigger
    • Pipeline crawls Gradcracker
    • Scores jobs with AI
    • Generates tailored resumes for top 10
  2. You review in the UI:

    • See jobs at http://localhost:5173
    • "Ready" tab shows jobs with generated PDFs
    • Use command bar search (Cmd/Ctrl+K) to quickly find and open jobs
    • Click "View Job" to open application
    • Download PDF and apply manually
    • Click "Mark Applied" to mark application status

n8n Setup

Create a workflow with:

  1. Schedule Trigger - Every day at 17:00
  2. HTTP Request:
    • Method: POST
    • URL: http://localhost:3001/api/webhook/trigger
    • Headers: Authorization: Bearer YOUR_WEBHOOK_SECRET

Development

# Run just the server
npm run dev:server

# Run just the client
npm run dev:client

# Run the pipeline manually
npm run pipeline:run

# Build for production
npm run build
npm start

Tech Stack

  • Backend: Express, TypeScript, Drizzle ORM, SQLite
  • Frontend: React, Vite, CSS (custom design system)
  • AI: Configurable LLM provider (OpenRouter default; also supports OpenAI/Gemini/LM Studio/Ollama)
  • PDF Generation: RxResume v4 API export (configured via Settings)
  • Job Crawling: Wraps existing TypeScript Crawlee crawler