feat(cv): publish CV variants and explain empty selections - #15
Merged
Merged
Conversation
The deploy workflow built cv.tex alone and staged one PDF, so the two bundled variants built for review in cv.yml and never reached a reader. It now builds all three, checks each baseline, and stages them as cv.pdf, cv-short.pdf and cv-teaching.pdf: cv.pdf keeps the address every existing link uses, and the cv- prefix keeps a variant saying what it is once it has left the site. /cv/ keeps the full CV as its primary download and offers each variant beside it, only when that file is really staged. A test fails when the two workflows disagree about the set, since one builds for review and the other alone publishes. A \printbibliography whose filter matches nothing prints nothing at all, its own heading included, so cv/short.tex's hand-written "Selected Publications" section left a title over silence for any adopter with publications but nothing marked `selected`. \cvbibfiltered now prints such a block and, when nothing matched, repeats the heading biblatex skipped and names the filter. It does not fail the build: an adopter who has marked nothing yet has a valid record, and a variant they never asked for must not stop their CV from building. The full CV is unmoved — all three baselines still pass byte-exact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
eduardstan
added a commit
that referenced
this pull request
Jul 31, 2026
* feat(cv): publish all three CVs and announce an empty filtered section The deploy workflow built cv.tex alone and staged one PDF, so the two bundled variants built for review in cv.yml and never reached a reader. It now builds all three, checks each baseline, and stages them as cv.pdf, cv-short.pdf and cv-teaching.pdf: cv.pdf keeps the address every existing link uses, and the cv- prefix keeps a variant saying what it is once it has left the site. /cv/ keeps the full CV as its primary download and offers each variant beside it, only when that file is really staged. A test fails when the two workflows disagree about the set, since one builds for review and the other alone publishes. A \printbibliography whose filter matches nothing prints nothing at all, its own heading included, so cv/short.tex's hand-written "Selected Publications" section left a title over silence for any adopter with publications but nothing marked `selected`. \cvbibfiltered now prints such a block and, when nothing matched, repeats the heading biblatex skipped and names the filter. It does not fail the build: an adopter who has marked nothing yet has a valid record, and a variant they never asked for must not stop their CV from building. The full CV is unmoved — all three baselines still pass byte-exact. * no-mistakes(review): Trigger CV workflow for deploy workflow changes * no-mistakes(document): Refresh CV publication and filter documentation ---------
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Publish all three bundled CV variants from the deploy workflow, and stop a bundled variant from failing silently on a stranger's record.
Two captain decisions land here. Scope was corrected before work started: the variant machinery (cv/short.tex, cv/teaching.tex, cv/supervision.tex, cv/preamble.tex, cv/header.tex, and the short and teaching baselines) already exists on main and must NOT be rebuilt.
Part 1 - publish all three. .github/workflows/deploy.yml built cv.tex alone and staged one PDF, so the two variants built for review in cv.yml never reached a reader. It now builds all three, checks each baseline, and stages them as cv.pdf, cv-short.pdf and cv-teaching.pdf. Deliberate naming decision: cv.pdf keeps the exact address every existing link and the check-deployment-base assertion already use, and the cv- prefix means a downloaded variant still says 'CV' once it has left the site (a bare short.pdf would not). The full CV deliberately STAYS the primary link on /cv/; the two variants are secondary links beside it, each rendered only when its file is really staged, so a repository whose workflow builds only cv.tex still shows exactly one link as before. A new test in scripts/build-cv-data.test.mjs fails when deploy.yml and cv.yml disagree about the document set, since one builds for review and the other alone publishes. The link blurbs deliberately describe what each variant curates rather than a page count: length depends on the adopter's record, and AGENTS.md forbids hand-written numbers the build never derived (short.pdf is in fact 1 page on the bundled record, not 2).
Part 2 - make a variant fail loudly rather than emptily. A bundled variant references record fields a stranger will not have. The prose case was already handled by the \ifx ... \cvempty idiom and was verified to still work both ways (teaching_statement, Short Bio and Research Focus headings all vanish when absent). The real work was the bibliography case: a \printbibliography whose filter matches nothing prints nothing at all, its own heading included, so cv/short.tex's hand-written 'Selected Publications' section left a title over silence for any adopter with publications but nothing marked 'selected'. New \cvbibfiltered in cv/preamble.tex prints such a block and, when nothing matched, repeats the heading biblatex swallowed and names the filter. Deliberate decision, argued rather than dictated: a one-line placeholder, NOT a hard build failure - an adopter who has simply not marked any work yet has a valid record, and a variant they never asked for must not stop their CV from building. The generated sections in cv/generated/cv-data.tex are deliberately exempt and must stay so, because cv.tex prints one heading over the whole sequence, so a section matching nothing correctly leaves no trace there.
Constraints the user imposed: the full CV must not move (data/cv-baseline/cv-baseline.txt still passes byte-exact and the page count is unchanged - verified, all three baselines pass byte-exact, cv.pdf still 3 pages); apply ponytail laziness - no new content-interface field, no configuration key, no registry, just a workflow change, two links and one guard; do not create a root CLAUDE.md or any root instruction pointer; invent no real organisations in the record (the record was just swept clean of them).
Verification already performed: all three PDFs build and reach web/public/assets/; /cv/ checked in a real Chrome browser under the real /ledgerpress/ deployment base - 3 PDF links, all HTTP 200, served bytes cmp-identical to the built PDFs; cold-start proof done against a minimal record (profile and appointments only, no teaching_statement, no selected keywords) showing the reader sees a named placeholder instead of a silent empty heading; the new guard test was mutation-checked and fails when the guard is reverted; scripts/check-cv-baseline.sh (all three), scripts/check-adopter-build.mjs, scripts/check-deployment-base.mjs, npm test and the full npm run check all pass.
Docs were updated to match: README.md, web/README.md, and AGENTS.md (its Delivery section and one new load-bearing constraint), plus .gitignore for the two new staged names.
What Changed
\cvbibfilteredso hand-authored filtered sections show a named placeholder when no bibliography entries match.Risk Assessment
✅ Low: The two-line fix correctly triggers the CV workflow for deploy-only changes, closes the prior guard gap, and introduces no new source risk.
Testing
Focused guard tests passed; all three XeLaTeX documents built and matched their byte-exact baselines (full CV remained 3 pages); real
/ledgerpress/builds showed three working byte-identical PDF downloads and the correct full-only fallback; a no-selection short CV visibly named the unmatched filter; evidence was captured and the worktree was left clean at the target commit./tmp/no-mistakes-evidence/01KYSSQF6H3EHV34BW0TF5PWPF/cv-page-three-downloads.png)Evidence: Built full CV
Evidence: Built short CV
Evidence: Built teaching CV
Evidence: Published PDF download verification
All three URLs returned HTTP 200 and were byte-identical to the corresponding built PDFs./tmp/no-mistakes-evidence/01KYSSQF6H3EHV34BW0TF5PWPF/cv-page-full-only.png) - Evidence: Short CV showing named empty-selection placeholder (local file:/tmp/no-mistakes-evidence/01KYSSQF6H3EHV34BW0TF5PWPF/short-no-selected.png)Evidence: Short CV built without selected publications
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
scripts/build-cv-data.test.mjs:555- This new guard reads deploy.yml, but its CI owner (cv.yml) does not include deploy.yml in either path filter, while deploy.yml runs only web tests. A deploy-only change can therefore make the document sets disagree with all triggered checks green. Add.github/workflows/deploy.ymlto both cv.yml path lists or execute this root test from deploy.yml.🔧 Fix: Trigger CV workflow for deploy workflow changes
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
npm ciandnpm --prefix web ci(test setup)node --test --test-name-pattern='both workflows build every printed document|cv/.*announces a filtered bibliography|cvbibfiltered prints' scripts/build-cv-data.test.mjslatexmk -xelatex -cd cv/cv.texlatexmk -xelatex -cd cv/short.texlatexmk -xelatex -cd cv/teaching.texbash scripts/check-cv-baseline.shbash scripts/check-cv-baseline.sh cv/short.pdfbash scripts/check-cv-baseline.sh cv/teaching.pdfWorkflow-equivalent staging followed byLEDGERPRESS_SITE='https://eduardstan.github.io/ledgerpress/' npm --prefix web run buildReal Chrome verification athttp://127.0.0.1:4321/ledgerpress/cv/under the/ledgerpress/baseDownloaded all three rendered PDF links withcurl, confirmed HTTP 200, and compared each response byte-for-byte withcmpRemoved only staged variant PDFs, rebuilt, and verified Chrome rendered exactly the canonicalcv.pdflinkTemporarily removed allselectedkeywords, rebuiltcv/short.tex, and visually/textually verified the placeholder namesshortselectedandcv/short.texRestored the record and removed all generated worktree artifacts; finalgit status --short --ignoredwas empty✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.