Install
$ agentstack add skill-axoniq-agent-skills-axon4to5-migrate-code ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
axon4to5-migrate-code
Goal
> Fully (or as most as possible) compiling, green-test codebase on AF5, same architecture as AF4. > No DCB. No new patterns. Legacy event storage preserved. > > Scope: code and configuration only. This skill migrates source code and wiring. It does NOT > migrate stored data — the event store contents (stored events) and tracking tokens are left untouched. > Migrating to AF5 with the legacy event-storage layout keeps those stores compatible as-is; moving data > to a new storage layout (e.g. DCB) is out of scope. > The migration preserves the project's existing configuration style: a Spring Boot > project stays on Spring auto-config (recipes use @Component / @Bean > idioms); a plain framework-configuration project stays on the direct > Configurer API (recipes use EventSourcingConfigurer / > MessagingConfigurer / CommandHandlingModule / EventSourcedEntityModule).
Available recipes (auto-listed)
Run bash scripts/list-recipes.sh from the skill root directory. Output format:
- file: references/recipes//RECIPE.md
id:
title:
description:
applicable: |
Inputs
framework(required): which Axon flavor to migrate. Currently supported values:axon,axoniq. Any other
value → STOP.
configuration(required): how the application wires Axon. Currently supported values:native,spring. Any
other value → STOP.
mode(required): what gets migrated in one invocation.single— one element (a class, e.g. an Aggregate). Requiressource.project— the whole application (default: current working directory).sourceignored.execution(optional, defaultinline): how the orchestrator runs its steps. Only meaningful formode=project—
for mode=single it has no observable effect.
inline— main session does discovery + recipe runs sequentially. NoAgenttool use.subagent— orchestrator MAY dispatch via theAgenttool: discovery →Exploresubagent, recipe sub-flow per
item → general-purpose subagent (parallel batches). Useful for project mode on large codebases.
source(required formode=single): hint identifying the thing to migrate (class name, file path, FQN).skip-openrewrite(optional, defaultfalse): whentrue, the orchestrator SKIPS Pre-step 2 (the OpenRewrite bulk pass) and goes straight to the mode-specific producer. Use this when (a) OpenRewrite Phase 1 has already been run separately on the tree, (b) the caller is exercising a recipe in isolation, or (c) the project is not built with Maven/Gradle so the OpenRewrite plugin is unreachable. Values:true/false. Any other value → STOP. The downstream recipe must still tolerate both AF4-shaped and partially-migrated sources (see each recipe's# Applicablepredicates).max-subagents(optional, default0): max parallelgeneral-purposesubagents for item processing in the drain loop.mode=projectonly — ignored formode=single.0= inline (no subagents, sequential).N > 0= dispatch up to N items simultaneously as subagents; BLOCKER_RESOLUTION always runs in main session regardless of this value. Any non-integer or value ` so the log stays auditable.
| Decision point | Auto action | |---|---| | Ambiguous recipe match (mode=single) | Pick first candidate by applicable score. | | Blocker | Auto-select the Option the recipe marked (Recommended); if none is marked, skip. A recommended migration path (e.g. saga stateful-rewrite) re-enters the recipe with that option id; skip/revert resolve in-place. See BLOCKER_RESOLUTION.md § Auto mode. | | Resume + selection-args mismatch | Args identical → auto-resume. Args differ → auto-start-over. | | Working tree mismatch on resume | Proceed; record ⚠️ auto: tree mismatch ignored in progress.md. | | OpenRewrite step completes | Immediately continue to mode-specific producer. Do NOT pause or end session. |
Durability
Load order — see § Recipe sub-flow. FLOW.md first, then DURABILITY.md (second). Defines state files under .axon4to5-migration/, hooks across pre-steps + queue + recipe results + caller decisions, and commit protocol. Reads progress.md on entry to decide resume vs fresh.
Pre-steps (common to every mode)
These run before any mode-specific logic — independent of whether mode=single, project, or anything added later.
- Parse — read
framework,configuration,mode,execution,skip-openrewrite,max-subagents,autofrom$ARGUMENTS.
- If
frameworkis missing or ∉ {axon,axoniq} → STOP and report unsupported framework. - If
configurationis missing or ∉ {native,spring} → STOP and report unsupported configuration. - If
modeis missing or ∉ {single,project} → STOP and report unsupported mode. executiondefaults toinlineif missing. If present and ∉ {inline,subagent} → STOP and report unsupported execution.skip-openrewritedefaults tofalseif missing. If present and ∉ {true,false} → STOP and report unsupported value.max-subagentsdefaults to0if missing. If present and not a non-negative integer → STOP.autodefaults tofalseif missing. If present and ∉ {true,false} → STOP.
- OpenRewrite — skipped entirely when
skip-openrewrite=true. Otherwise, internally invoke
axon4to5-openrewrite via the Skill tool, passing --framework $framework --commit false. Do NOT pass --commit true or omit --commit; DURABILITY's on:openrewrite-done hook owns the single combined commit. This is a step of this orchestrator, not a separate command. Idempotent — safe even on a partially-migrated tree. If it fails → STOP and report the failure (no gap-filling on a broken bulk pass). When skipped, surface that fact in the eventual report (Notes or Learnings) so the caller knows the queue ran against unprocessed AF4 (or already-partially-migrated) sources and the recipes did all the work themselves. auto=true: after this step returns (success or skip), immediately continue to the mode-specific producer — do NOT end the session or pause.
Only after pre-steps complete does the mode-specific producer below run.
Modes
single
Migrate ONE element (one aggregate, one event processor, etc.) using exactly one recipe from the list above.
Steps (after the common pre-steps):
- Match — map user's request +
sourceto ONE recipe in the auto-listed set. Primary signal: the catalog's
applicable block (surface predicates against $SOURCE — annotations / type markers). Fallback signal: id + title + description. If ambiguous → ask user via AskUserQuestion to pick (show title to the user; dispatch by id). If no applicable block matches and description is also unclear → STOP and report.
- Execute —
Readthe chosen recipe file under [references/recipes/](references/recipes/) (/RECIPE.md)
and execute it per the Recipe sub-flow ([FLOW.md](references/recipes/FLOW.md), already loaded). Recipe-local auxiliary files (examples, fixtures, supporting docs) live alongside it in the same / directory.
- Verify — behavior is preserved (no DCB, keep
AggregateBasedEventStorageEngine, etc.). - Report — render the report (see Queue flow § Render report).
MUST NOT:
- Run without all required parameters resolved to a supported value.
- Run multiple recipes in one invocation.
- Migrate more than the single source named by the user.
- Migrate anything outside the supported
(framework, configuration)matrix — the rest of the codebase stays untouched. - Introduce DCB or swap event storage engine.
project
Migrate everything in the working directory that any recipe in the catalog declares applicable. source is ignored.
Steps (after the common pre-steps):
Recipe loop — iterate recipes in discovery order. For each recipe:
- Discover — evaluate the recipe's
applicablepredicates across the codebase to produce candidate sources.
execution=inline→ orchestrator scans inline usingGrep/Glob/Read.execution=subagent→ dispatch oneExploresubagent for this recipe. Read-only — no edits.- Scan
.javaAND.kt, across all source roots. JVM projects are mixed and Kotlin files often sit undersrc/main/java/(and vice-versa) — never filter candidates by extension or source directory. Key on the recipe's Axon annotations, not on path. SeeDEFAULT.md § Source file conventions(theExploresubagent must be told this too).
- Enqueue — add
(recipe, source)candidates. Deduplication is recipe's concern. - Drain — exhaust all pending items for this recipe before advancing to the next:
max-subagents=0(default) → inline, main session, sequentially.max-subagents=N→ main session acts as coordinator. Dispatches up to N pending items simultaneously asgeneral-purposesubagents (singleAgentmessage per batch). Each subagent executes ONE recipe sub-flow and returns a result block (RESULT:line + Notes + aLearningsfield — complete authored entries, or an explicitnone —). The subagent authors the learning prose (it witnessed the run) but MUST NOT writelearnings.mditself — the coordinator is the single safe writer under parallelism and stamps the date + commit sha, relaying each entry verbatim (see DURABILITY § Proactive Learnings).- ✅ Success / ⏭ Rejected / ❌ Failure → main records result, immediately dispatches next pending item. No pause.
- 🚧 Blocker → BLOCKER_RESOLUTION in main session:
AskUserQuestionifauto=false; auto-skip ifauto=true. Resolved → re-dispatch same item to a new subagent. Not resolved → mark blocked, dispatch next pending item. - Main session never pauses unless waiting for user input on a blocker (
auto=false). - Fallback — if a subagent cannot be spawned, process inline and continue.
- Mark recipe done —
on:recipe-donehook records status inprogress.mdRecipe status table.
After all recipes drained → Debugging loop → Finalize → Report.
Context hygiene — after every 5 items drained, emit this tip once (then reset counter):
> 💡 Context is growing. Run /clear and re-invoke the skill — it resumes automatically from .axon4to5-migration/progress.md, no work is lost.
MUST NOT:
- Spawn a subagent under
execution=inline. - Pass anything beyond
(recipe path, source, framework, configuration)to a recipe subagent — context bloat defeats
the parallelism win.
- Cross repository boundaries during discovery.
- Halt the queue on a single Failure — record and drain the rest.
- Introduce DCB or swap event storage engine.
Queue flow
$SOURCE is referenced throughout the recipe sub-flow as the argument passed to the skill from source.
> [[Execute recipe sub-flow]] = [references/recipes/FLOW.md](references/recipes/FLOW.md), loaded at skill start. [[Resolve blocker]] = [references/recipes/BLOCKER_RESOLUTION.md](references/recipes/BLOCKER_RESOLUTION.md), budget = 1 attempt per item; on exhaustion item is marked blocked and drain continues.
Single mode flow
flowchart TD
A[Skill invoked] --> PARSE["Parseframework, configuration, source"]
PARSE --> ORW[["OpenRewrite(internal Skill, idempotent)"]]
ORW -- fail --> XORW[STOP: bulk-rewrite failed]
ORW -- ok --> B["list-recipes (catalog)"]
B --> MATCH{"Matchrequest + source → recipe"}
MATCH -- ambiguous --> ASK["AskUserQuestion(show titles, dispatch by id)"]
ASK --> EXEC
MATCH -- "no match" --> XNOMATCH[STOP: no applicable recipe]
MATCH -- matched --> EXEC[["Execute recipe sub-flow(FLOW.md)"]]
EXEC --> R{RESULT?}
R -- "Blocker (first attempt)" --> BR[["Resolve blocker(BLOCKER_RESOLUTION.md)"]]
BR --> BRQ{"Resolved?budget = 1"}
BRQ -- yes --> EXEC
BRQ -- "no / exhausted" --> BLK["mark blocked(🚧 caller must resolve)"]
R -- "Blocker (already retried)" --> BLK
R -- "Success / Rejected / Failure" --> VER["Verifybehavior preservedsame architecture as AF4"]
VER --> RPT["Report & END"]
BLK --> RPT
Project mode flow
flowchart TD
A[Skill invoked] --> PARSE["Parseframework, configuration, execution"]
PARSE --> ORW[["OpenRewrite(internal Skill, idempotent)"]]
ORW -- fail --> XORW[STOP: bulk-rewrite failed]
ORW -- ok --> B["list-recipes (catalog)"]
B --> RL{"Next recipein order?"}
RL -- "yes: <recipe>" --> DISC["Discoverexecution=inline: Grep/Globexecution=subagent: 1 Explore→ Enqueue items"]
DISC --> Q[(Recipe queue)]
Q --> L{"Drainpending?"}
L -- yes --> INP["pick next → in-progress"]
INP --> W[["Execute recipe sub-flowexecution=inline: main sessionexecution=subagent: general-purpose (parallel batch)"]]
W --> R{RESULT?}
R -- "Blocker (first attempt)" --> BR[["Resolve blocker(BLOCKER_RESOLUTION.md)"]]
BR --> BRQ{"Resolved?budget = 1"}
BRQ -- yes --> W
BRQ -- "no / exhausted" --> BLK["mark blocked(🚧 caller must resolve)"]
R -- "Blocker (already retried)" --> BLK
R -- "Success / Rejected / Failure" --> VER["Verifybehavior preservedsame architecture as AF4"]
VER --> DONE["mark done in queue"]
DONE --> Q
BLK --> Q
L -- "no (recipe drained)" --> RDONE["on:recipe-doneupdate Recipe status table"]
RDONE --> RL
RL -- "no more recipes" --> DBG_COMP
subgraph DEBUG ["🔍 Debugging — all recipes applied, build still red"]
direction TB
DBG_COMP["Full compilemvn compile / gradle classes"]
DBG_REDISC["Re-discoverre-scan applicable predicatesany recipe match remaining errors?"]
DBG_COMP -- "⚠️ errors" --> DBG_REDISC
end
DBG_COMP -- "✅ green" --> FIN["Finalizeremove isolated-* scaffoldingfinal compile · count by recipe"]
DBG_REDISC -- "new candidates" --> REENQ["re-enqueue as pending"]
REENQ --> Q
DBG_REDISC -- "nothing newrecipes exhausted" --> FIN
FIN --> RPT["Report & END"]
Discovery order — Discover scans recipes in this fixed sequence. Aggregates first: they define the events and commands consumed by downstream types.
| # | Recipe (id per frontmatter order:) | |---|----------------------------------------| | 1 | aggregate | | 2 | event-processor | | 3 | command-gateway | | 4 | query-gateway | | 5 | query-handler | | 6 | interceptors | | 7 | saga | | 8 | event-store |
Debugging loop (project mode only)
Entered when drain empties but build is still red. All known recipes have been applied — this phase asks: "is there still something a recipe can fix?"
Before the first full compile: Scan for application classes in subdirectories that OpenRewrite does NOT process by default — e.g., a microservices/ tree or separately deployable modules at the same repo root. These classes often use the same AF4 patterns (AF4 ConfigurationEnhancer, SagaEntry, DeadlineManager) as the main modules but are missed by the recipe drain because discovery only scanned the main source sets. Add any found classes to the queue and drain them before running the full compile.
- Full compile —
mvn compile/gradle classes(Java + Kotlin). Green → exit loop → Finalize. - Errors remain:
- Re-discover — re-scan every recipe's
applicablepredicates against current codebase. Sources mutated during drain may now match recipes that rejected earlier. - New
(recipe, source)pairs not already terminal → re-enqueue aspending, resume drain (loop repeats). - Nothing new → all applicable recipes exhausted → Finalize.
Finalize (project mode only)
- Cleanup scaffolding —
Grepallpom.xml/build.gradle(.kts)forisolated-*Maven profiles andisolated*Gradle source-sets added byaxon4to5-isolatedtest.Editeach build file to remove found blocks. Commit: `chore
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: AxonIQ
- Source: AxonIQ/agent-skills
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.