graphcompose-ai-flow · v0.21.0 · installable harness

AI doesn't draw PDFs.
It compiles them.

Install the harness into your coding agent, drop in a document reference, and ask Claude Code, Codex or Gemini CLI to recreate it. The agent generates, renders, compares against the reference and iterates until the template is ready for your approval — and what comes out is maintainable GraphCompose Java, not a one-shot drawing.

Five columns. The user supplies a visual reference and a plain-language request. The host agent — Claude Code or Codex — owns the model loop: reasoning, vision, context, filesystem, shell and code editing. GraphCompose AI Flow, the installable harness, holds four workflow skills, versioned GraphCompose knowledge, six deterministic tools, a bounded iteration loop with gates, and the workspace artifacts. The GraphCompose engine compiles and renders. The output is a maintainable Java template, a typed spec and data JSON, versioned revisions, PDF and PNG previews, and a published template bundle.
The harness sits between the host agent and GraphCompose. It owns the workflow, the versioned API knowledge and the gates; the host owns the model. Click to open full size.

01 · the loop, as it actually ran

Watch 31 revisions arrive.

Three runs, replayed revision by revision. The left panel is the reference the harness was given; the right is what it rendered; the rail underneath is every revision it took to get there, failures included. Nothing in either frame is regenerated imagery — each step is the render that revision actually produced, read back off disk.

Invoice

flowing table, repeated header, page numbering

12 revisions 113 min approved at r012

12 revisions, 113 minutes.

The first two did not compile, which is the point of recording them: revision-001 and revision-002 are FAILED and still on disk, with what broke and what was tried. The last two were plain sentences with no instructions attached.

the dividers disappeared, for some reason

translated from the user’s Russian

Professional sidebar CV

photo, sidebar, timeline rail

10 revisions 120 min approved at r010

10 revisions, 120 minutes.

From a screenshot and the sentence “Create this screenshot as a GraphCompose template.” The corrections in the second half are the ones worth reading: initials that must derive from the name rather than be hardcoded, a LinkedIn href that has to apply to the icon as well as the text, a timeline rail that should not run above its first marker.

Charcoal and gold CV

dark sidebar, rated skill rows, timeline rail

9 revisions approved at r009

9 revisions, and the ninth was one sentence.

The loop declared itself ready at revision-008, after seven passes that each named one thing and fixed it — calibrate the type scale, drop the main column’s uniform 9px offset, seat the skill bullets in their own column. Then one sentence closed it. No duration is quoted here: the session stayed open across a long gap, so the telemetry measures the window rather than the work.

the timeline ring should close over the rail, not let it pass through

translated from the user’s Russian

All three were published as standalone bundles — template, typed spec, data JSON, assets, previews — which is what the harness exists to produce. The same recordings are in the README as GIFs, and the revision model behind the rail is in docs/revision-model.md.

02 · one request, measured

How close does one request get?

A first render is never right. What matters is how close one request gets, and what closing the rest costs. Both runs below are the reference, what one request produced with no further input, and the result after the corrections.

Two-column CV

photo, navy sidebar, timeline rail

5 revisions unattended 2 corrections 8 in total 77 min

Reference
Two-column CV reference
After one request
Two-column CV after one request
After 2 corrections
Two-column CV after corrections

Both about the timeline: the rail overran its markers, and a job title drifted off centre.

Twice the loop could not tell a layout fault from a painting fault, so it wrote a probe — a throwaway document that renders one arrangement and measures the pixels — and settled it. Both findings are recorded observations now, with probes that re-confirm them.

Single-column CV

display serif, proportional skill bars, icon rows

8 revisions unattended 3 corrections 11 in total

Reference
Single-column CV reference
After one request
Single-column CV after one request
After 3 corrections
Single-column CV after corrections

Three plain sentences about what looked wrong, none explaining how to fix it.

just the certification and achievement divider — vertical

translated from the user’s Russian

measured by the harness itself

create from the reference 68 min 280.4k output 61.0M cache read
first correction 7 min 25.2k output 16.0M cache read
second correction 10 min 36.4k output 21.8M cache read
approve and publish 2 min 8.1k output 6.5M cache read

A correction costs roughly a tenth of the original run.

Neither run was pixel-perfect from one request, and the pixel-similarity figure stayed unimpressive in both: the references are rasterised in typefaces no bundled family reproduces, so glyph edges dominate the comparison. The images are the evidence; the score is not.

03 · getting it

Install it into the agent you already use.

There is no service to sign up for and nothing to run alongside your editor. The harness installs into the host as three packagings of the same runtime, and after that the skills fire from plain sentences.

Claude Code

a plugin
/plugin marketplace add DemchaAV/graphcompose-ai-flow
/plugin install graphcompose-flow@graphcompose

Then, once, inside the installed copy — ~/.claude/plugins/cache/graphcompose/graphcompose-flow/<version>/ — run npm run setup: two of the tools are TypeScript compiled into dist/, which is not committed. Again after every /plugin update, or let the first preflight of a run do it.

docs/plugin-installation.md

Codex

four skills
git clone https://github.com/DemchaAV/graphcompose-ai-flow
cd graphcompose-ai-flow
npm run setup
node adapters/codex/install.mjs

Copies the runtime to ~/.codex/graphcompose-flow/<version>/, so the clone is not needed afterwards — move it, rename it or delete it and the skills keep working.

adapters/codex/README.md

Gemini CLI

an extension
git clone https://github.com/DemchaAV/graphcompose-ai-flow
cd graphcompose-ai-flow
npm run setup
node adapters/gemini/install.mjs

Gemini takes extensions rather than plugins, so this writes one to ~/.gemini/extensions/graphcompose-flow/ — commands, skill, hooks and runtime inside it. Restart, then gemini extensions list.

adapters/gemini/README.md

You need Node 20+, Java 21+, Maven and ImageMagick, plus a Java project that pins io.github.demchaav:graph-compose — the version in your build file decides which skill pack the agent authors against. npm run setup checks the whole toolchain before it builds anything.

Then say what you want.

Every render rewrites current.pdf next to the project, so one open window follows the whole run — you watch the layout arrive instead of hunting for the newest file under revisions/. It needs a viewer that reloads on change and does not hold the file open.

04 · the shape of it

Three layers, and a boundary each one respects.

The host agent already has the model, the reasoning, the vision and the shell. Rebuilding any of that would duplicate it and add nothing. What a host does not have is how to reconstruct a document out of GraphCompose primitives, when a change needs a new revision, and what “close enough to the reference” means. That is the whole of this project's job.

  1. You

    what you hand over

    • Visual reference a screenshot, a PDF, a design image
    • A sentence “Create this in GraphCompose”, “Make the sidebar wider”
  2. Host agent

    Claude Code · Codex · Gemini CLI — outside the harness, owns the model loop

    • Reasoning plans, decomposes, decides
    • Vision reads the reference image
    • Context tracks state and artifacts
    • Filesystem + shell runs the tools, edits the Java

    The project deliberately builds none of this: no LLM API client, no MCP server, no standalone runtime.

  3. GraphCompose AI Flow

    the installable harness — this repository

    Anything a script can decide is decided by a script; anything the model decides is written to a file a script can read back.

  4. GraphCompose

    the compile target and rendering engine

    • Semantic primitives sections, rows, tables, images, lists, charts
    • Layout engine flow, alignment, constraints, styles
    • Pagination page breaks, repeated headers, numbering
    • Layout snapshot post-layout geometry, so a mismatch is arithmetic
  5. Output

    what you are left holding

    • A maintainable Java template semantic primitives, one named method per region
    • Typed spec + data JSON content separated from layout
    • Revisions and assets every pass kept, statuses owned by the tool
    • PDF and PNG previews the render, and the page it was diffed as
    • A published bundle self-contained: Java, data, assets, pom — no dependency on this harness

    A human APPROVE is required before anything is published.

Reuse is checked before reconstruction. “An invoice like the one we did before” consults the catalog of published bundles first — a match is a file copy into your project, not the loop. The two cost different orders of magnitude, so the question is asked before any of the above runs. scope-routing.md

The full split, including what this project deliberately does not build, is in docs/architecture.md.

05 · how it works

Four skills. One bounded loop.

You say what you want; a workflow skill owns the request end to end. Behind it the same ordered stages run — some are the model's judgement, most are a CLI, and the last two are gates that decide with an exit code, not an opinion. This is the moat against prompt-and-hope.

  1. 01 route llm scope the change; open/route the revision
  2. 02 resolve-version tool resolve GraphCompose version + skill pack
  3. 03 validate-skills tool validate the skill pack against the target API
  4. 04 visual-analysis llm ratios, anchors, regions from the reference
  5. 05 architecture llm map regions to primitives + theme tokens
  6. 06 asset-resolve tool resolve Iconify icons + Google Fonts
  7. 07 template-authoring llm write generated-template.java + data spec
  8. 08 compile-render tool compile + render + diff + loop verdict, one call <- mechanical scripts/render-and-diff.mjs
  9. 09 visual-evaluation gate parity classification vs reference/parent
  10. 10 iterate gate ask whether the loop may take another pass (0 ready / 2 revise / 3 blocked) scripts/iterate-status.mjs

the gate each scope ends on

  • exact-diff Binary pixel diff against the parent revision. vs the parent
  • region-diff Region-aware pixel diff against the parent revision. vs the parent
  • visual-review Layer-by-layer review against the reference image. vs the reference

what ends the loop

  • 8 iterations
  • 3 build failures in a row
  • 3 attempts at one mismatch

Reaching a bound with work still open is its own exit code, not a silent stop. 6 scopes route through these stages; 7 failure categories are declared for what can go wrong.

Which stages a scope runs, the gate it ends on and the loop bounds are declared once in config/pipeline.json, which this list is generated from at build time. The skills that run them live in skills/workflows/.

06 · the moat

Why this isn't prompt-and-hope.

Most AI document tools are a prompt with a hopeful disposition. This one is closer to a build pipeline: the work has contracts, outputs go through tests, regressions get caught, and nothing ships without a human-signed APPROVE.

07 · the target language

GraphCompose is the compile target. The harness teaches the agent to speak it.

GraphCompose is a Java library for building PDFs out of semantic primitives: sections, rows, tables, themes, layer stacks, shape containers, layout snapshots and visual regression checks. It treats a document as composable nodes, not as a canvas of coordinates.

This project is the harness that targets GraphCompose as a compile target. The agent receives a visual reference and reconstructs it out of those primitives. The output is not a one-shot PDF — it is a maintainable Java template you can read, diff and revise, with the content in a JSON file beside it.

The library is resolved from Maven Central as io.github.demchaav:graph-compose, and the version your build file pins decides which knowledge the agent may use. Four packs ship — 1.6.x, 1.7.x, 1.9.x, 2.2.x — and the current one carries a source-generated allow-list plus a loading map that answers given this task, which of its 16 files do I open? A line with no pack stops the run. skills/versions/

the artist paradigm one-shot · unreviewable
canvas.moveTo(72, 720);
canvas.drawString("INVOICE");
canvas.rect(72, 600, 460, 80);
the compiler paradigm typed · testable · revisable
document.pageFlow(page -> page
        .name("Invoice")
        .spacing(BASE_GAP)
        .addSection("Terms", this::renderTerms));

section.softPanel(theme.surfaceMuted(), 6.0, 8.0)
        .addParagraph(p -> p
                .text(data.terms())
                .textStyle(theme.body()));

The shape the fixtures compile and render against 2.2.0 in CI: one named method per visible region, geometry derived from a base constant, content out of the data file.