Using JGen
Generation records and regeneration
Every generated project records how it was made. jgen -r re-cuts it with a newer blueprint and classifies every file, so a person, git or an AI agent can do the alterations.
From 2.1.35, every project JGen generates carries a record of how it was made, and jgen -r can re-cut the
project with a newer blueprint and say exactly what changed. JGen does not merge anything itself. It gives a
person, git, or an AI agent the three facts an alteration needs: which pattern cut the project, with which
measurements, and what the pattern produces today.
Why
Generated projects drift. People and agents edit them; blueprints improve. Six months later nobody knows which lines came from the blueprint and which were written by hand, so taking a blueprint fix means guesswork. Tools like Copier answer this with a built-in update engine. JGen takes a smaller step: it records provenance and classifies files, and leaves the merging to tools that are now good at it.
The generation record
After a top-level blueprint generates a project, JGen writes <project>/.jgen/generation.json:
| Field | Content |
|---|---|
schema |
jgen.generation/1 |
generatedAt, jgenVersion |
when, and with which jgen-base |
blueprint |
name, the version in blueprint.json, and the Maven coordinates of the jar it came from |
projectDirectory |
the project folder name |
config |
the JGen config used |
inputs |
the validated inputs for the blueprint’s declared fields, before any hook added derived values (no destFolder) |
fileInputs |
FILE inputs (a workflow XML, a process definition), copied into .jgen/inputs/<field>/ so the project can be regenerated without the original file |
files |
every generated file and its SHA-256 |
extensions |
(2.5.1, only when there are any) the added extensions the project depended on, as kind:name@version; jgen -r needs them installed |
Child blueprints expanded inside a parent do not get their own record: regenerating the parent regenerates them.
Tool folders (.git, target, build, node_modules, .idea) are never hashed. Commit .jgen/ with the
project. To switch recording off, run JGen with -Djgen.record=false.
Regenerating
jgen -r ./output/orders # regenerates into ./output/orders.regenerated
jgen -r ./output/orders --into /tmp/orders-v2 # or somewhere else
jgen -r ./output/orders -c config/new.json # with a different config instead of the recorded one
JGen reads the record, finds the blueprint by name on the current classpath, validates the recorded inputs against the blueprint’s current contract (a renamed or newly required field fails here, with every error listed), and generates into the separate folder. The original project is never touched.
It then compares three states of every file: as first generated (the recorded hashes), as it is now, and as the current blueprint generates it.
| Classification | Meaning | Usual action |
|---|---|---|
SAFE_UPDATE |
unchanged by the user, changed by the blueprint | take the regenerated file |
KEEP_USER |
changed by the user, unchanged by the blueprint | keep the project’s file |
CONFLICT |
changed on both sides (or added on both sides with different content) | merge by hand or with an agent |
NEW_FILE |
new in the blueprint | add it |
SAFE_DELETE |
dropped by the blueprint, unchanged by the user | delete it |
DELETE_CONFLICT |
dropped by the blueprint, changed by the user | decide by hand |
USER_DELETED |
deleted by the user, still generated by the blueprint | usually leave it deleted |
Files that are the same on both sides, and files the user added, are not listed. The CLI prints the summary,
writes <project>.regeneration.json next to the regenerated project for tools and agents, and suggests a
git diff --no-index command.
From code
var report = new Regenerator().regenerate(projectDir, Path.of("/tmp/regen"), null);
report.only(Regenerator.Change.CONFLICT).forEach(c -> System.out.println(c.path()));
Regenerator.writeReport(report);
The classes are in org.chenile.jgen.provenance in jgen-base: GenerationRecord, ProvenanceRecorder and
Regenerator.
Limits
- The blueprint must still be on the classpath under the same name. Regeneration uses whatever version is there now; the record says which version cut the original.
- The recorded inputs must satisfy the blueprint’s current input contract. If a new version renames or adds a
required field, edit
.jgen/generation.json(it is plain JSON) and run again. - Projects generated before 2.1.35 have no record. Generate the same blueprint again with the original inputs to
create one, then copy its
.jgen/folder into the old project. Files the user has changed will then show as user changes, which is accurate.