# Job Search Tracker CSV Format and Application Archiving Explained

> Understand the job search tracker CSV format and application archiving in MadsLorentzen/ai-job-search. Learn how to organize your job applications effectively.

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

---

**The AI Job Search repository maintains a single `job_search_tracker.csv` file with nine standardized columns to track every application, while archiving supporting documents in structured folders under `documents/applications/`.**

The `MadsLorentzen/ai-job-search` repository provides a complete workflow for managing job applications programmatically. Understanding the **job search tracker CSV format** is essential because this file serves as the central database that powers the scraping, ranking, and reporting commands. The system enforces a strict relationship between the CSV rows and the archive folder structure to ensure data consistency across all operations.

## CSV Schema and Column Definitions

The tracker follows a rigid schema defined in [`.claude/commands/outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/outcome.md). When the file does not exist, the `/outcome` command initializes it with a specific header row.

### Standard Header Structure

The CSV uses a comma-separated format with the following column order:

- date
- company
- role
- sector
- channel
- status
- notes
- url
- funnel

### Column Descriptions

Each column serves a distinct purpose in the application lifecycle:

- **date** — ISO-8601 date stamp (YYYY-MM-DD) indicating when the application was first recorded.
- **company** — Normalized company name in lowercase with spaces converted to underscores; this value directly determines the archive folder name.
- **role** — Job title in normalized format (lowercase, underscores), also used in the archive path.
- **sector** — Industry category such as `tech` or `finance` for filtering and reporting.
- **channel** — Source platform (`linkedin`, `jobnet`, etc.) tracking where opportunities originate.
- **status** — Current state of the application: `in_progress`, `interview_only`, `hired`, `rejected`, `no_response`, or `offer_declined`.
- **notes** — Append-only field containing pipe-separated timestamped updates from commands like `/gmail-sync`.
- **url** — Direct link to the original job posting for reference and deduplication.
- **funnel** — Numeric stage identifier used by the HTML report generator to visualize conversion rates.

## Application Archive Folder Structure

Every row in the CSV corresponds to a dedicated archive folder that stores the full application context. This structure is documented in [`documents/README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/documents/README.md) and enforced by the `/outcome` command.

### Naming Convention

Archive folders reside under `documents/applications/` and follow a strict naming pattern:

```

documents/applications/<company>_<role>/

```

Both `<company>` and `<role>` use the normalized values from the CSV (lowercase, spaces replaced with underscores). This deterministic mapping allows commands to locate archive materials programmatically using only the CSV row data.

### Archive Contents

Each application folder contains standardized files:

- **posting.md** — Raw text of the job description scraped by the `/scrape` command.
- **cv.pdf** — The curriculum vitae submitted for this specific role.
- **cover_letter.pdf** — Customized cover letter associated with the application.
- **outcome.md** — Detailed markdown record of the application journey, including interview stages and final results, formatted according to the template in [`.claude/commands/outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/outcome.md).

## How the Outcome Command Updates the System

The `/outcome` command serves as the primary interface for adding new applications. According to the implementation in [`.claude/commands/outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/outcome.md), the command performs three atomic operations:

1. **Creates** the archive folder at `documents/applications/<company>_<role>/` if it does not exist.
2. **Generates** an [`outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/outcome.md) file using a predefined template that includes sections for status tracking and notes.
3. **Appends** a new row to `job_search_tracker.csv` with the current date and provided metadata.

When updating existing applications (for example, via `/gmail-sync`), the system appends timestamped entries to both the `notes` column in the CSV and the `## Notes` section of the corresponding [`outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/outcome.md) file. This append-only approach maintains a complete audit trail without overwriting historical data.

## Integration with Other Commands

The CSV and archive system acts as the backbone for the entire workflow:

- **`/scrape`** and **`/rank`** query `job_search_tracker.csv` first to exclude companies already in the pipeline, preventing duplicate applications.
- **`/html-report`** (documented in [`.claude/commands/html-report.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/html-report.md)) parses every CSV row and merges it with archive data to generate visual dashboards and searchable tables.
- **`/setup`** (described in [`.claude/commands/setup.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/setup.md)) extracts historical lessons from [`outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/outcome.md) files to calibrate the scoring algorithm, relying on the CSV-archive relationship to correlate outcomes with original postings.

## Programmatic Access to the Tracker

You can interact with the CSV directly using Python's standard library to add entries while maintaining the required folder structure:

```python
import csv
from datetime import date
from pathlib import Path

def add_application(company: str, role: str, url: str, channel: str = "linkedin"):
    """Append a new application to the tracker and create its archive."""
    csv_path = Path("job_search_tracker.csv")
    fieldnames = ["date", "company", "role", "sector", "channel", 
                  "status", "notes", "url", "funnel"]
    
    # Normalize names for CSV and folder naming

    norm_company = company.lower().replace(" ", "_")
    norm_role = role.lower().replace(" ", "_")
    
    row = {
        "date": date.today().isoformat(),
        "company": norm_company,
        "role": norm_role,
        "sector": "tech",
        "channel": channel,
        "status": "in_progress",
        "notes": "",
        "url": url,
        "funnel": "1"
    }
    
    # Append to CSV

    file_exists = csv_path.exists()
    with open(csv_path, "a", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        if not file_exists:
            writer.writeheader()
        writer.writerow(row)
    
    # Create archive folder structure

    archive_dir = Path(f"documents/applications/{norm_company}_{norm_role}")
    archive_dir.mkdir(parents=True, exist_ok=True)
    (archive_dir / "outcome.md").touch()
    print(f"Created archive at: {archive_dir}")

```

You can also create the archive structure manually using bash:

```bash

# Create archive folder for a specific application

mkdir -p documents/applications/examplecorp_senior_data_scientist
touch documents/applications/examplecorp_senior_data_scientist/outcome.md
touch documents/applications/examplecorp_senior_data_scientist/posting.md

```

## Summary

- The **job search tracker CSV** uses nine columns (`date`, `company`, `role`, `sector`, `channel`, `status`, `notes`, `url`, `funnel`) to record application metadata.
- **Archive folders** under `documents/applications/<company>_<role>/` store [`posting.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/posting.md), CVs, cover letters, and [`outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/outcome.md) for each application.
- The **`/outcome`** command creates both the CSV row and the archive folder, ensuring synchronized data.
- Commands like **`/scrape`**, **`/rank`**, **`/html-report`**, and **`/setup`** depend on this relationship for deduplication, reporting, and calibration.
- The append-only design of the `notes` field and [`outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/outcome.md) preserves complete application histories.

## Frequently Asked Questions

### What columns are required in the job search tracker CSV?

The CSV requires nine specific columns defined in [`.claude/commands/outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/outcome.md): `date`, `company`, `role`, `sector`, `channel`, `status`, `notes`, `url`, and `funnel`. The `company` and `role` fields are particularly critical because they determine the archive folder naming convention.

### How does the archive folder naming work?

The system generates folder names by combining the normalized `company` and `role` values from the CSV, separated by an underscore. Both values are converted to lowercase with spaces replaced by underscores, creating paths like `documents/applications/examplecorp_senior_data_scientist/`.

### Can I manually edit the CSV without breaking the system?

Manual edits are possible but risky. The commands expect the `company` and `role` fields to match the archive folder names exactly. If you modify these values in the CSV without renaming the corresponding folders in `documents/applications/`, commands like `/html-report` and `/gmail-sync` will fail to locate the associated [`outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/outcome.md) files.

### How does the HTML report use the tracker data?

The `/html-report` command reads every row from `job_search_tracker.csv` and correlates it with the [`outcome.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/outcome.md) files in the archive folders. This merge operation enables the generation of funnel visualizations, status breakdowns, and searchable application tables, as implemented in [`.claude/commands/html-report.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/html-report.md).