How the AI Job Search Framework Evaluates Job Fit Against Scoring Dimensions
The AI Job Search Framework evaluates job fit through a three-stage pipeline that applies hard-filter eligibility gates, scores postings across five weighted dimensions, and aggregates results into actionable verdict bands ranging from "Strong Fit" to "Poor Fit."
The MadsLorentzen/ai-job-search repository implements a transparent, reproducible evaluation system that separates mandatory exclusion criteria from quantitative fit assessment. This article breaks down exactly how the framework uses defined scoring dimensions to transform raw job postings into ranked, actionable opportunities.
The Three-Stage Evaluation Pipeline
The evaluation logic is defined in .claude/skills/job-application-assistant/04-job-evaluation.md and executed through the /rank command (.claude/commands/rank.md). The architecture strictly separates binary exclusion filters from numeric scoring to ensure only viable candidates enter the weighted calculation.
Stage 1: Pre-Scoring Eligibility Gates
Before any numeric analysis occurs, the framework applies Eligibility and Language gates. These act as binary filters:
- Eligibility Gate: Validates mandatory requirements (citizenship, visa status, years of experience).
- Language Gate: Confirms language proficiency matches posting requirements.
If a posting fails either gate, the framework immediately excludes it from ranking. The veto reason persists in job_scraper/seen_jobs.json under language_note or eligibility_note fields for auditability.
Stage 2: The Five Scoring Dimensions
Postings that pass the gates receive numeric scores across five dimensions. Four dimensions use a 0-100 scale weighted toward the final score, while Location operates as a hard veto:
| Dimension | Scale | Weight | Description |
|---|---|---|---|
| Technical Skills Match | 0-100 | 30% | Alignment between required/preferred skills and candidate capabilities |
| Experience Match | 0-100 | 25% | Functional requirement alignment with candidate's work history |
| Behavioral / Culture Fit | 0-100 | 15% | Compatibility with organizational culture and team dynamics |
| Career Alignment & Motivation | 0-100 | 30% | Role's potential to advance candidate's long-term career goals |
| Location & Logistics | PASS/FAIL/FLAG | Veto only | Hard filter for relocation requirements; excluded from weighted calculation |
The specific meaning of score buckets (80-100, 60-79, etc.) is rigidly defined in 04-job-evaluation.md to ensure consistent interpretation across scoring agents.
Stage 3: Aggregation and Verdict Assignment
The framework calculates a weighted overall score using the formula:
overall = (
scores["technical"] * 0.30 +
scores["experience"] * 0.25 +
scores["behavioral"] * 0.15 +
scores["career"] * 0.30
)
This aggregates into five verdict bands:
- Strong Fit (≥75): Apply outright
- Good Fit (60-74): Apply with gap-addressing notes
- Moderate Fit (45-59): Consider; discuss with user
- Weak Fit (30-44): Usually skip unless strategic
- Poor Fit (<30): Skip
Any FAIL verdict from Location & Logistics removes the job from the shortlist regardless of other scores.
Technical Implementation in the Codebase
The /rank command orchestrates this evaluation by dispatching agents to apply the rubric to each posting. Agents return structured data that the command persists and displays.
Scoring Agent Output Format
When an agent evaluates a posting, it returns JSON matching this schema (example from the implementation):
{
"key": "job-12345",
"status": "scored",
"scores": {
"technical": 78,
"experience": 85,
"behavioral": 70,
"career": 65
},
"location_verdict": "PASS",
"language_gate": "FLAG",
"language_note": "Posting requires fluent Polish – candidate lists Polish at Basic",
"strengths": [
"Strong Python & ML experience matches required skills"
],
"gaps": [
"Missing exposure to cloud-native CI/CD tools"
]
}
Weighted Calculation Logic
The explicit aggregation implementation from .claude/commands/rank.md follows this Python logic:
weights = {"technical": 0.30, "experience": 0.25,
"behavioral": 0.15, "career": 0.30}
def overall_score(scores):
return sum(scores[dim] * weights[dim] for dim in weights)
# Example calculation
scores = {"technical": 78, "experience": 85,
"behavioral": 70, "career": 65}
print(overall_score(scores)) # → 73.5 → "Good Fit"
The command then maps the float to the verdict bands using threshold comparisons identical to the pseudocode above.
Architectural Design Principles
Single Source of Truth
All gate definitions, dimension descriptions, weighting schemes, and verdict thresholds live in 04-job-evaluation.md. The /rank command loads this file once at execution start and passes the rubric to all scoring agents, preventing drift or inconsistency across evaluations.
Agent-Driven Triage
Scoring agents receive only the posting text (fetched via WebFetch) and the compact rubric. They perform no external research—salary benchmarking and company-wide culture checks are explicitly reserved for the subsequent /apply workflow to keep the triage step lightweight.
Persistent Audit Trail
After evaluation, the framework writes gate verdicts (location_verdict, language_gate), numeric scores, and identified gaps back into job_scraper/seen_jobs.json. This persistence enables downstream commands (/apply, /outcome) to reference the original scoring rationale without recomputing.
Threshold-Driven Presentation
The /rank command partitions results into Shortlisted (passing all gates) and Excluded (failing gates or expired) sections. Shortlisted jobs display their weighted score, verdict band, and any FLAG markers (e.g., heavy travel warnings) so users can quickly prioritize applications.
Summary
- The framework evaluates job fit through three distinct stages: eligibility gates, weighted dimension scoring, and verdict aggregation.
- Technical Skills Match (30%) and Career Alignment (30%) carry the highest weights, while Behavioral Fit contributes 15%.
- Location & Logistics operates as a hard veto (FAIL) or warning (FLAG) rather than entering the weighted calculation.
- The
/rankcommand in.claude/commands/rank.mdimplements the aggregation logic and persists all outputs tojob_scraper/seen_jobs.json. - Verdict bands translate numeric scores into actionable decisions: Strong Fit (≥75) triggers immediate application, while Poor Fit (<30) results in automatic exclusion.
Frequently Asked Questions
What happens if a job fails the Language Gate?
The posting is immediately excluded from the ranked shortlist. The framework persists the specific failure reason—such as "Posting requires fluent Polish – candidate lists Polish at Basic"—to the language_note field in job_scraper/seen_jobs.json. This allows users to review exclusion criteria and identify skill gaps or language learning priorities.
How does the framework handle remote versus on-site requirements?
The Location & Logistics dimension evaluates relocation support and travel intensity. Fully remote positions receive a PASS and proceed to scoring. Mandatory relocation without sponsorship receives a FAIL, triggering immediate removal from consideration. Heavy travel requirements receive a FLAG, meaning the job remains in results but displays a visible warning to the user.
Can the scoring weights be customized?
According to the source code in .claude/skills/job-application-assistant/04-job-evaluation.md, the weights are hardcoded as Technical (30%), Experience (25%), Behavioral (15%), and Career (30%). To modify these percentages, users must edit both the framework definition file and the aggregation logic in .claude/commands/rank.md where the calculation is explicitly implemented.
Where does the scoring data persist after evaluation?
The /rank command writes all evaluation outputs—including numeric dimension scores, gate verdicts (location_verdict, language_gate), identified strengths/gaps, and final verdict classifications—to job_scraper/seen_jobs.json. This JSON file serves as the persistent datastore that subsequent commands like /apply and /outcome reference when generating tailored applications or tracking interview outcomes.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →