From cc3d2f2a6ef198d7d712b4823ee45319aae2ffe9 Mon Sep 17 00:00:00 2001 From: Adrian Immer Date: Tue, 3 Nov 2026 20:08:01 +0100 Subject: [PATCH 1/2] fix(ci): repair AppTest page paths and ruff markdown formatting - Newer Streamlit resolves relative AppTest.from_file() paths against the calling test file, so the verify/unsubscribe page tests looked for tests/immermatch/pages/*.py. Anchor PAGE_FILE to the repo root, matching tests/test_app_ui.py. - ruff 0.17 formats Python code blocks inside Markdown; apply it to the model snippets in AGENTS.md (whitespace-only). Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 53 +++++++++++++++++++-------------- tests/test_pages_unsubscribe.py | 3 +- tests/test_pages_verify.py | 3 +- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 299e6eb..062133c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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]: ... ``` @@ -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()) @@ -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 @@ -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 diff --git a/tests/test_pages_unsubscribe.py b/tests/test_pages_unsubscribe.py index 8b1a20e..9af8cc7 100644 --- a/tests/test_pages_unsubscribe.py +++ b/tests/test_pages_unsubscribe.py @@ -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 @@ -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: diff --git a/tests/test_pages_verify.py b/tests/test_pages_verify.py index 089a91e..add573e 100644 --- a/tests/test_pages_verify.py +++ b/tests/test_pages_verify.py @@ -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 @@ -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: From 3d216a6f10cb738527c723ccb08380118d6dcab0 Mon Sep 17 00:00:00 2001 From: Adrian Immer Date: Tue, 3 Nov 2026 20:09:01 +0100 Subject: [PATCH 2/2] chore: switch runtime Python from 3.13 to 3.14 Bump the Python used by CI, the daily digest workflow, .python-version and the devcontainer image. The supported floor (requires-python >=3.10, ruff target py310) is unchanged. Co-Authored-By: Claude Opus 5.5 --- .devcontainer/devcontainer.json | 2 +- .github/workflows/ci.yml | 8 ++++---- .github/workflows/daily-digest.yml | 2 +- .python-version | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cc7b505..dd3958f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -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": [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e917baf..eaea629 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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) @@ -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) @@ -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 @@ -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) diff --git a/.github/workflows/daily-digest.yml b/.github/workflows/daily-digest.yml index c65eaf0..fbc11c7 100644 --- a/.github/workflows/daily-digest.yml +++ b/.github/workflows/daily-digest.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/setup-python@v7 with: - python-version: "3.13" + python-version: "3.14" cache: pip - name: Install dependencies diff --git a/.python-version b/.python-version index 24ee5b1..6324d40 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.13 +3.14