- cloud_session.py: add _ensure_provisioned() called in resolve_session() so
new Google OAuth signups get a free Heimdall key created on first page load;
previously resolve returned "free" tier but no key was ever written to
Heimdall, leaving users in an untracked state
- Home.py: replace conda run invocation in "Score All Unscored Jobs" with
sys.executable so the button works inside Docker where conda is not present
When _profile is None the fallback pattern \w+ only matched the first
word of a two-word sign-off (e.g. 'Alex' from 'Alex Rivera'), silently
dropping the last name. Switch fallback to \w+(?:\s+\w+)? so a full
first+last sign-off is preserved in no-config environments (CI, first run).
When source == "jobgether", build_prompt() injects a recruiter context
note directing the LLM to address the Jobgether recruiter using
"Your client [at {company}] will appreciate..." framing rather than
addressing the employer directly. generate() and task_runner both
thread the is_jobgether flag through automatically.
Replaces the spawn-per-task model for LLM task types with scheduler
routing: cover_letter, company_research, and wizard_generate are now
enqueued via the TaskScheduler singleton for VRAM-aware batching.
Non-LLM tasks (discovery, email_sync, etc.) continue to spawn daemon
threads directly. Adds autouse clean_scheduler fixture to
test_task_runner.py to prevent singleton cross-test contamination.
Addresses 16 review findings across two passes:
- Clarify _active.pop/double-decrement non-issue
- Fix app.py change target (inline SQL, not kill_stuck_tasks)
- Scope durability to LLM types only
- Add _budgets to state table with load logic
- Fix singleton safety explanation (lock, not GIL)
- Ghost row fix: mark dropped tasks failed in DB
- Document static _available_vram as known limitation
- Fix test_llm_tasks_batch_by_type description
- Eliminate circular import via routing split in submit_task()
- Add missing budget warning at construction
LinkedIn's unauthenticated public profile only exposes name, summary (truncated),
current employer name, and certifications. Past roles, education, and skills are
blurred server-side behind a login wall — not a scraper limitation.
- Update selectors: data-section='summary' (was 'about'), .profile-section-card
for certs, .visible-list for current experience entry
- Strip login-wall noise injected into summary text after 'see more'
- Skip aria-hidden blurred placeholder experience items
- Add info callout in UI directing users to data export zip for full history
- app.py: wizard gate now reads get_config_dir()/user.yaml instead of
hardcoded repo-level config/ — fixes perpetual onboarding loop in
cloud mode where per-user wizard_complete was never seen
- app.py: page title corrected to "Peregrine"
- cloud_session.py: add get_config_dir() returning per-user config path
in cloud mode, repo config/ locally
- cloud_session.py: replace st.error() with JS redirect on missing/invalid
session token so users land on login page instead of error screen
- Home.py, 4_Apply.py, migrate.py: remove remaining AIHawk UI references
- Pop _linkedin_extracted before st.tabs() so tab_builder sees the
freshly populated _parsed_resume in the same render pass (no extra rerun needed)
- Fix tab label capitalisation: "Build Manually" (capital M) per spec
- Add st.rerun() after LinkedIn merge in Settings so form fields
refresh immediately to show the newly applied data