How the Language Gate Filters Job Postings in the AI-Job-Search Repository

The language gate is a deal-breaker rule that filters job postings based on the candidate's declared language capabilities, assigning each posting a PASS, FLAG, or FAIL verdict during the /rank command.

The language gate ensures that job postings match the candidate's actual language skills as defined in the Languages table of CLAUDE.md. This prevents wasted time on positions requiring fluency in languages the candidate does not speak or where their proficiency falls short of requirements. According to the MadsLorentzen/ai-job-search source code, the gate operates as a structured evaluation layer within the broader job scoring framework.

Where the Language Gate Is Defined

The core logic for the language gate resides in .claude/skills/job-application-assistant/04-job-evaluation.md. This file establishes:

  • The candidate's Languages table as the authoritative data source
  • Comparison rules between required and declared proficiency levels
  • The three-verdict classification system (PASS, FLAG, FAIL)

The gate functions as one of several "deal-breaker" evaluations alongside location and other filters, but it applies distinct logic based on partial matches rather than binary inclusion.

How the Language Gate Verdict Works

When the /rank command executes Step 2 (batch-fetch and score), each job posting receives a language_gate field with one of three possible values:

Verdict When Applied Effect on Results
PASS No language required, OR required proficiency ≤ candidate's declared level Job proceeds normally; language_gate omitted or null
FLAG Required language is declared, BUT required proficiency exceeds candidate's level Job retained with ⚠ marker and language_note displayed
FAIL Required language not declared at all Job excluded from shortlist, listed under "Excluded"

The language_note field accompanies FLAG and FAIL verdicts, quoting the specific requirement that triggered the mismatch. This transparency allows candidates to understand why a posting was demoted or removed.

Implementation Flow in the /rank Command

The orchestration logic in .claude/commands/rank.md implements the gate through four discrete steps:

  1. Load State – Import the scoring framework from 04-job-evaluation.md and candidate profile data
  2. Batch-Fetch and Score – For each posting, the scoring agent identifies language requirements and compares against declared capabilities, emitting JSON with language_gate and language_note
  3. Aggregate and Rank – Apply veto logic: FAIL entries removed, FLAG entries highlighted, PASS entries unchanged
  4. Persist – Write language_gate and language_note back to job_scraper/seen_jobs.json for reproducibility in re-rankings

This persistence layer ensures that --all re-ranking commands can reproduce identical decisions without re-invoking the scoring agent.

Code Examples

Scoring Agent Output

The scoring agent produces structured JSON for each evaluated posting:

{
  "key": "12345",
  "status": "scored",
  "scores": { "technical": 78, "experience": 65, "behavioral": 55, "career": 72 },
  "location": "PASS",
  "language_gate": "FLAG",
  "language_note": "Requires German fluency (candidate level: B1)",
  "deadline": null,
  "strengths": ["Strong Python experience", "Relevant fintech background"],
  "gaps": ["German fluency needed"],
  "language": "en"
}

Veto Application Logic

The ranking step implements gate enforcement through conditional branching:

def apply_language_gate(job):
    gate = job["language_gate"]
    note = job.get("language_note")
    if gate == "FAIL":
        # Exclude from shortlist, add to excluded list

        excluded.append((job, f"language FAIL: {note}"))
        return None
    if gate == "FLAG":
        # Keep, but mark with warning

        job["flags"].append("⚠ language")
        job["notes"].append(note)
    # PASS – nothing to modify

    return job

Shortlist Display Format

Flagged postings appear with visual indicators in the ranked output:


- Senior Backend Engineer at FinTech SA – 78 – Strong Fit – ⚠ – language NOTE: Requires German fluency (candidate level: B1)

Test Coverage

The regression test suite in tests/test_rank_command.py validates gate behavior through four specific assertions:

  • Schema includes language_gate and language_note fields
  • Verdict set matches location veto set (PASS/FAIL/FLAG)
  • FAIL verdicts trigger proper exclusion documentation
  • Persistence into seen_jobs.json functions correctly

These guards prevent regression as the evaluation framework evolves.

Key Implementation Files

File Purpose
.claude/skills/job-application-assistant/04-job-evaluation.md Gate definition and Languages table reference
.claude/commands/rank.md /rank command orchestration and veto logic
tests/test_rank_command.py Regression tests for gate behavior
job_scraper/seen_jobs.json Runtime storage of gate results

Summary

  • The language gate evaluates job postings against candidate-declared language capabilities during the /rank command
  • Three verdicts exist: PASS (proceed), FLAG (warn but include), and FAIL (exclude entirely)
  • Gate logic is defined in 04-job-evaluation.md and orchestrated through rank.md
  • Results persist in seen_jobs.json to enable reproducible re-rankings
  • Test coverage in test_rank_command.py ensures behavioral stability

Frequently Asked Questions

What happens if a job posting doesn't specify any language requirement?

The language gate assigns PASS automatically. Since no constraint exists to violate, the posting proceeds through ranking without any language-related annotation or flag.

Can I override a FAIL verdict and still view the excluded posting?

The source implementation does not provide an override mechanism within the standard /rank workflow. Excluded postings appear only in the "Excluded" section with explanatory language_note text. Modifying this would require changes to the veto logic in rank.md.

How does the gate handle proficiency levels like "fluent" versus "conversational"?

The comparison logic in 04-job-evaluation.md maps declared proficiency levels against requirements. When the candidate's level meets or exceeds the requirement, PASS results. When the candidate has the language but at insufficient proficiency, FLAG applies. The specific mapping of named levels (B1, fluent, conversational) depends on the standardized schema in the Languages table.

Why are FLAG and FAIL treated differently?

FLAG indicates a surmountable gap—the candidate possesses foundational capability in the required language and could potentially improve. FAIL indicates complete absence of the language from the candidate's profile, making the mismatch fundamental rather than incremental. This distinction allows candidates to prioritize applications where language acquisition might close the gap.

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 →