Skip to content

Commit 14fc086

Browse files
garrytanclaude
andauthored
v1.58.0.0 feat: diagram + multi-format document engine (mermaid, excalidraw, single-file HTML, DOCX) (garrytan#1990)
* docs(todos): P3 content-hash diagram render cache for make-pdf Deferred from the diagram-engine eng review (Codex outside-voice D7): repeat make-pdf runs re-render every fence; cache keyed on fence source + bundle version once multi-diagram docs make it worth building. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(diagram-render): offline mermaid+excalidraw render bundle for browse Single self-contained page (dist/diagram-render.html, 9.2MB, committed per eng-review D2) exposing __renderMermaid / __mermaidToExcalidraw / __excalidrawToSvg / __rasterize / __probeImage through browse load-html + js --out. Render contract per D3: securityLevel strict, per-fence ids, print-css font lock, htmlLabels off (canvas-taint-safe). Deterministic build (same sha twice); drift test pins dist == BUILD_INFO == package.json pins and rebuild-reproducibility when toolchain matches. Spike-proven offline: flowchart + sequence SVG, editable .excalidraw scene, 300dpi PNG. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(diagram-render): __downscaleRaster for print-resolution image normalization Data-URI rasters re-encode in their own format (JPEG stays JPEG at q0.9 — PNG-encoding photos bloats them) at an explicit target pixel width. Used by make-pdf's pre-pass for the 300dpi content-box ceiling (eng-review D4). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(make-pdf): diagram pre-pass — mermaid/excalidraw fences render as vector SVG; local images inline as data URIs ```mermaid / ```excalidraw fences extract to placeholder tokens, render in one diagram-render bundle tab per run (reset contract: bundle page reloads after any render error), and substitute back as accessible <figure> blocks with the raw source preserved in a comment. Render failures produce a loud red diagnostic block, never silent raw code. render=false keeps a fence as code; title="..." becomes the aria-label and caption. Local images now actually render: page.setContent loads at about:blank (tab-session.ts:194), so relative paths silently 404'd before. The pre-pass resolves them against the markdown's directory, inlines as data URIs, probes intrinsic dimensions from the bytes (pure-TS PNG/JPEG/GIF/WebP/SVG sniffing), and downscales rasters wider than 2x the content box at 300dpi. Remote URLs warn (offline posture, --allow-network exempts); missing files get a visible placeholder; --strict hard-fails both for CI pipelines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(make-pdf): diagram pre-pass unit suite + e2e render gates 34 unit tests (fence extraction incl. nested/tilde/unclosed/render=false, info-string parsing, slot substitution, diagnostic/figure escaping + SVG script strip, byte-level dimension probing across 5 formats, content-box math, image inlining incl. strict/remote/missing/data-URI paths). E2E gate proves through the compiled binary: both fences render as vector text (id-collision check), raw mermaid ships only via render=false, broken fence yields the diagnostic block, and the relative fixture image rasterizes to colored pixels (CRITICAL regression for the about:blank image fix). --strict exits non-zero on a missing image. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(make-pdf): width directives + conservative auto-landscape via CSS named pages `![a](x.png){width=full|<pct>|<dim>}` and `{page=landscape|portrait}` suffixes translate to data-gstack-* attrs in render() (before the sanitizer, which keeps data- attributes; unrecognized brace groups stay visible text). Default width rule needs no code: intrinsic CSS-px capped at the content box, never upscaled — figure img max-width owns it. Auto-landscape promotes a block to `@page wide { size: <pagesize> landscape }` only when aspect >= 1.8 AND intrinsic width > 2.5x the content box (~1600px on letter) AND diagram provenance (rendered fences) or a whole-word alt token (diagram|architecture|flowchart|chart|graph) for plain images. {page=...} forces or vetoes; fence info strings accept page=... too. preferCSSPageSize is passed to Chromium only when a promotion exists, so every other document prints exactly as before. False negatives are cheap; false positives feel broken (eng-review P4, Codex challenge accepted). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(make-pdf): width-policy unit suite + landscape e2e gate with negative fixtures 24 unit tests weighted toward the false-positive guards: wide screenshot without an alt hint stays portrait, sub-threshold and tall images stay portrait, deterministic 1560/1561px boundary, whole-word alt matching ('photographic' must not match 'graph'), page=portrait veto beats every heuristic, diagnostic blocks never promote. E2E gate asserts pdfinfo per-page boxes through the compiled binary: exactly 3 of 5 fixture blocks get landscape pages (alt-hinted image, directive-forced image, wide sequence diagram) while the unhinted screenshot and the veto'd diagram stay portrait — plus the --toc combo proving TOC and named-page landscape coexist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(make-pdf): --to html|docx output formats --to html writes the assembled self-contained document directly (no print round-trip): inline vector diagrams, data-URI images, zero network references, plus an @media screen layer for browser reading. --to docx is the content-fidelity export (eng-review P8): html-to-docx@1.8.0 (exact pin; pure JS, bun-compile-verified) maps headings/tables/code/lists; diagrams and SVG images rasterize at 300dpi of the content-box width via the render tab; diagnostic figures convert to plain p/pre so the converter can't silently drop an error. --format keeps its page-size-alias meaning; --to is the output format, and the CLI says so when confused. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(make-pdf): format gate — html no-network-refs + docx zip content checks HTML: zero src/href network refs, no script/link tags, inline SVG diagrams, data-URI images, screen layer, diagnostic survives. DOCX: valid OOXML zip (document.xml + Content_Types), >=2 PNG media (diagram raster + fixture image), headings + render=false source + diagnostic text in document.xml, no leaked mermaid source from rendered fences. Plus --to validation UX. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(diagram): /diagram skill — English in, editable diagram triplet out New skill: agent authors mermaid from the user's description and renders the triplet through the offline diagram-render bundle in the browse daemon — .mmd source (the single source of truth), editable .excalidraw (opens at excalidraw.com, round-trips back through re-render), and SVG + PNG. Flowcharts convert to fully editable scenes; other mermaid types render with an explicit upstream-converter limitation note. Never ships an unrendered source file; offline is the contract (no CDN fallback). Inventory rows in AGENTS.md + docs/skills.md; generated SKILL.md + llms.txt via gen:skill-docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(diagram): paid E2E pair — gate triplet contract + periodic authoring judge diagram-triplet (gate, deterministic functional): a fresh claude -p agent following the skill extract must emit a parseable triplet — graph LR/TD in .mmd, excalidraw scene with >3 elements, SVG markup, PNG magic bytes. Verified live: pass, $0.17, 58s. diagram-authoring-quality (periodic, LLM-judged): faithfulness/labels/size rubric with a diagnostic-path cap, floor 6/10. Verified live: pass at exactly 6 with substantive critique. Touchfiles select both on diagram/** and lib/diagram-render/** changes; tier split per E2E_TIERS rules (eng-review D5). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(diagram): register /diagram in the skill coverage matrix Gate: triplet contract + structural floor; periodic: authoring-quality judge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(make-pdf): typography scale-up, zero image truncation, landscape vertical centering Dogfooding round on the repo README surfaced four output-quality bugs: - Type was too small everywhere: body 11→12pt, h1 22→26pt, h2 15→18pt, cover title 32→56pt with poster spacing, cover meta 10→13pt, TOC 11→12pt with tighter leading, code 9.5→10.5pt, tables 10→11pt. - Zero image truncation, ever: the max-width cap was figure-scoped, but markdown images render as <p><img> — a 1850px GitHub screenshot ran off the page edge. Global img { max-width: 100%; height: auto; } cap. - hyphens: auto put real 'dif-\nferent' breaks into the PDF text layer the moment 12pt made lines wrap (combined-gate caught it). Clean copy-paste is the product contract; left-aligned rag doesn't need hyphenation → hyphens: manual. - Promoted landscape blocks now vertically center. CSS flex/min-height centering fragments into phantom empty landscape pages in Chromium (bisected: min-height at ANY value; 3 promotions printed 5 pages), so image-policy computes an inline margin-top from each block's known aspect ratio against the landscape content box instead — fragmentation handles margins fine. .page-wide also drops its explicit break-before/ after (the page-name change already breaks on both sides). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(make-pdf): pin zero-truncation invariant, typography floor, centering math Global img cap pinned as a regex invariant (the figure-scoped-cap regression class); typography floor (12pt body, 56pt cover, 12pt TOC); .page-wide must NOT carry min-height/flex (the phantom-landscape-page regression class); centering margin math verified both ways (2400×1000 image → 1.38in, 2050×600 viewBox diagram → 1.93in, page-filling directive block → no margin). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: diagram + multi-format documentation across README, make-pdf skill, and how-to guide README gains /make-pdf (Publisher) and /diagram (Diagram Maker) rows in the sprint table. make-pdf's skill doc — the agent-facing contract — gains Core patterns for mermaid/excalidraw fences (title/render=false/page= options), the image policy ({width=}/{page=} directives, zero-truncation, conservative auto-landscape), --to html|docx, and --strict, plus the --to vs --format disambiguation in Common flags. New docs/howto-diagrams-and-formats.md is the user-facing walkthrough: fences, directives, formats, /diagram triplet, the mermaid racetrack trick, troubleshooting. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(make-pdf): fill ship-audit coverage gaps — downscale, reset contract, excalidraw fence, WebP Ship coverage audit found 9 gaps (85%); this fills the 2 HIGH + 3 MEDIUM and most LOW. diagram-gate fixture gains a 4200px incompressible photo (the only live coverage of __downscaleRaster AND the 64KB chunked jsViaBuffer eval transport — asserted via the downscale stderr warning), an ```excalidraw scene fence rendered through exportToSvg (vector labels + caption in pdftotext, no leaked scene JSON), and the broken fence MOVED BETWEEN the two mermaid fences so the second diagram rendering proves the D6.2 reset contract end-to-end. New coverage-gaps.test.ts (16 tests): mock-tab reset contract (exactly one reload, post-failure fence renders), excalidraw fail-fast diagnostic without a bundle call, rasterize error fallbacks (figure/tag kept, never silent), WebP VP8/VP8L/VP8X byte parsers, landscapeContentBox a4/asymmetric margins, bare-token slot fallback, resolveBundlePath env override + error shape, screenCss media scoping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): pre-landing review wave — fence fidelity, injection hardening, Windows paths, transport rework Review army (6 specialists + red team) findings, all fixed: - Indented fences replay byte-for-byte and indented diagram fences are NOT extracted (red-team conf-9: the pre-pass reconstructed fences at column 0, splitting any list containing fenced code — every ordinary document). - String.replace $-pattern injection killed at every seam: substituteSlots, mergeStyle, img/src rewrites all use function replacements (a diagram label containing $' duplicated the document tail). - Big-expression transport reworked: browse `eval <file>` (one spawn, any size, Windows-safe) replaces the 64KB chunked window-buffer eval — fixes the per-chunk spawn cost, the char-vs-byte argv units, AND the Windows 32,767-char command-line ceiling in one move. - Staged-bundle trust: content verified by hash even when the file exists, and the rename-failure path re-hashes the survivor (sticky-bit /tmp EPERM would otherwise ride a pre-planted file past the check). - Windows drive-letter img srcs (C:/x.png) reach the local-path branch instead of being swallowed as unknown URL schemes. - DOCX rasterize-failure now embeds the decoded source as visible text — returning the figure made diagrams vanish silently (converter drops svg). - Fence source preserved as base64 data-gstack-source attribute (the comment encoding corrupted every '-->' arrow); decodeFigureSource() round-trips. - inlineLocalImages memoizes per path; file:// uses fileURLToPath; preview prints a divergence note for fences/local images; --to docx strips the watermark div and warns about print-only flags; TOC links resolve in html/docx (heading ids assigned); waitForExpression sleeps instead of busy-spinning; escapeHtml/svg-dims deduped to single definitions; typography stragglers (blockquote 12pt, footnotes 10pt, 42em screen measure); bundle BUILD_INFO gains srcSha256 for no-node_modules drift detection; MAX_TARGET_PX shared guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: make-pdf gate covers the diagram-render bundle; bundle pinned to LF make-pdf-gate.yml paths gain lib/diagram-render/** and the drift test (a bundle-only PR previously skipped every render gate AND no CI lane ran the drift check at all). .gitattributes pins dist html/json to LF so Windows autocrlf can't break the hash-pinned bundle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(make-pdf)+feat(diagram): review-wave test pins + skill transport hardening Tests: indented-fence byte-for-byte replay + no-extraction-in-lists, drive-letter local-path routing, $-pattern slot immunity, base64 source round-trip ('A --> B' exact), existing-style merge preservation, DOCX rasterize-failure surfaces source, srcSha256 + font-stack drift guards, landscape veto asserted as some-portrait/no-landscape (layout-order-proof), judge rubric cap lowered to 5 so it actually fails, vacuous error-shape test removed honestly, tmpdir cleanup. /diagram skill: base64 transport (template literals corrupted backticks/${ in sources), content-addressed staging with hash verification, and --tab-id pinned on every browse call so a concurrent /qa session can't be clobbered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(make-pdf): out-of-tree image reads warn; --strict makes them fatal (D8.1) Local CLI semantics stay (absolute paths and ../ still inline, like pandoc), but never silently: an agent PDF-ing untrusted markdown can't quietly embed a file from outside the input directory into a shareable document without a visible warning, and --strict pipelines hard-fail. Two unit tests. Also: TODOS.md gains the deferred e2e-harness dedup entry (D8.2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: pre-existing test failure in skill-e2e-bws operational-learning Root cause was the fixture, not model behavior: gstack-learnings-log gained an import of lib/jsonl-store.ts in the v1.57.5.0 injection-sanitization wave, but the test copies only bin/ scripts into its sandbox — the inline bun import failed and the script exited 1 before writing, on every run, on main too (reproduced at a5833c4). Fixture now stages lib/jsonl-store.ts beside bin/; verified deterministically (script exits 0, learning written) and via the paid test (1 pass). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): adversarial-review wave — offline posture enforced, symlink-aware confinement, bounded reads Codex adversarial + structured review findings: - Remote images are now BLOCKED with a visible placeholder instead of warn-and-keep — leaving the tag meant Chromium fetched the URL at print time anyway, so the offline posture was a lie (tracking pixels and internal-URL probes ran without --allow-network). - The out-of-tree read check compares REAL paths: a symlink inside the input dir pointing at ~/.ssh/... passed the string-prefix check, including under --strict. Ordered after the existence check (realpath of a missing file false-positives on macOS /var → /private/var). - Image reads are bounded BEFORE reading: statSync first, non-regular files (fifo/device/dir) and >64MB files degrade to placeholders instead of hanging or exhausting memory; malformed percent-encoding (foo%zz.png) degrades to missing-image instead of crashing decodeURIComponent. - browse shell-outs get a 120s timeout — a wedged daemon or hostile mermaid source fails the run instead of hanging it. - TOC entries link to the heading's ACTUAL id (pre-id'd raw-HTML headings previously got dead #toc-N links); per-side margins compose into the CSS @page shorthand so a landscape promotion flipping preferCSSPageSize no longer silently reverts --margin-left/right to defaults (Codex P2). - The image memo is a typed object — literal NUL-byte separators had made diagram-prepass.ts register as binary to text tooling. Codex structured review GATE: PASS (no P1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump version and changelog (v1.58.0.0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: sync make-pdf image-policy docs with final shipped behavior (v1.58.0.0) The docs wave (8759442) predated the final review-wave commits, so two docs drifted from shipped behavior: - make-pdf/SKILL.md.tmpl + generated SKILL.md: remote images are BLOCKED with a visible placeholder (not warned-and-kept); out-of-tree reads (including via symlink) warn and --strict makes them fatal; --strict also covers oversized (>64MB) and non-regular files; troubleshooting entry now names the actual "[remote image blocked]" symptom. - docs/howto-diagrams-and-formats.md: same corrections in the image section, CI section, and troubleshooting. - README.md: docs/howto-diagrams-and-formats.md added to the Docs table (was unreachable from any entry-point doc). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: apply Codex doc-review findings for v1.58.0.0 Cross-model doc review (Codex, read-only) checked the v1.58.0.0 docs against the shipped code. Fixes: - howto + make-pdf SKILL: diagram source is preserved base64 in a data-gstack-source attribute, not an HTML comment (-- in mermaid arrows would corrupt a comment); fences must start at column 0; fence options example gains page=portrait; --to html "zero network refs" qualified (--allow-network deliberately keeps remote tags). - /diagram description, README + docs/skills.md rows: the hand-drawn aesthetic belongs to the .excalidraw artifact; rendered SVG/PNG use mermaid's clean neutral theme (lib/diagram-render entry.ts pins theme: "neutral"). - CHANGELOG v1.58.0.0 wording: --strict coverage lists all five fatal classes (missing/remote/out-of-tree/oversized/non-regular); fences are vector SVG in pdf+html, 300dpi PNG in docx; hand-drawn claim scoped to the .excalidraw file. - lib/diagram-render/README: Page API table gains __downscaleRaster. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent a5833c4 commit 14fc086

53 files changed

Lines changed: 11129 additions & 69 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,9 @@ bin/* text eol=lf
3737
*.gif binary
3838
*.ico binary
3939
*.pdf binary
40+
41+
# The committed diagram-render bundle is hash-pinned (BUILD_INFO sha256);
42+
# a CRLF rewrite on Windows checkout would break the drift test and change
43+
# the content-addressed staged filename.
44+
lib/diagram-render/dist/*.html text eol=lf
45+
lib/diagram-render/dist/*.json text eol=lf

.github/workflows/make-pdf-gate.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ on:
44
branches: [main]
55
paths:
66
- 'make-pdf/**'
7+
- 'lib/diagram-render/**'
8+
- 'test/diagram-render-drift.test.ts'
79
- 'browse/src/meta-commands.ts'
810
- 'browse/src/write-commands.ts'
911
- 'browse/src/commands.ts'
@@ -81,7 +83,7 @@ jobs:
8183
which pdftotext && pdftotext -v 2>&1 | head -1 || true
8284
8385
- name: Run make-pdf unit tests
84-
run: bun test make-pdf/test/*.test.ts
86+
run: bun test make-pdf/test/*.test.ts test/diagram-render-drift.test.ts
8587

8688
- name: Run E2E gates (combined-features copy-paste + emoji render)
8789
env:

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ dist/
44
browse/dist/
55
design/dist/
66
make-pdf/dist/
7+
# diagram-render ships its built bundle (offline-at-install premise, eng-review D2)
8+
!lib/diagram-render/dist/
9+
!lib/diagram-render/dist/**
710
bin/gstack-global-discover*
811
.gstack/
912
.claude/skills/

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ End-to-end walkthrough: [docs/howto-ios-testing-with-gstack.md](docs/howto-ios-t
104104
| `/guard` | Activate both careful + freeze at once. |
105105
| `/unfreeze` | Remove directory edit restrictions. |
106106
| `/make-pdf` | Turn any markdown file into a publication-quality PDF. |
107+
| `/diagram` | English in, diagram out: mermaid source + editable .excalidraw + SVG/PNG, offline. |
107108

108109
## Build commands
109110

CHANGELOG.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,111 @@
11
# Changelog
22

3+
## [1.58.0.0] - 2026-06-12
4+
5+
## **Your documents grow diagrams. Mermaid and excalidraw fences render as real pictures,**
6+
## **and make-pdf now ships single-file HTML and Word output from the same markdown.**
7+
8+
Put a ` ```mermaid ` fence in your markdown and `make-pdf` renders it as a crisp
9+
vector diagram, fully offline, with the source preserved for round-trips. A broken
10+
fence prints a loud red diagnostic block with the parse error, never silent raw
11+
code. The new `/diagram` skill goes the other way: describe a flow in English and
12+
get a triplet back, the mermaid source, an editable `.excalidraw` file you can open
13+
at excalidraw.com in the hand-drawn style, and rendered SVG + PNG. Images got the
14+
same care: local paths inline automatically and never truncate, phone photos
15+
downscale to print resolution instead of blowing up the file, and a wide small-text
16+
diagram promotes itself onto a vertically centered landscape page inside an
17+
otherwise portrait document. One markdown file now exports three ways:
18+
`--to pdf | html | docx`, where html is one self-contained file with zero network
19+
references. Type is bigger across the board (12pt body, 56pt cover titles), TOC
20+
links actually jump, and `--strict` turns missing, remote, out-of-tree, or
21+
oversized images into hard CI failures.
22+
23+
### The numbers that matter
24+
25+
Measured on this repo's README (5,940 words, lists, code, screenshots, one
26+
diagram fence) and the free gate suite. Reproduce: `make-pdf generate README.md
27+
--cover --toc` and `bun test make-pdf/test/`.
28+
29+
| Metric | Before | After | Δ |
30+
|--------|--------|-------|---|
31+
| A mermaid fence in your PDF | raw code block | vector diagram | rendered |
32+
| Output formats from one markdown | 1 (pdf) | 3 (pdf, html, docx) | +2 |
33+
| Network requests at render time | up to 1 per remote image | 0 by default | sealed |
34+
| Wide-diagram handling | shrunk into portrait | own centered landscape page | rotated |
35+
| Free make-pdf gate tests | 121 | 189 | +68 |
36+
| README → 29-page PDF with diagram | n/a | 4.4s | one command |
37+
38+
The sealed-network number is the one to notice: the mermaid and excalidraw
39+
runtimes are vendored into a 9.2MB sha-pinned bundle, so rendering works on a
40+
plane and a tracking pixel in pasted markdown fetches nothing.
41+
42+
### What this means for your documents
43+
44+
The diagram you describe in English stays editable forever: `/diagram` writes the
45+
source, you embed the source in markdown, and every export renders it fresh. Stop
46+
pasting screenshots of diagrams into documents. Run `/diagram` for the picture,
47+
` ```mermaid ` for the document, and `--to html` when the reader doesn't want a PDF.
48+
49+
### Itemized changes
50+
51+
#### Added
52+
- ` ```mermaid ` and ` ```excalidraw ` fences render as inline vector SVG in pdf
53+
and html output (docx embeds them as 300dpi PNGs). Fence options: `title="..."` (caption + aria-label),
54+
`render=false` (keep as code), `page=landscape|portrait` (orientation override).
55+
Render failures produce a visible diagnostic block with the parse error.
56+
- `/diagram` skill: English in, editable triplet out (`.mmd` source,
57+
`.excalidraw` scene, SVG + PNG). Flowcharts convert to fully editable
58+
excalidraw scenes; other mermaid types render with an explicit limitation note.
59+
- `lib/diagram-render/`: vendored offline bundle (mermaid 11.12.2, excalidraw
60+
0.18.0, exact pins), deterministic build, committed dist with sha256 + source
61+
fingerprint, drift tests, THIRD-PARTY-LICENSES.
62+
- `--to pdf|html|docx` output formats. HTML is one self-contained file (inline
63+
SVG diagrams, data-URI images, zero network refs, screen-readable). DOCX is a
64+
content-fidelity export with diagrams embedded as 300dpi PNGs and alt text.
65+
- Per-image directives: `![x](a.png){width=full|50%|3in}` and
66+
`{page=landscape|portrait}`.
67+
- Conservative auto-landscape: wide, small-text, diagram-like images get their
68+
own vertically centered landscape page (aspect ≥ 1.8, width over ~2.5x the
69+
content box, diagram-ish alt word). Directives override in both directions.
70+
- `--strict` for CI: missing images, remote images, out-of-tree image reads,
71+
oversized files, and non-regular files fail the run instead of degrading to
72+
placeholders.
73+
- `docs/howto-diagrams-and-formats.md`: the full walkthrough, fences to formats.
74+
75+
#### Changed
76+
- Typography scale: 12pt body, 26pt h1, 56pt poster cover with 13pt meta, 12pt
77+
TOC entries, larger code and tables. Auto-hyphenation is off so copy-paste
78+
yields clean words.
79+
- Local images inline as data URIs with byte-probed dimensions and never
80+
truncate; oversized photos downscale to print resolution at inline time;
81+
repeated images are read once.
82+
- TOC links resolve in every format (headings get real anchor ids); the screen
83+
layer hides print-only page-number dots in HTML output.
84+
- Remote images are blocked with a visible placeholder unless `--allow-network`
85+
is passed; out-of-tree image reads (including via symlink) warn loudly.
86+
- `make-pdf preview` prints a note when the document contains fences or local
87+
images that only `generate` renders fully.
88+
89+
#### Fixed
90+
- Relative image paths render correctly in PDFs (previously resolved against the
91+
wrong base and could show as broken boxes).
92+
- Fenced code inside lists survives the render byte-for-byte; indented fences
93+
keep their list placement.
94+
- Documents containing `$&`-style sequences in diagram labels render exactly;
95+
Windows drive-letter image paths resolve as local files; malformed
96+
percent-encoded image URLs degrade gracefully instead of failing the run.
97+
- Per-side margins (`--margin-left` etc.) are honored on documents containing
98+
landscape pages.
99+
100+
#### For contributors
101+
- 68 new free-tier gates (fence extraction, image policy, landscape promotion
102+
with negative fixtures, format contracts, bundle drift) plus a paid gate-tier
103+
/diagram triplet test and a periodic authoring-quality judge.
104+
- make-pdf-gate CI now covers `lib/diagram-render/**` and the drift test; the
105+
committed bundle is pinned to LF in .gitattributes.
106+
- Fixed the `operational-learning` E2E fixture (bin scripts now ship with the
107+
lib module they import).
108+
3109
## [1.57.10.0] - 2026-06-10
4110

5111
## **Codex review now runs by default everywhere it matters.**

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ Each skill feeds into the next. `/office-hours` writes a design doc that `/plan-
206206
| `/autoplan` | **Review Pipeline** | One command, fully reviewed plan. Runs CEO → design → eng review automatically with encoded decision principles. Surfaces only taste decisions for your approval. |
207207
| `/spec` | **Spec Author** | Turn vague intent into a precise, executable spec in five phases (why, scope, technical with mandatory code-reading, draft, file). Codex quality gate before file (blocks below 7/10), fail-closed secret redaction, dedupe against existing issues, archive to `$GSTACK_STATE_ROOT/projects/$SLUG/specs/` for team-corpus recall. `--execute` spawns `claude -p` in a fresh worktree; `/ship` auto-closes the source issue on merge. Plan-mode aware. |
208208
| `/learn` | **Memory** | Manage what gstack learned across sessions. Review, search, prune, and export project-specific patterns, pitfalls, and preferences. Learnings compound across sessions so gstack gets smarter on your codebase over time. |
209+
| `/make-pdf` | **Publisher** | Markdown in, publication-quality document out. Mermaid and excalidraw fences render as vector diagrams, fully offline. Images scale to the page and never truncate; wide diagrams get their own landscape page. `--to html` emits one self-contained file, `--to docx` a Word doc. |
210+
| `/diagram` | **Diagram Maker** | English in, editable diagram out. Emits a triplet: mermaid source, `.excalidraw` you can open and edit on excalidraw.com (hand-drawn style), and rendered SVG/PNG. Zero network. Embed the source in markdown and `/make-pdf` renders it. |
209211

210212
### Which review should I use?
211213

@@ -429,6 +431,7 @@ Other references: [docs/gbrain-sync.md](docs/gbrain-sync.md) (sync-specific guid
429431
| Doc | What it covers |
430432
|-----|---------------|
431433
| [Skill Deep Dives](docs/skills.md) | Philosophy, examples, and workflow for every skill (includes Greptile integration) |
434+
| [Diagrams & Document Formats](docs/howto-diagrams-and-formats.md) | Mermaid/excalidraw fences in PDFs, image sizing and safety defaults, `--to html\|docx`, `/diagram` triplets |
432435
| [Builder Ethos](ETHOS.md) | Builder philosophy: Boil the Ocean, Search Before Building, three layers of knowledge |
433436
| [Using GBrain with GStack](USING_GBRAIN_WITH_GSTACK.md) | Every path, flag, bin helper, and troubleshooting step for `/setup-gbrain` |
434437
| [GBrain Sync](docs/gbrain-sync.md) | Cross-machine memory setup, privacy modes, troubleshooting |

TODOS.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,3 +2377,41 @@ Pre-existing in `auq-sdk-capture.ts` — affects `skill-e2e-ship-section-loading
23772377
path to the fixture during the run.
23782378

23792379
**Effort:** S (human ~3h, CC ~30min). **Depends on:** None.
2380+
2381+
### P3: Content-hash diagram render cache for make-pdf
2382+
2383+
**What:** Cache rendered diagram SVG/PNG in `~/.gstack/cache/diagram-render/`,
2384+
keyed on `sha256(fence source + bundle version + render options)`, so repeat
2385+
`make-pdf` runs skip the browse render tab for unchanged diagrams.
2386+
2387+
**Why:** Every run currently re-renders every fence (~150-300ms each). Docs with
2388+
10+ diagrams pay seconds per iteration during write-preview loops. Codex
2389+
outside-voice flagged the missing cache story during the eng review of the
2390+
diagram engine plan (2026-06-11, D7).
2391+
2392+
**Context:** The diagram-render bundle ships a `BUILD_INFO.json` with a content
2393+
hash (see `lib/diagram-render/`) — use that as the bundle-version cache key
2394+
component so bundle bumps invalidate cleanly. Invalidation surface is the main
2395+
risk: stale renders after a mermaid theme change must not survive. Only worth
2396+
building once users hit multi-diagram docs; wedge perf is fine without it.
2397+
2398+
**Effort:** S (human ~1d, CC ~30min). **Depends on:** diagram engine wedge
2399+
shipping (lib/diagram-render bundle versioning).
2400+
2401+
### P3: Dedupe the make-pdf e2e gate-test harness
2402+
2403+
**What:** Five e2e files (`combined-gate`, `emoji-gate`, `diagram-gate`,
2404+
`landscape-gate`, `format-gate`) each hand-roll the same prerequisite probe
2405+
(binary/browse/poppler checks with CI hard-fail vs local skip), mkdtemp/rm
2406+
lifecycle, and child-timeout constants. Extract a shared
2407+
`make-pdf/test/e2e/helpers.ts` (prerequisites(), withWorkDir(), runGenerate()).
2408+
2409+
**Why:** Review-army maintainability finding on v1.58.0.0 — the boilerplate
2410+
diverges a little more with each new gate (diagram-gate now captures stderr
2411+
via Bun.spawnSync while the others use execFileSync), and a future fix to the
2412+
CI-hard-fail contract has to land five times.
2413+
2414+
**Context:** Deferred at ship time (D8.2) because it's test-only churn across
2415+
five green files at the tail of a release. Zero user-facing value; pure DRY.
2416+
2417+
**Effort:** S (human ~3h, CC ~20min). **Depends on:** None.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.57.10.0
1+
1.58.0.0

0 commit comments

Comments
 (0)