Skip to main content
Source

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

  • 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_stack and blockers inform which porting steps apply (e.g. which System.Web surfaces 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):

  1. 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.
  2. 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.
  3. If the user approves none of the target-branch-derived options, continue in the previously approved working branch / working directory.
  4. 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.

  1. 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, so git 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.

  2. 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 .gitignore does 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.

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:

  1. 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.
  2. 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.
  3. 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).
  4. 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.
  5. 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:

  1. 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.
  2. 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.
  3. 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, and AssemblyInfo.cs attributes that the SDK now generates (or set <GenerateAssemblyInfo>false</GenerateAssemblyInfo> initially to defer that change).
  • Set <TargetFramework>: an SDK-style project can still target net48 — converting format before retargeting keeps this step independently buildable and verifiable. Retarget to net8.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.config entries 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.ps1 scripts 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.Json stays; Microsoft.AspNet.* packages are replaced, not upgraded — see step 5).

Step 3 — web.config → appsettings.json

web.config constructModern .NET equivalent
<appSettings> key/valuesappsettings.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 sitesInject 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.

Step 4 — Global.asax → Program.cs and the Middleware Pipeline

Global.asax memberModern .NET equivalent
Application_Start (route/filter/bundle registration)Program.cs: builder.Services.Add* registrations + app.Map* endpoint routing
Application_BeginRequest / EndRequestCustom middleware (app.Use(async (ctx, next) => ...)) placed at the pipeline position matching the old event order
Application_Errorapp.UseExceptionHandler(...) / UseStatusCodePages
Session_Start / Session_EndAddSession() + session middleware; Session_End has no equivalent (design around it)
HttpModules (<system.webServer><modules>)Middleware classes registered in pipeline order
HttpHandlers (.ashx, custom handlers)Endpoint routing (app.MapGet/MapPost) or minimal APIs; note .ashx is also a documented AWS Transform gap — it commonly arrives here as residual work

Step 5 — System.Web Replacement Map

The core reference for porting ASP.NET (System.Web-based) code:

System.Web surfaceModern .NET replacementNotes
System.Web.Mvc (ASP.NET MVC 5)ASP.NET Core MVC (Microsoft.AspNetCore.Mvc)Controllers/actions port with attribute changes (HttpGet namespaces, ActionResultIActionResult); Razor views largely port; HtmlHelpers partially → Tag Helpers
System.Web.Http (Web API 2)ASP.NET Core controllers ([ApiController])ApiController base → ControllerBase; message handlers → middleware; HttpResponseMessage returns → typed results
Web Forms (.aspx, code-behind)No direct port — Blazor or Razor Pages rewritePage-lifecycle and ViewState models have no equivalent; treat as a rewrite work item, sized separately
HttpContext.CurrentInjected IHttpContextAccessor / HttpContext on controllersEliminate static access; the DI-injected context is request-scoped
HttpRequest / HttpResponse (System.Web)Microsoft.AspNetCore.Http equivalentsProperty names differ (Request.QueryStringRequest.Query; streams instead of direct writes)
Session (HttpSessionState)ISession via AddSession() + app.UseSession()Values are byte/string based — object session state needs explicit serialization; distributed cache backing for multi-instance
System.Web.Caching.CacheIMemoryCache / IDistributedCacheChoose distributed cache when the ECS target runs multiple tasks
FormsAuthenticationASP.NET Core cookie authentication / ASP.NET Core IdentityAuth cookies are not compatible across the boundary; plan re-authentication or use System.Web adapters' shared-auth during incremental migration
Membership / Roles providersASP.NET Core IdentitySchema migration for existing user stores
Server.MapPathIWebHostEnvironment.ContentRootPath / WebRootPath
HttpModules / HttpHandlersMiddleware / endpoint routingSee step 4
Bundling (System.Web.Optimization)Build-time bundling (e.g. front-end toolchain) or static-file middlewareNo runtime bundling equivalent
WCF service hosting (System.ServiceModel, .svc)CoreWCF (community/Microsoft-supported port) or reshape to gRPC / REST.svc hosting is also a documented AWS Transform gap; CoreWCF covers common bindings (BasicHttp, NetTcp) — check its coverage for the bindings actually used

For large ASP.NET apps, Microsoft's incremental migration approach (YARP proxy + Microsoft.AspNetCore.SystemWebAdapters) allows routing migrated endpoints to the new app while the rest stays on .NET Framework — propose it as an option when a big-bang port of one item is too large to verify in increments.

Step 6 — IIS → Kestrel

  • The ported app self-hosts on Kestrel; WebApplication.CreateBuilder wires it by default. IIS-specific artifacts (web.config server sections, IIS modules) do not port.
  • Responsibilities that IIS held move explicitly:
    • TLS termination, compression, static files: in the ECS target, typically an ALB / reverse proxy terminates TLS; static files via UseStaticFiles; compression via UseResponseCompression if not proxy-handled.
    • Windows Authentication (NTLM/Kerberos via IIS): needs Microsoft.AspNetCore.Authentication.Negotiate on Windows hosts, or an auth redesign for Linux containers — surface this as a decision, not a silent change.
    • App lifecycle (IIS app pool recycling, warmup): container orchestrator concerns now; health checks via MapHealthChecks.
  • Listen address in containers: bind to 0.0.0.0 on the container port (ASPNETCORE_URLS / UrlPrefixes), never localhost.
  • If the approved path keeps the app on IIS in Windows containers (Windows_Container_Path replatform without porting), this step does not apply — that path's knowledge lives in the Replatform module, not here.

Step 7 — Windows API Dependencies and .NET Alternatives

Address the blockers entries (category os_specific_api) after the base port. Detection support: the Windows Compatibility Pack (Microsoft.Windows.Compatibility) makes many Windows-only APIs compile on modern .NET but throw PlatformNotSupportedException on Linux — it is a porting bridge, not a Linux fix. The Platform Compatibility Analyzer (CA1416) flags call sites.

Windows-only dependency.NET / cloud alternative
Registry access (Microsoft.Win32.Registry)Configuration via IConfiguration (appsettings/env vars) or a parameter store
Windows Services (ServiceBase)Worker Service template (BackgroundService); runs anywhere, container-friendly
System.Drawing.Common (Linux-unsupported since .NET 6)ImageSharp, SkiaSharp, or Microsoft.Maui.Graphics
MSMQ (System.Messaging)Amazon SQS, RabbitMQ, or other broker (no modern .NET MSMQ client)
DPAPI (ProtectedData)ASP.NET Core Data Protection with a shared key ring (e.g. persisted to S3/SSM for multi-instance)
Windows Event LogILogger structured logging to stdout (container-native) → CloudWatch Logs
System.DirectoryServices (AD)System.DirectoryServices.Protocols (cross-platform LDAP) or an identity-provider integration
COM interop / P/Invoke into Windows DLLsNo cross-platform equivalent — rewrite, isolate behind an interface on a Windows-hosted service, or keep the component on the Windows container path
WCF client usageSystem.ServiceModel.* client packages (supported on modern .NET) — distinct from service hosting (step 5)

Every item in this step that has no drop-in alternative is a decision point for the user, not a silent substitution: present the alternative, its behavioral difference, and let the approved plan govern.


Porting Knowledge — Java EOL Upgrades

The workflow for "Java 8 (or other EOL version) → supported LTS" work items. The same incremental discipline applies: one increment (namespace sweep, one dependency group, one plugin) → build/test → checkpoint.

javax → jakarta Namespace Migration

Java EE APIs moved to Jakarta EE; from Jakarta EE 9 the package namespace changed javax.*jakarta.* (e.g. javax.servletjakarta.servlet, javax.persistencejakarta.persistence, javax.validationjakarta.validation). This bites any upgrade that crosses the boundary — most prominently Spring Boot 2 → 3 (which requires Java 17 and Jakarta EE 9+) and Tomcat 9 → 10+.

  • Scope the sweep first: grep the codebase for javax. imports and classify which are Jakarta-governed (servlet, persistence, validation, annotation, transaction, mail, JAX-B/JAX-WS…) versus which remain in the JDK (javax.crypto, javax.net, javax.sql stay — do NOT rename those).
  • Mechanical rewrites are automatable: OpenRewrite's Jakarta/Spring Boot 3 recipes and the Eclipse Transformer perform the import/package rename plus descriptor updates; Apache Tomcat also ships a migration tool for webapps. Tool output goes through the same verify-and-checkpoint loop as manual edits.
  • Dependencies must move in lockstep: a classpath mixing javax.* and jakarta.* variants of the same API fails at runtime — the namespace sweep and the dependency updates below land together (one work-item increment), not separately.

Dependency Update Ordering

Update dependencies in this order — BOM / parent first, then libraries:

  1. Parent POM / platform BOM first (e.g. spring-boot-starter-parent or the spring-boot-dependencies BOM; Jakarta EE platform BOM). The BOM re-pins the entire managed-dependency graph consistently; upgrading individual libraries before the BOM produces version skew.
  2. Framework and Jakarta-governed libraries next, letting the BOM's managed versions apply — remove explicit <version> overrides that now conflict with the BOM.
  3. Standalone third-party libraries last, upgraded to versions compatible with the new JDK and (where applicable) the jakarta namespace.
  4. Rebuild and run tests after each group — not once at the end. Dependency-order violations surface as NoClassDefFoundError / NoSuchMethodError at test time; a per-group checkpoint isolates which group introduced them.

For multi-module builds, the same rule fractally: the root/parent module's management sections first, then leaf modules in dependency order.

Build Plugin Updates

Old plugin versions fail on new JDKs before the application code even compiles — update them early:

PluginWhat to update and why
maven-compiler-pluginRecent version; switch <source>/<target> to <release> (correct cross-compilation against the new JDK's API); set the target release (e.g. 17, 21)
maven-surefire-plugin / failsafeOld versions break on the module system and newer JDK internals; recent versions required for tests to run at all
maven-enforcer-pluginUpdate requireJavaVersion rules to the new floor
Bytecode-touching plugins/libs (Jacoco, Lombok, ASM-based, Mockito/ByteBuddy)Each has a minimum version per JDK bytecode level — upgrade before interpreting "weird" verifier or agent errors
Gradle buildsThe Gradle version itself gates JDK support — upgrade the wrapper first, then the toolchain (java.toolchain.languageVersion), then plugins

JDK Behavioral and Removal Changes

Crossing Java 8 → 11+ removes APIs the code may silently depend on:

  • Java EE modules removed from the JDK (Java 11): JAXB (java.xml.bind), JAX-WS, javax.annotation (@PostConstruct), javax.activation, CORBA — add them back as explicit dependencies (their Jakarta artifacts, matching the namespace decision above).
  • Strong encapsulation of JDK internals (Java 16+): illegal reflective access now fails by default; --add-opens is a stopgap, upgrading the offending library is the fix.
  • Removals in later JDKs: SecurityManager deprecation/degradation, Nashorn removal (15), Applet API, Thread.stop removal (20+) — check against the Oracle/OpenJDK migration guide for the specific source → target pair.
  • Consult the target JDK's official migration guide per hop; do not assume 8 → 17 issues equal 8 → 11 issues plus 11 → 17 issues found separately.

Porting Knowledge — WebSphere Traditional (tWAS) → Liberty

The workflow for a "WebSphere traditional → WebSphere Liberty / Open Liberty" work item — the application-server migration option from rearchitect-path.md. The same incremental discipline applies: one increment (feature configuration, one resource group, one API replacement sweep) → build/run/test → checkpoint.

Workflow order:

1. Scope from the assessment (binary scanner / Transformation Advisor report
when the user has one; otherwise the Source_Analysis
proprietary-API findings)
2. Build the Liberty server.xml (minimal feature set; resources; JNDI parity)
3. Carry over supported bindings (ibm-web-bnd.xml family — largely honored by Liberty)
4. Replace proprietary APIs (the map below; the largest step for coupled apps)
5. Decide the namespace generation (Java EE 8 features keep javax; Jakarta features
require the jakarta sweep from the Java section)
6. Wire local verification (liberty-maven-plugin / liberty-gradle-plugin)
  • Step 1 — scope. If the user has a Migration Toolkit for Application Binaries (binary scanner) or Transformation Advisor report, use its issue list as the work inventory — it enumerates Liberty-incompatible API usage per application. Without one, the tech_stack proprietary-API evidence (com.ibm.websphere.* / com.ibm.wsspi.* / CommonJ imports) is the inventory. The standalone binary scanner is a downloadable CLI the user can run; treat running it as a user action, and consume its report.
  • Step 2 — Liberty server.xml. Enable only the features the application's API surface needs (e.g. servlet, jsp, jdbc, ejbLite, jaxrs, jms) — Liberty's composability is the point; a maximal feature set recreates tWAS's weight. Recreate data sources as <dataSource> + <jdbcDriver> elements preserving the existing JNDI names so application lookups keep working; source the current values from the extracted cell properties or the user. Externalize environment-specific values with Liberty variables (${env.VAR}) so the same image moves across environments.
  • Step 3 — bindings. Liberty supports the traditional in-application bindings/extensions files (ibm-web-bnd.xml, ibm-ejb-jar-bnd.xml and family) — carry them over rather than rewriting (Liberty application bindings). Bindings and extensions may alternatively be declared inside server.xml; when both sources specify the same setting they are merged, with server.xml taking precedence — so avoid declaring a setting in both places unless the override is intentional. Remove only extension settings (*-ext.xml) whose behavior Liberty does not implement, reporting each removal.
  • Step 4 — proprietary API replacement map:
tWAS proprietary surfaceLiberty / spec replacement
CommonJ WorkManager / com.ibm.websphere.asynchbeansJakarta/EE Concurrency Utilities — ManagedExecutorService, ManagedScheduledExecutorService (concurrent feature). Lower-effort bridge: Liberty 22.0.0.1+ can run CommonJ Timer / Work Manager code unchanged by enabling the heritageAPIs-1.1 feature — a legitimate first increment that defers the API replacement; present both options with the trade-off (heritage feature keeps the proprietary dependency alive)
WAS scheduler serviceEJB Timer Service / ManagedScheduledExecutorService; or externalize to an orchestrator-level scheduler (ECS scheduled tasks) as a decision point
DynaCache (com.ibm.websphere.cache, DistributedMap)External cache (e.g. Redis/ElastiCache) or JCache — an integration change; present it as a decision point, not a silent substitution
SIBus (WAS-embedded messaging)External broker — the JMS API survives; the provider moves (e.g. Amazon MQ, IBM MQ)
com.ibm.websphere.security.* helpers, custom TAIJakarta Security / Liberty security configuration; custom Trust Association Interceptors need redesign — decision point
Startup beans (com.ibm.websphere.startupservice)@Startup @Singleton EJB or CDI @Observes @Initialized(ApplicationScoped.class)
WAS-specific JNDI namespace assumptions (cell/nodes/...)Flat java:comp/env / configured JNDI entries in server.xml

Every replacement that changes an integration (cache, messaging, scheduler) is a decision point for the user, same rule as the .NET Windows-API step.

  • Step 5 — namespace. Liberty runs both Java EE 8 (javax, features like javaee-8.0) and Jakarta EE (jakarta, features like jakartaee-10.0) generations. Migrating tWAS → Liberty does not by itself force the javaxjakarta sweep — landing on EE 8 features first is a legitimate, smaller increment. When the approved plan targets a Jakarta feature level, the javax → jakarta section applies and lands together with the feature-level choice.
  • Step 6 — verification. Wire liberty-maven-plugin (or the Gradle plugin) into the working branch so each increment is verifiable locally (liberty:dev for interactive runs, liberty:test-start + failsafe for CI-style checks). Liberty runs anywhere the JDK runs, so local verifiability is ordinary toolchain presence — no tWAS installation is needed to verify the target; the tWAS baseline, like the .NET Framework baseline, is typically not locally verifiable and follows the honest-reporting rule.

Other Runtime Upgrades — Node.js / Python

Node.js and Python EOL upgrades follow the same discipline (plan order, small increments, per-increment verify, checkpoint commits) with lighter mechanics: engine/interpreter floor updates (package.json engines, pyproject.toml requires-python), dependency-tree upgrades against the new runtime (lockfile regeneration, deprecation warnings as the worklist), and removal checks against the runtime's release notes. Local verifiability is simply toolchain presence — both are cross-platform. The verification, failure-handling, interruption, and review rules of this file apply unchanged.


Verification

Local Verifiability

Determined per dimension (b) of code-transformation.md — mechanical: language × host OS × toolchain presence. The pivotal asymmetry for .NET items:

  • The .NET Framework baseline cannot be built on a non-Windows host (MSBuild + .NET Framework reference assemblies + often IIS/Windows deps).
  • The ported, cross-platform .NET result is verifiable with the dotnet SDK on any host.

So on a Linux/macOS host, a .NET port typically has an unverifiable "before" and a verifiable "after": the agent cannot demonstrate baseline behavior locally, but can build/test the ported output as soon as the retarget compiles. Java / Node.js / Python items are locally verifiable wherever the matching toolchain (JDK + Maven/Gradle, Node, Python) is present. Record the determination per item — it drives which of the two paths below applies.

Per-Item Build and Test Verification

When local verification is possible (Requirement 15.19):

  • Build verification at each work item's completion — the full compile of the affected project(s), in the working location.
  • Test verification when tests are available — run the codebase's existing test suite (or the affected module's tests). "Available" means runnable tests exist for the affected code; absence of tests is recorded as test_verified: not_available, never silently equated with passing.
  • Record both results on the item (build_verified, test_verified) and in the Execution_Log, then record the checkpoint commit.

Verification Failure — Report, Block, Log

When an item's build or test verification fails (Requirement 15.21):

  1. Report the failure content — which verification failed, the error output's substance, and the increment it implicates.
  2. Block subsequent work items. Do not proceed to the next item in the plan until EITHER the failure is resolved by fixes within the working branch (then re-verify and checkpoint), OR the user decides the disposition: continue (accept and move on), skip (mark this item skipped, proceed), or abort (stop plan execution — interruption handling takes over).
  3. Record in the Execution_Log both the failure fact and its disposition (fixed-in-branch / user-continue / user-skip / user-abort), per Requirement 19, before the next action starts.

The fix loop stays inside the working branch and inside the safety rails — a verification failure never justifies touching the source branch, force-pushing, or rewriting the checkpoint history.

When Local Verification Is Not Possible — Honest Reporting

When the source or the target of a transformation cannot be locally build-verified (Requirement 15.20) — the .NET Framework baseline on a non-Windows host, an absent toolchain, or any other local gap:

  1. Report exactly what could not be verified locally and why (e.g. "the net48 baseline cannot be built on this Linux host; the ported net8.0 output was built and tested locally").
  2. Present the alternative verification means, at minimum from: the user's CI pipeline building the affected configurations; the remote build paths of Requirement 16 (e.g. CodeBuild-based image build environments reachable in Migration_Execution); and verification performed by the user on a capable host.
  3. Never claim the transformation is verified when it is not. Unverified is reported as unverified — in the item's result (build_verified: not_possible), in the Execution_Log, in the completion review perspectives, and in conversation. Partial verification (target verified, baseline not) is reported with exactly that granularity.

Record every such gap in local_verification_gaps so the completion report and the human review inherit the complete list.


Interruption and Incompletion

When Agent_Executed_Item execution is interrupted, or the plan's work items cannot all be completed (Requirement 15.22):

  1. Report completed vs incomplete items, distinguished — per item: name, status (done with its checkpoint commit / in_progress with last checkpoint / pending / skipped / verification_failed), so the user sees precisely where the plan stands.
  2. Preserve the working branch and every checkpoint commit. Nothing is deleted, reset, or rewritten — the branch and its checkpoints are the user's recovery assets (and Requirement 11.17 forbids destroying them anyway).
  3. Present the options, at minimum:
    • Manual continuation by the user — from the preserved working branch and its last checkpoint;
    • Scope adjustment — shrink or reshape the remaining transformation scope (a plan change, handled by the plan-change rules in code-transformation.md, including re-presentation and re-confirmation);
    • Transform_Augmentation proposal for incomplete items within coverage — for each unfinished item whose transformation type is inside AWS Transform's documented coverage (per the two-condition test, availability included), propose augmentation per the companion file's proposal rules.
  4. Never claim the transformation is complete. Partial completion is reported as partial — the same honesty rule as verification.
  5. Log the interruption — what stopped, item states, and the user's chosen option — in the Execution_Log.

Plan Completion — Mandatory Human Review, No Merge

When ALL work items of the Transformation_Plan are complete (Requirement 15.23) — the agent-executed ones per this file and the Transform-augmented ones per the companion:

  1. Present human code review as a mandatory step before merge. The transformed result is not merged, deployed, or built upon for release until a human has reviewed it.
  2. Report the review perspectives, at minimum:
    • Per-item build and test verification results — the build_verified / test_verified outcome of every item, checkpoint commits included;
    • What could not be verified locally — the full local_verification_gaps list from the rule above, with the suggested alternative verification means;
    • Untransformed parts — anything the plan did not cover or items that were skipped, plus (for Transform-augmented items) the residual gaps their job reports flagged;
    • The need for behavioral verification — functional testing beyond successful builds: green builds and passing unit tests do not establish behavioral equivalence of a port.
  3. Never merge. The agent does not merge the working branch (or any Transform target branch) into the original source branch — under any circumstances. State explicitly that the merge decision belongs to the user, after their review. This mirrors the companion file's no-merge rule for Transform jobs; the two rules are one policy.
  4. Log the completion report in the Execution_Log.

Output Schema

Agent_Executed_Item execution fills the agent-side fields of the code_transformation block defined in code-transformation.md — this file does not define a second schema. The fields this module owns:

# within code_transformation.transformation_plan.items[] (execution: agent):
status: pending | in_progress | done | verification_failed | skipped
build_verified: bool | not_possible # per-item completion build verification
test_verified: bool | not_available # tests run when runnable tests exist
checkpoint_commit: string | null # commit hash recorded at item completion

# within code_transformation:
agent_work_location:
branch_or_dir: string # the user-approved working location
based_on_transform_target: bool # true when continuing on a Transform target
# branch / derived branch, user-approved
local_verification_gaps: [string] # what could not be verified locally, and why
human_review_required: true # constant — review precedes any merge, always

Reporting invariants (agent side):

  • status: done implies a recorded checkpoint_commit and a completed verification step (or an explicit build_verified: not_possible with its gap recorded).
  • build_verified: true is never reported without a build actually having run locally; not_possible items appear in local_verification_gaps.
  • based_on_transform_target: true is never set without the user's explicit approval of that branch option.
  • No item transitions to in_progress before the "code transformation start" confirmation covering the current working location.

Edge Cases

The working location was never explicitly approved

An unapproved location is not a working location. Do not start any item: propose a concrete working branch / directory (and, where Transform target branches exist, the options of Requirement 15.18), obtain approval as part of — or as a re-confirmation of — the "code transformation start" confirmation, and only then begin.

The user asks the agent to edit the source branch directly "to save time"

Decline and explain the rail: Requirement 11.9 confines code changes to approved non-source locations, and the working-branch model preserves the user's rollback path. Offer the fast alternative that stays inside the rails (work on the approved branch; the user merges after review).

A work item's target overlaps a Transform target branch the user did not approve for agent work

Operate only in the approved location. If residual work naturally belongs on an unapproved target branch, present the 15.18 options again rather than touching that branch without approval.

Verification failure whose fix requires changing another item's completed output

Fix forward in the working branch (new commits — never rewrite the earlier checkpoint), re-run the current item's verification, and record both the failure and the forward fix in the Execution_Log. The earlier item's done status and checkpoint remain historical facts.

Tests exist but cannot run locally (missing test infrastructure)

That is a partial local-verification gap: report build_verified from the local build, test_verified: not_available with the reason, add the gap to local_verification_gaps, and present the alternative means (CI, user-run tests) — do not claim test verification.

The toolchain appears mid-plan (e.g. the user installs the JDK)

Local verifiability is re-determined for pending items; newly verifiable items follow the full verify-and-checkpoint discipline. Completed items are not retroactively re-verified unless the user asks — but the option can be offered.

An interrupted plan is resumed later

Resume from the preserved working branch and checkpoints, in plan order, starting at the first non-done item. If the plan or working location changed in the interim, the re-confirmation rules apply before the first pending item executes.

Conflicting guidance between this file and live Microsoft/Jakarta documentation

Live documentation wins — the porting tables here are authoring-time knowledge subject to the same freshness posture as the companion's directive. Verify version-specific claims (tool names, supported bindings, removal lists) against the cited sources before presenting them as current.


Sources

The transformation process model, augmentation determination, evidence comparison, and Transform-augmented execution knowledge live in the companion file, code-transformation.md; its Technical Freshness Directive's posture — verify against live sources before relying on version-specific claims — applies to the porting knowledge in this file as well.