fix: remove hardcoded personal values — Phase 1 audit findings
- 3_Resume_Editor.py: replace "Alex's" in docstring and caption - user_profile.py: expose mission_preferences and candidate_accessibility_focus - user.yaml.example: add mission_preferences section + candidate_accessibility_focus flag - generate_cover_letter.py: build _MISSION_NOTES from user profile instead of hardcoded personal passion notes; falls back to generic defaults when not set - company_research.py: gate "Inclusion & Accessibility" section behind candidate_accessibility_focus flag; section count adjusts (7 or 8) accordingly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
cbaa7e019e
commit
40bd297b14
5 changed files with 66 additions and 26 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# app/pages/3_Resume_Editor.py
|
||||
"""
|
||||
Resume Editor — form-based editor for Alex's AIHawk profile YAML.
|
||||
Resume Editor — form-based editor for the user's AIHawk profile YAML.
|
||||
FILL_IN fields highlighted in amber.
|
||||
"""
|
||||
import sys
|
||||
|
|
@ -12,7 +12,7 @@ import yaml
|
|||
|
||||
st.set_page_config(page_title="Resume Editor", page_icon="📝", layout="wide")
|
||||
st.title("📝 Resume Editor")
|
||||
st.caption("Edit Alex's application profile used by AIHawk for LinkedIn Easy Apply.")
|
||||
st.caption("Edit your application profile used by AIHawk for LinkedIn Easy Apply.")
|
||||
|
||||
RESUME_PATH = Path(__file__).parent.parent.parent / "aihawk" / "data_folder" / "plain_text_resume.yaml"
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,20 @@ career_summary: >
|
|||
|
||||
nda_companies: [] # e.g. ["FormerEmployer"] — masked in research briefs
|
||||
|
||||
# Optional: industries you genuinely care about.
|
||||
# When a company/JD matches an industry, the cover letter prompt injects
|
||||
# your personal note so Para 3 can reflect authentic alignment.
|
||||
# Leave a value empty ("") to use a sensible generic default.
|
||||
mission_preferences:
|
||||
music: "" # e.g. "I've played in bands for 15 years and care deeply about how artists get paid"
|
||||
animal_welfare: "" # e.g. "I volunteer at my local shelter every weekend"
|
||||
education: "" # e.g. "I tutored underserved kids for 3 years and care deeply about literacy"
|
||||
|
||||
# Set to true to include an Inclusion & Accessibility section in research briefs.
|
||||
# When true, each company brief will assess disability/ADA accommodation signals,
|
||||
# ERGs, and accessibility culture. Useful if this is a personal factor in your decisions.
|
||||
candidate_accessibility_focus: false
|
||||
|
||||
docs_dir: "~/Documents/JobSearch"
|
||||
ollama_models_dir: "~/models/ollama"
|
||||
vllm_models_dir: "~/models/vllm"
|
||||
|
|
|
|||
|
|
@ -370,6 +370,19 @@ def research_company(job: dict, use_scraper: bool = True, on_stage=None) -> dict
|
|||
_stage("Generating brief with LLM… (30–90 seconds)")
|
||||
name = _profile.name if _profile else "the candidate"
|
||||
career_summary = _profile.career_summary if _profile else ""
|
||||
accessibility_focus = _profile.candidate_accessibility_focus if _profile else False
|
||||
_section_count = 8 if accessibility_focus else 7
|
||||
_accessibility_section = """
|
||||
## Inclusion & Accessibility
|
||||
Assess {company}'s commitment to disability inclusion and accessibility. Cover:
|
||||
- ADA accommodation language in job postings or company policy
|
||||
- Disability Employee Resource Group (ERG) or affinity group
|
||||
- Product or service accessibility (WCAG compliance, adaptive features, AT integrations)
|
||||
- Any public disability/accessibility advocacy, partnerships, or certifications
|
||||
- Glassdoor or press signals about how employees with disabilities experience the company
|
||||
If no specific signals are found, say so clearly — absence of public commitment is itself signal.
|
||||
This section is for the candidate's personal decision-making only and will not appear in any application.
|
||||
""".format(company=company) if accessibility_focus else ""
|
||||
prompt = f"""You are preparing {name} for a job interview.
|
||||
{f"Candidate background: {career_summary}" if career_summary else ""}
|
||||
|
||||
|
|
@ -385,8 +398,8 @@ Role: **{title}** at **{company}**
|
|||
|
||||
---
|
||||
|
||||
Produce a structured research brief using **exactly** these eight markdown section headers
|
||||
(include all eight even if a section has limited data — say so honestly):
|
||||
Produce a structured research brief using **exactly** these {_section_count} markdown section headers
|
||||
(include all {_section_count} even if a section has limited data — say so honestly):
|
||||
|
||||
## Company Overview
|
||||
What {company} does, core product/service, business model, size/stage (startup / scale-up / enterprise), market positioning.
|
||||
|
|
@ -408,16 +421,7 @@ Draw on the live snippets above; if none available, note what is publicly known.
|
|||
Culture issues, layoffs, exec departures, financial stress, or Glassdoor concerns worth knowing before the call.
|
||||
If nothing notable, write "No significant red flags identified."
|
||||
|
||||
## Inclusion & Accessibility
|
||||
Assess {company}'s commitment to disability inclusion and accessibility. Cover:
|
||||
- ADA accommodation language in job postings or company policy
|
||||
- Disability Employee Resource Group (ERG) or affinity group
|
||||
- Product or service accessibility (WCAG compliance, adaptive features, AT integrations)
|
||||
- Any public disability/accessibility advocacy, partnerships, or certifications
|
||||
- Glassdoor or press signals about how employees with disabilities experience the company
|
||||
If no specific signals are found, say so clearly — absence of public commitment is itself signal.
|
||||
This section is for the candidate's personal decision-making only and will not appear in any application.
|
||||
|
||||
{_accessibility_section}
|
||||
## Talking Points for {name}
|
||||
Five specific talking points for the phone screen. Each must:
|
||||
- Reference a concrete experience from {name}'s matched background by name
|
||||
|
|
|
|||
|
|
@ -62,27 +62,45 @@ _MISSION_SIGNALS: dict[str, list[str]] = {
|
|||
|
||||
_candidate = _profile.name if _profile else "the candidate"
|
||||
|
||||
_MISSION_NOTES: dict[str, str] = {
|
||||
_MISSION_DEFAULTS: dict[str, str] = {
|
||||
"music": (
|
||||
f"This company is in the music industry, which is one of {_candidate}'s genuinely "
|
||||
"ideal work environments — they have a real personal passion for the music scene. "
|
||||
"Para 3 should warmly and specifically reflect this authentic alignment, not as "
|
||||
"a generic fan statement, but as an honest statement of where they'd love to apply "
|
||||
"their CS skills."
|
||||
f"This company is in the music industry — an industry {_candidate} finds genuinely "
|
||||
"compelling. Para 3 should warmly and specifically reflect this authentic alignment, "
|
||||
"not as a generic fan statement, but as an honest statement of where they'd love to "
|
||||
"apply their skills."
|
||||
),
|
||||
"animal_welfare": (
|
||||
f"This organization works in animal welfare/rescue — one of {_candidate}'s dream-job "
|
||||
"domains and a genuine personal passion. Para 3 should reflect this authentic "
|
||||
"connection warmly and specifically, tying their CS skills to this mission."
|
||||
f"This organization works in animal welfare/rescue — a mission {_candidate} finds "
|
||||
"genuinely meaningful. Para 3 should reflect this authentic connection warmly and "
|
||||
"specifically, tying their skills to this mission."
|
||||
),
|
||||
"education": (
|
||||
f"This company works in children's education or EdTech — one of {_candidate}'s ideal "
|
||||
"work domains, reflecting genuine personal values around learning and young people. "
|
||||
"Para 3 should reflect this authentic connection specifically and warmly."
|
||||
f"This company works in education or EdTech — a domain that resonates with "
|
||||
f"{_candidate}'s values. Para 3 should reflect this authentic connection specifically "
|
||||
"and warmly."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _build_mission_notes() -> dict[str, str]:
|
||||
"""Merge user's custom mission notes with generic defaults."""
|
||||
prefs = _profile.mission_preferences if _profile else {}
|
||||
notes = {}
|
||||
for industry, default_note in _MISSION_DEFAULTS.items():
|
||||
custom = (prefs.get(industry) or "").strip()
|
||||
if custom:
|
||||
notes[industry] = (
|
||||
f"Mission alignment — {_candidate} shared: \"{custom}\". "
|
||||
"Para 3 should warmly and specifically reflect this authentic connection."
|
||||
)
|
||||
else:
|
||||
notes[industry] = default_note
|
||||
return notes
|
||||
|
||||
|
||||
_MISSION_NOTES = _build_mission_notes()
|
||||
|
||||
|
||||
def detect_mission_alignment(company: str, description: str) -> str | None:
|
||||
"""Return a mission hint string if company/JD matches a preferred industry, else None."""
|
||||
text = f"{company} {description}".lower()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ _DEFAULTS = {
|
|||
"ollama_models_dir": "~/models/ollama",
|
||||
"vllm_models_dir": "~/models/vllm",
|
||||
"inference_profile": "remote",
|
||||
"mission_preferences": {},
|
||||
"candidate_accessibility_focus": False,
|
||||
"services": {
|
||||
"streamlit_port": 8501,
|
||||
"ollama_host": "localhost",
|
||||
|
|
@ -58,6 +60,8 @@ class UserProfile:
|
|||
self.ollama_models_dir: Path = Path(data["ollama_models_dir"]).expanduser().resolve()
|
||||
self.vllm_models_dir: Path = Path(data["vllm_models_dir"]).expanduser().resolve()
|
||||
self.inference_profile: str = data["inference_profile"]
|
||||
self.mission_preferences: dict[str, str] = data.get("mission_preferences", {})
|
||||
self.candidate_accessibility_focus: bool = bool(data.get("candidate_accessibility_focus", False))
|
||||
self._svc = data["services"]
|
||||
|
||||
# ── Service URLs ──────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Reference in a new issue