@hegemonart/get-design-done 1.34.2 → 1.34.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "Get Design Done — 5-stage agent-orchestrated design pipeline with 9 connections, handoff-first workflow, bidirectional Figma write-back, 22+ specialized agents, queryable knowledge layer (intel store, dependency analysis, learnings extraction), and a self-improvement loop (reflector, frontmatter + budget feedback, global-skills layer). v1.20.0 ships the SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream, and resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) for rate-limit + 429 + context-overflow recovery. Full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows) and release automation (auto-tag + GitHub Release + release-time smoke test).",
8
- "version": "1.34.2"
8
+ "version": "1.34.3"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "get-design-done",
13
13
  "source": "./",
14
14
  "description": "Agent-orchestrated 5-stage design pipeline: Brief → Explore → Plan → Design → Verify. 22+ specialized agents, 9 connections (Figma, Refero, Preview, Storybook, Chromatic, Figma Writer, Graphify, Pinterest, Claude Design), Claude Design handoff, bidirectional Figma write-back, and a queryable intel store (.design/intel/) for dependency and learnings queries. Standalone commands: style, darkmode, compare, figma-write, graphify, handoff, analyze-dependencies, skill-manifest, extract-learnings. Embeds NNG heuristics, WCAG thresholds, typographic systems, motion framework, and anti-pattern catalog. Ships with a full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows) and release automation. Optimization layer (v1.0.4.1, retroactive): gdd-router + gdd-cache-manager skills, PreToolUse budget-enforcer hook, tier-aware agent frontmatter, lazy checker gates, streaming synthesizer, /gdd:warm-cache + /gdd:optimize commands, and cost telemetry at .design/telemetry/costs.jsonl — targeting 50-70% per-task token-cost reduction with no quality-floor regression. v1.20.0 SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream at .design/telemetry/events.jsonl, resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) with rate-limit + 429 + context-overflow recovery, and TypeScript toolchain.",
15
- "version": "1.34.2",
15
+ "version": "1.34.3",
16
16
  "author": {
17
17
  "name": "hegemonart"
18
18
  },
@@ -75,7 +75,9 @@
75
75
  "swift",
76
76
  "compose",
77
77
  "flutter",
78
- "email"
78
+ "email",
79
+ "print",
80
+ "pdf"
79
81
  ]
80
82
  }
81
83
  ]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "get-design-done",
3
3
  "short_name": "gdd",
4
- "version": "1.34.2",
4
+ "version": "1.34.3",
5
5
  "description": "Agent-orchestrated 5-stage design pipeline: Brief → Explore → Plan → Design → Verify. 22+ specialized agents, 9 connections (Figma, Refero, Preview, Storybook, Chromatic, Figma Writer, Graphify, Pinterest, Claude Design), handoff-first workflow via Claude Design bundles, bidirectional Figma write-back (annotations, Code Connect), queryable intel store (`.design/intel/`) for O(1) design surface lookups, and self-improvement loop (reflector agent, frontmatter + budget feedback, global-skills layer at `~/.claude/gdd/global-skills/`). Standalone commands: style, darkmode, compare, figma-write, graphify, handoff, analyze-dependencies, skill-manifest, extract-learnings, reflect, apply-reflections. Embeds NNG heuristics, WCAG thresholds, typographic systems, motion framework, and anti-pattern catalog. Ships with a full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows, lint + schema + frontmatter + stale-ref + shellcheck + gitleaks + injection-scan + blocking size-budget) and release automation (auto-tag + GitHub Release + release-time smoke test). Optimization layer (v1.0.4.1, retroactive): gdd-router + gdd-cache-manager skills, PreToolUse budget-enforcer hook, tier-aware agent frontmatter, lazy checker gates, streaming synthesizer, /gdd:warm-cache + /gdd:optimize commands, and cost telemetry at .design/telemetry/costs.jsonl — targeting 50-70% per-task token-cost reduction with no quality-floor regression. v1.20.0 SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream at .design/telemetry/events.jsonl, resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) with rate-limit + 429 + context-overflow recovery, and TypeScript toolchain. v1.27.7 ships gdd-mcp (Phase 27.7): 12 read-only MCP tools for sub-3s priming. v1.28.0 (Phase 28): Foundational References Tier 2 — 5 new reference files (color-theory, composition, proportion-systems, i18n, contrast-advanced), 2 verifier i18n probes + 1 explore i18n-readiness probe, 12 additive cross-link insertions across 10 existing references, 2 orthogonal audit-scoring lens-tags (composition_alignment + i18n_readiness).",
6
6
  "author": {
7
7
  "name": "hegemonart",
@@ -69,7 +69,9 @@
69
69
  "swift",
70
70
  "compose",
71
71
  "flutter",
72
- "email"
72
+ "email",
73
+ "print",
74
+ "pdf"
73
75
  ],
74
76
  "skills": [
75
77
  "./skills/"
package/CHANGELOG.md CHANGED
@@ -4,6 +4,28 @@ All notable changes to get-design-done are documented here. Versions follow [sem
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.34.3] - 2026-05-31
8
+
9
+ ### Phase 34.3 — Non-Web Output Layer: Print/PDF
10
+
11
+ Third and **FINAL** sub-phase of the split Phase 34 (Non-Web Output Layer) — **completing it completes the parent Phase 34** (native 34.1 + email 34.2 + print 34.3 all shipped). Adds **print/PDF output** — a dedicated executor that generates print-ready HTML/CSS honoring the real production constraints (`@page` box model, bleed + crop marks, CMYK color-space awareness, font embedding, 300dpi raster fallback) that screen-RGB web HTML ignores — behind the same project-type detector that routes native (34.1) and email (34.2). Print generation is **opt-in via project-type detection — web remains the default**. A decimal release on the v1.34.x arc (CHANGELOG-only, D-01); **no new runtime dependency** — the executor generates the print HTML/CSS as an agent-prompt and the plugin checks it with a deterministic static validator, no `pdfkit`/`paged`/`puppeteer`/`playwright` runtime (D-02/D-10). Print is the final Phase-34 output type — the project-type seam is now **closed**. 4 plans across Waves A–C.
12
+
13
+ ### Added
14
+
15
+ - **Print-constraint catalogue + static validator (no `pdfkit`/`paged` dependency, D-02/D-03).** `reference/print-design.md` — the authoritative print-constraint catalogue (`@page` size/margin/marks — the print box model, bleed box + crop/registration marks, CMYK color-space awareness, font embedding/outlining, 300dpi raster fallback, and the on-press guidance the render-test verifies), registered in `reference/registry.json`. `scripts/lib/print/validate-print-css.cjs` — a pure, deterministic static checker (zero `require`, no fs/network/pdfkit/paged) that flags the statically-verifiable subset: `PR-PAGE-01` (an `@page` rule present), `PR-BLEED-01` (a bleed box / crop-marks signal), `PR-CMYK-01` (a CMYK-awareness signal), `PR-FONT-01` (a font-embed signal), `PR-DPI-01` (a 300dpi raster-fallback signal) — returning `{ ok, violations:[{ rule, detail }] }`.
16
+ - **`pdf-executor` agent (Paged.js-compatible HTML/CSS + PDFKit fallback, D-02/D-04).** `agents/pdf-executor.md` — generates **print-ready output per task**: Paged.js-compatible print HTML/CSS (`@page` size/margin/marks, bleed, CMYK-aware color notes, font embedding, 300dpi raster fallback) against the `reference/print-design.md` catalogue, with PDFKit-fallback notes for Chrome-less runtimes, run through the static validator as its own self-check. It is an agent-prompt (like `design-executor`/`email-executor`), **not** a compiler — no running headless Chrome, no PDFKit, no network is required to produce the print HTML/CSS. Carries a `## Record` section from the start (the record-contract lesson) and an honest `size_budget`.
17
+ - **`print-renderer` connection (optional render-test, degrade-to-static-validator, D-03).** `connections/print-renderer.md` — Paged.js-via-headless-Chrome (or PDFKit on Chrome-less runtimes) rendered PDF/page evidence for the verify stage when present, mirroring `connections/preview.md`. **Never hard-required**: when absent the verify stage degrades to the static print-CSS validator / code-only structural audit. Added to the `connections/connections.md` index + Capability Matrix in this closeout.
18
+ - **`design-context-builder` `print` project-type route (seam CLOSED) + `design-verifier` consolidated non-web verify (D-06/D-07).** The context-builder appends the `print` enum + the `pdf-executor` route at the 34.1/34.2 seam and **closes the seam** — print is the final Phase-34 output type. The verifier gains the print-verify branch by **consolidating** the native + email + print non-web verify branches into ONE delegated "Non-Web Verify" section (routing by `<project_type>` to native-platforms.md / email-design.md / print-design.md + the matching validator) — a net line-reduction that keeps the verifier within its ≤700-line budget.
19
+ - **Regression baseline.** `test/fixtures/baselines/phase-34-3/` freezes the print surface — a valid print fixture (`print-good.css`), a validator golden (`validator-golden.json`, the recorded `validatePrintCss` output for a passing + a failing fixture, proving the rule-output shape is frozen), and `manifests-version.txt`=1.34.3 — pinned by `test/suite/phase-34-3-baseline.test.cjs` so a future change cannot silently break the validator or its verdict shape.
20
+
21
+ ### Notes
22
+
23
+ - All Phase 34.3 tests are hermetic (D-10): the static print-CSS validator is a pure string→verdict function (fixture CSS → constraint checks, no network/headless-Chrome/PDFKit), the pdf-executor is validated **structurally** (frontmatter + catalogue reference + validator reference + presence), and the default `npm test` invokes **no** headless Chrome / PDFKit and pulls in **no** `pdfkit`/`paged`/`puppeteer`/`playwright` runtime. Rendered PDF/page verification is the opt-in degraded-mode path (D-03).
24
+ - The 31.5 tarball golden (`test/fixtures/baselines/phase-31-5/tarball-manifest.txt`) was regenerated as a reviewed delta: **+4** newly-shipped files (`agents/pdf-executor.md`, `connections/print-renderer.md`, `reference/print-design.md`, `scripts/lib/print/validate-print-css.cjs`), zero removals. Tests are not shipped.
25
+ - 6-manifest lockstep at **v1.34.3** (`package.json` + `package-lock.json` (root + `packages.""`) + `.claude-plugin/plugin.json` + `.claude-plugin/marketplace.json` (metadata.version + plugins[0].version) + `.cursor-plugin/plugin.json` + `.codex-plugin/plugin.json`); marketplace `plugins[0].keywords` + plugin keywords gain `print`/`pdf`. Version-sync hygiene done upfront (D-09): `OFF_CADENCE_VERSIONS.add('1.34.3')` + the 17 live-pinned `manifests-version.txt` baselines forward-propagated 1.34.2 → 1.34.3 (phase-34-2, the prior closeout's own baseline, joined the set). **This completes the parent Phase 34 (Non-Web Output Layer — native + email + print).**
26
+
27
+ ---
28
+
7
29
  ## [1.34.2] - 2026-05-31
8
30
 
9
31
  ### Phase 34.2 — Non-Web Output Layer: Email
package/README.md CHANGED
@@ -122,6 +122,14 @@ GDD also generates **email templates** — the project-type detector routes an `
122
122
 
123
123
  The constraints live in [`reference/email-design.md`](reference/email-design.md) — table-based layout (no flexbox/grid/`position`), inline styles (no `<style>` sheet in Gmail), MSO conditional comments for Outlook's Word engine, dark-mode `color-scheme` handling, ~600px width, and the top-20-client quirks. A deterministic static checker ([`scripts/lib/email/validate-email-html.cjs`](scripts/lib/email/validate-email-html.cjs)) flags the statically-verifiable subset (`EM-LAYOUT`/`EM-STYLE`/`EM-MSO`/`EM-DARK`). Cross-client rendered verification via [`Litmus`](connections/litmus.md) (or Email-on-Acid) is **optional** — when absent the verify stage degrades to the static validator. Email generation is opt-in; **web stays the default**.
124
124
 
125
+ ### Print/PDF output (v1.34.3)
126
+
127
+ GDD also generates **print/PDF** output — the project-type detector routes a `print` brief to a dedicated executor that honors the production constraints screen-RGB web HTML ignores. This **completes the Non-Web Output Layer** (native + email + print all shipped):
128
+
129
+ - **[`pdf-executor`](agents/pdf-executor.md)** — generates **Paged.js-compatible print HTML/CSS** (with PDFKit-fallback notes for Chrome-less runtimes) against the constraint catalogue, with the static validator as its own self-check. It is an agent-prompt, not a compiler — **no headless Chrome, no PDFKit, no network** is needed to produce the print HTML/CSS.
130
+
131
+ The constraints live in [`reference/print-design.md`](reference/print-design.md) — the `@page` box model (size/margin/marks), bleed box + crop/registration marks, CMYK color-space awareness (print is subtractive CMYK, not screen RGB), font embedding/outlining (print RIPs have no web fonts), and a 300dpi raster fallback. A deterministic static checker ([`scripts/lib/print/validate-print-css.cjs`](scripts/lib/print/validate-print-css.cjs)) flags the statically-verifiable subset (`PR-PAGE`/`PR-BLEED`/`PR-CMYK`/`PR-FONT`/`PR-DPI`). Rendered PDF/page verification via the optional [`print-renderer`](connections/print-renderer.md) connection (Paged.js/headless-Chrome or PDFKit) is **optional** — when absent the verify stage degrades to the static validator. Print generation is opt-in; **web stays the default**.
132
+
125
133
  ### Previous releases
126
134
 
127
135
  - **v1.26.0** — Headless Model Resolver (per-runtime tier→model map, `resolved_models` router field, per-runtime price tables, `reasoning-class` runtime-neutral alias).
@@ -218,19 +218,20 @@ Proceed to Step 0E regardless of whether Step 0D ran or was skipped.
218
218
 
219
219
  Detect the **project type** so the pipeline routes the brief to the correct executor. Reuse the Step 0C / Step 1 grep/glob idiom (file reads only, < 1 second, no skip condition).
220
220
 
221
- **Enum (5 values — D-06):** `web` (DEFAULT) · `native-ios` · `native-android` · `flutter` · `email`.
221
+ **Enum (6 values — D-06, the full set):** `web` (DEFAULT) · `native-ios` · `native-android` · `flutter` · `email` · `print`.
222
222
 
223
- **Detection signals + precedence** (first match wins; brief overrides — if the user explicitly says "iOS app" / "Android app" / "Flutter app" / "email" / "newsletter" / "email template", honor that):
223
+ **Detection signals + precedence** (first match wins; brief overrides — if the user explicitly says "iOS app" / "Android app" / "Flutter app" / "email" / "newsletter" / "email template" / "print" / "PDF" / "print-ready" / "brochure" / "flyer" / "poster", honor that):
224
224
 
225
225
  ```bash
226
226
  ls pubspec.yaml 2>/dev/null # → flutter
227
227
  ls *.xcodeproj Package.swift 2>/dev/null # → native-ios (when no pubspec)
228
228
  ls build.gradle build.gradle.kts settings.gradle 2>/dev/null # → native-android (when no pubspec)
229
229
  ls **/*.mjml email/ emails/ templates/email 2>/dev/null # → email (email-template signals)
230
+ ls **/*.print.css print/ templates/print 2>/dev/null # → print (print-output signals: a print stylesheet / print-template dir)
230
231
  ls package.json 2>/dev/null # → web (default; also the fallback when none match)
231
232
  ```
232
233
 
233
- Precedence: an explicit brief override (the user says "email" / "newsletter" / "email template") wins like the other brief-overrides; otherwise `pubspec.yaml` (flutter) > `*.xcodeproj`/`Package.swift` (native-ios) > `build.gradle*`/`settings.gradle` (native-android) > `.mjml` files / an `email/` templates directory (email) > `package.json` / none (web — DEFAULT).
234
+ Precedence: an explicit brief override (the user says "email" / "newsletter" / "email template", or "print" / "PDF" / "print-ready" / "brochure" / "flyer" / "poster") wins like the other brief-overrides; otherwise `pubspec.yaml` (flutter) > `*.xcodeproj`/`Package.swift` (native-ios) > `build.gradle*`/`settings.gradle` (native-android) > `.mjml` files / an `email/` templates directory (email) > a print stylesheet (`*.print.css`) / a `print/` templates directory (print) > `package.json` / none (web — DEFAULT).
234
235
 
235
236
  **Routing table** (project type → executor — one row per type, trivially appendable):
236
237
 
@@ -241,10 +242,11 @@ Precedence: an explicit brief override (the user says "email" / "newsletter" / "
241
242
  | native-android | compose-executor |
242
243
  | flutter | flutter-executor |
243
244
  | email | email-executor |
245
+ | print | pdf-executor |
244
246
 
245
- <!-- 34.2 added `email` (above) email-executor; 34.3 (print) appends its project type + executor row HERE this enum and routing table stay intentionally OPEN and extensible (D-06). 34.2 adds email ONLY; do NOT add a print row until 34.3. -->
247
+ <!-- Phase 34 output types complete: native (34.1: native-ios/native-android/flutter) + email (34.2) + print (34.3). Print is the FINAL Phase-34 output type no further Phase-34 output types; the enum + routing table above are the full set. (34.1/34.2 kept this seam OPEN for the next type; 34.3 ties it off.) -->
246
248
 
247
- Record the detected type in DESIGN-CONTEXT.md as a `<project_type>` line (e.g. `<project_type>native-ios</project_type>`) so downstream stages route correctly. The native specifics (token→theme bridge) live in `reference/native-platforms.md`; the email specifics (table layout, inline styles, MSO/dark-mode constraints) live in `reference/email-design.md` — do not inline either here.
249
+ Record the detected type in DESIGN-CONTEXT.md as a `<project_type>` line (e.g. `<project_type>native-ios</project_type>`) so downstream stages route correctly. The native specifics (token→theme bridge) live in `reference/native-platforms.md`; the email specifics (table layout, inline styles, MSO/dark-mode constraints) live in `reference/email-design.md`; the print specifics (the `@page` box model, bleed/crop marks, CMYK awareness, font embedding, 300dpi raster) live in `reference/print-design.md` — do not inline any of them here.
248
250
 
249
251
  Proceed to Step 1 regardless of outcome.
250
252
 
@@ -354,21 +354,17 @@ In DESIGN-VERIFICATION.md, add a `## Phase 4B — Screenshot Evidence` section l
354
354
 
355
355
  ---
356
356
 
357
- ## Phase 4D — Native Verify (no-DOM targets)
357
+ ## Phase 4D — Non-Web Verify (no-DOM targets)
358
358
 
359
- When `<project_type>` in DESIGN-CONTEXT.md is `native-ios` / `native-android` / `flutter` (no browser DOM), the Phase-1 DOM grep audit + the Phase-4B Preview loop do not apply as-is. Run instead:
359
+ When `<project_type>` is a **no-DOM target** `native-ios`/`native-android`/`flutter`, `email`, or `print` the Phase-1 web DOM grep + the Phase-4B Preview loop do not apply as-is. Route by `<project_type>` to the matching constraint/structural audit **by delegation** (the per-type rules live in the reference, never inlined here), with the optional render-connection as a degrade-able enhancement — the Phase-4B precedent:
360
360
 
361
- - **Snapshot audit** IF a simulator/emulator screenshot is supplied (via `connections/xcode-simulator.md`, `connections/android-emulator.md`, or Preview for Flutter-web all OPTIONAL): reuse the Phase-4B screenshot-evidence machinery against the supplied image.
362
- - **Code-only structural audit** (DEFAULT — no screenshot/simulator): verify the generated native source structurally — expected SwiftUI views / Compose composables / Flutter widgets present + token-bridge usage — instead of rendered pixels. What "structurally valid" means per platform lives in `reference/native-platforms.md` (do not inline it). Like Phase 4B, the simulator/emulator is an **enhancement, not a requirement** — this branch NEVER hard-requires one; it degrades to the code-only audit and raises no blocker unless a must_have explicitly demands rendered evidence.
363
-
364
- ---
365
-
366
- ## Phase 4E — Email Verify (project_type: email)
367
-
368
- When `<project_type>` is `email` (no browser DOM — the Phase-1 DOM grep + Phase-4B Preview loop do not apply), run an email-constraint audit BY DELEGATION (the ~30 constraints live in `reference/email-design.md` — the authority; do NOT inline them):
361
+ | `<project_type>` | reference (authority) + static audit | optional render-connection (degrade if absent) |
362
+ |---|---|---|
363
+ | `native-ios` / `native-android` / `flutter` | `reference/native-platforms.md` — **code-only structural audit**: expected SwiftUI views / Compose composables / Flutter widgets present + token-bridge usage (a snapshot audit when a screenshot is supplied) | `xcode-simulator` / `android-emulator` / Preview (Flutter-web) → degrade to the code-only structural audit |
364
+ | `email` | `reference/email-design.md` + `scripts/lib/email/validate-email-html.cjs` (`validateEmailHtml`) over the generated HTML — table layout / inline styles / MSO comments / dark-mode `color-scheme` | `connections/litmus.md` cross-client screenshots → degrade to the static validator / code-only |
365
+ | `print` | `reference/print-design.md` + `scripts/lib/print/validate-print-css.cjs` (`validatePrintCss`) over the print CSS/HTML — `@page` box, bleed/crop marks, CMYK awareness, font embedding, 300dpi | `connections/print-renderer.md` (Paged.js-headless / PDFKit render) → degrade to the static validator / code-only |
369
366
 
370
- - **Static constraint audit** (DEFAULT) run `scripts/lib/email/validate-email-html.cjs` (`validateEmailHtml`) over the generated email HTML and report its violations (table layout / inline styles / MSO conditional comments / dark-mode `color-scheme`).
371
- - **Rendered enhancement** (OPTIONAL) — IF the Litmus connection (`connections/litmus.md`) is available, reuse the Phase-4B screenshot-evidence machinery against its cross-client screenshots; when absent, DEGRADE to the static validator / code-only. Litmus is an **enhancement, never hard-required** (D-03 — the Phase-4D precedent); raise no blocker for its absence.
367
+ **Degrade posture (D-03, the Phase-4B precedent — applies to every row):** the render-connection (simulator/emulator/Litmus/print-render) is an **enhancement, NEVER hard-required**. When it is absent, run the default code-only/static audit for that type and raise **no blocker** for the missing render unless a must_have explicitly demands rendered evidence. Each reference owns its own constraint detail; this section is a pure router.
372
368
 
373
369
  ---
374
370
 
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: pdf-executor
3
+ description: Executes one plan task by generating print-ready output — Paged.js-compatible print HTML/CSS (@page/bleed/CMYK-aware/font-embed/300dpi) with PDFKit-fallback notes — honoring reference/print-design.md constraints, validated by the static print-CSS checker. Single-shot; mirrors design-executor.
4
+ tools: Read, Write, Edit, Bash, Grep, Glob
5
+ color: pink
6
+ default-tier: sonnet
7
+ tier-rationale: "Follows an Opus-authored plan; executes print codegen rather than plans it"
8
+ size_budget: M
9
+ size_budget_rationale: "Honest tier sized to the actual ~150-line body (M cap 300), NOT inflated to the design-family XXL default. Print carries a single-artifact generation contract (Paged.js-compatible print HTML/CSS, D-02) plus a five-class static-validator self-check (PR-PAGE/BLEED/CMYK/FONT/DPI) and an optional render-test posture — comparable to the email-executor (also M) for a single-target lean executor body. The @page box model, 3mm-bleed/crop-mark, rich-black-vs-K100, font-embed, and 300dpi per-press/per-RIP detail is DELEGATED to reference/print-design.md (the catalogue), keeping the body well under M; only the generation + validation + degrade contract is stated here. Raise to LARGE only if the per-press surface is ever inlined here instead of the catalogue."
10
+ parallel-safe: conditional-on-touches
11
+ typical-duration-seconds: 60
12
+ reads-only: false
13
+ writes:
14
+ - "**/*.html"
15
+ - "**/*.css"
16
+ ---
17
+
18
+ @reference/shared-preamble.md
19
+
20
+ # pdf-executor
21
+
22
+ ## Role
23
+
24
+ You execute **exactly one task** from the plan: you generate **one print-ready document** — **Paged.js-compatible print HTML/CSS** (the `@page` box model, bleed, CMYK-aware color, embedded fonts, a 300dpi raster fallback) — honoring the print-production constraints. Your scope is a single task — you do not re-plan, coordinate waves, spawn other agents, or ask clarifying questions. The stage handles dispatch; you handle one task completely and correctly.
25
+
26
+ You are a single-shot agent: receive context, read the references, generate the print HTML/CSS, write the file(s), run the static validator, commit, emit the completion marker, done.
27
+
28
+ You are an **agent-prompt**, not a compiler (D-04): GDD generates the print document when an LLM (you) invokes this prompt, consistent with `design-executor.md` / `email-executor.md` / `flutter-executor.md`. You do **not** require a running headless Chrome, a Paged.js runtime, PDFKit, or any network to produce the print HTML/CSS — rendered PDF verification is the verify stage's degraded-mode concern, never a precondition here (D-03/D-10).
29
+
30
+ ---
31
+
32
+ ## Required Reading
33
+
34
+ Read every file the stage lists in its `<required_reading>` block before taking any action. At minimum:
35
+
36
+ - `.design/STATE.md` — pipeline state (decisions, blockers, must-haves)
37
+ - `.design/DESIGN-PLAN.md` — your task is identified by `task_id`
38
+ - `.design/DESIGN-CONTEXT.md` — brand decisions, constraints, locked choices
39
+ - **`reference/print-design.md`** — the **authoritative** print-constraint catalogue: the `@page` box model (`size`/`margin`/`marks`), the bleed box + crop/registration marks (~3mm), CMYK awareness (subtractive ink, not screen RGB), font embedding (RIPs have no web fonts — `@font-face` with an embedded `src:` or outline-to-vector), and the 300dpi raster fallback (`image-resolution`/`min-resolution`). This is how you pick the correct print idiom — you **generate against the catalogue**, you do **not** re-derive these rules (the `email-executor`→`reference/email-design.md`, `flutter-executor`→`reference/native-platforms.md` precedent).
40
+
41
+ **Invariant:** read all listed files FIRST, before making any changes.
42
+
43
+ ---
44
+
45
+ ## Paged.js-compatible print HTML/CSS + PDFKit fallback (the D-02 generation contract)
46
+
47
+ Per **D-02** the executor's canonical output is **Paged.js-compatible print HTML/CSS**, and **you (the LLM) perform the generation as your contract** — there is **NO `pdfkit`/`paged`/`puppeteer`/`playwright` build step / runtime dependency** (an opt-in real Paged.js-via-headless-Chrome / PDFKit render is out of scope, like the simulator/Litmus connections):
48
+
49
+ - Generate a **Paged.js-compatible print stylesheet** as the canonical artifact: an `@page` rule (`size` A4/Letter or explicit physical `WIDTH HEIGHT`, `margin`, `marks: crop cross`), a `bleed:` declaration (~3mm), CMYK-aware color (a `cmyk()` value, an ICC `color-profile` reference, or an explicit CMYK-target note), `@font-face` font embedding (embedded `src:`), and a 300dpi raster fallback (`image-resolution: 300dpi`/`from-image` or a documented note) — per the catalogue.
50
+ - Document a **PDFKit-fallback construction path** for **Chrome-less runtimes** — a note on how PDFKit would build the same page box (`new PDFDocument({ size, margins })`), embed fonts (`doc.registerFont(...)`), and place the bleed when Paged.js-via-headless-Chrome is unavailable. This is documentation, not a dependency.
51
+ - State, in the file header comment and your output, that the **print HTML/CSS is the canonical artifact** and a **rendered PDF is the optional print-render connection's product** — never produced by a bundled build step.
52
+ - Do **not** add `pdfkit`/`paged`/`puppeteer`/`playwright` to `package.json` or shell out to them — the generation is your job.
53
+
54
+ ---
55
+
56
+ ## Token Consumption — the canonical token form
57
+
58
+ Where the task themes the document (colors, spacing, type), consume the **canonical design tokens** (the css-vars token form) for those values rather than inventing ad-hoc hex/px — consistent with the design-family executors. Print is **subtractive CMYK**, so **resolve** the token color to a **print-safe literal** at generation time and **note CMYK awareness** (the token is the RGB source; the print output carries the CMYK-aware resolved value + the production-intent note per the catalogue). Prefer physical units (`mm`/`pt`) for page geometry. Keep color, type scale, and brand voice consistent with the rest of the design system.
59
+
60
+ ---
61
+
62
+ ## Self-check via the static validator (the deterministic gate)
63
+
64
+ Before completing, **run the static print-CSS validator** on your **generated print CSS/HTML**:
65
+
66
+ ```js
67
+ const { validatePrintCss } = require('scripts/lib/print/validate-print-css.cjs');
68
+ const { ok, violations } = validatePrintCss(cssOrHtmlString);
69
+ ```
70
+
71
+ `validatePrintCss` (Phase 34.3-01) deterministically checks the five statically-verifiable constraint classes — **PR-PAGE-01** (an `@page` rule present), **PR-BLEED-01** (a `bleed:`/`marks:` or documented crop-marks signal), **PR-CMYK-01** (a `cmyk()`/`color-profile`/CMYK-note signal), **PR-FONT-01** (an `@font-face` `src:` or font-embed/outline note), **PR-DPI-01** (an `image-resolution`/`min-resolution`/300dpi note). **Fix every flagged violation** before you finish — this is your deterministic self-check against the catalogue. The remaining catalogue rules (3mm bleed value, rich-black vs K100, overprint/knockout, trap/registration, true vector tessellation, effective ≥300dpi) are render-tested guidance, not statically asserted — honor them from the catalogue.
72
+
73
+ ---
74
+
75
+ ## Optional print-render (degraded / not a precondition)
76
+
77
+ Code generation needs **no** render service (D-04/D-10). Rendered **PDF/page-proof** verification is the **verify stage's** degraded-mode concern (D-03): point it at the `connections/print-renderer.md` print-render connection **as an enhancement**, **never** a precondition.
78
+
79
+ - When the **print-render** (Paged.js via headless Chrome, or **PDFKit** for Chrome-less runtimes) is available → the verify stage captures a paginated PDF/page proof.
80
+ - When **absent** → verification **degrades** to the static validator above, then a code-only structural audit. Never hard-require the print-render.
81
+
82
+ Print ships its render-test connection at `connections/print-renderer.md`; you only **name** it — you never run it to generate.
83
+
84
+ ---
85
+
86
+ ## Execution Principles
87
+
88
+ 1. **Honor DESIGN-CONTEXT.md decisions as locked.** `D-XX:` decisions are non-negotiable.
89
+ 2. **`reference/print-design.md` is authoritative** for the print constraints. Apply its rules directly; do not paste them wholesale and do not re-derive them.
90
+ 3. **Observable outcomes only.** Acceptance criteria describe observable states ("the CSS declares an `@page` rule with `marks: crop`", "an `@font-face` with an embedded `src:` is present", "validatePrintCss returns `ok: true`").
91
+ 4. **Decision authority:** in-context choices → proceed; out-of-context (architectural, contradicts a locked D-XX, changes external API) → Rule 4: STOP, write a blocker, mark the task `status: deviation`, still emit the marker.
92
+ 5. **Single-task scope.** Do not modify the plan, the context file, the connection index, or any file outside the task's `Touches:`/`writes` list (unless a deviation fix requires it — document it).
93
+
94
+ ---
95
+
96
+ ## Deviation Rules
97
+
98
+ Apply automatically; track each in the task output `## Deviations` section.
99
+
100
+ - **Rule 1 — Bug:** broken print HTML/CSS, a flagged `validatePrintCss` violation, wrong token resolution in files you author → fix inline.
101
+ - **Rule 2 — Missing Critical:** a missing `@page` rule, no bleed/crop-marks signal, pure screen-RGB with no CMYK awareness, a bare system-font-stack with no embed, no 300dpi signal → add it (the catalogue requires it).
102
+ - **Rule 3 — Blocking:** a referenced file/import missing, the validator not resolvable → fix (resolve import, create stub) and note it.
103
+ - **Rule 4 — Architectural:** switching the print engine, restructuring the document system, a schema-level change, or anything contradicting a locked D-XX → STOP, write a `<blocker>`, mark `status: deviation`, still emit the marker.
104
+
105
+ **Scope boundary:** only fix issues directly caused by this task's changes. **Fix attempt limit:** stop after 3 attempts on one issue; document the remainder and continue to commit.
106
+
107
+ ---
108
+
109
+ ## Output
110
+
111
+ Emit the **Paged.js-compatible print HTML/CSS** to the path(s) the task declares. In your final response, state: the file(s) written, the page geometry chosen (`@page size`/`margin`/`bleed`/`marks`), how tokens were resolved to print-safe CMYK-aware values, the PDFKit-fallback note, and the `validatePrintCss` result (`ok: true` / the violations you fixed). Write the task record per the design-family output contract and make an atomic commit (stage files individually — never `git add .`/`-A`; never run `git clean`).
112
+
113
+ Terminate with exactly this line, on its own line:
114
+
115
+ ```
116
+ ## EXECUTION COMPLETE
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Constraints
122
+
123
+ This agent MUST NOT:
124
+
125
+ - Run `git clean` (any flags) — absolute prohibition.
126
+ - Require a running headless Chrome, a Paged.js runtime, PDFKit, or any network to generate the print HTML/CSS (D-04/D-10).
127
+ - Add a `pdfkit`/`paged`/`puppeteer`/`playwright` dependency to `package.json` or shell out to them — the generation is the agent's contract (D-02).
128
+ - Re-derive the print constraints — consume `reference/print-design.md` (the catalogue).
129
+ - Emit screen-only RGB HTML with no `@page`/bleed/CMYK/font-embed/300dpi print semantics — the print contract is the deliverable.
130
+ - Create or edit the connection index, or modify the plan or context file, re-plan, spawn other agents, ask clarifying questions, or `git add .`/`-A`.
131
+
132
+ ---
133
+
134
+ ## Record
135
+
136
+ At run-end, append one JSONL line to `.design/intel/insights.jsonl`:
137
+
138
+ ```json
139
+ {"ts":"<ISO-8601>","agent":"pdf-executor","cycle":"<cycle from STATE.md>","stage":"<stage from STATE.md>","one_line_insight":"<which print document (Paged.js-compatible HTML/CSS) was generated + the validatePrintCss result>","artifacts_written":["<files written>"]}
140
+ ```
141
+
142
+ Schema: `reference/schemas/insight-line.schema.json`.
143
+
144
+ ## EXECUTION COMPLETE
@@ -27,6 +27,7 @@ This directory contains connection specifications for external tools and MCPs th
27
27
  | Xcode Simulator | Active | [`connections/xcode-simulator.md`](connections/xcode-simulator.md) | **Optional** (macOS-only); CLI: `simctl` (no MCP); native-iOS rendered evidence for verify; degrade-to-code-only when absent (D-03) |
28
28
  | Android Emulator | Active | [`connections/android-emulator.md`](connections/android-emulator.md) | **Optional**; CLI: `adb` / `emulator` (no MCP); native-Android rendered evidence for verify; degrade-to-code-only when absent (D-03) |
29
29
  | Litmus | Active | [`connections/litmus.md`](connections/litmus.md) | **Optional** render-test (email; Email-on-Acid alternative); cross-client rendered screenshots for verify; degrade-to-static-validator / code-only when absent (D-03) |
30
+ | Print-Renderer | Active | [`connections/print-renderer.md`](connections/print-renderer.md) | **Optional** print render-test (Paged.js/headless-Chrome or PDFKit); rendered PDF/page proof for verify; degrade-to-static-validator (validate-print-css.cjs) / code-only when absent (D-03) |
30
31
 
31
32
  ---
32
33
 
@@ -53,6 +54,7 @@ Each cell describes what the connection contributes at that pipeline stage, or `
53
54
  | Xcode Simulator | — | — | — | native iOS code-gen target (swift-executor / emitSwift) | rendered SwiftUI snapshot when simulator available, else degrade to code-only structural audit (D-03) | — | — |
54
55
  | Android Emulator | — | — | — | native Android code-gen target (compose-executor / emitCompose) | rendered Compose screenshot when emulator available, else degrade to code-only structural audit (D-03) | — | — |
55
56
  | Litmus | — | — | — | email render-test target (email-executor) | cross-client rendered evidence when Litmus available, else degrade to the static email-HTML validator / code-only (D-03) | — | — |
57
+ | Print-Renderer | — | — | — | print render-test target (pdf-executor) | rendered PDF/page evidence when the print-render is available, else degrade to the static print-CSS validator / code-only (D-03) | — | — |
56
58
 
57
59
  **Column definitions:**
58
60
 
@@ -0,0 +1,113 @@
1
+ # Print-Renderer — Connection Specification
2
+
3
+ This file is the connection specification for the print-render within the get-design-done pipeline. It lives in `connections/` alongside the other connection specs (the closest analog is `connections/preview.md`, the headless-Chrome visual-truth connection). See the connection index for the full connection capability matrix (the print-renderer row is added at the 34.3 closeout).
4
+
5
+ ---
6
+
7
+ The print-render is the **verify stage's RENDERED proof for the `print` project type**. It renders the `pdf-executor`'s **Paged.js-compatible print HTML/CSS** to a **paginated PDF / page image** — the print analog of `preview.md`'s browser screenshots. Its pipeline role: after the `pdf-executor` generates the print HTML/CSS (validated by the static checker), the verify stage uses the print-render — **when available** — to surface print breakage the static validator cannot see (actual pagination, bleed placement, font embedding in the PDF, raster resolution), and narrates the result in plain English.
8
+
9
+ **Key relationship to the static validator:** the print-render is the *rendered* complement to the *static* checker `scripts/lib/print/validate-print-css.cjs`. The static validator deterministically checks constraint **classes** (`@page` present, bleed/crop-marks signal, CMYK awareness, font-embed, 300dpi — `PR-PAGE/BLEED/CMYK/FONT/DPI`); the print-render checks the **rendered output** — what actually paginates and rasterizes. The print-render is **optional** — its absence is a quality reduction, not a blocking error (D-03).
10
+
11
+ ---
12
+
13
+ ## Setup
14
+
15
+ **Prerequisites:**
16
+
17
+ - **PRIMARY — Paged.js via headless Chrome:** the same headless-Chrome family `preview.md` uses. Paged.js consumes the `@page`/bleed print CSS and paginates in headless Chrome, producing a paginated PDF. No bundled dependency is required to *probe*; the render is opt-in — like the Preview MCP, prefer a built-in / no-external-package path where possible, and where a tool IS needed it is opt-in (never installed by the pipeline).
18
+ - **ALTERNATIVE — PDFKit for Chrome-less runtimes:** where no headless Chrome is available, PDFKit constructs the page box programmatically (`new PDFDocument({ size, margins })`), embeds fonts (`doc.registerFont(...)`), and places the bleed. This is the documented Chrome-less render path.
19
+
20
+ Per **D-02 / D-10** there is **NO bundled `pdfkit`/`paged`/`puppeteer`/`playwright` dependency** and **no live render in the default `npm test`** — the print-render is an optional, opt-in enhancement the maintainer wires up in the verify stage when a renderer is present.
21
+
22
+ **Verification:**
23
+
24
+ ```bash
25
+ command -v chromium 2>/dev/null || command -v chrome 2>/dev/null || command -v node 2>/dev/null
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Why the print-render is useful
31
+
32
+ Print rendering breakage is invisible to both code review and the static validator. A print stylesheet can pass every static class-check (`validatePrintCss` returns `ok: true`) and still render broken: a table splits badly across a page boundary because a `break-inside: avoid` was missing, the bleed box does not actually extend past the trim, an `@font-face` fails to embed and the RIP substitutes a metrically-different face (reflowing the layout), a raster image that *claimed* 300dpi is upscaled and pixelates.
33
+
34
+ The static validator checks constraint **classes**; it cannot render. The print-render renders the **actual** document to a paginated PDF / page image, so pagination, bleed placement, font embedding, and raster resolution surface as visible output rather than a press reject weeks later.
35
+
36
+ ---
37
+
38
+ ## When to use the print-render
39
+
40
+ **Verify stage:** After the `pdf-executor` generates the print HTML/CSS, run the print-render — when available — to capture a paginated PDF / page proof and check for rendered breakage. The verify stage narrates the delta and notes it in `DESIGN-VERIFICATION.md`.
41
+
42
+ The print-render is **not** used at generation time. The `pdf-executor` needs no headless Chrome, no Paged.js runtime, no PDFKit, and no network to produce the print HTML/CSS — generation is gated by the static validator only (D-04/D-10).
43
+
44
+ ---
45
+
46
+ ## Availability Probe
47
+
48
+ The print-render is consumed via a CLI/binary presence check (headless Chrome / PDFKit), not ToolSearch.
49
+
50
+ **Step PR1 — renderer presence:**
51
+
52
+ ```bash
53
+ command -v chromium 2>/dev/null || command -v chrome 2>/dev/null
54
+ ```
55
+
56
+ - A headless-Chrome binary (for Paged.js) OR a PDFKit-capable runtime is found → proceed to Step PR2
57
+ - Neither → `print-renderer: not_configured` (skip all print-render steps)
58
+
59
+ **Step PR2 — render capability check:**
60
+
61
+ - A renderer is present and invokable → `print-renderer: available`
62
+ - Present but not invokable (missing flag, sandbox error) → `print-renderer: unavailable`
63
+
64
+ **Write the print-renderer status to `.design/STATE.md` `<connections>` immediately after probing** — the three-value schema `preview.md` uses (`available` / `unavailable` / `not_configured`).
65
+
66
+ ---
67
+
68
+ ## Fallback Behavior
69
+
70
+ When the print-render is `not_configured` or `unavailable`, the verify stage degrades gracefully — no error is raised.
71
+
72
+ **verify stage (`skills/verify/SKILL.md` + `agents/design-verifier.md`):**
73
+
74
+ - `print-renderer: unavailable` → skip the rendered PDF/page proof; **degrade** to the static validator `scripts/lib/print/validate-print-css.cjs` (the `PR-PAGE/BLEED/CMYK/FONT/DPI` class-checks), then a **code-only** structural audit of the print HTML/CSS; note in `DESIGN-VERIFICATION.md`: "Print render-test skipped — no headless Chrome / PDFKit; verified via the static print-CSS validator + a code-only structural audit."
75
+ - `print-renderer: not_configured` → same as unavailable; note: "Print render-test skipped — print-render not configured; verified via the static validator + a code-only audit. (Alternative: PDFKit on a Chrome-less runtime.)"
76
+
77
+ **Graceful degradation required:** the pipeline must continue when the print-render is unavailable. Missing rendered-PDF data is a **quality reduction, not a blocking error** (D-03 — the print-render is an enhancement, never hard-required, mirroring the 34.1 simulator / 34.2 Litmus connections). The static validator is always available and is the deterministic floor; the print-render is the rendered ceiling on top of it. Neither stage appends a `<blocker>` for a missing print-render connection. If a `must_have` explicitly requires rendered-PDF evidence, THEN append a blocker.
78
+
79
+ ---
80
+
81
+ ## STATE.md Integration
82
+
83
+ Every stage that probes the print-render writes the result to `.design/STATE.md` under the `<connections>` section:
84
+
85
+ ```xml
86
+ <connections>
87
+ figma: available
88
+ preview: available
89
+ print-renderer: not_configured
90
+ </connections>
91
+ ```
92
+
93
+ **Status values:**
94
+
95
+ | Value | Meaning |
96
+ |---|---|
97
+ | `available` | A headless-Chrome (Paged.js) or PDFKit renderer is present AND invokable |
98
+ | `unavailable` | A renderer binary is present but the render call errored (missing flag, sandbox) |
99
+ | `not_configured` | No headless Chrome and no PDFKit runtime — the print-render is not set up |
100
+
101
+ The verify stage re-probes at stage entry (renderer availability can change between sessions). If STATE.md already carries a `print-renderer:` status from a prior stage in the SAME session, that status can be trusted for the rest of that session.
102
+
103
+ ---
104
+
105
+ ## Caveats and Pitfalls
106
+
107
+ 1. **The print-render is an enhancement, not a requirement.** Its absence degrades to the static validator + a code-only audit and never blocks the pipeline (D-03). Do not gate a print build on a rendered PDF.
108
+
109
+ 2. **Physical-unit / DPI gotchas.** The page is a physical object — `mm`/`pt`/`in` have fixed physical sizes, `px` does not across RIPs. A render at the wrong DPI (72/96 screen vs 300 print) misrepresents raster sharpness; render at the document's declared output resolution. Bleed (~3mm) and crop marks live in the trim waste — confirm the rendered PDF actually carries them.
110
+
111
+ 3. **No bundled render dependency.** Do NOT add `pdfkit`/`paged`/`puppeteer`/`playwright` to `package.json` to make this connection work — the render is opt-in and the maintainer supplies the renderer. The default `npm test` stays hermetic (D-10).
112
+
113
+ 4. **Do NOT edit the connection index here.** The print-renderer's Active-Connections entry and Capability-Matrix row are added by the 34.3 closeout plan, not by this spec (the 34.1/34.2 disjointness pattern).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hegemonart/get-design-done",
3
- "version": "1.34.2",
3
+ "version": "1.34.3",
4
4
  "description": "A design-quality pipeline for AI coding agents: brief, plan, implement, and verify UI work against your design system.",
5
5
  "author": "Hegemon",
6
6
  "homepage": "https://github.com/hegemonart/get-design-done",
@@ -0,0 +1,223 @@
1
+ # Print Design — Constraint Catalogue
2
+
3
+ This reference is the **print/PDF constraint catalogue**: the hard print-production rules a
4
+ print-ready document MUST honor. Print is a *constrained* output surface — the screen-RGB
5
+ HTML/CSS the web executor emits has no `@page` box model, no bleed/crop marks, no CMYK
6
+ color space, no font embedding, and no 300dpi raster guidance, so it cannot be sent to a
7
+ press as-is. This file is the **authority** that the `pdf-executor` (Phase 34.3-02) generates
8
+ against and the design-verifier's print branch (34.3-03) audits against; neither re-derives
9
+ these rules. The deterministic subset of this catalogue is checked by
10
+ [`scripts/lib/print/validate-print-css.cjs`](../scripts/lib/print/validate-print-css.cjs),
11
+ whose emitted `rule` ids are the constraint-ids defined below (the spec is the authority).
12
+
13
+ It is the print sibling of the other non-web output references. The files have distinct jobs
14
+ and must not be confused:
15
+
16
+ | File | Job |
17
+ | --- | --- |
18
+ | `reference/platforms.md` (Phase 19) | Interaction **conventions** — navigation, safe areas, gestures, native typography, haptics. *Behavioral* knowledge for native/web screens. |
19
+ | `reference/native-platforms.md` (Phase 34.1) | The native **token bridge** — maps canonical CSS tokens to SwiftUI / Jetpack Compose / Flutter with a precision contract. *Token-identity* knowledge for native generators. |
20
+ | `reference/email-design.md` (Phase 34.2) | The email **constraint catalogue** — table layout, inline styles, MSO comments, dark-mode `color-scheme`. *Structural* knowledge an email template implements. |
21
+ | `reference/print-design.md` (Phase 34.3, this file) | The print **constraint catalogue** — the `@page` box model, bleed + crop marks, CMYK awareness, font embedding, and 300dpi raster fallback. *Production* knowledge a print/PDF document implements. |
22
+
23
+ Per **D-02** there is **no bundled `pdfkit` / `paged` / `puppeteer` / `playwright`
24
+ dependency**: the `pdf-executor` generates **Paged.js-compatible print HTML/CSS** as its
25
+ canonical artifact, and the PDF is rendered by the agent's contract or the *optional*
26
+ print-render connection, never by a build step. Per **D-03** print verify is this
27
+ catalogue's *static* validator plus an *optional* Paged.js-headless-Chrome / PDFKit
28
+ render-test connection that degrades to the static validator when absent. Per **D-10** the
29
+ static checks are deterministic (same CSS/HTML string → same result), with no network and
30
+ no PDF runtime, so the default `npm test` stays green on any machine.
31
+
32
+ Each constraint carries a **rule-id** (`PR-<CLASS>-NN`). Section 8 marks exactly which ids
33
+ the static validator asserts versus which are render-tested guidance only.
34
+
35
+ ---
36
+
37
+ ## 1. Purpose
38
+
39
+ Phase 19 shipped platform *references*; Phase 23 shipped the token engine; Phase 34.1 added
40
+ native generators; Phase 34.2 added the email catalogue. Print/PDF is the last untouched
41
+ product surface, and its constraints are unlike screen, native, and email alike: a physical
42
+ trim with bleed and registration marks, a *subtractive* CMYK color space instead of additive
43
+ screen RGB, a print RIP with **no web fonts** (fonts must be embedded or outlined), and a
44
+ 300dpi raster floor (vs the screen's 72/96dpi). Instead of each print document being authored
45
+ from memory, the constraints live once here (the catalogue) and once in the validator (the
46
+ statically-checkable subset), and the executor + verifier consume them. This file is the
47
+ single SC#9-print authority.
48
+
49
+ The catalogue is **prose + tables**, not an implementation. Illustrative snippets are kept to
50
+ 2–3 lines. The implementation is `validate-print-css.cjs`.
51
+
52
+ ---
53
+
54
+ ## 2. The print box model — `@page`
55
+
56
+ Print uses the CSS **`@page`** rule to define the page box: its `size` (a named page such as
57
+ `A4` / `Letter`, or an explicit `WIDTH HEIGHT` with physical units), its `margin`, and its
58
+ `marks` (`crop` / `cross`). Screen CSS has no page box at all — content flows in one
59
+ continuous viewport — so a print stylesheet that omits `@page` has no defined page geometry
60
+ and cannot paginate predictably. Paged.js consumes the `@page` CSS to paginate in headless
61
+ Chrome; PDFKit instead constructs the page box programmatically (`new PDFDocument({ size,
62
+ margins })`).
63
+
64
+ | Rule-id | Constraint |
65
+ | --- | --- |
66
+ | **PR-PAGE-01** | A print stylesheet MUST declare an `@page` rule — the print box model. Its absence means no defined page geometry. *(Statically checkable: absence of an `@page` rule is flagged.)* |
67
+ | PR-PAGE-02 | The `@page` rule SHOULD set `size` (named `A4`/`Letter` or explicit physical `WIDTH HEIGHT`) and `margin`; use `@page :first` / `:left` / `:right` for cover/spread-specific geometry. |
68
+ | PR-PAGE-03 | Control pagination with `break-before` / `break-after` / `break-inside: avoid` (and the legacy `page-break-*`) so headings, tables, and figures do not split badly across page boundaries. |
69
+
70
+ ```css
71
+ @page { size: A4; margin: 12mm; marks: crop cross; bleed: 3mm; }
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 3. Bleed + crop marks
77
+
78
+ Print is **trimmed** to a bleed box: any color or image that should reach the physical edge
79
+ must extend ~**3mm past the trim line** (the *bleed*), so that slight cutting variance never
80
+ exposes a white paper edge. **Crop/registration marks** tell the printer (and the trimming
81
+ guillotine) exactly where to cut, and align the CMYK separations on press. Screen CSS has no
82
+ notion of either. The CSS print idioms are the `bleed:` descriptor and `marks: crop` (and/or
83
+ `cross`) on `@page`; Paged.js supports both. A safe area is kept *inside* the trim so no
84
+ critical content sits in the cut-tolerance band.
85
+
86
+ | Rule-id | Constraint |
87
+ | --- | --- |
88
+ | **PR-BLEED-01** | A print document targeting edge-to-edge output MUST signal a bleed box / crop marks — a CSS `bleed:` declaration, a `marks: crop\|cross` declaration, or a documented bleed/crop-marks convention. *(Statically checkable: total absence of any bleed/marks signal is flagged.)* |
89
+ | PR-BLEED-02 | Bleed is conventionally **3mm** (≈0.125in); registration marks sit in the trim waste outside the bleed box so they are removed when the sheet is cut. |
90
+ | PR-BLEED-03 | Keep a **safe area** inside the trim (≈3–5mm) — critical text and logos stay inside it so cut tolerance never clips them. |
91
+
92
+ ---
93
+
94
+ ## 4. CMYK awareness
95
+
96
+ Print is **subtractive CMYK** (cyan/magenta/yellow/key-black ink on paper), not the additive
97
+ screen **RGB** the web executor emits. Pure-RGB output risks visible color shift on press:
98
+ bright RGB blues/greens fall outside the CMYK gamut and reproduce duller, and an untagged
99
+ document leaves the RIP to guess a conversion. A print artifact must therefore signal CMYK
100
+ awareness — a `cmyk()` color, a `color-profile` / `@color-profile` reference (an ICC/CMYK
101
+ target profile), or an explicit documented CMYK-target note. Exact ICC-profile correctness
102
+ and on-press gamut matching are **render-tested**, not statically assertable (see §8).
103
+
104
+ | Rule-id | Constraint |
105
+ | --- | --- |
106
+ | **PR-CMYK-01** | A print document MUST show CMYK awareness — a `cmyk()` color value, a `color-profile` / `@color-profile` reference, or a documented CMYK-target note. *(Statically checkable: total absence of any CMYK-awareness signal — pure screen-RGB only — is flagged.)* |
107
+ | PR-CMYK-02 | Prefer named spot/`cmyk()` values for brand colors that must match on press; flag wide-gamut RGB (`display-p3`, neon RGB) that will not survive CMYK conversion. |
108
+ | PR-CMYK-03 | Rich black (e.g. `C30 M30 Y30 K100`) for large solid areas; pure `K100` for small text to avoid registration fringing. *(ICC correctness is render-tested — §8.)* |
109
+
110
+ ```css
111
+ :root { color-profile: url(./CoatedFOGRA39.icc); } /* CMYK target */
112
+ .brand { color: cmyk(0% 80% 95% 0%); }
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 5. Font embedding
118
+
119
+ Print **RIPs have no web fonts** and no system-font-stack fallback chain — whatever font is
120
+ referenced must be **embedded** in the document (`@font-face` with an embedded `src:`) or the
121
+ text must be **outlined to vector**. A bare `font-family: Arial, sans-serif` system-font-stack
122
+ assumption is a print bug: the RIP may substitute a metrically-different face (reflowing the
123
+ layout) or fail to render the glyphs at all. PDFKit embeds fonts via `doc.registerFont(…)`;
124
+ Paged.js relies on `@font-face` declarations resolved before pagination.
125
+
126
+ | Rule-id | Constraint |
127
+ | --- | --- |
128
+ | **PR-FONT-01** | Fonts MUST be embedded or outlined — an `@font-face` rule with an embedded `src:`, or a documented font-embed/outline note. A bare system-font-stack assumption (no embed) is a print bug. *(Statically checkable: absence of any font-embed signal is flagged.)* |
129
+ | PR-FONT-02 | Embed only the weights/styles actually used (subset where possible) to keep the PDF small; ensure the license permits embedding. |
130
+ | PR-FONT-03 | Outline display/headline type to vector when exact rendering matters more than text selectability; keep body copy as embedded text for accessibility and reflow. |
131
+
132
+ ```css
133
+ @font-face { font-family: "Brand"; src: url(./Brand.woff2) format("woff2"); }
134
+ ```
135
+
136
+ ---
137
+
138
+ ## 6. 300dpi raster fallback
139
+
140
+ Raster/image assets need **300dpi** at final print size or they pixelate — screen assets are
141
+ authored at 72/96dpi, which is ~3–4× too coarse for press. **Vector is preferred** wherever
142
+ possible (logos, icons, rules) because it is resolution-independent; where raster is
143
+ unavoidable (photography), it must carry a 300dpi guarantee. The CSS signals are
144
+ `image-resolution: 300dpi` (or `from-image`), a `min-resolution` media query, or a documented
145
+ 300dpi note.
146
+
147
+ | Rule-id | Constraint |
148
+ | --- | --- |
149
+ | **PR-DPI-01** | Raster assets MUST carry a 300dpi raster-fallback signal — an `image-resolution:` declaration (`300dpi` / `from-image`), a `min-resolution` query, or a documented 300dpi note. *(Statically checkable: absence of any 300dpi signal is flagged.)* |
150
+ | PR-DPI-02 | Prefer vector (SVG/PDF) for logos, icons, and line art so they stay crisp at any output size; reserve raster for continuous-tone photography. |
151
+ | PR-DPI-03 | Size raster assets so their *effective* resolution at the placed dimensions is ≥300dpi; upscaling a 72dpi screen asset does not add real detail. |
152
+
153
+ ---
154
+
155
+ ## 7. Print color + units
156
+
157
+ Print prefers **physical units** (`mm` / `cm` / `pt` / `in`) over screen `px`, because the
158
+ page is a physical object — `px` has no fixed physical size across RIPs. Print-safe color,
159
+ overprint, and knockout are production concerns the executor should honor; most are
160
+ **render-tested guidance** (see §8), not statically asserted by the validator.
161
+
162
+ | Rule-id | Constraint |
163
+ | --- | --- |
164
+ | PR-UNIT-01 | Use physical units (`mm`/`cm`/`pt`/`in`) for page geometry, margins, and bleed; reserve `px` for screen. *(Guidance.)* |
165
+ | PR-COLOR-01 | **Overprint vs knockout** — small black text overprints (prints on top) to avoid registration gaps; light-on-dark knocks out. *(Render-tested — §8.)* |
166
+ | PR-COLOR-02 | **Trap/registration** — adjacent CMYK separations are trapped (slightly overlapped) so misregistration on press shows no white gap. *(Render-tested — §8.)* |
167
+ | PR-COLOR-03 | **True vector tessellation** — complex vector fills/gradients must tessellate without seams in the RIP. *(Render-tested — §8.)* |
168
+
169
+ ---
170
+
171
+ ## 8. Statically-checkable vs render-tested
172
+
173
+ This table is the **contract** the validator's `rule` ids map to. The five rule-ids below are
174
+ the deterministic subset that `scripts/lib/print/validate-print-css.cjs` asserts via
175
+ regex/string analysis of the supplied print CSS/HTML string. Every other rule-id in this
176
+ catalogue is **render-tested guidance** — verified by the optional Paged.js-headless-Chrome /
177
+ PDFKit render-test connection (34.3-02), never asserted by the static validator.
178
+
179
+ | Rule-id | Check | Statically checked by the validator? | How verified otherwise |
180
+ | --- | --- | --- | --- |
181
+ | **PR-PAGE-01** | An `@page` rule is present (the print box model) | **YES** — absence flagged | — |
182
+ | **PR-BLEED-01** | A bleed box / crop-marks signal is present (`bleed:` / `marks:` / a documented bleed-marks note) | **YES** — total absence flagged | — |
183
+ | **PR-CMYK-01** | A CMYK-awareness signal is present (`cmyk(` / `color-profile` / `@color-profile` / a CMYK note) | **YES** — total absence (pure RGB) flagged | — |
184
+ | **PR-FONT-01** | A font-embed signal is present (`@font-face` with `src:` / a font-embed/outline note) | **YES** — absence flagged | — |
185
+ | **PR-DPI-01** | A 300dpi raster-fallback signal is present (`image-resolution:` / `min-resolution` / a 300dpi note) | **YES** — absence flagged | — |
186
+ | PR-PAGE-02..03 | `size`/`margin` set, sensible page breaks | No | Render test (paginated output) |
187
+ | PR-BLEED-02..03 | 3mm bleed value, marks in trim waste, safe area | No | Render test (preflight) |
188
+ | PR-CMYK-02..03 | In-gamut brand colors, rich-black vs K100, **ICC-profile correctness** | No | Render test (press proof / ICC) |
189
+ | PR-FONT-02..03 | Subsetted/licensed embeds, vector outlining | No | Render test (PDF inspect) |
190
+ | PR-DPI-02..03 | Vector-preferred, effective ≥300dpi at placed size | No | Render test (preflight) |
191
+ | PR-UNIT-01, PR-COLOR-01..03 | Physical units, **overprint/knockout**, **trap/registration**, **true vector tessellation** | No | Render test (press / RIP) |
192
+
193
+ Notes on the five statically-checked rules:
194
+
195
+ - **Each check is a presence/absence test.** The validator flags the *total absence* of a
196
+ catalogued signal class in the supplied string; the presence of **any one** accepted signal
197
+ for a class satisfies it. The checks are independent, so a document can satisfy four classes
198
+ and trip exactly one.
199
+ - **CMYK awareness is satisfied by any of three signals.** A `cmyk()` color, a
200
+ `color-profile` / `@color-profile` reference, **or** a documented `/* CMYK … */` note each
201
+ satisfy PR-CMYK-01 on their own — a fully RGB document with an explicit CMYK-target note
202
+ still passes (the note records the production intent the RIP needs).
203
+ - **Render-tested rules are out of the static validator's scope by design.** Overprint
204
+ behavior, ICC-profile correctness, trap/registration, and true vector tessellation require
205
+ an actual PDF RIP / rendering engine — they are catalogued here as executor guidance and
206
+ verified by the optional print-render connection, never asserted statically (D-03 / D-10).
207
+
208
+ ---
209
+
210
+ ## 9. Cross-references
211
+
212
+ - [`reference/email-design.md`](./email-design.md) — the email-constraint sibling
213
+ (table layout, inline styles, MSO comments, dark mode). The non-web siblings share the
214
+ catalogue-plus-static-validator shape.
215
+ - [`reference/native-platforms.md`](./native-platforms.md) — the native token-bridge sibling
216
+ (SwiftUI / Compose / Flutter). The other non-web output surface.
217
+ - [`reference/platforms.md`](./platforms.md) — the interaction-conventions sibling.
218
+ - [`scripts/lib/print/validate-print-css.cjs`](../scripts/lib/print/validate-print-css.cjs)
219
+ — the deterministic static validator that asserts the §8 subset; its `rule` ids are the
220
+ constraint-ids defined here.
221
+ - [`reference/registry.json`](./registry.json) — this catalogue is registered as the
222
+ `print-design` entry (type `heuristic`, phase `34.3`) so the registry round-trip test
223
+ (`test/suite/reference-registry.test.cjs`) stays green (D-05, the 34.1-01 / 34.2-01 lesson).
@@ -902,6 +902,13 @@
902
902
  "type": "heuristic",
903
903
  "phase": 34.2,
904
904
  "description": "Phase 34.2 email-constraint catalogue — table-based layout (not flexbox/grid/position), inline styles (not a <style> block), MSO conditional comments for Outlook, dark-mode color-scheme/prefers-color-scheme handling, ~600px max-width, image/alt rules, and top-20-client quirks; the authority the email-executor generates against and the design-verifier email branch audits against, and the rule-id source for scripts/lib/email/validate-email-html.cjs."
905
+ },
906
+ {
907
+ "name": "print-design",
908
+ "path": "reference/print-design.md",
909
+ "type": "heuristic",
910
+ "phase": 34.3,
911
+ "description": "Phase 34.3 print-constraint catalogue — @page size/margin/marks (the print box model), bleed box + crop/registration marks, CMYK color-space awareness (subtractive, not screen RGB), font embedding/outlining (print RIPs have no web fonts), and 300dpi raster-fallback guidance; the authority the pdf-executor generates against and the design-verifier print branch audits against, and the rule-id source for scripts/lib/print/validate-print-css.cjs."
905
912
  }
906
913
  ]
907
914
  }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * print/validate-print-css.cjs — static print-CSS constraint validator
3
+ * (Phase 34.3-01).
4
+ *
5
+ * Pure, deterministic regex/string analysis of a print CSS/HTML STRING. Same
6
+ * input -> identical output. It checks the statically-verifiable SUBSET of the
7
+ * constraint catalogue in `reference/print-design.md` §8 — the spec is the
8
+ * authority; the `rule` ids emitted here are constraint-ids defined there.
9
+ *
10
+ * WHAT IS CHECKED (the five deterministic classes, emitted in a stable order):
11
+ * PR-PAGE-01 an `@page` rule is present — the print box model. Its absence in
12
+ * a print stylesheet means no defined page geometry. (Absence flagged.)
13
+ * PR-BLEED-01 a bleed box / crop-marks signal is present — a CSS `bleed:`
14
+ * declaration, a `marks:` (crop|cross) declaration, or a documented
15
+ * bleed/crop-marks note. (Total absence flagged.)
16
+ * PR-CMYK-01 a CMYK-awareness signal is present — a `cmyk(` color, a
17
+ * `color-profile` / `@color-profile` reference, or a documented CMYK
18
+ * note. (Total RGB-only absence flagged.)
19
+ * PR-FONT-01 a font-embed signal is present — an `@font-face` rule carrying a
20
+ * `src:` (embedded font), or a documented font-embed/outline note. A
21
+ * bare system-font-stack with no embed is flagged.
22
+ * PR-DPI-01 a 300dpi raster-fallback signal is present — an `image-resolution:`
23
+ * declaration (300dpi / from-image), a `min-resolution` query, or a
24
+ * documented 300dpi note. (Absence flagged.)
25
+ *
26
+ * WHAT IS *NOT* CHECKED (catalogued in reference/print-design.md as render-tested
27
+ * guidance — verified by the optional Paged.js-headless-Chrome / PDFKit render
28
+ * connection at 34.3-02, never by this validator): exact overprint/knockout
29
+ * behavior, ICC-profile correctness / on-press gamut matching, trap/registration,
30
+ * true vector tessellation, and per-output preflight (PR-*-02/03, PR-UNIT-01,
31
+ * PR-COLOR-01..03).
32
+ *
33
+ * PURITY (D-02 / D-10): operates only on the passed string — no fs of the
34
+ * document, no network, no child-process spawn, no pdfkit/paged/puppeteer/
35
+ * playwright runtime import, no Date, no process.env. This file has zero
36
+ * require() calls (node builtins included).
37
+ */
38
+
39
+ 'use strict';
40
+
41
+ // --- PR-PAGE-01: an @page rule is present ---------------------------------
42
+ // Require an actual @page RULE (an optional `:pseudo` selector then a `{` block),
43
+ // not the bare token — so negative prose like "NO @page rule" in a comment does
44
+ // not count as a present rule.
45
+ const AT_PAGE_RE = /@page\b[^;{}]*\{/i;
46
+
47
+ // --- PR-BLEED-01: a bleed box / crop-marks signal -------------------------
48
+ // A CSS `bleed:` descriptor, a `marks:` (crop|cross) descriptor, or a
49
+ // documented bleed/crop-marks note. The declaration forms require a value after
50
+ // the colon (so a negative-prose "NO bleed box" comment does NOT match); the
51
+ // note form requires the word "crop"/"registration" adjacent to "mark(s)".
52
+ const BLEED_DECL_RE = /\bbleed\s*:\s*\S/i;
53
+ const MARKS_DECL_RE = /\bmarks\s*:\s*(?:crop|cross)\b/i;
54
+ const CROP_MARKS_NOTE_RE = /\b(?:crop|registration)\s+marks?\b/i;
55
+
56
+ // --- PR-CMYK-01: a CMYK-awareness signal ----------------------------------
57
+ // A cmyk() color, a color-profile / @color-profile reference, or a documented
58
+ // "CMYK" note (the word CMYK anywhere — a comment recording production intent).
59
+ const CMYK_FN_RE = /\bcmyk\s*\(/i;
60
+ const COLOR_PROFILE_RE = /@?color-profile\b/i;
61
+ const CMYK_NOTE_RE = /\bCMYK\b/i;
62
+
63
+ // --- PR-FONT-01: a font-embed signal --------------------------------------
64
+ // An @font-face rule whose body carries a `src:` (an embedded font), or a
65
+ // documented font-embed/outline note.
66
+ const FONT_FACE_SRC_RE = /@font-face\s*\{[^{}]*\bsrc\s*:/i;
67
+ const FONT_EMBED_NOTE_RE = /\b(?:font[\s-]*embed(?:ding|ded)?|embed(?:ded)?\s+font|outline(?:d)?\s+(?:to\s+)?(?:vector|font)|font[\s-]*outline)\b/i;
68
+
69
+ // --- PR-DPI-01: a 300dpi raster-fallback signal ---------------------------
70
+ // An image-resolution declaration, a min-resolution query, or a documented
71
+ // 300dpi note (300 immediately followed by dpi/ppi, optionally spaced/hyphenated).
72
+ const IMAGE_RESOLUTION_RE = /\bimage-resolution\s*:/i;
73
+ const MIN_RESOLUTION_RE = /\bmin-resolution\b/i;
74
+ const DPI_300_NOTE_RE = /\b300\s*-?\s*(?:dpi|ppi)\b/i;
75
+
76
+ /**
77
+ * Validate a print CSS/HTML string against the statically-checkable constraint
78
+ * subset of reference/print-design.md §8.
79
+ *
80
+ * @param {string} input the print CSS (or HTML carrying a print stylesheet);
81
+ * the caller reads the file and passes the content — this function never
82
+ * touches the filesystem
83
+ * @param {{ checks?: string[] }} [opts] reserved for future toggles; default
84
+ * runs all five classes
85
+ * @returns {{ ok: boolean, violations: Array<{ rule: string, detail: string }> }}
86
+ * `ok === (violations.length === 0)`; each violation's `rule` is a catalogued
87
+ * constraint-id and `detail` is a short human string.
88
+ */
89
+ function validatePrintCss(input, opts) {
90
+ if (typeof input !== 'string') {
91
+ throw new TypeError('validatePrintCss(input): input must be a string');
92
+ }
93
+ void opts; // reserved
94
+ /** @type {Array<{ rule: string, detail: string }>} */
95
+ const violations = [];
96
+
97
+ // --- PR-PAGE-01: an @page rule is present (the print box model) -----------
98
+ if (!AT_PAGE_RE.test(input)) {
99
+ violations.push({
100
+ rule: 'PR-PAGE-01',
101
+ detail: 'a print stylesheet must declare an @page rule (the print box model — size/margin/marks); none found',
102
+ });
103
+ }
104
+
105
+ // --- PR-BLEED-01: a bleed box / crop-marks signal -------------------------
106
+ const hasBleed =
107
+ BLEED_DECL_RE.test(input) ||
108
+ MARKS_DECL_RE.test(input) ||
109
+ CROP_MARKS_NOTE_RE.test(input);
110
+ if (!hasBleed) {
111
+ violations.push({
112
+ rule: 'PR-BLEED-01',
113
+ detail: 'declare a bleed box / crop marks (a `bleed:` declaration, a `marks: crop|cross` declaration, or a documented bleed/crop-marks note) so edge-to-edge content survives the trim',
114
+ });
115
+ }
116
+
117
+ // --- PR-CMYK-01: a CMYK-awareness signal ----------------------------------
118
+ const hasCmyk =
119
+ CMYK_FN_RE.test(input) ||
120
+ COLOR_PROFILE_RE.test(input) ||
121
+ CMYK_NOTE_RE.test(input);
122
+ if (!hasCmyk) {
123
+ violations.push({
124
+ rule: 'PR-CMYK-01',
125
+ detail: 'signal CMYK awareness (a cmyk() color, a color-profile/@color-profile reference, or a documented CMYK-target note); print is subtractive CMYK, not screen RGB',
126
+ });
127
+ }
128
+
129
+ // --- PR-FONT-01: a font-embed signal --------------------------------------
130
+ const hasFontEmbed =
131
+ FONT_FACE_SRC_RE.test(input) ||
132
+ FONT_EMBED_NOTE_RE.test(input);
133
+ if (!hasFontEmbed) {
134
+ violations.push({
135
+ rule: 'PR-FONT-01',
136
+ detail: 'embed or outline fonts (an @font-face with an embedded src:, or a documented font-embed/outline note); print RIPs have no web fonts and no system-font fallback',
137
+ });
138
+ }
139
+
140
+ // --- PR-DPI-01: a 300dpi raster-fallback signal ---------------------------
141
+ const hasDpi =
142
+ IMAGE_RESOLUTION_RE.test(input) ||
143
+ MIN_RESOLUTION_RE.test(input) ||
144
+ DPI_300_NOTE_RE.test(input);
145
+ if (!hasDpi) {
146
+ violations.push({
147
+ rule: 'PR-DPI-01',
148
+ detail: 'provide a 300dpi raster-fallback signal (image-resolution: 300dpi/from-image, a min-resolution query, or a documented 300dpi note); screen 72/96dpi rasters pixelate in print',
149
+ });
150
+ }
151
+
152
+ return { ok: violations.length === 0, violations };
153
+ }
154
+
155
+ module.exports = { validatePrintCss };