Commit graph

348 commits

Author SHA1 Message Date
5266aa52e8 ci: configure Forgejo git credentials before pip install
Some checks failed
CI / test (push) Failing after 27s
FORGEJO_TOKEN secret injected via env var (not inline expression) to
avoid CI injection risk; git insteadOf redirect authenticates the
git+https:// circuitforge-core VCS URL at install time.
2026-04-01 13:43:54 -07:00
d31a31b263 chore: release v0.8.2
Some checks failed
CI / test (push) Failing after 28s
2026-04-01 13:22:43 -07:00
a153546335 fix(ci): replace local -e path with Forgejo VCS URL for circuitforge-core
Some checks failed
CI / test (push) Has been cancelled
CI checks out only the peregrine repo — ../circuitforge-core doesn't exist,
causing 'pip install -r requirements.txt' to fail.

requirements.txt now uses git+https://...@main as the fallback for CI and
bare pip installs. Dockerfile.cfcore installs cfcore from the local COPY
first (skipping the requirements.txt line) to avoid a redundant network
fetch during Docker builds.
2026-04-01 13:22:06 -07:00
88c662f08d feat: switch main compose to Dockerfile.cfcore parent-context build
Some checks failed
CI / test (push) Failing after 31s
compose.yml was still using build: . which fails because requirements.txt
has -e ../circuitforge-core outside the build context. Now matches
compose.cloud.yml and compose.test-cfcore.yml.
2026-04-01 13:00:45 -07:00
83c87d4a13 feat(cloud): promote cfcore integration to production cloud instance
Some checks failed
CI / test (push) Failing after 19s
Switch compose.cloud.yml build context to Dockerfile.cfcore (parent
context includes circuitforge-core/ as sibling). Adds CF_ORCH_URL so
the cloud container can reach the cf-orch coordinator on the host.
2026-04-01 11:25:00 -07:00
d00d74d994 feat(scheduler): read CF_ORCH_URL env var for coordinator address
Some checks failed
CI / test (push) Failing after 19s
Threads coordinator_url from CF_ORCH_URL env var (default localhost:7700)
into the cfcore TaskScheduler so Docker instances can point at
host.docker.internal:7700 instead of the container's own loopback.

Also adds CF_ORCH_URL to compose.test-cfcore.yml and mounts persistent
patched configs (llm.docker.yaml, user.docker.yaml) for the test instance.
2026-04-01 11:06:38 -07:00
a8b08f3a45 fix: prevent Vue-nav reload loop when running without Caddy proxy
Some checks failed
CI / test (push) Failing after 20s
sync_ui_cookie() was calling window.parent.location.reload() on every
render when user.yaml has ui_preference=vue, but no Caddy is in the
traffic path (test instances, bare Docker). This caused an infinite
reload loop because the reload just came back to Streamlit.

Gate the reload on PEREGRINE_CADDY_PROXY=1. Without it, the cookie is
still written silently but no reload is attempted. Add the env var to
compose.yml and compose.cloud.yml (both are behind Caddy); omit from
compose.test-cfcore.yml so test instances stay stable.
2026-04-01 08:21:15 -07:00
be19947cb4 chore(release): v0.8.1
Some checks failed
CI / test (push) Failing after 42s
2026-04-01 07:25:13 -07:00
a6b32917ea chore(docker): add cfcore-aware Dockerfile and test compose
- Dockerfile: restored to original (build: . context, no cfcore) so
  existing compose.yml / compose.cloud.yml builds are unaffected
- Dockerfile.cfcore: parent-context build that copies circuitforge-core/
  alongside peregrine/ before pip install; resolves -e ../circuitforge-core
- compose.test-cfcore.yml: single-user test instance on port 8516;
  run from parent dir with context: .. so both repos are in scope

Use this to smoke-test cfcore shims before promoting to prod cloud.
2026-04-01 07:24:47 -07:00
2959abb3da fix(settings): improve suggest feedback for empty/failed LLM results
- Catch all exceptions (not just RuntimeError) so FileNotFoundError,
  connection errors, etc. surface as error messages rather than crashing
  the page silently
- Show "No new suggestions found" info message when the LLM returns
  empty arrays — previously the spinner completed with no UI feedback
- Hint to upload resume when RESUME_PATH is missing (new users)
- Only rerun() when there are actual results to display
2026-04-01 07:17:21 -07:00
98754cbe43 chore(release): v0.8.0 2026-04-01 07:12:48 -07:00
8c42de3f5c feat(merge): merge feature/vue-spa into main
Full Vue 3 SPA merge — closes #8. Major additions:

Backend (dev API):
- dev_api.py → symlink to dev-api.py (importable module alias)
- dev-api.py: full FastAPI backend (settings, jobs, interviews, prep,
  survey, digest, resume optimizer endpoints); cloud session middleware
- scripts/user_profile.py: load_user_profile / save_user_profile helpers
- scripts/discover.py + scripts/imap_sync.py: API-compatible additions

Frontend (web/src/):
- ApplyWorkspace: ATS resume optimizer panel (gap report free, rewrite paid+)
- ResumeOptimizerPanel.vue: new component with task polling + .txt download

Test suite:
- test_dev_api_settings/survey/prep/digest/interviews: full API test coverage
- fix: replace importlib.reload with monkeypatch.setattr(dev_api, "DB_PATH")
  to prevent module global reset breaking isolation across test files

Docs:
- docs/vue-spa-migration.md: migration guide
2026-04-01 07:11:14 -07:00
faa1807e96 feat(api): add job ranker and credential store scripts
- scripts/job_ranker.py: two-stage rank pipeline for /api/jobs/stack
  endpoint; scores pending jobs by match_score + seniority signals
- scripts/credential_store.py: per-user credential management (BYOK
  API keys, email passwords); used by dev_api settings endpoints
2026-04-01 07:10:46 -07:00
ee66b6b235 feat(web): add task indicator component and task store for background jobs
- web/src/stores/tasks.ts: Pinia store polling /api/tasks/active
- web/src/components/TaskIndicator.vue: sidebar + mobile task queue
  display with live count badge
- web/public/: peregrine logo assets (SVG + PNG variants)
2026-04-01 07:09:55 -07:00
02e004ee5c feat(apply): ATS resume optimizer backend — gap report + LLM rewrite
- scripts/resume_optimizer.py: full pipeline (extract_jd_signals →
  prioritize_gaps → rewrite_for_ats → hallucination_check)
- scripts/db.py: add optimized_resume + ats_gap_report columns +
  save_optimized_resume / get_optimized_resume helpers
- tests/test_resume_optimizer.py: 17 unit tests; patches at source
  module (scripts.llm_router.LLMRouter), not consumer

Tier gate: gap report is free; full LLM rewrite is paid+.
2026-04-01 07:09:46 -07:00
9702646738 fix(cloud): replace DEFAULT_DB with get_db_path() across all Streamlit pages
Pages were hardcoding DEFAULT_DB at import time, meaning cloud-mode
per-user DB routing was silently ignored. Pages affected:
1_Job_Review, 5_Interviews, 6_Interview_Prep, 7_Survey.

Adds resolve_session("peregrine") + get_db_path() pattern to each,
matching the pattern already used in 4_Apply.py.

Fixes #24.
2026-04-01 07:09:35 -07:00
dfac0f3d7a fix(tests): replace importlib.reload with monkeypatch.setattr for DB_PATH isolation
importlib.reload(dev_api) reset all module-level globals (RESUME_PATH,
SEARCH_PREFS_PATH, etc.) on every digest/interviews test, causing
subsequent monkeypatch.setattr calls in test_dev_api_settings.py to
silently fail — the patched attribute was reset between fixture setup
and the actual HTTP request.

Fix: patch dev_api.DB_PATH directly via monkeypatch, which pytest reverts
cleanly after each test without touching any other module state.

Also sync resume optimizer endpoints to dev-api.py (hyphen variant).
2026-04-01 06:58:28 -07:00
931a07d4e0 chore(merge): merge main into feature/vue-spa — resolve ApplyWorkspace conflict
ApplyWorkspace.vue: kept HEAD (vue-spa) version for resume optimizer panel,
cl-error__actions wrapper, and ResumeOptimizerPanel import. main's older
version lacked these additions.
2026-03-31 21:25:15 -07:00
faf0a7c4dc feat(apply): ATS resume optimizer — gap report + LLM rewrite (paid tier)
- scripts/resume_optimizer.py: extract_jd_signals, prioritize_gaps,
  rewrite_for_ats, hallucination_check, render_resume_text
- dev_api.py: GET/POST /api/jobs/{id}/resume_optimizer + /task endpoints
- web/src/components/ResumeOptimizerPanel.vue: gap report (all tiers),
  per-section LLM rewrite + hallucination badge (paid+)
- ApplyWorkspace.vue: ResumeOptimizerPanel wired in below cover letter

Closes #29
2026-03-31 21:24:49 -07:00
15dc4b2646 chore: rename conda env job-seeker to cf; update README
Some checks failed
CI / test (pull_request) Failing after 22s
2026-03-31 10:39:25 -07:00
922d91fb91 refactor(scheduler): shim to circuitforge_core.tasks.scheduler
VRAM detection now uses cf-orch free VRAM when coordinator is running,
making the scheduler cooperative with other cf-orch consumers.
Enqueue return value now checked — queue-full tasks are marked failed.
2026-03-31 09:27:43 -07:00
818e46c17e feat: migrate to circuitforge-core for db, llm router, and tiers
Some checks failed
CI / test (push) Failing after 24s
2026-03-25 11:44:19 -07:00
608e0fa922 fix(demo): block Vue navigation in demo mode; fix wizard gate ui sync
- ui_switcher.py: add explicit guard that forces pref=streamlit when
  DEMO_MODE=true, before the tier-downgrade check. Demo Vue SPA (#46)
  is not yet implemented, so navigating there produced a blank screen.
- app.py: call sync_ui_cookie inside wizard gate block before st.stop()
  so that cloud users with ui_preference=vue are redirected correctly
  even when the first-run wizard is still active. Previous behaviour
  called sync_ui_cookie after pg.run() which was never reached.
- demo/config/user.yaml: reset ui_preference to streamlit (belt-and-
  suspenders alongside the code guard).

Closes: demo blank-screen regression reported 2026-03-24.
2026-03-24 12:31:37 -07:00
e9c3c45612 fix(app): pass yaml_path and tier args to render_banner and sync_ui_cookie
Both functions require (yaml_path, tier) — calling them with no args was
silently failing inside the try/except, causing the banner to never render.
2026-03-22 19:28:25 -07:00
e95272c92f fix(app): show ui switcher banner in demo mode
render_banner() was incorrectly guarded by 'if not IS_DEMO' — the spec
says the banner is open to all demo visitors. render_banner() already
handles its own eligibility check internally (_DEMO_MODE or can_use).
2026-03-22 19:18:58 -07:00
c42ba318cf chore(release): v0.7.0
UI switcher (vue_ui_beta paid tier gate), demo toolbar (tier simulation),
Vue SPA merged from feature/vue-spa, nginx Docker web service, Caddy
cookie routing prgn_ui → :8506/:8507/:8508
2026-03-22 18:58:48 -07:00
8208731064 feat(docker): add web service for Vue SPA (nginx, multi-stage build)
Ports: dev=8506, demo=8507, cloud=8508
manage.sh build now includes web service
2026-03-22 18:47:46 -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
86de5d2f3f feat(settings): add ui_switcher toggle to Deployment expander 2026-03-22 16:50:17 -07:00
c94a9d5b30 chore(settings): remove old SettingsView placeholder — new shell at views/settings/SettingsView.vue
Full test suite: 71 frontend (14 files) + 583 backend tests passing.
2026-03-22 16:40:37 -07:00
17eed400f8 feat(app): wire ui_switcher and demo_toolbar into app.py render pass
- Initialize simulated_tier session state for demo mode after resolve_session/init_db
- Render demo toolbar before pg.run() when IS_DEMO is set
- Render ui_switcher banner before pg.run() for non-demo paid-tier users (guarded with try/except)
- Sync ui_preference cookie after pg.run() (guarded with try/except)
- All imports are local (inside if-blocks) to avoid Streamlit circular import issues
2026-03-22 16:30:52 -07:00
3e41dbf030 test(settings): settingsGuard unit tests — tab gating scenarios
Extract guard logic to settingsGuard.ts for testability.
Router beforeEach keeps async config.load() wrapper, delegates to sync guard.
14 test cases cover system/fine-tune/developer gates across cloud/self-hosted/tier/GPU profile combos.
2026-03-22 16:27:45 -07:00
35e8f7513c fix(demo): remove reload antipattern, fix label consistency in demo_toolbar tests 2026-03-22 16:27:20 -07:00
feea057463 test(settings): backend tests for all settings API endpoints 2026-03-22 16:25:37 -07:00
c7a785da4c test(demo): add render_demo_toolbar test (94% coverage) 2026-03-22 16:20:16 -07:00
88e870df5c feat(demo): add demo_toolbar component (tier simulation for DEMO_MODE) 2026-03-22 16:11:58 -07:00
d748081a53 refactor(ui-switcher): narrow exception handling, remove duplicate profile loads, fix test isolation
- Add explanatory comments to all 5 bare except Exception blocks clarifying that UI components must not crash the app
- Refactor sync_ui_cookie() to load UserProfile once instead of up to 3 times in normal path
- Store profile reference and reuse it in tier downgrade protection block
- Replace importlib.reload() pattern in tests with unittest.mock.patch for _DEMO_MODE
- Improves test isolation and eliminates module state contamination across test runs
- All 5 tests pass (100%)
2026-03-22 16:05:53 -07:00
fa2569c7e4 feat(settings): License, Data, Privacy, Developer tabs — stores, views, endpoints
- useLicenseStore: load/activate/deactivate with tier badge and key input
- useDataStore: createBackup with file count and size display
- usePrivacyStore: BYOK panel logic (dismissal snapshot tracks new backends),
  telemetry toggle (self-hosted) and master-off/usage/content controls (cloud)
- Views: LicenseView (cloud/self-hosted split), LicenseSelfHosted,
  LicenseCloud, DataView, PrivacyView, DeveloperView
- dev-api.py: /api/settings/license, /activate, /deactivate;
  /api/settings/data/backup/create; /api/settings/privacy GET+PUT;
  /api/settings/developer GET, /tier PUT, /hf-token PUT+test, /wizard-reset,
  /export-classifier; _load_user_config/_save_user_config helpers; CONFIG_DIR
- TDD: 10/10 store tests passing (license×3, data×2, privacy×5)
2026-03-22 16:01:29 -07:00
5f7e7ee912 feat(ui-switcher): add ui_switcher component (sync_ui_cookie, switch_ui, render_banner, render_settings_toggle) 2026-03-22 16:01:07 -07:00
0acde6d199 feat(profile): add ui_preference field (streamlit|vue, default: streamlit) 2026-03-22 15:55:38 -07:00
eb72776e9f feat(settings): Fine-Tune tab — wizard, polling, step lifecycle
Add useFineTuneStore (Pinia setup-function) with step state, polling via
setInterval, loadStatus, startPolling/stopPolling, and submitJob. Add
FineTuneView.vue with a 3-step wizard (upload → extract → train), mode-aware
train step (self-hosted shows make finetune + model check; cloud shows
submit job + quota). Add fine-tune endpoints to dev-api.py: status, extract,
upload, submit, and local-status. All 4 store unit tests pass.
2026-03-22 15:52:53 -07:00
bd24275455 refactor(tiers): replace importlib.reload with mock.patch in demo_tier tests
- Replace fragile reload pattern with unittest.mock.patch('app.wizard.tiers._DEMO_MODE', ...)
- Eliminates parallel test run failures (pytest-xdist) and improves test isolation
- All 4 demo_tier tests now use context managers for clean setup/teardown
- Add explanatory comment to _DEMO_MODE definition about immutability and env-based init
2026-03-22 15:52:03 -07:00
a380ec33ec fix(settings): task 6 review fixes — credential paths, email security, integrationResults in store
- Anchor CRED_DIR/KEY_PATH to __file__ (not CWD) in credential_store.py
- Fix email PUT: separate password pop from sentinel discard (was fragile or-chain)
- Fix email test: always use stored credential, remove password override path
- Move integrationResults into system store (was view-local — spec violation)
- saveFilePaths/saveDeployConfig write to dedicated error refs, not saveError
2026-03-22 15:46:47 -07:00
1c7a093125 feat(tiers): add vue_ui_beta feature key and demo_tier kwarg to can_use 2026-03-22 15:31:54 -07:00
f6ddaca14f feat(settings): credential store + fix Task 6 blocking review issues
- add scripts/credential_store.py (keyring/file/env-ref backends, Fernet encryption)
- email password stored via credential store, never returned in GET
- email GET returns password_set flag; PUT accepts new password or ${ENV_VAR} ref
- move integration actions to store (connectIntegration, testIntegration, disconnectIntegration)
- add tier-gating UI with locked state and upgrade prompt
- move subprocess/socket/imaplib/ssl imports to top level
2026-03-22 15:31:45 -07:00
bce997e596 feat(settings): System tab — services, email, integrations, paths, deployment 2026-03-22 13:25:38 -07:00
5afb752be6 fix(settings): system tab review fixes
- guard confirmByok() against byok-ack POST failure (leave modal open on error)
- fix drag reorder to use ID-based index lookup (not filtered-list index)
- guard cancelByok() against empty snapshot
- add LlmConfigPayload Pydantic model for PUT endpoint
- add test for confirmByok() failure path
2026-03-22 12:01:55 -07:00
7af0366330 feat(settings): System tab — LLM backends, BYOK gate, store + view 2026-03-22 07:26:07 -07:00
a38d9e5663 fix(settings): search prefs review fixes
- add try/except to suggest endpoint
- use immutable spread/filter in addTag, removeTag, acceptSuggestion
- add toggleBoard store action, remove direct v-model on board.enabled
2026-03-22 07:21:10 -07:00
2200d05b5c feat(settings): Search Prefs tab — store, view, API endpoints, remote preference filter 2026-03-21 03:09:51 -07:00