Snapshot: full project state

This commit is contained in:
2026-10-06 23:43:21 -07:00
commit 1551408fc5
37 changed files with 2827 additions and 0 deletions

87
audit.py Normal file
View File

@@ -0,0 +1,87 @@
# Procyon — self-audit before send. Every item must PASS before an application
# is eligible for the user's approval/send gate.
import re
import db
import tailor
def run_audit(app_id, job, email_subject, email_body, tailored_resume):
"""Run the checklist, log results, return list of {item, result, detail}."""
checks = []
def add(item, result, detail):
checks.append({'item': item, 'result': result, 'detail': detail})
db.add_audit(app_id, job['id'], item, result, detail)
company = (job.get('company') or '').strip()
title = (job.get('title') or '').strip()
# 1. Employer name present in email (personalization signal, NOT a safety gate — the LLM
# often abbreviates ("PSI" for "Physical Superintelligence") or says "your team", and the
# subject line already carries the company, so a missing exact match is only a WARN).
if company:
if company.lower() in (email_body or '').lower():
add('Employer name in email', 'PASS', f'"{company}" appears in body')
else:
add('Employer name in email', 'WARN', f'"{company}" not verbatim in body (may be abbreviated)')
else:
add('Employer name in email', 'WARN', 'No company recorded — verify before send')
# 2. No leftover placeholder / other-company names
placeholders = ['[company]', '[name]', '{{', '}}', 'lorem', 'TODO', 'xxx']
found = [p for p in placeholders if p.lower() in (email_body or '').lower()]
add('No placeholder tokens', 'FAIL' if found else 'PASS',
f'placeholders: {found}' if found else 'clean')
# 3. Contact info present
phone = db.get_setting('resume_phone', '')
email = db.get_setting('resume_email', '')
missing = []
if phone and phone not in (email_body or ''):
missing.append('phone')
if email and email not in (email_body or ''):
missing.append('email')
add('Contact info in email', 'FAIL' if missing else 'PASS',
f'missing: {missing}' if missing else 'phone + email present')
# 4. Subject present and sane length
if not email_subject:
add('Email subject', 'FAIL', 'missing')
elif len(email_subject) > 120:
add('Email subject', 'FAIL', f'too long ({len(email_subject)} chars)')
else:
add('Email subject', 'PASS', email_subject)
# 5. Tailored resume non-empty and no fabrication markers
if not tailored_resume or len(tailored_resume) < 100:
add('Tailored resume', 'FAIL', 'too short / empty')
else:
add('Tailored resume', 'PASS', f'{len(tailored_resume)} chars')
# 6. Resume is a real deterministic resume (was "targeting header matches job" — removed:
# the resume is now deterministic and no longer carries a job-name header, so that check
# always false-warned).
if tailored_resume and len(tailored_resume) > 100:
add('Resume present', 'PASS', f'{len(tailored_resume)} chars, deterministic facts')
else:
add('Resume present', 'FAIL', 'resume missing')
# 7. ATS keyword coverage — quality signal, never blocks sending (informational only).
try:
kws = tailor.extract_job_keywords(job.get('description') or '', top_n=12)
cov, _kws, missing = tailor.ats_coverage(tailored_resume, kws)
if cov >= 0.5:
add('ATS keyword coverage', 'PASS', f'{int(cov*100)}% of top keywords present')
else:
add('ATS keyword coverage', 'WARN',
f'{int(cov*100)}% — resume may not strongly echo this role ({", ".join(missing[:5])})')
except Exception:
add('ATS keyword coverage', 'WARN', 'could not compute')
return checks
def all_pass(checks):
return all(c['result'] != 'FAIL' for c in checks)