Skip to content

Commit aa8f70c

Browse files
csharpfritzCopilot
andcommitted
Add Run 35 gap transforms
Implement CLI fixes for G1, G3, G8, and G10 by normalizing display expressions, rewriting EF6 DbContext constructors, extending ServerShim compatibility, and rewriting HttpUtility calls to WebUtility. Include transform registration updates, source-copy coverage, docs, and regression tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a6fa245 commit aa8f70c

26 files changed

Lines changed: 725 additions & 66 deletions

.squad/agents/bishop/history.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,18 @@
117117
- Added `System.Web.HttpUtility` compatibility methods (`UrlEncode`, `UrlDecode`, `HtmlEncode`, `HtmlDecode`) backed by `System.Net.WebUtility`. Tests had to use reflection because the test graph also references the legacy `System.Web.HttpUtility` package, which makes direct symbol references ambiguous.
118118
- Added CLI `DataBindingAttributeTransform` (Order 615) and registered it in production/test pipelines so attribute values like `NavigateUrl='<%# Item.GetUrl() %>'` become `NavigateUrl='@(Item.GetUrl())'` after prefix stripping.
119119
- Validation: `dotnet build src\BlazorWebFormsComponents\BlazorWebFormsComponents.csproj --nologo`, `dotnet test src\BlazorWebFormsComponents.Test --nologo` (2895 passing per target), and `dotnet test tests\BlazorWebFormsComponents.Cli.Tests --nologo` (552 passing).
120+
121+
### 2026-05-06T16:54:14-0400 : WingtipToys Migration Run 35 � Benchmark after gap fixes (Bishop)
122+
- Executed fresh Run 35 benchmark on commit `a6fa2455bec610b01127ab852886263f80f4bb1e` from branch `feature/wingtip-next-features-review` using `migration-toolkit\scripts\bwfc-migrate.ps1`.
123+
- Final result: build succeeded and **25/25 Wingtip acceptance tests passed**; screenshots captured under `dev-docs\migration-tests\wingtiptoys\run35\images\`.
124+
- Gap #3 (string Width/Height) and Gap #4 (attribute databinding transform) helped immediately in fresh output; no new `Request.QueryString.Get(...)` compile blocker surfaced.
125+
- Gap #10 (`System.Web.HttpUtility`) still needs follow-up for real migrated apps that also reference the legacy `System.Web.HttpUtility` package; `Logic\PayPalFunctions.cs` required manual `WebUtility` substitutions to avoid symbol ambiguity.
126+
- Biggest remaining automation gaps were invalid Razor syntax in generated markup, unsupported master-page script/bundle constructs, EF6-style `ProductContext` constructor emission, and a long tail of unresolved account/admin/mobile compile-surface pages that had to be stubbed or simplified manually.
127+
- Report: `dev-docs\migration-tests\wingtiptoys\run35\report.md`
128+
129+
### 2026-05-06T17:30:00-04:00: Run 35 gap transforms G1/G3/G8/G10 (Bishop)
130+
- Added CLI markup `DisplayExpressionTransform` (Order 490) so `<%#: expr %>`, `<%=: expr %>`, and broken `@(: expr)` output normalize to valid Razor `@(...)` before the broader expression pass.
131+
- Added code-behind transforms `HttpUtilityRewriteTransform` (Order 104) and `EfContextConstructorTransform` (Order 106); they now rewrite `HttpUtility.*` calls to `WebUtility.*`, add `using System.Net`, modernize EF6 `DbContext` string constructors to EF Core `DbContextOptions<TContext>`, and are included in `SourceFileCopier` so copied Models/Logic files are fixed too.
132+
- Extended `ServerShim` with `Transfer`, `GetLastError`, and `ClearError`, and updated `ServerShimTransform` guidance to treat those APIs as supported compatibility shims instead of dead-end TODOs.
133+
- Added focused regression tests for the new transforms, source-file copying, and ServerShim behavior; updated CLI docs plus migration docs so HttpUtility guidance now points to inline `WebUtility` rewrites rather than relying on the legacy shim package.
134+
- Validation: `dotnet build src\BlazorWebFormsComponents\BlazorWebFormsComponents.csproj --nologo`, `dotnet test src\BlazorWebFormsComponents.Test --nologo`, and `dotnet test tests\BlazorWebFormsComponents.Cli.Tests --nologo`.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Bishop decisions — G1 / G3 / G8 / G10
2+
3+
## Context
4+
Run 35 exposed four recurring Layer 1 gaps in migrated Wingtip output: invalid display-expression Razor, EF6 DbContext constructor emission, unsupported `Server.*` compatibility calls, and `HttpUtility` ambiguity when migrated apps still carry the legacy package.
5+
6+
## Decisions
7+
8+
1. **Normalize Web Forms display expressions before the main expression pass.**
9+
- Added `DisplayExpressionTransform` at Order 490.
10+
- It converts `<%#: expr %>`, `<%=: expr %>`, and broken `@(: expr)` into `@(...)` early so later markup transforms operate on valid Razor.
11+
12+
2. **Modernize EF6 DbContext constructors in Layer 1 instead of leaving them for manual repair.**
13+
- Added `EfContextConstructorTransform` at Order 106.
14+
- It rewrites `: base("name")` constructors on `DbContext` / `IdentityDbContext` types to EF Core `DbContextOptions<TContext>` constructors and ensures `using Microsoft.EntityFrameworkCore;` exists.
15+
16+
3. **Treat `Server.Transfer`, `Server.GetLastError`, and `Server.ClearError` as supported shim surface.**
17+
- Extended `ServerShim` with compatibility implementations/stubs.
18+
- Updated CLI guidance so these calls are no longer flagged as “NO SHIM” manual rewrites.
19+
20+
4. **Rewrite `HttpUtility` inline instead of depending on the compatibility shim.**
21+
- Added `HttpUtilityRewriteTransform` at Order 104.
22+
- The CLI now rewrites supported `HttpUtility` calls directly to `WebUtility`, adds `using System.Net`, and avoids relying on `System.Web.HttpUtility` in the generated app.
23+
24+
5. **Apply G3/G10 fixes to copied source files, not only page code-behind.**
25+
- Updated `SourceFileCopier` to include the new transforms so `Models`, `Logic`, and similar copied files receive the same modernization pass.
26+
27+
6. **Guard generated projects against the legacy HttpUtility package.**
28+
- Added an explicit `System.Web.HttpUtility` package-strip guard in `ProjectScaffolder` and a regression assertion in scaffolding tests.
29+
30+
## Validation
31+
- `dotnet build src\BlazorWebFormsComponents\BlazorWebFormsComponents.csproj --nologo`
32+
- `dotnet test src\BlazorWebFormsComponents.Test --nologo`
33+
- `dotnet test tests\BlazorWebFormsComponents.Cli.Tests --nologo`

docs/Migration/ControlCoverage.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ BWFC provides a comprehensive shim layer that enables Web Forms API calls to com
303303
| **FormShim** | `Request.Form["key"]`, `Request.Form.AllKeys`, `Request.Form.Count` | Dual-mode SSR+Interactive; requires `<WebFormsForm>` wrapper |
304304
| **SessionShim** | `Session["key"]`, `Session.Clear()`, `Session.Remove()` | Scoped in-memory dictionary — per-circuit lifetime |
305305
| **CacheShim** | `Cache["key"]`, `Cache.Insert()`, `Cache.Remove()` | Singleton in-memory cache |
306-
| **ServerShim** | `Server.MapPath("~/path")`, `Server.HtmlEncode()`, `Server.UrlEncode()` | Maps virtual paths; delegates encoding to `WebUtility` |
307-
| **HttpUtility** | `HttpUtility.UrlEncode()`, `HttpUtility.UrlDecode()`, `HttpUtility.HtmlEncode()`, `HttpUtility.HtmlDecode()` | Static `System.Web` compatibility shim backed by `WebUtility` |
306+
| **ServerShim** | `Server.MapPath("~/path")`, `Server.HtmlEncode()`, `Server.UrlEncode()`, `Server.Transfer()`, `Server.GetLastError()`, `Server.ClearError()` | Maps virtual paths; delegates encoding to `WebUtility`; routes `Transfer()` through `NavigationManager`; provides compatibility stubs for error methods |
307+
| **HttpUtility rewrite** | `HttpUtility.UrlEncode()`, `HttpUtility.UrlDecode()`, `HttpUtility.HtmlEncode()`, `HttpUtility.HtmlDecode()` | CLI rewrites calls inline to `WebUtility` during Layer 1 migration to avoid legacy package collisions |
308308
| **ClientScriptShim** | `Page.ClientScript.RegisterStartupScript()`, `RegisterClientScriptBlock()` | Registers scripts via JS interop |
309309
| **ScriptManagerShim** | `ScriptManager.GetCurrent(Page)`, `RegisterStartupScript()` | Compatibility shim for ScriptManager API |
310310
| **ViewStateDictionary** | `ViewState["key"]` dictionary access | In-memory dictionary (not serialized to page) |
@@ -314,7 +314,7 @@ BWFC provides a comprehensive shim layer that enables Web Forms API calls to com
314314
| **RouteConfig** | `RouteTable.Routes.MapPageRoute()`, `Routes.Ignore()` | No-op compile shim — compiles but does nothing |
315315
| **WebFormsForm** | `<form>` POST data capture | Blazor component — wraps forms to feed `FormShim` |
316316

317-
> **Key insight:** With `AddBlazorWebFormsComponents()` + `@inherits WebFormsPageBase`, code-behind files using `Response.Redirect`, `Session`, `Request`, `Request.QueryString.Get()`, `IsPostBack`, `Page.Title`, `Cache`, `Server.MapPath`, `HttpUtility`, or `ClientScript` **compile and run without modification**. This shifts ~20% of previously-manual Layer 2 work into automatic Layer 1 coverage.
317+
> **Key insight:** With `AddBlazorWebFormsComponents()` + `@inherits WebFormsPageBase`, code-behind files using `Response.Redirect`, `Session`, `Request`, `Request.QueryString.Get()`, `IsPostBack`, `Page.Title`, `Cache`, `Server.*`, or `ClientScript` **compile and run without modification**. For `HttpUtility`, the CLI now rewrites calls inline to `WebUtility` during Layer 1 so the migrated app avoids legacy package collisions. This shifts ~20% of previously-manual Layer 2 work into automatic Layer 1 coverage.
318318
319319
### Migration Shims (15)
320320

@@ -327,8 +327,8 @@ These drop-in shims allow Web Forms API calls to compile and function in Blazor.
327327
| **ResponseShim** | `Response.Redirect()`, `Response.Cookies` | `Response.Redirect()` — auto-wired |
328328
| **SessionShim** | `Session["key"]`, `Session.Clear()`, `Session.Remove()` | `Session["key"]` — auto-wired |
329329
| **CacheShim** | `Cache["key"]`, `Cache.Insert()`, `Cache.Remove()` | `Cache["key"]` — auto-wired |
330-
| **ServerShim** | `Server.MapPath()`, `Server.HtmlEncode()`, `Server.UrlEncode()` | `Server.MapPath()` — auto-wired |
331-
| **HttpUtility** | `HttpUtility.UrlEncode()`, `HttpUtility.UrlDecode()`, `HttpUtility.HtmlEncode()`, `HttpUtility.HtmlDecode()` | Add `using System.Web;` and call static methods directly |
330+
| **ServerShim** | `Server.MapPath()`, `Server.HtmlEncode()`, `Server.UrlEncode()`, `Server.Transfer()`, `Server.GetLastError()`, `Server.ClearError()` | `Server.*` — auto-wired |
331+
| **HttpUtility rewrite** | `HttpUtility.UrlEncode()`, `HttpUtility.UrlDecode()`, `HttpUtility.HtmlEncode()`, `HttpUtility.HtmlDecode()` | Let the CLI rewrite calls to `WebUtility` during migration |
332332
| **ClientScriptShim** | `Page.ClientScript.RegisterStartupScript()`, `RegisterClientScriptBlock()`, `RegisterClientScriptInclude()`, `GetPostBackEventReference()` | `ClientScript.*` — auto-wired |
333333
| **ScriptManagerShim** | `ScriptManager.GetCurrent(Page)`, `ScriptManager.RegisterStartupScript()` | Via `ScriptManager.GetCurrent(this)` |
334334
| **ConfigurationManager** | `ConfigurationManager.AppSettings["key"]`, `ConfigurationManager.ConnectionStrings["name"]` | Call `app.UseConfigurationManagerShim()` in Program.cs |

docs/Migration/QuickStart.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,11 @@ After the migration script runs, verify these are in place (the script scaffolds
109109
- `Response.Redirect("~/path")` (auto-strips `~/` and `.aspx`)
110110
- `Request.Url`, `Request.QueryString["key"]`, `Request.QueryString.Get("key")`, `Request.Form["key"]`
111111
- `Cache["key"]` (application-level cache)
112-
- `Server.MapPath("~/path")`, `System.Web.HttpUtility.UrlEncode()`, `System.Web.HttpUtility.HtmlEncode()`
112+
- `Server.MapPath("~/path")`, `Server.Transfer("~/path")`, `Server.GetLastError()`, `Server.ClearError()`
113+
- `System.Web.HttpUtility.UrlEncode()` and `HtmlEncode()` are rewritten by the CLI to `WebUtility.*`
113114
- `ClientScript.RegisterStartupScript(...)` (JS interop)
114115

115-
Your Web Forms code-behind compiles **unchanged**. No manual conversion needed.
116+
Your Web Forms code-behind compiles with minimal manual conversion. Most preserved APIs stay unchanged, and `HttpUtility` calls are normalized automatically during Layer 1.
116117

117118
**`Program.cs`** — register BWFC services:
118119
```csharp
@@ -124,11 +125,11 @@ builder.Services.AddBlazorWebFormsComponents();
124125
- Registers `ResponseShim` (handles `Response.Redirect`, `Response.Write`)
125126
- Registers `RequestShim` (provides `Request.QueryString`, `Request.Form`, `Request.Url`)
126127
- Registers `CacheShim` (in-memory application cache)
127-
- Registers `ServerShim` (provides `Server.MapPath`)
128+
- Registers `ServerShim` (provides `Server.MapPath`, `Server.Transfer`, and error-method compatibility)
128129
- Registers `ClientScriptShim` (JS interop for `ClientScript.RegisterStartupScript`)
129130
- Registers `ViewStateShim` (compile-compatible dictionary)
130131

131-
After this single call, all Web Forms APIs work AS-IS in your migrated code — no manual conversion required.
132+
After this single call, most Web Forms APIs work AS-IS in your migrated code. `HttpUtility` is handled by the CLI through inline `WebUtility` rewrites during Layer 1.
132133

133134
**Layout (`MainLayout.razor`)** — add the Page render component:
134135
```razor
@@ -166,7 +167,7 @@ Alternatively, point Copilot at the BWFC migration skill directly:
166167

167168
Open each migrated `.razor` file and work through the structural transforms that the script couldn't handle. These are the patterns Copilot handles well with the migration skill:
168169

169-
> 💡 **Many Web Forms API calls now compile unchanged thanks to BWFC shims.** `Response.Redirect`, `Session["key"]`, `IsPostBack`, `Page.Title`, `Request.QueryString`, `Cache`, and `Server.MapPath` all work AS-IS — no conversion needed. Focus Layer 2 effort on data binding, templates, and event handler signatures.
170+
> 💡 **Many Web Forms API calls now compile unchanged thanks to BWFC shims.** `Response.Redirect`, `Session["key"]`, `IsPostBack`, `Page.Title`, `Request.QueryString`, `Cache`, and `Server.*` all work AS-IS. `HttpUtility` is rewritten automatically to `WebUtility` during Layer 1. Focus Layer 2 effort on data binding, templates, and event handler signatures.
170171
171172
| Transform | What To Do |
172173
|---|---|

docs/UtilityFeatures/ServerShim.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Server & Path Resolution Shim
22

3-
The `ServerShim` class provides compatibility with the ASP.NET Web Forms `Server` object (`HttpServerUtility`). It wraps ASP.NET Core's `IWebHostEnvironment` and `System.Net.WebUtility` so that migrated code-behind using `Server.MapPath()`, `Server.HtmlEncode()`, and `Server.UrlEncode()` compiles and works correctly. The `ResolveUrl()` and `ResolveClientUrl()` methods on `WebFormsPageBase` handle virtual path and `.aspx` extension stripping.
3+
The `ServerShim` class provides compatibility with the ASP.NET Web Forms `Server` object (`HttpServerUtility`). It wraps ASP.NET Core's `IWebHostEnvironment`, `NavigationManager`, and `System.Net.WebUtility` so that migrated code-behind using `Server.MapPath()`, encoding helpers, `Server.Transfer()`, `Server.GetLastError()`, and `Server.ClearError()` compiles and works correctly. The `ResolveUrl()` and `ResolveClientUrl()` methods on `WebFormsPageBase` handle virtual path and `.aspx` extension stripping.
44

55
Original Microsoft implementation: https://docs.microsoft.com/en-us/dotnet/api/system.web.httpserverutility?view=netframework-4.8
66

@@ -25,6 +25,8 @@ In Blazor, these concerns are split across several APIs. The `ServerShim` bridge
2525
1. **`MapPath("~/path")`** — Resolves `~/` to `IWebHostEnvironment.WebRootPath` (wwwroot) and other paths to `ContentRootPath`
2626
2. **`HtmlEncode()` / `HtmlDecode()`** — Delegates to `System.Net.WebUtility`
2727
3. **`UrlEncode()` / `UrlDecode()`** — Delegates to `System.Net.WebUtility`
28+
4. **`Transfer(path)`** — Delegates to `NavigationManager.NavigateTo(path)`
29+
5. **`GetLastError()` / `ClearError()`** — Compatibility stubs for Web Forms error-handling patterns
2830

2931
The `ResolveUrl()` and `ResolveClientUrl()` methods live on `WebFormsPageBase` and:
3032

docs/cli/index.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ This tool **reduces manual migration effort** by:
99
- Removing boilerplate Web Forms directives and syntax
1010
- Converting ASP.NET server controls to BWFC components
1111
- Replacing Web Forms expressions with Blazor syntax
12+
- Normalizing `<%#:` / `<%=:` display expressions and broken `@(: expr)` output to valid Razor `@(...)`
1213
- Applying semantic page-pattern rewrites after the core transform pass
1314
- Injecting explicit validator generic arguments for BWFC validation components
1415
- Converting `<%# ... %>` data-binding expressions that appear inside attribute values into Razor `@(...)` expressions
16+
- Rewriting legacy `HttpUtility.*` calls inline to `WebUtility.*`
17+
- Upgrading EF6-style `DbContext` string constructors to EF Core `DbContextOptions<TContext>` constructors
1518
- Generating compile-safe stubs for markup-referenced members that are still missing after code-behind conversion
1619
- Extracting code patterns and flagging them with TODO comments for Copilot L2 automation
1720
- Quarantining risky legacy bootstrap/source artifacts out of the generated SSR compile surface
@@ -111,8 +114,8 @@ webforms-to-blazor convert \
111114
The tool applies an ordered transform pipeline and then a semantic pattern catalog:
112115

113116
1. **Directives** (5) — Page, Master, Control, Register, Import directives
114-
2. **Markup** (17) — Controls, expressions, templates, validator typing, data binding
115-
3. **Code-Behind** (23) — Using statements, base classes, lifecycle, event handlers, markup-driven safety stubs
117+
2. **Markup** (18) — Controls, expressions, display-expression cleanup, templates, validator typing, data binding
118+
3. **Code-Behind** (25) — Using statements, HttpUtility/EF modernization, base classes, lifecycle, event handlers, markup-driven safety stubs
116119

117120
See **[Transform Reference](transforms.md)** for the flat transform list and **[Semantic Pattern Catalog](semantic-pattern-catalog.md)** for the bounded semantic pass that runs afterward.
118121

docs/cli/transforms.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This page documents the flat markup and code-behind transforms applied by the `w
1515
| 250 | MasterPageTransform | Markup | Markup | Convert master markup to runnable `<MasterPage>` shell syntax |
1616
| 300 | ContentWrapperTransform | Markup | Markup | Wrap loose content in `<div>` if needed |
1717
| 310 | FormWrapperTransform | Markup | Markup | Convert `<form runat="server">` to Blazor form |
18+
| 490 | DisplayExpressionTransform | Markup | Markup | Normalize `<%#:` / `<%=:` display expressions and broken `@(: expr)` output to valid Razor |
1819
| 500 | ExpressionTransform | Markup | Markup | Convert `<%: %>`, `<%= %>`, and inline data expressions to Razor |
1920
| 510 | ServerCodeBlockTransform | Markup | Markup | Convert `<% ... %>` statement blocks to Razor control flow |
2021
| 510 | LoginViewTransform | Markup | Markup | Convert `<asp:LoginView>``<AuthorizeView>` |
@@ -43,6 +44,8 @@ This page documents the flat markup and code-behind transforms applied by the `w
4344
| 25 | ResponseRedirectTransform | Code-Behind | Code-Behind | Convert `Response.Redirect()``NavigationManager.NavigateTo()` |
4445
| 40 | DataBindTransform | Code-Behind | Code-Behind | Flag `DataBind()` calls |
4546
| 50 | UrlCleanupTransform | Code-Behind | Code-Behind | Clean URL literals in code |
47+
| 104 | HttpUtilityRewriteTransform | Code-Behind | Code-Behind | Rewrite `HttpUtility.*` calls to `WebUtility.*` and add `using System.Net;` |
48+
| 106 | EfContextConstructorTransform | Code-Behind | Code-Behind | Rewrite EF6 `base("name")` DbContext constructors to EF Core `DbContextOptions<TContext>` constructors |
4649
| 900 | MarkupReferencedMemberStubTransform | Code-Behind | Code-Behind | Add fallback fields, render-method stubs, and event handlers for markup references still missing from emitted partial classes |
4750

4851
---

src/BlazorWebFormsComponents.Cli/Io/SourceFileCopier.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public SourceFileCopier(OutputWriter outputWriter, IEnumerable<ICodeBehindTransf
4040
_outputWriter = outputWriter;
4141
// Only apply a subset of transforms relevant to non-page .cs files
4242
_transforms = transforms
43-
.Where(t => t.Name is "UsingStrip" or "EntityFramework" or "IdentityUsing")
43+
.Where(t => t.Name is "UsingStrip" or "IdentityUsing" or "HttpUtilityRewrite" or "EntityFramework" or "EfContextConstructor")
4444
.OrderBy(t => t.Order)
4545
.ToList();
4646
}

0 commit comments

Comments
 (0)