sunny haladker
← work

Case study · built with Claude Code

JobOps

A job-search operating system, built with Claude Code. Sources jobs from ~19 integrations, gates them against policy I encoded in data, scores them against a rubric I wrote, and drafts applications within guardrails I defined. No database, no UI, no auto-submit: every application is reviewed by a human before it goes out. That human is me.

Role
Designer + sole engineer
Stack
Python · CSV · Claude Code
Surface
CLI, no UI
Status
Built; loop closed Aug 3 2026

01 · The brief

  • The constraint: As a product designer who usually starts with a UI, I chose a CLI, agent-driven workflow with no UI to force engineering rigor.
  • No hiding place: Every decision had to be named and every failure handled explicitly.

02 · Shape of the system

  • Stack: 6 agent specs (agents/*.md), 19 scraper integrations, Python runtime, and 5 CSV state files.
  • Scale: 93 target companies, 60 jurisdiction rules, ~5,500 scored job rows since April 2026.
[ATS APIs + job boards]
        ↓
[scrapers — 19 integrations]
        ↓
[data/inbox/*.json — raw, untouched]
        ↓
[ingest.py: dedupe → eligibility gate → audit gate → score]
        ↓
[data/jobs.csv — the scored funnel]
        ↓
[triage.py — the review queue]
        ↓
[drafter agent → cover.md + resume.md]
        ↓
[render.sh → PDF / DOCX / plain text]
        ↓
[human review → submission → applications.csv]
[VISUAL] System map — boxes connected by arrows, from ATS APIs → scrapers → inbox → ingest → jobs.csv → triage → drafter → render → human.

03 · The spec/runtime split — the part that matters

  • Markdown specs vs Python runtime: Agents are prose instructions (non-deterministic); runtime code is strict Python (deterministic and testable).
  • Decoupled ingestion: scrapers/lever.md is the spec; scripts/scrape.py fetches, validates schemas, and writes to disk. If the LLM slips, the code holds.
“Data integrity must hold even when the LLM hallucinates.”— CLAUDE.md

04 · File-first state

  • No in-memory state: Raw output lives in data/inbox/, scored funnel in jobs.csv, mutations logged to interactions.csv.
  • Restartable architecture: Because state lives on disk, model failures cannot corrupt application state.
[VISUAL] State flow — small three-box strip: inbox/*.json (raw) → jobs.csv (scored) → inbox/processed/ (archive).

05 · The eligibility gate — domain knowledge became data

  • The BGV audit trap: As an independent founder, my income evidence is client invoices and ITRs, not corporate payslips. SOP-driven mid/large enterprises fail this post-offer.
  • Encoded as data, not code: I codified global jurisdiction rules into CSV/Python policy gates rather than leaving it to LLM prompts.
if country_hire == "IN" and headcount in ("200-1000", "1000+"):
    return SKIP          # SOP-driven HR can't defend the offer
if headcount == "10000+":
    return PASS if country_hire == "global" else SKIP   # federated big tech
if leadership_hires == "yes":
    return PASS          # founder in the loop can defend the offer
[VISUAL] The gate as a tree — top-down binary decision tree ending in APPLY / SKIP.

06 · The scoring system

  • Rubric weights (0–100): Title (20), skills (30), seniority (15), location (15), comp (20), minus staleness/aggregator penalties.
  • Regression-tested strategy: Changes to strategy re-score all historical rows, locked with fixtures (52/52, 20/20, 13/13).

07 · The drafter — volume with taste

  • Strict guardrails:<250-word letters, zero invented metrics, ≥80% keyword match, mandatory human pre-flight.
  • Multi-faceted positioning: The same WellingZen project mapped to three different GitLab JDs with distinct framings.
JDFace of WellingZen
Senior PD, AIAI-augmented engineering proof
Staff PD, Knowledge GraphSystem-prototyping credibility
Staff PD, Security & ComplianceSecurity/policy chops
The volume of a machine, the discrimination of a senior contractor.
[VISUAL] The fan-out — one project diverging into three distinct framing angles.

08 · The retro + pivot triggers

  • Weekly funnel telemetry: Fridays, /retro computes reply rates, track conversions, and comp-floor drift.
  • Surface, don't decide: The system surfaces signals for strategic pivots; the human pulls the trigger.

09 · The recursive plan

“It was not exactly a realisation, it was kind of the plan to leverage Claude to somehow improve my chances.”
  • The loop: Built JobOps with Claude Code → used it to target AI-native teams → ran the portfolio site gating applications → drafted this case study.
  • Milestone: On August 3, 2026, the first end-to-end application completed the loop (sourced → scored → gated → drafted → submitted).
[VISUAL] The loop — four nodes showing the recursive toolchain.

10 · What this v0 doesn't cover yet

  • CLI screencaps and /scrape demos.
  • Public repo release (sanitized skeleton planned).
  • Conversion telemetry at scale (early stage).

What the reader should take away

  • Clear division of labor: LLMs handle labor (fetching, drafting); code handles integrity (schemas, gates); humans handle judgment (rubric, policy, final yes).
  • The core thesis: Anyone can prompt an LLM. The value is the person who builds the system around it.