Remove claude_code, github_copilot, and anthropic from all cloud fallback
orders — cloud accounts must not route through personal/dev LLM backends.
vllm_research and ollama_research are the only permitted research backends.
llm.cloud.yaml is now bind-mounted at /app/config/llm.yaml in compose.cloud.yml.
- discover.py: run_discovery() accepts config_dir param; auto-derives it
from db_path parent (per-user in cloud, falls back to /app/config)
- task_runner.py: passes db_path.parent/config as config_dir to run_discovery
- wizard (0_Setup.py): write 'titles' key not 'job_titles' — matches what
discover.py and all custom board scrapers read
- adzuna/theladders/craigslist: fall back to 'job_titles' for existing
profiles written by older wizard versions
- Fixed Sheridan's live config in place (job_titles → titles)
Module-level DB_PATH is frozen at import time, so monkeypatch.setenv()
in tests had no effect on _user_yaml_path(). Reading os.environ directly
fixes 6 test_dev_api_settings PUT/POST failures in CI where
/devl/job-seeker/ doesn't exist.
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.
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.
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.
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.
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.
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.
- 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.
- 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
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.
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).
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.
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.
- 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.
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).
- 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
- 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%)
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.
- 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
- 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