diff --git a/CHANGELOG.md b/CHANGELOG.md
index 17eb8177..c495bec6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -127,6 +127,21 @@ follow semantic versioning; release dates are ISO 8601.
### Documentation
+- **The template-authoring guide describes the packages that exist.** It told a
+ contributor to put a new family under `templates..v2` in exactly five
+ sub-packages, one of them a per-family `theme/` — a layout 2.0 replaced. A family has
+ `data` / `components` / `widgets` / `presets`, the cosmetic tokens are the shared
+ `BrandTheme`, and the rule about not editing the v1 surface outlived that surface. The
+ contributing guide had the same problem in one paragraph, routing new template code
+ into `templates.builtins` and `templates.support`; both are gone, so the instruction
+ produced code that does not compile. Package names join the retired-surface guard,
+ which until now could only see types.
+- **Three documents stop pointing at things that are not there.** The extension guide
+ sent a reader to a snapshot-test directory that does not exist, the layout-snapshot
+ page listed two tests among six that are not in the repository, and the docs index
+ still carried a rework warning for a page reworked a major release ago. The roadmap
+ asked for a warning on dropped DOCX content that the backend already logs; what it is
+ really asking for — a mode that refuses instead of dropping — now says so.
- **The example catalogue stops offering a type that ships nowhere.** Its entry-point
table introduced the cover letter as a `BusinessTheme.modern()` document, and
`BusinessTheme` is a record local to the examples module — a reader adding the
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f47981ff..ff75325d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -120,7 +120,7 @@ The 2.0 GA shipped, so the branches now hold their long-term roles:
- `render-docx/` — module **graph-compose-render-docx**
Semantic exporter `DocxSemanticBackend` (Apache POI based), under `com.demcha.compose.document.backend.semantic.docx`
- `templates/src/main/java/com/demcha/compose/document/templates/*` — module **graph-compose-templates**
- Built-in templates (CV, cover letter, invoice, proposal, weekly schedule), DTOs, themes, registries, and scene composition helpers
+ Template families (CV, cover letter, invoice, proposal), their data records, the shared `BrandTheme`, and the widgets they compose from. Schedule data records live here too; the schedule document is composed by an example rather than by a template.
- `core/src/main/java/com/demcha/compose/document/showcase`
`FontShowcase` (bundled-font preview renderer) — stays in the core engine
- `core/src/main/java/com/demcha/compose/engine/*`
@@ -176,12 +176,13 @@ template feature. The rules:
- Layout integration for a new node is a `NodeDefinition`
registered with `NodeRegistry`. See `BuiltInNodeDefinitions` for
the established pattern.
-- Built-in templates in `...document.templates.builtins` stay thin
- public facades over reusable scene composers in
- `...document.templates.support`. Keep PDF-only setup in the document
- session/backend layer rather than inside template composers, and do
- not import `PDDocument`, `PDPage`, `PDRectangle`, or low-level PDF
- composer types into scene composer classes.
+- A template family lives under `...document.templates.` with
+ `data` / `components` / `widgets` / `presets`; cosmetic tokens are the
+ shared `BrandTheme` in `...document.templates.core.theme`, never a
+ package of the family's own. Keep PDF-only setup in the document
+ session/backend layer rather than inside a preset, and do not import
+ `PDDocument`, `PDPage`, `PDRectangle` or other low-level PDF types into
+ template code.
- Public template contracts are compose-first: prefer
`compose(DocumentSession, ...)`. New README snippets, runnable
examples, and integration docs must show `compose(...)` rather than
@@ -275,9 +276,9 @@ There is one template authoring pattern, whether you are adding a
new family or a new preset inside an existing one: the layered
architecture documented in
[**docs/templates/v2-layered/contributor-guide.md**](./docs/templates/v2-layered/contributor-guide.md).
-Five sub-packages (`data/` / `theme/` / `components/` / `widgets/`
-/ `presets/`), each with a clear contract, over the shared
-`templates.core` layer.
+A family has `presets/` and adds `data/`, `components/` and `widgets/`
+as it needs them, each with a clear contract, over the shared
+`templates.core` layer that owns the theme.
- Every preset is a `public final class` — no inheritance — with a
`create(BrandTheme)` factory returning `DocumentTemplate`, plus a
diff --git a/ROADMAP.md b/ROADMAP.md
index 98fa0d4d..a51cf188 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -37,7 +37,7 @@ Committed internal direction for the post-2.0 line: refactors, scale work, and t
Not committed. Reflects current thinking; priorities may shift based on user feedback and adoption signals.
- **PPTX beyond beta.** Graduate the fixed-layout PPTX backend from `@Beta` to stable and close the remaining fidelity gaps — true vector clipping instead of the raster fallback ([#413](https://github.com/DemchaAV/GraphCompose/issues/413)), exact numeric dash arrays, and distinct per-corner radii.
-- **DOCX visibility for unsupported nodes.** Make currently-silent skips (`shape`, `line`, `ellipse`, `barcode`) loud — minimum a warn log, ideally a strict-mode flag that fails instead of dropping content silently.
+- **A strict mode for DOCX.** Dropping a node the format cannot carry (`shape`, `line`, `ellipse`, `barcode`) now warns, so the loss is at least visible in the log. What is missing is the option to refuse: a flag that fails the export instead of silently producing a document with content gone.
- **Block-level alignment for fixed-size flow children.** Paths, images, layer stacks, shape containers and barcodes currently left-align in a flow; centring one means wrapping it in a full-width `ShapeContainer` just to use its CENTER anchor. Add a per-node horizontal align (left / centre / right — the `margin: auto` / `align(center)` analogue) so a fixed box can place itself in the flow directly. Surfaced by the v1.8 SVG icon-gallery and feature-catalog work.
- **Backend-neutral layout measurement.** Decouple measurement from PDFBox-specific resources so non-PDF backends do not pull PDFBox into the dependency graph.
- **DOCX maturity.** Either expand DOCX coverage toward PDF parity, or move DOCX behind an explicitly experimental flag.
diff --git a/core/src/test/java/com/demcha/documentation/CanonicalSurfaceGuardTest.java b/core/src/test/java/com/demcha/documentation/CanonicalSurfaceGuardTest.java
index 0ba64a6d..a97156d5 100644
--- a/core/src/test/java/com/demcha/documentation/CanonicalSurfaceGuardTest.java
+++ b/core/src/test/java/com/demcha/documentation/CanonicalSurfaceGuardTest.java
@@ -29,13 +29,14 @@ class CanonicalSurfaceGuardTest {
"ModuleSummary");
/**
- * Types the 2.0 line removed. Every one is absent from every {@code src/main}
- * tree, so a contributor-facing document naming one is teaching code that cannot
- * compile — which is how the template-authoring and engine-primitive sections of
- * CONTRIBUTING went stale for a full release cycle.
+ * Names the 2.0 line removed — types, and the packages that held them. Every one is
+ * absent from every {@code src/main} tree, so a contributor-facing document naming
+ * one is teaching code that cannot compile — which is how the template-authoring
+ * and engine-primitive sections of CONTRIBUTING went stale for a full release
+ * cycle, and how two dead package names outlived the types that lived in them.
*
* Deliberately absent: {@code BusinessTheme} survives as an examples-local
- * theme helper used by twenty-one example sources, and {@code PptxSemanticBackend}
+ * theme helper the example catalogue still uses, and {@code PptxSemanticBackend}
* still ships beside the fixed-layout PPTX backend. Forbidding either would fail
* the build on text that is correct.
*/
@@ -49,7 +50,19 @@ class CanonicalSurfaceGuardTest {
"Breakable",
"hasRender(",
"CvSpec",
- "CvBuilder");
+ "CvBuilder",
+ // Package names, not types. The retired-type list could not catch a
+ // contributing guide that routed new template code into
+ // `templates.builtins` and `templates.support` — two packages the 2.0
+ // split left behind and nothing has occupied since, so the instruction
+ // read as current and produced code that does not compile. Both spellings
+ // are listed: a doc names a package either as a dotted coordinate or as the
+ // path it lives at, and the sentence that carried this one for a release
+ // used the path.
+ "templates.builtins",
+ "templates.support",
+ "templates/builtins/",
+ "templates/support/");
/**
* Types a contributor must not be pointed at when told how to build against the
@@ -211,9 +224,9 @@ void contributorFacingDocsShouldNotNameSurfaceRetiredIn2_0() throws IOException
}
assertThat(violations)
- .describedAs("these documents name a type 2.0 removed, so anyone following "
- + "them writes code that does not compile. A document whose purpose "
- + "is to record the removal belongs under one of %s.",
+ .describedAs("these documents name a type or package 2.0 removed, so anyone "
+ + "following them writes code that does not compile. A document whose "
+ + "purpose is to record the removal belongs under one of %s.",
HISTORICAL_RECORD_PREFIXES)
.isEmpty();
}
diff --git a/docs/README.md b/docs/README.md
index efc6ff57..74f6930d 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -34,8 +34,8 @@ back here.
- **[troubleshooting.md](troubleshooting.md)** — symptom-first fixes for common gotchas: stray `?` glyphs, silent DOCX drops, optional-dependency `NoClassDefFoundError`, running the bundled examples.
### Templates
-- **[templates/business-templates.md](templates/business-templates.md)** — invoice & proposal templates: the compose-first contract, end to end. ⚠️ Being reworked for the layered `ModernInvoice` / `ModernProposal` surface.
-- **[templates/v2-layered/](templates/v2-layered/)** — the template surface (CV is the reference implementation): `data` / `theme` / `components` / `widgets` / `presets`.
+- **[templates/business-templates.md](templates/business-templates.md)** — invoice & proposal templates: the compose-first contract, end to end, on the layered `ModernInvoice` / `ModernProposal` surface.
+- **[templates/v2-layered/](templates/v2-layered/)** — the template surface (CV is the reference implementation): `data` / `components` / `widgets` / `presets` per family, over the shared `templates.core.theme`.
- **[templates/v1-classic/](templates/v1-classic/)** — 🗄️ archived: the classic spec/builder/presets surface removed in 2.0; kept for pre-2.0 callers.
### Output backends
@@ -107,7 +107,7 @@ it does.
- **[adr/0012-nested-list-evolution.md](adr/0012-nested-list-evolution.md)** — nested list rendering evolution.
- **[adr/0013-composed-table-cell.md](adr/0013-composed-table-cell.md)** — composed table cell model.
- **[adr/0014-controlled-absolute-placement.md](adr/0014-controlled-absolute-placement.md)** — controlled absolute placement strategy.
-- **[adr/0015-layered-template-architecture.md](adr/0015-layered-template-architecture.md)** — the layered `cv.v2` / `coverletter.v2` authoring model (current standard); supersedes the preset/builder portion of 0011.
+- **[adr/0015-layered-template-architecture.md](adr/0015-layered-template-architecture.md)** — the layered `templates.cv` / `templates.coverletter` authoring model (current standard); supersedes the preset/builder portion of 0011.
> **ADR numbering gap (0005–0010)** is intentional — those numbers
> were reserved during a v1.5 restructure that landed under ADR 0011
diff --git a/docs/contributing/extension-guide.md b/docs/contributing/extension-guide.md
index 082e9524..02546114 100644
--- a/docs/contributing/extension-guide.md
+++ b/docs/contributing/extension-guide.md
@@ -212,9 +212,9 @@ Set-up per node:
The
[`LayoutSnapshotAssertions`](../../testing/src/main/java/com/demcha/compose/testing/layout/LayoutSnapshotAssertions.java)
-helper class wraps the diff machinery; existing snapshot tests under
-`core/src/test/java/com/demcha/compose/document/templates/builtins/`
-(e.g. `BuiltInTemplateLayoutSnapshotTest`) show the call site shape.
+helper class wraps the diff machinery; the snapshot tests in the `qa`
+module (`ChartLayoutSnapshotTest`, `ShapeContainerLayoutSnapshotTest`)
+show the call site shape.
## Reading the source by responsibility
diff --git a/docs/operations/layout-snapshot-testing.md b/docs/operations/layout-snapshot-testing.md
index 518a04e0..b1afdf30 100644
--- a/docs/operations/layout-snapshot-testing.md
+++ b/docs/operations/layout-snapshot-testing.md
@@ -340,8 +340,6 @@ Prioritize documents that are most sensitive to layout regressions:
## Examples in this repository
-- `RepositoryShowcaseRenderTest`
-- `TablePaginationIntegrationTest`
- `FontShowcaseLayoutSnapshotTest`
- `ChartLayoutSnapshotTest`
- `ShapeContainerLayoutSnapshotTest`
diff --git a/docs/templates/v2-layered/README.md b/docs/templates/v2-layered/README.md
index 36a988b5..00cd3ccc 100644
--- a/docs/templates/v2-layered/README.md
+++ b/docs/templates/v2-layered/README.md
@@ -52,20 +52,19 @@ inline DSL, and how to ship a new preset as ~150 lines that anyone can
read end-to-end.
### 🛠 You're adding a new template family to the library
-You're a GraphCompose maintainer or contributor. You want to bring
-invoice-v2, cover-letter-v2, or a new document type onto the same
-5-layer pattern that CV uses.
+You're a GraphCompose maintainer or contributor. You want to bring a
+new document type onto the same layering the shipped families use.
→ **[contributor-guide.md](contributor-guide.md)**
-You'll get the package convention (`/v2/data` /
-`theme` / `components` / `widgets` / `presets`), naming rules, test
-expectations, doc expectations, and a worked checklist for a new
-template family from empty folder to merged PR.
+You'll get the package convention (`/data` / `components` /
+`widgets` / `presets` over the shared `templates.core.theme`), naming
+rules, test expectations, doc expectations, and a worked checklist for a
+new template family from empty folder to merged PR.
---
-## The 5-layer pattern at a glance
+## The layering at a glance
```
presets/ composition: data + theme + widgets → DocumentTemplate
@@ -74,10 +73,11 @@ presets/ composition: data + theme + widgets → DocumentTemplate
widgets/ LEGO bricks: Headline, Subheadline, ContactLine, SectionHeader, …
│ delegate to read tokens from
▼ ▼
-components/ internal renderers + primitives theme/ palette
- typography
- spacing
- decoration
+components/ internal renderers + primitives templates.core.theme
+ BrandTheme: palette
+ typography
+ spacing
+ decoration
│ render
▼
data/ records describing what to render (no styling)
@@ -95,16 +95,17 @@ The detailed contract for each layer is in
## What this pattern is *not*
-- ❌ **Not a migration mandate.** Existing v1 templates
- (`cv/spec`, `cv/builder`, `cv/presets`) continue to work and
- ship. The layered pattern is for **new** templates and major
- rewrites.
+- ❌ **Not one shape for every family.** `presets/` is the only
+ package a family always has. CV carries all four; cover letter has no
+ `widgets/`; invoice and proposal are presets alone, reading data
+ records from the shared `templates.data.`. Add a layer when
+ the family needs one.
- ❌ **Not a framework with magic.** Every file is plain
Java records + static helpers. No reflection, no annotations,
no codegen.
- ❌ **Not coupled to CV.** The pattern is domain-agnostic; CV is
- just the first family migrated. Invoice or cover-letter would
- use the same five folders with their own data shapes inside.
+ just the family that uses every layer, which is why it is the
+ reference implementation.
- ❌ **Not a UI framework.** No state, no events, no lifecycle.
Templates render static PDFs from immutable data.
diff --git a/docs/templates/v2-layered/authoring-presets.md b/docs/templates/v2-layered/authoring-presets.md
index 0ee2259a..a54739ac 100644
--- a/docs/templates/v2-layered/authoring-presets.md
+++ b/docs/templates/v2-layered/authoring-presets.md
@@ -384,7 +384,7 @@ When you do add a new widget:
A new preset needs at least:
-1. **Smoke test** in `core/src/test/.../cv/presets/MyPresetSmokeTest.java`:
+1. **Smoke test** in `qa/src/test/.../cv/presets/MyPresetSmokeTest.java`:
- `exposes_stable_identity` — checks `id()` and `displayName()`
- `default_factory_renders` — calls `create().compose(...)` with
a full sample document, asserts `session.roots()` is non-empty
diff --git a/docs/templates/v2-layered/contributor-guide.md b/docs/templates/v2-layered/contributor-guide.md
index aab4da0f..e0049dcb 100644
--- a/docs/templates/v2-layered/contributor-guide.md
+++ b/docs/templates/v2-layered/contributor-guide.md
@@ -1,9 +1,9 @@
# Contributor Guide — add a new template family
You're a GraphCompose contributor and you want to bring a brand-new
-document type onto the layered architecture — invoice-v2,
-cover-letter-v2, report-v2, anything that isn't CV. This doc is
-your checklist + convention reference.
+document type onto the layered architecture — a report, a
+statement, anything the shipped families do not already cover. This
+doc is your checklist + convention reference.
It assumes you've read [quickstart.md](quickstart.md) and
[authoring-presets.md](authoring-presets.md) — those explain *why*
@@ -13,7 +13,7 @@ the layered pattern looks the way it does.
## Table of contents
-1. [The 5-layer convention](#the-5-layer-convention)
+1. [The layer convention](#the-5-layer-convention)
2. [Package map for a new family](#package-map-for-a-new-family)
3. [Naming rules](#naming-rules)
4. [Worked walkthrough — `invoice`](#worked-walkthrough)
@@ -26,30 +26,35 @@ the layered pattern looks the way it does.
---
-## The 5-layer convention
+## The layer convention
Every new template family lives under
-`com.demcha.compose.document.templates..v2` with **exactly
-five sub-packages**:
+`com.demcha.compose.document.templates.` with these
+sub-packages:
```
-/v2/
+/
├── data/ records describing what's on the page
-├── theme/ cosmetic tokens (palette, typography, spacing, decoration)
├── components/ internal renderers + low-level primitives
-├── widgets/ reusable visual LEGO bricks
+├── widgets/ reusable visual LEGO bricks — omit if the family has none
└── presets/ composition: data + theme + widgets → DocumentTemplate
```
+Cosmetic tokens are **not** per-family. `templates.core.theme` holds
+`BrandTheme` and its parts (`Palette`, `Typography`, `Spacing`,
+`Decoration`), and every family reads from it — that is what keeps two
+presets from two families looking like one product. A family that needs
+a token nobody else has adds it there, not to a package of its own.
+
Each layer's contract:
| Layer | Contract |
|---|---|
| `data/` | Pure records. Zero dependencies on rendering, theming, or DSL. Sealed hierarchy for section / block subtypes. |
-| `theme/` | Records + factories. No rendering logic. Aggregate root is `Theme(palette, typography, spacing, decoration)`. |
+| `templates.core.theme` | Shared, not per-family. `BrandTheme(palette, typography, spacing, decoration)` plus its parts. Records + factories, no rendering logic. |
| `components/` | Static helpers. Take `(host, data, theme)`. No statics holding state. No magic numbers — read tokens from theme. |
-| `widgets/` | Static helpers. Named factory methods per visual variant. Compose internally from `components/`. |
-| `presets/` | One `public final class` per visual style. Two factories: `create()` and `create(Theme)`. Inner `Template` implements `DocumentTemplate<Document>`. |
+| `widgets/` | Static helpers. Named factory methods per visual variant. Compose internally from `components/`. Optional — the cover-letter family has none. |
+| `presets/` | One `public final class` per visual style. Two factories: `create()` and `create(BrandTheme)`. Inner `Template` implements `DocumentTemplate<Document>`. |
**The convention is the same regardless of domain.** A cv has
identity + sections; an invoice has parties + line items + totals; a
@@ -61,10 +66,14 @@ data differs; the *layering* is identical.
## Package map for a new family
-Mirror the CV v2 layout. Concrete example for invoice:
+The fullest shape, which is CV's. A family takes the layers it needs and
+no more: cover letter has no `widgets/`, and the shipped invoice and
+proposal families are `presets/` alone, reading their records from the
+shared `templates.data.`. The tree below is an invoice-shaped
+domain drawn at full size, so every layer has something in it:
```
-templates/src/main/java/com/demcha/compose/document/templates/invoice/
+templates/src/main/java/com/demcha/compose/document/templates//
├── package-info.java ← ASCII diagram + 4-step walkthrough
├── AUTHORS.md ← recipe cookbook
├── data/
@@ -79,13 +88,6 @@ templates/src/main/java/com/demcha/compose/document/templates/invoice/
│ ├── TotalsSection.java ← concrete subtype
│ ├── NotesSection.java ← concrete subtype
│ └── Slot.java ← if multi-column variants needed
-├── theme/
-│ ├── package-info.java
-│ ├── InvoicePalette.java ← ink / muted / rule / accent
-│ ├── InvoiceTypography.java ← scale
-│ ├── InvoiceSpacing.java ← margins, gaps
-│ ├── InvoiceDecoration.java ← row separators, totals divider
-│ └── InvoiceTheme.java ← aggregate + factories
├── components/
│ ├── package-info.java
│ ├── ParagraphPrimitive.java ← internal — package-private
@@ -106,8 +108,8 @@ templates/src/main/java/com/demcha/compose/document/templates/invoice/
└── (more as added)
```
-The structure is **identical** to cv — only the records inside
-differ.
+That is cv's structure with invoice records inside. A smaller family
+keeps `presets/` and drops the rest.
---
@@ -116,13 +118,13 @@ differ.
- **Family prefix** on top-level records to avoid name collisions.
CV uses `CvName`, `CvIdentity`, `CvSection`. Invoice should use
- `InvoiceParty`, `InvoiceLine`, `InvoiceTheme`. Cover letter:
- `CoverLetterRecipient`, `CoverLetterTheme`. Etc.
+ `InvoiceParty`, `InvoiceLine`. Cover letter:
+ `CoverLetterRecipient`, `CoverLetterBody`. Etc.
- **`Document`** for the root record. (`CvDocument`,
`InvoiceDocument`, `CoverLetterDocument`.)
- **`Section` (sealed)** for the body content hierarchy.
-- **`Theme(palette, typography, spacing, decoration)`** for
- the aggregate theme record.
+- **No `Theme`.** The aggregate theme record is the shared
+ `BrandTheme`; a family names a factory on it, not a type of its own.
- **Widgets** are domain-specific verbs / nouns describing the
visual: `Headline`, `ContactLine`, `SectionHeader` (CV);
`Letterhead`, `LineTable`, `TotalsCard` (invoice). Don't try to
@@ -162,23 +164,16 @@ Sealed `InvoiceSection` lists every body shape an invoice can have.
Concrete subtypes (`LineItemsSection`, `TotalsSection`, …) are
records carrying the section data.
-### 2. `theme/` second (no rendering, just tokens)
+### 2. the theme second (there is nothing to write)
+
+Pick the `BrandTheme` factory the family should default to, or add
+one beside the others in `templates.core.theme` if none fits. The
+record is shared, so a token added for an invoice is available to a
+CV — which is the point, and also the reason to think before adding
+one.
```java
-public record InvoiceTheme(
- InvoicePalette palette,
- InvoiceTypography typography,
- InvoiceSpacing spacing,
- InvoiceDecoration decoration) {
-
- public static InvoiceTheme classic() {
- return new InvoiceTheme(
- InvoicePalette.classic(),
- InvoiceTypography.classic(),
- InvoiceSpacing.classic(),
- InvoiceDecoration.classic());
- }
-}
+BrandTheme theme = BrandTheme.invoiceModern();
```
### 3. `components/` third (low-level renderers consume data + theme)
@@ -188,7 +183,7 @@ public final class LineRowRenderer {
private LineRowRenderer() {}
public static void render(SectionBuilder host, InvoiceLine line,
- InvoiceTheme theme) {
+ BrandTheme theme) {
// … DSL calls reading theme tokens …
}
}
@@ -205,7 +200,7 @@ public final class LineTable {
private LineTable() {}
public static void render(SectionBuilder host,
- List lines, InvoiceTheme theme) {
+ List lines, BrandTheme theme) {
for (InvoiceLine line : lines) {
LineRowRenderer.render(host, line, theme);
}
@@ -227,10 +222,10 @@ public final class ClassicInvoice {
private ClassicInvoice() {}
public static DocumentTemplate create() {
- return create(InvoiceTheme.classic());
+ return create(BrandTheme.invoiceModern());
}
- public static DocumentTemplate create(InvoiceTheme theme) {
+ public static DocumentTemplate create(BrandTheme theme) {
return new Template(theme);
}
@@ -250,15 +245,15 @@ Mirror the CV preset shape exactly.
A new template family PR ships with:
-- [ ] **5 packages** under `/v2/` populated per convention
+- [ ] **The sub-packages** under `/` populated per convention
- [ ] **At least 1 reference preset** that renders a sample document
- [ ] **`AUTHORS.md`** in the family root (recipe cookbook — copy
the cv one as starting structure)
- [ ] **`package-info.java`** at family root + each sub-package
- [ ] **Sample fixture** in `ExampleDataFactory.sampleDocumentV2()`
-- [ ] **Example runner** in `examples/.../templates//v2/`
+- [ ] **Example runner** under `examples/.../templates//`
- [ ] **Smoke tests** per the checklist below
-- [ ] **No edits** to `engine/`, `dsl/`, or v1 `/` surface
+- [ ] **No edits** to `engine/` or `dsl/`
---
@@ -270,7 +265,6 @@ Minimum test coverage matching CV v2:
| Test class | What it asserts |
|---|---|
| `DocumentTest` | Builder rejects null / blank required fields; valid build succeeds |
-| `ThemeTest` | All factories produce valid themes; deprecated constructors (if any) wrap correctly |
| `SmokeTest` | `id()`, `displayName()`, default-factory render, custom-theme render |
| `SectionDispatcherTest` *(optional)* | Each sealed subtype routes correctly |
| `WidgetSmokeTest` | Each public widget variant renders without throwing |
@@ -301,7 +295,7 @@ git commit -m "test: refresh visual baselines after "
```
**Where baselines live:**
-`core/src/test/resources/visual-baselines/-v2-layered/-page-N.png`
+`qa/src/test/resources/visual-baselines/-v2-layered/-page-N.png`
One PNG per page per preset. Pages overflow naturally — a 2-page
preset gets `-page-0.png` and `-page-1.png`.
@@ -331,7 +325,7 @@ baseline so a reviewer can see exactly what changed before deciding
to re-bless or fix.
**Reference**: see
-`core/src/test/java/com/demcha/compose/document/templates/cv/presets/CvV2VisualParityTest.java`
+`qa/src/test/java/com/demcha/compose/document/templates/cv/presets/CvV2VisualParityTest.java`
— a 200-line drop-in template you can copy for a new family.
---
@@ -339,13 +333,13 @@ to re-bless or fix.
## Doc checklist
-- [ ] `/v2/package-info.java` — ASCII diagram of the 5
- layers, plus a 4-step "how to author a document" walkthrough
- (copy the cv one's structure).
-- [ ] `/v2/AUTHORS.md` — recipe cookbook. At least:
+- [ ] `/package-info.java` — ASCII diagram of the layers,
+ plus a 4-step "how to author a document" walkthrough (copy the
+ cv one's structure).
+- [ ] `/AUTHORS.md` — recipe cookbook. At least:
change a glyph, change colours, add a new section subtype,
conditional sections.
-- [ ] `/v2//package-info.java` — each
+- [ ] `//package-info.java` — each
sub-package gets a paragraph explaining its role.
- [ ] **Update [`docs/templates/v2-layered/README.md`](README.md)** to
list the new family in the "implementations" section.
@@ -359,9 +353,9 @@ to re-bless or fix.
`document/engine`, `document/node`, `document/style`). If your
family needs an engine feature that doesn't exist, that's a
separate prerequisite PR.
-- ❌ **Don't edit v1 surface** for the same family. They coexist.
- Mark v1 `@Deprecated` only after the v2 surface is feature-complete
- and shipped — that's a follow-up PR.
+- ❌ **Don't add a theme package to your family.** Tokens live in
+ `templates.core.theme` so families stay visually consistent; a
+ private palette is how that consistency is lost one family at a time.
- ❌ **Don't fork widgets across families.** If invoice needs a
`Headline`, write `templates/invoice/widgets/Letterhead.java`
— an invoice letterhead has different needs from a CV name
@@ -389,13 +383,13 @@ A new template family PR is reviewed against:
1. **Layer discipline** — do data / theme / components / widgets /
presets each obey their contract?
2. **Test coverage** — smoke tests for every public surface;
- builder validation tests; theme factory tests.
+ builder validation tests.
3. **Doc completeness** — `package-info.java` everywhere,
`AUTHORS.md` with at least 4 recipes, root README updated.
4. **Visual signature** — render the reference preset, attach the
PDF to the PR description, eyeball-validate it matches the
intent.
-5. **No engine / v1 edits** — additive only.
+5. **No engine edits** — additive only.
Expected size: ~1500-2500 lines of new code for a fresh family.
Compare to cv baseline (PR #45) which was 2082 lines including
diff --git a/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java b/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java
index 6385003c..85b0e5c3 100644
--- a/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java
+++ b/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java
@@ -45,7 +45,7 @@
* GraphCompose source tree calls.
*
* See {@code docs/recipes/streaming.md} and the existing
- * {@code BuiltInTemplateLayoutSnapshotTest} for the production
+ * {@code ChartLayoutSnapshotTest} for the production
* pattern.
*
* @author Artem Demchyshyn