You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .squad/decisions.md
+198Lines changed: 198 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13861,3 +13861,201 @@ Tests are written *ahead of implementation* so Cyclops has a clear, testable con
13861
13861
13862
13862
**Why this matters:** End-users invoking the `bwfc-migration` or `migration-standards` skills will now get accurate guidance on Phase 1 capabilities instead of being told to handle these items manually.
**Decision:** Determine whether to strip both params or keep specialized EventArgs by checking if the type name is *exactly* `EventArgs` (strip both) vs. ends with `EventArgs` but is longer (keep specialized).
13875
+
**Rationale:** PowerShell regex can't inspect the C# type hierarchy. String matching on `\w*EventArgs` is sufficient because all Web Forms EventArgs subtypes follow the naming convention `*EventArgs`. This avoids false positives on non-EventArgs types like `string` or `int`.
13876
+
**Risk:** If a custom EventArgs subclass is named exactly `EventArgs` (impossible per C# rules) it would be incorrectly stripped. Extremely low risk.
**Decision:** Inject `await base.OnInitializedAsync();` at the start of the converted `OnInitializedAsync` body.
13880
+
**Rationale:** Blazor requires calling the base lifecycle method. Missing this call is a common source of bugs when components use `WebFormsPageBase` which has initialization logic in the base.
**Decision:** Wrap the entire `Page_PreRender` body in `if (firstRender) { ... }` when converting to `OnAfterRenderAsync`.
13884
+
**Rationale:** `Page_PreRender` runs once before the first render. `OnAfterRenderAsync` runs after *every* render. The `firstRender` guard preserves the original single-execution semantics.
13885
+
13886
+
### D4: Transform ordering — Lifecycle before Event Handlers
13887
+
**Decision:** Run GAP-05 (lifecycle) before GAP-07 (event handlers) in the pipeline.
13888
+
**Rationale:** Lifecycle conversion changes `Page_Load(object sender, EventArgs e)` to `OnInitializedAsync()`. If event handler conversion ran first, it would strip the lifecycle method's params but not rename it. Running lifecycle first ensures the method name is changed, and the params no longer match the event handler regex.
13889
+
13890
+
### D5: Test expected files updated in-place
13891
+
**Decision:** Updated 6 existing expected test files (TC13–TC16, TC18, TC19) to reflect the new transforms rather than excluding lifecycle/event handler test assertions.
13892
+
**Rationale:** The L1 pipeline is cumulative — all transforms run on every code-behind file. Expected outputs must reflect the full transform chain. Selective exclusion would be fragile and mask regressions.
13893
+
13894
+
## Validation
13895
+
- Script parses cleanly (0 errors)
13896
+
- All 21 L1 tests pass at 100% line accuracy
13897
+
- TC19 (lifecycle), TC20 (standard handlers), TC21 (specialized handlers) are dedicated test cases
13898
+
13899
+
13900
+
# L1 Migration Script Test Framework Extension
13901
+
13902
+
**Date:** 2026-03-17
13903
+
**Author:** Colossus (Integration Test Engineer)
13904
+
**Status:** Implemented
13905
+
13906
+
## Context
13907
+
13908
+
The L1 migration script (`migration-toolkit/scripts/bwfc-migrate.ps1`) was enhanced with 6 new capabilities in Phase 1. The test framework at `migration-toolkit/tests/` needed expansion to provide regression coverage for these enhancements.
13909
+
13910
+
## Decision
13911
+
13912
+
Extended the L1 test suite from 15 to 18 test cases by adding:
The test runner (`Run-L1Tests.ps1`) discovers test cases by scanning `inputs/` for `.aspx` files and comparing actual script output to expected output using normalized line-by-line comparison.
13925
+
13926
+
## Implementation Notes
13927
+
13928
+
- Expected files must match **actual script output** exactly, including:
13929
+
- Whitespace/indentation preserved from AST transformations
13930
+
- Attributes added by script (e.g., `ItemType="object"`)
13931
+
- Standard TODO comment headers
13932
+
- Base class removal (`: System.Web.UI.Page` stripped)
13933
+
- URL cleanup only transforms `Response.Redirect()` arguments, not arbitrary string literals
13934
+
- Test suite now at 78% pass rate (14/18), 98.2% line accuracy
13935
+
13936
+
## Rationale
13937
+
13938
+
These test cases provide:
13939
+
1. Regression protection for Phase 1 enhancements
13940
+
2. Documentation of expected script behavior through executable examples
Phase 2 added SessionShim (GAP-04), Page Lifecycle Transforms (GAP-05), and Event Handler Signatures (GAP-07). GAP-05 and GAP-07 are script transforms with no dedicated UI — they are covered by L1 unit tests. GAP-04 has a live sample page at `/migration/session`.
13960
+
13961
+
## Decision
13962
+
13963
+
- **Test GAP-04 (SessionShim) with 5 Playwright tests** covering set/get, count, clear, typed counter, and cross-navigation persistence.
13964
+
- **Add 1 regression test for the Phase 1 ConfigurationManager page** to prevent regressions.
13965
+
- **Skip browser tests for GAP-05 and GAP-07** since they are script-level transforms with no direct UI surface; L1 tests provide sufficient coverage.
13966
+
- **Use `data-audit-control` attribute selectors** (already present on the sample page) for robust element targeting that won't break with CSS changes.
13967
+
- **Use `DOMContentLoaded` wait strategy** (not `NetworkIdle`) for interactive Blazor Server pages per established patterns.
Implemented `SessionShim` as a scoped service that provides `Session["key"]` dictionary-style access for migrated Web Forms code. Registered in DI via `AddBlazorWebFormsComponents()`. Exposed as `protected SessionShim Session` on `WebFormsPageBase`.
13984
+
13985
+
## Design Choices
13986
+
13987
+
1. **System.Text.Json** for serialization — no Newtonsoft dependency, matches project zero-external-deps policy.
13988
+
2. **Graceful fallback** to `ConcurrentDictionary<string, object?>` when `ISession` is unavailable (interactive Blazor Server mode). No exceptions thrown.
13989
+
3. **One-time log warning** via `ILogger<SessionShim>` on first fallback — gives visibility without spam.
13990
+
4. **`IHttpContextAccessor` as optional** constructor parameter — prevents DI failures in test environments.
13991
+
5. **`AddDistributedMemoryCache()` + `AddSession()`** added to DI registration — required by ASP.NET Core session middleware. Safe to call multiple times (idempotent).
13992
+
6. **`TryGetSession` wraps access in try/catch** for `InvalidOperationException` — covers the case where session middleware is not in the pipeline.
13993
+
13994
+
## Why
13995
+
13996
+
Web Forms apps use `Session["key"]` pervasively for shopping carts, wizard state, user preferences. This shim lets migrated code compile and run with only the `asp:` prefix removal. The fallback mode ensures Blazor Server interactive circuits work correctly (session state is per-circuit anyway).
13997
+
13998
+
## Impact
13999
+
14000
+
- `WebFormsPageBase.Session` is now available on all migrated pages
14001
+
- `ServiceCollectionExtensions.AddBlazorWebFormsComponents()` now registers session infrastructure
**Transform Ordering Decision (D4):** Run GAP-05 (lifecycle) before GAP-07 (event handlers) in pipeline. Lifecycle conversion changes Page_Load(object sender, EventArgs e) to OnInitializedAsync(). If event handler conversion ran first, it would strip the lifecycle method's params but not rename it. Running lifecycle first ensures the method name is changed, and the params no longer match the event handler regex.
14012
+
14013
+
**Event Handler Type-Name Matching (D1):** Determine whether to strip both params or keep specialized EventArgs by checking if the type name is exactly EventArgs (strip both) vs. ends with EventArgs but is longer (keep specialized). PowerShell regex can't inspect C# type hierarchy. String matching on \w*EventArgs is sufficient.
14014
+
14015
+
**PreRender Guard (D3):** Wrap the entire Page_PreRender body in if (firstRender) { ... } when converting to OnAfterRenderAsync. Page_PreRender runs once before the first render. OnAfterRenderAsync runs after every render. The irstRender guard preserves original single-execution semantics.
14016
+
14017
+
**Why:** Web Forms page lifecycle and event handlers require specific transformations to map to Blazor component lifecycle. Transform ordering prevents interference between lifecycle renaming and event handler param stripping.
14018
+
14019
+
**Test Status:** 72/72 tests passing, TC13TC21 all at 100% accuracy.
14020
+
14021
+
### 2026-03-31: L1 Migration Test Framework Extension
14022
+
14023
+
**By:** Colossus
14024
+
14025
+
**What:** Extended L1 test suite from 15 to 18 test cases by adding TC16-IsPostBackGuard, TC17-BindExpression, TC18-UrlCleanup. Created xUnit test project ( ests/BlazorWebFormsComponents.Cli.Tests) with 7 transform test stubs.
14026
+
14027
+
**Test Case Design:** Input: .aspx + optional .aspx.cs files in migration-toolkit/tests/inputs/. Expected: .razor + optional .razor.cs files in xpected/. Test runner discovers inputs and compares against expected using normalized line-by-line comparison.
14028
+
14029
+
**Why:** Test infrastructure provides regression protection for L1 enhancements, documents expected script behavior, and provides foundation for future enhancement testing. L1 PowerShell tests validate script behavior; xUnit tests validate C# component behavior.
**What:** GAP-05 and GAP-07 are script-level transforms with no direct UI surface; covered by L1 tests. GAP-04 (SessionShim) has live sample page at /migration/session. Create 5 Playwright tests for SessionShim (set/get, count, clear, typed counter, cross-navigation persistence) + 1 regression test for Phase 1 ConfigurationManager page.
14038
+
14039
+
**Element Selection:** Use data-audit-control attribute selectors (already present on sample page) for robust element targeting that won't break with CSS changes. Use DOMContentLoaded wait strategy (not NetworkIdle) for interactive Blazor Server pages.
**Why:** SessionShim is critical for migrated Web Forms code that uses Session["key"]. Tests ensure the shim works across different page navigations and component lifecycle scenarios.
14044
+
14045
+
### 2026-03-31: SessionShim Design (GAP-04)
14046
+
14047
+
**By:** Cyclops
14048
+
14049
+
**What:** Implemented SessionShim as scoped service providing Session["key"] dictionary-style access for migrated Web Forms code. Registered in DI via AddBlazorWebFormsComponents(). Exposed as protected SessionShim Session on WebFormsPageBase.
14050
+
14051
+
**Design Choices:**
14052
+
1. **System.Text.Json** for serialization no Newtonsoft dependency, aligns with project zero-external-deps policy
14053
+
2. **Graceful fallback** to ConcurrentDictionary<string, object?> when ISession unavailable (interactive Blazor Server mode)
14054
+
3. **One-time log warning** via ILogger<SessionShim> on first fallback
14055
+
4. **IHttpContextAccessor as optional** constructor parameter prevents DI failures in test environments
14056
+
5. **AddDistributedMemoryCache() + AddSession()** added to DI registration required by ASP.NET Core session middleware
14057
+
6. **TryGetSession wraps access** in try/catch for InvalidOperationException
14058
+
14059
+
**Why:** Web Forms apps use Session["key"] pervasively. This shim lets migrated code compile and run with only sp: prefix removal. Fallback mode ensures Blazor Server interactive circuits work correctly (session state is per-circuit).
14060
+
14061
+
**Impact:** WebFormsPageBase.Session now available on all migrated pages. ServiceCollectionExtensions.AddBlazorWebFormsComponents() registers session infrastructure. Build verified on net8.0, net9.0, net10.0.
0 commit comments