# Language Gate Criteria and Persistence in the AI Job Search Framework

> Understand Language Gate criteria in the AI Job Search framework. Learn how PASS FAIL or FLAG verdicts are persisted to track job eligibility efficiently.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: internals
- Published: 2026-08-26

---

**The Language Gate evaluates job postings against your declared language skills in the MadsLorentzen/ai-job-search framework, producing PASS, FAIL, or FLAG verdicts that are persisted as `language_gate` and `language_note` fields in [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) to track eligibility across the workflow.**

The Language Gate operates as a pre-scoring eligibility filter that runs before any skill-based evaluation occurs. According to the `ai-job-search` repository, it checks job posting requirements against your *Languages* table defined in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md) to prevent wasting time on positions requiring undeclared languages or higher proficiency levels than you possess.

## What Is the Language Gate?

The Language Gate is implemented in [`.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) as a structured eligibility check that executes prior to the five main scoring dimensions. Its sole purpose is to validate that you meet the linguistic requirements stated in a job posting before the framework calculates skill fit scores or generates application materials.

This gate references your candidate profile—specifically the *Languages* section in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md)—to determine whether you have declared the required languages and whether your proficiency level meets or exceeds the posting's requirements.

## Language Gate Evaluation Criteria

The gate produces three distinct verdicts based on how your declared language skills compare to the job requirements.

### FAIL Verdict (Required Language Not Declared)

When a job posting requires a language that does not appear in your *Languages* table, the gate returns a `FAIL` verdict. According to the logic defined at line 35 of [`04-job-evaluation.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/04-job-evaluation.md), this result treats the posting the same as a location-based FAIL, excluding it entirely from your shortlist regardless of how well your other skills match the position.

### FLAG Verdict (Proficiency Level Mismatch)

If you have declared the required language but the posting demands a higher proficiency level than you possess, the gate returns `FLAG`. The job remains in your shortlist but displays a warning symbol (⚠), and the specific mismatch is stored in the `language_note` field. This allows you to see opportunities where you might still apply if you can demonstrate equivalent competency through other means.

### PASS Verdict (Requirements Met)

When your declared language proficiency meets or exceeds the posting's requirements, or when the posting does not specify language constraints, the gate returns `PASS`. This verdict imposes no veto, allowing the job to proceed immediately to normal scoring across the five evaluation dimensions.

## How Language Gate Results Are Persisted

Persistence of Language Gate results ensures downstream tools can explain inclusion or exclusion decisions and maintain state across workflow sessions.

### The language_gate and language_note Fields

As documented in [`.claude/commands/rank.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/rank.md) at line 53, each evaluated job record contains two critical fields:
- **`language_gate`**: An enumerated string containing `PASS`, `FAIL`, or `FLAG`
- **`language_note`**: A descriptive string explaining the verdict, particularly for FLAG cases (e.g., "German – required fluent, candidate B1")

These fields are written to the persisted job record during the **Step 4** persistence phase, as verified by regression tests in [`tests/test_rank_command.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tests/test_rank_command.py) at line 225.

### Integration Across Workflow Commands

The persistence mechanism operates consistently across three primary commands:

- **`/rank`**: Records the verdict and note in [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) after evaluation
- **`/scrape`**: Surfaces the gate result in the scraper's output table; an undeclared required language forces the entry to be marked **Low** regardless of skill fit, as noted in [`.claude/skills/job-scraper/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/SKILL.md) at line 127
- **`/apply`**: Extracts the posting's required language and feeds it through the same gate logic to ensure consistency

Line 87 of [`.claude/commands/rank.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/rank.md) emphasizes that persisting these fields is critical for debugging and generating "why was this excluded" explanations when reviewing your job history.

## Working with Language Gate Data

You can programmatically inspect Language Gate results from your persisted job data to analyze filtering patterns or audit exclusions.

```python

# Example: Extracting the Language Gate result from a ranked job entry

import json

with open("seen_jobs.json") as f:
    jobs = json.load(f)

for job in jobs:
    gate = job.get("language_gate")      # "PASS" | "FAIL" | "FLAG"

    note = job.get("language_note")      # explanatory text or None

    if gate == "FAIL":
        print(f"Excluded – language requirement not met: {note}")
    elif gate == "FLAG":
        print(f"Flagged – level gap: {note}")

```

When creating entries during the ranking process, the framework structures the data as follows:

```python

# Example: How /rank stores the fields (simplified)

ranked_entry = {
    "status": "ranked",
    "language_gate": "FLAG",
    "language_note": "German – required fluent, candidate B1",
    # … other ranking fields …

}

# The entry is later persisted to `seen_jobs.json`

```

## Key Source Files and Implementation Details

The complete Language Gate implementation spans four critical files in the repository:

| File | Role |
|------|------|
| [`.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) | Defines the Language Gate criteria and execution order before scoring dimensions |
| [`.claude/commands/rank.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/rank.md) | Documents the `language_gate` and `language_note` fields and their impact on ranking |
| [`.claude/skills/job-scraper/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/SKILL.md) | Implements Language Gate integration during scraping, overriding skill fit for undeclared languages |
| [`tests/test_rank_command.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tests/test_rank_command.py) | Contains regression tests verifying correct persistence of gate fields in [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) |

These files collectively demonstrate the full lifecycle—from evaluation logic in [`04-job-evaluation.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/04-job-evaluation.md) to persistence verification in the test suite.

## Summary

- The Language Gate acts as a pre-scoring filter that compares job language requirements against your declared skills in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md).
- Three verdicts exist: **FAIL** (excludes job), **FLAG** (warns but keeps job), and **PASS** (proceeds to scoring).
- Results persist as `language_gate` and `language_note` fields in [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) during Step 4 of the ranking workflow.
- The scraper command uses these fields to mark entries **Low** when required languages are undeclared.
- Downstream debugging and exclusion explanation tools rely on this persistence to justify why specific jobs were filtered from your shortlist.

## Frequently Asked Questions

### What happens when a job requires a language not in my profile?

The Language Gate returns a `FAIL` verdict and excludes the posting from your shortlist entirely, treating it similarly to a location-based exclusion. This prevents you from wasting time on positions you cannot linguistically fulfill, and the exclusion is recorded in [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) with the `language_gate` field set to `FAIL`.

### How is the FLAG verdict different from FAIL?

A `FLAG` verdict indicates you have declared the required language but lack the specific proficiency level demanded (for example, the job requires fluency but you listed B1). Unlike `FAIL`, the job remains in your shortlist with a warning symbol and explanatory text in `language_note`, allowing you to decide whether to apply anyway or improve your skills.

### Where are Language Gate results stored?

The results persist in the [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) file as two fields: `language_gate` containing the verdict string (`PASS`, `FAIL`, or `FLAG`) and `language_note` containing descriptive text about the mismatch. These fields are written during the Step 4 persistence phase of the `/rank` command and are accessible to all downstream tools in the workflow.

### Can I override a Language Gate FAIL verdict?

The framework does not provide a direct override mechanism for Language Gate FAILs within the automated scoring pipeline. However, because the gate runs before skill scoring, you could manually edit [`seen_jobs.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/seen_jobs.json) to change the `language_gate` value or add the required language to your *Languages* table in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md) and re-run the evaluation if you believe the language requirement is negotiable or incorrectly parsed.