| name | axon4to5-isolatedtest |
| description | Internal helper, invoked ONLY by other skills (e.g. axon4to5-migrate-code). Scopes Maven/Gradle compile+test to ONE target class plus all classes required for it to compile, via per-target profile or source-set. Do NOT auto-trigger from user prompts. |
| allowed-tools | Bash, Read, Edit, Write, Grep, Glob |
| argument-hint | <TargetName> [build-file=<path>] [cleanup=true|false] |
| user-invocable | false |
| effort | medium |
| model | haiku |
axon4to5-isolatedtest
Goal
Add (or extend) ONE self-contained build-scope unit per target so:
- Compilation is scoped to the target class plus all classes required for it to compile (helpers, dependencies, tests).
- Test runs hit only the target's test classes.
- The scope is independent — adding or removing it does not affect siblings.
- The scope is reversible — deleting the block reverses the change.
A target is a single class identified by its simple name (e.g., ResourcesPool, PaymentId). The scope id grammar:
isolated-<TargetName> # Maven profile id (used verbatim — TargetName stays PascalCase)
isolated<TargetName> # Gradle source-set name (camelCase already; <TargetName> is PascalCase)
<TargetName> = simple class name only. Java identifier chars only.
Inputs
| Input | Required | Default | Description |
|---|
target-name | yes | — | Simple class name, PascalCase. Drives the scope id. |
build-file | no | deduced | Absolute path to pom.xml or build.gradle(.kts). Deduce from the project. |
main-sources | no | deduced | Repo-relative paths under src/main/{java,kotlin} for files to compile in scope. Deduce from the target's imports and the project layout. |
test-sources | no | deduced | Repo-relative paths under src/test/{java,kotlin} for tests to run in scope. Deduce from the project — usually <TargetName>Test and its siblings. |
extra-deps | no | [] | Extra dependency coordinates to add only to this scope. Rare — most projects rely on inherited deps. |
cleanup | no | false | If true, remove the isolated-<TargetName> scope from the build file after a green test run (compile + tests both pass). If false, leave the scope in place so the user can iterate or re-run. On non-green runs the scope is always kept regardless. |
Pick the path
Detect build tool from build-file extension/name:
Each reference file is self-contained: it owns the install steps, the verify commands, and the cleanup steps for its build tool.
Why per-target scoping
One scope per <TargetName>, never a shared scope across targets:
- Parallel-friendly — concurrent invocations on different targets touch disjoint scopes; no edit race on shared include lists.
- Clean rollback — a failed run leaves ONE self-contained block to delete; no grep-and-pick across mixed entries.
- Bisect-friendly —
git bisect across commits that add or remove scopes sees stable siblings.
Do NOT collapse multiple targets into one shared isolated-* scope — you lose all three properties.
When to run
- First time for a target → create
isolated-<TargetName> scope.
- Re-running for the same target → augment the existing scope's include list (add transitive helpers surfaced by compile errors). Idempotent: re-running augments, never replaces.
- Different target → create a new scope, do not touch sibling
isolated-* scopes.
- Combined verification → activate / invoke multiple
isolated-* scopes in one command to verify several targets together; see references for the exact syntax.
Preflight
- Does a scope
isolated-<TargetName> (Maven) / isolated<TargetName> (Gradle) already exist in build-file?
- If yes AND its include list covers
main-sources ∪ test-sources → skip install, jump to verify.
- If yes BUT some inputs are missing from the include list → augment (merge new entries, dedupe by exact path string). Never replace.
Use Grep to detect existing scopes:
grep -E "<id>isolated-<TargetName></id>" <build-file>
grep -E "isolated<TargetName>" <build-file>
End condition
Run in this order — fail fast on the cheaper check:
- The build file contains a well-formed
isolated-<TargetName> scope.
- Compile-only check first — passes against the scope (or fails on missing symbols you EXPECTED, signalling the include list is too narrow). Do NOT proceed to step 3 until compile is green.
- Scoped test run — green (or fails on legitimate work that's the caller's job to fix — never on missing symbols from files OUTSIDE the include list).
NEVER
- Use
<excludes> (Maven) or source-set exclude(...) (Gradle) to silence compile errors — defeats the purpose. If the include list is too broad, narrow it.
- Mutate a sibling
isolated-* scope to "borrow" its files. Each target owns exactly one scope.
- Activate
isolated-* scopes in CI by default — these are working scaffolds, not production scope.
- Leave
isolated-* scopes in the build file once the surrounding module compiles and tests pass cleanly without them — see the Cleanup section in maven.md / gradle.md.
- Pass
-Dmaven.compiler.includes=… or -Dmaven.compiler.testIncludes=… from the command line — those properties are silently ignored by maven-compiler-plugin. The scope MUST live in the POM (inside the profile).
Output (what to report back)
ALWAYS use this exact template. The user reads this summary to know what was verified, how to re-run it manually, and whether the build file still contains scaffolding.
## axon4to5-isolatedtest: <TargetName>
**Build file**: <absolute path>
**Build tool**: maven | gradle
**Scope id**: isolated-<TargetName> | isolated<TargetName>
### Files in scope
- Main: <count> file(s)
- <relpath 1>
- <relpath 2>
- Test: <count> file(s)
- <relpath 1>
### Compile result
✅ passed | ❌ failed — `<compile-only command, copy-pasteable>`
### Test result
✅ N passed, 0 failed | ❌ N passed, M failed (or 0 executed when no `@Test` methods exist)
- `<FQTestClass1>` — <tests run> passed
- `<FQTestClass2>` — <tests run> passed, <failures> failed
- <failure name>: <one-line reason>
Re-run manually:
```
<scoped test command, copy-pasteable, including the -Dtest=... filter or :testIsolated<Target> task>
```
### Scaffolding status
- `cleanup` input: true | false
- Scope in build file: **kept** | **removed**
- If kept: delete it later with `git checkout <build-file>` or by hand. See the **Cleanup** section in references/<maven|gradle>.md.
- If removed: the build file is back to its pre-skill state; the verification commands above will no longer work — re-invoke the skill if you need to re-verify.
### Notes
<optional — e.g. "Augmented existing scope with 2 new files", "Compile failed naming `Foo.java` outside the include list — add it and re-run", "`cleanup=true` requested but skipped because tests failed">
Field rules
- Compile result — paste the EXACT command you ran, not a template. The user must be able to copy and re-run without edits.
- Test result — quote real counts from the build output (
Tests run: N, Failures: M for surefire; the JUnit XML report's tests= / failures= attrs for Gradle). Don't summarize as "passed" if zero tests executed — say 0 executed and flag that the target test class may have no @Test methods.
- Scaffolding status — this is the truth-telling field. Be explicit:
kept means the user's repo has uncommitted edits to the build file; removed means the file is restored. Never claim removed if any part of the scope (configurations block, source set, task, profile) is still in the file.
Common pitfall — transitive references leak
javac and kotlinc pull in classes referenced from kept files even if those classes are NOT in the include list. If Kept.java imports Broken.java, you must either add Broken.java to includes or fix Broken.java. The compiler error names the file to add. See references/transitive-references.md.
Fallback: RunTests.java JUnit launcher
If your project pins a JUnit Platform version that doesn't publish a working junit-platform-console-launcher, use scripts/RunTests.java — a minimal launcher that takes FQ test class names and exits 0/1. Usage instructions are in the file's header comment.