How the AI-Job-Search Framework Evaluates Job Fit: A Deterministic Scoring Pipeline

The framework evaluates job fit through a deterministic, rule-based scoring pipeline that applies hard-stop eligibility gates before calculating weighted dimension scores across technical skills, experience, behavioral fit, and career alignment.

The ai-job-search repository by MadsLorentzen implements a reproducible, code-driven approach to triage job postings using a fixed rubric defined in specification files rather than subjective judgment. This framework processes scraped job data through a strict evaluation protocol that combines hard filters with quantitative scoring to generate ranked shortlists.

The Two-Phase Evaluation Architecture

The framework splits job fit evaluation into distinct gate-checking and scoring phases, ensuring only viable candidates proceed to quantitative analysis.

Phase 1: Hard-Stop Gates (Eligibility and Language)

Before any numeric scoring occurs, the system runs eligibility and language gates defined in .claude/skills/job-application-assistant/04-job-evaluation.md. These are deterministic filters that immediately exclude postings based on citizenship requirements, security clearances, residency restrictions, or unsupported languages.

  • Eligibility Gate: Returns FAIL for jobs requiring attributes the candidate lacks (e.g., US citizenship when unavailable), immediately vetoing the posting.
  • Language Gate: Returns FAIL if the posting requires a language not listed in the candidate profile, or FLAG to highlight potential mismatches while keeping the job in consideration.

Jobs that fail either gate receive no score and generate no application draft.

Phase 2: Quantitative Scoring Dimensions

For postings that pass the gates, the framework evaluates five dimensions on a 0-100 scale:

  • Technical Skills (0-100): Match between required technical stack and candidate capabilities
  • Experience Match (0-100): Alignment of years and type of experience with role requirements
  • Behavioral/Culture Fit (0-100): Compatibility with stated company values and working style
  • Career Alignment (0-100): Progression toward candidate's stated long-term goals
  • Location & Logistics: Pass/Fail/Flag verdict (unweighted but can veto shortlisting)

Unlike the weighted dimensions, Location & Logistics acts as a final veto gate rather than contributing to the numeric score.

How Dimension Scores Are Calculated and Weighted

According to the specification in 04-job-evaluation.md, raw dimension scores combine using fixed weights to produce an overall fit percentage.

The Weighting Formula

The framework applies these predetermined weights:

  • Technical: 30%
  • Experience: 25%
  • Behavioral: 15%
  • Career: 30%

The calculation follows this arithmetic:


Overall Score = (Technical × 0.30) + (Experience × 0.25) + (Behavioral × 0.15) + (Career × 0.30)

Fit Band Classification

The weighted total maps to discrete fit bands that determine triage priority:

  • Strong Fit: ≥ 75 points
  • Good Fit: 60-74 points
  • Moderate Fit: 45-59 points
  • Weak Fit: 30-44 points
  • Poor Fit: < 30 points

Only postings achieving "Strong Fit" or "Good Fit" typically receive application drafts, while "Moderate Fit" jobs may be flagged for secondary review.

The /rank Command Execution Flow

The .claude/commands/rank.md file orchestrates the complete evaluation pipeline through seven discrete steps:

  1. Parse Input: Determines which jobs to rank (defaults to new unprocessed postings)
  2. Load State: Reads job_scraper/seen_jobs.json and exclusion tracker job_search_tracker.csv to avoid re-evaluating previously processed or applied positions
  3. Load Specifications: Imports the evaluation rubric from 04-job-evaluation.md and candidate data from 01-candidate-profile.md
  4. Parallel Scoring: Dispatches agents to fetch each posting URL, apply gates, extract relevant sections, and return JSON with dimension scores
  5. Aggregation: Computes weighted scores, applies fit bands, enforces location/language vetoes, and adds urgency markers (🔥) for deadlines under 7 days
  6. Persistence: Writes enriched data back to seen_jobs.json with fields including rank_score, rank_verdict, location_verdict, language_gate, strengths, and gaps
  7. Presentation: Generates a markdown table showing shortlists, closing-soon opportunities, and excluded entries with clickable URLs

All calculations remain pure data transforms with no external API calls, ensuring cheap, repeatable execution.

Implementation Example: Scoring Logic in Python

The core arithmetic implemented by the /rank command follows this deterministic pattern:

import json
from pathlib import Path

# Load static weights and thresholds from the evaluation spec

WEIGHTS = {
    "technical": 0.30,
    "experience": 0.25,
    "behavioral": 0.15,
    "career": 0.30,
}

THRESHOLDS = {
    "strong": 75,
    "good": (60, 74),
    "moderate": (45, 59),
    "weak": (30, 44),
    "poor": (0, 29),
}

def weighted_score(dim_scores: dict) -> float:
    """Combine dimension scores using fixed weights from 04-job-evaluation.md."""
    return sum(dim_scores[d] * WEIGHTS[d] for d in WEIGHTS)

def fit_band(overall: float) -> str:
    """Map overall score to fit band for triage decisions."""
    if overall >= THRESHOLDS["strong"]:
        return "Strong Fit"
    lo, hi = THRESHOLDS["good"]
    if lo <= overall <= hi:
        return "Good Fit"
    lo, hi = THRESHOLDS["moderate"]
    if lo <= overall <= hi:
        return "Moderate Fit"
    lo, hi = THRESHOLDS["weak"]
    if lo <= overall <= hi:
        return "Weak Fit"
    return "Poor Fit"

# Example evaluation workflow

dim_scores = {
    "technical": 78,
    "experience": 65,
    "behavioral": 70,
    "career": 80,
}

overall = weighted_score(dim_scores)
band = fit_band(overall)

print(f"Score: {overall:.0f}/100 → {band}")

# Output: Score: 77/100 → Strong Fit

The gate-checking logic operates separately before scoring:

def passes_gates(job: dict) -> bool:
    """Verify hard-stop gates before calculating fit scores."""
    if job.get("eligibility_gate") == "FAIL":
        return False
    if job.get("language_gate") == "FAIL":
        return False
    return True

Summary

  • The framework uses a deterministic two-phase pipeline: hard-stop gates first, then weighted dimension scoring.
  • Five dimensions (Technical, Experience, Behavioral, Career, Location) evaluate job fit, with four contributing to a weighted 0-100 score.
  • Fixed weights (30% Technical, 25% Experience, 15% Behavioral, 30% Career) ensure consistent evaluation across all postings.
  • The /rank command in .claude/commands/rank.md orchestrates parallel agent processing, aggregation, and persistence to seen_jobs.json.
  • Pure data transforms ensure the scoring pipeline operates without external API dependencies, making job fit evaluation fast, cheap, and reproducible.

Frequently Asked Questions

How does the framework handle jobs that require security clearance or specific citizenship?

The eligibility gate defined in .claude/skills/job-application-assistant/04-job-evaluation.md immediately returns FAIL for postings requiring citizenship, permanent residency, or security clearances that the candidate lacks. These jobs are excluded from scoring and receive no application draft, functioning as a hard filter before any quantitative analysis begins.

What happens if a job posting is in a language the candidate doesn't speak?

The language gate checks against the candidate's declared languages in 01-candidate-profile.md. If the posting requires an undeclared language, it receives a FAIL verdict and is excluded entirely. If the match is uncertain, it receives a FLAG status—keeping the job in consideration but marking it for manual review in the final output table.

Can I adjust the weights for different types of job searches?

The weights are hard-coded in the specification file (04-job-evaluation.md) as Technical 30%, Experience 25%, Behavioral 15%, and Career 30%. To modify these, you would edit the WEIGHTS dictionary in the evaluation specification and ensure the /rank command loads the updated values when processing seen_jobs.json.

Where does the framework store the final job fit scores?

After evaluation, the framework enriches job_scraper/seen_jobs.json with structured fields including rank_score (the weighted 0-100 total), rank_verdict (the fit band classification), location_verdict, language_gate, plus arrays of strengths and gaps extracted during agent analysis. This file serves as the persistent state for tracking which jobs to pursue.

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 →