JAA-ATS-Tool / scripts /verify_feedback_repair.py
saitejatirunagari's picture
feat(ats): External ATS Feedback Repair Mode (honest path toward 95%)
f5dd879
Raw
History Blame
3.88 kB
"""
Deterministic regression for External ATS Feedback Repair Mode.
Uses StubProvider (no API keys). Pastes fake external-checker feedback that mixes:
- LOW/plausible PM terms (should be woven in),
- a BLOCKED credential (CISSP β€” must NEVER appear),
- a HIGH-risk specialized term (SIEM β€” must NOT be auto-added; surfaced for
confirmation).
Asserts the honesty guarantees hold: blocked/high terms stay OUT of the resume,
scores come from the re-parsed export, and the result carries the right buckets.
"""
import os, sys, io, shutil
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.path.insert(0, os.path.abspath("."))
# Isolate the vault so prior confirmations don't change results.
import src.candidate_vault as _cv
_cv._VAULT_PATH = "data/_test_vault_feedback.json"
if os.path.exists(_cv._VAULT_PATH):
os.remove(_cv._VAULT_PATH)
import shutil as _sh
src_pdf = r"C:\Users\Nxtwave\Desktop\resume\Saiteja_Tirunagari_Resume A 26 - Copy.pdf"
dst = "data/resume/resume.pdf"
os.makedirs("data/resume", exist_ok=True)
if not os.path.exists(dst) and os.path.exists(src_pdf):
_sh.copyfile(src_pdf, dst)
from src.resume_parser_v2 import parse_resume_pdf
from src.providers import StubProvider
from src.jobalytics_repair import parse_external_feedback, repair_with_external_feedback
from src.resume_customizer import _read_docx_text
ok = True
def check(name, cond, detail=""):
global ok; ok = ok and cond
print(f" [{'PASS' if cond else 'FAIL'}] {name} {detail}")
# 1. Feedback parser
fb = ("Resume Match 73%. Missing keywords: roadmap, stakeholder management, "
"A/B testing, CISSP, SIEM. Matched keywords: agile, jira")
parsed = parse_external_feedback(fb)
print("1. parse_external_feedback:")
check("external_score parsed = 73", parsed["external_score"] == 73, str(parsed["external_score"]))
check("missing keywords parsed", "roadmap" in [m.lower() for m in parsed["missing_keywords"]]
and "cissp" in [m.lower() for m in parsed["missing_keywords"]])
check("no trailing punctuation", all(not m.endswith(".") for m in parsed["missing_keywords"]))
# 2. Repair honesty
base = parse_resume_pdf(dst)
jd = open("tests/fixtures/jds/generic_pm_3_7yrs.txt", encoding="utf-8").read()
job = {"title": "Product Manager", "company": "TestCo", "description": jd, "_raw_assessment": {}}
res = repair_with_external_feedback(job, feedback_text=fb, provider=StubProvider(),
base_resume=base, output_dir="data/output/resumes/_fb_test")
print("2. repair_with_external_feedback:", res.get("status"), res.get("scores"))
check("no error", "error" not in res, res.get("error", ""))
check("CISSP is blocked", "CISSP" in res.get("blocked_terms", []))
check("SIEM is unresolved-high", "SIEM" in res.get("unresolved_high_risk_terms", []))
check("external_score carried = 73", res.get("external_score") == 73)
check("scores present (internal/independent)",
isinstance(res.get("scores", {}).get("internal_jd_match"), int)
and isinstance(res.get("scores", {}).get("independent_jd_match"), int))
txt = _read_docx_text(res["resume_path"]).lower()
check("CISSP NOT in resume (no fabricated cert)", "cissp" not in txt)
check("SIEM NOT auto-added (high-risk, unconfirmed)", "siem" not in txt)
# 3. No-keywords guard
res2 = repair_with_external_feedback(job, feedback_text="great resume, no notes",
provider=StubProvider(), base_resume=base,
output_dir="data/output/resumes/_fb_test")
check("empty feedback -> no_missing_keywords error", res2.get("error") == "no_missing_keywords")
print("\n" + ("βœ“ ALL FEEDBACK-REPAIR CHECKS PASS" if ok else "βœ— SOME FAILED"))
shutil.rmtree("data/output/resumes/_fb_test", ignore_errors=True)
if os.path.exists(_cv._VAULT_PATH):
os.remove(_cv._VAULT_PATH)
sys.exit(0 if ok else 1)