Plan Lexical Edit Avalonia migration and add coverage#902
Conversation
7e5569a to
ab7e529
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR establishes an OpenSpec-based plan for the Lexical Edit → Avalonia migration, and adds Phase 2 characterization tests/evidence to support upcoming seam extractions (DataTree refresh, morph-type launcher logic, XMLViews browse filter reachability, and SliceFactory fallback behavior).
Changes:
- Adds a comprehensive OpenSpec change set for
lexical-edit-avalonia-migration(requirements, design, tasks, coverage/evidence, inventories, manifests, and diagrams). - Adds a companion OpenSpec change set for
fieldworks-avalonia-shell-migration(requirements/design/tasks/proposal). - Adds/extends net48 characterization tests around DataTree refresh behavior, MorphTypeAtomicLauncher data-loss classification, SliceFactory unknown-editor fallback, and XMLViews filter-bar reachability.
Reviewed changes
Copilot reviewed 50 out of 50 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| openspec/changes/lexical-edit-avalonia-migration/view-inventory.md | Inventories legacy Lexical Edit surfaces and migration risks. |
| openspec/changes/lexical-edit-avalonia-migration/tasks.md | Tracks phased migration tasks and coverage gates. |
| openspec/changes/lexical-edit-avalonia-migration/specs/lexical-edit-view-definition/spec.md | Defines requirements for typed view-definition + XML import boundary. |
| openspec/changes/lexical-edit-avalonia-migration/specs/lexical-edit-parity-automation/spec.md | Defines layered parity automation requirements (UIA2 + headless + semantic snapshots). |
| openspec/changes/lexical-edit-avalonia-migration/specs/lexical-edit-font-decommissioning/spec.md | Defines Graphite/font decommissioning requirements and evidence gates. |
| openspec/changes/lexical-edit-avalonia-migration/specs/lexical-edit-avalonia-migration/spec.md | Defines end-to-end phased migration requirements and completion gates. |
| openspec/changes/lexical-edit-avalonia-migration/specs/avalonia-validation/spec.md | Requires validation seam + Avalonia-native validation presentation. |
| openspec/changes/lexical-edit-avalonia-migration/specs/avalonia-undo-redo/spec.md | Requires domain-authoritative undo/redo with leaf-local control undo permitted. |
| openspec/changes/lexical-edit-avalonia-migration/specs/avalonia-ui-scheduler/spec.md | Requires a thin UI scheduling seam for non-view layers. |
| openspec/changes/lexical-edit-avalonia-migration/specs/avalonia-lifetime/spec.md | Requires a thin lifetime/dialog seam for non-view layers. |
| openspec/changes/lexical-edit-avalonia-migration/specs/avalonia-edit-sessions/spec.md | Requires FieldWorks-owned edit-session boundaries for migration. |
| openspec/changes/lexical-edit-avalonia-migration/specs/avalonia-command-focus/spec.md | Requires a FieldWorks-owned command/focus bridge for global behavior. |
| openspec/changes/lexical-edit-avalonia-migration/specs/architecture/ui-framework/winforms-patterns/spec.md | Adds seam/automation expectations for WinForms DetailControls refactors. |
| openspec/changes/lexical-edit-avalonia-migration/specs/architecture/ui-framework/views-rendering/spec.md | Extends render verification requirements with semantic snapshots and audits. |
| openspec/changes/lexical-edit-avalonia-migration/specs/architecture/testing/test-strategy/spec.md | Requires layered UI migration tests and explicit coverage plans. |
| openspec/changes/lexical-edit-avalonia-migration/specs/architecture/interop/native-boundary/spec.md | Requires elimination/classification of native render interop for completed regions. |
| openspec/changes/lexical-edit-avalonia-migration/seam-recommendations.md | Records recommended seam directions and pivot triggers. |
| openspec/changes/lexical-edit-avalonia-migration/region-manifest.md | Defines proposed migrated-region manifest shape and enablement gates. |
| openspec/changes/lexical-edit-avalonia-migration/proposal.md | Describes goals/scope/non-goals and migration approach. |
| openspec/changes/lexical-edit-avalonia-migration/phase2-execution-evidence.md | Summarizes Phase 2 characterization coverage added and remaining gaps. |
| openspec/changes/lexical-edit-avalonia-migration/override-fixtures.md | Plans override fixture families and typed-IR assertions/diagnostics needs. |
| openspec/changes/lexical-edit-avalonia-migration/migration-map.md | Maps Speckit sources into OpenSpec destinations. |
| openspec/changes/lexical-edit-avalonia-migration/graphite-decommissioning.md | Documents Graphite/native rendering inventory and default-path policy. |
| openspec/changes/lexical-edit-avalonia-migration/design.md | Consolidates architecture decisions, risks, and phase sequencing. |
| openspec/changes/lexical-edit-avalonia-migration/coverage-map.md | Coverage map for DataTree/SliceFactory/launchers/XMLViews and seams. |
| openspec/changes/lexical-edit-avalonia-migration/avalonia-validation.md | Plan for validation model, tests, and phase gates. |
| openspec/changes/lexical-edit-avalonia-migration/avalonia-undo-redo.md | Plan for undo/redo routing, tests, and phase gates. |
| openspec/changes/lexical-edit-avalonia-migration/avalonia-ui-scheduler.md | Plan for UI-thread marshalling seam and required tests. |
| openspec/changes/lexical-edit-avalonia-migration/avalonia-lifetime.md | Plan for deterministic disposal and navigation/close semantics. |
| openspec/changes/lexical-edit-avalonia-migration/avalonia-edit-sessions.md | Plan contrasting current prototype sessions vs proposed seam contract. |
| openspec/changes/lexical-edit-avalonia-migration/avalonia-command-focus.md | Plan for local vs shell-phase command/focus behavior. |
| openspec/changes/lexical-edit-avalonia-migration/architecture-diagrams.md | Mermaid diagrams of current state, seams, testing layers, and target architecture. |
| openspec/changes/lexical-edit-avalonia-migration/.openspec.yaml | Declares spec-driven schema metadata for the change. |
| openspec/changes/fieldworks-avalonia-shell-migration/tasks.md | Shell migration task plan and dependencies. |
| openspec/changes/fieldworks-avalonia-shell-migration/specs/fieldworks-avalonia-shell-migration/spec.md | Defines requirements for typed shell composition and Avalonia default shell. |
| openspec/changes/fieldworks-avalonia-shell-migration/proposal.md | Describes shell migration rationale/scope and decommissioning goals. |
| openspec/changes/fieldworks-avalonia-shell-migration/design.md | Decisions and migration sequencing for shell/windowing replacement. |
| openspec/changes/fieldworks-avalonia-shell-migration/.openspec.yaml | Declares spec-driven schema metadata for the change. |
| Src/xWorks/xWorksTests/BulkEditBarTests.cs | Adds XMLViews filter-bar reachability “smoke substitute” baseline helper + test. |
| Src/Common/Controls/DetailControls/MorphTypeAtomicLauncher.cs | Extracts morph-type data-loss classification into flag enum + helper methods. |
| Src/Common/Controls/DetailControls/DetailControlsTests/SliceFactoryTests.cs | Adds test pinning unknown-editor fallback to MessageSlice + AccessibleName. |
| Src/Common/Controls/DetailControls/DetailControlsTests/MorphTypeAtomicLauncherTests.cs | Adds characterization tests for DataTree refresh edge + morph-type data-loss logic. |
| Src/Common/Controls/DetailControls/DetailControlsTests/DataTreeTests.cs | Adds semantic slice baseline assertion helper and Cf/Bib baseline test. |
| .github/skills/fieldworks-winforms-to-avalonia-migration/SKILL.md | Adds migration review skill guidance for plans/tests/seams/parity. |
| .github/skills/fieldworks-uia2-parity-testing/SKILL.md | Adds guidance for UIA2 vs headless automation and parity evidence. |
| .github/skills/fieldworks-semantic-render-parity/SKILL.md | Adds guidance for semantic snapshot discipline and render evidence. |
| .github/skills/fieldworks-migration-scope-review/SKILL.md | Adds guidance for scope splits and evidence/claim alignment in large PRs. |
| .github/skills/fieldworks-managed-netfx-review/SKILL.md | Adds guidance for net48 vs net8 boundary hygiene and validation. |
| .github/skills/fieldworks-avalonia-ui/SKILL.md | Adds guidance for Avalonia module review, tests, and parity claims. |
| .github/instructions/avalonia.instructions.md | Adds repo guidance for building/testing/preview-hosting Avalonia modules. |
ab7e529 to
ec4222e
Compare
|
Addressed Copilot review brittleness notes:
Also fixed a build blocker encountered while running the focused tests: |
Add FieldWorks Avalonia migration review skills Narrow Phase 1-2 Avalonia migration foundation
- Avoid localized UI strings in BulkEditBarTests filter baseline - Stop using reflection in MorphTypeAtomicLauncherTests - Fix ViewsInterfaces duplicate TargetFrameworkAttribute build
Sequence the Avalonia migration as one gated path: POC spike -> DataTree region (Plan A) -> Lexical Edit program (Plan B) -> shell. Adds the avalonia-migration-roadmap umbrella change and the lexical-edit-avalonia-poc-spike change (flagged, in-proc net48 dual-run, density/parity evidence). Brings the datatree-model-view-separation plan onto this branch as the first migrated region with a hybrid-alignment note, and records the approach comparison and recommendation.
Locks the normalized per-slice snapshot (label, field, flid, editor, visibility, focus order, a11y) that the Avalonia POC slice must reproduce, and asserts it is deterministic across realizations. All expected values reuse those already proven by CfAndBib_SemanticSliceBaselineCapturesStableBindingsAndFocusOrder.
Implements lexical-edit-avalonia-poc-spike as an isolated, flag-gated proof of concept under Src/Common/FwAvalonia (intentionally not in the traversal build or solution yet, so it cannot break the default build). Evidence executed on net48 with Avalonia 11.3.17: - restore + build of the library and headless test project succeed - 20 headless/unit tests pass (dotnet test), covering the two-adapter flag (default WinForms; no Avalonia runtime constructed when off), the three-editor slice, fenced commit/cancel, morph-type popup focus return, writing-system fonts, and a no-native/no-Graphite reference audit Pins Avalonia to 11.3.x in CPM because 12.x dropped netstandard2.0 and cannot load on net48; Avalonia.Win32.Interoperability (net461) restores and builds on net48, confirming the in-process embedding path. Live embedding into RecordEditView and DPI density screenshots require the running app and are deferred to the regional migration (Gate 0 follow-up); see spike-evidence.md. Recommendation: GO.
Implements sections 3 (refactor seams) and 4 (typed view definition + XML import) of lexical-edit-avalonia-migration as isolated, tested code in Src/Common/FwAvalonia (not in the traversal build yet, like the POC). Seams (framework-neutral contracts + pure implementations + tests): - ILexicalRefreshCoordinator / RefreshCoordinator: pure model of the DataTree DoNotRefresh/RefreshPending gate (LT-22414) - ILexicalEditorRegistry / LexicalEditorRegistry: editor-key boundary in front of SliceFactory with fallback-to-legacy - IEditSession (PocEditSession now implements it); IUiScheduler, IRegionLifetime, IPropertyStateStore implementations - MorphTypeSwapLogic humble object mirroring MorphTypeAtomicLauncher IsStemType and the stem/affix data-loss decision Typed view definition (sections 4.1-4.6): - ViewDefinitionModel/ViewNode immutable IR with deterministic snapshot - XmlLayoutImporter parses the real Parts/Layout schema (parts, grouping slices, obj/seq, indent, custom-field placeholder, visibility/expansion overrides) and raises diagnostics for dynamic/unknown/obsolete editors and unresolved parts, never throwing - EditorKindMap classifies editors faithfully to SliceFactory's switch - ViewDefinitionCompiler/Cache with content-fingerprint keys, invalidation, and cancellable off-thread compile over immutable snapshots Evidence: dotnet test on net48 passes 67 tests (47 new). Live wiring into DataTree/SliceFactory/RecordEditView is deferred to the regional migration; task notes mark contract-vs-wired status honestly.
Retroactively completes the "test coverage before refactor" tasks (2.5-2.7) of lexical-edit-avalonia-migration with tests that run against the REAL WinForms DataTree/Slice/RenderVerification code on net48, locking current behavior so the Avalonia refactor is protected. DetailControlsTests (real DataTree/Slice, 11 tests): - DataTreeUndoRedoCharacterizationTests: multistring CitationForm/Bibliography edits revert and replay; multiple edits in one task form a single undo step; consecutive edits are distinct steps; a freshly built slice reflects the reverted model. Uses the base action handler + UndoableUnitOfWorkHelper. - DataTreeDisposalCharacterizationTests: Dispose cascades to slices, removes the LCModel notification (no throw on later change), is idempotent, and is safe with a current slice; slices expose AccessibleName == label and a stable focus order (in-process accessibility substitute for 2.4). RenderVerification (failure-artifact bundling, 4 tests): - RenderFailureArtifactBundler bundles received/diff images + a failure-summary.json into a CI-discoverable folder on a failed render/parity verification; wired into DataTreeRenderTests.VerifyDataTreeBitmap. Compiled into RenderTestInfrastructure alongside RenderSnapshotVerifier so the result type is in scope; covered by RenderFailureArtifactBundlerTests. Evidence: dotnet test on net48 passes all 15 new tests. True UIA2/FlaUI baselines (2.4) and keyboard/IME/localization characterization (2.7 remainder) still need a running app host and remain pending; tasks.md notes this honestly.
a0d372b to
89f3ea6
Compare
Keep the Avalonia spike strictly on .NET Framework 4.8 for now. - `build.ps1 -BuildAvalonia` now filters optional Avalonia projects to those that target `net48`, skips non-net48 hosts/modules with an explicit message, and avoids double-building the same project when `-Project` already points at an Avalonia csproj. - `Directory.Packages.props` now pins `HarfBuzzSharp` and its Win32 native assets to 8.3.1.1, matching Avalonia 11.3.17 and removing the NU1109 downgrade failure for the net48 Avalonia spike. Validated: - `dotnet msbuild Src/Common/FwAvalonia/FwAvalonia.csproj` with `/t:Restore;Build` succeeds on net48. - `dotnet test Src/Common/FwAvalonia/FwAvaloniaTests/FwAvaloniaTests.csproj` still passes (67 tests). - `build.ps1 -BuildAvalonia` with `-Project` set to `Src/Common/FwAvalonia/FwAvalonia.csproj` succeeds and reports that the existing preview host is net8-based and outside this branch's net48-only policy.
Pull the preview-host idea onto this branch, but refactor it to the branch's net48-only policy instead of the prototype branch's net8 shape. Adds a new net48 `FwAvaloniaPreviewHost` that discovers preview modules via assembly attributes in `FwAvalonia`, along with a runner script at `scripts/Agent/Run-AvaloniaPreview.ps1`. Wires the current lexical-edit POC into that host with: - `FwPreviewModuleAttribute` / `IFwPreviewDataProvider` - `PocPreviewWindow` + `PocPreviewDataProvider` - stable Avalonia automation IDs/names on the slice, text editors, labels, and morph-type chooser Adds native desktop automation tests in `FwAvaloniaPreviewHostTests` using `System.Windows.Automation` that launch the real preview-host exe and verify: - the main preview window and core controls expose stable automation IDs - the morph-type button supports InvokePattern and shows the popup list Updates `build.ps1 -BuildAvalonia` so the optional Avalonia lane also builds `FwAvaloniaPreviewHostTests` when `-BuildTests` is requested. Validated: - `dotnet build` `Src/Common/FwAvaloniaPreviewHost/FwAvaloniaPreviewHost.csproj` - `dotnet test` `Src/Common/FwAvaloniaPreviewHost/FwAvaloniaPreviewHostTests/FwAvaloniaPreviewHostTests.csproj` passes repeatedly (2 UIA tests) - `scripts/Agent/Run-AvaloniaPreview.ps1 -BuildOnly` - the exact previously failing command now succeeds: `./build.ps1 -BuildAvalonia -Project` `"Src/Common/FwAvalonia/FwAvalonia.csproj" -NodeReuse $false` - `./build.ps1 -BuildAvalonia -BuildTests -Project` `"Src/Common/FwAvaloniaPreviewHost/FwAvaloniaPreviewHost.csproj"` `-SkipRestore -SkipDependencyCheck -NodeReuse $false` succeeds
Pull the preview-host idea onto this branch, but refactor it to the branch's net48-only policy instead of the prototype branch's net8 shape. Adds a new net48 `FwAvaloniaPreviewHost` that discovers preview modules via assembly attributes in `FwAvalonia`, along with a runner script at `scripts/Agent/Run-AvaloniaPreview.ps1`. Wires the current lexical-edit POC into that host with: - `FwPreviewModuleAttribute` / `IFwPreviewDataProvider` - `PocPreviewWindow` + `PocPreviewDataProvider` - stable Avalonia automation IDs/names on the slice, text editors, labels, and morph-type chooser Adds native desktop automation tests in `FwAvaloniaPreviewHostTests` using `System.Windows.Automation` that launch the real preview-host exe and verify: - the main preview window and core controls expose stable automation IDs - the morph-type button supports InvokePattern and shows the popup list Updates `build.ps1 -BuildAvalonia` so the optional Avalonia lane also builds `FwAvaloniaPreviewHostTests` when `-BuildTests` is requested. Validated: - `dotnet build` `Src/Common/FwAvaloniaPreviewHost/FwAvaloniaPreviewHost.csproj` - the preview-host UIA test project passes repeatedly (2 UIA tests) - `scripts/Agent/Run-AvaloniaPreview.ps1 -BuildOnly` - the exact previously failing command now succeeds: `./build.ps1 -BuildAvalonia -Project` `"Src/Common/FwAvalonia/FwAvalonia.csproj" -NodeReuse $false` - `./build.ps1 -BuildAvalonia -BuildTests -Project` `FwAvaloniaPreviewHost.csproj` `-SkipRestore -SkipDependencyCheck -NodeReuse $false` succeeds
|
|
Integrate the real app-side feature flag path for the lexical-edit Avalonia POC while keeping the WinForms DataTree path unchanged by default. Changes: - `RecordEditView` now uses `LexicalEditSurfaceResolver` and `LexicalEditSurfaceFactory` to select the visible surface. - When `FW_AVALONIA_LEXEDIT` is enabled, `RecordEditView` swaps the visible control to `PocWinFormsHostControl`, a WinForms wrapper around the Avalonia POC slice hosted through `WinFormsAvaloniaControlHost`. - `LexicalEditPocMapper` projects the current `LexEntry` into the detached DTO used by the Avalonia POC (lexeme form, morph type, first-sense gloss). - `xWorks` now references `FwAvalonia`; Avalonia package references are kept local to `FwAvalonia` so they do not pollute the `xWorks` restore graph. - The preview host and its UIA tests now use isolated `bin` outputs instead of the shared `Output/Debug` folder, avoiding runtime assembly-version clashes. Validated: - `dotnet build Src/xWorks/xWorks.csproj` - `dotnet build Src/xWorks/xWorksTests/xWorksTests.csproj` - `dotnet test Src/xWorks/xWorksTests/xWorksTests.csproj` with the `LexicalEditPocMapperTests` filter - `dotnet test` on the preview-host UIA test project passes - `./build.ps1 -BuildAvalonia -Project "Src/xWorks/xWorks.csproj"` with `-SkipRestore -SkipDependencyCheck -NodeReuse $false` - `./build.ps1 -BuildAvalonia -Project` `"Src/Common/FwAvalonia/FwAvalonia.csproj" -NodeReuse $false`
Introduce an app-wide user preference named `UIMode` with values `Legacy` and `New`. The preference is persisted in `FwApplicationSettings` and mirrored into the active `PropertyTable` at startup so existing colleague/property-change broadcasts can react to it immediately. `LexOptionsDlg` now exposes the setting on the Interface tab. When the user changes it and clicks OK: - the new mode is saved to app settings - the runtime `PropertyTable` mirror is updated - the current lexical edit view reloads without requiring an app restart `RecordEditView` now resolves its visible surface from the persisted UIMode (preference first, env var still available as a developer override), lazily initializes the hidden legacy DataTree and the embedded Avalonia host, and switches between them live when the UIMode property changes. Validated: - `dotnet build Src/Common/FwUtils/FwUtils.csproj` - `dotnet build Src/LexText/LexTextControls/LexTextControls.csproj` - `dotnet build Src/xWorks/xWorks.csproj` - `dotnet build Src/Common/FwAvalonia/FwAvaloniaTests/FwAvaloniaTests.csproj` - `dotnet test Src/Common/FwAvalonia/FwAvaloniaTests/FwAvaloniaTests.csproj` with `LexicalEditSurfaceResolverTests` - `dotnet test Src/xWorks/xWorksTests/xWorksTests.csproj` with `LexicalEditPocMapperTests` - `dotnet test` on the preview-host UIA test project - `./build.ps1 -BuildAvalonia -Project "Src/xWorks/xWorks.csproj"` with `-SkipRestore -SkipDependencyCheck -NodeReuse $false`
This is the first of a few PRs for the Lexical Edit Avalonia migration.
Focus reviewers on the overall OpenSpec plan and Phase 1/2 characterization tests.
Summary:
Validation evidence:
This change is