fix(F17): a PDF in a folder is not proof the package can be rebuilt
Build sweep run this session: 48 sources, 46 built, 2 failed. Infineon_AI_Engineer's cover letter fails on font expansion (the font is not scalable); cv_template.tex fails on unfilled placeholders, which is expected for the academic legacy path. Overfull boxes: Microsoft ISE 25.33pt, Isovalent 0.88pt. All historical, all left alone. New: resume_builder/helpers/package_manifest.py records, per application package, what a rebuild would ACTUALLY do - sources, class-file hashes, PDFs present, and the reasons a rebuild would not reproduce the submitted document. It is descriptive; nothing in it authorises rewriting a submitted file. What it found across 21 packages: - 17 are DATE-DEPENDENT. The cover letters use oday, so recompiling a historical package silently restamps it with today's date. - 1 has an INCOMPLETE SOURCE DISTRIBUTION: output/NATO_AI_ENGINEER ships no resume.cls. It builds on this machine only because a copy exists elsewhere, and the shipped PDF hid that. Not repaired by copying the newest class in - the repo carries THREE different resume.cls variants (13/7/1 copies) and older macros differ, so that "fix" could silently change a historical document. - 1 has no PDF at all, so nothing is reviewable without a build. Classification: 7 application packages were in no reuse category. Added to historical_revalidate, and the register now records the default explicitly - anything unlisted is treated as revalidate-before-reuse. Being recent is not the same as being verified; none of the seven was re-read for this. Prevention rather than repair: oday removed from the cover-letter template so new packages stop adding to the pile. A test now fails if it comes back, if the manifest goes stale, or if a package is left unclassified. Also: scan_document gained a template mode. A placeholder in a TEMPLATE is what makes it a template; the defect is one surviving into a generated document. 37/37 tests pass; counter-proof still catches all 9. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qej5SBvhcbGXHwLqNWiskM
This commit is contained in:
1 parent
c7e8d4e058
commit
eecdbdade6
7 files changed
+797
-24
No files matched your search
+18
-7
@@ -6,13 +6,12 @@ Do not rewrite submitted application archives or invent missing evidence.
|
|||||||
|
|
||||||
## Resume point
|
## Resume point
|
||||||
|
|
||||||
F01–F16 complete. **Next: F17** — reproducible builds and safe historical-archive
|
F01–F17 complete. **Next: F18** — evidence earlier on page one and a rendered skills budget.
|
||||||
classification. Start from the 13 older packages listed in the F05/F06 verification notes
|
F18/F19 change how the CVs look; the user authorised this on 2026-09-11. They need compiling
|
||||||
below; they carry genuine findings under today's rules and must NOT be silently rewritten.
|
and visual inspection, not blind patching, so budget for a build loop.
|
||||||
F17 also needs the 45-document LaTeX sweep, which has not been re-run this session.
|
|
||||||
Read this file and inspect `git log`/`git diff` before resuming; do not repeat completed work.
|
Read this file and inspect `git log`/`git diff` before resuming; do not repeat completed work.
|
||||||
|
|
||||||
Checkpoints: `afdef7e` (F01+F02) · `fc5f3e4` (F03–F06) · `ce5b7f5` (F07) · `3519b2f` (F08–F10) · `afd8174` (F11–F13) · `ce8619b` (F14) · plus F15–F16.
|
Checkpoints: `afdef7e` (F01+F02) · `fc5f3e4` (F03–F06) · `ce5b7f5` (F07) · `3519b2f` (F08–F10) · `afd8174` (F11–F13) · `ce8619b` (F14) · `c7e8d4e` (F15–F16) · plus F17.
|
||||||
|
|
||||||
## User rulings — 2026-09-11
|
## User rulings — 2026-09-11
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ Checkpoints: `afdef7e` (F01+F02) · `fc5f3e4` (F03–F06) · `ce5b7f5` (F07) ·
|
|||||||
| F14 | DONE | Buckets renamed to keyword relevance with an explicit 'NOT a fit judgement'; German titles and platform/DevOps vocabulary added. Kdo Cy *ICT-System Engineer (DevSecOps/Platform Automation)* went 0→10; *Senior Dateningenieur* 1→3, level with its English twin |
|
| F14 | DONE | Buckets renamed to keyword relevance with an explicit 'NOT a fit judgement'; German titles and platform/DevOps vocabulary added. Kdo Cy *ICT-System Engineer (DevSecOps/Platform Automation)* went 0→10; *Senior Dateningenieur* 1→3, level with its English twin |
|
||||||
| F15 | DONE | Taleo deadline moved out of `posted` (a closing date was making roles look freshly published); `unit`, `compensation`, `contact` and `date_provenance` carried as fields through to the report and JSON. Named contacts now surface — directly serving the channel rule |
|
| F15 | DONE | Taleo deadline moved out of `posted` (a closing date was making roles look freshly published); `unit`, `compensation`, `contact` and `date_provenance` carried as fields through to the report and JSON. Named contacts now surface — directly serving the channel rule |
|
||||||
| F16 | DONE | Restricted runs write `<date>.only-x.md` and leave the daily report intact; seen-state committed only after the report is on disk, and never by a restricted run; unique temp names for atomic writes |
|
| F16 | DONE | Restricted runs write `<date>.only-x.md` and leave the daily report intact; seen-state committed only after the report is on disk, and never by a restricted run; unique temp names for atomic writes |
|
||||||
| F17 | TODO | Reproducible new builds and safe historical archive classification |
|
| F17 | DONE | Build sweep 46/48; `package_manifest.py` records per package what a rebuild would really do; 7 unclassified packages registered with a safe default; ` oday` removed from the cover-letter template. Historical packages untouched |
|
||||||
| F18 | TODO | Evidence earlier on page one; rendered skills budget |
|
| F18 | TODO | Evidence earlier on page one; rendered skills budget |
|
||||||
| F19 | TODO | Education layout, phrasing guidance, reproducible word counts |
|
| F19 | TODO | Education layout, phrasing guidance, reproducible word counts |
|
||||||
|
|
||||||
@@ -96,7 +95,19 @@ mid-remediation — it would destroy the ability to attribute a regression to a
|
|||||||
`--only=kdocy` wrote `2026-09-11.only-kdocy.md` while leaving the full report untouched.
|
`--only=kdocy` wrote `2026-09-11.only-kdocy.md` while leaving the full report untouched.
|
||||||
- F16 context: the daily-report clobber is not hypothetical. It happened twice in this session
|
- F16 context: the daily-report clobber is not hypothetical. It happened twice in this session
|
||||||
(2026-09-10 and 09-11) and both times the full run had to be repeated by hand.
|
(2026-09-10 and 09-11) and both times the full run had to be repeated by hand.
|
||||||
- Not re-run this session: the 45-document LaTeX build sweep and the visual sampling.
|
- F17 build sweep RUN this session: 48 sources, 46 built, 2 failed —
|
||||||
|
`Infineon_AI_Engineer` cover letter (font expansion: the font is not scalable) and
|
||||||
|
`cv_template.tex` (unfilled placeholders, expected for the academic legacy template).
|
||||||
|
Overfull boxes: Microsoft ISE 25.33pt, Isovalent 0.88pt. Both are historical; not touched.
|
||||||
|
- F17 findings across 21 application packages: **17 are date-dependent** (` oday` in the cover
|
||||||
|
letter, so a rebuild restamps them), **1 has an incomplete source distribution** (NATO ships no
|
||||||
|
`resume.cls` and only builds because a copy exists elsewhere on this machine), 1 has no PDF.
|
||||||
|
The repo carries **three different `resume.cls` variants** (13/7/1 copies), which is exactly why
|
||||||
|
copying the newest one into NATO would not be a safe repair.
|
||||||
|
- F17 classification: 7 packages were unregistered; added to `historical_revalidate`, with a
|
||||||
|
recorded default that anything unlisted is treated that way. Being recent is not being verified —
|
||||||
|
none was re-read for this.
|
||||||
|
- Still not re-run: the visual page sampling (F18).
|
||||||
|
|
||||||
## Completion rules
|
## Completion rules
|
||||||
|
|
||||||
|
|||||||
@@ -2,22 +2,68 @@
|
|||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"policy": "Generated output is never a source for a new application. Regenerate from canonical claims and normalized experience files.",
|
"policy": "Generated output is never a source for a new application. Regenerate from canonical claims and normalized experience files.",
|
||||||
"unsafe_do_not_reuse": [
|
"unsafe_do_not_reuse": [
|
||||||
{"folder": "output/Apple_Data_Engineer", "reasons": ["fabricated LangChain", "unsupported petabyte scale", "overstated platform and agentic ownership"]},
|
{
|
||||||
{"folder": "output/Infineon_AI_Engineer", "reasons": ["fabricated LangChain"]},
|
"folder": "output/Apple_Data_Engineer",
|
||||||
{"folder": "output/Equinor_AI_Manager", "reasons": ["unsupported model serving and evaluation", "inflated responsible-AI and security ownership"]},
|
"reasons": [
|
||||||
{"folder": "output/Microsoft_ISE_Senior_SWE", "reasons": ["unsupported model-evaluation skill", "customer-delivery framing exceeds verified evidence"]},
|
"fabricated LangChain",
|
||||||
{"folder": "output/Kraken_SRE_AI_Agents", "reasons": ["Security Champion scope inflation", "stretch-role framing requires complete regeneration"]},
|
"unsupported petabyte scale",
|
||||||
{"folder": "output/Kraken_AI_Infrastructure", "reasons": ["pre-canonical package", "agent-infrastructure bridges require revalidation"]}
|
"overstated platform and agentic ownership"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"folder": "output/Infineon_AI_Engineer",
|
||||||
|
"reasons": [
|
||||||
|
"fabricated LangChain"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"folder": "output/Equinor_AI_Manager",
|
||||||
|
"reasons": [
|
||||||
|
"unsupported model serving and evaluation",
|
||||||
|
"inflated responsible-AI and security ownership"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"folder": "output/Microsoft_ISE_Senior_SWE",
|
||||||
|
"reasons": [
|
||||||
|
"unsupported model-evaluation skill",
|
||||||
|
"customer-delivery framing exceeds verified evidence"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"folder": "output/Kraken_SRE_AI_Agents",
|
||||||
|
"reasons": [
|
||||||
|
"Security Champion scope inflation",
|
||||||
|
"stretch-role framing requires complete regeneration"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"folder": "output/Kraken_AI_Infrastructure",
|
||||||
|
"reasons": [
|
||||||
|
"pre-canonical package",
|
||||||
|
"agent-infrastructure bridges require revalidation"
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"historical_revalidate": [
|
"historical_revalidate": [
|
||||||
"output/Google_Senior_Data_Engineer",
|
"output/AkerBP_Data_Product_Architect",
|
||||||
"output/Snowflake_Observe_Enterprise",
|
|
||||||
"output/QuantCo_Cloud_Engineer",
|
|
||||||
"output/NATO_AI_ENGINEER",
|
|
||||||
"output/BIS",
|
"output/BIS",
|
||||||
"output/Isovalent_DataEngineer",
|
"output/CitadelSecurities_PlatformEngineer",
|
||||||
|
"output/Google_Business_Home",
|
||||||
|
"output/Google_Senior_Data_Engineer",
|
||||||
"output/Infineon",
|
"output/Infineon",
|
||||||
"output/Microsoft_Principal_FDE_SWE"
|
"output/Isovalent_DataEngineer",
|
||||||
|
"output/KdoCy_DevOps_Data_Platform",
|
||||||
|
"output/Microsoft_Principal_FDE_SWE",
|
||||||
|
"output/NATO_AI_ENGINEER",
|
||||||
|
"output/NVIDIA_SrDevOps_Zurich",
|
||||||
|
"output/QuantCo_Cloud_Engineer",
|
||||||
|
"output/RUAG_AI_Engineer_C5I",
|
||||||
|
"output/SBB_DataEngineer_AssetMgmt",
|
||||||
|
"output/Snowflake_Observe_Enterprise"
|
||||||
],
|
],
|
||||||
"paused_no_package": ["output/Google_FDE_GenAI"]
|
"paused_no_package": [
|
||||||
|
"output/Google_FDE_GenAI"
|
||||||
|
],
|
||||||
|
"classification_default": "Any package folder NOT listed in this file defaults to historical_revalidate: never reuse a bullet from it without re-checking the claim against claims.json first. Recorded 2026-09-11 (review finding F17), which found 8 unclassified folders against 14 classified ones. The seven application packages among them were added to historical_revalidate; being recent is not the same as being verified, and none of them was re-read for this classification."
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,534 @@
|
|||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"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 submitted document.",
|
||||||
|
"packages": {
|
||||||
|
"AkerBP_Data_Product_Architect": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_akerbp_data_product_architect_cover_letter.tex",
|
||||||
|
"e2e_akerbp_data_product_architect_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b3a032a850cd0d6d"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"article",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_akerbp_data_product_architect_cover_letter.pdf",
|
||||||
|
"e2e_akerbp_data_product_architect_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [],
|
||||||
|
"problems": [],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Apple_Data_Engineer": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_apple_data_engineer_cover_letter.tex",
|
||||||
|
"e2e_apple_data_engineer_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"e2e_apple_data_engineer_cover_letter.pdf",
|
||||||
|
"e2e_apple_data_engineer_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_apple_data_engineer_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_apple_data_engineer_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"BIS": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_bis_senior_data_analytics_ai_cover_letter.tex",
|
||||||
|
"e2e_bis_senior_data_analytics_ai_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"critique_resume_check.pdf",
|
||||||
|
"e2e_bis_senior_data_analytics_ai_cover_letter.pdf",
|
||||||
|
"e2e_bis_senior_data_analytics_ai_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_bis_senior_data_analytics_ai_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_bis_senior_data_analytics_ai_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"CitadelSecurities_PlatformEngineer": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_citadel_securities_platform_engineer_cover_letter.tex",
|
||||||
|
"e2e_citadel_securities_platform_engineer_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b3a032a850cd0d6d"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"article",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_citadel_securities_platform_engineer_cover_letter.pdf",
|
||||||
|
"e2e_citadel_securities_platform_engineer_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [],
|
||||||
|
"problems": [],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Equinor_AI_Manager": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_equinor_ai_manager_cover_letter.tex",
|
||||||
|
"e2e_equinor_ai_manager_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_equinor_ai_manager_cover_letter.pdf",
|
||||||
|
"e2e_equinor_ai_manager_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_equinor_ai_manager_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_equinor_ai_manager_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Google_Business_Home": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_google_software_engineer_iii_business_home_cover_letter.tex",
|
||||||
|
"e2e_google_software_engineer_iii_business_home_cover_letter_v2.tex",
|
||||||
|
"e2e_google_software_engineer_iii_business_home_resume.tex",
|
||||||
|
"e2e_google_software_engineer_iii_business_home_resume_v2.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b3a032a850cd0d6d"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"article",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_google_software_engineer_iii_business_home_cover_letter.pdf",
|
||||||
|
"e2e_google_software_engineer_iii_business_home_cover_letter_v2.pdf",
|
||||||
|
"e2e_google_software_engineer_iii_business_home_resume.pdf",
|
||||||
|
"e2e_google_software_engineer_iii_business_home_resume_v2.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [],
|
||||||
|
"problems": [],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Google_Senior_Data_Engineer": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_google_senior_data_engineer_cover_letter.tex",
|
||||||
|
"e2e_google_senior_data_engineer_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_google_senior_data_engineer_cover_letter.pdf",
|
||||||
|
"e2e_google_senior_data_engineer_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_google_senior_data_engineer_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_google_senior_data_engineer_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Infineon": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_infineon_doctoral_cover_letter.tex",
|
||||||
|
"e2e_infineon_doctoral_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_infineon_doctoral_cover_letter.pdf",
|
||||||
|
"e2e_infineon_doctoral_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_infineon_doctoral_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_infineon_doctoral_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Infineon_AI_Engineer": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_infineon_ai_engineer_cover_letter.tex",
|
||||||
|
"e2e_infineon_ai_engineer_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"e2e_infineon_ai_engineer_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_infineon_ai_engineer_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_infineon_ai_engineer_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Isovalent_DataEngineer": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_isovalent_data_engineer_cover_letter.tex",
|
||||||
|
"e2e_isovalent_data_engineer_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_isovalent_data_engineer_cover_letter.pdf",
|
||||||
|
"e2e_isovalent_data_engineer_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_isovalent_data_engineer_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_isovalent_data_engineer_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"KdoCy_DevOps_Data_Platform": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_kdocy_devops_data_platform_cover_letter.tex",
|
||||||
|
"e2e_kdocy_devops_data_platform_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b3a032a850cd0d6d"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"article",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Lebenslauf.pdf",
|
||||||
|
"Dennis_Thiessen_Motivationsschreiben.pdf",
|
||||||
|
"e2e_kdocy_devops_data_platform_cover_letter.pdf",
|
||||||
|
"e2e_kdocy_devops_data_platform_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_kdocy_devops_data_platform_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_kdocy_devops_data_platform_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Kraken_AI_Infrastructure": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_kraken_ai_infra_cover_letter.tex",
|
||||||
|
"e2e_kraken_ai_infra_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"Senior Software Engineer – AI Infrastructure @ Kraken.pdf",
|
||||||
|
"e2e_kraken_ai_infra_cover_letter.pdf",
|
||||||
|
"e2e_kraken_ai_infra_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_kraken_ai_infra_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_kraken_ai_infra_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Kraken_SRE_AI_Agents": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_kraken_sre_ai_agents_cover_letter.tex",
|
||||||
|
"e2e_kraken_sre_ai_agents_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_kraken_sre_ai_agents_cover_letter.pdf",
|
||||||
|
"e2e_kraken_sre_ai_agents_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_kraken_sre_ai_agents_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_kraken_sre_ai_agents_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Microsoft_ISE_Senior_SWE": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_microsoft_ise_cover_letter.tex",
|
||||||
|
"e2e_microsoft_ise_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_microsoft_ise_cover_letter.pdf",
|
||||||
|
"e2e_microsoft_ise_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_microsoft_ise_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_microsoft_ise_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Microsoft_Principal_FDE_SWE": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_microsoft_principal_fde_swe_cover_letter.tex",
|
||||||
|
"e2e_microsoft_principal_fde_swe_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_microsoft_principal_fde_swe_cover_letter.pdf",
|
||||||
|
"e2e_microsoft_principal_fde_swe_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_microsoft_principal_fde_swe_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_microsoft_principal_fde_swe_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"NATO_AI_ENGINEER": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_nato_ai_engineer_cover_letter.tex",
|
||||||
|
"e2e_nato_ai_engineer_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_nato_ai_engineer_resume.pdf",
|
||||||
|
"nato_ai_engineer_check.pdf",
|
||||||
|
"nato_ai_engineer_final_check.pdf",
|
||||||
|
"nato_cover_final_check.pdf",
|
||||||
|
"nato_cover_letter_check.pdf",
|
||||||
|
"nato_critique_cover.pdf",
|
||||||
|
"nato_critique_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [],
|
||||||
|
"problems": [
|
||||||
|
"source distribution incomplete: requires resume.cls, which is not in the folder. A build here only succeeds if a copy happens to exist elsewhere on the machine. Do NOT repair by copying the newest class in - the repo carries several class variants and older macros differ."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"NVIDIA_SrDevOps_Zurich": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_nvidia_sr_devops_cover_letter.tex",
|
||||||
|
"e2e_nvidia_sr_devops_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b3a032a850cd0d6d"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"article",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_nvidia_sr_devops_cover_letter.pdf",
|
||||||
|
"e2e_nvidia_sr_devops_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_nvidia_sr_devops_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_nvidia_sr_devops_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"QuantCo_Cloud_Engineer": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_quantco_cloud_engineer_cover_letter.tex",
|
||||||
|
"e2e_quantco_cloud_engineer_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_quantco_cloud_engineer_cover_letter.pdf",
|
||||||
|
"e2e_quantco_cloud_engineer_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_quantco_cloud_engineer_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_quantco_cloud_engineer_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"RUAG_AI_Engineer_C5I": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_ruag_ai_engineer_c5i_cover_letter.tex",
|
||||||
|
"e2e_ruag_ai_engineer_c5i_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b52a6f7123ae9067"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"article",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Lebenslauf.pdf",
|
||||||
|
"Dennis_Thiessen_Motivationsschreiben.pdf",
|
||||||
|
"e2e_ruag_ai_engineer_c5i_cover_letter.pdf",
|
||||||
|
"e2e_ruag_ai_engineer_c5i_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_ruag_ai_engineer_c5i_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_ruag_ai_engineer_c5i_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"SBB_DataEngineer_AssetMgmt": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_sbb_data_engineer_asset_mgmt_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"resume.cls": "b3a032a850cd0d6d"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"e2e_sbb_data_engineer_asset_mgmt_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [],
|
||||||
|
"problems": [],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
},
|
||||||
|
"Snowflake_Observe_Enterprise": {
|
||||||
|
"sources": [
|
||||||
|
"e2e_snowflake_observe_enterprise_cover_letter.tex",
|
||||||
|
"e2e_snowflake_observe_enterprise_resume.tex"
|
||||||
|
],
|
||||||
|
"class_files": {
|
||||||
|
"cv.cls": "a623d286e808b27e",
|
||||||
|
"resume.cls": "b26f882a42d30d90"
|
||||||
|
},
|
||||||
|
"requires_class": [
|
||||||
|
"moderncv",
|
||||||
|
"resume"
|
||||||
|
],
|
||||||
|
"pdfs": [
|
||||||
|
"Dennis_Thiessen_Cover_Letter.pdf",
|
||||||
|
"Dennis_Thiessen_Resume.pdf",
|
||||||
|
"e2e_snowflake_observe_enterprise_cover_letter.pdf",
|
||||||
|
"e2e_snowflake_observe_enterprise_resume.pdf"
|
||||||
|
],
|
||||||
|
"uses_today": [
|
||||||
|
"e2e_snowflake_observe_enterprise_cover_letter.tex"
|
||||||
|
],
|
||||||
|
"problems": [
|
||||||
|
"rebuild is date-dependent: e2e_snowflake_observe_enterprise_cover_letter.tex use \\today, so recompiling a historical package silently restamps it with the current date. New packages must carry the explicit application date instead."
|
||||||
|
],
|
||||||
|
"reuse_classification": "recorded"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Build a reproducibility manifest for every application package (review finding F17).
|
||||||
|
|
||||||
|
A package folder that contains a PDF looks finished. It is not necessarily reproducible:
|
||||||
|
`output/NATO_AI_ENGINEER/` ships no `resume.cls` at all and only builds on a machine where
|
||||||
|
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.
|
||||||
|
|
||||||
|
python resume_builder/helpers/package_manifest.py # write the manifest
|
||||||
|
python resume_builder/helpers/package_manifest.py --check # fail if it is stale
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
OUTPUT = ROOT / "output"
|
||||||
|
MANIFEST = ROOT / "resume_builder" / "canonical" / "package_manifest.json"
|
||||||
|
REGISTER = ROOT / "resume_builder" / "canonical" / "historical_outputs.json"
|
||||||
|
TODAY_MACRO = re.compile(r"\\today")
|
||||||
|
# Ship with MiKTeX/TeX Live, so their absence from a package folder is not a missing file.
|
||||||
|
# Only project-local classes (resume.cls, cv.cls) have to travel with the package.
|
||||||
|
CTAN_CLASSES = {"article", "report", "book", "letter", "moderncv", "scrartcl", "scrlttr2",
|
||||||
|
"memoir", "extarticle", "europasscv", "europecv", "altacv"}
|
||||||
|
|
||||||
|
|
||||||
|
def digest(path: Path) -> str:
|
||||||
|
return hashlib.sha256(path.read_bytes()).hexdigest()[:16]
|
||||||
|
|
||||||
|
|
||||||
|
def describe(folder: Path) -> dict:
|
||||||
|
sources = sorted(p.name for p in 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")
|
||||||
|
match = re.search(r"\\documentclass(?:\[[^\]]*\])?\{([^}]+)\}", text)
|
||||||
|
if match:
|
||||||
|
needs.add(match.group(1))
|
||||||
|
if TODAY_MACRO.search(text):
|
||||||
|
uses_today.append(name)
|
||||||
|
|
||||||
|
problems = []
|
||||||
|
for required in sorted(needs):
|
||||||
|
# Classes that ship with a TeX distribution are not part of the package's own source
|
||||||
|
# distribution; only project-local classes (resume.cls, cv.cls) have to travel with it.
|
||||||
|
if required in CTAN_CLASSES:
|
||||||
|
continue
|
||||||
|
if f"{required}.cls" not in classes:
|
||||||
|
problems.append(
|
||||||
|
f"source distribution incomplete: requires {required}.cls, which is not in the "
|
||||||
|
f"folder. A build here only succeeds if a copy happens to exist elsewhere on the "
|
||||||
|
f"machine. Do NOT repair by copying the newest class in - the repo carries "
|
||||||
|
f"several class variants and older macros differ.")
|
||||||
|
if uses_today:
|
||||||
|
problems.append(
|
||||||
|
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 {
|
||||||
|
"sources": sources,
|
||||||
|
"class_files": classes,
|
||||||
|
"requires_class": sorted(needs),
|
||||||
|
"pdfs": pdfs,
|
||||||
|
"uses_today": uses_today,
|
||||||
|
"problems": problems,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build() -> dict:
|
||||||
|
register = json.loads(REGISTER.read_text(encoding="utf-8"))
|
||||||
|
classified = set()
|
||||||
|
for key in ("unsafe_do_not_reuse", "historical_revalidate", "paused_no_package"):
|
||||||
|
for item in register.get(key, []):
|
||||||
|
classified.add(item["folder"] if isinstance(item, dict) else item)
|
||||||
|
|
||||||
|
packages = {}
|
||||||
|
for folder in sorted(OUTPUT.iterdir()):
|
||||||
|
if not folder.is_dir() or not any(folder.glob("*.tex")):
|
||||||
|
continue
|
||||||
|
# Only APPLICATION packages. A folder holding a report or analysis document is not a
|
||||||
|
# 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["reuse_classification"] = (
|
||||||
|
"recorded" if f"output/{folder.name}" in classified else "UNCLASSIFIED")
|
||||||
|
if f"output/{folder.name}" not in classified:
|
||||||
|
entry["problems"].append(
|
||||||
|
"not classified in historical_outputs.json. Default until someone classifies "
|
||||||
|
"it: treat as historical_revalidate - never reuse a bullet from it without "
|
||||||
|
"re-checking the claim against claims.json first.")
|
||||||
|
packages[folder.name] = entry
|
||||||
|
|
||||||
|
return {
|
||||||
|
"schema_version": 1,
|
||||||
|
"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 "
|
||||||
|
"submitted document."),
|
||||||
|
"packages": packages,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--check", action="store_true", help="fail if the manifest is stale")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
manifest = build()
|
||||||
|
text = json.dumps(manifest, indent=2, ensure_ascii=False) + "\n"
|
||||||
|
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")
|
||||||
|
return 1
|
||||||
|
print("Package manifest is current.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
MANIFEST.write_text(text, 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():
|
||||||
|
print(f" {name}")
|
||||||
|
for problem in entry["problems"]:
|
||||||
|
print(f" - {problem[:150]}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -218,7 +218,7 @@ def _negated(haystack: str, term: str) -> bool:
|
|||||||
return bool(hits) and all(_NEGATION.search(haystack[max(0, hit.start() - 70):hit.start()]) for hit in hits)
|
return bool(hits) and all(_NEGATION.search(haystack[max(0, hit.start() - 70):hit.start()]) for hit in hits)
|
||||||
|
|
||||||
|
|
||||||
def scan_document(path: Path, claims: dict) -> list[str]:
|
def scan_document(path: Path, claims: dict, template: bool = False) -> list[str]:
|
||||||
"""Deterministic, mechanically checkable properties of a generated document.
|
"""Deterministic, mechanically checkable properties of a generated document.
|
||||||
|
|
||||||
This is NOT a truth check and must never be reported as one. It cannot judge whether a
|
This is NOT a truth check and must never be reported as one. It cannot judge whether a
|
||||||
@@ -266,7 +266,9 @@ def scan_document(path: Path, claims: dict) -> list[str]:
|
|||||||
|
|
||||||
# Scan the comment-stripped RAW text: _MARKUP treats "\item [GENERATE: x]" as a command
|
# Scan the comment-stripped RAW text: _MARKUP treats "\item [GENERATE: x]" as a command
|
||||||
# with an optional argument and would swallow the placeholder whole.
|
# with an optional argument and would swallow the placeholder whole.
|
||||||
for match in _PLACEHOLDER.finditer(_COMMENT.sub("", raw)):
|
# Templates are EXEMPT: an unfilled placeholder is what makes a template a template. The
|
||||||
|
# defect is a placeholder surviving into a generated document.
|
||||||
|
for match in ([] if template else _PLACEHOLDER.finditer(_COMMENT.sub("", raw))):
|
||||||
errors.append(f"{path}: unresolved placeholder left in the document: {match.group(0)!r}")
|
errors.append(f"{path}: unresolved placeholder left in the document: {match.group(0)!r}")
|
||||||
|
|
||||||
identity = claims.get("identity", {})
|
identity = claims.get("identity", {})
|
||||||
|
|||||||
@@ -27,7 +27,12 @@ Bern, Switzerland $\vert$
|
|||||||
\textit{City, country}
|
\textit{City, country}
|
||||||
|
|
||||||
\vspace{1em}
|
\vspace{1em}
|
||||||
\today
|
% APPLICATION DATE -- replace with the literal date this letter is sent,
|
||||||
|
% e.g. 11 September 2026. Do NOT use \today (review finding F17, 2026-09-11):
|
||||||
|
% it makes the document date-dependent, so recompiling a historical letter silently
|
||||||
|
% restamps it with the current date. 17 existing packages already carry that defect
|
||||||
|
% and are deliberately left alone; new ones must not add to it.
|
||||||
|
[GENERATE: application date, literal]
|
||||||
|
|
||||||
\vspace{1em}
|
\vspace{1em}
|
||||||
\textbf{Application for \textit{exact role title}}
|
\textbf{Application for \textit{exact role title}}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
import re
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -62,7 +63,35 @@ class CanonicalSystemTests(unittest.TestCase):
|
|||||||
"resume_builder/templates/resume_template.tex",
|
"resume_builder/templates/resume_template.tex",
|
||||||
"resume_builder/templates/coverletter_template.tex",
|
"resume_builder/templates/coverletter_template.tex",
|
||||||
):
|
):
|
||||||
self.assertEqual([], validator.scan_document(ROOT / relative, self.claims), relative)
|
self.assertEqual([], validator.scan_document(ROOT / relative, self.claims, template=True), relative)
|
||||||
|
|
||||||
|
|
||||||
|
class PackageReproducibilityTests(unittest.TestCase):
|
||||||
|
"""F17: a PDF in a folder is not evidence that the package can be rebuilt."""
|
||||||
|
|
||||||
|
def test_manifest_is_current(self):
|
||||||
|
import subprocess
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, str(ROOT / "resume_builder" / "helpers" / "package_manifest.py"),
|
||||||
|
"--check"], capture_output=True, text=True)
|
||||||
|
self.assertEqual(0, result.returncode, result.stdout + result.stderr)
|
||||||
|
|
||||||
|
def test_cover_letter_template_does_not_use_today(self):
|
||||||
|
# \today makes a rebuild date-dependent: recompiling a historical letter
|
||||||
|
# silently restamps it with the current date.
|
||||||
|
template = (ROOT / "resume_builder" / "templates" / "coverletter_template.tex").read_text(
|
||||||
|
encoding="utf-8")
|
||||||
|
body = "\n".join(
|
||||||
|
line for line in template.splitlines() if not line.strip().startswith("%"))
|
||||||
|
self.assertNotIn("\\today", body)
|
||||||
|
|
||||||
|
def test_every_application_package_is_classified_for_reuse(self):
|
||||||
|
manifest = json.loads(
|
||||||
|
(ROOT / "resume_builder" / "canonical" / "package_manifest.json").read_text(
|
||||||
|
encoding="utf-8"))
|
||||||
|
unclassified = [name for name, entry in manifest["packages"].items()
|
||||||
|
if entry["reuse_classification"] == "UNCLASSIFIED"]
|
||||||
|
self.assertEqual([], unclassified)
|
||||||
|
|
||||||
|
|
||||||
class WorkflowPolicyTests(unittest.TestCase):
|
class WorkflowPolicyTests(unittest.TestCase):
|
||||||
|
|||||||
Reference in new issue
Block a user