How `/rank` Batch-Scores Multiple Job Postings Against the Fit Framework

The /rank command performs a lightweight triage scoring pass over new job postings by parallelizing agent-based evaluation of posting content against a four-dimensional rubric, then aggregating weighted scores to generate a shortlist while excluding previously applied positions and location mismatches.

The /rank command in the MadsLorentzen/ai-job-search repository serves as the critical bridge between bulk scraping (/scrape) and deep-dive evaluation (/apply). It batch-scores multiple job postings against the fit framework using parallel agent processing to quickly identify high-potential opportunities without expensive company research or salary lookups.

Input Parsing and Job Selection

The command determines which postings to evaluate based on CLI arguments parsed at runtime.

  • No arguments: Targets all jobs with status new in job_scraper/seen_jobs.json
  • Focus term (e.g., /rank data science): Filters for jobs whose title or stored fit-notes contain the term
  • --all: Re-scores every non-applied job, useful after profile updates
  • --top <N>: Defines the shortlist size (default 5)

State Loading and Exclusion Logic

Before scoring, the system establishes the evaluation universe by reading persistent state files.

It first attempts to read job_scraper/seen_jobs.json, exiting with a prompt to run /scrape if the file is missing. Simultaneously, it reads job_search_tracker.csv to build an exclusion set of company-role pairs for positions already applied to. The pipeline filters candidate jobs against this exclusion set and any focus term, ensuring only relevant, unapplied positions enter the scoring queue.

Framework and Profile Loading

The scoring rubric and candidate profile load once at the start of the batch process to ensure consistency across parallel evaluations.

According to the specification in /.claude/commands/rank.md, the system pulls evaluation criteria from .claude/skills/job-application-assistant/04-job-evaluation.md and candidate constraints from .claude/skills/job-application-assistant/01-candidate-profile.md. This single-load approach prevents rubric drift across the distributed agent calls.

Parallel Agent Scoring

Batch Processing with WebFetch

Jobs are grouped into batches of approximately five per parallel general-purpose agent. Each agent receives an inline payload containing the job list (title, company, URL) plus the compact rubric extracted during initialization. Agents fetch posting URLs using WebFetch and score exclusively from the fetched content. Dead or redirected URLs are marked expired rather than scored.

Evaluation Dimensions

The returned JSON for each job contains raw scores for four dimensions: technical, experience, behavioral, and career fit. It also includes a location verdict, deadline, identified strengths, gaps, and language detection. As specified in /.claude/commands/rank.md lines 44-56, the agent outputs structured data without conducting additional web searches or salary lookups.

{
  "key": "12345",
  "status": "scored",
  "scores": { "technical": 82, "experience": 75, "behavioral": 68, "career": 90 },
  "location": "PASS",
  "deadline": "2026-08-01",
  "strengths": ["Implemented CI pipelines", "Led a 5-person team"],
  "gaps": ["No cloud-native experience"],
  "language": "en"
}

Score Aggregation and Ranking Logic

Weighted Score Calculation

Back in the main context, an overall score is computed using weights defined in 04-job-evaluation.md: Technical 30%, Experience 25%, Behavioral 15%, and Career 30%. These weights map to verdict bands: Strong Fit (≥75), Good Fit (60-74), Moderate Fit (45-59), Weak Fit (30-44), and Poor Fit (<30).

Location Veto and Deadline Urgency

A location verdict of FAIL removes the job from the shortlist entirely, while FLAG keeps it with a warning. Jobs with deadlines within seven days receive a 🔥 marker and win tie-breakers. Past-due jobs are marked expired and excluded from ranking. The final list sorts by overall score descending, with urgency as the secondary sort key.

State Persistence and Output

Updating Job Status

The system updates job_scraper/seen_jobs.json in-place. Ranked jobs receive "status":"ranked" plus computed fields rank_score, rank_verdict, and rank_date. Expired or past-deadline jobs receive "status":"expired". The job_search_tracker.csv remains untouched, as it only records actual applications.

Presentation Format

The command outputs a markdown table summarizing the shortlist, below-threshold jobs, and excluded items. The display explicitly states that numbers represent triage scores from posting text only, prompting users to invoke /apply for selected jobs to trigger deeper evaluation.

Usage Examples

Invoke /rank from the CLI using the following patterns:


# Rank all new postings

$ claude /rank

# Filter for specific roles

$ claude /rank data science

# Re-score everything and return top 10

$ claude /rank --all --top 10

Sample output table format:


## Job Ranking - 2026-07-14

Ranked 12 new postings (5 shortlisted, 4 below threshold, 3 expired/vetoed).

### Shortlist

| # | Score | Verdict | Title | Company | Location | Deadline | |

|---|-------|---------|-------|---------|----------|----------|-|
| 1 | 78 | Strong Fit | Senior Data Engineer | Acme Corp | Remote | 2026-08-03 | 🔥 |
| 2 | 71 | Good Fit | ML Researcher | Beta Ltd | Copenhagen | — | |

Summary

  • /rank acts as a triage layer between /scrape and /apply, batch-scoring multiple job postings using parallel agents
  • The system evaluates four dimensions (Technical 30%, Experience 25%, Behavioral 15%, Career 30%) using rubrics from 04-job-evaluation.md
  • Location vetoes (FAIL) and deadline urgency (≤7 days) automatically filter and prioritize the shortlist
  • State updates persist to job_scraper/seen_jobs.json while respecting exclusions from job_search_tracker.csv
  • Output remains deliberately shallow—no company research or salary data—to maintain processing speed

Frequently Asked Questions

How does /rank differ from the /apply command?

/rank performs a lightweight triage scoring pass using only the job posting content fetched via WebFetch, while /apply conducts deep-dive evaluation including company research, salary lookups, and comprehensive fit analysis. /rank updates seen_jobs.json with triage scores, whereas /apply generates application materials and logs entries to job_search_tracker.csv.

What happens if a job posting URL is dead or redirected?

Agents mark dead or redirected URLs as expired during the WebFetch phase. These jobs receive "status":"expired" in job_scraper/seen_jobs.json and are excluded from the ranked shortlist, preventing wasted evaluation cycles on inaccessible postings.

Can I re-score jobs after updating my candidate profile?

Yes. Use the --all flag to re-score every non-applied job against your updated profile. This is useful after modifying .claude/skills/job-application-assistant/01-candidate-profile.md or changing evaluation criteria in 04-job-evaluation.md, as it forces a fresh batch-scoring pass regardless of previous new or ranked statuses.

How are ties broken when multiple jobs have the same score?

The ranking algorithm uses deadline urgency as the primary tie-breaker. Jobs with deadlines within seven days receive a 🔥 marker and priority placement. If urgency is equal, the original scrape order or internal key may determine final positioning, though the specific secondary sort key is not explicitly weighted in the aggregation logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →