Morgan Lunt 96e3e8c6af
code-modernization: pilot-first uplift migration, brief-driven execution, deeper preflight
/modernize-uplift migrates one representative project end-to-end and
writes its lessons to analysis/<system>/PLAYBOOK.md before touching the
rest. The remaining projects then fan out through a new uplift-migrate
workflow, one uplift-migrator agent per project, in dependency-aware
escalating batches behind a per-batch circuit breaker. A recorded
per-test baseline (analysis/<system>/BASELINE.md) gates the migration,
and the delta catalog reports a test framework whose runner does not
support the target as its own highest-blast-radius dependency.

The three execution commands (uplift, transform, reimagine) read
MODERNIZATION_BRIEF.md and treat their phase's scope and entry and exit
criteria as gates, so editing the brief steers execution. For a
same-stack uplift the brief requires the delta catalog and applies the
same ordering overrides the execution command does.

/modernize-preflight opens with a short interview (scope, local build
and test, bespoke build infrastructure, prior attempts, what is off
limits) without blocking on the answers, reads the CI/build definition
for how the system builds, escalates the smoke test to a whole-project
restore and build, and adds a scope-boundary check that enumerates
inbound and outbound dependencies when the system directory is a slice
of a larger repository.

Workflow scripts accept args delivered as either a JSON string or an
object.
2026-07-08 18:48:30 -07:00

5.7 KiB

description argument-hint
Transform one legacy module to the target stack — idiomatic rewrite with behavior-equivalence tests <system-dir> <module> <target-stack>

Transform legacy/$1 module $2 into $3, with proof of behavioral equivalence.

This is a surgical, single-module transformation — one vertical slice of the strangler fig. Output goes to modernized/$1/$2/.

Step 0a — Toolchain check (fail fast on target, adapt on legacy)

Verify the build environment before planning, not when the tests first run:

  • Target stack ($3) — required. Runtime, package manager, and test framework all respond (java -version + mvn -v, node -v + npm -v, python3 -V + pytest --version, …). If any are missing, stop and report what to install — the new code and its tests cannot run without them, so a plan gate now would just defer the failure an hour. Suggest /modernize-preflight $1 $3 for the full readiness report.
  • Legacy stack — advisory, never a blocker. Try a syntax-only compile of the module being transformed (e.g. cobc -fsyntax-only). Legacy code often cannot build locally by nature, not by misconfiguration — CICS/IMS programs have no local translator, and the real runtime may be a mainframe you don't have. A failed or impossible legacy compile does not stop the transform; it changes the equivalence strategy:
    • dual-execution proof is off the table — characterization tests assert against recorded traces / golden-master fixtures (real production outputs, captured reports/screens, SME-confirmed examples) instead of live legacy runs
    • say so explicitly in the Step 0b plan and later in TRANSFORMATION_NOTES.md ("equivalence is trace-based; legacy was not executable in this environment"), so reviewers know the strength of the proof they're approving

Step 0b — Plan (HITL gate)

The brief is binding — read it first. If analysis/$1/MODERNIZATION_BRIEF.md exists, this transform is one phase (or one module of a phase) of that plan: read it before deciding anything below. Find the phase that names this command with $2 in scope, and treat that phase's scope, entry criteria, exit criteria, and any edits the user made to it as binding on the plan you present below. Entry criteria are gates, not context: if one is not met (a prior phase's exit criteria, an SME sign-off the brief requires), meeting it is the next step — do not proceed past it and do not silently re-plan around it. If the brief exists but no phase covers $2, stop and ask which phase this is. The user steers execution by editing the brief; a brief the execution command never reads cannot steer anything.

Read the source module and any business rules in analysis/$1/BUSINESS_RULES.md that reference it. Then present the plan and stop — write no code until the user explicitly approves (use plan mode if the session supports it):

  • Which source files are in scope
  • The target module structure (packages/classes/files you'll create)
  • Which business rules / behaviors this module implements
  • How you'll prove equivalence (test strategy)
  • Anything ambiguous that needs a human decision NOW

Wait for approval before writing any code.

Step 1 — Characterization tests FIRST

Before writing target code, spawn the test-engineer subagent:

"Write characterization tests for legacy/$1 module $2. Read the source, identify every observable behavior, and encode each as a test case with concrete input → expected output pairs derived from the legacy logic. Target framework: <appropriate for $3>. Write to modernized/$1/$2/src/test/. These tests define 'done' — the new code must pass all of them. Follow your secret-handling rules: no credential literal from legacy code becomes a fixture; substitute fake same-shape values and read anything genuinely live from environment variables."

Show the user the test file. Get a 👍 before proceeding.

Step 2 — Idiomatic transformation

Write the target implementation in modernized/$1/$2/src/main/.

Critical: Write code a senior $3 engineer would write from the specification, not from the legacy structure. Do NOT mirror COBOL paragraphs as methods, do NOT preserve legacy variable names like WS-TEMP-AMT-X. Use the target language's idioms: records/dataclasses, streams, dependency injection, proper error types, etc.

Include: domain model, service logic, API surface (REST controller or equivalent), and configuration. Add concise Javadoc/docstrings linking each class back to the rule IDs it implements.

Step 3 — Prove it

Run the characterization tests:

cd modernized/$1/$2 && <appropriate test command for $3>

Show the output. If anything fails, fix and re-run until green.

Step 4 — Side-by-side review

Generate modernized/$1/$2/TRANSFORMATION_NOTES.md:

  • Mapping table: legacy file:lines → target file:lines, per behavior
  • Deliberate deviations from legacy behavior (with rationale)
  • What was NOT migrated (dead code, unreachable branches) and why
  • Follow-ups for the next module that depends on this one

Then show a visual diff of one representative behavior, legacy vs modern:

delta --side-by-side <(sed -n '<lines>p' legacy/$1/<file>) modernized/$1/$2/src/main/<file>

(Fall back to diff -y --width=160 if delta isn't installed.) Never pick a credential-bearing line range for this diff, and mask any credential-like literal quoted in TRANSFORMATION_NOTES.md — the notes live in modernized/ and get committed.

Step 5 — Architecture review

Spawn the architecture-critic subagent to review the transformed code against $3 best practices. Apply any HIGH-severity feedback; list the rest in TRANSFORMATION_NOTES.md.

Report: tests passing, lines of legacy retired, location of artifacts.