fix: follow-up review corrections, BIS rejection, and a gitignore miss
Second-pass review of this session's work by another agent. It corrected several
real mistakes of mine and finished F06 gaps I had left open. Reviewed, verified
and committed as-is.
THE MISS THAT MATTERS
`job_scout/state/applications.json` — the authoritative application registry
introduced by F02 — was never actually tracked. `job_scout/state/*` in
.gitignore silently excluded it, so my checkpoint commit afdef7e claimed to add
it and did not. Only `application_cohort.json`, the GENERATED projection, was
version-controlled; the source of truth and the migration history backup were
not. .gitignore now carries explicit negations for both.
MY ERRORS, CORRECTED
- "kastellaun" was in CH_LOCATION_KEYWORDS. Kastellaun is in Rhineland-Palatinate.
I took it from a Bundeswehr reserve listing and filed it under Switzerland -
precisely the class of bug F12 existed to fix.
- The F12 fix only widened the keyword list; SmartRecruiters still discarded the
structured country, which was the concrete case the review named
(city=Baar, country=CH). The adapter now preserves it, and a COUNTRY_NAMES
mapping gives country precedence over a bare town name.
- F15 was applied to Taleo and Prospective only. Greenhouse still wrote
`updated_at` into `posted`; it now uses `published_at` and carries provenance.
- My negation guard `\b(no|not|...)\b[^.;]{0,60}$` was too loose - an unrelated
"not" within 60 characters could mask a real claim. Tightened to require the
negation to govern the term.
- F18's retirement of the macro-count rule missed "Four to six compact skills
lines" in the workflow; removed now.
F06 GAPS I LEFT OPEN, NOW CLOSED
Date-format validation on education/employment periods (the 2035-99 case named
in the review), evidence-structure validation, referential integrity on evidence
source files, and the missing-identity KeyError.
F10 TAKEN FURTHER, AND RIGHTLY
I demoted the US-GPA fields to *_orientation_only. They are now removed
entirely, with grade_output_rule saying what to do instead: use the German grade
and scale, and if a recipient requires a US GPA, use THEIR method and record it.
Keeping a number derived by a non-recognised method invites its use.
Also: the Capgemini omit-preference now sits in experience_capgemini.md itself,
alongside the instruction not to fill or explain the intentional gap.
BIS BASEL - REJECTED 2026-09-11
jr100429, applied 2026-07-10, no interview, no reason given. Recorded in the
registry with a dated event and in the decision log; the "prep interview brief
when screening lands" next action is cleared. This also settles the 2026-09-04
observation that the req had vanished from the BIS board - it was a real
rejection, not just a closed posting.
VERIFICATION I RAN MYSELF BEFORE COMMITTING
49/49 tests. Validator PASS with 0 advisory warnings. Package manifest and
registry views current. All 12 F12 location cases still correct, including
Kastellaun now rejecting. The F05 counter-proof still catches all 9 violations.
SBB, NVIDIA, RUAG and Kdo Cy resumes still scan clean. Diffs are net-additive
across every reference file - the deletions are replacements, not losses.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qej5SBvhcbGXHwLqNWiskM
This commit is contained in:
1 parent
925a2d74a3
commit
34299bce54
25 files changed
+1427
-322
No files matched your search
@@ -6,9 +6,9 @@ A package folder that contains a PDF looks finished. It is not necessarily repro
|
||||
one happens to be installed elsewhere, and its submitted PDF differs in content from the
|
||||
e2e source's PDF. The existing PDF hid both problems.
|
||||
|
||||
This records, per package: which sources exist, the hash of the class each one would compile
|
||||
against, whether a PDF is present, and whether the submission-named PDF still matches the
|
||||
e2e one. It does NOT rewrite anything - historical packages are evidence.
|
||||
Records source/class content hashes. Optional PDF snapshots detect changed local artifacts;
|
||||
missing PDFs are allowed because generated PDFs are deliberately not tracked in Git.
|
||||
Filename association is not proof that a PDF was built from the current source.
|
||||
|
||||
python resume_builder/helpers/package_manifest.py # write the manifest
|
||||
python resume_builder/helpers/package_manifest.py --check # fail if it is stale
|
||||
@@ -34,17 +34,19 @@ CTAN_CLASSES = {"article", "report", "book", "letter", "moderncv", "scrartcl", "
|
||||
|
||||
|
||||
def digest(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()[:16]
|
||||
# Git/Windows may change line endings without changing the TeX source.
|
||||
data = path.read_text(encoding="utf-8").encode("utf-8") if path.suffix in {".tex", ".cls"} else path.read_bytes()
|
||||
return hashlib.sha256(data).hexdigest()[:16]
|
||||
|
||||
|
||||
def describe(folder: Path) -> dict:
|
||||
sources = sorted(p.name for p in folder.glob("*.tex"))
|
||||
def describe(folder: Path, record_pdfs: bool = False) -> dict:
|
||||
sources = {p.name: digest(p) for p in sorted(folder.glob("*.tex"))}
|
||||
classes = {p.name: digest(p) for p in folder.glob("*.cls")}
|
||||
pdfs = sorted(p.name for p in folder.glob("*.pdf"))
|
||||
needs = set()
|
||||
uses_today = []
|
||||
for name in sources:
|
||||
text = (folder / name).read_text(encoding="utf-8", errors="replace")
|
||||
text = re.sub(r"(?<!\\)%.*$", "", text, flags=re.M)
|
||||
match = re.search(r"\\documentclass(?:\[[^\]]*\])?\{([^}]+)\}", text)
|
||||
if match:
|
||||
needs.add(match.group(1))
|
||||
@@ -68,20 +70,19 @@ def describe(folder: Path) -> dict:
|
||||
f"rebuild is date-dependent: {', '.join(uses_today)} use \\today, so recompiling a "
|
||||
f"historical package silently restamps it with the current date. New packages must "
|
||||
f"carry the explicit application date instead.")
|
||||
if sources and not pdfs:
|
||||
problems.append("no PDF present: nothing reviewable without a build")
|
||||
|
||||
return {
|
||||
entry = {
|
||||
"sources": sources,
|
||||
"class_files": classes,
|
||||
"requires_class": sorted(needs),
|
||||
"pdfs": pdfs,
|
||||
"uses_today": uses_today,
|
||||
"problems": problems,
|
||||
}
|
||||
if record_pdfs:
|
||||
entry["pdf_artifacts"] = {p.name: digest(p) for p in sorted(folder.glob("*.pdf"))}
|
||||
return entry
|
||||
|
||||
|
||||
def build() -> dict:
|
||||
def build(record_pdfs: bool = False) -> dict:
|
||||
register = json.loads(REGISTER.read_text(encoding="utf-8"))
|
||||
classified = set()
|
||||
for key in ("unsafe_do_not_reuse", "historical_revalidate", "paused_no_package"):
|
||||
@@ -96,7 +97,7 @@ def build() -> dict:
|
||||
# submission and has nothing to classify for reuse.
|
||||
if not any(folder.glob("*resume*.tex")) and not any(folder.glob("*cover*letter*.tex")):
|
||||
continue
|
||||
entry = describe(folder)
|
||||
entry = describe(folder, record_pdfs)
|
||||
entry["reuse_classification"] = (
|
||||
"recorded" if f"output/{folder.name}" in classified else "UNCLASSIFIED")
|
||||
if f"output/{folder.name}" not in classified:
|
||||
@@ -107,32 +108,57 @@ def build() -> dict:
|
||||
packages[folder.name] = entry
|
||||
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"schema_version": 2,
|
||||
"generated_note": (
|
||||
"Generated by package_manifest.py (review finding F17). A PDF in a folder is not "
|
||||
"evidence that the package is reproducible. This file records what would actually "
|
||||
"happen on a rebuild. It is descriptive: nothing here authorises rewriting a "
|
||||
"evidence that the package is reproducible. Source and class hashes detect edits; "
|
||||
"optional pdf_artifacts are observations, not verified source-to-PDF mappings. "
|
||||
"Absent PDFs are allowed; present PDFs must match their recorded hash. "
|
||||
"Nothing here authorises rewriting a "
|
||||
"submitted document."),
|
||||
"packages": packages,
|
||||
}
|
||||
|
||||
|
||||
def check_manifest(recorded: dict, current: dict, output: Path = OUTPUT) -> list[str]:
|
||||
# Compare the portable source inventory separately from optional local build products.
|
||||
baseline = json.loads(json.dumps(recorded))
|
||||
errors = []
|
||||
for name, entry in baseline.get("packages", {}).items():
|
||||
for filename, expected in entry.pop("pdf_artifacts", {}).items():
|
||||
path = output / name / filename
|
||||
if path.exists() and digest(path) != expected:
|
||||
errors.append(f"changed PDF artifact: {name}/{filename}; inspect before recording a new baseline")
|
||||
if baseline != current:
|
||||
errors.append("source manifest is stale; inspect changes, then run package_manifest.py")
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--check", action="store_true", help="fail if the manifest is stale")
|
||||
parser.add_argument("--record-pdfs", action="store_true", help="explicitly snapshot current local PDF hashes (not proof of source association)")
|
||||
args = parser.parse_args()
|
||||
|
||||
manifest = build()
|
||||
text = json.dumps(manifest, indent=2, ensure_ascii=False) + "\n"
|
||||
if args.check and args.record_pdfs:
|
||||
parser.error("--check cannot refresh PDF baselines")
|
||||
recorded = json.loads(MANIFEST.read_text(encoding="utf-8")) if MANIFEST.exists() else {}
|
||||
manifest = build(record_pdfs=args.record_pdfs)
|
||||
if args.check:
|
||||
current = MANIFEST.read_text(encoding="utf-8") if MANIFEST.exists() else ""
|
||||
if current != text:
|
||||
print("ERROR: package manifest is stale; re-run package_manifest.py")
|
||||
errors = check_manifest(recorded, manifest)
|
||||
if errors:
|
||||
for error in errors:
|
||||
print("ERROR: " + error)
|
||||
return 1
|
||||
print("Package manifest is current.")
|
||||
return 0
|
||||
|
||||
MANIFEST.write_text(text, encoding="utf-8")
|
||||
if not args.record_pdfs:
|
||||
for name, entry in manifest["packages"].items():
|
||||
old = recorded.get("packages", {}).get(name, {})
|
||||
if "pdf_artifacts" in old:
|
||||
entry["pdf_artifacts"] = old["pdf_artifacts"]
|
||||
MANIFEST.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||||
flagged = {name: entry for name, entry in manifest["packages"].items() if entry["problems"]}
|
||||
print(f"{len(manifest['packages'])} packages described, {len(flagged)} with findings\n")
|
||||
for name, entry in flagged.items():
|
||||
|
||||
Reference in new issue
Block a user