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:
dennisthiessenandClaude Opus 5 committed 2026-09-11 23:57:06 +02:00
1 parent 925a2d74a3
commit 34299bce54
25 files changed
+1427 -322

No files matched your search

+49 -23
View File
@@ -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():