| name | migrate-bpmn-to-code-v1-to-v2 |
| argument-hint | [path/to/scan] |
| description | Migrate generated API usage from bpmn-to-code v1.1.0 to v2.0.0. Use when the user asks to 'upgrade to v2', 'migrate from v1 to v2', 'fix TaskTypes references', or 'update bpmn-to-code API calls after upgrade'. |
| allowed-tools | Read, Glob, Grep, Edit, AskUserQuestion |
Skill: migrate-bpmn-to-code-v1-to-v2
Update user source code that references the v1.1.0 generated Process API to use the v2.0.0 API shape.
IMPORTANT
- Read-only until confirmation: Do not edit any files until the user explicitly approves the proposed changes.
- Never modify generated Process API files (files starting with
// Generated by bpmn-to-code).
- Never modify BPMN model files (
.bpmn).
- If no generated Process API files are found, abort and tell the user to run code generation first (
./gradlew generateBpmnModelApi or mvn io.miragon:bpmn-to-code-maven:generate-bpmn-api).
- Variable path changes (
ProcessApi.Variables.VAR → ProcessApi.Variables.ElementName.VAR) cannot be resolved automatically — the element name must be looked up in the regenerated API. Flag these for manual review.
- BPMN property rename (
additionalVariables → additionalInputVariables / additionalOutputVariables) cannot be resolved automatically — BPMN files must not be edited by the skill. Flag occurrences for manual rewrite.
- Preserve existing code formatting and style.
Breaking Changes Reference
| v1.1.0 | v2.0.0 |
|---|
ProcessApi.TaskTypes.FOO | ProcessApi.ServiceTasks.FOO |
ProcessApi.Timers.BpmnTimer (nested type) | import io.miragon.bpmn.runtime.BpmnTimer (standalone) |
ProcessApi.Errors.BpmnError (nested type) | import io.miragon.bpmn.runtime.BpmnError (standalone) |
ProcessApi.Variables.VAR_NAME | ProcessApi.Variables.ElementName.VAR_NAME (manual — look up the element in the regenerated API; direction is now carried by the wrapper type, not the path) |
<camunda:property name="additionalVariables" value="a,b"/> (BPMN) | <camunda:property name="additionalInputVariables" .../> and/or <camunda:property name="additionalOutputVariables" .../> (manual BPMN edit — skill flags occurrences, cannot rewrite) |
useVersioning = true/false (Gradle / Maven plugin config) | removed — delete the line |
const val PROCESS_ID: String + Elements/Messages/Variables/Signals/Compensations/CallActivities.X: String | typed wrappers: ProcessId, ElementId, MessageName, VariableName, SignalName (Kotlin data class, imported from io.miragon.bpmn.runtime.* — shipped by the bpmn-to-code-runtime artifact) |
Typed leaf constants
In v2.0.0 every leaf identifier except ServiceTasks.* and PROCESS_ENGINE is wrapped in a type-safe class:
PROCESS_ID → ProcessId
Elements.*, Compensations.* → ElementId
CallActivities.* → ProcessId
Messages.* → MessageName
Signals.* → SignalName
Variables.<Element>.* → VariableName.Input, VariableName.Output, or VariableName.InOut — sealed interface with direction-aware subtypes
ServiceTasks.* and PROCESS_ENGINE stay as const val String because Kotlin/Java annotation arguments must be compile-time constants.
Every wrapper overrides toString() to return its underlying string. Consumers now have three options at each call site, in preference order:
- Retype (preferred): update the consumer API signature to accept the wrapper (e.g.
fun sendMessage(name: MessageName)). No unwrap needed.
- Rely on
toString(): for string templates, logging, println, or any Any?-accepting API, the wrapper is implicitly converted to its string form. No unwrap needed.
- Unwrap: append
.value (Kotlin) or .value() (Java record) only where a strict String parameter is declared and retyping isn't available.
Call sites that previously used these constants in annotation arguments (other than ServiceTasks) cannot be trivially migrated — Kotlin value-class instances and Java records are not compile-time constants.
Instructions
Step 1 – Locate generated Process API files
- Use Glob to find candidate files:
**/*ProcessApi*.kt and **/*ProcessApi*.java.
- Read each candidate and confirm it starts with
// Generated by bpmn-to-code.
- For each confirmed API file, extract:
- The fully qualified package (e.g.
de.emaarco.example)
- The API class/object name (e.g.
NewsletterSubscriptionProcessApi)
- Whether it contains a
ServiceTasks object (confirms it is already v2)
- If no API files are found, abort with the message in the IMPORTANT section.
- If all found API files already use
ServiceTasks and lack nested BpmnTimer/BpmnError types, inform the user that the generated API is already v2 and only source code references may need updating.
Step 2 – Determine scan scope
- If
$ARGUMENTS contains a directory path, use that as the scan root.
- Otherwise, default to
src/main/ in the project root.
- Scan
**/*.kt and/or **/*.java files depending on what exists.
- Exclude generated API files and test directories (
src/test/) unless the user requests test migration.
Step 3 – Scan for removed plugin parameters in build files
Search for useVersioning in build configuration files:
- Gradle:
build.gradle.kts and build.gradle — look for useVersioning inside any bpmnToCode { } or plugin configuration block
- Maven:
pom.xml — look for <useVersioning> inside the bpmn-to-code plugin <configuration> block
The parameter was removed entirely. The fix is to delete the line — no replacement is needed.
If any occurrences are found, include them in the migration plan (Step 5) and apply the deletion in Step 6.
Step 4 – Scan for v1 patterns in source code
Search the scoped files for the following patterns and record file path, line number, and full line for each match:
A. TaskTypes references
- Pattern:
\.TaskTypes\.
- Replacement:
.ServiceTasks.
- Example:
ProcessApi.TaskTypes.SEND_MAIL → ProcessApi.ServiceTasks.SEND_MAIL
B. Nested BpmnTimer type usage
- Pattern:
\w+ProcessApi\.Timers\.BpmnTimer or \.Timers\.BpmnTimer used as a type reference (in variable declarations, function parameters, return types)
- Replacement: use
BpmnTimer directly and add import import io.miragon.bpmn.runtime.BpmnTimer
- Example:
val t: NewsletterSubscriptionProcessApi.Timers.BpmnTimer → val t: BpmnTimer
C. Nested BpmnError type usage
- Pattern:
\w+ProcessApi\.Errors\.BpmnError or \.Errors\.BpmnError used as a type reference
- Replacement: use
BpmnError directly and add import import io.miragon.bpmn.runtime.BpmnError
D. Variables flat references (flag only — do not auto-fix)
- Pattern:
ProcessApi\.Variables\.[A-Z_]+ where [A-Z_]+ is not a sub-object name
- These are now nested under an element object (e.g.
Variables.StartEventFoo.VAR_NAME). Direction is carried by the variable's type (VariableName.Input / .Output / .InOut), not by the path. Flag each occurrence for manual review; the user must open the regenerated API to find the element the variable belongs to.
E. Legacy Inputs / Outputs paths from earlier v2 iterations (auto-fix when direction is clear)
- Pattern:
ProcessApi\.Variables\.[A-Z][a-zA-Z0-9]*\.(Inputs|Outputs)\.[A-Z_]+
- Early v2 betas nested variables as
Variables.Element.Inputs.NAME / Outputs.NAME. The final shape drops the Inputs / Outputs segment; direction now lives in the type.
- Replacement:
ProcessApi.Variables.<Element>.<NAME> — drop the .Inputs / .Outputs segment.
- Safe to auto-fix: the element name is preserved and the leaf name is preserved; only the middle segment is removed.
F. Typed leaf constants used where a String is expected (flag only)
In v2.0.0 leaf constants under PROCESS_ID, Elements, CallActivities, Messages, Compensations, Signals, and Variables are wrapper types, not String. Every wrapper overrides toString(), so:
- String templates / logging /
println / Any?-accepting APIs: no change required — toString() handles the conversion implicitly.
- Strict
String parameter types (val s: String = ..., Map<String, String>, third-party APIs with String arg): require either unwrap (.value / .value()) or retyping the consumer API.
The skill cannot reliably distinguish these cases without type information. Flag each occurrence for manual review.
- Patterns to search (examples — adapt to the detected
ProcessApi class name):
ProcessApi\.PROCESS_ID
ProcessApi\.Elements\.[A-Z_]+ (excluding sub-object name qualifiers)
ProcessApi\.CallActivities\.[A-Z_]+
ProcessApi\.Messages\.[A-Z_]+
ProcessApi\.Signals\.[A-Z_]+
ProcessApi\.Compensations\.[A-Z_]+
ProcessApi\.Variables\.[A-Z][a-zA-Z0-9]*\.[A-Z_]+
- Skip matches where
.value (Kotlin) or .value() (Java) already follows the constant — those are already unwrapped. In practice: if the matched span is immediately followed by .value or .value(), the call site is migrated and no action is needed.
- If the match appears inside a Kotlin/Java annotation argument (e.g.
@JobWorker(type = ...), @MessageCorrelationKey(...)), flag as BLOCKED — annotation arguments cannot reference value-class / record instances, so the consumer needs a different approach (e.g. keep a separate const val String constant).
G. Legacy additionalVariables in BPMN files (flag only — do not auto-fix)
- Scope:
**/*.bpmn files (all directories; do not exclude test BPMNs — they model real processes too)
- Pattern:
name="additionalVariables" (literal, inside <camunda:property> / <operaton:property>)
- The property is no longer extracted. The generated API will silently drop these variables until the property is renamed. Flag each occurrence with file and line, and instruct the user to split the comma-separated value list into
name="additionalInputVariables" (values read by the element) and/or name="additionalOutputVariables" (values produced by the element). The skill must not edit BPMN files per the IMPORTANT section.
Step 5 – Present the migration plan
Group findings by file and present a summary:
## Migration Plan
### src/main/kotlin/com/example/MyWorker.kt
| Line | Pattern | Change |
|------|---------|--------|
| 14 | .TaskTypes.SEND_MAIL | → .ServiceTasks.SEND_MAIL |
| 28 | .Timers.BpmnTimer (type ref) | → BpmnTimer + import |
**Imports to add:**
- `MyWorker.kt` → `import io.miragon.bpmn.runtime.BpmnTimer`
### ⚠️ Manual review required
| File | Line | Pattern | Reason |
|------|------|---------|--------|
| src/main/kotlin/com/example/MyWorker.kt | 42 | .Variables.SUBSCRIPTION_ID | Variables are now nested per-element — check generated API for correct path |
**Total: 2 automatic replacements in 1 file, 1 item requires manual review**
If nothing is found, report that no v1 API patterns were detected.
Ask: "Apply these replacements? (yes / skip [file-or-line] / cancel)"
- yes — proceed to Step 6
- skip — remove the specified entries, show updated plan, ask again
- cancel — stop without changes
Step 6 – Apply replacements
For each approved change:
- useVersioning in build files: Delete the entire line containing
useVersioning from build.gradle.kts, build.gradle, or pom.xml.
- TaskTypes → ServiceTasks: Use Edit to replace
.TaskTypes. with .ServiceTasks. on the matched line.
- Nested type references: Replace the qualified type path with the simple class name, then add the import after the last existing
import line in the file.
- Kotlin:
import io.miragon.bpmn.runtime.BpmnTimer / import io.miragon.bpmn.runtime.BpmnError
- Java:
import io.miragon.bpmn.runtime.BpmnTimer; / import io.miragon.bpmn.runtime.BpmnError;
- Skip if the import already exists.
- Legacy
Inputs / Outputs paths (Pattern E): drop the .Inputs. / .Outputs. segment while preserving the element and leaf names. Example: Variables.StartEventX.Inputs.FOO → Variables.StartEventX.FOO.
- Process files one at a time, top-to-bottom by line number.
Step 7 – Report
- Summarize all changes made (replacements per file).
- List any items flagged for manual review with a brief explanation.
- Suggest the user verify compilation:
- Gradle:
./gradlew compileKotlin or ./gradlew compileJava
- Maven:
mvn compile
- Point the user to the v2.0.0 changelog for the full list of breaking changes and new features. The "Directional Variables via typed wrappers" section at the top of the changelog explains how direction moved into the wrapper type and how
toString() makes .value optional in most contexts.
- Link to ADR 015 for the rationale behind removing
additionalVariables and always splitting Variables.<Element>.