JGen Chenile code generator

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.