Back to blog
NO.
013
DATE
Updated 2026-07-20
READ
~10 min
KIND
Case study
STATUS
Reviewed

TAGS: AI collaboration

A Job-Search System That Isn't an Auto-Applier

Version one should not be an application bot. What actually shipped: a Markdown kanban where the column is the only source of truth.

I wanted to build an AI job-search automation system, and the more I broke it down the more convinced I got that version one should not be an auto-applier.

That started as intuition. Then I actually built the thing along those lines and ran it for about a month — dozens of roles moving from collection through outreach, interviews, and retrospectives. Looking back, not building an application bot was right. So this post has three parts: how I originally decomposed the system, what actually shipped, and what I still want to add.

System boundaries

Auto-applying sounds the most satisfying: scrape roles, rewrite the résumé, fill the form, submit. It is also the fastest way to build the wrong system. A job search is not ad buying; the goal is not to scatter a résumé more widely but to make each application a better-grounded choice. What actually drains a person is re-reading JDs, manually aligning experience, keeping versions of materials, and forgetting to follow up or debrief.

So version one should address those. It should behave like a job-search CRM and a materials workbench, not a button-pusher. I split it into four layers:

  1. Collection: save the link, company, role, location/remote, salary, posting date, and the raw JD.
  2. Comprehension: use AI to extract hard requirements, implicit preferences, keywords, risk signals, and likely interview follow-ups.
  3. Matching: from my experience library, generate a match rationale, résumé rewrite suggestions, and a cover-letter draft.
  4. Process retrospective: record application status, conversations, interview feedback, rejection reasons, and the next action.

The most important design decision: the AI only analyzes and drafts; it never applies on my behalf. A human confirms before anything is sent, because role selection, résumé accuracy, and tone of communication are all part of a long-term reputation. At most the system puts "is this worth applying to, what should change, what evidence is missing" in front of me. It does not get to skip that judgment. That was my position going in, and a month of use made me more sure of it.

What the AI should do

Four kinds of work.

Denoise. A JD usually mixes hard requirements, nice-to-haves, boilerplate, and a wish list the employer has not thought through. AI can separate those so I can quickly decide whether it deserves my time.

Map. If a role asks for "familiarity with content systems, automation, Cloudflare Workers," the system should surface the blog CMS, the publishing pipeline, and the Worker API from my project library — instead of making me recall them each time.

Produce editable drafts. Résumé bullets, project descriptions, email openings can all be generated, but each must cite the experience it drew on, so nothing I did not do ends up on the page.

Flag gaps. When a role asks for something I have no clear evidence for, the system should say "no evidence" rather than produce a smooth-sounding sentence. Job-search materials should err conservative; a model hallucination must never reach a résumé.

What it does not do

Version one does not auto-apply, does not script around job-platform restrictions, and does not generate fabricated experience. Those look like efficiency in the short run and pollute the data long-term, distorting the retrospectives that follow.

I am also not dumping every posting into a vector store in pursuit of a mystical "match score." A score is at best a sort hint, never a substitute for judgment. Whether a role is worth applying to usually depends on company condition, business direction, hiring motive, role scarcity, and where I am in my own trajectory.

The last boundary is privacy. Résumés, project history, conversation records, salary expectations, and interview feedback should not be shipped to untrusted third-party endpoints. Version one belongs in local storage with minimal fields and manual import: no scraping of platform account cookies, no storing verification codes, no auto-login, no recording of personal information unrelated to the search.

What actually shipped

When I started building, I made a choice I had not anticipated: no database. The whole system landed as a pile of Markdown files organized by a kanban plugin.

I had assumed the data structure had to be settled first, defaulting to SQLite or some structured store so the system could search, compare, and support retrospectives. Using it revealed that the bottleneck in a job search is not query capability — it is maintenance friction. I process a handful to a dozen roles a day, and what kills a system like this is finding every entry tedious. Plain text plus git, readable directly by any AI environment, is a durability that matters far more than query power. A database asks me to settle a schema and wire up tooling before I can start; a Markdown file lets me type a line right now.

The kanban is the single source of truth for status

The core is one Markdown kanban file, with columns left to right: inbox → needs research → to contact → applied/contacted → interview (R1/R2/R3) → offer → closed (rejected/withdrawn) → archive (not tracking). Two permanent columns sit alongside: a template library and the usage rules.

Each role is one row in a fixed format: company – title – salary – channel/date – priority (A/B/C/D plus one line of reasoning) – next action – link to its detail card. One scan tells me where every live role is stuck and what to do next.

Each role also gets a detail card, a single Markdown file recording facts, research, conversations, interview prep, and the debrief. The crucial rule: the detail card carries no status field. Whether a role is "to contact" or "interview R2" is determined solely by which kanban column it sits in, and the card never repeats it. That is what "the column is the single source of truth, the card only accumulates detail" means — the dual-write mismatch I had worried about (status stored twice, one copy updated and the other forgotten) cannot occur.

The same logic extends to derived views. I wanted a table and a page I could open in a browser, so a script derives both from the kanban. The derived files are not to be hand-edited, because they are only projections; edit the source, or the dual-write problem returns.

The permanent files around it

A kanban alone is not enough. What keeps it running is a few standing files:

  • A decision-rules file fixing direction, offer floor, and communication rules, so I do not re-argue them (with myself, or with an AI) for every role.
  • A PROFILE recording my background and capability boundaries, used as the baseline whenever an AI analyzes a role.
  • STATUS / TODO / LOG, designed for "several AI environments taking turns": each session reads them first, updates STATUS, and appends a line to LOG.
  • A prompts directory holding the fixed prompts for starting a task and generating a handoff package.
  • The template column: written scripts for first contact, scheduling, asking about the team, following up, and going quiet; light résumé-customization rules; and an interview debrief template.

Bringing an AI in is simple: I hand it one role, it analyzes the opportunity by default, then updates the kanban and that role's card. Later conversations, interviews, and rejections update the same card; no second card is created. One rule I hold to firmly — an AI conversation cannot be the long-term source of truth. Conversations scroll away and get lost. Anything worth keeping goes into a file. The kanban and the cards are the memory; the chat window is a temporary workbench.

Privacy landed as a red-line list

"Privacy tight by default" became an explicit list: never record ID numbers, verification codes, account passwords, salary bank records, raw recruiter DMs, or screenshots of a logged-in session. Chat screenshots are distilled locally into facts written onto the card; the screenshot itself never enters git. Once an idea becomes a list, execution stops requiring a judgment call each time.

If the scale ever grows

After about a month and dozens of roles through the full state flow, including interviews and debriefs, the plain-text version holds up fine. I did not delete the structured design; it waits. If volume ever outgrows hand-maintained Markdown, it upgrades to something like:

job: id / company / title / source_url / jd_summary / requirements / risks / status
profile_asset: id / type(project|work|education|achievement) / title / evidence / keywords
application: job_id / resume_version / cover_letter / human_notes / confirmed_by_human / next_action

But that is worth doing only when scale forces it. At this stage, the lowest-maintenance option is the best option.

What I still want to add

First in line is something I have not built and will probably wait until after I have a job to do: moving the "capture and analyze" step off the desktop and into a chat entry point I always have on me. Right now, entering a role means returning to the desktop kanban workflow. What I want: spot a role, drop the link or material into an AI assistant wired into a messaging app, and have it analyze against my profile and decision rules and answer "worth applying, what to ask, which résumé version." The system's entrance moves to the phone; archiving happens back at the desk later.

The rest are smaller, aimed at specific maintenance pains in the current system:

  • Frontmatter on the detail cards. The cards are mostly prose today, so any statistic requires counting by hand. A few structured fields at the top (channel, priority, status timestamps) would let a script derive the kanban rows and basic stats instead of me transcribing them.
  • Channel and funnel analysis. After dozens of roles I still cannot say which channel is worth the effort, or how many drop out between collection, contact, and interview. Structured fields make that computable, so effort can move toward the channels that return.
  • A broken-link checker. Kanban rows and detail cards point at each other by link, and hand maintenance will eventually produce an unlinked card or a link to a file that no longer exists. A script that reports broken links removes the "click through and find nothing" annoyance.
  • Follow-up reminders. The easiest thing to lose is a role sitting in "to contact" for weeks. I want the system to surface cards where "to contact" has been idle for N days, or where an interview has had no follow-up for N days, instead of relying on my memory.
  • Feeding debriefs back into an evidence library. Each interview debrief accumulates useful material — "this experience lands better told this way" — currently buried in individual cards. Flowing that back into PROFILE or a résumé evidence library means the next revision can reuse phrasing that has already been tested in an interview.

The principles

These were my positions going in. A month of use gives them some practical backing:

  • Judgment is not outsourced: whether to apply, how to write it, whether to accept — a human decides.
  • Evidence is traceable: every generated sentence should trace back to real experience.
  • Privacy tight by default: process locally where possible, store as little as possible, keep credentials and unrelated personal information out of the system.
  • Retrospectives before scale: better to apply to fewer roles and know why each one was chosen and why it went the way it did.

The value of job-search automation is not making the system look busier on my behalf. It is seeing more clearly the distance between me and a role. The plainest lesson from the month: keeping the thing in daily use matters far more than making it look advanced.

Comments →

CC BY-NC-SA 4.0

Comments

Comments are powered by GitHub Discussions. Sign in with GitHub to comment. Open the matching Discussion