Generate a complete, runnable Spring Boot application on top of the flow-driven-domain (FDD)
library. FDD turns a domain into a process-centric one: actions/states/transitions are declared in
a workflow JSON, a FlowEngine drives the process, and flow history is persisted with the aggregate.
Your job: take the user’s process description, design the state machine, confirm it, then emit a
full project that compiles and runs. The framework stack is fixed: io.github.progmodek:flow:1.1.0,
Spring Boot 4.1.0, Java 25, Postgres (jsonb), Flyway. The build tooling, however, adapts to where
the skill is invoked — a new module inside the caller’s existing Gradle/Maven build, or a standalone
project — so read references/build-files.md and pick the layout in Step 4 before writing any build
files. Reactive is out of scope — always generate the imperative (blocking) stack.
Read the reference files as you go — do not generate from memory. In particular, copy the exact
import statements from the “Exact imports” section at the top of references/fdd-api.md — the
framework packages are counter-intuitive (FlowAction/FlowState/FlowType live in
com.progmod.flow.domain.service.parser.definition, and ActionDelegate/SystemActionDelegate in
com.progmod.flow.domain.service.delegate, not in domain.model / domain.delegate). Guessing
these is the #1 cause of a generated app failing to compile.
references/fdd-api.md — the library’s public API (interfaces, engine, enums, persistence, rules).references/workflow-json.md — the workflow JSON schema and runtime semantics.references/flowable-direct-example.md — a complete Flowable-direct app, file by file.references/baseflow-example.md — a complete BaseFlow app, file by file (the deltas).references/build-files.md — the four build layouts (Gradle/Maven × standalone/module), their
build files, wiring, and verify commands. Read before writing build.gradle/pom.xml.The user gives a natural-language description. Design the flow yourself, then confirm — don’t walk them through a long questionnaire. From the description infer:
TO_PREPARE, UNDER_REVIEW, APPROVED, EXPIRED, …).USER (invoked via REST) or SYSTEM (fired automatically by
a timer/retry — timeouts, auto-notifications, escalations).If the description is genuinely ambiguous on something that changes the machine (e.g. “should an unreviewed request auto-expire, and after how long?”), ask a short, targeted question — don’t ask about things you can reasonably default.
There are two usage models; the user must pick (see fdd-api.md §1). Ask which they want, briefly
explaining the trade-off:
implements Flowable<ID> carrying business fields +
invariants, persisted whole. Pick this when there’s real domain data/rules (the common case; it’s
what the order-preparation POC uses). → follow references/flowable-direct-example.md.BaseFlow (id = String) is the workflow instance
and business data lives in a variables map. Pick this when the process is essentially a
standalone workflow with no rich domain. → follow references/baseflow-example.md.Recommend Flowable-direct when the description clearly has domain data + invariants; recommend BaseFlow when it’s a thin orchestration/approval-style flow. Let the user decide.
Present a compact spec before writing any files: aggregate + fields, the state list (mark initial & terminal), the action list (mark USER/SYSTEM), and the transition table (action, from → to, branches/retries/timers). Keep it skimmable. Get a yes (or adjust), then generate.
Also confirm (or state your defaults and proceed): base package (default
com.example.<domain>), app/root name, DB schema name, and the HTTP base path.
Before writing any build files, work out where this app is being generated, because it decides whether you emit a standalone project or wire a module into the caller’s existing build. Look at the target directory and its parents:
settings.gradle/settings.gradle.kts — or a build.gradle(.kts) +
gradlew — at the target dir or an ancestor): generate the app as a Gradle subproject of that
build. No own wrapper, no own settings.gradle; add include '<module>' to the root settings.pom.xml with <modules> / <packaging>pom</packaging>): generate the
app as a Maven submodule — a module pom.xml plus a <module> entry in the reactor POM.Auto-detect, then state what you found and are about to do (“this is a Gradle build — I’ll add
<app> as a subproject”) so the user can override (e.g. force standalone, or pick Gradle over Maven).
The counterpart to settings.gradle/pom.xml wiring, the exact build-file templates for all four
layouts, and their per-layout verify commands live in references/build-files.md — read it now and
follow the matching layout in the next step. Everything below the build files (Java, resources,
migration, docker-compose, README) is identical across layouts.
Create the app under the chosen location (a module directory inside the build, or a standalone
./<app-name>/). Mirror the chosen example’s structure, wiring, and idioms exactly — only the domain
specifics change. Generate every file:
references/build-files.md:
the module build.gradle or pom.xml, plus the root-build wiring (include/<module>) for a
subproject/submodule, or the full standalone build (with Gradle wrapper only for standalone
Gradle). Keep the exact framework/plugin/dependency versions.*Application.java (@SpringBootApplication @EnableScheduling) and *Config.java (the
FlowRepository + FlowEngine beans).domain/flow/ — the *Action, *State, *FlowType enums.domain/ — the aggregate + entities (Flowable-direct) or nothing extra (BaseFlow).domain/delegate/ — one @Component ActionDelegate per action. Bean name = class name with
first letter lowercased, and it must equal the delegate string in the JSON.dto/ — request records (Jackson com.fasterxml.jackson.annotation.* namespace).infra/primary/ — the REST controller (one endpoint per USER action + create + GET) and an
ErrorHandler. infra/secondary/ — one working EventsPublisher (a LoggingEventPublisher
that reads the flow’s domain events and logs them). Always generate exactly one: it is the evidence
that publishing domain events is plug-in — the engine fans out to every EventsPublisher bean
after each action, so adding another sink (Kafka, an HTTP webhook, an outbox) is just another
@Component. Keep the generated one a log sink (no Kafka/broker) so the app runs on Postgres alone,
with a comment marking the body as the swap-point for a real destination. See fdd-api.md §8.resources/flow/<name>.json — the workflow JSON (validate against the workflow-json.md
checklist).resources/application.yaml — datasource + Flyway (own schema) + flow.* props.resources/db/migration/V1__Initial_version.sql — schema + aggregate table (id column type
matches the id type: uuid for UUID, varchar for BaseFlow) + the mandatory flow_task table.docker-compose.yml — copy from assets/docker-compose.yml (skip if the caller’s build already
ships one at the repo root that exposes Postgres on 5432; reuse it instead).README.md — fill in assets/README.template.md (states, actions, curl examples for each USER
endpoint, run instructions). For a module, note how to run it from the root build
(./gradlew :<module>:bootRun or mvn -pl <module> spring-boot:run).Confirm the app is actually runnable before handing off. The command depends on the layout (see the
“Verify” line for each layout in references/build-files.md):
# standalone Gradle: cd <app-dir> && ./gradlew compileJava --console=plain -q
# Gradle subproject: ./gradlew :<module>:compileJava --console=plain -q # from the build root
# standalone Maven: cd <app-dir> && mvn -q compile
# Maven submodule: mvn -q -pl <module> -am compile # from the build root
Fix any compilation errors. The usual suspects, in order of frequency:
package com.progmod.flow... does not exist) — re-check against the
“Exact imports” section of references/fdd-api.md. FlowAction/FlowState/FlowType →
...domain.service.parser.definition; ActionDelegate/SystemActionDelegate →
...domain.service.delegate. Do not add a dependency to “fix” a missing framework package.delegate string../gradlew build / mvn package) also works but needs no DB; bootRun needs Postgres up.Compile ≠ runs. A clean compile does NOT exercise Flyway, the datasource, or delegate wiring —
e.g. a missing spring-boot-flyway module lets the app start but silently skips all migrations, so
flow_task never gets created and the task consumer errors every second. When you can (Postgres
available), do a real smoke test: docker compose up -d, boot the app, and confirm the startup log
shows Migrating schema ... to version and no relation "flow_task" does not exist. If you can’t
boot it, tell the user the app is compile-verified but not run-verified, and give them the smoke-test
steps.
Then tell the user how to run it (docker compose up → the layout’s bootRun/spring-boot:run
command from references/build-files.md) and give a copy-pasteable curl walkthrough that drives the
process end to end (create → each USER action → GET to watch state evolve, noting where a SYSTEM
action fires automatically).
These come from fdd-api.md §10 — check them in the generated output:
delegate (lowerCamel of the class name). This is the #1 failure."initial": true; every state/action named in the JSON exists in the
enums; terminal states have "transitions": [].timer — that’s how the engine fires it. No
endpoint is generated for SYSTEM actions.flow_task table is always created in the migration (any timer/retry needs it).actionContext.put(ACTION_TRANSITION_VARIABLE, key)
with a key present in the transition’s result map; single-outcome transitions use
"result": {"success": "NEXT"} and the delegate sets nothing.SystemActionDelegate and may throw new DelegateException(code,msg)
to drive exceptions / retry transitions.@SpringBootApplication @EnableScheduling (no @EnableTransactionManagement
needed); the flow jar auto-configures the rest — there is no @Enable annotation for FDD.1.0.1; the current library is 1.1.0 — always generate
1.1.0.FlowRepository<T,ID> bean works — but the default and recommended path is
BasePostgresJsonRepository.