test(corpus): add google-docs/ + weasyprint fixtures (closes #1) - #26
Conversation
…ures Closes part of #1 (#12 tracks remaining manual-export categories). Adds 4 synthetic resume PDFs rendered via Chromium headless print-to-pdf, which uses the same Skia/PDF renderer family as Google Docs's "Download as PDF" export. Lands them in the previously-empty `google-docs/` category folder so the corpus now covers 4 source categories (latex, word, unknown, google-docs) — meeting issue #1's "≥4 source categories" acceptance criterion. Persona is synthetic end-to-end (Jane Smith / @example.com / 555-style phone / fictional Acme Corp / Globex / Initech / Springfield State University). Multi-stage PII preflight clean: body text, Info dict (Title only — "Resume", non-identifying), no XMP packet, raw byte grep against the known real-author token list returns 0 on every PDF. The 4 templates each exercise a different mix of parser paths: | Fixture | Score | Notable signal | |---------|-------|----------------| | `chromium-headless-classic` | 98 | Well-formed baseline — full single-column, every bullet has metric + verb + length, all sections detected. Happy-path anchor. | | `chromium-headless-two-column` | 53 | CSS grid sidebar + main column. Exercises the `COLUMN_GAP_THRESHOLD` split in `groupIntoLines` landed in PR #18; 23 bullets correctly counted across both columns. | | `chromium-headless-nonstandard-headers` | 84 | Uses `On Campus Involvement / Volunteer Experience / Internships` — none in `SECTION_KEYWORDS.experience`, so reports `experienceCount: 0` and `missing: ["work experience"]` despite 8 visible bullets in those sections. **Direct regression anchor for issue #19** — when the section-keyword expansion lands, this fixture's snapshot will update and pin the new behaviour. | | `chromium-headless-minimal` | 24 | Short bullets, no metrics, no summary, no LinkedIn. Confirms the post-#9 grading correctly penalises low-quality content rather than hiding it. | Two related findings surfaced during this work, neither blocking but worth flagging for follow-up: 1. **Chromium `--print-to-pdf` renders CSS list-style bullets as graphics, not text** — pdftotext sees no `•` glyph at line start, so the parser counts 0 bullets and grades dimensions as ungradable. All 4 fixtures here use explicit `<p>• text</p>` markup to work around this. Worth filing as a separate finding: Chrome-exported PDFs from apps that emit native `<ul>` markup (most modern web resume tools) may silently lose all bullet structure. Not files yet — leaving it for a focused issue with a dedicated CSS-bullets fixture. 2. **`nonstandard-headers` fixture validates issue #19's scope** — confirms the section-keyword gap affects not just laverne but any student/early-career resume layout with non-canonical experience headings. Already tracked in #19; this fixture adds independent evidence. Corpus state after this commit: | Category | PDFs | Status | |-------------|------|-------------------------------------------------------| | latex/ | 5 | awesome-cv (cv + resume), deedy (mac + open), header-as-name | | word/ | 1 | openresume-laverne-word-quartz | | google-docs/| 4 | chromium-headless × 4 (this commit) | | unknown/ | 1 | openresume-react-pdf | | mac-pages/ | 0 | needs manual export — tracked in #12 | | mac-preview/| 0 | tried cupsfilter, doesn't expose a clean Quartz | | | | re-rendering path on modern macOS — manual export | | | | needed, tracked in #12 | | indesign/ | 0 | needs InDesign access — tracked in #12 | Total: 11 PDFs across 4 categories. Issue #1's "≥4 categories" criterion met; "≥15 PDFs" still gapped at 11/15 — covered by #12's manual-export follow-up which was already filed for exactly this content work. Filename convention follows the README example (`google-docs-skia-m146.pdf`): `<renderer>-<variant>.pdf` inside the category folder. "chromium-headless" is honest about provenance (these are Chromium-rendered, not literal Google Docs exports, though they share the Skia/PDF renderer family — so they live in `google-docs/` for parser-path categorisation purposes). Verification: - `npm run typecheck`: clean - `npm run test`: 185 / 185 (181 baseline + 4 new corpus fixtures) - `npm run bake-fixtures` + `git diff` after: empty (snapshots round-trip) - All 4 PDFs: 0 hits on raw-byte grep for real-author tokens; no XMP packet - CONTRIBUTING.md test count refreshed 167 → 185 Refs #1, #12
…arget Brings corpus to 16 PDFs across 4 categories — closes both of issue #1's remaining acceptance criteria. Renders the same 4 HTML templates from the prior commit through WeasyPrint (Cairo backend) instead of Chromium-headless (Skia). Drops them in the existing `unknown/` category per the README's "Generator unknown or one-off" classification — WeasyPrint isn't a category called out in #1's body, and re-using `unknown/` is honest about provenance (these aren't mac-pages / mac-preview / indesign exports, which genuinely require GUI apps I can't drive autonomously and remain tracked in #12). Why the same content twice with different renderers --------------------------------------------------- Each WeasyPrint PDF carries the same source HTML as its Chromium twin in google-docs/, but the rendered byte stream differs (different font subsetting, item layout, line breaks). That's a feature: identical content rendered by two distinct generators is exactly the test the corpus exists to do — surface renderer-dependent parser drift that a single-generator corpus would miss. Initial snapshot comparison shows the parser produces near-identical scores on Chrome vs WeasyPrint for the simpler layouts (98 vs 98, 24 vs 24, 84 vs 84), but diverges on the two-column case (53 vs 43). Worth a focused look later if that gap is a real Cairo-vs-Skia layout difference vs a parser regression — but not blocking this PR; the snapshots pin whatever the current behaviour is. Fixtures -------- - `weasyprint-cairo-classic.pdf` (score 98) - `weasyprint-cairo-two-column.pdf` (score 43) - `weasyprint-cairo-nonstandard-headers.pdf` (score 84 — second regression anchor for #19) - `weasyprint-cairo-minimal.pdf` (score 24) PII preflight (same multi-stage sweep as PR #13) ------------------------------------------------ - Body text grep against known real-author tokens: 0 hits per PDF - Info dict: `Title: Resume` only (HTML `<title>`, non-identifying) - XMP packet: absent - Raw byte grep: 0 hits Corpus state after this commit ------------------------------ | Category | PDFs | Notes | |-------------|------|------------------------------------------------| | latex/ | 5 | awesome-cv × 2, deedy × 2, header-as-name | | word/ | 1 | openresume-laverne (Quartz) | | unknown/ | 6 | openresume-react-pdf, name-set-apart-tagline, | | | | weasyprint-cairo × 4 (this commit) | | google-docs/| 4 | chromium-headless × 4 (prior commit) | | mac-pages/ | 0 | needs Apple Pages — tracked in #12 | | mac-preview/| 0 | needs macOS Preview re-save — tracked in #12 | | indesign/ | 0 | needs InDesign access — tracked in #12 | | **Total** | **16** | **≥15 ✓** (issue #1 acceptance) | Verification ------------ - npm run typecheck: clean - npm run test: 189 / 189 (185 baseline + 4 new corpus tests) - npm run bake-fixtures + git diff after: empty (snapshots round-trip) - All 4 weasyprint PDFs: 0 hits on raw-byte grep, no XMP, generic Title only - CONTRIBUTING.md test count refreshed 185 → 189 Install note for reviewers reproducing locally: WeasyPrint requires native Cairo/Pango/GLib libs. On macOS: pip install --user weasyprint brew install pango cairo glib export DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_FALLBACK_LIBRARY_PATH" weasyprint input.html output.pdf The committed `.pdf` files are the artifact — re-rendering isn't required to run tests; only baking new snapshots is, which uses the existing PDFs. Refs #1, #12
s-annam
left a comment
There was a problem hiding this comment.
PR Review: test(corpus): add google-docs/ + weasyprint fixtures (closes #1)
Summary
Solid corpus expansion — 8 deterministic fixtures across two renderers, clean snapshots, exemplary multi-layer PII hygiene, tests green (189/189). One blocking issue: the google-docs/ fixtures are actually headless-Chrome prints, which contradicts the repo's own category taxonomy and makes "Closes #1" premature.
Spec Alignment (issue #1)
| Criterion | Status | Notes |
|---|---|---|
| ≥15 PDFs | ✅ | 16 total |
| ≥4 source categories | Only if google-docs/ counts; its PDFs self-ID as HeadlessChrome/Skia in the Info dict, which README routes to unknown/. True distinct categories: latex, word, unknown = 3 |
|
Co-located *.expected.json |
✅ | every PDF paired |
corpus.test.ts passes on branch |
✅ | 16 corpus tests green |
| New failure → fix or issue filed | ✅ | #19 anchored, drift noted |
| CONTRIBUTING/README workflow documented | ✅ | count refreshed 167→189 |
| No real PII | ✅ | re-verified body + Info dict + XMP + raw bytes, all 8 |
| No flaky tests | ✅ | deterministic snapshots |
Highlights
- PII discipline is exemplary — re-verified all 8 binaries across 4 layers (body / Info / XMP / raw bytes); every persona is Jane Smith /
@example.com/(555). Info dict carries onlyTitle: Resume. nonstandard-headersas a #19 regression anchor is exactly the right use of the corpus — snapshotting a known-broken cascade (experienceCount: 0with 8 visible bullets) so a future fix has a tripwire.- Renderer-drift design (same source HTML through Skia vs Cairo) is a genuinely clever stress signal;
minimal/nonstandardsnapshots are byte-identical across renderers,two-columndiverges — pinned both ways.
Key Findings
- [Blocking]
google-docs/is populated with headless-Chrome fixtures. The 4 PDFs' Info dict readsCreator: …HeadlessChrome/149…,Producer: Skia/PDF m149.tests/fixtures/pdfs/README.mdexplicitly lists "headless Chrome" underunknown/and reservesgoogle-docs/for actual Google Docs "Download as PDF" exports — so these belong inunknown/. This is more than tidiness:google-docs/is the only thing satisfying #1's "≥4 categories," so "Closes #1" rests on the mislabel. Resolve one of two ways:- (a) Move them to
unknown/(renamechromium-headless-*), leavegoogle-docs/empty — then #1 stays open (which referenced #12 already tracks), and this PR is "Refs #1," not "Closes #1." - (b) Deliberately redefine
google-docs/to accept the Skia headless-Chrome print as a faithful proxy (Google Docs does export via Skia), update README's two taxonomy lines, and add a one-liner noting the Info-dict Creator is HeadlessChrome by design. Then the closure is honest.
Either is fine — it's a team taxonomy call — but the corpus can't ship self-contradicting against its own README.
- (a) Move them to
- [Suggestion] If you keep them in
google-docs/per (b),chromium-headless-*will still read as "unknown" provenance to the next contributor. Encode the rationale in the filename (e.g.google-docs-skia-proxy-*) and note in README why the Creator string says HeadlessChrome. - [Nit]
metricBulletsdrifts 8→6 between chromium and weasyprintclassic, yet both score 98 (absorbed by the 40-pt specificity cap). Harmless now, but it's a latent metric-detection drift the cap is hiding — worth a line in the drift section soclassicisn't read as "no drift."
Verdict
Action: REQUEST_CHANGES
Rationale: Corpus quality, tests, and PII are all strong. The blocker is provenance integrity — google-docs/ filled with self-identifying headless-Chrome prints contradicts the repo's taxonomy and makes the #1 closure unearned. Cheap to resolve (relabel folder, or deliberately redefine + doc it); the spec ambiguity is not the author's fault.
| @@ -0,0 +1,67 @@ | |||
| { | |||
There was a problem hiding this comment.
[Blocking]: These 4 fixtures' Info dict is Creator: HeadlessChrome/149, Producer: Skia/PDF m149 — i.e. headless Chrome, which tests/fixtures/pdfs/README.md routes to unknown/, not google-docs/ (reserved for real Google Docs exports). Either move to unknown/ (and keep #1 open per #12), or deliberately redefine google-docs/ as the Skia-proxy category and update the README's two taxonomy lines. As-is the corpus contradicts its own doc, and "Closes #1" rests on this being the 4th category.
| @@ -0,0 +1,66 @@ | |||
| { | |||
There was a problem hiding this comment.
[Suggestion]: Nice — clean #19 regression anchor (experienceCount: 0 with 8 visible bullets, snapshotted as known-broken). Make sure the Refs #19 link survives into the squash-merge commit body so the tripwire is discoverable from git blame later.
…y + doc the taxonomy decision Addresses the [Blocking] + [Suggestion] findings from @s-annam's review on PR #26. The reviewer flagged that the 4 chromium-headless fixtures self-identify as `Creator: HeadlessChrome/149` / `Producer: Skia/PDF m149`, while `tests/fixtures/pdfs/README.md` previously routed "headless Chrome" to `unknown/` and reserved `google-docs/` for actual Google Docs exports. Without resolution, "Closes #1" rests on a mislabel. Choosing reviewer's option (b) — deliberately redefine `google-docs/` as a Skia/PDF category that accepts both real Google Docs exports and Chromium headless prints as a faithful Skia proxy. Google Docs's "Download as PDF" pipeline uses Skia/PDF; a `chrome --headless --print-to-pdf` of the same HTML produces structurally-equivalent output (same renderer family, same item-layout patterns, same font-subsetting behaviour the parser needs to handle). Treating them as the same category for parser-failure-mode purposes is honest about what the corpus is for — distinct renderers, not distinct product provenance. Changes: 1. Renamed 4 PDFs + 4 snapshots: chromium-headless-classic.{pdf,expected.json} → google-docs-skia-proxy-classic.{pdf,expected.json} chromium-headless-two-column.{pdf,expected.json} → google-docs-skia-proxy-two-column.{pdf,expected.json} chromium-headless-nonstandard-headers.{pdf,expected.json} → google-docs-skia-proxy-nonstandard-headers.{pdf,expected.json} chromium-headless-minimal.{pdf,expected.json} → google-docs-skia-proxy-minimal.{pdf,expected.json} New filename encodes the rationale per finding #2 — next contributor reading `google-docs-skia-proxy-*` immediately knows these are Skia prints used as proxies, not real Google Docs exports. 2. Updated `tests/fixtures/pdfs/README.md` taxonomy in two places: - `google-docs/` description now explicitly says it accepts Chromium `--print-to-pdf` Skia exports as a deliberate proxy, names the `google-docs-skia-proxy-*` filename prefix, and pre-empts confusion about the `Creator: HeadlessChrome/<v>` Info-dict string ("that is by design, not a mislabel"). - `unknown/` description no longer claims headless Chrome (it's now in `google-docs/` per the redefinition); WeasyPrint added as an example of what `unknown/` genuinely covers. 3. PDFs themselves are byte-identical to the prior commit — only the filenames changed. Snapshots re-baked to match the new filenames; counts and scores are unchanged. Re finding #3 (metricBullets drift, nit) --------------------------------------- Reviewer noted that on the `classic` variant, `metricBullets` drifts 8 → 6 between the chromium and weasyprint snapshots even though both score 98 — the difference gets absorbed by the 40-pt Specificity cap at the score boundary. Surfacing here so it's discoverable from `git log` rather than buried in the snapshot files: | variant | chromium metricBullets | weasyprint metricBullets | |---------|------------------------|--------------------------| | classic | 8 | 6 | | two-column | varies | varies | | nonstandard-headers | 8 | 8 | | minimal | 0 | 0 | The `classic` 8→6 gap suggests `bulletHasMetric` is sensitive to something in WeasyPrint's text layout that Chrome doesn't reproduce — likely word breaking or numeric-character spacing across line wraps. Worth a focused look as a separate parser investigation; not blocking this PR because the snapshot diff is fully captured either way. Re inline finding on nonstandard-headers ---------------------------------------- Reviewer asked the `Refs #19` link survive into the squash-merge commit body so the regression tripwire is discoverable from `git blame` later. Including the explicit trailer below. Verification ------------ - npm run typecheck: clean - npm run test: 189 / 189 (snapshots round-trip cleanly through the rename) - `git diff --cached` confirms no content changes on the 8 renamed files beyond the rename itself Refs #1, #12, #19
The previous commit (`284a6cb`) renamed the chromium fixtures and *claimed* in its body to also update `tests/fixtures/pdfs/README.md`, but the README diff didn't make it into the commit due to a stale cwd swallowing the relative path. The README change is what gives the rename its meaning — without it, the new filenames are arbitrary. This commit adds the actual README edit: - `google-docs/` description now explicitly states it accepts Chromium `--print-to-pdf` Skia exports as a deliberate proxy, names the `google-docs-skia-proxy-*` filename prefix, and pre-empts confusion about the `Creator: HeadlessChrome/<v>` Info-dict string. - `unknown/` description no longer lists "headless Chrome" (it's now in `google-docs/` per the redefinition); WeasyPrint added as an example of what `unknown/` genuinely covers. Together with `284a6cb`, this fully resolves the [Blocking] finding from @s-annam's review on PR #26 — taxonomy is now self-consistent and the corpus no longer contradicts its own README. Refs #1
|
Review feedback addressed across two commits ( [Blocking]
[Suggestion] Make [Nit] Verification post-fix: Ready for re-review. |
s-annam
left a comment
There was a problem hiding this comment.
PR Review (re-review): test(corpus): add google-docs/ + weasyprint fixtures
Action: APPROVE
Prior round's single blocker is resolved cleanly via option (b): google-docs/ is deliberately redefined to accept Chromium --print-to-pdf Skia exports as a faithful proxy (Google Docs uses the same Skia/PDF renderer), fixtures renamed google-docs-skia-proxy-*, README's two taxonomy lines updated, and the unknown/ line no longer lists "headless Chrome" — so the corpus no longer contradicts its own doc. With the taxonomy honestly redefined, Closes #1 is earned (4 real categories, 16 PDFs).
Re-verified on branch
| Check | Result |
|---|---|
| Info dicts | proxies self-ID Creator: HeadlessChrome/149 + Producer: Skia/PDF m149 (by design, doc'd); weasyprint Producer: WeasyPrint 69.0 correctly in unknown/ |
| PII (all 8, 4 layers) | clean — 0 hits; persona Jane Smith / @example.com / (555); Info dict only Title: Resume |
| Tests | 189 / 189 |
| typecheck | clean |
| bake-fixtures roundtrip | empty diff — snapshots round-trip |
Highlights
- Provenance now honest and self-documenting — the filename prefix + README note mean the next contributor won't read these as mislabeled.
nonstandard-headersremains a clean #19 regression anchor (experienceCount: 0with 8 visible bullets). Keep theRefs #19link in the squash body so the tripwire survives ingit blame.- Renderer-drift design (Skia vs Cairo, same source HTML) is a genuinely useful stress signal.
Non-blocking nit
The classic metricBullets 8→6 drift between chromium and weasyprint is masked by the 40pt specificity cap (both score 98) — harmless, but worth a line in any future drift note so classic isn't read as "zero drift."
Nice work turning the blocker into a documented taxonomy decision rather than just shuffling files.
* test(corpus): add google-docs/ category with 4 chromium-headless fixtures Closes part of #1 (#12 tracks remaining manual-export categories). Adds 4 synthetic resume PDFs rendered via Chromium headless print-to-pdf, which uses the same Skia/PDF renderer family as Google Docs's "Download as PDF" export. Lands them in the previously-empty `google-docs/` category folder so the corpus now covers 4 source categories (latex, word, unknown, google-docs) — meeting issue #1's "≥4 source categories" acceptance criterion. Persona is synthetic end-to-end (Jane Smith / @example.com / 555-style phone / fictional Acme Corp / Globex / Initech / Springfield State University). Multi-stage PII preflight clean: body text, Info dict (Title only — "Resume", non-identifying), no XMP packet, raw byte grep against the known real-author token list returns 0 on every PDF. The 4 templates each exercise a different mix of parser paths: | Fixture | Score | Notable signal | |---------|-------|----------------| | `chromium-headless-classic` | 98 | Well-formed baseline — full single-column, every bullet has metric + verb + length, all sections detected. Happy-path anchor. | | `chromium-headless-two-column` | 53 | CSS grid sidebar + main column. Exercises the `COLUMN_GAP_THRESHOLD` split in `groupIntoLines` landed in PR #18; 23 bullets correctly counted across both columns. | | `chromium-headless-nonstandard-headers` | 84 | Uses `On Campus Involvement / Volunteer Experience / Internships` — none in `SECTION_KEYWORDS.experience`, so reports `experienceCount: 0` and `missing: ["work experience"]` despite 8 visible bullets in those sections. **Direct regression anchor for issue #19** — when the section-keyword expansion lands, this fixture's snapshot will update and pin the new behaviour. | | `chromium-headless-minimal` | 24 | Short bullets, no metrics, no summary, no LinkedIn. Confirms the post-#9 grading correctly penalises low-quality content rather than hiding it. | Two related findings surfaced during this work, neither blocking but worth flagging for follow-up: 1. **Chromium `--print-to-pdf` renders CSS list-style bullets as graphics, not text** — pdftotext sees no `•` glyph at line start, so the parser counts 0 bullets and grades dimensions as ungradable. All 4 fixtures here use explicit `<p>• text</p>` markup to work around this. Worth filing as a separate finding: Chrome-exported PDFs from apps that emit native `<ul>` markup (most modern web resume tools) may silently lose all bullet structure. Not files yet — leaving it for a focused issue with a dedicated CSS-bullets fixture. 2. **`nonstandard-headers` fixture validates issue #19's scope** — confirms the section-keyword gap affects not just laverne but any student/early-career resume layout with non-canonical experience headings. Already tracked in #19; this fixture adds independent evidence. Corpus state after this commit: | Category | PDFs | Status | |-------------|------|-------------------------------------------------------| | latex/ | 5 | awesome-cv (cv + resume), deedy (mac + open), header-as-name | | word/ | 1 | openresume-laverne-word-quartz | | google-docs/| 4 | chromium-headless × 4 (this commit) | | unknown/ | 1 | openresume-react-pdf | | mac-pages/ | 0 | needs manual export — tracked in #12 | | mac-preview/| 0 | tried cupsfilter, doesn't expose a clean Quartz | | | | re-rendering path on modern macOS — manual export | | | | needed, tracked in #12 | | indesign/ | 0 | needs InDesign access — tracked in #12 | Total: 11 PDFs across 4 categories. Issue #1's "≥4 categories" criterion met; "≥15 PDFs" still gapped at 11/15 — covered by #12's manual-export follow-up which was already filed for exactly this content work. Filename convention follows the README example (`google-docs-skia-m146.pdf`): `<renderer>-<variant>.pdf` inside the category folder. "chromium-headless" is honest about provenance (these are Chromium-rendered, not literal Google Docs exports, though they share the Skia/PDF renderer family — so they live in `google-docs/` for parser-path categorisation purposes). Verification: - `npm run typecheck`: clean - `npm run test`: 185 / 185 (181 baseline + 4 new corpus fixtures) - `npm run bake-fixtures` + `git diff` after: empty (snapshots round-trip) - All 4 PDFs: 0 hits on raw-byte grep for real-author tokens; no XMP packet - CONTRIBUTING.md test count refreshed 167 → 185 Refs #1, #12 * test(corpus): add 4 WeasyPrint Cairo-renderer fixtures to close ≥15 target Brings corpus to 16 PDFs across 4 categories — closes both of issue #1's remaining acceptance criteria. Renders the same 4 HTML templates from the prior commit through WeasyPrint (Cairo backend) instead of Chromium-headless (Skia). Drops them in the existing `unknown/` category per the README's "Generator unknown or one-off" classification — WeasyPrint isn't a category called out in #1's body, and re-using `unknown/` is honest about provenance (these aren't mac-pages / mac-preview / indesign exports, which genuinely require GUI apps I can't drive autonomously and remain tracked in #12). Why the same content twice with different renderers --------------------------------------------------- Each WeasyPrint PDF carries the same source HTML as its Chromium twin in google-docs/, but the rendered byte stream differs (different font subsetting, item layout, line breaks). That's a feature: identical content rendered by two distinct generators is exactly the test the corpus exists to do — surface renderer-dependent parser drift that a single-generator corpus would miss. Initial snapshot comparison shows the parser produces near-identical scores on Chrome vs WeasyPrint for the simpler layouts (98 vs 98, 24 vs 24, 84 vs 84), but diverges on the two-column case (53 vs 43). Worth a focused look later if that gap is a real Cairo-vs-Skia layout difference vs a parser regression — but not blocking this PR; the snapshots pin whatever the current behaviour is. Fixtures -------- - `weasyprint-cairo-classic.pdf` (score 98) - `weasyprint-cairo-two-column.pdf` (score 43) - `weasyprint-cairo-nonstandard-headers.pdf` (score 84 — second regression anchor for #19) - `weasyprint-cairo-minimal.pdf` (score 24) PII preflight (same multi-stage sweep as PR #13) ------------------------------------------------ - Body text grep against known real-author tokens: 0 hits per PDF - Info dict: `Title: Resume` only (HTML `<title>`, non-identifying) - XMP packet: absent - Raw byte grep: 0 hits Corpus state after this commit ------------------------------ | Category | PDFs | Notes | |-------------|------|------------------------------------------------| | latex/ | 5 | awesome-cv × 2, deedy × 2, header-as-name | | word/ | 1 | openresume-laverne (Quartz) | | unknown/ | 6 | openresume-react-pdf, name-set-apart-tagline, | | | | weasyprint-cairo × 4 (this commit) | | google-docs/| 4 | chromium-headless × 4 (prior commit) | | mac-pages/ | 0 | needs Apple Pages — tracked in #12 | | mac-preview/| 0 | needs macOS Preview re-save — tracked in #12 | | indesign/ | 0 | needs InDesign access — tracked in #12 | | **Total** | **16** | **≥15 ✓** (issue #1 acceptance) | Verification ------------ - npm run typecheck: clean - npm run test: 189 / 189 (185 baseline + 4 new corpus tests) - npm run bake-fixtures + git diff after: empty (snapshots round-trip) - All 4 weasyprint PDFs: 0 hits on raw-byte grep, no XMP, generic Title only - CONTRIBUTING.md test count refreshed 185 → 189 Install note for reviewers reproducing locally: WeasyPrint requires native Cairo/Pango/GLib libs. On macOS: pip install --user weasyprint brew install pango cairo glib export DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_FALLBACK_LIBRARY_PATH" weasyprint input.html output.pdf The committed `.pdf` files are the artifact — re-rendering isn't required to run tests; only baking new snapshots is, which uses the existing PDFs. Refs #1, #12 * test(corpus): address PR #26 review — rename to google-docs-skia-proxy + doc the taxonomy decision Addresses the [Blocking] + [Suggestion] findings from @s-annam's review on PR #26. The reviewer flagged that the 4 chromium-headless fixtures self-identify as `Creator: HeadlessChrome/149` / `Producer: Skia/PDF m149`, while `tests/fixtures/pdfs/README.md` previously routed "headless Chrome" to `unknown/` and reserved `google-docs/` for actual Google Docs exports. Without resolution, "Closes #1" rests on a mislabel. Choosing reviewer's option (b) — deliberately redefine `google-docs/` as a Skia/PDF category that accepts both real Google Docs exports and Chromium headless prints as a faithful Skia proxy. Google Docs's "Download as PDF" pipeline uses Skia/PDF; a `chrome --headless --print-to-pdf` of the same HTML produces structurally-equivalent output (same renderer family, same item-layout patterns, same font-subsetting behaviour the parser needs to handle). Treating them as the same category for parser-failure-mode purposes is honest about what the corpus is for — distinct renderers, not distinct product provenance. Changes: 1. Renamed 4 PDFs + 4 snapshots: chromium-headless-classic.{pdf,expected.json} → google-docs-skia-proxy-classic.{pdf,expected.json} chromium-headless-two-column.{pdf,expected.json} → google-docs-skia-proxy-two-column.{pdf,expected.json} chromium-headless-nonstandard-headers.{pdf,expected.json} → google-docs-skia-proxy-nonstandard-headers.{pdf,expected.json} chromium-headless-minimal.{pdf,expected.json} → google-docs-skia-proxy-minimal.{pdf,expected.json} New filename encodes the rationale per finding #2 — next contributor reading `google-docs-skia-proxy-*` immediately knows these are Skia prints used as proxies, not real Google Docs exports. 2. Updated `tests/fixtures/pdfs/README.md` taxonomy in two places: - `google-docs/` description now explicitly says it accepts Chromium `--print-to-pdf` Skia exports as a deliberate proxy, names the `google-docs-skia-proxy-*` filename prefix, and pre-empts confusion about the `Creator: HeadlessChrome/<v>` Info-dict string ("that is by design, not a mislabel"). - `unknown/` description no longer claims headless Chrome (it's now in `google-docs/` per the redefinition); WeasyPrint added as an example of what `unknown/` genuinely covers. 3. PDFs themselves are byte-identical to the prior commit — only the filenames changed. Snapshots re-baked to match the new filenames; counts and scores are unchanged. Re finding #3 (metricBullets drift, nit) --------------------------------------- Reviewer noted that on the `classic` variant, `metricBullets` drifts 8 → 6 between the chromium and weasyprint snapshots even though both score 98 — the difference gets absorbed by the 40-pt Specificity cap at the score boundary. Surfacing here so it's discoverable from `git log` rather than buried in the snapshot files: | variant | chromium metricBullets | weasyprint metricBullets | |---------|------------------------|--------------------------| | classic | 8 | 6 | | two-column | varies | varies | | nonstandard-headers | 8 | 8 | | minimal | 0 | 0 | The `classic` 8→6 gap suggests `bulletHasMetric` is sensitive to something in WeasyPrint's text layout that Chrome doesn't reproduce — likely word breaking or numeric-character spacing across line wraps. Worth a focused look as a separate parser investigation; not blocking this PR because the snapshot diff is fully captured either way. Re inline finding on nonstandard-headers ---------------------------------------- Reviewer asked the `Refs #19` link survive into the squash-merge commit body so the regression tripwire is discoverable from `git blame` later. Including the explicit trailer below. Verification ------------ - npm run typecheck: clean - npm run test: 189 / 189 (snapshots round-trip cleanly through the rename) - `git diff --cached` confirms no content changes on the 8 renamed files beyond the rename itself Refs #1, #12, #19 * test(corpus): include README taxonomy update missed from 284a6cb The previous commit (`284a6cb`) renamed the chromium fixtures and *claimed* in its body to also update `tests/fixtures/pdfs/README.md`, but the README diff didn't make it into the commit due to a stale cwd swallowing the relative path. The README change is what gives the rename its meaning — without it, the new filenames are arbitrary. This commit adds the actual README edit: - `google-docs/` description now explicitly states it accepts Chromium `--print-to-pdf` Skia exports as a deliberate proxy, names the `google-docs-skia-proxy-*` filename prefix, and pre-empts confusion about the `Creator: HeadlessChrome/<v>` Info-dict string. - `unknown/` description no longer lists "headless Chrome" (it's now in `google-docs/` per the redefinition); WeasyPrint added as an example of what `unknown/` genuinely covers. Together with `284a6cb`, this fully resolves the [Blocking] finding from @s-annam's review on PR #26 — taxonomy is now self-consistent and the corpus no longer contradicts its own README. Refs #1 --------- Co-authored-by: Srinivas Annam <annam@annam.org>
* test(corpus): add google-docs/ category with 4 chromium-headless fixtures Closes part of #1 (#12 tracks remaining manual-export categories). Adds 4 synthetic resume PDFs rendered via Chromium headless print-to-pdf, which uses the same Skia/PDF renderer family as Google Docs's "Download as PDF" export. Lands them in the previously-empty `google-docs/` category folder so the corpus now covers 4 source categories (latex, word, unknown, google-docs) — meeting issue #1's "≥4 source categories" acceptance criterion. Persona is synthetic end-to-end (Jane Smith / @example.com / 555-style phone / fictional Acme Corp / Globex / Initech / Springfield State University). Multi-stage PII preflight clean: body text, Info dict (Title only — "Resume", non-identifying), no XMP packet, raw byte grep against the known real-author token list returns 0 on every PDF. The 4 templates each exercise a different mix of parser paths: | Fixture | Score | Notable signal | |---------|-------|----------------| | `chromium-headless-classic` | 98 | Well-formed baseline — full single-column, every bullet has metric + verb + length, all sections detected. Happy-path anchor. | | `chromium-headless-two-column` | 53 | CSS grid sidebar + main column. Exercises the `COLUMN_GAP_THRESHOLD` split in `groupIntoLines` landed in PR #18; 23 bullets correctly counted across both columns. | | `chromium-headless-nonstandard-headers` | 84 | Uses `On Campus Involvement / Volunteer Experience / Internships` — none in `SECTION_KEYWORDS.experience`, so reports `experienceCount: 0` and `missing: ["work experience"]` despite 8 visible bullets in those sections. **Direct regression anchor for issue #19** — when the section-keyword expansion lands, this fixture's snapshot will update and pin the new behaviour. | | `chromium-headless-minimal` | 24 | Short bullets, no metrics, no summary, no LinkedIn. Confirms the post-#9 grading correctly penalises low-quality content rather than hiding it. | Two related findings surfaced during this work, neither blocking but worth flagging for follow-up: 1. **Chromium `--print-to-pdf` renders CSS list-style bullets as graphics, not text** — pdftotext sees no `•` glyph at line start, so the parser counts 0 bullets and grades dimensions as ungradable. All 4 fixtures here use explicit `<p>• text</p>` markup to work around this. Worth filing as a separate finding: Chrome-exported PDFs from apps that emit native `<ul>` markup (most modern web resume tools) may silently lose all bullet structure. Not files yet — leaving it for a focused issue with a dedicated CSS-bullets fixture. 2. **`nonstandard-headers` fixture validates issue #19's scope** — confirms the section-keyword gap affects not just laverne but any student/early-career resume layout with non-canonical experience headings. Already tracked in #19; this fixture adds independent evidence. Corpus state after this commit: | Category | PDFs | Status | |-------------|------|-------------------------------------------------------| | latex/ | 5 | awesome-cv (cv + resume), deedy (mac + open), header-as-name | | word/ | 1 | openresume-laverne-word-quartz | | google-docs/| 4 | chromium-headless × 4 (this commit) | | unknown/ | 1 | openresume-react-pdf | | mac-pages/ | 0 | needs manual export — tracked in #12 | | mac-preview/| 0 | tried cupsfilter, doesn't expose a clean Quartz | | | | re-rendering path on modern macOS — manual export | | | | needed, tracked in #12 | | indesign/ | 0 | needs InDesign access — tracked in #12 | Total: 11 PDFs across 4 categories. Issue #1's "≥4 categories" criterion met; "≥15 PDFs" still gapped at 11/15 — covered by #12's manual-export follow-up which was already filed for exactly this content work. Filename convention follows the README example (`google-docs-skia-m146.pdf`): `<renderer>-<variant>.pdf` inside the category folder. "chromium-headless" is honest about provenance (these are Chromium-rendered, not literal Google Docs exports, though they share the Skia/PDF renderer family — so they live in `google-docs/` for parser-path categorisation purposes). Verification: - `npm run typecheck`: clean - `npm run test`: 185 / 185 (181 baseline + 4 new corpus fixtures) - `npm run bake-fixtures` + `git diff` after: empty (snapshots round-trip) - All 4 PDFs: 0 hits on raw-byte grep for real-author tokens; no XMP packet - CONTRIBUTING.md test count refreshed 167 → 185 Refs #1, #12 * test(corpus): add 4 WeasyPrint Cairo-renderer fixtures to close ≥15 target Brings corpus to 16 PDFs across 4 categories — closes both of issue #1's remaining acceptance criteria. Renders the same 4 HTML templates from the prior commit through WeasyPrint (Cairo backend) instead of Chromium-headless (Skia). Drops them in the existing `unknown/` category per the README's "Generator unknown or one-off" classification — WeasyPrint isn't a category called out in #1's body, and re-using `unknown/` is honest about provenance (these aren't mac-pages / mac-preview / indesign exports, which genuinely require GUI apps I can't drive autonomously and remain tracked in #12). Why the same content twice with different renderers --------------------------------------------------- Each WeasyPrint PDF carries the same source HTML as its Chromium twin in google-docs/, but the rendered byte stream differs (different font subsetting, item layout, line breaks). That's a feature: identical content rendered by two distinct generators is exactly the test the corpus exists to do — surface renderer-dependent parser drift that a single-generator corpus would miss. Initial snapshot comparison shows the parser produces near-identical scores on Chrome vs WeasyPrint for the simpler layouts (98 vs 98, 24 vs 24, 84 vs 84), but diverges on the two-column case (53 vs 43). Worth a focused look later if that gap is a real Cairo-vs-Skia layout difference vs a parser regression — but not blocking this PR; the snapshots pin whatever the current behaviour is. Fixtures -------- - `weasyprint-cairo-classic.pdf` (score 98) - `weasyprint-cairo-two-column.pdf` (score 43) - `weasyprint-cairo-nonstandard-headers.pdf` (score 84 — second regression anchor for #19) - `weasyprint-cairo-minimal.pdf` (score 24) PII preflight (same multi-stage sweep as PR #13) ------------------------------------------------ - Body text grep against known real-author tokens: 0 hits per PDF - Info dict: `Title: Resume` only (HTML `<title>`, non-identifying) - XMP packet: absent - Raw byte grep: 0 hits Corpus state after this commit ------------------------------ | Category | PDFs | Notes | |-------------|------|------------------------------------------------| | latex/ | 5 | awesome-cv × 2, deedy × 2, header-as-name | | word/ | 1 | openresume-laverne (Quartz) | | unknown/ | 6 | openresume-react-pdf, name-set-apart-tagline, | | | | weasyprint-cairo × 4 (this commit) | | google-docs/| 4 | chromium-headless × 4 (prior commit) | | mac-pages/ | 0 | needs Apple Pages — tracked in #12 | | mac-preview/| 0 | needs macOS Preview re-save — tracked in #12 | | indesign/ | 0 | needs InDesign access — tracked in #12 | | **Total** | **16** | **≥15 ✓** (issue #1 acceptance) | Verification ------------ - npm run typecheck: clean - npm run test: 189 / 189 (185 baseline + 4 new corpus tests) - npm run bake-fixtures + git diff after: empty (snapshots round-trip) - All 4 weasyprint PDFs: 0 hits on raw-byte grep, no XMP, generic Title only - CONTRIBUTING.md test count refreshed 185 → 189 Install note for reviewers reproducing locally: WeasyPrint requires native Cairo/Pango/GLib libs. On macOS: pip install --user weasyprint brew install pango cairo glib export DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_FALLBACK_LIBRARY_PATH" weasyprint input.html output.pdf The committed `.pdf` files are the artifact — re-rendering isn't required to run tests; only baking new snapshots is, which uses the existing PDFs. Refs #1, #12 * test(corpus): address PR #26 review — rename to google-docs-skia-proxy + doc the taxonomy decision Addresses the [Blocking] + [Suggestion] findings from @s-annam's review on PR #26. The reviewer flagged that the 4 chromium-headless fixtures self-identify as `Creator: HeadlessChrome/149` / `Producer: Skia/PDF m149`, while `tests/fixtures/pdfs/README.md` previously routed "headless Chrome" to `unknown/` and reserved `google-docs/` for actual Google Docs exports. Without resolution, "Closes #1" rests on a mislabel. Choosing reviewer's option (b) — deliberately redefine `google-docs/` as a Skia/PDF category that accepts both real Google Docs exports and Chromium headless prints as a faithful Skia proxy. Google Docs's "Download as PDF" pipeline uses Skia/PDF; a `chrome --headless --print-to-pdf` of the same HTML produces structurally-equivalent output (same renderer family, same item-layout patterns, same font-subsetting behaviour the parser needs to handle). Treating them as the same category for parser-failure-mode purposes is honest about what the corpus is for — distinct renderers, not distinct product provenance. Changes: 1. Renamed 4 PDFs + 4 snapshots: chromium-headless-classic.{pdf,expected.json} → google-docs-skia-proxy-classic.{pdf,expected.json} chromium-headless-two-column.{pdf,expected.json} → google-docs-skia-proxy-two-column.{pdf,expected.json} chromium-headless-nonstandard-headers.{pdf,expected.json} → google-docs-skia-proxy-nonstandard-headers.{pdf,expected.json} chromium-headless-minimal.{pdf,expected.json} → google-docs-skia-proxy-minimal.{pdf,expected.json} New filename encodes the rationale per finding #2 — next contributor reading `google-docs-skia-proxy-*` immediately knows these are Skia prints used as proxies, not real Google Docs exports. 2. Updated `tests/fixtures/pdfs/README.md` taxonomy in two places: - `google-docs/` description now explicitly says it accepts Chromium `--print-to-pdf` Skia exports as a deliberate proxy, names the `google-docs-skia-proxy-*` filename prefix, and pre-empts confusion about the `Creator: HeadlessChrome/<v>` Info-dict string ("that is by design, not a mislabel"). - `unknown/` description no longer claims headless Chrome (it's now in `google-docs/` per the redefinition); WeasyPrint added as an example of what `unknown/` genuinely covers. 3. PDFs themselves are byte-identical to the prior commit — only the filenames changed. Snapshots re-baked to match the new filenames; counts and scores are unchanged. Re finding #3 (metricBullets drift, nit) --------------------------------------- Reviewer noted that on the `classic` variant, `metricBullets` drifts 8 → 6 between the chromium and weasyprint snapshots even though both score 98 — the difference gets absorbed by the 40-pt Specificity cap at the score boundary. Surfacing here so it's discoverable from `git log` rather than buried in the snapshot files: | variant | chromium metricBullets | weasyprint metricBullets | |---------|------------------------|--------------------------| | classic | 8 | 6 | | two-column | varies | varies | | nonstandard-headers | 8 | 8 | | minimal | 0 | 0 | The `classic` 8→6 gap suggests `bulletHasMetric` is sensitive to something in WeasyPrint's text layout that Chrome doesn't reproduce — likely word breaking or numeric-character spacing across line wraps. Worth a focused look as a separate parser investigation; not blocking this PR because the snapshot diff is fully captured either way. Re inline finding on nonstandard-headers ---------------------------------------- Reviewer asked the `Refs #19` link survive into the squash-merge commit body so the regression tripwire is discoverable from `git blame` later. Including the explicit trailer below. Verification ------------ - npm run typecheck: clean - npm run test: 189 / 189 (snapshots round-trip cleanly through the rename) - `git diff --cached` confirms no content changes on the 8 renamed files beyond the rename itself Refs #1, #12, #19 * test(corpus): include README taxonomy update missed from 9596d2e The previous commit (`9596d2e`) renamed the chromium fixtures and *claimed* in its body to also update `tests/fixtures/pdfs/README.md`, but the README diff didn't make it into the commit due to a stale cwd swallowing the relative path. The README change is what gives the rename its meaning — without it, the new filenames are arbitrary. This commit adds the actual README edit: - `google-docs/` description now explicitly states it accepts Chromium `--print-to-pdf` Skia exports as a deliberate proxy, names the `google-docs-skia-proxy-*` filename prefix, and pre-empts confusion about the `Creator: HeadlessChrome/<v>` Info-dict string. - `unknown/` description no longer lists "headless Chrome" (it's now in `google-docs/` per the redefinition); WeasyPrint added as an example of what `unknown/` genuinely covers. Together with `9596d2e`, this fully resolves the [Blocking] finding from @s-annam's review on PR #26 — taxonomy is now self-consistent and the corpus no longer contradicts its own README. Refs #1 --------- Co-authored-by: Srinivas Annam <annam@annam.org>
* test(corpus): add google-docs/ category with 4 chromium-headless fixtures Closes part of #1 (#12 tracks remaining manual-export categories). Adds 4 synthetic resume PDFs rendered via Chromium headless print-to-pdf, which uses the same Skia/PDF renderer family as Google Docs's "Download as PDF" export. Lands them in the previously-empty `google-docs/` category folder so the corpus now covers 4 source categories (latex, word, unknown, google-docs) — meeting issue #1's "≥4 source categories" acceptance criterion. Persona is synthetic end-to-end (Jane Smith / @example.com / 555-style phone / fictional Acme Corp / Globex / Initech / Springfield State University). Multi-stage PII preflight clean: body text, Info dict (Title only — "Resume", non-identifying), no XMP packet, raw byte grep against the known real-author token list returns 0 on every PDF. The 4 templates each exercise a different mix of parser paths: | Fixture | Score | Notable signal | |---------|-------|----------------| | `chromium-headless-classic` | 98 | Well-formed baseline — full single-column, every bullet has metric + verb + length, all sections detected. Happy-path anchor. | | `chromium-headless-two-column` | 53 | CSS grid sidebar + main column. Exercises the `COLUMN_GAP_THRESHOLD` split in `groupIntoLines` landed in PR #18; 23 bullets correctly counted across both columns. | | `chromium-headless-nonstandard-headers` | 84 | Uses `On Campus Involvement / Volunteer Experience / Internships` — none in `SECTION_KEYWORDS.experience`, so reports `experienceCount: 0` and `missing: ["work experience"]` despite 8 visible bullets in those sections. **Direct regression anchor for issue #19** — when the section-keyword expansion lands, this fixture's snapshot will update and pin the new behaviour. | | `chromium-headless-minimal` | 24 | Short bullets, no metrics, no summary, no LinkedIn. Confirms the post-#9 grading correctly penalises low-quality content rather than hiding it. | Two related findings surfaced during this work, neither blocking but worth flagging for follow-up: 1. **Chromium `--print-to-pdf` renders CSS list-style bullets as graphics, not text** — pdftotext sees no `•` glyph at line start, so the parser counts 0 bullets and grades dimensions as ungradable. All 4 fixtures here use explicit `<p>• text</p>` markup to work around this. Worth filing as a separate finding: Chrome-exported PDFs from apps that emit native `<ul>` markup (most modern web resume tools) may silently lose all bullet structure. Not files yet — leaving it for a focused issue with a dedicated CSS-bullets fixture. 2. **`nonstandard-headers` fixture validates issue #19's scope** — confirms the section-keyword gap affects not just laverne but any student/early-career resume layout with non-canonical experience headings. Already tracked in #19; this fixture adds independent evidence. Corpus state after this commit: | Category | PDFs | Status | |-------------|------|-------------------------------------------------------| | latex/ | 5 | awesome-cv (cv + resume), deedy (mac + open), header-as-name | | word/ | 1 | openresume-laverne-word-quartz | | google-docs/| 4 | chromium-headless × 4 (this commit) | | unknown/ | 1 | openresume-react-pdf | | mac-pages/ | 0 | needs manual export — tracked in #12 | | mac-preview/| 0 | tried cupsfilter, doesn't expose a clean Quartz | | | | re-rendering path on modern macOS — manual export | | | | needed, tracked in #12 | | indesign/ | 0 | needs InDesign access — tracked in #12 | Total: 11 PDFs across 4 categories. Issue #1's "≥4 categories" criterion met; "≥15 PDFs" still gapped at 11/15 — covered by #12's manual-export follow-up which was already filed for exactly this content work. Filename convention follows the README example (`google-docs-skia-m146.pdf`): `<renderer>-<variant>.pdf` inside the category folder. "chromium-headless" is honest about provenance (these are Chromium-rendered, not literal Google Docs exports, though they share the Skia/PDF renderer family — so they live in `google-docs/` for parser-path categorisation purposes). Verification: - `npm run typecheck`: clean - `npm run test`: 185 / 185 (181 baseline + 4 new corpus fixtures) - `npm run bake-fixtures` + `git diff` after: empty (snapshots round-trip) - All 4 PDFs: 0 hits on raw-byte grep for real-author tokens; no XMP packet - CONTRIBUTING.md test count refreshed 167 → 185 Refs #1, #12 * test(corpus): add 4 WeasyPrint Cairo-renderer fixtures to close ≥15 target Brings corpus to 16 PDFs across 4 categories — closes both of issue #1's remaining acceptance criteria. Renders the same 4 HTML templates from the prior commit through WeasyPrint (Cairo backend) instead of Chromium-headless (Skia). Drops them in the existing `unknown/` category per the README's "Generator unknown or one-off" classification — WeasyPrint isn't a category called out in #1's body, and re-using `unknown/` is honest about provenance (these aren't mac-pages / mac-preview / indesign exports, which genuinely require GUI apps I can't drive autonomously and remain tracked in #12). Why the same content twice with different renderers --------------------------------------------------- Each WeasyPrint PDF carries the same source HTML as its Chromium twin in google-docs/, but the rendered byte stream differs (different font subsetting, item layout, line breaks). That's a feature: identical content rendered by two distinct generators is exactly the test the corpus exists to do — surface renderer-dependent parser drift that a single-generator corpus would miss. Initial snapshot comparison shows the parser produces near-identical scores on Chrome vs WeasyPrint for the simpler layouts (98 vs 98, 24 vs 24, 84 vs 84), but diverges on the two-column case (53 vs 43). Worth a focused look later if that gap is a real Cairo-vs-Skia layout difference vs a parser regression — but not blocking this PR; the snapshots pin whatever the current behaviour is. Fixtures -------- - `weasyprint-cairo-classic.pdf` (score 98) - `weasyprint-cairo-two-column.pdf` (score 43) - `weasyprint-cairo-nonstandard-headers.pdf` (score 84 — second regression anchor for #19) - `weasyprint-cairo-minimal.pdf` (score 24) PII preflight (same multi-stage sweep as PR #13) ------------------------------------------------ - Body text grep against known real-author tokens: 0 hits per PDF - Info dict: `Title: Resume` only (HTML `<title>`, non-identifying) - XMP packet: absent - Raw byte grep: 0 hits Corpus state after this commit ------------------------------ | Category | PDFs | Notes | |-------------|------|------------------------------------------------| | latex/ | 5 | awesome-cv × 2, deedy × 2, header-as-name | | word/ | 1 | openresume-laverne (Quartz) | | unknown/ | 6 | openresume-react-pdf, name-set-apart-tagline, | | | | weasyprint-cairo × 4 (this commit) | | google-docs/| 4 | chromium-headless × 4 (prior commit) | | mac-pages/ | 0 | needs Apple Pages — tracked in #12 | | mac-preview/| 0 | needs macOS Preview re-save — tracked in #12 | | indesign/ | 0 | needs InDesign access — tracked in #12 | | **Total** | **16** | **≥15 ✓** (issue #1 acceptance) | Verification ------------ - npm run typecheck: clean - npm run test: 189 / 189 (185 baseline + 4 new corpus tests) - npm run bake-fixtures + git diff after: empty (snapshots round-trip) - All 4 weasyprint PDFs: 0 hits on raw-byte grep, no XMP, generic Title only - CONTRIBUTING.md test count refreshed 185 → 189 Install note for reviewers reproducing locally: WeasyPrint requires native Cairo/Pango/GLib libs. On macOS: pip install --user weasyprint brew install pango cairo glib export DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_FALLBACK_LIBRARY_PATH" weasyprint input.html output.pdf The committed `.pdf` files are the artifact — re-rendering isn't required to run tests; only baking new snapshots is, which uses the existing PDFs. Refs #1, #12 * test(corpus): address PR #26 review — rename to google-docs-skia-proxy + doc the taxonomy decision Addresses the [Blocking] + [Suggestion] findings from @s-annam's review on PR #26. The reviewer flagged that the 4 chromium-headless fixtures self-identify as `Creator: HeadlessChrome/149` / `Producer: Skia/PDF m149`, while `tests/fixtures/pdfs/README.md` previously routed "headless Chrome" to `unknown/` and reserved `google-docs/` for actual Google Docs exports. Without resolution, "Closes #1" rests on a mislabel. Choosing reviewer's option (b) — deliberately redefine `google-docs/` as a Skia/PDF category that accepts both real Google Docs exports and Chromium headless prints as a faithful Skia proxy. Google Docs's "Download as PDF" pipeline uses Skia/PDF; a `chrome --headless --print-to-pdf` of the same HTML produces structurally-equivalent output (same renderer family, same item-layout patterns, same font-subsetting behaviour the parser needs to handle). Treating them as the same category for parser-failure-mode purposes is honest about what the corpus is for — distinct renderers, not distinct product provenance. Changes: 1. Renamed 4 PDFs + 4 snapshots: chromium-headless-classic.{pdf,expected.json} → google-docs-skia-proxy-classic.{pdf,expected.json} chromium-headless-two-column.{pdf,expected.json} → google-docs-skia-proxy-two-column.{pdf,expected.json} chromium-headless-nonstandard-headers.{pdf,expected.json} → google-docs-skia-proxy-nonstandard-headers.{pdf,expected.json} chromium-headless-minimal.{pdf,expected.json} → google-docs-skia-proxy-minimal.{pdf,expected.json} New filename encodes the rationale per finding #2 — next contributor reading `google-docs-skia-proxy-*` immediately knows these are Skia prints used as proxies, not real Google Docs exports. 2. Updated `tests/fixtures/pdfs/README.md` taxonomy in two places: - `google-docs/` description now explicitly says it accepts Chromium `--print-to-pdf` Skia exports as a deliberate proxy, names the `google-docs-skia-proxy-*` filename prefix, and pre-empts confusion about the `Creator: HeadlessChrome/<v>` Info-dict string ("that is by design, not a mislabel"). - `unknown/` description no longer claims headless Chrome (it's now in `google-docs/` per the redefinition); WeasyPrint added as an example of what `unknown/` genuinely covers. 3. PDFs themselves are byte-identical to the prior commit — only the filenames changed. Snapshots re-baked to match the new filenames; counts and scores are unchanged. Re finding #3 (metricBullets drift, nit) --------------------------------------- Reviewer noted that on the `classic` variant, `metricBullets` drifts 8 → 6 between the chromium and weasyprint snapshots even though both score 98 — the difference gets absorbed by the 40-pt Specificity cap at the score boundary. Surfacing here so it's discoverable from `git log` rather than buried in the snapshot files: | variant | chromium metricBullets | weasyprint metricBullets | |---------|------------------------|--------------------------| | classic | 8 | 6 | | two-column | varies | varies | | nonstandard-headers | 8 | 8 | | minimal | 0 | 0 | The `classic` 8→6 gap suggests `bulletHasMetric` is sensitive to something in WeasyPrint's text layout that Chrome doesn't reproduce — likely word breaking or numeric-character spacing across line wraps. Worth a focused look as a separate parser investigation; not blocking this PR because the snapshot diff is fully captured either way. Re inline finding on nonstandard-headers ---------------------------------------- Reviewer asked the `Refs #19` link survive into the squash-merge commit body so the regression tripwire is discoverable from `git blame` later. Including the explicit trailer below. Verification ------------ - npm run typecheck: clean - npm run test: 189 / 189 (snapshots round-trip cleanly through the rename) - `git diff --cached` confirms no content changes on the 8 renamed files beyond the rename itself Refs #1, #12, #19 * test(corpus): include README taxonomy update missed from 9596d2e The previous commit (`9596d2e`) renamed the chromium fixtures and *claimed* in its body to also update `tests/fixtures/pdfs/README.md`, but the README diff didn't make it into the commit due to a stale cwd swallowing the relative path. The README change is what gives the rename its meaning — without it, the new filenames are arbitrary. This commit adds the actual README edit: - `google-docs/` description now explicitly states it accepts Chromium `--print-to-pdf` Skia exports as a deliberate proxy, names the `google-docs-skia-proxy-*` filename prefix, and pre-empts confusion about the `Creator: HeadlessChrome/<v>` Info-dict string. - `unknown/` description no longer lists "headless Chrome" (it's now in `google-docs/` per the redefinition); WeasyPrint added as an example of what `unknown/` genuinely covers. Together with `9596d2e`, this fully resolves the [Blocking] finding from @s-annam's review on PR #26 — taxonomy is now self-consistent and the corpus no longer contradicts its own README. Refs #1 --------- Co-authored-by: Srinivas Annam <annam@annam.org>
Summary
Closes #1 — hits both remaining acceptance criteria (≥4 source categories AND ≥15 PDFs total). Refs #12 for the GUI-app categories that remain genuinely manual.
Adds 8 synthetic fixtures across 2 renderers and updates
CONTRIBUTING.md's test count. All PII-clean.What changed
Two commits, kept separable for review:
e3d5282google-docs/(new category populated)tests/fixtures/pdfs/google-docs/chromium-headless-*.{pdf,expected.json},CONTRIBUTING.mdd092608unknown/(third renderer beyond Chrome and LaTeX)tests/fixtures/pdfs/unknown/weasyprint-cairo-*.{pdf,expected.json},CONTRIBUTING.mdCorpus state
latex/word/unknown/google-docs/mac-pages/mac-preview/cupsfilter; macOS doesn't expose a clean Quartz-rerender CLI on PDF input — tracked in #12indesign/Each fixture exercises a different parser path
chromium-headless-classicchromium-headless-two-columngroupIntoLinesCOLUMN_GAP_THRESHOLDsplit from PR #18chromium-headless-nonstandard-headersexperienceCount: 0andmissing: ["work experience"]despite 8 visible bullets inOn Campus Involvement / Volunteer Experience / Internshipschromium-headless-minimalweasyprint-cairo-*× 4Bonus signal: parser-renderer drift. Same source content through Chrome (Skia) vs WeasyPrint (Cairo) scores near-identical on simple layouts (98/98, 24/24, 84/84) but diverges on the two-column case (53 vs 43). Could be real layout-engine differences or could expose a parser regression — snapshots pin both either way. Not blocking; worth a focused look if anyone's curious.
PII preflight — same multi-stage sweep as PR #13
All 8 new PDFs:
joel/pearson/byungjin/posquit/debarghya/ashesh/ashutosh/tilsen/claud d/deedy@/607.379/10-9030): 0 hits per PDFTitle: Resume(the HTML<title>, non-identifying)Verification
npm run typecheck: cleannpm run test: 189 / 189 (185 baseline + 4 new corpus tests)npm run bake-fixtures+git diffafter: empty (snapshots round-trip)CONTRIBUTING.mdtest count refreshed 167 → 189Repro note for reviewers
WeasyPrint needs native Cairo/Pango/GLib libs. On macOS:
The committed
.pdffiles are the artifact — re-rendering isn't required to run tests; only baking new snapshots is, which uses the existing PDFs.Out of scope / follow-ups worth filing
--print-to-pdf—<ul><li>markup renders the bullet glyph as a graphic, sopdftotextand our parser see 0 bullets. Worked around in these fixtures via explicit<p>• text</p>markup, but resumes exported from web apps (Google Docs, Notion, web resume builders) likely hit this in the wild. Worth a dedicated fixture + issue.Closes #1
Refs #12, #19
🤖 Generated with Claude Code