Commit graph

16 commits

Author SHA1 Message Date
9101e716ba fix: async survey/analyze via task queue (#107)
Move POST /api/jobs/:id/survey/analyze off the FastAPI worker thread
by routing it through the LLM task queue (same pattern as cover_letter,
company_research, resume_optimize).

- Extract prompt builders + run_survey_analyze() to scripts/survey_assistant.py
- Add survey_analyze to LLM_TASK_TYPES (task_scheduler.py) with 2.5 GB VRAM budget
  (text mode: phi3:mini; visual mode uses vision service's own VRAM pool)
- Add elif branch in task_runner._run_task; result stored as JSON in error col
- Replace sync endpoint body with submit_task(); add GET /survey/analyze/task poll
- Update survey.ts store: analyze() now fires task + polls at 3s interval;
  silently attaches to existing in-flight task when is_new=false
- SurveyView button label shows task stage while polling

Fixes load-test spike: ~22 greenlets blocking on LLM inference at 100 concurrent
users, causing 90s poll timeouts on cover_letter and research tasks.
2026-04-20 11:06:14 -07:00
22bc57242e feat: ResumeProfileView — career_summary, education, achievements sections and sync status label 2026-04-16 14:22:36 -07:00
fe3e4ff539 feat: ResumesView — Apply to profile button, Active profile badge, sync notice, unsaved-changes guard 2026-04-16 14:13:44 -07:00
03b9e52301 feat: references tracker and recommendation letter system (#96)
- references_ + job_references tables with CREATE + migration
- Full CRUD: GET/POST /api/references, PATCH/DELETE /api/references/:id
- Link/unlink to jobs: POST/DELETE /api/references/:id/link-job/:job_id
- GET /api/references/for-job/:job_id — linked refs with prep/letter drafts
- POST /api/references/:id/prep-email — LLM drafts heads-up email to send
  reference before interview; persisted to job_references.prep_email
- POST /api/references/:id/rec-letter — LLM drafts recommendation letter
  reference can edit and send on their letterhead (Paid/BYOK tier)
- ReferencesView.vue: add/edit/delete form, tag system (technical/managerial/
  character/academic), inline confirm-before-delete
- Route /references + IdentificationIcon nav link
2026-04-15 08:42:06 -07:00
0e4fce44c4 feat: shadow listing detector, hired feedback widget, contacts manager
Shadow listing detector (#95):
- Capture date_posted from JobSpy in discover.py + insert_job()
- Add date_posted migration to _MIGRATIONS
- _shadow_score() heuristic: 'shadow' (≥30 days stale), 'stale' (≥14 days)
- list_jobs() computes shadow_score per listing
- JobCard.vue: 'Ghost post' and 'Stale' badges with tooltip

Post-hire feedback widget (#91):
- Add hired_feedback migration to _MIGRATIONS
- POST /api/jobs/:id/hired-feedback endpoint
- InterviewCard.vue: optional widget on hired cards with factor
  checkboxes + freetext; dismissible; shows saved state
- PipelineJob interface extended with hired_feedback field

Contacts manager (#73):
- GET /api/contacts endpoint with job join, direction/search filters
- New ContactsView.vue: searchable table, inbound/outbound filter,
  signal chip column, job link
- Route /contacts added; Contacts nav link (UsersIcon) in AppNav

Also: add git to Dockerfile apt-get for circuitforge-core editable install
2026-04-15 08:34:12 -07:00
8e36863a49 feat: Interview prep Q&A, cf-orch hardware profile, a11y fixes, dark theme
Some checks failed
CI / Backend (Python) (push) Failing after 2m15s
CI / Frontend (Vue) (push) Failing after 21s
Mirror / mirror (push) Failing after 9s
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
f22e713968 fix: review issues — import size limit, aria labels, CSS vars, Set reactivity 2026-04-12 11:52:23 -07:00
d4a2107411 feat: add ResumesView standalone resume library manager 2026-04-12 11:32:29 -07:00
ac3e97d6c8 feat(#62): Fine-Tune tab — training pair management + real submit
Some checks failed
CI / test (push) Failing after 21s
API (dev-api.py):
- GET /api/settings/fine-tune/pairs — list pairs from JSONL with index/instruction/source_file
- DELETE /api/settings/fine-tune/pairs/{index} — remove a pair and rewrite JSONL
- POST /api/settings/fine-tune/submit — now queues prepare_training task (replaces UUID stub)
- GET /api/settings/fine-tune/status — returns pairs_count from JSONL (not just DB task)

Store (fineTune.ts):
- TrainingPair interface
- pairs, pairsLoading refs
- loadPairs(), deletePair() actions

Vue (FineTuneView.vue):
- Step 2 shows scrollable pairs list with instruction + source file
- ✕ button on each pair calls deletePair(); list/count update immediately
- loadPairs() called on mount
2026-04-04 22:30:16 -07:00
42c9c882ee feat(#59): LLM-assisted generation for all settings form fields
Some checks failed
CI / test (push) Failing after 21s
API endpoints (dev-api.py):
- POST /api/settings/profile/generate-summary → {summary}
- POST /api/settings/profile/generate-missions → {mission_preferences}
- POST /api/settings/profile/generate-voice → {voice}
- POST /api/settings/search/suggest → replaces stub; handles titles/locations/exclude_keywords

Vue (MyProfileView.vue):
- Generate ✦ button on candidate_voice textarea (was missing)

Vue (SearchPrefsView.vue + search store):
- Suggest button for Exclude Keywords section (matches titles/locations pattern)
- suggestExcludeKeywords() in search store
- acceptSuggestion() extended to 'exclude' type
2026-04-04 22:27:20 -07:00
065c02feb7 feat(vue): Home dashboard parity — Enrich button, Danger Zone, setup banners (closes #57)
Some checks failed
CI / test (push) Failing after 20s
API additions (dev-api.py):
- GET /api/tasks — list active background tasks
- DELETE /api/tasks/{task_id} — per-task cancel
- POST /api/tasks/kill — kill all stuck tasks
- POST /api/tasks/discovery|email-sync|enrich|score|sync — queue/trigger each workflow
- POST /api/jobs/archive — archive by statuses array
- POST /api/jobs/purge — hard delete by statuses or target (email/non_remote/rescrape)
- POST /api/jobs/add — queue URL imports
- POST /api/jobs/upload-csv — upload CSV with URL column
- GET  /api/config/setup-banners — list undismissed onboarding hints
- POST /api/config/setup-banners/{key}/dismiss — dismiss a banner

HomeView.vue:
- 4th WorkflowButton: "Fill Missing Descriptions" (always visible, not gated on enrichment_enabled)
- Danger Zone redesign: scope radio (pending-only vs pending+approved), Archive & reset (primary)
  vs Hard purge (secondary), inline confirm dialogs, active task list with per-task cancel,
  Kill all stuck button, More Options (email purge / non-remote / wipe+rescrape)
- Setup banners: dismissible onboarding hints pulled from /api/config/setup-banners,
  5-second polling for active task list to stay live

app/Home.py:
- Danger Zone redesign: same scope radio + archive/purge with confirm steps
- Background task list with per-task cancel and Kill all stuck button
- More options expander (email purge, non-remote, wipe+rescrape)
- Setup banners section at page bottom
2026-04-04 22:05:06 -07:00
53b07568d9 feat(vue): accumulated parity work — Q&A, Apply highlights, AppNav switcher, cloud API
API additions (dev-api.py split across this and next commit):
- /api/jobs/{job_id}/qa GET/PATCH/suggest — Interview Prep answer storage + LLM suggestions
- /api/settings/ui-preference POST — persist streamlit/vue preference to user.yaml
- cancel_task() added to scripts/db.py (per-task cancel for Danger Zone)

Vue / UI:
- AppNav: " Classic" button to switch back to Streamlit UI (writes cookie + persists to user.yaml)
- ApplyWorkspace: Resume Highlights panel (collapsible skills/domains/keywords with job-match highlighting)
- SettingsView: hide Data tab in cloud mode (showData guard)
- ResumeProfileView: minor improvements
- useApi.ts: error handling improvements

Infra:
- compose.cloud.yml: add api service (uvicorn dev_api running in cloud container)
- docker/web/nginx.conf: proxy /api/* to api service in cloud mode
- README.md: Vue SPA now listed as Free tier feature
2026-04-04 22:04:51 -07:00
b79d13b4f2 feat(vue): parity gaps #50, #54, #61 — sort/filter, research modal, draft CL button
Some checks failed
CI / test (push) Failing after 30s
#50 Job Review list view — sort + filter controls:
- Sort by best match / newest first / company A-Z (client-side computed)
- Remote-only checkbox filter
- Job count indicator; filters reset on tab switch
- Remote badge on list items

#61 Cover letter generation from approved tab:
- ' Draft' button on each approved-list item → /apply/:id
- No extra API call; ApplyWorkspace handles generation from there

#54 Company research modal (all API endpoints already existed):
- CompanyResearchModal.vue: 3-state machine (empty→generating→ready)
  polling /research/task every 3s, displays all 7 research sections
  (company, leadership, talking points, tech, funding, red flags,
  accessibility), copy-to-clipboard for talking points, ↺ Refresh
- InterviewCard: new 'research' emit + '🔍 Research' button for
  phone_screen/interviewing/offer stages
- InterviewsView: wires modal with researchJobId/Title/AutoGen state;
  auto-opens modal with autoGenerate=true when a job is moved to
  phone_screen (mirrors Streamlit behaviour)
2026-04-02 19:26:13 -07:00
deeba0211d fix(isolation): 4 user config isolation + resume upload bugs
Some checks failed
CI / test (pull_request) Failing after 33s
- _user_yaml_path(): remove dangerous fallback to /devl/job-seeker/
  config/user.yaml (Meg's legacy profile); a missing user.yaml now
  returns an empty dict via load_user_profile, never another user's data
- RESUME_PATH: replace hardcoded relative Path('config/plain_text_
  resume.yaml') with _resume_path() that derives from _user_yaml_path()
  so resume file is always co-located with the correct user.yaml
- upload_resume: was passing a file path string to structure_resume()
  which expects raw text; now extracts bytes, dispatches to the correct
  extractor (pdf/odt/docx), then passes text — matches Streamlit wizard
- WizardResumeStep.vue: upload response is {ok, data: {experience…}}
  but component was reading data.experience (top level); fixed to
  read resp.data.experience to match the actual API envelope
2026-04-02 18:23:02 -07:00
e0828677a4 feat(wizard): Vue onboarding wizard — all 7 steps + router wiring
- WizardLayout.vue: full-page card, progress bar, crash-recovery via
  loadStatus(isCloud); auto-skips steps 1/2/5 in cloud mode
- wizard.css: shared step styles (headings, banners, radio cards,
  chip lists, form fields, expandables, nav buttons)
- Step 1 — Hardware: GPU detection, profile select, mismatch warning
- Step 2 — Tier: Free/Paid/Premium radio cards
- Step 3 — Resume: upload (PDF/DOCX/ODT) or manual experience builder;
  pre-fills identity fields from parsed resume data
- Step 4 — Identity: name/email/phone/LinkedIn/career summary;
  full validation before saveStep
- Step 5 — Inference: remote API keys vs local Ollama; advanced
  service-host/port expandable; soft-fail connection test
- Step 6 — Search: chip-style job title + location input with
  comma/Enter commit; remote-only checkbox
- Step 7 — Integrations: optional tile-grid (Notion/Calendar/Slack/
  Discord/Drive); paid-tier badge for gated items; calls
  wizard.complete() on Finish
- wizard.ts Pinia store: loadStatus, detectHardware, saveStep,
  testInference, complete; cloud auto-skip logic
- wizardGuard.ts: gates all routes behind /setup until
  wizard_complete; redirects complete users away from /setup
- router/index.ts: /setup nested route tree; unified beforeEach guard
  (wizard gate → setup branch → settings tier gate)
- App.vue: hide AppNav + strip sidebar margin on /setup routes
2026-04-02 18:11:57 -07:00
49e3265132 feat(web): merge Vue SPA from feature/vue-spa; add ClassicUIButton + useFeatureFlag
- Import web/ directory (Vue 3 + Vite + UnoCSS SPA) from feature/vue-spa branch
- Add web/src/components/ClassicUIButton.vue: switch-back to Streamlit via
  cookie (prgn_ui=streamlit) + ?prgn_switch=streamlit query param bridge
- Add web/src/composables/useFeatureFlag.ts: reads prgn_demo_tier cookie for
  demo toolbar visual consistency (not an authoritative gate, see issue #8)
- Update .gitignore: add .superpowers/, pytest-output.txt, docs/superpowers/
2026-03-22 18:46:11 -07:00