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

> Learn how the /rank command efficiently batch-scores multiple job postings against the fit framework in parallel. Discover a fast triage process for job applications.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: how-to-guide
- Published: 2026-08-05

---

**The `/rank` command performs a lightweight, triage-only scoring pass over job postings using the same four-dimension fit rubric as `/apply`, but without company research or salary lookups—processing jobs in parallel groups of approximately five via general-purpose agents.**

The `/rank` command in the `MadsLorentzen/ai-job-search` repository bridges bulk-scraped job pools and deep-dive evaluations. It enables efficient batch-scoring of multiple job postings against a consistent fit framework, helping users prioritize which positions deserve further attention through the `/apply` command.

## Input Parsing: Determining Which Postings to Score

The command supports four input modes controlled by arguments in [`/.claude/commands/rank.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main//.claude/commands/rank.md):

- **No arguments** — Scores all jobs with status `new` in [`job_scraper/seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/job_scraper/seen_jobs.json)
- **Focus term** (e.g., `/rank data science`) — Filters to jobs whose title or stored fit-notes contain the term
- **`--all`** — Re-scores every non-applied job, useful after profile updates
- **`--top <N>`** — Defines shortlist size (default 5)

## State Loading and Exclusion Logic

Before scoring begins, `/rank` loads two data sources to build its working set. First, it reads [`job_scraper/seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/job_scraper/seen_jobs.json); if this file is missing, the command prompts the user to run `/scrape` first. Second, it reads `job_search_tracker.csv` to construct an exclusion set of already-applied-to company + role pairs. The system then filters new entries against this exclusion set and any provided focus term.

## Framework and Profile Loading

The scoring rubric and candidate profile are loaded once at the start of each `/rank` invocation. These come from two files in the Claude skills directory:

- [`.claude/skills/job-application-assistant/04-job-evaluation.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-application-assistant/04-job-evaluation.md) — Contains the four-dimension scoring framework
- [`.claude/skills/job-application-assistant/01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-application-assistant/01-candidate-profile.md) — Defines candidate constraints and preferences【/claude/commands/rank.md†L28-L31】

This single load ensures consistency across all jobs in the batch.

## Batch Fetch and Parallel Scoring

Jobs are grouped into batches of approximately five and distributed to parallel general-purpose agents. Each agent receives inline:

- The job list (title, company, URL)
- The compact rubric extracted during framework loading

Agents use **WebFetch** to retrieve posting content and score **only** from that fetched content. Dead or redirected URLs are marked `expired` rather than scored. The returned JSON for each job includes:

| Field | Description |
|-------|-------------|
| `scores` | Raw scores for four dimensions (technical, experience, behavioral, career) |
| `location` | Verdict (PASS/FAIL/FLAG) against location constraints |
| `deadline` | Application deadline if detected |
| `strengths` | Candidate-role alignment points |
| `gaps` | Missing qualifications or experience |
| `language` | Detected posting language |

【/claude/commands/rank.md†L44-L56】

## Aggregation, Weighting, and Ranking

Back in the main context, `/rank` computes an **overall score** using weights defined in [`04-job-evaluation.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/04-job-evaluation.md):

- **Technical**: 30%
- **Experience**: 25%
- **Behavioral**: 15%
- **Career**: 30%【/claude/commands/rank.md†L67-L68】

Scores map to verdict bands: Strong Fit (≥75), Good Fit (60-74), Moderate Fit (45-59), Weak Fit (30-44), and Poor Fit (<30). Two critical modifiers apply:

- **Location veto**: `FAIL` removes the job from the shortlist; `FLAG` keeps it with a warning
- **Deadline urgency**: Jobs with deadlines ≤7 days receive a 🔥 marker and win tie-breakers; past-due jobs become `expired`

The final list sorts by overall score descending, with urgency as the tie-breaker【/claude/commands/rank.md†L70-L73】.

## State Update and Persistence

After processing, [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) updates in-place with:

```json
{
  "status": "ranked",
  "rank_score": 78,
  "rank_verdict": "Strong Fit",
  "rank_date": "2026-07-14"
}

```

Expired or past-deadline jobs receive `"status": "expired"`【/claude/commands/rank.md†L78-L82】. The `job_search_tracker.csv` remains untouched—it only records actual applications, not ranking results.

## Command Usage Examples

```bash

# Rank all new postings

$ claude /rank

# Only titles or fit-notes containing "data science"

$ claude /rank data science

# Re-score everything, return top 10

$ claude /rank --all --top 10

```

## Example Agent Response Structure

```json
{
  "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"
}

```

## Output Presentation

`/rank` renders a markdown table summarizing three categories: shortlisted jobs, below-threshold jobs, and excluded items. The output explicitly states that numbers represent **triage scores from posting text only**, and prompts the user to invoke `/apply` for any selected job for deeper evaluation【/claude/commands/rank.md†L87-L101】.

## Summary

- `/rank` batch-scores multiple job postings against the fit framework using parallel agent processing
- Scoring stays shallow by design: no company research, salary lookups, or fabricated data
- Four-dimension rubric with weighted aggregation (Technical 30%, Experience 25%, Behavioral 15%, Career 30%)
- Location constraints can veto jobs; deadline urgency affects ranking and visual markers
- Results persist to [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) with `ranked` or `expired` status
- Output explicitly distinguishes triage scores from deep `/apply` evaluations

## Frequently Asked Questions

### What is the difference between `/rank` and `/apply` in the ai-job-search system?

`/rank` performs lightweight batch-scoring of multiple postings using only fetched job content, while `/apply` conducts deep-dive evaluations including company research, salary benchmarking, and tailored application materials. `/rank` is designed for triage; `/apply` is designed for execution on selected opportunities.

### How does `/rank` handle jobs that fail location constraints?

Jobs receiving a `FAIL` location verdict are removed entirely from the shortlist. Jobs with a `FLAG` verdict are retained but displayed with a warning. This veto system prevents wasted time on positions that violate hard constraints like geographic requirements or visa sponsorship needs.

### 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 reads the fresh candidate profile from [`01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/01-candidate-profile.md) and applies current rubric weights from [`04-job-evaluation.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/04-job-evaluation.md) to all previously scraped positions.

### Why does `/rank` use groups of five jobs for parallel processing?

The ~5-job batch size balances parallelism against context window limitations. Each agent receives the full rubric and job list inline, so smaller batches keep individual agent contexts manageable while still achieving meaningful throughput across multiple parallel workers.