# AI Job Search Framework Architecture: A Deep Dive into the Thin-Pointer Design

> Explore the AI Job Search framework architecture featuring a thin-pointer design. Understand its four layers: candidate profile, workflow specs, portal skills, and core utilities.

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

---

**The AI Job Search framework uses a thin-pointer architecture built around four layers—a canonical candidate profile, markdown-driven workflow specifications, modular portal-specific skills, and reusable core utilities.**

This open-source automation system, maintained at `MadsLorentzen/ai-job-search`, eliminates configuration drift by making a single source of truth the driver for all agents, commands, and data. Every component reads from centralized specifications rather than maintaining independent state.

## Four Core Architectural Layers

### 1. Candidate Profile (Canonical Data) — [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md) and Skill Files

The foundation of the framework is **集中式数据存储**. All agent runtimes load candidate information from one location.

- **[`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md)** — Master file containing personal details, education, preferences, and CV information
- **`.claude/skills/job-application-assistant/`** — Directory of skill-specific supplements (e.g., `01-*.md` files)

This design prevents duplication. When a candidate updates their profile in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md), every downstream agent automatically uses the refreshed data.

### 2. Workflow Specification (Orchestration) — `.claude/commands/`

High-level processes are defined as **pure markdown specifications** rather than hardcoded logic.

| Command File | Purpose |
|-------------|---------|
| [`.claude/commands/setup.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/setup.md) | Environment initialization |
| [`.claude/commands/rank.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/rank.md) | Job ranking workflow |
| [`.claude/commands/apply.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/apply.md) | Application submission |
| [`.claude/commands/scrape.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/scrape.md) | Data collection |
| [`.claude/commands/upskill.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/upskill.md) | Interview preparation |

A generic runner interprets these markdown files and stitches together the required tools. Non-programmers can edit workflows without touching source code.

### 3. Portal-Specific Skills (Plug-ins) — `.agents/skills/`

Each job portal operates as a **self-contained skill module**:

```

.agents/skills/
├── linkedin-search/
│   ├── SKILL.md          # Markdown description

│   └── cli/              # TypeScript implementation

├── jobnet-search/
├── freehire-search/
└── [new-portal]/         # Auto-discovered on commit

```

The **LinkedIn skill** demonstrates the pattern:
- **[`.agents/skills/linkedin-search/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/SKILL.md)** — Defines required CLI flags (`--query`, `--location`), output format (JSON), and rate-limit handling
- **`.agents/skills/linkedin-search/cli/`** — Houses the TypeScript entry point and package configuration

The core runner automatically discovers new skill folders. Adding a portal requires zero changes to the orchestration layer.

### 4. Core Utilities and Test Suite — `tools/` and `tests/`

**Reusable components** accessible across all commands:

| Tool | Function |
|------|----------|
| [`tools/verify_pdf.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/verify_pdf.py) | LaTeX source validation for cover letters |
| [`tools/salary_lookup.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/salary_lookup.py) | Region-adjusted compensation data |
| [`tools/rank.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/rank.py) | Ranking algorithm implementation |

The **`tests/`** directory contains 30+ unit and integration test files. Example: [`tests/test_rank_command.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tests/test_rank_command.py) validates the ranking workflow end-to-end.

## Key Architectural Principles

- **Single Source of Truth** — All specifications live in `.claude/`; all candidate data in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md). This eliminates drift between agent instances.
- **Modular Skill Plugins** — Portal logic is encapsulated, making replacement trivial.
- **Command-Driven Workflow** — Markdown specifications allow declarative process definition.
- **Extensive Test Coverage** — Every skill, tool, and command has automated validation.
- **Tool-Centric Utilities** — DRY patterns encourage reuse across commands.

## Code Examples

### Adding a New Portal Skill

Create the folder structure:

```bash
mkdir -p .agents/skills/myportal-search/cli

```

Add [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md):

```markdown

# MyPortal Search Skill

This skill scrapes job listings from MyPortal.

- Required flags: `--query`, `--location`
- Output: JSON array of listings
- Rate limit: 1 request/second

```

The runner detects the new skill on next execution.

### Running the Rank Command

```bash
python -m ai_job_search rank \
  --profile CLAUDE.md \
  --input ./job_scraper/results.json \
  --output ./ranked_jobs.json

```

The `rank` command loads the candidate profile from [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md), applies the algorithm in [`tools/rank.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/rank.py), and outputs the ordered list.

### Verifying a PDF Resume

```python
from tools.verify_pdf import verify_pdf

result = verify_pdf("cover_letters/cover_example.tex")
print(result)

# → {"valid": true, "issues": []}

```

The `verify_pdf` function parses LaTeX source, checks for required sections, and returns a JSON-compatible report.

### Looking Up Salary Data

```python
from salary_lookup import lookup_salary

salary = lookup_salary(company="Acme Corp", title="Software Engineer")
print(f"Estimated salary: ${salary:,.0f}")

```

[`salary_lookup.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/salary_lookup.py) queries cached CSV data and applies regional multipliers.

## Critical Source Files

| Path | Role |
|------|------|
| [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md) | Central candidate profile |
| [`.claude/commands/setup.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/setup.md) | Setup command specification |
| [`.claude/commands/rank.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/rank.md) | Ranking workflow definition |
| [`.agents/skills/linkedin-search/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/SKILL.md) | LinkedIn skill description |
| [`tools/verify_pdf.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/verify_pdf.py) | PDF verification utility |
| [`salary_lookup.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/salary_lookup.py) | Salary lookup helper |
| [`tests/test_rank_command.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tests/test_rank_command.py) | Rank command unit tests |
| [`SETUP.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SETUP.md) | Detailed installation guide |

## Summary

- The **thin-pointer architecture** centralizes all state in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md) and `.claude/` specifications
- **Markdown-driven commands** enable non-programmer workflow customization
- **Self-contained skill modules** allow portal addition without core changes
- **Shared utilities** in `tools/` enforce DRY principles
- **Comprehensive test coverage** in `tests/` ensures reliability as the framework scales

## Frequently Asked Questions

### How does the framework handle new job portals?

Drop a new folder under `.agents/skills/` with a [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) description and CLI implementation. The generic runner auto-discovers the skill on next execution—no orchestration code changes required.

### Where is candidate data stored?

All candidate information lives in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md) at the repository root, supplemented by skill files in `.claude/skills/job-application-assistant/`. This single source of truth prevents synchronization errors across agents.

### Can non-developers modify workflows?

Yes. Workflow steps are defined in markdown files under `.claude/commands/`. Users can edit [`rank.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/rank.md), [`apply.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/apply.md), or other command specifications without writing code—the runner interprets the markdown directly.

### What testing coverage exists?

The `tests/` directory contains over 30 files covering unit and integration scenarios. Every command, skill, and core utility has corresponding test files like [`tests/test_rank_command.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tests/test_rank_command.py).