# How Candidate Profile Enrichment Works in AI-Job-Search: GitHub, Portfolios & Course Syllabi

> Discover how candidate profile enrichment works using GitHub, portfolios, and syllabi. Learn about the six-step pipeline in the ai-job-search repository for additive updates and traceability.

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

---

**The `/expand` command enriches candidate profiles by harvesting hidden competencies from GitHub repositories, course syllabi, and external portfolios through a six-step pipeline that ensures additive-only updates with full traceability.**

Candidate profile enrichment transforms implicit experience into explicit, searchable skills within the `MadsLorentzen/ai-job-search` repository. By analyzing public code repositories, official learning outcomes, and linked professional portfolios, the system captures technical competencies that traditional CVs often omit. This process operates through a strict, repeatable pipeline defined in [`.claude/commands/expand.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/expand.md).

## The `/expand` Command Architecture

The enrichment workflow follows a six-step pipeline that guarantees no duplicate entries and maintains complete source attribution. Each step is implemented as a discrete phase in the command specification.

### Step 0 – Load Existing Profile

Before harvesting new data, the system reads [`01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/01-candidate-profile.md) and [`02-behavioral-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/02-behavioral-profile.md) to establish a baseline. This prevents duplicate entries and ensures all additions are strictly additive. The command references these files located in `.claude/skills/job-application-assistant/` to understand the current competency landscape.

### Step 1 – Discovery and Scanning

The discovery phase operates through multiple parallel streams to capture comprehensive candidate data.

#### Local Document Parsing (1a-d)

The command first scans local documents including CVs, LinkedIn exports, diplomas, and reference letters. It extracts courses, certifications, projects, responsibilities, and volunteer work to build an initial competency foundation.

#### GitHub Portfolio Analysis (1e)

When a GitHub URL or username is present in the profile, the system executes a deep repository scan. As specified in [`expand.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/expand.md) Step 1e, the command:

- Retrieves the public profile and pinned repositories
- Fetches the README of each repository to extract name, description, languages, topics, and frameworks
- Optionally lists all repositories to capture unpinned work

This process exposes concrete artifacts and tech stacks that candidates may not explicitly list in their CVs.

#### External URL Processing (1f)

For additional URLs such as personal portfolio sites, Kaggle profiles, or Google Scholar pages, the command fetches each page and extracts tools, datasets, awards, or skills. This captures professional activity outside of version control systems.

### Step 2 – Web Enrichment Strategies

For each discovered item from Step 1, the system applies two parallel approaches defined in [`expand.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/expand.md) Step 2:

- **Approach A (Direct lookup):** Searches the web for official syllabi or certification guides using queries like `"[Course name] [Provider] syllabus learning outcomes"`
- **Approach B (Inferred competencies):** Applies reasoning to descriptions to capture domain knowledge, standard tool-chains, and methods

These approaches run simultaneously to maximize coverage while maintaining accuracy.

### Step 3 – Competency Map Construction

The system deduplicates and categorizes findings into a structured competency map. Each entry is classified into one of five categories: *Technical Primary*, *Technical Secondary*, *Domain Knowledge*, *Methods & Practices*, or *Soft/Behavioral*.

Every competency stores three critical metadata fields:
- Competency name
- Source item (e.g., "Coursera – Deep Learning Specialisation")
- Origin flag (A, B, or both)

### Steps 4-6 – Confirmation and Persistence

The pipeline concludes with human-in-the-loop verification:

1. **Step 4 (Present grouped summary):** Displays a markdown report of new competencies grouped by source, requesting confirmation via `all`, `review`, `skip`, or custom selections
2. **Step 5 (Write confirmed additions):** Appends selected competencies to [`01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/01-candidate-profile.md) or [`02-behavioral-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/02-behavioral-profile.md) with source annotations (e.g., *(GitHub — my-repo)*)
3. **Step 6 (Summary report):** Prints a completion report listing additions, skips, and manual review items

## Technical Implementation Details

The enrichment logic relies on specific fetching and parsing mechanisms for external data sources.

### Fetching GitHub Repository Data

The system interacts with the GitHub API to retrieve repository metadata and content. Below is a Python implementation matching the logic found in the `/expand` workflow:

```python
import requests
from urllib.parse import urljoin

def fetch_readme(username: str, repo: str) -> str:
    """Return the raw README markdown for a public repo."""
    api_url = f"https://api.github.com/repos/{username}/{repo}/readme"
    headers = {"Accept": "application/vnd.github.v3.raw"}
    resp = requests.get(api_url, headers=headers, timeout=10)
    resp.raise_for_status()
    return resp.text

```

This function uses the public GitHub API (no authentication required for public repositories) to fetch raw README content. The returned text is subsequently parsed for languages, topics, and framework mentions to identify technical competencies.

### Syllabus Lookup and Parsing

For course syllabi, the system implements web search and HTML parsing to extract official learning outcomes:

```python
import requests
from bs4 import BeautifulSoup

def lookup_syllabus(course_name: str, provider: str) -> list[str]:
    """Search Google for the official syllabus and extract bullet-point competencies."""
    query = f"{course_name} {provider} syllabus learning outcomes"
    search_url = f"https://www.google.com/search?q={requests.utils.quote(query)}"
    html = requests.get(search_url, headers={"User-Agent": "Mozilla/5.0"}).text
    soup = BeautifulSoup(html, "html.parser")
    # Grab the first result that looks like a .pdf or .html syllabus page

    for a in soup.select("a"):
        href = a.get("href")
        if "syllabus" in href.lower():
            page = requests.get(href, timeout=10).text
            page_soup = BeautifulSoup(page, "html.parser")
            # Very naive extraction: list items under <ul>/<ol>

            bullets = [li.get_text(strip=True) for li in page_soup.select("ul li, ol li")]
            return bullets
    return []

```

This approach mimics the "Approach A" direct lookup strategy, pulling authoritative competency lists directly from official syllabus pages rather than relying on candidate self-description.

## Data Integrity and Traceability

The candidate profile enrichment system maintains strict data governance through additive-only updates and source attribution. Every competency added to [`01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/01-candidate-profile.md) or [`02-behavioral-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/02-behavioral-profile.md) includes a parenthetical source reference, creating an audit trail from the original GitHub repository or course syllabus to the final profile entry.

This traceability ensures that downstream job-fit scoring and tailored application generation rely on verified, attributable skills rather than inferred or unverified claims.

## Summary

- **The `/expand` command** orchestrates candidate profile enrichment through a six-step pipeline defined in [`.claude/commands/expand.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/expand.md)
- **GitHub portfolios** provide concrete evidence of technical skills through repository READMEs, languages, and topics
- **Course syllabi** offer authoritative competency lists via direct web lookup of official learning outcomes
- **Additive-only updates** ensure no data is overwritten, with full source attribution maintained in [`01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/01-candidate-profile.md) and [`02-behavioral-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/02-behavioral-profile.md)
- **Human confirmation** gates all write operations, presenting grouped summaries before modifying profile files

## Frequently Asked Questions

### How does the system handle private GitHub repositories?

The enrichment process only accesses **public repositories** through the GitHub API. Private repositories remain inaccessible unless explicitly authenticated, which the current implementation in [`expand.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/expand.md) does not require. Candidates must ensure relevant repositories are public to be included in the competency scan.

### What happens if a course syllabus cannot be found online?

When Approach A (direct lookup) fails to locate an official syllabus, the system relies on **Approach B (inferred competencies)**. This method analyzes the course description, title, and provider to deduce likely skills and knowledge areas. All inferred competencies are flagged with origin "B" to distinguish them from directly verified sources.

### Can the enrichment process remove or modify existing competencies?

No. The `/expand` command operates on an **additive-only basis**. Step 0 explicitly loads existing profiles to prevent duplicates, and Step 5 only appends new entries. Existing competencies remain untouched to preserve manually curated information and maintain data integrity.

### Which file stores the enriched technical competencies?

Technical competencies are appended to [`.claude/skills/job-application-assistant/01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-application-assistant/01-candidate-profile.md), while behavioral and soft skills are stored in [`02-behavioral-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/02-behavioral-profile.md). Both files use markdown format with source annotations for traceability.