peregrine/HANDOFF-xanderland.md
pyr0ball 8e36863a49
Some checks failed
CI / Backend (Python) (push) Failing after 2m15s
CI / Frontend (Vue) (push) Failing after 21s
Mirror / mirror (push) Failing after 9s
feat: Interview prep Q&A, cf-orch hardware profile, a11y fixes, dark theme
Backend
- dev-api.py: Q&A suggest endpoint, Log Contact, cf-orch node detection in wizard
  hardware step, canonical search_profiles format (profiles:[...]), connections
  settings endpoints, Resume Library endpoints
- db_migrate.py: migrations 002/003/004 — ATS columns, resume review, final
  resume struct
- discover.py: _normalize_profiles() for legacy wizard YAML format compat
- resume_optimizer.py: section-by-section resume parsing + scoring
- task_runner.py: Q&A and contact-log task types
- company_research.py: accessibility brief column wiring
- generate_cover_letter.py: restore _candidate module-level binding

Frontend
- InterviewPrepView.vue: Q&A chat tab, Log Contact form, MarkdownView rendering
- InterviewCard.vue: new reusable card component for interviews kanban
- InterviewsView.vue: rejected analytics section with stage breakdown chips
- ResumeProfileView.vue: sync with new resume store shape
- SearchPrefsView.vue: cf-orch toggle, profile format migration
- SystemSettingsView.vue: connections settings wiring
- ConnectionsSettingsView.vue: new view for integration connections
- MarkdownView.vue: new component for safe markdown rendering
- ApplyWorkspace.vue: a11y — h1→h2 demotion, aria-expanded on Q&A toggle,
  confirmation dialog on Reject action (#98 #99 #100)
- peregrine.css: explicit [data-theme="dark"] token block for light-OS users (#101),
  :focus-visible outline (#97)
- wizard.css: cf-orch hardware step styles
- WizardHardwareStep.vue: cf-orch node display, profile selection with orch option
- WizardLayout.vue: hardware step wiring

Infra
- compose.yml / compose.cloud.yml: cf-orch agent sidecar, llm.cloud.yaml mount
- Dockerfile.cfcore: cf-core editable install in image build
- HANDOFF-xanderland.md: Podman/systemd setup guide for beta tester
- podman-standalone.sh: standalone Podman run script

Tests
- test_dev_api_settings.py: remove stale worktree path bootstrap (credential_store
  now in main repo); fix job_boards fixture to use non-empty list
- test_wizard_api.py: update profiles assertion to superset check (cf-orch added);
  update step6 assertion to canonical profiles[].titles format
2026-04-14 17:01:18 -07:00

3.9 KiB

Peregrine → xanderland.tv Setup Handoff

Written from: dev machine (CircuitForge dev env) Target: xanderland.tv (beta tester, rootful Podman + systemd) Date: 2026-02-27


What we're doing

Getting Peregrine running on the beta tester's server as a Podman container managed by systemd. He already runs SearXNG and other services in the same style — rootful Podman with --net=host, --restart=unless-stopped, registered as systemd units.

The script podman-standalone.sh in the repo root handles the container setup.


Step 1 — Get the repo onto xanderland.tv

From navi (or directly if you have a route):

ssh xanderland.tv "sudo git clone <repo-url> /opt/peregrine"

Or if it's already there, just pull:

ssh xanderland.tv "cd /opt/peregrine && sudo git pull"

Step 2 — Verify /opt/peregrine looks right

ssh xanderland.tv "ls /opt/peregrine"

Expect to see: Dockerfile, compose.yml, manage.sh, podman-standalone.sh, config/, app/, scripts/, etc.


Step 3 — Config

ssh xanderland.tv
cd /opt/peregrine
sudo mkdir -p data
sudo cp config/llm.yaml.example config/llm.yaml
sudo cp config/notion.yaml.example config/notion.yaml   # only if he wants Notion sync

Then edit config/llm.yaml and set searxng_url to his existing SearXNG instance (default is http://localhost:8888 — confirm his actual port).

He won't need Anthropic/OpenAI keys to start — the setup wizard lets him pick local Ollama or whatever he has running.


Step 4 — Fix DOCS_DIR in the script

The script defaults DOCS_DIR=/Library/Documents/JobSearch which is the original user's path. Update it to wherever his job search documents actually live, or a placeholder empty dir:

sudo mkdir -p /opt/peregrine/docs   # placeholder if he has no docs yet

Then edit the script:

sudo sed -i 's|DOCS_DIR=.*|DOCS_DIR=/opt/peregrine/docs|' /opt/peregrine/podman-standalone.sh

Step 5 — Build the image

ssh xanderland.tv "cd /opt/peregrine && sudo podman build -t localhost/peregrine:latest ."

Takes a few minutes on first run (downloads python:3.11-slim, installs deps).


Step 6 — Run the script

ssh xanderland.tv "sudo bash /opt/peregrine/podman-standalone.sh"

This starts a single container (peregrine) with --net=host and --restart=unless-stopped. SearXNG is NOT included — his existing instance is used.

Verify it came up:

ssh xanderland.tv "sudo podman ps | grep peregrine"
ssh xanderland.tv "sudo podman logs peregrine"

Health check endpoint: http://xanderland.tv:8501/_stcore/health


Step 7 — Register as a systemd service

ssh xanderland.tv
sudo podman generate systemd --new --name peregrine \
  | sudo tee /etc/systemd/system/peregrine.service
sudo systemctl daemon-reload
sudo systemctl enable --now peregrine

Confirm:

sudo systemctl status peregrine

Step 8 — First-run wizard

Open http://xanderland.tv:8501 in a browser.

The setup wizard (page 0) will gate the app until config/user.yaml is created. He'll fill in his profile — name, resume, LLM backend preferences. This writes config/user.yaml and unlocks the rest of the UI.


Troubleshooting

Symptom Check
Container exits immediately sudo podman logs peregrine — usually a missing config file
Port 8501 already in use sudo ss -tlnp | grep 8501 — something else on that port
SearXNG not reachable Confirm searxng_url in config/llm.yaml and that JSON format is enabled in SearXNG settings
Wizard loops / won't save config/ volume mount permissions — sudo chown -R 1000:1000 /opt/peregrine/config

To update Peregrine later

cd /opt/peregrine
sudo git pull
sudo podman build -t localhost/peregrine:latest .
sudo podman restart peregrine

No need to touch the systemd unit — it launches fresh via --new in the generate step.