Skip to content

Loops · dev-team

modernize

Replace or rewrite a legacy component without changing what it does: orient in the old code, plan the restructuring and the move, record the old system's behaviour before anything is rebuilt, rebuild in small steps, prove the new one gives the same results on the recordings, and cut over only when a person accepts the evidence.

Use it when
Use when the user is migrating, rewriting or porting a legacy service, batch job or module (COBOL to Java, Python 2 to 3, a monolith to a service, one framework to another), says "run the modernize loop", or asks how to replace old code without breaking what depends on it.
Comes with
The dev-team pack
Where you decide
  • Prove (G10): a script decides
  • Cutover (G10.1): you decide
Size
6 stages · 2 gates · up to 3 revision rounds
npx skilldrop-cli install --loop modernize

Installs the loop as an invokable skill, plus every skill its stages run.

Other ways to install

Every loop in its pack, with their skills:

npx skilldrop-cli install --loop --pack dev-team

The loop alone, without its stage skills (each stage falls back to an inline version):

npx skilldrop-cli install --loop modernize --no-skills

In Claude Code, the dev-team plugin includes it:

/plugin install dev-team@skilldrop

The loop

%%{init: {'theme':'base','themeVariables':{'fontFamily':'Segoe UI, Helvetica, Arial','fontSize':'15px','lineColor':'#9AA5B1'},'flowchart':{'curve':'basis','rankSpacing':70,'nodeSpacing':50,'padding':16}}}%%
flowchart LR
    classDef gen    fill:#E8F0FE,stroke:#4C7DF0,stroke-width:1.5px,color:#1A3A8F;
    classDef review fill:#FDEAEA,stroke:#E05B5B,stroke-width:1.5px,color:#8A1F1F;
    classDef gate   fill:#FBE3A2,stroke:#D9971E,stroke-width:2px,color:#6B4500,font-weight:bold;
    classDef ship   fill:#E6F7EC,stroke:#34A853,stroke-width:2px,color:#0F6B33,font-weight:bold;

    survey["survey<br/><small>codebase-tour·context-graph</small>"]:::gen
    plan["plan<br/><small>refactor-plan·migration-plan</small>"]:::gen
    capture["capture<br/><small>behaviour-equivalence</small>"]:::gen
    rebuild["rebuild<br/><small>feature-implement-loop</small>"]:::gen
    prove["prove<br/><small>behaviour-equivalence</small>"]:::review
    cutover["cutover<br/><small>launch-readiness</small>"]:::gate
    G10{"G10 · mechanical"}:::gate
    G10_1{"G10.1 · human"}:::gate
    DONE(["complete"]):::ship

    survey --> plan
    plan --> capture
    capture --> rebuild
    rebuild --> prove
    prove --> G10
    G10 == "READY" ==> cutover
    cutover --> G10_1
    G10_1 == "PROCEED" ==> DONE

    G10 -- "NOT READY (max 3)" --> rebuild
    G10_1 -- "REVISE (max 3)" --> capture

Stages

#StageWhat it doesSkillsGate
1survey
generate
Find the boundary the component is replaced at, what crosses it, who calls it, and the traps in the old code, each cited to a file; then the recorded decisions that govern it, and which of them the code has drifted from.codebase-tour, context-graph—
2plan
generate
Sequence the work: behaviour-preserving restructuring steps, and the phases of any data, schema or platform move.refactor-plan, migration-plan—
3capture
generate
Record the old system's inputs and outputs at the boundary, run it twice to find its nondeterminism, and write the normalisation rules, before any new code exists.behaviour-equivalence—
4rebuild
generate
Build the new component in the planned steps, each reviewed and merged on its own.feature-implement-loop—
5prove
verify
Replay the recordings against the new component and compare after the rules; the script's exit code decides.behaviour-equivalenceG10 — a script decides
READY · NOT READY · BLOCKED
6cutover
gate
A person accepts the coverage the recordings give and every listed intended change, and decides the cutover. The readiness check informs that decision; it does not make it.launch-readinessG10.1 — you decide
PROCEED · PROCEED WITH CONDITIONS · REVISE · RECONSIDER · BLOCKED

One legacy component, replaced without changing its behaviour. Models now migrate legacy code at scale; what they can't do on their own is show that the result behaves the same. This loop makes that the gate. The old system's behaviour is recorded before any new code exists, so the evidence can't be shaped by what was built. A script decides whether the new component matches the recordings. Then a person decides the cutover, because only a person can accept what the recordings don't cover and approve every intended change.

How to run this loop

  1. Survey the boundary. Run codebase-tour on the component being replaced. The tour must end with the boundary it will be swapped at (an HTTP API, a function signature, a batch file, a queue) and every caller and side effect that crosses it, each cited to a file. A boundary nobody can name means the component isn't ready to be replaced. Then run context-graph on the same component, with git history. It finds the recorded decisions that govern it: why it was built this way, what was tried before, and the constraints the rewrite must keep. It also finds where the code has changed since those decisions were made, and who changed it. Carry every constraint a current decision sets into plan. A behaviour the old code has that contradicts its decision is a question for the owner before capture: record it as it is, and ask whether the rewrite should keep it. Each person who changed the code since the decision is worth asking what the records don't say.
  2. Plan the work.
    • Restructuring the code in place → refactor-plan.
    • Moving data, a schema or a platform → migration-plan.
    • A rewrite usually needs both: a refactor to carve out a seam, then a migration across it.
  3. Capture before rebuilding. Run behaviour-equivalence against the old component only:
    • record production-shaped inputs and the edge cases at the boundary;
    • run the old component twice on them;
    • write the normalisation rules from what differed between those two runs. Save recordings/, rules.json and the coverage note. Nothing from the new build may change these later; if they must change, that is a return to this stage.
  4. Rebuild in steps. Hand each planned step to feature-implement-loop. Each step merges on its own, behind the boundary, with the old component still serving.
  5. Prove at G10. Replay the recordings against the new component and compare:
# Claude Code (the loop and behaviour-equivalence installed side by side)
python3 "${CLAUDE_SKILL_DIR}/../behaviour-equivalence/scripts/golden_diff.py" old.jsonl new.jsonl --rules rules.json --accepted accepted.json
# Other IDEs (from the folder the skills were installed into)
python3 behaviour-equivalence/scripts/golden_diff.py old.jsonl new.jsonl --rules rules.json --accepted accepted.json

The exit code is the verdict: 0 is READY (equivalent, or equivalent except the changes in accepted.json), 1 is NOT READY, 2 is BLOCKED (the recordings or rules can't be read). NOT READY returns to rebuild with the differences the script lists. Adding a normalisation rule to make a difference disappear is a return to capture, not a fix.

  1. Decide the cutover at G10.1. Run launch-readiness for the switch itself: rollback to the old component, detection, the data cutover. Then show the person:
    • the G10 result;
    • what the recordings cover and what they don't;
    • every entry in accepted.json, with the approver each one names. The person decides.

Verdicts

Defined in contracts/terminals.json.

Non-interactive runs

With no person present, G10.1 cannot be satisfied. Run every stage through G10, deliver the evidence, and emit BLOCKED: need the cutover decision from <the owner>. Never mark an intended change as approved, and never widen a normalisation rule to reach READY.

When a stage's skill is not installed

Name the missing skill and skilldrop as its source, do the minimal inline version of that stage, and record that it ran degraded. Without behaviour-equivalence there is no G10 script. Say so, compare the recordings field by field by hand under the same rules, and mark G10 degraded. Never skip capture or G10.1.

Run log (opt-in)

Only when the environment variable SKILLDROP_LOOP_LOG is set to a file path: after each gate's verdict, append one JSON line to that file. Nothing else is recorded, and nothing is sent anywhere. When the variable isn't set, or you can't read it, skip this section.

printf '%s\n' '{"ts":"2026-10-20T14:03:00Z","loop":"modernize","stage":"prove","gate":"G10","verdict":"NOT READY","round":1}' >> "$SKILLDROP_LOOP_LOG"

Fields: ts (UTC), loop, stage, gate (null for a stage without one), verdict (the gate's exact word), and round (1 for the first attempt, counting returns under the cap). The log holds verdicts only: never recordings, code or data. skilldrop loop-stats summarises the file.

Quality bar

Anti-patterns to avoid

Source

LOOP.md · loop.json on GitHub · Why loops