Skip to content

fix(templates): draw the employer in Monogram Sidebar experience entries - #577

Merged
DemchaAV merged 1 commit into
developfrom
fix/cv-monogram-employer-and-cap-docs
Aug 27, 2026
Merged

DemchaAV merged 1 commit into
developfrom
fix/cv-monogram-employer-and-cap-docs

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Stacked on #576 — retarget to develop once that merges.

Why

MonogramSidebar.addExperienceEntries rendered entry.title(), entry.date() and
entry.body() and never entry.subtitle(). In an experience section the subtitle is the
employer, so every company name was missing from the rendered CV. The education path in
the same preset does render its subtitle, which is what made the gap look accidental
rather than chosen.

Separately, three presets drop content without saying so. MonogramSidebar,
SidebarPortrait and MintEditorial each cap their blocks — a caller with four jobs who
picks one of them gets two, and nothing in the API or the produced PDF reports it.

What changed

The employer is drawn between the position and the date, in theme.entrySubtitleStyle()
— the shared theme style, not a preset-local one.

Each of the three presets documents its own caps, and the gallery
(docs/templates/v2-layered/using-templates.md) carries the same table under Picking a
preset
.

The caps themselves are unchanged, and the documentation says why. Each preset builds
its columns as one addRow, which is atomic: it fits a page whole or the paginator raises
AtomicNodeTooLargeException. Removing every cap and rendering a dense CV through the
three presets reproduces that immediately — MonogramSidebar asks for 1043pt of an
841pt page, SidebarPortrait 1157pt, MintEditorial 894pt of 745pt — and it does so at
two experience entries, because the skill and education caps are load-bearing too. So
lifting them does not paginate; it turns a CV that silently lost an entry into one that
fails to render. Doing it properly means teaching each preset to pick its own page
boundaries, the way TimelineMinimal does with ColumnPagination — which that helper
cannot serve as written, since it models flattened text blocks and these presets carry a
monogram ring, an SVG contact stack, a portrait and skill bars.

Verification

./mvnw -B -ntp clean verify -pl :graph-compose-core,:graph-compose-render-pdf,:graph-compose-render-docx,:graph-compose-render-pptx,:graph-compose-templates,:graph-compose-testing,:graph-compose-qa,:graph-compose-coverage -am — BUILD SUCCESS, 762 qa tests.
./mvnw -B -ntp javadoc:javadoc -pl :graph-compose-core,:graph-compose-templates — BUILD SUCCESS.

  • MonogramSidebarContentFidelityTest (qa, 2 tests) pins that an experience entry names
    its employer, and that the entry past the documented cap is the only one missing. Both
    go red without this change.
  • One visual baseline (cv-v2-layered/monogram_sidebar-page-0.png) and one committed
    preview (cv-monogram-sidebar-v2.pdf) carry the extra line; the drift gate names no
    others.

An experience entry rendered the position, the date and the description
and never CvEntry.subtitle(), so every company name was missing from the
rendered CV. The education block in the same preset does render its
subtitle, which is what made the gap look accidental rather than chosen.
The employer is now drawn between the position and the date, in the
shared theme entry-subtitle style.

The three presets that cap content — Monogram Sidebar, Sidebar Portrait
and Mint Editorial — now say so. Entries past a per-block cap are not
drawn, do not move to a continuation page, and are reported nowhere, so a
caller picking one of them had no way to learn that a four-job CV shows
two. Each class documents its own caps and the gallery carries the same
table.

The caps stay, and the documentation says why: the columns are one
addRow, which is atomic, so an uncapped block does not spill onto a
second page — it raises AtomicNodeTooLargeException and the CV does not
render at all. Removing every cap and rendering a dense CV through each
preset reproduces that immediately. Lifting them means teaching each
preset to pick its own page boundaries first, the way TimelineMinimal
does with ColumnPagination.

Tests: MonogramSidebarContentFidelityTest pins the employer and the
documented cap; both go red without this change. Full reactor gate green
(762 qa tests). The Monogram Sidebar visual baseline and committed
preview carry the extra line.
@DemchaAV
DemchaAV force-pushed the fix/cv-monogram-employer-and-cap-docs branch from b9d0d57 to 534d97a Compare August 27, 2026 08:32
@DemchaAV
DemchaAV merged commit 79898df into develop Aug 27, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/cv-monogram-employer-and-cap-docs branch August 27, 2026 08:38
DemchaAV added a commit that referenced this pull request Sep 13, 2026
…#686)

* test(templates): restore CV and cover-letter layout snapshot coverage

Sixteen layered CV presets and all fifteen cover letters shipped with a pixel
gate and no layout snapshot. The suites that pinned them were deleted with the
classic families in #276 and #277; their baselines stayed behind, and no test
has read them since. The pixel gates tolerate 50 000 mismatched pixels, so a
shift of a few points in any of these presets passes unseen.

- CvPresetFixtures and CoverLetterPresetFixtures hold each family's roster and
  canonical document, moved verbatim out of the two pixel gates, so the pixel
  gate and the snapshot gate render the same presets on the same document. The
  only text change is a javadoc pointer that named the old location.
- CvPresetLayoutSnapshotTest and CoverLetterPresetLayoutSnapshotTest snapshot
  every preset on the roster, as <pixel slug>_layout.
- CoverLetterTextLayerGateTest reads the letter from its new home.

The 31 baselines were recorded by these gates on this tree, after every
difference from the renders these presets last had a snapshot of was
explained. Against their v2.2.0 renders 18 are identical and 13 moved, and
each move traces to a develop commit rendered on either side of it:

- #676, spaced caps set with tracking instead of padded strings: heading and
  name widths only, no coordinate, height or page change. classic_serif,
  sidebar_portrait, mint_editorial, timeline_minimal (CV and letter), and part
  of minimal_underlined, centered_headline and monogram_sidebar.
- d0c946a (in #633), stacked rows drawn as lists: bullet paragraphs become
  ListNodes, four fewer nodes on each of boxed_sections, minimal_underlined,
  modern_professional, executive and panel, and the list half of blue_banner
  and centered_headline.
- #577, the employer drawn in Monogram Sidebar's experience entries: two
  added nodes.

Page counts are unchanged in all 31, and every node that moved still sits
inside its parent and inside the page's content band.

* test(templates): restore proposal and invoice layout snapshot coverage

ModernProposal and ModernInvoice had no layout snapshot either; every other
proposal and invoice preset carries a suite of its own.

- ProposalPresetLayoutSnapshotTest snapshots ModernProposal on the canonical
  proposal its pixel gate already renders.
- InvoicePresetFixtures gains layoutSnapshotPresets(), read by
  InvoicePresetLayoutSnapshotTest. It lists ModernInvoice only: ClassicInvoice
  takes the same spec, but ClassicInvoiceLayoutSnapshotTest already pins it on
  the same invoice at the same margin, so listing it would record that page
  twice. canonicalInvoice() and stressInvoice() are unchanged.

modern_invoice_layout is identical to its v2.2.0 render.

modern_proposal_layout differs from its v2.2.0 render in one node:
ProposalV2ModernFooter[6], a bare page-flow wrapper around the footer note,
moves from y 20.24 on pages 0..1 to y 790.64 on page 1. The paragraph inside
it, the only thing it draws, is at y 790.64 on page 1 in both. The note needs
23.25pt and 15.49pt were left under the acceptance terms, so it went to page
1 and the empty wrapper stayed behind claiming page 0 - the defect 361ff83
(in #633) fixed, and the commit this node changes at when rendered on either
side of it. Page count 2 and node count 43 are unchanged, and nothing drawn
moved.

* test(templates): hold every shipped preset to a layout snapshot

The thirty-three presets restored in the previous two commits went two months
without a layout snapshot and nothing said so. Their suites were deleted along
with another family, their baselines stayed on disk, and every gate that
remained only checked the presets it listed.

ShippedPresetLayoutSnapshotCoverageTest derives the two sets independently
and compares them:

- shipped: every class under a templates presets package that declares a
  public ID, read from the templates sources;
- covered: every preset an active layout-snapshot suite renders, read from the
  qa sources. A suite is a test that calls assertCanonicalSnapshot and is not
  disabled; it covers a preset of its own family it creates directly, or
  through a Stream<Arguments> roster it reads from a fixture. A fixture read
  only for its document covers nothing, and comments and strings are stripped
  before any of this is matched.

Neither side is a hand-kept list and no count is asserted, so a preset added
without a suite fails it by name. An allow-list takes a reviewed exemption
with its reason and is empty; a second test rejects an entry that does not
ship, or that a suite already covers.

Sabotaged, each change confirmed on disk before the run and reverted after:
- the four gates added in this branch removed: fails naming exactly the 33
- mint_editorial dropped from the CV roster: names cv/MintEditorial
- a new preset class with an ID and no suite: names it
- CobaltRota's suite disabled by a fully qualified @disabled, or no longer
  calling assertCanonicalSnapshot: names rota/CobaltRota
- ClassicInvoice's suite rendering another preset while still reading
  InvoicePresetFixtures for its document: names invoice/ClassicInvoice
- an exemption for a covered preset, and one for a preset that does not ship:
  the second test fails, naming each

* test(templates): remove the orphaned snapshots and dead fixture helpers

Thirty-eight baselines under layout-snapshots/canonical-templates had no
reader. Thirty-two lost their suites when the classic families went: the
template_v2_cv and cv-v2/regression files in #277, the template_v2_letter
files in #276. The invoice_standard, proposal_long and four weekly-schedule
layouts were last read by tests removed in the v1.6 cleanup, 47241c8. Nine
TemplateTestSupport helpers that fed those tests had no callers left.

Both removals were proven on this tree immediately before deleting:
- no tracked file names any of the 38 outside the snapshot folders, except
  docs/operations/layout-snapshot-testing.md, whose naming section lists two
  of them as examples beside two names that never existed as files;
- nothing builds their names from parts (template_v2_, regression,
  weekly_schedule), and no test or tool walks the snapshot folders;
- the nine helpers have no qualified call, static import, wildcard import,
  method reference, or string or reflective use; the only calls to them were
  from one another.

TemplateTestSupport now says what the canonical root is for, that a baseline
belongs to the suite that writes it, and that
ShippedPresetLayoutSnapshotCoverageTest holds every shipped preset to it.

Every *LayoutSnapshotTest in qa and the coverage test stay green after the
removal: 34 classes, 74 tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant