This page is generated from skills/ecs-modernize/references/code-transformation-agent-led.md. Edit the source, not this page.
Module: Code Transformation — Agent-Executed Items
Part of: ecs-modernize Purpose: Execute the Agent_Executed_Items of the Transformation_Plan — hands-on .NET Framework → modern .NET porting and Java / other runtime EOL upgrades — under strict working-location safety rails, an incremental change → verify → commit discipline, and honest verification reporting Prerequisites: Execution_Gate passage (Requirement 14) AND the "code transformation start" action-class confirmation obtained via the process model in code-transformation.md. This file is the second file of the code transformation module: the plan construction, augmentation determination, partitioning, and adoption logic live in code-transformation.md — this file holds the HOW for the items that plan routes to
execution: agent
An Agent_Executed_Item is a work item of the single Transformation_Plan that Transform_Augmentation does not cover: AWS Transform unavailable, transformation type outside documented coverage, augmentation not adopted, or residual work an AWS Transform job left unfinished. Every such item is executed by the agent, inside the same plan, in plan order — interleaved with Transform-augmented items as the order dictates. This file supplies the porting and upgrade knowledge, the safety rails that keep the original source untouched, and the verification and reporting rules for that execution.
Like its companion, this knowledge is application-level and orchestrator-neutral: it describes how code is ported and upgraded, not where it will be deployed.
Table of Contents
- Inputs
- Working Location — Safety Rails
- Execution Discipline — Item by Item, Small Steps
- Porting Knowledge — .NET Framework → Modern .NET
- Workflow Order
- Step 1 — Project Conversion to SDK Style
- Step 2 — packages.config → PackageReference
- Step 3 — web.config → appsettings.json
- Step 4 — Global.asax → Program.cs and the Middleware Pipeline
- Step 5 — System.Web Replacement Map
- Step 6 — IIS → Kestrel
- Step 7 — Windows API Dependencies and .NET Alternatives
- Porting Knowledge — Java EOL Upgrades
- Porting Knowledge — WebSphere Traditional (tWAS) → Liberty
- Other Runtime Upgrades — Node.js / Python
- Verification
- Interruption and Incompletion
- Plan Completion — Mandatory Human Review, No Merge
- Output Schema
- Edge Cases
- Sources
Inputs
- The partitioned Transformation_Plan (required) — from code-transformation.md: the items with
execution: agent, each carrying its per-item reason (not applicable / not adopted / Transform residual work), in plan order. - The "code transformation start" confirmation (required) — the Requirement 14 action-class confirmation, whose presented content included the working branch / working directory for Agent_Executed_Items. No item in this file executes before it, and a change to the presented content (including the working location) requires re-confirmation before execution continues under it.
- The user-approved working location (required) — the working branch or working directory the user approved. Where Transform_Augmentation has completed, the approved location may be a Transform target branch or a branch derived from one (see working-location options).
- Host OS and toolchain facts — determine local verifiability per dimension (b) of the proposal dimensions (language × host OS × toolchain presence). Established by inspection of the execution host and by asking the user where inspection cannot settle it.
- Source_Analysis results (when available) —
tech_stackandblockersinform which porting steps apply (e.g. whichSystem.Websurfaces exist, which OS-specific APIs need alternatives). When the assessment was skipped, the user-specified targets and the code itself are the evidence base.
Working Location — Safety Rails
These rails implement Requirements 11.9, 11.17, 15.17, and 15.18. They are absolute: no porting step, however small, operates outside them.
The Approved Working Location
- All transformation operations are confined to the user-approved working branch or working directory. File edits, file creation, dependency changes, build outputs under the working tree — everything this module does to code happens inside that approved location and nowhere else.
- The original source branch is never modified. Files that existed under the target source directory at the start of Migration_Execution are never directly edited; the agent works on a code copy inside the approved location (new branch checked out from the source, or a copied working directory), consistent with Requirement 11.9. Files the agent itself created in the approved location during Migration_Execution may be freely modified.
- The working location is part of the confirmed content. It was presented at the "code transformation start" confirmation; changing it (a different branch, a different directory) changes the confirmed content and requires re-confirmation before any pending item executes under the new location.
Working-Location Options After Transform_Augmentation Completion
When one or more Transform_Augmentation jobs have completed and their target branches exist (Requirement 15.18):
- Present the working-location options for the remaining Agent_Executed_Items, including ALL of:
- each completed job's target branch (every target branch when multiple jobs completed — enumerate them by name);
- a new branch derived from any of those target branches;
- the previously approved working branch / working directory of the rule above.
- Continue on a target-branch-derived option only on the user's approval. If the user approves a target branch or a derived branch, subsequent Agent_Executed_Items (typically the residual work the job left unfinished) execute on that approved branch.
- If the user approves none of the target-branch-derived options, continue in the previously approved working branch / working directory.
- In every case, the original source branch remains unmodified — the choice is only ever between approved non-source locations.
Working on the Transform target branch is usually the right default to propose for residual work (the ported code is already there), but it is never assumed: the approval is explicit, and it is part of the confirmed content per the re-confirmation rule.
Preparing the Working Location for Checkpointing
The execution discipline requires a checkpoint commit per work item, so the approved working location must be able to take a clean commit before the first item runs. Verify both conditions below once, before executing item 1 — discovering them mid-plan means the early checkpoints are already polluted, and the safety invariants forbid rewriting history to fix them.
-
The working location is a git working tree. A branch checked out from the source repository satisfies this inherently. A copied working directory frequently does not: a plain recursive copy carries no
.git, sogit init(plus an initial commit capturing the unmodified baseline) is a prerequisite, not an optional nicety. The baseline commit matters independently: it is what makes every later checkpoint a readable diff of one work item. -
Build output is excluded from version control. Confirm the working location ignores the build's output directories and any generated binaries —
target/,build/,bin/,obj/, generated JARs. Two failure modes make this worth an explicit check:- A copied working directory may sit below the ignore file that covered it in its original location (a repository-root or parent-directory
.gitignoredoes not travel with a subdirectory copy), so the copy is unprotected even though the original was fine. - Per-item verification runs the build, so by the time the first checkpoint is taken there is output to capture. An unignored
target/turns every checkpoint into a commit dominated by regenerated artifacts, which defeats the diff-readability the checkpoints exist for and can bury the source change entirely.
When the exclusion is missing, add it in the working location before the first checkpoint (this is a file the agent itself creates in the approved location, so the rails permit it) and record the addition in the Execution_Log.
- A copied working directory may sit below the ignore file that covered it in its original location (a repository-root or parent-directory
One checkpoint per work item, and no batching. Two items landing in a single commit leaves the earlier item without its own recovery point — the checkpoint exists precisely so that item's state can be returned to. If a verification for item N is only reached after item N+1's edits are already in the tree, the items were not executed one at a time; the fix is to slow the increments down, not to combine the commits. When commits do end up combined, the safety invariants forbid rewriting them — record the combination and its consequence (the missing recovery point) honestly in the Execution_Log rather than presenting the checkpoint history as finer-grained than it is.
Repository Safety Invariants
Per Requirement 11.17, at all times, regardless of working location:
- No deletion of existing branches.
- No force push to existing branches.
- No commit-history rewriting (no rebase of pushed history, no amend of commits that predate this execution, no filter-branch/filter-repo).
Checkpoint commits are ordinary forward commits on the working branch. If a checkpoint needs correction, make a new commit — never rewrite.
Execution Discipline — Item by Item, Small Steps
Requirement 15.19 makes incremental execution mandatory. The discipline:
- Follow the plan order. Execute Agent_Executed_Items in the Transformation_Plan's order — one work item at a time, never batching several items into one sweep. Items are interleaved with Transform-augmented items as the plan dictates.
- Inside each work item, work in small increments: make a small, coherent change (one project conversion, one namespace sweep, one config migration) → verify it (build; tests where available) → commit it. Never accumulate a large uncommitted diff: a failed verification should implicate a small change, not an afternoon of edits.
- Verify at item completion. When local build verification is possible (local verifiability), run the build at each work item's completion — and the tests, when the codebase has runnable tests. An item is not "done" until its completion verification has run (or been honestly reported as not locally possible).
- Record a checkpoint commit at each item's completion on the working branch, and record its identifier (commit hash) in the item's result and the Execution_Log. Checkpoints are the recovery points that interruption handling preserves.
- Log every action. Item start, each verification result, each checkpoint commit — recorded in the Execution_Log before the next action starts, per the logging rules in code-transformation.md (storage forms and save-failure fallback per the canonical deploy-verify-handoff.md — Execution_Log Rules).
Why this order and granularity: plan order preserves the dependency ordering the plan encoded (shared libraries before consumers; base port before API replacements); small increments keep every verification failure attributable and every checkpoint restorable; per-item checkpoints make interruption cheap instead of catastrophic.
Editing Structured Files — Build Definitions and Server Configuration
Build definitions (pom.xml, build.gradle, *.csproj) and server configuration (server.xml, web.xml, web.config) carry most of what these work items change, and they are the files where a careless edit costs a whole increment. Two disciplines:
Never delete a region of a structured file with an unanchored pattern match. A multi-line regular expression written to remove one element (a <dependency>, a <configuration> block, a <dataSource>) readily matches more than intended — greedy quantifiers spanning to a later closing tag, or a pattern whose start anchor also matches the file header. The failure is not subtle but it is late: the build reports a parse error (Non-parseable POM, malformed XML) rather than the semantic change that was wanted, and the increment is spent diagnosing the edit instead of the port. Prefer, in order:
- Targeted replacement of exact known text — match the complete element including both of its tags, verbatim, and replace it. Uniqueness matters: if the element text appears twice, the match must include enough surrounding context to be unambiguous.
- Rewriting the whole file from a known-good base when the changes are extensive. For a build definition this is often cleaner than surgical edits, because the result is auditable in one read and its provenance is explicit.
- A parser (an XML/TOML/JSON library) when the edit is genuinely structural and must preserve unrelated content.
Verify the file still parses before running the build. A structured-file edit's first verification is that the file is well-formed — a parse check is cheaper than a build and localizes the error precisely. Treat the build as the second check, of behavior, not of syntax.
Stale comments are part of the change. These files commonly carry comments describing the configuration the work item is removing — an inline credential, a whole-platform feature, a local state directory. Deleting the element while leaving its comment produces a file that documents the opposite of what it now does, and the next assessment reads that tree as evidence. Update or remove the comment in the same increment as the element it describes, and let comments state the current state (with the transformation noted) rather than the state that was just eliminated.
Porting Knowledge — .NET Framework → Modern .NET
The workflow for a ".NET Framework → .NET 8 LTS (or later)" work item. Verify current tooling and target-version guidance against the live Microsoft porting documentation before relying on version-specific details — the same freshness posture as the companion file's Technical Freshness Directive.
Workflow Order
1. Project conversion to SDK style (per project, dependency order:
2. packages.config -> PackageReference libraries first, then apps)
3. web.config -> appsettings.json
4. Global.asax -> Program.cs / middleware
5. System.Web surface replacement (the largest step for web apps)
6. IIS -> Kestrel hosting
7. Windows API dependencies -> .NET alternatives
Steps 1–2 modernize the project format and restore model while still targeting .NET Framework where possible — they are verifiable on their own and make excellent early checkpoints. Steps 3–6 are the web-application port proper. Step 7 addresses the blockers entries with os_specific_api category and runs after the base port, per the plan's ordering rules. For multi-project solutions, run each step across projects in dependency order: class libraries first (consider netstandard2.0 as an intermediate target where both old and new consumers must coexist), then executables and web apps.
Tooling: the .NET Upgrade Assistant automates parts of steps 1–2 and some source rewrites; try-convert (its archived predecessor) handled SDK-style conversion only. Use them as accelerators inside the working branch when available — their output goes through the same verify-and-checkpoint discipline as manual edits. The Platform Compatibility Analyzer (CA1416) and the upgrade tools' analysis phase identify Windows-only API usage for step 7.
Step 1 — Project Conversion to SDK Style
Convert each .csproj from the legacy verbose format to the SDK-style format:
- Replace the legacy header with
<Project Sdk="Microsoft.NET.Sdk">(web apps:Microsoft.NET.Sdk.Web). - Remove explicit
<Compile>globs (SDK-style projects include sources by default),packages.config-era<Reference>hint paths, andAssemblyInfo.csattributes that the SDK now generates (or set<GenerateAssemblyInfo>false</GenerateAssemblyInfo>initially to defer that change). - Set
<TargetFramework>: an SDK-style project can still targetnet48— converting format before retargeting keeps this step independently buildable and verifiable. Retarget tonet8.0(or the approved target) when the port steps proper begin. Multi-targeting (<TargetFrameworks>net48;net8.0</TargetFrameworks>) is a useful transitional state for libraries.
Checkpoint after each converted project builds.
Step 2 — packages.config → PackageReference
- Migrate each project's
packages.configentries to<PackageReference>items in the.csproj(Visual Studio offers a built-in migrator; manual migration is a mechanical rewrite). - PackageReference resolves transitive dependencies automatically — remove packages that were only ever transitive; keep only direct dependencies.
- Delete the
packages/folder reliance; restore now uses the global package cache. - Watch for packages that relied on
install.ps1scripts or content-file injection — PackageReference does not run install scripts; such packages may need replacement or manual equivalent steps. - Update package versions toward the target framework's compatible versions as part of the retarget (e.g.
Newtonsoft.Jsonstays;Microsoft.AspNet.*packages are replaced, not upgraded — see step 5).
Step 3 — web.config → appsettings.json
| web.config construct | Modern .NET equivalent |
|---|---|
<appSettings> key/values | appsettings.json sections + IConfiguration (builder.Configuration["Key"]), strongly typed via the options pattern (IOptions<T>) |
<connectionStrings> | ConnectionStrings section in appsettings.json; IConfiguration.GetConnectionString(...) |
<system.web> (compilation, httpRuntime, sessionState, authentication) | No direct equivalent — behavior moves to Program.cs service/middleware configuration (step 4/5) |
<system.webServer> (modules, handlers, rewrite) | Middleware pipeline; URL Rewriting Middleware for rewrite rules |
Config transforms (web.Release.config) | Environment-specific files (appsettings.Production.json) + environment variables |
ConfigurationManager.AppSettings call sites | Inject IConfiguration / IOptions<T>; avoid the static-access pattern |
Secrets discovered in web.config during migration are relocated, never transcribed into the report or log: point the user to environment variables or a secrets store, and per the all-phase invariant, never write a detected credential value anywhere — not in generated files' comments, not in the Execution_Log, not in conversation output.