Job Intelligence is my local-first workflow for finding, reviewing, and preparing job applications. It collects vacancies from public sources, stores them in a readable registry, checks each role against my candidate evidence, and prepares vacancy-specific application packages through Codex tasks.
This README is a review of how my flow works. It is not a generic project README written to market a product. I use it to show the process in the open: what code does, where Codex helps, where I keep human judgment, and how reviewers can audit the result.
The full technical README has been preserved in
TECHNICAL_README.md.
Source API -> collector -> normalized vacancy -> deduplication -> registry
-> match analysis -> application package -> catalog
HR readers can use this repository to understand how I search, filter, compare, and prepare applications without hiding behind generic career language.
Senior developers can review the architecture, data boundaries, tests, model-use policy, and failure handling. The goal is transparency: every important step leaves files, hashes, commands, or reviewable Markdown behind.
- Collects vacancies from public job APIs and company career pages.
- Normalizes each vacancy into a shared local format.
- Deduplicates roles across sources without merging ambiguous records.
- Stores operational vacancy data in the configured MongoDB backend, with Git-managed application artifacts and a temporary YAML migration mode.
- Rejects obvious non-profile roles through deterministic filters.
- Uses committed candidate records as the source of truth.
- Runs Codex tasks for judgment-heavy match analysis and application writing.
- Validates Codex-produced drafts before publishing them.
- Generates CV, cover letter, application analysis, and interview preparation files.
- Builds a human-readable vacancy catalog from registry metadata.
- It does not call the OpenAI Platform API from project code.
- It does not use a hidden model proxy, OpenAI SDK, or OpenAI-compatible endpoint.
- It does not invent candidate claims.
- It does not auto-submit applications.
- It does not infer application status from artifacts or external events.
- It does not require a database, service process, Docker setup, or UI.
This boundary matters. Deterministic code handles repeatable work. Codex handles tasks that need language judgment. The repository keeps the handoff visible.
| Path | Purpose |
|---|---|
sources/ |
Collectors, source configs, and source-specific guides. |
registry/jobs/ |
Canonical vacancy records, match results, and application packages. |
registry/candidate/ |
Candidate source records used as evidence. |
catalog/ |
Generated vacancy catalog for human review. |
docs/ |
Architecture notes for collection, matching, and preparation. |
prompts/ |
Versioned Codex prompts for model-dependent work. |
tests/ |
Unit tests for deterministic behavior. |
run.py |
Main command entrypoint. |
TECHNICAL_README.md |
Full technical operating manual. |
-
Collect vacancies
Source collectors fetch vacancies from Adzuna, Arbeitnow, CleanJobData, Djinni, DOU, Himalayas, Jobicy, Jobspresso, Jooble, Techyon, We Work Remotely, public Ashby boards, public Greenhouse boards, and selected company career pages. Remotive, Remote OK and LaraJobs have bounded pilots. Hacker News employer posts and additional Ashby boards expand European remote coverage; see European remote sources for expiration, eligibility, attribution and deferred search channels.
-
Normalize and deduplicate
Each source record becomes a shared
NormalizedJob. The registry matches exact source IDs first, then uses conservative fingerprints for possible cross-source matches. -
Filter unsuitable roles
Deterministic filters keep old vacancies and obvious non-profile roles out of the main registry. Rejected records remain available under
registry/rejected/with structured reasons. -
Analyze the match
A Codex task compares one vacancy, or a sealed batch of up to fifteen vacancies, against the candidate evidence. Project code validates the produced draft before publishing
match.yamlandmatch.md. -
Prepare the application
A separate Codex task handles an explicitly selected batch of one to ten vacancies. For each vacancy, the task can use two coordination waves: company research, CV/evidence mapping, and requirement/risk review run in parallel first; after the coordinating agent fixes the shared
cv.md, cover-letter, interview, and analysis work can run in parallel. Subagents exchange intermediate work only through that vacancy's.codex-work/application/<vacancy>/parts/directory and have exclusive ownership of their output files. Inputs stay minimal: research does not receive the full candidate CV, CV drafting does not browse the web, and second-wave workers see only the finalized CV and handoffs they need. The letter still goes through the dedicated$write-cover-letterplugin skill. The coordinator performs one cross-file check, validates the complete package once, and publishes it once. The same ownership and vacancy-isolation rules apply when concurrency is unavailable and the waves run sequentially.Validation is a content gate, not just a file-existence check. The draft must meet the document word ranges and required sections, include substantive Wave 1 handoffs, and provide
quality.yamlwith the exact cover-letter skill version, two grounded evidence stories, a sourced company hook, and the final consistency review. Publishedmanifest.yamlrecords the contract version, document and handoff hashes, word counts, and method provenance.The full package is the default. If the user explicitly asks for one document, add
--document cv,--document cover-letter,--document analysis, or--document interview-preparation; only that draft is required and only its published outputs are replaced. Every generated CV Experience role includes its own evidence-backedTechnologies:line. -
Regenerate the catalog
The catalog gives a readable overview of vacancies, scores, statuses, links, and available application artifacts.
The workflow starts with two imported candidate records and supplements them with append-only clarification and experience-inventory files:
registry/candidate/linkedin-profile.mdregistry/candidate/backend-engineer-cv.mdregistry/candidate/user-confirmed-*.mdregistry/candidate/*-experience-inventory-*.mdregistry/candidate/employment-letter-extract-*.md
Inventory files keep candidate-reported delivery, proposals, and unresolved questions separate; their presence does not verify a CV claim. Employment-letter extracts preserve candidate-supplied transcripts and their chronology caveats. The reviewed achievements bank selects usable excerpts. Application drafts and match analysis must not add experience, metrics, tools, companies, or responsibilities unless verified candidate evidence supports them. If records conflict, the workflow preserves the conflict for review instead of guessing.
For an explicitly requested LinkedIn audit, rewrite, Skills alignment, or Featured
case study, I use the project skill
linkedin-profile-workflow.
It loads the published linkedin-profile-optimizer and linkedin-featured-proof
skills from agent-plugins, using the same immutable candidate sources, reviewed
evidence, and current personal voice as the application workflow.
The workflow prompt defines the inputs, evidence review, draft outputs, and external-action boundaries. The integration configuration pins a reviewed library revision as a read-only fallback when the installed plugin predates these skills. Profile work does not generate applications, change vacancy status, overwrite the source LinkedIn record, or update the live LinkedIn profile automatically.
Example request: Use $linkedin-profile-workflow to audit my LinkedIn headline, About, Experience and Skills against the three vacancies I explicitly selected. Keep unsupported skills and metrics out of the proposed copy.
Project code must stay deterministic. It can collect, normalize, deduplicate, validate, index, convert, and publish local files. It must not call the OpenAI Platform API or an OpenAI-compatible model endpoint.
Codex runs model-dependent work in separate interactive or scheduled tasks. The repository then validates and publishes Codex-produced local drafts. This keeps the model boundary explicit and auditable.
Scheduled analysis also publishes eligible Telegram notifications as tracked JSON
manifests under notifications/telegram/outbox/. GitHub Actions sends those manifests
and commits delivery acknowledgements; the Codex task itself performs no Telegram
network call.
Set up the project:
py -3.11 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -e .
Copy-Item sources/.env.example sources/.envRun collection and maintenance:
python run.py all
python scripts/archive_jobs.py rejected --keep-items 50
python run.py reindex
python run.py catalog
python run.py doctor
python run.py top 10
Run one source:
python run.py adzuna
python run.py arbeitnow
python run.py cleanjobdata
python run.py dou
python run.py djinni
python run.py himalayas
python run.py jobicy
python run.py jobspresso
python run.py jooble
python run.py techyon
python run.py weworkremotely
python run.py ashby
python run.py greenhouse
python run.py custom
Change a vacancy status after an explicit decision:
python run.py status <job-directory-or-vacancy-id> reviewing --reason "needs human review"
Manual status changes made through the user/Codex loop are audited in
registry/manual-status-log.yaml, including counts by target status and reason.
For employer rejections reported by the user, retain the full original message in
registry/feedback/<vacancy-directory>/<recorded-date>-rejection.md and pass that
text plus the feedback-file path through --status-note when setting rejected.
Preserve the original language and paragraphs, distinguish the recording date from
the message date, and label a user summary if the original message is unavailable.
Feedback records remain available even if the vacancy directory is later removed.
Publish Codex-produced work:
python run.py analyze <job-directory-or-vacancy-id> --input <draft.yaml> --workflow analyze
python run.py triage <job-directory-or-vacancy-id>
python run.py pending analyze <job-directory-or-vacancy-id> --workflow analyze --pack <pack.yaml>
python run.py validate-application <job-directory-or-vacancy-id> --input <draft-directory>
python run.py prepare <selector-1> [<selector-2> ...] --input .codex-work/application --workflow prepare
# Explicit single-document preparation (the default remains the full package).
python run.py validate-application <job-directory-or-vacancy-id> --input <draft-directory> --document cv
python run.py prepare <selector-1> --input .codex-work/application --workflow prepare --document cv
# Select an allowed model profile when the active Codex task uses a non-default model.
python run.py analyze <job-directory-or-vacancy-id> --input <draft.yaml> --workflow analyze --model-profile terra_medium
python run.py prepare <selector-1> --input .codex-work/application --workflow prepare --model-profile terra_medium
Preparation checks match freshness against its selected model profile. When the
existing match used another profile, run a real vacancy-specific analysis with the
preparation profile and --force; never edit or relabel the old provenance.
An explicit selector limits both deterministic triage and an analysis pack to that
single vacancy; use all only when intentionally processing the scheduled queue.
The shared interactive and scheduled workflow contract lives in
prompts/job-intelligence-workflow.md.
Model profiles are configured in config/codex-workflows.yaml; the Codex task must
actually use the selected profile's model and reasoning because repository code cannot
switch the active model of a running task.
Run tests:
python -m unittest discover -v
See TECHNICAL_README.md for the full command list, source
configuration, queue APIs, workflow routing, and collector authoring guide.
This repository is meant to be inspected. A reviewer can open a vacancy directory and see the original job text, metadata, match analysis, generated application files, and publication manifest. A developer can run the tests and trace the deterministic path from source record to registry entry.
That is the point of the project. I want the workflow to be useful, but I also want it to show how I work: transparent about tools, careful with evidence, willing to automate repetitive steps, and honest about where human judgment still matters.
Job Intelligence collects, normalizes, deduplicates, stores, ranks, indexes, and prepares vacancy-specific application materials.
MongoDB stores operational vacancy and lifecycle records; candidate evidence and application documents remain files. Submission is manual, and vacancy status changes require an explicit user request. The repository keeps a reviewable record of those decisions.
The quality v3 contract adds source-backed evidence, grounded claim ledgers, compact document formats, a final CV review, and real DOCX export checks. Old receipts stay explicitly legacy. The application lifecycle preserves exact sent files, records confirmed outcomes, reports descriptive conversion rates and follow-up timing, and publishes approved form-answer/interview/follow-up drafts. No application is sent and no status changes automatically.
An employer rejection blocks that vacancy and every vacancy at the same company
for six calendar months, through the day before the six-month anniversary in
Europe/Rome. End-of-month dates clamp to the last day of the target month.
Collection, top results, catalog listings, analysis queues/sealed packs and explicit
preparation/publication all apply this rule, including new duplicate adverts.
Archived vacancy histories and the retained manual status audit remain inputs.
The latest rejection and any longer explicit company_retry_after deadline win;
configured aliases and legal-suffix variants identify the same company.
Model not_match results, prefilter exclusions and withdrawals are not employer
rejections. History, statuses and existing application files remain intact.
Recording a confirmed past submission stays descriptive and does not authorize a
new application.