peregrine/docs/plans/2026-02-24-generalization-handoff.md
pyr0ball f11a38eb0b chore: seed Peregrine from personal job-seeker (pre-generalization)
App: Peregrine
Company: Circuit Forge LLC
Source: github.com/pyr0ball/job-seeker (personal fork, not linked)
2026-02-24 18:25:39 -08:00

108 lines
4.8 KiB
Markdown

# Session Handoff — Generalization Implementation
**Date:** 2026-02-24
**For:** Next Claude session implementing the public fork
---
## Current State
The personal version (`/devl/job-seeker/`) is **complete and working** on `main`.
### What was completed in the 2026-02-24 session
- Survey Assistant page (`app/pages/7_Survey.py`) — text paste + screenshot via moondream2
- Vision Service (`scripts/vision_service/`) — FastAPI on port 8002, `job-seeker-vision` conda env
- LLM Router `images=` parameter — vision-aware routing
- `survey_responses` table + `survey_at` column in SQLite
- Kanban consolidation — applied+survey as pre-kanban section; offer+hired merged column
- `survey_received` email classifier label
- Forgejo remote: https://git.opensourcesolarpunk.com/pyr0ball/job-seeker.git
### Remote repo
```
git remote: https://git.opensourcesolarpunk.com/pyr0ball/job-seeker.git
branch: main (up to date as of 2026-02-24)
```
---
## What to Implement Next
Follow the plan at `docs/plans/2026-02-24-job-seeker-app-generalize.md`.
The design doc is at `docs/plans/2026-02-24-generalize-design.md`.
**Target directory:** `/Library/Development/devl/job-seeker-app/` (new repo, no shared history)
**CRITICAL:** Do NOT start implementing the public fork until explicitly asked. The user confirmed this.
---
## Complete List of Hardcoded Personal References
Everything that must be extracted into `config/user.yaml` via a `UserProfile` class:
| File | Hardcoded value | Generalized as |
|------|----------------|----------------|
| `company_research.py` | `"Alex Rivera"` in prompts | `profile.name` |
| `company_research.py` | `_NDA_COMPANIES = {"upguard"}` | `profile.nda_companies` |
| `company_research.py` | `_SCRAPER_DIR = Path("/Library/...")` | bundled in Docker image |
| `generate_cover_letter.py` | `SYSTEM_CONTEXT` with Alex's bio | `profile.career_summary` |
| `generate_cover_letter.py` | `LETTERS_DIR = Path("/Library/...")` | `profile.docs_dir` |
| `4_Apply.py` | contact block (name/email/phone) | `profile.*` |
| `4_Apply.py` | `DOCS_DIR = Path("/Library/...")` | `profile.docs_dir` |
| `5_Interviews.py` | email assistant persona "Alex Rivera is a Customer Success..." | `profile.name + profile.career_summary` |
| `6_Interview_Prep.py` | `"Alex"` in interviewer prompts | `profile.name` |
| `7_Survey.py` | `_SURVEY_SYSTEM` — "The candidate values collaborative teamwork..." | `profile.career_summary` or survey persona field |
| `scripts/vision_service/main.py` | `model_id = "vikhyatk/moondream2"`, `revision = "2025-01-09"` | `config/llm.yaml` vision_service block |
| `match.py` | `RESUME_PATH = Path("/Library/...Alex_Rivera_Resume...")` | configurable in Settings |
| `Home.py` | `"Alex's Job Search"` | `f"{profile.name}'s Job Search"` |
| `finetune_local.py` | all `/Library/` paths + `"alex-cover-writer"` | `profile.*` |
| `2_Settings.py` | `PFP_DIR`, host service paths (manage-services.sh etc.) | removed / compose-driven |
| `config/llm.yaml` | hard-coded `base_url` values | auto-generated from `user.yaml` |
---
## New Components to Dockerize
### Vision Service
- Currently: `job-seeker-vision` conda env, port 8002, `manage-vision.sh`
- In public fork: separate container in `single-gpu` / `dual-gpu` profiles only
- In `remote` / `cpu` profiles: vision falls back to cloud backends
- Model configurable via env var in container (default: moondream2)
### CompanyScraper
- Currently: `/Library/Development/scrapers/companyScraper.py` (external path)
- In public fork: bundled directly in the app image at a fixed internal path
---
## Key Architectural Decisions (from design doc)
1. **`UserProfile` class** wraps `config/user.yaml` — imported everywhere personal data is used
2. **Four Docker Compose profiles:** `remote`, `cpu`, `single-gpu`, `dual-gpu`
3. **First-run wizard** gates the app until `config/user.yaml` exists (5-step flow)
4. **No shared git history** with personal repo — fresh `git init` in target dir
5. **`.env` file** generated by wizard (never hand-edited), gitignored, contains resolved paths
6. **`config/llm.yaml` base URLs** are derived values auto-generated from `user.yaml` services block
7. **Claude Code Wrapper + Copilot Wrapper** removed from Services tab entirely
---
## Files/Paths in Personal Repo to Reference
- Entry point: `app/app.py`
- All pages: `app/pages/`
- DB helpers: `scripts/db.py` (single source of truth for schema)
- LLM router: `scripts/llm_router.py`
- Config: `config/llm.yaml`, `config/search_profiles.yaml`
- Vision service: `scripts/vision_service/` (FastAPI + environment.yml)
- Test suite: `tests/`
---
## Skill to Use
When starting the generalization session:
1. Load `superpowers:executing-plans` skill
2. Reference `docs/plans/2026-02-24-job-seeker-app-generalize.md` as the plan
3. Work task-by-task with review checkpoints