Skip to content

Orchestrator

The conductor of the pipeline. It sequences the phases, remembers where a run is, and asks you before anything moves forward.


Orchestrator Coordinates

  • Discover Collects
  • Determine Plans
  • Test Strengthens
  • Code Upgrades

What it is

The orchestrator is not a subagent. It is the main Claude Code session itself, driven by the project's CLAUDE.md. It invokes one phase agent at a time, waits for it to finish, then decides what happens next.

Its work splits into two halves, and each half lives in a different place:

Bookkeeping

Lives in the orchestrator skill

  • Read and write state.json
  • Decide the next state transition
  • Compare a score with a threshold

Scripts only. No judgment.

Judgment

Lives in CLAUDE.md

  • Invoke a phase agent with the right prompt
  • Summarise a report for you
  • Ask you to approve or reject, and react to it

The main session's own reasoning.

Neither half can replace the other. A skill cannot invoke a subagent or ask you a question. And state transitions should not be re-derived from prose every turn, where they can drift, so they are encoded once as a lookup table in a read-only script.

  • One phase at a time


    Phase agents run synchronously. Nothing to poll, no two phases ever overlap.

  • State lives in files


    Every decision and invocation is recorded under .modernization/. Stop, resume, audit.

  • Agents stay isolated


    Each agent starts with a clean context and a short prompt. It reads the rest itself.


The pipeline

The orchestrator's flow

flowchart TD
    OA(["orchestrator"])
    OA -->|" 1. calls "| DA["discover-agent"]
    DA -->|" produces "| DR[("discover-report.md")]
    DR -->|" reviewed by "| OA
    OA -.->|" rejected → revise "| DA
    OA -->|" 2. approved → calls "| DETA["determine-agent"]
    DETA -->|" produces "| DETR[("modernization-plan.md")]
    DETR -->|" reviewed by "| OA
    OA -.->|" rejected → revise "| DETA
    OA -->|" 3. testability < 80% → calls "| TA["test-agent"]
    TA -->|" produces "| TR[("tests + testability-report.md")]
    TR -->|" reviewed by "| OA
    OA -.->|" rejected → revise "| TA
    OA -->|" 4. testability ≥ 80% → calls "| CA["coding-agent"]
    CA -->|" once the build passes "| MP[("upgrade + modernization-summary-report.md")]

You approve each phase's report before the next one starts. Test runs only when the testability score is below the threshold. Code ends with a branch ready for your review.

Checkpoints

Mechanical checks

Decided from a number, without asking

  • Test gate Score ≥ threshold → Code, else → Test
  • Build verification Build and tests pass → Done, else retry, then ask you

Human gates

You approve, or reject with a reason

  • discover-report.md Rejected → Discover re-runs
  • modernization-plan.md Rejected → Determine re-runs
  • testability-report.md Rejected → Test continues

Why the code phase has no approval gate

Its changes are judged by whether the build and tests pass. The human review happens when you inspect and merge its branch.


Building blocks

  • Orchestrator prompt


    Runs the turn loop, invokes agents, asks you questions.

    CLAUDE.md

  • Orchestrator skill


    Creates, reads and updates state files. Decides the next step.

    .claude/skills/orchestrator/

  • Phase agents


    One specialist per phase, each with its own context and tools.

    .claude/agents/<phase>-agent.md

  • Skills


    Reusable procedures the agents call: scripts plus instructions.

    .claude/skills/<name>/

  • Settings


    Tool permissions and pipeline configuration.

    .claude/settings.json

  • State and artifacts


    State files, reports, logs and worktrees, created at run time.

    .modernization/

Agent Skills it uses
discover-agent inventory
determine-agent dependency-analysis, test-analysis
test-agent testability
coding-agent upgrade-java-spring-boot, build-verify, dependency-upgrade

Every agent also uses the orchestrator skill to read and write its own state file.

Related, not interchangeable

  • A skill knows how to do one job, not when to run.
  • An agent applies skills within its phase, but cannot move the pipeline or ask you anything.
  • Only the orchestrator sequences phases and talks to you.

Who does what

  • Scripts Everything deterministic


    Run Maven, generate the SBOM, compute coverage, edit pom.xml, verify a build.

  • Agents Everything that needs judgment


    Write the plan, pick classes to test, write tests, repair code after OpenRewrite.

  • Orchestrator Sequencing


    Read state, ask a script what is next, invoke an agent or ask you.

  • You Decisions


    Approve or reject reports, choose target versions, review and merge.

Scripts write full output to log files and return a single status line. Agents read finished summaries, never raw build output, so their context stays small.


How a turn works

sequenceDiagram
    actor You
    participant O as Orchestrator (CLAUDE.md)
    participant T as orchestrator-turn.sh
    participant A as Phase agent
    participant S as .modernization/

    You->>O: "Start the modernization pipeline."
    O->>S: state-init.sh (first run only)
    loop until DONE or stopped
        O->>T: What next?
        T->>S: read current_state (+ score or attempts)
        T-->>O: one action
        alt RUN_AGENT
            O->>S: set phase to RUNNING
            O->>A: invoke (with rejection reason or retry context)
            A->>S: write artifacts and phase state.json
            A-->>O: short conclusion
            O->>S: append session record, set next state
        else AWAIT_APPROVAL
            O->>You: report summary: approve or reject?
            You-->>O: decision (+ reason if rejected)
            O->>S: record decision, set next state
        else AWAIT_MITIGATION
            O->>You: the error: retry or abort?
        end
    end

orchestrator-turn.sh is read-only and returns exactly one of four actions:

Action The orchestrator then…
RUN_AGENT Invokes the next phase agent and records the session
AWAIT_APPROVAL Summarises the report and asks you to approve or reject
AWAIT_MITIGATION Shows the error and asks you to retry or abort
DONE Reports the final summary and stops

The state machine is a lookup table, so the running pipeline cannot drift from the designed flow.


Configuration

All configuration lives in .claude/settings.json, in two blocks:

Env

Pipeline settings, passed as environment variables to every command the agents run.

Permissions

Which commands and files the agents may use without asking.

Pipeline settings

Variable Default Phase Meaning
MODERNIZATION_TESTABILITY_THRESHOLD 80 Determine, Test Score (%) needed to skip Test, and the target Test works toward
MODERNIZATION_JACOCO_WEIGHT 0.6 Determine, Test Weight of JaCoCo line coverage
MODERNIZATION_PIT_WEIGHT 0.4 Determine, Test Weight of PIT mutation strength
MODERNIZATION_TEST_MAX_ITERATIONS 5 Test Max write-tests-and-re-measure rounds
MODERNIZATION_CODING_MAX_ATTEMPTS 3 Code Attempts before asking you to retry or abort
MODERNIZATION_TARGET_JAVA_VERSION 25 Code Default Java version offered
MODERNIZATION_TARGET_SPRING_BOOT_VERSION 4.0 Code Default Spring Boot version offered
MODERNIZATION_CODING_BUMP_BATCH_SIZE 1 Code Dependency bumps per build verification
MODERNIZATION_CODING_MAX_BUMPS 0 Code Max bumps per invocation (0 = no limit)

Testability score

line coverage % × JaCoCo weight + PIT strength % × PIT weight

Keep the two weights summing to 1.

Discover also accepts a few advanced variables (scanner choice, task timeouts). See Discover.

Permissions

Allowed without asking

  • The pipeline's own scripts under .claude/skills/*/scripts/
  • mvn, ./mvnw, mvn -v, java -version
  • Read-only git: status, branch, diff, log, worktree
  • Commits and resets only inside the pipeline's worktrees (git -C .modernization/test/worktree …, git -C .modernization/code/worktree …)
  • Anything under .modernization/
  • Basic shell: ls, cat, find, head, tail, wc, echo, pwd, cp, mkdir -p .modernization/**

Always asks first

git push, and git commit or git reset on your own branch.

Always denied

.env, any *.pem file, and anything under secrets/.

Least privilege: agents build, test and commit freely inside their own worktrees, and nowhere else.

Extending the pipeline with a new script? Add its exact path to allow, rather than broadening an existing pattern.

Changing configuration safely

Edit settings.json before you start a run

  • At run start, state-init.sh snapshots the threshold, weights and caps into gates in .modernization/state.json.
  • The test gate uses that snapshot, so a mid-run change won't alter a plan you already approved. Some scripts read the environment directly (weights, batch size), so mid-run edits apply unevenly. Avoid them.
  • Score looks wrong? Check the testability report's Configuration section. "Built-in default" where you expected your own values means the script ran outside a Claude Code session.

State

The pipeline's memory lives in files, not in the chat. Close the session any time, resume later, and see exactly what happened.

Each phase records which steps it has finished. The orchestrator records which phase the run is in and what you have approved.

Where state lives

.modernization/
├── state.json                      Top-level run state (orchestrator)
├── discover/
│   ├── state.json                  Discover step progress
│   ├── discover-report.md          ► Report you approve
│   ├── summary.md                  Scan task table (state, duration)
│   ├── bom-catalog/                Reference BOM data for Determine
│   └── <task>/                     One folder per scan task, each with logs/
├── determine/
│   ├── state.json                  Includes outputs.testability_score
│   ├── modernization-plan.md       ► Report you approve
│   ├── dependency-report.md
│   ├── plugin-report.md
│   ├── testability-report.md
│   ├── testability-data.csv
│   ├── dependency-analysis/logs/
│   └── test-analysis/logs/
├── test/                           Only if the test phase runs
│   ├── state.json
│   ├── testability-report.md       ► Report you approve
│   ├── iterations/<n>/             Measurements per round
│   └── worktree/                   Git worktree with the new tests
├── code/
│   ├── state.json
│   ├── worktree/                   Git worktree with the upgrade
│   ├── upgrade-worklist.csv        Per-dependency status
│   ├── dependency-report.md        Refreshed after the upgrade
│   ├── change-log.md
│   └── build-report.md
└── modernization-summary-report.md ► Final summary

► marks the files written for people. Everything else is machine state, intermediate data or logs.

One change outside .modernization/

On its first run inside a git repository, Discover adds .modernization/ to your .gitignore if it is missing. You will see it as an uncommitted edit.

The state machine

stateDiagram-v2
    [*] --> INIT
    INIT --> DISCOVER_RUNNING
    DISCOVER_RUNNING --> DISCOVER_AWAITING_APPROVAL: report written
    DISCOVER_AWAITING_APPROVAL --> DETERMINE_RUNNING: approved
    DISCOVER_AWAITING_APPROVAL --> DISCOVER_RUNNING: rejected
    DETERMINE_RUNNING --> DETERMINE_AWAITING_APPROVAL: plan written
    DETERMINE_AWAITING_APPROVAL --> TEST_GATE_CHECK: approved
    DETERMINE_AWAITING_APPROVAL --> DETERMINE_RUNNING: rejected
    TEST_GATE_CHECK --> CODING_RUNNING: score ≥ threshold
    TEST_GATE_CHECK --> TEST_RUNNING: score below threshold
    TEST_RUNNING --> TEST_AWAITING_APPROVAL: threshold reached or cap hit
    TEST_AWAITING_APPROVAL --> CODING_RUNNING: approved
    TEST_AWAITING_APPROVAL --> TEST_RUNNING: rejected
    CODING_RUNNING --> DONE: build green
    CODING_RUNNING --> CODING_RUNNING: failed, attempts left
    CODING_RUNNING --> MITIGATION: failed, no attempts left
    DISCOVER_RUNNING --> FAILED: phase failed
    DETERMINE_RUNNING --> FAILED: phase failed
    TEST_RUNNING --> FAILED: phase failed
    FAILED --> MITIGATION
    state "FAILED / MITIGATION_WAIT" as MITIGATION
    DONE --> [*]
State Meaning
INIT Run created, nothing started
<PHASE>_RUNNING The phase's agent is working, or runs next turn
<PHASE>_AWAITING_APPROVAL Waiting for your decision. Not stuck
TEST_GATE_CHECK Next turn compares the score with the threshold
FAILED A phase failed. You are asked to retry or abort
DONE Green build. Branch ready for review

Not every stop is a failure

  • A test phase that hits its iteration cap still writes a report and waits for your approval.
  • A failing code phase retries on its own up to MODERNIZATION_CODING_MAX_ATTEMPTS before asking you.

Who writes what

File Field Written by
.modernization/state.json Everything Orchestrator only
.modernization/<phase>/state.json status, steps, outputs, error, attempt, max_attempts That phase's agent
.modernization/<phase>/state.json sessions Orchestrator, after every invocation

All writes go through state-set.sh or state-append-session.sh: write to a temp file, then rename. A killed process can never leave a half-written state file.

Key fields reference
File Field What it tells you
state.json run_id Unique ID for this run, also used in branch names
state.json current_state Where the run is in the state machine
state.json phases.<phase>.status pending until you decide, then approved or rejected
state.json gates.* Settings snapshotted at run start, plus your chosen target versions
<phase>/state.json status running, reported, completed or failed
<phase>/state.json steps Which steps are done. This makes resume step-level
<phase>/state.json error {message, step, occurred_at} when the phase failed
<phase>/state.json sessions One entry per invocation: session ID, start, end, transcript
determine/state.json outputs.testability_score The number the test gate compares

Phases are named discover, determine, test and code in state files and directories.

To watch these during a run, see monitoring.


  • Discover


    Collects a factual inventory of dependencies, versions, vulnerabilities and test coverage. Changes nothing.

  • Determine


    Plans the upgrade from that evidence, and scores whether your tests are strong enough to trust it.

  • Test


    Strengthens weak tests, round by round on its own branch, before anything is upgraded.

  • Code


    Upgrades Java, Spring Boot and dependencies, one verified commit at a time.