Skip to content

Phase-1 base: Avalonia migration spine (UIMode defaults Legacy)#964

Draft
johnml1135 wants to merge 5 commits into
mainfrom
phase1-base
Draft

Phase-1 base: Avalonia migration spine (UIMode defaults Legacy)#964
johnml1135 wants to merge 5 commits into
mainfrom
phase1-base

Conversation

@johnml1135

@johnml1135 johnml1135 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Phase-1 base: Avalonia migration spine

First of a 4-PR stack landing Phase 1 of the WinForms→Avalonia migration. Every Avalonia surface is gated behind the UIMode setting, which defaults to Legacy (Src/Common/FwUtils/Properties/Settings.Designer.cs) — so default users see no change.

What this PR contains

  • The Avalonia migration framework: region/composer (FullEntryRegionComposer), view-definition IR, owned controls, seam contracts, plugin registry.
  • The base detail-editor surfaces active under UIMode=New: lexiconEdit, lexiconEditPopup, notebookEdit, posEdit.
  • The browse table view code (LexicalBrowseView, LexicalBrowseHostControl, BulkEditBarView, ClerkBrowse*) and its RecordBrowseView wiring — shipped dormant (it's fused to base wiring and can't be cleanly carved out; activated by the table follow-up).
  • Skills/infra and the roadmap's core proposal/design/tasks/spec, plus the landed openspec change specs (lexical-edit-avalonia-migration, shared-editable-virtualized-table, avalonia-multi-writing-system-text-foundation) — pared back from the original diff (see "Branch cleanup" below).
  • Back-out of 4 cleanly-removable unwired Avalonia dialogs (SpecialCharacter, WritingSystemProperties, DeleteConfirmation, LexReferenceDetails); 13 dialog UIs + a shared MessageBox ride behind UIMode=New, each verified wired to a real WinForms call site.

Inert follow-up surfaces

The interlinear and rule-formula detail editors are carved out to the stacked follow-up PRs below (their openspec proposals and view files are not in this PR). The browse table and ChorusNotesBarControl (the Chorus/FLExBridge notes bar) ship here but inert. The inert lists are LexicalEditSurfaceRegistry.Phase1FollowUpSurfaceTools and LexicalEditSurfaceResolver.Phase1FollowUpBrowseTools; the migration skill documents the activation recipe.

Stack (each merges into the one above)

  1. thismain
  2. phase1-followup-interlinearphase1-base
  3. phase1-followup-rulephase1-followup-interlinear
  4. phase1-followup-tablephase1-followup-rule

Migration per-screen docs (the basis for JIRA tickets) and the full 13-stage migration-program planning material (speculative future-phase work, not needed to review this PR) both live on the separate, never-merged phase1-docs branch (draft PR, explicitly marked do-not-merge).

Verification

Whole-solution build green. Surface-registry census: 6 custom-slice classes classified (LexemeEditorBurnDownTests); 7 tools in LexicalEditSurfaceRegistry.Phase1FollowUpSurfaceTools; 8 tools in LexicalEditSurfaceResolver.Phase1FollowUpBrowseTools. Full CI-equivalent test run passes except 38 pre-existing, environment-specific test failures unrelated to this branch (a cross-worktree test-data collision, fixed separately in #979). Note: two of the hardening test files (RegionCommandAdapterHardeningTests.cs, RegionObjectCommandExecutionTests.cs) run roughly half their cases only on the manual [Explicit] desktop lane, not CI — "tests added" for these files should not be read as "tests running in CI." Draft until the stack is reviewed top-to-bottom.

Branch cleanup (post-review)

A multi-part internal review of this branch (PR_964_review.md, not included in this diff) found and fixed a substantial amount of unrelated or stale content bundled into the original diff. Squashed into this single commit; highlights:

  • Removed content unrelated to the Avalonia migration: an unrelated installer-docs deletion and an ungated Legacy-mode rendering change were reverted, and a misplaced/stale stray doc file was dropped.
  • Split two unrelated-but-legitimate fixes into their own PRs: #978 (VersionInfoProvider copyright/version-string bugs, a stale RegFree.targets entry, an opsx-prompt refactor) and #979 (a pre-existing cross-worktree test-collision bug in RealDataTestsBase, found while verifying this branch but unrelated to it).
  • Fixed a real build break (RecordBrowseView.cs used a pub/sub API signature main had already replaced) and a real product bug (the Avalonia refresh controller could stay unwired when a tool loads directly into UIMode=New) — and the test proving it (RecordEditViewSwitchTests) is now green: the shared idle-queue test-drain helper had never actually worked in this environment (it reflected into a private method whose message-pump politeness check always aborted), which also meant 5 test fixtures weren't proving what they looked like they proved. Centralized a correct drain in XWorksAppTestBase, which surfaced and fixed a second bug (2 fixtures missing a layout/parts Inventory bootstrap 2 sibling fixtures already had). All affected tests pass; full xWorksTests suite: 1645 passed, 0 failed.
  • Pared back ~7,500 lines of speculative future-phase openspec planning docs, deleted two fully superseded proposals and stale datatree-model-view-separation specs asserting an architecture that was never built, and removed remaining POC/spike language and stale cross-references repo-wide (including a real stale-default-value bug in Run-AvaloniaPreview.ps1).
  • Gated ChorusNotesBarControl as a Phase-1 follow-up surface (it was live, contradicting its "dormant" framing); wired the one missing UIMode gate (GoLinkEntryDlgListener.OnGotoLexEntry); documented one honest parity deferral (MsaInflectionFeatureListDlgLauncher switch-tools navigation); removed dead API and ~20 orphaned localization keys; strengthened the FwAvalonia engine-isolation audit to match its own documented symbol list; fixed three evidence-language issues in Path3BundleTests; reconciled dialog spacing tokens against real measured WinForms control geometry.
  • Code coverage review: ran line-level coverage analysis across the PR's own added code, found and closed the highest-value real gaps (35 new tests total: MSA-launcher resolver switch/not-found branches, ApplyInflectionFeatures's undo-task-safety null guards, an XmlViewsUtils part-ref regression test, and the pure/testable logic across all remaining under-tested Lcm*Launcher classes), and documented the genuinely modal-locked gates that can't be unit-tested without a production testability seam. Full breakdown below.

9-group breakdown: scope, gaps found, what was fixed vs. left as a finding

Every file in this PR's diff (562 files) is accounted for in exactly one of the 9 groups below (verified programmatically — zero unclassified). Full detail, file-by-file evidence, and exact test names are in PR_964_review.md §18.

# Group Files Scope Gaps found Fixed Left as a finding (owner decision needed)
1 Avalonia core frameworkSrc/Common/FwAvalonia/{Region,ViewDefinition,Seams,Preview}/, root FwAvalonia/*, FwAvaloniaPreviewHost/ 84 The framework substrate: XML-to-IR compiler, composer/render layer, framework-neutral seams, preview host 3 1 Preview/ code ships inside the production DLL (excluding it would break FwAvaloniaPreviewHost.exe's current project-reference wiring, a build-topology change); FwAvaloniaStrings.resx has orphaned keys (deleting/rewiring changes shipped localization surface)
2 Avalonia framework + dialog testsFwAvaloniaTests/, FwAvaloniaDialogsTests/, RenderTestInfrastructure/, RenderVerification/ 100 4-tier test suite: unit, headless-UI, visual/snapshot, parity-bundle 2 0 Extracting a shared AvaloniaHeadlessDialogFixtureBase (~24 files) and a shared ViewDefinitionModel test-builder (~9 files) are DRY/maintainability refactors of existing, passing tests, not coverage gaps; real but non-trivial regression risk, recommended as a dedicated follow-up rather than bundled in here
3 Avalonia dialogs (product)FwAvaloniaDialogs/ (excl. tests) 71 MVVM dialog kit: 13 dialog UIs + shared MessageBox 0 -- --
4 Product wiring / integrationSrc/xWorks/*.cs (excl. tests), Common/Controls/, Common/FwUtils/, Common/FieldWorks/, XCore/, Common/Framework/, Common/SimpleRootSite/, Common/ViewsInterfaces/ 62 The highest-risk layer: where a Legacy regression would surface for existing users 3 3 --
5 Src/xWorks/xWorksTests 47 Test evidence for the composer/plugin-registry, the 4 active surfaces, the dormant browse table 2 2 --
6 Src/LexTextLexTextControls/, Lexicon/, Morphology/, Interlinear/, LexTextDll/ 58 ~45 if (UIMode==New) {Avalonia} else {legacy} dialog-launcher gates; 13 new Lcm*Launcher glue classes 1 (coverage) 1 (35 new tests) 3 modal-locked gate/dispatch sites (EntryDlgListener.cs, 3 gates; MSAPopupTreeManager.cs, 2 gates; LexReferenceMultiSlice.GetRootObject) — every path ends in a real modal dialog this codebase deliberately excludes from unit tests; fixing needs a production testability seam, a decision for whoever owns this PR
7 openspec — landed changes + roadmap core 95 The honest record of what shipped (11,321 of 11,993 lines); only 672 lines remain forward-looking, both substantially implemented 1 0 datatree-model-view-separation's one openspec validate --strict failure predates this PR entirely — not this PR's scope to fix
8 .claude/skills + .github 31 Migration hub skill, satellite review skills, dialog-authoring skill, MCP docs 0 -- --
9 Repo mechanics / misc.sln/.proj/Directory.Packages.props/root docs 14 Mechanical project registration + package additions 0 -- --

Totals: 12 gaps found across the PR: 7 fixed (35 new tests + 4 doc/comment corrections), 5 left as explicit findings (2 production-restructuring, 2 test-refactor-only, 1 pre-existing-elsewhere) — none silently dropped.


This change is Reviewable

@github-actions

This comment has been minimized.

johnml1135 added a commit that referenced this pull request Jul 1, 2026
…ing docs

Per deep review of the Phase-1 base PR, this removes ~10,300 lines of
openspec content that does not belong in the base PR:

Deleted entirely (self-declared superseded, or already implemented on a
sibling branch with no home needed here):
- openspec/changes/graphite-transition-support/ (self-labeled superseded
  throughout; content restated elsewhere)
- openspec/changes/fieldworks-avalonia-shell-migration/ (self-labeled
  folded into/superseded by avalonia-end-game)
- openspec/changes/avalonia-interlinear-editor/ (implemented on
  phase1-followup-interlinear, commit 3c5893e)
- openspec/changes/avalonia-rule-formula-editor/ (implemented on
  phase1-followup-rule, commit 2c142bc)

Deleted here for relocation to phase1-docs (speculative future-phase
planning with real future value, not needed to review/merge this PR):
- openspec/changes/avalonia-migration-roadmap/complete-migration-program.md,
  epics/**, reviews/** (JIRA-epic drafting for unstarted stages 5-13)
- openspec/changes/legacy-screenshot-capture/ (dev tooling supporting a
  Docs/migration/ effort this PR isn't carrying)
- openspec/changes/avalonia-end-game/ (depends on a Phase-1 burn-down this
  PR hasn't finished)

Trimmed/deleted within datatree-model-view-separation (this change's own
proposal.md/hybrid-alignment.md already carry a 2026-06-09 supersession
note saying DataTreeModel/SliceSpec/IDataTreeView "should not be built";
these were the pieces that never caught up to that note):
- Deleted specs/datatree-model/spec.md (asserted the abandoned
  DataTreeModel/SliceSpec/IDataTreeView requirements as live ADDED
  reqs); kept specs/datatree-partial-split/spec.md (the partial-class-split
  slice the proposal says remains valid as optional legacy maintenance)
- Deleted three overlapping draft test plans for the abandoned design:
  testing-approach-2.md, test-plan-forms.md, test-plan-forms-future.md
- Deleted stale coverage-gap planning docs:
  specs/changes-from-test-before-refactor/coverage-wave2-test-matrix.md
  and ".../tests to fix coverage gaps.md"
- Trimmed datatree-mental-model.md to the current-state description only,
  removing the "target shape after split" section describing the
  abandoned architecture

Fixed two stale artifacts that never caught up to their sibling
supersession notes:
- avalonia-migration-roadmap/specs/avalonia-migration-roadmap/spec.md:
  added a supersession note to the "DataTree split is the first migrated
  region" requirement and added an as-built scenario describing the
  actual region-model path (ViewDefinitionModel/LexicalEditRegionModel),
  matching the note already in this change's own design.md
- lexical-edit-avalonia-migration/architecture-diagrams.md: removed the
  IPropertyStateStore port node (never built per task 18.6; state flows
  through IRecordNavigationContext + host PropertyTable), annotating the
  Navigation port instead

Kept as-is per review: avalonia-migration-roadmap/{proposal,design,
tasks}.md + specs/ (the ordered roadmap every later stack PR needs),
avalonia-multi-writing-system-text-foundation/ (substantially shipped,
not speculative), and the rest of datatree-model-view-separation
(design.md/tasks.md/proposal.md already carry accurate snapshot framing).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
First of a 4-PR stack landing Phase 1 of the WinForms->Avalonia migration.
Every Avalonia surface is gated behind the UIMode setting, which DEFAULTS TO
"Legacy" (Src/Common/FwUtils/Properties/Settings.Designer.cs), so default
users see no behavioral change.

Contents:
- The Avalonia migration framework: region/composer (FullEntryRegionComposer),
  the typed view-definition IR compiled from XML layouts, owned dense controls,
  the seam contracts, and the region-editor plugin registry.
- The base detail-editor surfaces active under UIMode=New: lexiconEdit,
  lexiconEditPopup, notebookEdit, posEdit.
- The Avalonia browse table (LexicalBrowseView, LexicalBrowseHostControl,
  BulkEditBarView, ClerkBrowse*) and its RecordBrowseView product wiring,
  shipped DORMANT (fused to base wiring; activated by the table follow-up PR).
- 13 dialog UIs + a shared MessageBox riding behind UIMode=New, each verified
  wired to a real WinForms call site; 4 cleanly-removable unwired Avalonia
  dialogs backed out (SpecialCharacter, WritingSystemProperties,
  DeleteConfirmation, LexReferenceDetails).
- ChorusNotesBarControl (the Chorus/FLExBridge notes bar) rides as a Phase-1
  follow-up surface alongside the browse table and the interlinear/
  rule-formula plugins -- built, but not registered in this base PR.
- Migration skills/playbook (incl. the inert-surface activation recipe), the
  roadmap's core proposal/design/tasks/spec, and the landed openspec change
  specs (lexical-edit-avalonia-migration, shared-editable-virtualized-table,
  avalonia-multi-writing-system-text-foundation).

The interlinear and rule-formula detail editors are carved out to their own
stacked follow-up PRs; the inert tool lists are
LexicalEditSurfaceRegistry.Phase1FollowUpSurfaceTools and
LexicalEditSurfaceResolver.Phase1FollowUpBrowseTools. Migration per-screen
docs (the JIRA-ticket basis) and the full 13-stage migration-program planning
material live on the separate never-merged phase1-docs branch (the latter
also mirrored to chore/relocate-roadmap-planning-docs).

Phase 2 (avalonia-end-game: net multiplatform + shell conversion + WinForms
removal) is planned only and gated on Phase 1 + tester burn-down; its
proposal lives with the roadmap material above, not in this PR.

Post-review cleanup folded into this commit (see PR_964_review.md for the
full audit trail):
- Removed content unrelated to this migration that had been swept in:
  an unrelated installer-docs deletion and an ungated Legacy-mode rendering
  change were reverted, and a misplaced/stale stray doc file was dropped.
- Split two unrelated-but-legitimate fixes into their own PRs rather than
  carrying them here: #978 (VersionInfoProvider copyright/version-string
  bugs, a stale RegFree.targets entry, an opsx-prompt refactor) and #979
  (a pre-existing cross-worktree test-collision bug in RealDataTestsBase,
  found while verifying this branch but unrelated to it).
- Fixed a real build break (RecordBrowseView.cs used a pub/sub API signature
  main had already replaced) and a real product bug (the Avalonia refresh
  controller could stay unwired when a tool loads directly into UIMode=New,
  fixed in RecordEditView.cs).
- Pared back ~7,500 lines of speculative future-phase openspec planning
  docs (the 13-stage migration program, legacy-screenshot-capture tooling,
  the end-game proposal) to a separate branch, and deleted two fully
  superseded proposals and stale DataTree-model-view-separation specs
  asserting an architecture that was never built.
- Wired the one missing UIMode gate (GoLinkEntryDlgListener.OnGotoLexEntry),
  documented one honest parity deferral (MsaInflectionFeatureListDlgLauncher
  switch-tools navigation), removed dead API and orphaned localization keys,
  strengthened the FwAvalonia engine-isolation audit to match its own
  documented symbol list, fixed three evidence-language issues in
  Path3BundleTests, and reconciled dialog spacing tokens against real
  measured WinForms control geometry.

Verification: whole-solution build green. Surface-registry census: 6
custom-slice classes classified (LexemeEditorBurnDownTests); 7 tools in
LexicalEditSurfaceRegistry.Phase1FollowUpSurfaceTools; 8 tools in
LexicalEditSurfaceResolver.Phase1FollowUpBrowseTools. Full CI-equivalent test
run: all tests pass except 38 pre-existing, environment-specific
RealDataTestsBase cross-worktree-collision failures (fix is PR #979, kept
separate) and one test-harness limitation in RecordEditViewSwitchTests
documented in PR_964_review.md (the underlying product bug is fixed; the
test's idle-queue draining has a separate, deeper, pre-existing issue that
needs its own follow-up).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

NUnit Tests

    1 files  ±    0      1 suites  ±0   14m 20s ⏱️ + 3m 41s
6 100 tests +1 801  6 019 ✅ +1 793  81 💤 +8  0 ❌ ±0 
6 109 runs  +1 801  6 028 ✅ +1 793  81 💤 +8  0 ❌ ±0 

Results for commit c37eb02. ± Comparison against base commit 323a022.

♻️ This comment has been updated with latest results.

johnml1135 and others added 3 commits July 1, 2026 13:03
Follow-up to the review's remaining small polish items:

- Removed "POC"/"spike" language that mischaracterized shipped, active code
  as throwaway prototype work: Directory.Packages.props' Avalonia package
  group (was "POC spike only"), LexicalEditSurfaceResolver.cs's doc comments,
  OptionsDialogView.axaml.cs, dialog-conversion.md's "spike" section header,
  and two test project file headers. Also fixed a real, functional staleness
  this surfaced: scripts/Agent/Run-AvaloniaPreview.ps1's default -Module
  value and example were still "lexical-edit-poc", which does not match the
  actually-registered "lexical-edit-preview" module id in
  AssemblyPreviewModules.cs -- the documented default command would have
  silently found no module.
- Fixed stale pointers to the renamed PocDensity.cs -> FwAvaloniaDensity.cs
  in fieldworks-avalonia-ui/SKILL.md and architecture-patterns.md.
- Corrected two files describing DataTreeTimingBaselines.json as "committed"
  when it's gitignored and regenerated per machine.
- Indexed Launch-FieldWorksInvisible.ps1 in fieldworks-winapp/SKILL.md's
  script list; it claimed default-launcher status but wasn't discoverable
  from the documented navigation path.
- Deleted Docs/avalonia-migration-approach-comparison.md (a decision record
  for a decision already made and recorded elsewhere) and updated its three
  referencing files to point at the surviving decision record instead of a
  dead link.
- Rewrote avalonia.instructions.md's forward references to a
  specs/010-advanced-entry-view/ spec and an AdvancedEntry.Avalonia module
  that don't exist in this repo, replacing them with the actual current
  structure and a real, working preview-module example.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
These are the screenshot-capture tools that produce the Docs/migration/
per-screen documentation (which itself lives entirely on the phase1-docs
branch, not here) -- they belong with what they produce, not on the code
migration branches. The openspec legacy-screenshot-capture proposal
describing this tooling was already relocated in an earlier commit; these
are the actual scripts it describes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The 5 new RecordEditView/region test fixtures each had a local
DrainMediatorAndIdleQueues that invoked IdleQueue's private
Application_Idle via reflection. That method's ShouldAbort() guard peeks
the real Win32 message queue and bails out unconditionally in this test
process (there is no real message loop), so queued idle work like
RecordEditView.ShowRecordOnIdle never actually ran and these tests never
exercised their target code path.

Centralize a correct drain in XWorksAppTestBase that pumps IdleQueue's
public ICollection surface directly instead. Running the real
ShowRecordOnIdle path this way surfaced a second, previously-latent gap:
RecordEditViewSwitchTests and RecordClerkNavigationContextTests never
bootstrapped the legacy layout/parts Inventory that DataTree.ShowObject
needs, causing an NRE the moment idle-queued work actually ran. Bootstrap
it the same way RegionObjectCommandExecutionTests/
RegionCommandAdapterHardeningTests already do.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Test coverage (34 new tests, 0 production changes, 0 regressions):
- Extend LcmMsaCreatorDialogLauncherTests/LcmInsertEntryDialogLauncherTests
  (already done in prior commit) plus the remaining 9 Lcm*Launcher classes'
  pure/testable logic: guard clauses, resolve/exclude branches, and the
  Inflection-vs-Phonological Apply(deleteWhenEmpty) divergence that was
  previously untested in either direction.
- Add a regression test for the XmlViewsUtils.cs part-ref caller-propagation
  fix (verified it actually catches a reversion of that fix before finalizing).

Doc/comment fixes (no behavior change):
- RecordBrowseView.cs: correct a doc comment that overstated the Avalonia
  browse overlay as live for lexiconBrowse today (it's inert for every tool
  in this PR); cross-reference the "flip this array" mechanism with
  LexicalEditSurfaceResolver.cs so it's discoverable from either side.
- Flag the 3 rule-formula composition test files as early groundwork that
  landed ahead of the avalonia-rule-formula-editor follow-up PR, and correct
  NaturalClassComposeTests.cs's overstated "flipped to Avalonia" claim.

See PR_964_review.md §18 for the full 9-group gap triage, including which
items were left as findings (production-restructuring or genuinely
modal-locked) rather than force-fixed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant