* feat(post-application): add schema and shared types for provider ingestion (#136) * test(orchestrator): ensure full localStorage shape in vitest setup * feat(post-application): add provider registry and dispatcher framework (#137) (#146) * Implement Gmail provider credential persistence (#147) * Add unified post-application provider action API (#148) * Implement Gmail ingestion sync with 95/60 relevance policy * Implement Gmail ingestion sync with 95/60 relevance policy (#149) * feat(post-application): add job mapping engine with llm rerank fallback * feat(post-application): add inbox review APIs with transactional approve/deny (#151) * feat(post-application): add tracking inbox UI with provider controls (#152) * oauth implementation * UI changes * see past runs in more detail * occurred at comes from email * state mismatch * better UI representation * comments * comments * comments * comments * documentation * explainer * set things manually * scrolling * any found email can be pending * searchable download * Email-to-Job Matching Decision Tree * email viewer list improvement * simplification initial commit * exclude discovered jobs * show only resady * dropdown * mermaid * syntax * targets is the same as logging that is done manually * event label * duplicate avoidance * clean up html * token saving * print * send idx not uuid * remove logging * formatting * better documentation * documentation * comments * process all * comments
5.2 KiB
Orchestrator: Job States and PDF Flow
This doc explains how the orchestrator thinks about job states, how the "Ready" flow is supposed to work, and how to generate or regenerate PDFs after edits.
Job states (what each one means)
discovered: The job was found by a crawler/import. It has not been processed into a tailored resume yet.processing: The system is currently generating tailoring data and/or the PDF.ready: A tailored PDF has been generated and the job is ready for you to apply.applied: You marked it as applied.skipped: You explicitly skipped it (so it stays out of your active queue).expired: Deadline has passed. This is a terminal state used for cleanup/triage.
The intended "Ready" flow
There are two main ways a job becomes Ready:
-
Manual flow (most common)
- A job starts in
discovered. - You open it in the Discovered panel, decide to Tailor.
- In Tailor mode you can edit job description (optional), tailored summary, tailored headline, tailored skills, and project picks.
- You click Finalize & Move to Ready.
- This runs summarization (if needed), generates the PDF, and sets status to
ready.
- A job starts in
-
Auto flow (pipeline top picks)
- The pipeline scores all discovered jobs.
- It auto-processes the top N above the score threshold.
- Those jobs go directly to
readywith PDFs generated.
Once a job is ready, the Ready panel is the "shipping lane":
- View/download the PDF.
- Open the job listing.
- Mark Applied (moves to
applied). - Optional: edit tailoring, edit the JD, or regenerate the PDF.
Generating PDFs (first time)
The PDF is generated from:
- The base resume selected from your v4.rxresu.me account (via Onboarding or Settings).
- The job description (used for AI tailoring and project selection).
- Your tailored summary, tailored headline, tailored skills, and selected projects.
Paths:
-
Discovered ? Tailor ? Finalize
- Calls
/api/jobs/:id/process. - Runs AI summary + project selection, then generates the PDF.
- Sets status to
readyand savespdfPath.
- Calls
-
Ready panel ? Regenerate PDF
- Calls
/api/jobs/:id/generate-pdfusing the current saved tailoring fields.
- Calls
Regenerating PDFs after edits
If the job description or tailoring changes, regenerate the PDF so it stays in sync.
Typical UI flow
- Edit job description or tailoring in the Discovered/Tailor view, or use ?Edit job description? in Ready.
- If you want AI to re-tailor based on the updated JD, click Generate draft (Discovered) or AI Summarize (editor).
- Click Finalize & Move to Ready (if still in Discovered) or Regenerate PDF (if already Ready).
API flow (for automation)
- Update the data:
PATCH /api/jobs/:id
{
"jobDescription": "<new JD>",
"tailoredSummary": "<optional>",
"tailoredHeadline": "<optional>",
"tailoredSkills": "[{\"name\":\"Backend\",\"keywords\":[\"TypeScript\",\"Node.js\"]}]",
"selectedProjectIds": "p1,p2"
}
- (Optional) re-run AI tailoring based on the new JD:
POST /api/jobs/:id/summarize?force=true
- Generate the PDF using current stored fields:
POST /api/jobs/:id/generate-pdf
Post-Application Tracking (Tracking Inbox)
After you've applied to jobs, the Tracking Inbox feature automatically monitors your Gmail for responses:
How it works
-
Gmail Sync: Periodically checks your Gmail for recruitment-related emails
-
Smart Router AI: Analyzes each email for:
- Relevance (is it about job applications?)
- Job matching (which applied job is this about?)
- Message type (interview, offer, rejection, update)
- Confidence score (0-100%)
-
Automatic Processing:
- 95-100% confidence: Auto-linked to the matched job, timeline updated
- 50-94% confidence: Goes to Inbox for review with suggested match
- <50% confidence: Goes to Inbox as "orphan" if relevant; ignored if not
-
User Review: Items in the Inbox wait for your approve/ignore decision
Gmail Setup
- Configure Gmail OAuth credentials (see
self-hosting.md) - In the UI: Tracking Inbox → Connect Gmail
- Authorize read-only Gmail access
Notes and gotchas
processingis transient. If PDF generation fails, the job is reverted back todiscovered.- The PDF is served at
/pdfs/resume_<jobId>.pdfand cache-busted with the job?supdatedAttimestamp. - If a job is
skippedorappliedand you want to re-open it, you can PATCH itsstatusback todiscovered. - Job text search is handled through the command bar (
Cmd/Ctrl+K) and is not persisted as a URL filter.
External payload and sanitization defaults
- LLM providers receive only prompt inputs required for scoring/tailoring/project selection/manual extraction tasks.
- By default, prompt construction uses minimized profile/job fields and avoids sending unnecessary sensitive data.
- Webhook payloads are sanitized and whitelisted by default; large/sensitive blobs are not sent.
- Server logs and error details are redacted/truncated by default (secrets, tokens, cookies, passwords, API keys, and oversized payload fields).
- Correlation data is included in logs (
requestId, and when availablepipelineRunId/jobId) to improve traceability without exposing raw payloads.