Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Immermatch Dev",
"image": "mcr.microsoft.com/devcontainers/python:3.13",
"image": "mcr.microsoft.com/devcontainers/python:3.14",
"customizations": {
"codespaces": {
"openFiles": [
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.13"
python-version: "3.14"
cache: pip
- run: pip install -e ".[test]"
- name: Lint (ruff)
Expand All @@ -30,7 +30,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.13"
python-version: "3.14"
cache: pip
- run: pip install -e ".[test]"
- name: Type check (mypy)
Expand All @@ -42,7 +42,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.13"
python-version: "3.14"
cache: pip
- run: pip install -e ".[test]"
- name: Tests
Expand All @@ -54,7 +54,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.13"
python-version: "3.14"
cache: pip
- run: pip install -e ".[test]"
- name: Dependency audit (pip-audit)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/daily-digest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

- uses: actions/setup-python@v7
with:
python-version: "3.13"
python-version: "3.14"
cache: pip

- name: Install dependencies
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.13
3.14
53 changes: 30 additions & 23 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ The search pipeline uses a pluggable `SearchProvider` protocol (defined in `imme
```python
class SearchProvider(Protocol):
name: str

def search(self, query: str, location: str, max_results: int = 50) -> list[JobListing]: ...
```

Expand Down Expand Up @@ -222,7 +223,7 @@ BA_API_KEY = "jobboerse-jobsuche" # pragma: allowlist secret # public API key,
# SerpApi Google Jobs parameters (retained for future non-German markets)
SERPAPI_PARAMS = {
"engine": "google_jobs",
"hl": "en", # Language: English (for broader results)
"hl": "en", # Language: English (for broader results)
"chips": "date_posted:week", # Temporal freshness filter
}
# gl= country code is inferred from location (see _infer_gl())
Expand Down Expand Up @@ -270,37 +271,41 @@ All models use `Field()` with descriptions and defaults where appropriate.

```python
class WorkEntry(BaseModel):
title: str # job title held
company: str # employer name
start_date: str # e.g. "2020-03" or "2020"
end_date: str | None = None # null = current role
duration_months: int | None = None # estimated from dates
skills_used: list[str] = [] # skills exercised in this role
description: str = "" # one-sentence summary
title: str # job title held
company: str # employer name
start_date: str # e.g. "2020-03" or "2020"
end_date: str | None = None # null = current role
duration_months: int | None = None # estimated from dates
skills_used: list[str] = [] # skills exercised in this role
description: str = "" # one-sentence summary


class EducationEntry(BaseModel):
degree: str # e.g. "MSc Computer Science"
institution: str = "" # university / school
degree: str # e.g. "MSc Computer Science"
institution: str = "" # university / school
start_date: str | None = None
end_date: str | None = None # null = still studying
end_date: str | None = None # null = still studying
status: Literal["completed", "in_progress", "dropped"] = "completed"


class CandidateProfile(BaseModel):
skills: list[str] # 15-20 hard skills
skills: list[str] # 15-20 hard skills
experience_level: Literal["Junior", "Mid", "Senior", "Lead", "CTO"]
years_of_experience: int = 0 # calculated from work history
roles: list[str] # 5 titles, EN + local language
languages: list[str] # with proficiency levels
years_of_experience: int = 0 # calculated from work history
roles: list[str] # 5 titles, EN + local language
languages: list[str] # with proficiency levels
domain_expertise: list[str]
certifications: list[str] = [] # empty list if none
education: list[str] = [] # degree + university
summary: str = "" # 2-3 sentence professional summary
work_history: list[WorkEntry] = [] # chronological, most recent first
certifications: list[str] = [] # empty list if none
education: list[str] = [] # degree + university
summary: str = "" # 2-3 sentence professional summary
work_history: list[WorkEntry] = [] # chronological, most recent first
education_history: list[EducationEntry] = [] # with completion status


class ApplyOption(BaseModel):
source: str # e.g., "LinkedIn", "Company Website"
url: str # direct application URL
source: str # e.g., "LinkedIn", "Company Website"
url: str # direct application URL


class JobListing(BaseModel):
title: str
Expand All @@ -309,15 +314,17 @@ class JobListing(BaseModel):
description: str = ""
link: str = ""
posted_at: str = ""
source: str = "" # search provider that produced this listing
source: str = "" # search provider that produced this listing
apply_options: list[ApplyOption] = [] # direct apply links (LinkedIn, career page, etc.)
reliability: Literal["verified", "aggregator", "unverified"] = "unverified" # source trust level


class JobEvaluation(BaseModel):
score: int # 0-100, constrained ge=0 le=100
score: int # 0-100, constrained ge=0 le=100
reasoning: str
missing_skills: list[str] = []


class EvaluatedJob(BaseModel):
job: JobListing
evaluation: JobEvaluation
Expand Down
3 changes: 2 additions & 1 deletion tests/test_pages_unsubscribe.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Uses Streamlit's AppTest framework to run the page script.
"""

from pathlib import Path
from unittest.mock import MagicMock, patch

from streamlit.testing.v1 import AppTest
Expand All @@ -14,7 +15,7 @@
"SUPABASE_SERVICE_KEY": "fake-service-key", # pragma: allowlist secret
}

PAGE_FILE = "immermatch/pages/unsubscribe.py"
PAGE_FILE = str(Path(__file__).resolve().parents[1] / "immermatch" / "pages" / "unsubscribe.py")


def _build_app(token: str | None = None) -> AppTest:
Expand Down
3 changes: 2 additions & 1 deletion tests/test_pages_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Uses Streamlit's AppTest framework to run the page script.
"""

from pathlib import Path
from unittest.mock import MagicMock, patch

from streamlit.testing.v1 import AppTest
Expand All @@ -21,7 +22,7 @@
"IMPRESSUM_EMAIL": "info@test.de",
}

PAGE_FILE = "immermatch/pages/verify.py"
PAGE_FILE = str(Path(__file__).resolve().parents[1] / "immermatch" / "pages" / "verify.py")


def _build_app(token: str | None = None) -> AppTest:
Expand Down
Loading