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
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:
The loop alone, without its stage skills (each stage falls back to an inline version):
In Claude Code, the dev-team plugin includes it:
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
| # | Stage | What it does | Skills | Gate |
|---|---|---|---|---|
| 1 | survey 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 | — |
| 2 | plan generate | Sequence the work: behaviour-preserving restructuring steps, and the phases of any data, schema or platform move. | refactor-plan, migration-plan | — |
| 3 | capture 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 | — |
| 4 | rebuild generate | Build the new component in the planned steps, each reviewed and merged on its own. | feature-implement-loop | — |
| 5 | prove verify | Replay the recordings against the new component and compare after the rules; the script's exit code decides. | behaviour-equivalence | G10 — a script decides READY · NOT READY · BLOCKED |
| 6 | cutover 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-readiness | G10.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
- Survey the boundary. Run
codebase-touron 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 runcontext-graphon 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 intoplan. A behaviour the old code has that contradicts its decision is a question for the owner beforecapture: 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. - 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.
- Restructuring the code in place →
- Capture before rebuilding. Run
behaviour-equivalenceagainst 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.jsonand the coverage note. Nothing from the new build may change these later; if they must change, that is a return to this stage.
- 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. - 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.
- Decide the cutover at G10.1. Run
launch-readinessfor 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.
- G10 emits
READY,NOT READYorBLOCKED, from the script's exit code alone. - G10.1 emits:
PROCEED: the cutover goes ahead.PROCEED WITH CONDITIONS: it goes ahead with named items, such as a shadow period or extra recordings for an uncovered input class, each with a date.REVISE: the coverage is too thin or an intended change wasn't approved. Return tocapture.RECONSIDER: the boundary was wrong, so replacing the component this way won't work. The loop ends here.BLOCKED: no person can accept the evidence, or no rollback exists.
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
- The old behaviour is recorded before the new code exists. Recordings made after the rebuild prove only that the new code agrees with itself.
- G10 is the script's exit code. No difference is waved through by reading it.
- Every normalisation rule is narrow and has a reason. A rule added after a G10 failure goes back through
capture. - Every intended change names its approver. The person at G10.1 sees each one.
- The coverage is stated. The cutover decision is made knowing which input classes the proof does not reach.
Anti-patterns to avoid
- ❌ Recording from the new system, or from tests written for it. The old system is the oracle.
- ❌ Fixing a G10 failure by ignoring the field. That hides the regression the loop exists to catch.
- ❌ Rebuilding everything, then comparing once. Each planned step merges on its own and is compared again.
- ❌ Calling the migration done at G10. Equivalence on the recordings is evidence; the cutover is a person's decision, with a rollback.
- ❌ Skipping
surveybecause the old code is "well known". The callers and side effects nobody remembers are the ones that break. - ❌ Rewriting without reading the decisions behind the old code. A constraint someone recorded years ago, such as a retention period or an ordering guarantee, is easy to drop in a rewrite and expensive to rediscover in production.