diff --git a/.claude/skills/fieldworks-avalonia-ui/SKILL.md b/.claude/skills/fieldworks-avalonia-ui/SKILL.md new file mode 100644 index 0000000000..3aac802ae2 --- /dev/null +++ b/.claude/skills/fieldworks-avalonia-ui/SKILL.md @@ -0,0 +1,98 @@ +--- +name: fieldworks-avalonia-ui +description: "Build, review, or fix Avalonia UI code in FieldWorks: XAML, MVVM, view models, owned controls, headless tests, preview host, accessibility identity, and product-vs-preview wiring. Use for any change under Src/Common/FwAvalonia/, Src/Common/FwAvaloniaPreviewHost/, or Src/**/*.Avalonia/, and for net48/net8 Avalonia test changes — even if the request only mentions a control, a binding, a style, or a flaky UI test. For whole-surface migration planning use fieldworks-winforms-to-avalonia-migration first." +--- + +# FieldWorks Avalonia UI + +## Use This For + +- Avalonia XAML, view models, commands, lifetimes, dispatching, and + resource/style changes. +- New or changed projects under `Src/**/**/*.Avalonia/`, + `Src/Common/FwAvalonia/`, and `Src/Common/FwAvaloniaPreviewHost/`. +- Preview Host module registration, sample data providers, and UI + diagnostics (see `.github/instructions/avalonia.instructions.md` for + build/preview commands and project layout rules). +- UI host wiring that selects between Avalonia and legacy UI — apply + `fieldworks-ui-wiring-review` alongside this skill. + +## Start From the Established Patterns + +Do not design controls or seams from scratch. The migration hub skill +(`fieldworks-winforms-to-avalonia-migration`) documents the decided +architecture; its `references/architecture-patterns.md` covers owned +controls, writing-system text fields, dialogs/flyouts, validation, and +lifetime. Canonical code to imitate: + +- Owned field controls: `Src/Common/FwAvalonia/Region/FwFieldControls.cs`, + `FwOptionPicker.cs`, `RegionMenuFlyout.cs`, `HoverReveal.cs` +- Region view + focus memory: `LexicalEditRegionView.cs`, + `RegionFocusMemory.cs` +- Seams (scheduler, lifetime, clipboard, edit sessions): + `Src/Common/FwAvalonia/Seams/ISeams.cs` +- Headless test setup: `Src/Common/FwAvalonia/FwAvaloniaTests/TestAppBuilder.cs`; + examples in `RegionEditingTests.cs`, `VisualParityAndDensityTests.cs` +- Density constants: `Src/Common/FwAvalonia/Poc/PocDensity.cs` + +## Required Checks + +- Use current Avalonia docs for uncertain APIs; do not guess dispatcher, + headless, automation, or binding behavior. +- Keep product UI strings localizable (`FwAvaloniaStrings.resx` or the + StringTable lane); prototype hardcoded strings must be called out as gaps. +- Stamp stable, nonlocalized `AutomationProperties.AutomationId` (derived + from IR `StableId` where applicable) and localized + `AutomationProperties.Name` on user-facing controls. +- UI logic stays in bindings/view models where practical; avoid + logic-heavy code-behind. +- For any Avalonia "select from a list" surface, prefer the shared + `FwOptionPicker` pattern in `Src/Common/FwAvalonia/Region/FwOptionPicker.cs` + (AutoCompleteBox-based, keyboard-safe, search-capable, compact density) + over ad hoc `ListBox` popups or one-off editable selectors. Reach for a raw + `ComboBox` only when the UX explicitly needs an always-visible inline combo + rather than the shared flyout selector. +- Do not fix Avalonia keyboard, focus, filtering, selection, popup, or + rendering bugs by patching `System.Windows.Forms` hosts, WinForms + interop message handling, or other legacy host-only routes unless the + task explicitly targets interop behavior. Default to fixing the issue + inside the Avalonia control tree or Avalonia-owned seams. +- Marshal to the UI thread through `IUiScheduler` (or Avalonia dispatcher + in non-region code); no hidden `Task.Run`, no sync-over-async. +- Keep preview data lightweight unless the change explicitly opts into + LCModel/project data; product-facing paths use real edit-session/domain + contracts — detached DTO-only models remain preview-only. +- Headless tests: simulate input on `Window`, flush with + `Dispatcher.UIThread.RunJobs()`, and capture visual regression frames + with Skia (`UseHeadlessDrawing=false` + `CaptureRenderedFrame()`). +- Evidence runs through `./build.ps1` and `./test.ps1` via the normal repo + graph, not branch-only lanes. + +## Review Red Flags + +- A Common project directly references a feature module without an + explicit architecture decision. +- Preview-only code launched from product UI without a feature gate. +- Tests manually call `OnPropertyChanged(...)`, `ShowRecord()`, or similar + instead of proving the real broadcast/wiring path. +- The active Avalonia path drives hidden legacy rendering/menu + infrastructure (see the hub skill's hard rules). +- Sleep-based or timing-sensitive UI tests. +- Claims of accessibility, localization, IME, or keyboard parity without + executable evidence (see the hub skill's + `references/parity-evidence.md` §"Evidence language"). + +## Handoff + +Report Avalonia docs consulted, tests run, remaining prototype gaps, +whether the change is product-facing or preview-only, and how the live +wiring path was validated for each affected host. For parity work, say +whether visual evidence is control-level headless capture or live desktop +capture, and which automation identities were assigned. + +## Keep This Skill Current + +When a control pattern, headless-test technique, or Avalonia API gotcha +proves out (or a pointer above goes stale), update this skill in the same +PR — and route durable architecture lessons through the protocol in +`fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md`. diff --git a/.claude/skills/fieldworks-localization-review/SKILL.md b/.claude/skills/fieldworks-localization-review/SKILL.md new file mode 100644 index 0000000000..b735dd43e5 --- /dev/null +++ b/.claude/skills/fieldworks-localization-review/SKILL.md @@ -0,0 +1,72 @@ +--- +name: fieldworks-localization-review +description: "Review or change FieldWorks user-facing strings: .resx resources, localization keys, the StringTable lane for field labels, Crowdin-facing assets, and localization-sensitive automation metadata. Use whenever a change adds or edits any user-visible text in WinForms or Avalonia, adds a new UI project, touches resource files, or claims localization parity — even for a single new label or error message." +--- + +# FieldWorks Localization Review + +## Use This For + +- Product-facing text in WinForms, Avalonia, settings UI, dialogs, + validation messages, fallback or unsupported-surface text, and promoted + preview paths. +- `.resx` additions or changes, localization key flow, and Crowdin-sensitive + resource updates. +- Automation metadata where `Name`, tooltip, or label is localized but + stable `AutomationId` must remain nonlocalized. + +## The Two Lanes (Avalonia surfaces) + +1. **Field labels** come from layout data and resolve through the legacy + StringTable lane (`XmlUtils.GetLocalizedAttributeValue`, + `strings-{locale}.xml`) at render time. The view-definition IR carries a + `LocalizationKey` per node; never bake English label text into the IR or + region model. +2. **Product messages** (Save, Cancel, validation errors, unsupported-row + text) live in `Src/Common/FwAvalonia/FwAvaloniaStrings.resx` with + translator comments; accessor class `FwAvaloniaStrings.cs`; key coverage + locked by tests in + `Src/Common/FwAvalonia/FwAvaloniaTests/RegionEditingTests.cs`. + +## Required Checks + +- Product-facing user-visible strings live in `.resx` or the established + localization mechanism; preview-only hardcoded text stays clearly + preview-only. +- New UI mode labels, fallback or unsupported messages, validation errors, + and diagnostics are localized before a product path is exposed. +- Stable `AutomationId` and other selectors remain nonlocalized; localized + names, tooltips, and labels may vary by locale. +- Resource keys and files align with existing Crowdin and repo conventions. +- New SDK-style csprojs declare `` explicitly — the Crowdin + satellite-assembly build + (`Build/Src/FwBuildTasks/Localization/ProjectLocalizer.cs`) fails + without it. +- If localization parity is claimed, tests or evidence cover the localized + path and confirm selectors do not depend on localized text. English on + the Avalonia surface where legacy shows translations is a parity + failure, not cosmetics. + +## Review Red Flags + +- Hardcoded English text in product C#, XAML, or product-facing + preview-promotion paths. +- Field labels rendered raw from the IR without StringTable resolution. +- Tests or automation selectors depending on localized labels when stable + IDs exist or are required. +- A product route reusing preview-only placeholder text. +- Localization claims without resource updates or without identifying + remaining hardcoded strings. + +## Handoff + +List the resource files or keys touched, remaining hardcoded product +strings, automation identity strategy, and whether localized behavior has +executable evidence or is still pending. + +## Keep This Skill Current + +When a new localization lane, Crowdin constraint, or resource convention +appears (or a gap like the `` one is found), record it here +in the same PR; route durable lessons through +`fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md`. diff --git a/.claude/skills/fieldworks-managed-netfx-review/SKILL.md b/.claude/skills/fieldworks-managed-netfx-review/SKILL.md new file mode 100644 index 0000000000..3243a5dab5 --- /dev/null +++ b/.claude/skills/fieldworks-managed-netfx-review/SKILL.md @@ -0,0 +1,57 @@ +--- +name: fieldworks-managed-netfx-review +description: "Review or change FieldWorks managed C# code that crosses the .NET Framework 4.8 / C# 7.3 vs SDK-style net8 boundary: project files, language-feature compatibility, test discovery across both runtimes, UI-thread marshaling, and deterministic disposal. Use whenever a change touches a .csproj, adds C# to an unfamiliar project, moves code between net48 and net8 projects, or changes test runners — even if the compile passes locally." +--- + +# FieldWorks Managed NetFx Review + +## Compatibility Split + +- Legacy product code is .NET Framework 4.8 and C# 7.3 unless a project + explicitly targets modern .NET. The compiler will not always save you: + check the project's `LangVersion`/target before writing modern syntax. +- New Avalonia modules may target `net8.0-windows`; do not leak C# 8+ + syntax or net8-only APIs into net48 projects. Note that + `Src/Common/FwAvalonia/` itself is consumed from net48 hosts — verify a + project's actual target rather than assuming Avalonia ⇒ net8. +- Legacy `.csproj` files require explicit source inclusion; SDK-style + projects glob by default. A file added on disk is not necessarily in the + build. +- SDK-style projects need an explicit `` for the Crowdin + satellite-assembly build (see `fieldworks-localization-review`). + +## Required Checks + +- User-visible strings use `.resx` patterns where product-facing. +- UI and async code marshals to the correct UI thread (via `IUiScheduler` + in region code) and does not use sync-over-async. +- Disposable WinForms/GDI/LCModel/test resources are owned and disposed + deterministically; region code follows the `IRegionLifetime` rules + (idempotent disposal, late-callback suppression, event unsubscribe). +- Test discovery changes are validated across both net48 and net8 test + assemblies. +- Use repo scripts for evidence: `./build.ps1` and `./test.ps1` — never + bare `dotnet build` conclusions. + +## Review Red Flags + +- Nullable annotations, records, file-scoped namespaces, switch + expressions, or `using var` in net48/C# 7.3 projects. +- Broad project/test-runner changes justified only by one local test + passing. +- Hardcoded Debug paths or absolute repo assumptions in tests. +- Skipped tests used as evidence of covered behavior. +- A new project added to disk but missing from `FieldWorks.proj` + (traversal build) or `FieldWorks.sln` (IDE discovery). + +## Handoff + +Report target frameworks touched, project-file implications, test +commands/results, and any remaining compatibility risks. + +## Keep This Skill Current + +When a new cross-target pitfall, project-file gotcha, or runtime +difference bites a migration, add it here in the same PR; route durable +lessons through +`fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md`. diff --git a/.claude/skills/fieldworks-migration-scope-review/SKILL.md b/.claude/skills/fieldworks-migration-scope-review/SKILL.md new file mode 100644 index 0000000000..aac60f87d4 --- /dev/null +++ b/.claude/skills/fieldworks-migration-scope-review/SKILL.md @@ -0,0 +1,67 @@ +--- +name: fieldworks-migration-scope-review +description: "Review the scope and evidence claims of large FieldWorks migration PRs, OpenSpec changes, and foundational branches. Use when sizing or splitting a branch, judging draft-PR readiness, verifying that checked tasks match their evidence, or whenever a reviewer or author asks whether a migration PR is too big, mixed, or trustworthy." +--- + +# FieldWorks Migration Scope Review + +## Review Posture + +Treat foundational migration PRs as architecture and evidence packages. +The main question is whether reviewers can trust the scope, claims, and +validation boundary. + +## Required Checks + +- Scope review is branch-relative: compare `main..HEAD` or the merge-base + diff, not calendar-time commit lists. Same-day commits already on `main` + are not branch scope. +- Compare PR title/body/tasks against the actual diff. +- Classify files as plan/spec, characterization test, infrastructure, + prototype, product behavior, or unrelated change. +- When product or global UI wiring appears, trace preview-vs-product + routing and host/listener wiring separately from plan/test changes + (apply `fieldworks-ui-wiring-review`). +- Verify checked tasks match evidence language; downgrade claims when + evidence says substitute, placeholder, skipped, future, or partial — + the taxonomy is defined in + `fieldworks-winforms-to-avalonia-migration/references/parity-evidence.md` + §"Evidence language". +- Confirm validation gates are explicit: OpenSpec validation + (`openspec validate --strict`), targeted tests, normal + `./build.ps1` and `./test.ps1` coverage for Avalonia, and + `CI: Full local check` when ready. + +## Split Triggers + +- Product-visible behavior appears in a planning/test PR. +- Branch-only diff mixes product-visible wiring with planning/test/docs/ + prototype work. +- Common infrastructure directly depends on the first feature module + without an explicit decision. +- Test-runner/build graph changes are mixed with UI migration work. +- Unrelated behavior changes require their own review context. + +## Review Red Flags + +- A draft PR so broad that each reviewer must reverse-engineer intent. +- Scope complaints based on "commits made today" instead of the + branch-only diff against `main`. +- Evidence stale after rebase or differing from visible CI state. +- A prototype wired as if it were a product feature. +- Skill/playbook updates from the migration retrospective missing from a + PR that completed a migration phase (see the hub skill's workflow + step 10) — institutional knowledge is part of the deliverable. + +## Handoff + +Lead with blockers, then list what to remove, split, reword, or validate +before review. Call out false scope signals separately from real +branch-only scope problems. + +## Keep This Skill Current + +When a new split trigger, evidence-language term, or scope failure mode +shows up in a real review, add it here in the same PR; route durable +lessons through +`fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md`. diff --git a/.claude/skills/fieldworks-semantic-render-parity/SKILL.md b/.claude/skills/fieldworks-semantic-render-parity/SKILL.md new file mode 100644 index 0000000000..5f40e689d3 --- /dev/null +++ b/.claude/skills/fieldworks-semantic-render-parity/SKILL.md @@ -0,0 +1,82 @@ +--- +name: fieldworks-semantic-render-parity +description: "Capture or review FieldWorks parity evidence: semantic snapshots, render/visual baselines, layout parity, failure artifacts, XML view definitions, and the Avalonia presentation IR. Use whenever a task creates or evaluates snapshot tests, screenshot baselines, view-definition compilation output, or any claim that an Avalonia surface matches its WinForms predecessor." +--- + +# FieldWorks Semantic Render Parity + +Shared definitions (Path 3 bundle, evidence lanes, artifact naming) live in +`fieldworks-winforms-to-avalonia-migration/references/parity-evidence.md`. +This skill covers how to build and review the snapshots themselves. + +## Snapshot Discipline + +Semantic snapshots preserve behaviorally meaningful identity and omit +incidental layout noise. The snapshot is the anchor artifact of a parity +bundle: when visual evidence diverges, the snapshot explains whether the +cause is the XML import, slice filtering, editor registry, or rendering. + +## Include + +- Stable node ID and source layout/part identity. +- Which route produced the artifact (`Avalonia`, legacy fallback, or + blocked state) when a scenario can run through multiple hosts. +- Object/class binding, field/flid binding, editor kind, writing-system + metadata, visibility, ghost state, expansion, focus order, localization + key, and accessibility identity. +- Unsupported construct diagnostics with enough path context to fix the + source layout. + +## Exclude Or Normalize + +- Pixel bounds, transient generated names, timestamps, machine paths, + culture-dependent ordering, and realized-control counts unless the test + explicitly owns them. + +## Canonical Examples + +- IR model and snapshot projection: + `Src/Common/FwAvalonia/ViewDefinition/ViewDefinitionModel.cs` +- Snapshot/parity tests: + `Src/Common/FwAvalonia/FwAvaloniaTests/RegionViewingParityTests.cs`, + `ViewDefinitionTests.cs`, `BrowseAndCanonicalJsonTests.cs`, + `Path3BundleTests.cs` +- Import coverage tracking: `LayoutImportCoverageTests.cs` and + `Src/Common/FwAvalonia/ViewDefinition/LayoutImportCoverage.cs` +- Visual/density evidence: `VisualParityAndDensityTests.cs` + +## Render Evidence + +- Pixel/render tests need deterministic fixtures, clear thresholds, and + failure artifacts reviewers can inspect (classified failure summary, not + a raw diff image). +- A semantic snapshot is not a substitute for visual/render parity when + typography, density, wrapping, or native rendering seams are under + review — and vice versa. One lane per axis; see parity-evidence.md §2. +- Control-level Avalonia visual evidence may come from Avalonia.Headless + rendered frames when the scenario is explicitly control-scoped; desktop + workflow/accessibility claims still need live-window evidence. + +## Review Red Flags + +- A preview-only or lossy route presented as if it proved product parity. +- Placeholder metadata presented as real binding or writing-system parity. +- Snapshot tests updating large JSON blobs without a small behavioral + explanation of what changed and why. +- Cache invalidation tests that depend on sleeps or filesystem timestamp + luck. +- A new layout construct silently dropped by the importer instead of + producing a diagnostic node and a coverage-tracking entry. + +## Handoff + +State whether evidence is semantic, visual, accessibility/workflow, or +performance parity, and identify remaining unproven axes. When a Path 3 +bundle is used, name each artifact and which lane it proves. + +## Keep This Skill Current + +When snapshot fields, normalization rules, or fixture patterns change, or +a new artifact type joins the bundle, update this skill and +parity-evidence.md together in the same PR; record durable lessons via +`fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md`. diff --git a/.claude/skills/fieldworks-ui-wiring-review/SKILL.md b/.claude/skills/fieldworks-ui-wiring-review/SKILL.md new file mode 100644 index 0000000000..513e51a0ff --- /dev/null +++ b/.claude/skills/fieldworks-ui-wiring-review/SKILL.md @@ -0,0 +1,74 @@ +--- +name: fieldworks-ui-wiring-review +description: "Review or change FieldWorks UI wiring — app-setting and PropertyTable routing, mediator notifications, current-content switching, host replacement, preview-vs-product boundaries, and the global legacy-vs-Avalonia UI selection. Use whenever a change touches which UI host is active, how a setting reaches a screen, RecordEditView/currentContentControl routing, save/PrepareToGoAway paths, or fallback behavior — even if the diff looks like a one-line settings change." +--- + +# FieldWorks UI Wiring Review + +## Use This For + +- Global or screen-level UI mode selection. +- `PropertyTable`, app-setting, mediator, or listener changes that affect + which UI host is active. +- `RecordEditView`, `currentContentControl`, host replacement, save or + `PrepareToGoAway()` routing, focus or command target routing, and + preview-to-product promotion work. + +## Canonical Wiring + +The decided routing model is explicit per-host behavior — supported +Avalonia, explicit legacy fallback, or blocked — never silent fallback: + +- Surface selection: `Src/Common/FwAvalonia/LexicalEditSurfaceSelectionService.cs`, + `LexicalEditSurfaceResolver.cs` (behavior enum + routing logic) +- Approved legacy adapters: `Src/Common/FwAvalonia/Seams/ActiveHostContract.cs` +- Contract tests to imitate: + `Src/xWorks/xWorksTests/RecordEditViewActiveHostContractTests.cs`, + `Src/Common/FwAvalonia/FwAvaloniaTests/SurfaceAndHostContractTests.cs`, + `LexicalEditSurfaceResolverTests.cs` + +## Required Checks + +- Review scope against the branch-only diff (`main..HEAD`) and list every + host or consumer affected. +- Trace the full wiring path end to end: setting source, persisted state, + `PropertyTable` key, mediator or property broadcast, listener + registration, host reload path, focus or command target routing, save or + `PrepareToGoAway()` path, and fallback or blocked state. +- For global switches, verify each current consumer has an explicit + contract: supported Avalonia surface, explicit legacy fallback, or + resource-backed unsupported state. +- The active Avalonia route must not instantiate or drive hidden legacy + rendering or menu infrastructure except through `ActiveHostContract` + approved adapters — and prove the negative with a contract test, not by + inspection alone. +- Product wiring and preview wiring are reviewed separately; preview DTOs, + preview hosts, and spike-only semantics do not satisfy product routing. +- Validation uses the normal repo build and test path (`./build.ps1`, + `./test.ps1`) plus host-specific tests when wiring changes. + +## Review Red Flags + +- Tests manually call `OnPropertyChanged(...)`, `ShowRecord()`, or similar + handlers instead of driving the real setting and broadcast path. +- A preview-only mapper or detached DTO model sits on a product-facing + route. +- Hidden legacy `DataTree`, menu handler, or renderer is still initialized + and driven while Avalonia is the active host. +- A global setting changes unrelated screens without a manifest or + explicit fallback story. +- Build or test evidence relies mainly on branch-only optional lanes or ad + hoc commands. + +## Handoff + +Report the setting source, listeners, affected hosts, per-host fallback +state, executable proof of the live wiring path, and any remaining hidden +legacy dependencies. + +## Keep This Skill Current + +When a new host type, routing pattern, or wiring failure mode appears in a +migration, add it here (and a red flag if it is a review smell) in the same +PR. Durable lessons also go through +`fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md`. diff --git a/.claude/skills/fieldworks-uia2-parity-testing/SKILL.md b/.claude/skills/fieldworks-uia2-parity-testing/SKILL.md new file mode 100644 index 0000000000..4d2e19b139 --- /dev/null +++ b/.claude/skills/fieldworks-uia2-parity-testing/SKILL.md @@ -0,0 +1,85 @@ +--- +name: fieldworks-uia2-parity-testing +description: "Design or review FieldWorks UI automation and accessibility tests: UIA2, FlaUI, Appium, WinAppDriver, Avalonia.Headless, keyboard, focus, IME, and automation-id strategy. Use whenever a task adds, changes, or evaluates automated UI tests or accessibility/workflow parity claims for WinForms or Avalonia surfaces — including deciding whether a test belongs in the headless or desktop lane." +--- + +# FieldWorks UIA2 Parity Testing + +## Lane Separation + +- Avalonia.Headless is for fast in-process control, layout, view-model, + binding, and input tests. +- UIA2/FlaUI/Appium/WinAppDriver tests require realized desktop windows and + validate native accessibility trees, focus, invoke patterns, and product + integration. +- Do not call a headless smoke test a UIA2 baseline. + +## Role in the Parity Bundle + +In a Path 3 parity bundle (defined in +`fieldworks-winforms-to-avalonia-migration/references/parity-evidence.md`), +desktop automation contributes the workflow/accessibility lane only: +launcher/chooser reachability, focus movement and return, invoke/cancel/ +accept paths, native automation tree identity, and shell-level keyboard +behavior. It does not replace semantic snapshots or visual/render evidence; +report it alongside those artifacts for the same scenario id. + +## Canonical Examples + +- Headless app/test setup: + `Src/Common/FwAvalonia/FwAvaloniaTests/TestAppBuilder.cs`; input and + focus patterns in `RegionEditingTests.cs`, `RegionFocusMemoryTests.cs` +- Realized-window UIA smoke on the legacy product path: + `Src/xWorks/xWorksTests/WinFormsUiaSmokeTests.cs` +- Preview-host UIA: + `Src/Common/FwAvalonia/FwAvaloniaPreviewHostTests/PreviewHostUiaTests.cs` +- Automation-id locking: `Src/Common/FwAvalonia/FwAvaloniaTests/PocLexEntrySliceTests.cs` + +## Automation Identity + +Derive AutomationIds from the IR `StableId` (`{StableId}`, +`{StableId}.Label`, `{StableId}.{WsAbbrev}`), defined as code constants — +never resource keys, never localized text. Localized names/tooltips go on +`AutomationProperties.Name`. Owned controls need custom automation peers +when stock peers do not expose the required patterns. + +## Required Evidence + +- Stable automation IDs or accessible names for controls under test. +- Explicit coverage of focus movement, invoke/click path, popup/chooser + reachability, keyboard shortcuts, and failure artifacts. +- When UI mode or host wiring changes, desktop automation must cover the + real switch-driven host refresh or fallback behavior on realized windows; + manual handler calls or headless-only assertions do not prove product + wiring. +- Clear CI lane: headless can run broadly; desktop automation needs an + interactive Windows desktop or a configured automation host. + +## Review Red Flags + +- "Runs in the background" used for UIA2/Appium without explaining the + required desktop/session. +- Manual `OnPropertyChanged(...)` or similar handler invocation presented + as proof of live UI-mode wiring. +- Tests assert implementation internals instead of user-observable + accessibility behavior. +- Automation selectors rely on localized labels when stable IDs are + available or required. +- IME coverage claimed without a real text editor/control surface and + input-method evidence. (IME composition/commit is a known open gap for + rich-text scope — do not let a checkbox claim it implicitly.) +- Sleep-based waits instead of event-driven synchronization. + +## Handoff + +Classify each test as headless, native desktop automation, or smoke +substitute, and state what parity claim it can and cannot support. For +bundle work, say which workflow/accessibility assertions the desktop lane +proved, whether switch wiring/fallback was exercised on a realized window, +and which claims still need another lane. + +## Keep This Skill Current + +When a new automation pattern, peer implementation, CI-lane constraint, or +flakiness fix proves out, add it here in the same PR; route durable lessons +through `fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md`. diff --git a/.claude/skills/fieldworks-winapp/navigation/screenshot-evidence.md b/.claude/skills/fieldworks-winapp/navigation/screenshot-evidence.md index ce8ea59202..cc451aa6c9 100644 --- a/.claude/skills/fieldworks-winapp/navigation/screenshot-evidence.md +++ b/.claude/skills/fieldworks-winapp/navigation/screenshot-evidence.md @@ -53,8 +53,11 @@ Use names that tell the story in sorted order: - `02-before-.png` - `03-after-.png` - `04-after-.png` +- Path 3 parity bundle: `01-winforms-.png`, `02-avalonia-.png`, `03-diff-.png` - `sequence--001.png`, `sequence--002.png`, ... +When the task is migration parity, capture matched WinForms and Avalonia framing for the same scenario id and store them under `openspec/changes//evidence/parity//` so the visual lane lines up with the semantic snapshot and the workflow/accessibility evidence. + ## Expected Signals - Screenshots should show FieldWorks, not VS Code or another foreground app. diff --git a/.claude/skills/fieldworks-winforms-to-avalonia-migration/SKILL.md b/.claude/skills/fieldworks-winforms-to-avalonia-migration/SKILL.md new file mode 100644 index 0000000000..23f5d896dc --- /dev/null +++ b/.claude/skills/fieldworks-winforms-to-avalonia-migration/SKILL.md @@ -0,0 +1,135 @@ +--- +name: fieldworks-winforms-to-avalonia-migration +description: "End-to-end playbook for migrating any FieldWorks WinForms surface (DataTree slices, XMLViews browse/table, dialogs, choosers, launchers, shell panes) to Avalonia using the established region/seam architecture. Use whenever planning, implementing, or reviewing WinForms-to-Avalonia work — including seam extraction, region composition, owned controls, plugin editors, parity evidence, or retiring legacy surfaces — even if the request only says port, modernize, replace WinForms, or new Avalonia view. Also use after finishing a migration to run the retrospective step that folds new lessons back into these skills." +--- + +# FieldWorks WinForms To Avalonia Migration + +This is the hub skill for the migration program. It tells you what +architecture already exists (do not reinvent it), what order to work in, +which companion skill to apply at each step, and how to keep this skill +set current as more surfaces are migrated. + +## Core Rule + +Migrate by proving behavior first, extracting seams second, and introducing +Avalonia controls only after legacy behavior has executable parity evidence. +A region is not "migrated" until it passes the symbol audit, parity gates, +and has zero runtime dependency on native Views/DataTree infrastructure — +otherwise you have only wrapped the old system. + +## Established Architecture — Reuse, Don't Reinvent + +Past migrations already decided the paradigms below. Before writing any new +abstraction, read `references/architecture-patterns.md` (table of contents at +top) for the decision, the why, and the gotchas. Quick map: + +| Pattern | Canonical code | Details | +| --- | --- | --- | +| Typed view-definition IR compiled from XML layouts | `Src/Common/FwAvalonia/ViewDefinition/ViewDefinitionModel.cs`, `XmlLayoutImporter.cs`, `ViewDefinitionCompiler.cs` | architecture-patterns.md §1 | +| Region model + composer (boundary sits *above* DataTree) | `Src/xWorks/FullEntryRegionComposer.cs`, `Src/Common/FwAvalonia/Region/LexicalEditRegionModel.cs`, `LexicalEditRegionMapper.cs` | §2 | +| Explicit surface selection per host (`HostUiBehavior`) | `Src/Common/FwAvalonia/LexicalEditSurfaceSelectionService.cs` | §3 | +| Owned dense controls, not stock property grids | `Src/Common/FwAvalonia/Region/FwFieldControls.cs`, `FwOptionPicker.cs`, `RegionMenuFlyout.cs` | §4 | +| Plugin registry for custom/legacy slice classes | `Src/xWorks/RegionEditorPlugins.cs`, `Src/xWorks/ChorusNotesPlugin.cs` | §5 | +| Seam contracts (edit session, undo, validation, scheduler, lifetime, refresh) | `Src/Common/FwAvalonia/Seams/ISeams.cs` | `references/seam-catalog.md` | +| Writing-system-aware text fields (font, RTL, keyboard per WS) | `Src/Common/FwAvalonia/Region/FwFieldControls.cs` (`FwMultiWsTextField`) | architecture-patterns.md §6 | +| Dialog ownership across the WinForms/Avalonia boundary | `openspec/changes/lexical-edit-avalonia-migration/dialog-ownership.md` | §7 | + +## Workflow + +Work through the phases in order. Copy +`references/migration-checklist.md` into your task notes and check items +off — it is the per-region definition of done. + +1. **Inventory and scope.** Identify the legacy surface, its entry points, + layouts/parts, custom slice classes, dialogs, and command wiring. + Produce a coverage map (surface × behavior × test status). Apply + `fieldworks-migration-scope-review` when sizing the PR/branch. +2. **Characterize before refactor.** Lock current behavior in executable + tests (semantic baselines, timing baselines, UIA smoke) *before* + extracting anything. Gates: every behavior is tested, consciously + deferred with an owner, or blocked by a named seam. Examples: + `Src/xWorks/xWorksTests/WinFormsUiaSmokeTests.cs`, + `Src/Common/Controls/DetailControls/DetailControlsTests/`. +3. **Extract seams.** Reuse the existing contracts in + `Src/Common/FwAvalonia/Seams/ISeams.cs`; only add a new seam when + `references/seam-catalog.md` has no fit, and record why there. +4. **Select controls.** Default to the owned-control decisions in + architecture-patterns.md §4. Re-evaluate only when a pivot trigger in + seam-catalog.md §"Pivot triggers" has fired. +5. **Compose the region.** Walk the compiled IR in a composer, project into + a region model, route custom classes through the plugin registry, and + render unclaimed classes as explicit "unsupported" rows — never silent + fallback. Apply `fieldworks-avalonia-ui` for the control work. +6. **Wire the host.** Explicit per-host contract: supported Avalonia, + explicit legacy fallback, or blocked. Apply `fieldworks-ui-wiring-review`. +7. **Prove parity.** Build the evidence bundle defined in + `references/parity-evidence.md` (semantic + visual + workflow lanes). + Apply `fieldworks-semantic-render-parity` and + `fieldworks-uia2-parity-testing`. +8. **Localize.** Apply `fieldworks-localization-review`; field labels go + through the StringTable lane, product messages through + `FwAvaloniaStrings.resx`. +9. **Retire and gate.** Run the symbol audit + (`Src/Common/FwAvalonia/FwAvaloniaTests/EngineIsolationAuditTests.cs`), + active-host contract tests + (`Src/xWorks/xWorksTests/RecordEditViewActiveHostContractTests.cs`), + and the normal repo gates (`./build.ps1`, `./test.ps1`). +10. **Retrospective.** Update these skills — see "Keep this skill set + current" below. This step is part of the migration, not optional polish. + +## Hard Rules + +- Active Avalonia hosts must not instantiate or drive hidden legacy + `DataTree`, `Slice`, `RootSite`, menu, or renderer infrastructure except + through approved baseline adapters + (`Src/Common/FwAvalonia/Seams/ActiveHostContract.cs`). +- Migrated-region production code must stay free of the forbidden symbols + listed in parity-evidence.md §"Forbidden symbols" (enforced by + `EngineIsolationAuditTests.cs`). +- Evidence comes from the normal repo path: `./build.ps1` and `./test.ps1`. + Branch-only lanes or ad hoc commands are not integration evidence. +- One global undo/redo stack (LCModel action handler). Never a parallel + Avalonia-only history for committed state. +- Avalonia modal windows are not supported during coexistence; anything + modal uses a WinForms dialog with the host form as owner (see + architecture-patterns.md §7). +- Performance budgets are measured against legacy baselines, not estimated + (parity-evidence.md §"Performance budgets"). + +## Review Red Flags + +- Tests manually invoke `OnPropertyChanged`, `ShowRecord`, or similar + handlers to simulate runtime wiring instead of driving the real path. +- Active Avalonia routing depends on a lossy DTO mapper or preview-only + code without an explicit product contract. +- Task checkboxes claim parity while evidence says substitute, placeholder, + skipped, or future work (see parity-evidence.md §"Evidence language"). +- A custom slice class silently renders wrong instead of producing an + explicit unsupported row. +- A PR mixes plans, tests, infrastructure, product wiring, and unrelated + changes — apply `fieldworks-migration-scope-review`. + +## Handoff + +State what is legacy baseline, what is extracted seam, what is Avalonia +product surface, what each affected host does under the global switch, what +remains outside parity, and what you changed in this skill set during the +retrospective. + +## Keep This Skill Set Current + +These skills are the institutional memory of the migration. Every completed +migration teaches something; if it stays in your head or in a PR thread it +is lost. The retrospective step (workflow step 10) is how the skills stay +ahead of the codebase instead of trailing it: + +1. Read `references/lessons-learned.md` and follow its update protocol — + it maps each kind of discovery (new pattern, new gotcha, fired pivot + trigger, new canonical example, stale pointer) to the exact file and + section to update. +2. Make the skill edits in the same PR as the migration, so reviewers see + the lesson next to the evidence that produced it. +3. If a file pointer in any of these skills is stale (file moved, openspec + change archived), fix the pointer immediately — do not work around it + silently. diff --git a/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/architecture-patterns.md b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/architecture-patterns.md new file mode 100644 index 0000000000..81bcad833b --- /dev/null +++ b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/architecture-patterns.md @@ -0,0 +1,256 @@ +# Established Migration Architecture Patterns + +Decisions already made by the lexical-edit migration. Each section gives the +decision, why it was made, the canonical code, and gotchas. Provenance for +every decision lives in `openspec/changes/lexical-edit-avalonia-migration/` +(if that change has been archived, look under `openspec/changes/archive/`). + +Contents: + +1. Typed view-definition IR (the long-term contract) +2. Region model + composer (boundary above DataTree) +3. Explicit surface selection per host +4. Owned dense controls (control-selection decisions) +5. Plugin registry for custom slice classes +6. Writing-system behavior (font, RTL, keyboard, multi-WS) +7. Dialog ownership and modality across the interop boundary +8. Undo/redo, edit sessions, and refresh +9. Validation +10. Custom fields and ghost rows +11. Localization lanes +12. Density and performance + +## 1. Typed view-definition IR (the long-term contract) + +**Decision.** XML Parts/Layouts are compiled into a typed +`ViewDefinitionModel` (one `ViewNode` per field carrying StableId, editor +kind, writing system, visibility, expansion, custom-field placeholder +metadata, accessibility id, and localization key). Avalonia consumes the IR, +never raw XML. XML is an import format during transition, not the runtime +abstraction; the retirement path is deterministic JSON +(`ViewDefinitionJsonSerializer.cs`) plus customer override patches. + +**Why.** Keeps customer layout customizations alive, creates a clean +DI/test boundary, enables off-thread compilation and snapshot-based parity +tests, and gives XML a retirement path. + +**Canonical code.** `Src/Common/FwAvalonia/ViewDefinition/` — +`ViewDefinitionModel.cs`, `XmlLayoutImporter.cs`, `ViewDefinitionCompiler.cs` +(caches by immutable source-snapshot fingerprint), +`ViewDefinitionJsonSerializer.cs`, `LayoutImportCoverage.cs`. +Tests: `Src/Common/FwAvalonia/FwAvaloniaTests/ViewDefinitionTests.cs`, +`LayoutImportCoverageTests.cs`, `BrowseAndCanonicalJsonTests.cs`. + +**Gotchas.** Compilation must stay deterministic (same source snapshot → +identical IR) because parity snapshots key off it. Track element/attribute +import coverage explicitly; an unimported construct must surface as a +diagnostic node, not vanish. + +## 2. Region model + composer (boundary above DataTree) + +**Decision.** The migration boundary sits at the region-model layer above +`DataTree`, not inside it. A composer walks the compiled IR the way legacy +DataTree walks layouts and emits a region model (renderable fields keyed by +IR StableId) plus an edit context. DataTree internals are never extracted — +they are deleted at the end of coexistence, so extracting them is throwaway +work. + +**Canonical code.** `Src/xWorks/FullEntryRegionComposer.cs` (walks IR, +emits `ComposedEntryRegion`), +`Src/Common/FwAvalonia/Region/LexicalEditRegionModel.cs`, +`LexicalEditRegionMapper.cs`, `IRegionEditContext.cs`, +`LexicalEditRegionView.cs`. +Tests: `RegionModelTests.cs`, `RegionEditingTests.cs`, +`RegionViewingParityTests.cs` in `Src/Common/FwAvalonia/FwAvaloniaTests/`. + +**Gotchas.** The region model is presentation data, not LCModel objects — +it is projected from `IRegionValueProvider` style seams so it can be built +and tested off-thread without WinForms or a real project. + +## 3. Explicit surface selection per host + +**Decision.** Every host that can show legacy or Avalonia UI resolves an +explicit `HostUiBehavior`: supported Avalonia, explicit legacy fallback, or +blocked. No silent fallback. The active host must never drive hidden legacy +DataTree/menu/renderer infrastructure except through approved baseline +adapters. + +**Canonical code.** +`Src/Common/FwAvalonia/LexicalEditSurfaceSelectionService.cs`, +`LexicalEditSurfaceResolver.cs`, `LexicalEditSurfaceFactory.cs`, +`Src/Common/FwAvalonia/Seams/ActiveHostContract.cs` (approved-adapter +whitelist). +Tests: `LexicalEditSurfaceResolverTests.cs`, +`SurfaceAndHostContractTests.cs`, +`Src/xWorks/xWorksTests/RecordEditViewActiveHostContractTests.cs`. + +**Gotchas.** "Convenience" calls into legacy internals while Avalonia is +visible (for example, to harvest metadata) defeat the boundary — the +contract tests exist to catch exactly that. + +## 4. Owned dense controls (control-selection decisions) + +**Decision.** Build FieldWorks-owned row/field controls on top of stock +virtualization primitives instead of adopting a stock property grid or +TreeDataGrid: + +- Detail view (DataTree replacement): owned slice list over + `ListBox`/`VirtualizingStackPanel` — flatten in the model, virtualize + with stock primitives, own the row. +- Browse/table (XMLViews replacement): owned virtualized table — flattened + row list + shared column header + owned cell layout + (`Src/Common/FwAvalonia/Region/LexicalBrowseView.cs`). +- Bounded popup trees (≤500 items): stock `TreeView` with an explicit + item-count ceiling, validated at 100%/150% DPI. +- Unbounded trees: the owned flattened virtualized list with + expander/indent row chrome. + +**Why.** Stock grids fit poorly with nested senses, multi-WS alternatives, +custom choosers, dense rows, and FieldWorks keyboard behavior; owning the +row keeps the UI framework out of domain semantics. TreeDataGrid was +rejected on licensing and editing/automation gaps (see pivot triggers in +`seam-catalog.md` — revisit if those facts change). + +**Canonical code.** `Src/Common/FwAvalonia/Region/FwFieldControls.cs` +(`RegionFieldKind`: Text, Chooser, Boolean, Image, Command, +ReferenceVector, Custom), `FwOptionPicker.cs`, `RegionMenuFlyout.cs`, +`HoverReveal.cs`, `RegionFocusMemory.cs`. + +## 5. Plugin registry for custom slice classes + +**Decision.** Legacy layouts reference custom slice classes by name (for +example `SIL.FieldWorks.XWorks.LexEd.MessageSlice`). A plugin registry maps +those same class identities to factories that build Avalonia controls. +Resolution order: plugin → companion-strip WinForms coexistence → +explicit "unsupported" row. Never silent mis-render. Keying by legacy class +identity means zero layout edits and measurable burn-down (census vs. +registry coverage). + +**Canonical code.** `Src/xWorks/RegionEditorPlugins.cs`, +`Src/xWorks/ChorusNotesPlugin.cs`, registry contracts in +`Src/Common/FwAvalonia/Region/LexicalEditRegionModel.cs`. +Tests: `Src/xWorks/xWorksTests/DialogLauncherPluginTests.cs`, +`LexemeEditorBurnDownTests.cs`, `MessagesCompanionLaneTests.cs`. + +**When migrating a new surface:** census its custom slice classes first, +check the registry for existing plugins, and add plugins (with tests) for +the rest. Add each new plugin to the burn-down tracking. + +## 6. Writing-system behavior (font, RTL, keyboard, multi-WS) + +**Decision.** Every text field renders per-writing-system rows: WS +abbreviation gutter + value box, with font family/size, flow direction +(RTL/LTR), and keyboard activation projected from LCModel WS metadata. +Keyboard switches on focus (legacy `EditingHelper.SetKeyboardForWs` +behavior). OpenType features ship via HarfBuzz; native Graphite is never +loaded on the Avalonia path — Graphite-dependent writing systems are +classified and warned, not blocked. + +**Canonical code.** `FwMultiWsTextField` in +`Src/Common/FwAvalonia/Region/FwFieldControls.cs`; `RegionWsValue` +(WsAbbrev, FontFamily, FontSize, RightToLeft, WsTag) in +`LexicalEditRegionModel.cs`. +Tests: `TreeSpikeAndRtlTests.cs`, `VisualParityAndDensityTests.cs`. + +**Gotchas.** Never assume one font, one direction, or one script per +field. Test mixed-script content at 100% and 150% DPI with real fonts. + +## 7. Dialog ownership and modality across the interop boundary + +**Decision.** During coexistence there is one UI thread and one message +loop. Rules (provenance: +`openspec/changes/lexical-edit-avalonia-migration/dialog-ownership.md`): + +- Anything modal is a WinForms dialog, owned by the hosting WinForms + top-level form (`Control.FindForm()` of the host) — never `null`, never + an Avalonia handle. Avalonia modal windows are not used (unsupported on + the 11.x coexistence path). +- Record the focused Avalonia control before `ShowDialog` and restore focus + explicitly after close. +- Use Avalonia flyouts inside the hosted surface, not free popup windows + (mixed-DPI positioning). +- No cross-boundary Tab order between WinForms siblings and the Avalonia + surface; own focus inside the surface. +- No WinForms modeless tool windows owned by an Avalonia surface. + +## 8. Undo/redo, edit sessions, and refresh + +**Decision.** Edits ride a fenced `IEditSession` +(Active → Saved/Canceled → Disposed) wrapping an LCModel undo task — one +undoable action per save regardless of field count. Transient text undo +stays local to the focused TextBox. Global undo/redo routes through +`IUndoRedoCoordinator` to the LCModel action handler, then refreshes the +region. Cancel rolls back the session and must not create a committed undo +action. Refresh coordination mirrors legacy +`DoNotRefresh`/`RefreshListNeeded` semantics via the refresh-coordinator +seam. + +**Canonical code.** `Src/Common/FwAvalonia/Seams/ISeams.cs`, +`SeamImplementations.cs`, `RefreshCoordinator.cs`. +Tests: `SeamTests.cs`, `RegionEditingTests.cs`. + +**Gotchas.** Two undo stacks produce user-visible data weirdness. Never +disable global undo while a session is dirty — route it. Defer PropChanged +fan-out during multi-field edits until commit/cancel. + +## 9. Validation + +**Decision.** Validation runs over immutable presentation snapshots, not +live LCModel. Errors are ordered by presentation/focus order (deterministic +for headless tests), skip unmaterialized lazy items, and carry node id, +object/flid, severity, localized message key + args, and accessibility +text. Only severity=Error blocks save; warnings do not. Stale async results +(from older snapshots) are discarded. + +**Canonical code.** `IValidationService` in +`Src/Common/FwAvalonia/Seams/ISeams.cs`; composer wiring in +`Src/xWorks/FullEntryRegionComposer.cs`. + +## 10. Custom fields and ghost rows + +**Decision.** Stored view definitions contain `CustomFieldPlaceholder` +nodes (typed equivalent of legacy `customFields="here"`), expanded from +LCModel metadata at compile time. Custom fields are never baked into stored +definitions (they differ per project). Ghost rows ("type to add" +placeholders) are runtime UI state managed by the composer/model, never +stored layout structure. + +**Canonical code.** `ViewDefinitionModel.cs` (placeholder node kind), +composer expansion in `FullEntryRegionComposer.cs`. +Tests: `RegionCustomFieldRenderingTests.cs`. + +## 11. Localization lanes + +**Decision.** Two lanes: + +- **Field labels** resolve through the legacy StringTable lane + (`XmlUtils.GetLocalizedAttributeValue`, `strings-{locale}.xml`) at render + time; the IR carries `LocalizationKey` per node, never baked English. +- **Product messages** (Save, Cancel, validation, unsupported-row text) + live in `Src/Common/FwAvalonia/FwAvaloniaStrings.resx` with translator + comments. + +**Gotchas.** SDK-style csprojs need an explicit `` element +or the Crowdin satellite-assembly build +(`Build/Src/FwBuildTasks/Localization/ProjectLocalizer.cs`) fails — verify +when adding any new Avalonia project. English-on-Avalonia where legacy +shows translations is a parity failure, not cosmetics. + +## 12. Density and performance + +**Decision.** Visual density (row spacing, gutters, box heights) is owned +by FieldWorks density constants, measured against legacy WinForms +baselines. Performance budgets are measured, not estimated: capture legacy +init/populate/total timings with the characterization harness, then hold +Avalonia to within 20% of legacy total (or record an explicitly accepted +delta in the region manifest). + +**Canonical code.** `Src/Common/FwAvalonia/Poc/PocDensity.cs`; +legacy harness +`Src/Common/Controls/DetailControls/DetailControlsTests/DataTreeRenderTests.cs`; +committed thresholds `DataTreeTimingBaselines.json` (same directory). +Tests: `VisualParityAndDensityTests.cs`. + +**Gotchas.** Validate virtualization against the large fixtures (253-slice +detail, 10k-row browse) before committing a control choice. Include the +150% DPI path — it exposes real layout regressions. diff --git a/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md new file mode 100644 index 0000000000..9d004a031a --- /dev/null +++ b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/lessons-learned.md @@ -0,0 +1,70 @@ +# Lessons Ledger and Skill-Update Protocol + +The fieldworks-* migration skills only stay useful if every migration +updates them. This file defines (a) the routing table that maps each kind +of discovery to the exact place to record it, and (b) an append-only ledger +of migration retrospectives so future agents can see how the skill set +evolved and why. + +## Update protocol — where each discovery goes + +Run this at the end of every migration (workflow step 10 in SKILL.md), and +immediately whenever you hit a stale pointer mid-task. Make the edits in +the same PR as the migration. + +| You discovered… | Update | +| --- | --- | +| A new architectural pattern, or a refinement of an existing one | `architecture-patterns.md` — add/extend the numbered section (decision, why, canonical code, gotchas); add a row to the SKILL.md quick map if it is load-bearing | +| A new seam contract | `seam-catalog.md` §1/§2 plus its pivot trigger in §3 | +| A pivot trigger fired (decision re-evaluated) | Record the outcome inline in `seam-catalog.md` §3 and summarize in the ledger below | +| A new plugin for a custom slice class | `architecture-patterns.md` §5 canonical-code list; keep the burn-down test list current | +| A new gotcha / failure mode (interop, DPI, fonts, focus, threading, lifetime…) | The Gotchas paragraph of the matching `architecture-patterns.md` section; if it is a review smell, also add a red flag to the most relevant satellite skill | +| A new forbidden legacy symbol | `EngineIsolationAuditTests.cs` (the enforcement) and `parity-evidence.md` §4 (the documentation) — both in the same PR | +| A new evidence lane, artifact type, or evidence-language term | `parity-evidence.md` | +| A new mandatory step in the per-region process | `migration-checklist.md` (and the workflow list in SKILL.md if it is a new phase) | +| A trigger phrase that failed to invoke a skill when it should have | The `description` frontmatter of that skill — add the missing vocabulary; keep descriptions quoted (YAML colons) and third-person | +| A stale file pointer (file moved/renamed, openspec change archived) | Fix the pointer in whichever skill file holds it; prefer pointing at code and tests over change docs | +| Updated performance baselines | `DataTreeTimingBaselines.json` stays the source of truth; update budget notes in `parity-evidence.md` §5 only if the policy (not the numbers) changed | + +Rules of thumb: + +- **Skills point, references explain, openspec records provenance.** Do + not paste large doc content into skills; capture the durable decision and + point at code/tests, citing the openspec doc as provenance. +- **Generalize before writing.** Record the class of problem, not the + one-off instance. If it only applies to one region, it goes in that + region's openspec change, not here. +- **Prune as you add.** If a section no longer pays for its tokens + (pattern superseded, gotcha fixed at the framework level), delete or + collapse it. Skills are working memory, not an archive — the archive is + git history and openspec. +- **Keep SKILL.md bodies under ~150 lines** and references one level deep + from SKILL.md. If a reference outgrows ~300 lines, split it by domain + and update the pointers. + +## Ledger + +Append one entry per completed migration (newest first). Keep entries to +~10 lines: link to the change, what was migrated, what was learned, which +skill files changed. + +### 2026-06 — Lexical Edit (full entry view), phases 1–2 (seed entry) + +- Change: `openspec/changes/lexical-edit-avalonia-migration/` (plus + `avalonia-migration-roadmap`, `lexical-edit-avalonia-poc-spike`). +- Migrated: first Avalonia lexical-edit region — typed IR pipeline, region + composer, owned field controls (`FwMultiWsTextField`, `FwOptionPicker`, + menus/flyouts), plugin registry, surface selection service, seam + contracts, Path 3 parity harness. +- Key lessons now encoded: boundary above DataTree (don't extract + internals); owned dense controls over stock grids; explicit + unsupported rows over silent fallback; one global undo stack; WinForms + dialogs own all modality during coexistence; measured (not estimated) + performance budgets; StringTable + `.resx` dual localization lanes; + `` required for Crowdin satellite builds. +- Skill set restructured (this commit): skills moved from + `.github/skills/` to `.claude/skills/` per AI_GOVERNANCE no-mirror rule; + hub skill rewritten with references/ (architecture-patterns, seam-catalog, + parity-evidence, migration-checklist, this ledger); satellite skill + descriptions rewritten for triggering; fixed YAML-colon bug that broke + `fieldworks-ui-wiring-review` triggering. diff --git a/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/migration-checklist.md b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/migration-checklist.md new file mode 100644 index 0000000000..9536b857df --- /dev/null +++ b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/migration-checklist.md @@ -0,0 +1,100 @@ +# Per-Region Migration Checklist + +Copy this checklist into your working notes (or the OpenSpec change tasks) +at the start of a migration and keep it updated. It is the per-region +definition of done. Items map to the workflow phases in SKILL.md. + +## Phase 1 — Inventory and scope + +- [ ] Legacy surface identified: entry points, layouts/parts, custom slice + classes, dialogs, choosers, command/listener wiring +- [ ] Custom slice class census taken and compared against the plugin + registry (`Src/xWorks/RegionEditorPlugins.cs`) — list of missing + plugins recorded +- [ ] Coverage map drafted (behavior × test status: covered / deferred + with owner / blocked by named seam) +- [ ] Branch scope reviewed with `fieldworks-migration-scope-review` + (branch-only diff, split triggers checked) + +## Phase 2 — Characterize before refactor + +- [ ] Semantic baseline captured for the legacy surface (bindings, labels, + editor kinds, visibility, ghost state, focus order, WS metadata, + accessibility identity) +- [ ] Legacy timing baseline measured and committed +- [ ] Legacy UIA smoke coverage exists for launcher/chooser reachability +- [ ] All characterization tests run via `./test.ps1` (not branch-only lanes) + +## Phase 3 — Seams + +- [ ] Existing seams reused from `Src/Common/FwAvalonia/Seams/ISeams.cs` +- [ ] Any new seam added to `references/seam-catalog.md` with purpose, + rules, and pivot trigger +- [ ] No region code reaches directly into PropertyTable/mediator/LCModel + outside a seam + +## Phase 4 — Controls + +- [ ] Control choices follow architecture-patterns.md §4 (owned controls; + bounded TreeView ceiling respected) +- [ ] Any deviation justified by a fired pivot trigger, recorded in + seam-catalog.md §3 + +## Phase 5 — Region composition + +- [ ] Composer walks compiled IR; region model keyed by StableId +- [ ] Custom classes resolve plugin → companion strip → explicit + unsupported row (no silent fallback) +- [ ] Custom-field placeholders expand from LCModel metadata at compile + time; ghost rows are runtime state only +- [ ] Stable AutomationIds derived from StableId + (`{StableId}`, `{StableId}.Label`, `{StableId}.{WsAbbrev}`) + +## Phase 6 — Host wiring + +- [ ] Every affected host has an explicit `HostUiBehavior` (supported / + explicit legacy fallback / blocked) +- [ ] Full wiring path traced: setting source → persisted state → + PropertyTable key → broadcast → listener → host reload → focus and + command routing → save/`PrepareToGoAway()` → fallback +- [ ] Active-host contract holds: no hidden legacy DataTree/menu/renderer + driven while Avalonia is active +- [ ] Reviewed with `fieldworks-ui-wiring-review` + +## Phase 7 — Parity evidence + +- [ ] Path 3 bundle produced per scenario (see parity-evidence.md §1) +- [ ] Semantic, visual, workflow, and performance lanes each prove their + own axis; no lane substitutes for another +- [ ] Performance within budget (≤ legacy total × 1.2, or accepted delta + recorded) +- [ ] 100% and 150% DPI captured + +## Phase 8 — Localization + +- [ ] Field labels resolve through the StringTable lane via the IR's + `LocalizationKey` +- [ ] Product messages in `FwAvaloniaStrings.resx` with translator comments +- [ ] New csprojs carry `` (Crowdin satellite build) +- [ ] AutomationIds nonlocalized; automation Names localized +- [ ] Reviewed with `fieldworks-localization-review` + +## Phase 9 — Retirement and gates + +- [ ] Forbidden-symbol audit passes (`EngineIsolationAuditTests.cs`); + new forbidden symbols added to the audit and parity-evidence.md §4 +- [ ] Active-host contract tests pass + (`RecordEditViewActiveHostContractTests.cs`) +- [ ] `./build.ps1` and `./test.ps1` pass; `openspec validate + --strict` passes when an OpenSpec change is attached +- [ ] Legacy code scheduled for removal is listed explicitly (what, when, + behind which gate) + +## Phase 10 — Retrospective (updates this skill set) + +- [ ] New patterns/gotchas/pivots recorded per the protocol in + `references/lessons-learned.md` +- [ ] New plugins added to the canonical examples in + architecture-patterns.md §5 +- [ ] Stale file pointers in any fieldworks-* skill fixed +- [ ] Skill edits included in the same PR as the migration diff --git a/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/parity-evidence.md b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/parity-evidence.md new file mode 100644 index 0000000000..47c3da62b0 --- /dev/null +++ b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/parity-evidence.md @@ -0,0 +1,108 @@ +# Parity Evidence — Shared Definitions + +Canonical home for the evidence vocabulary used across all fieldworks-* +migration skills. Other skills reference these definitions instead of +redefining them. + +Contents: + +1. The Path 3 bundle (triangulated parity evidence) +2. Evidence lanes and what each can prove +3. Evidence language (claim-downgrading taxonomy) +4. Forbidden symbols (engine isolation) +5. Performance budgets +6. Artifact naming + +## 1. The Path 3 bundle (triangulated parity evidence) + +"Path 3" is the migration-quality visual-fidelity lane. A single artifact +cannot prove a region is migrated; the bundle triangulates. For one +scenario id, produce: + +- `semantic.json` — semantic snapshot of the IR/region (the anchor; both + legacy import and Avalonia compose must produce it) +- `visual.legacy.png` — WinForms screenshot (100% and 150% DPI) +- `visual.avalonia.png` — Avalonia capture, same framing/DPI + (Avalonia.Headless rendered frames are acceptable when the scenario is + explicitly control-scoped) +- diff/variance artifact interpreted against stable binding/focus/ + accessibility identity — never a raw pixel diff in isolation +- `workflow.legacy.md` / `workflow.avalonia.md` — accessibility/keyboard + workflow evidence (UIA2 on realized windows for desktop claims) +- `performance.json` — init/populate/refresh timings +- `failure-summary.md` — when something fails, classify the broken lane + with diagnostics; do not hand reviewers a raw image diff + +Canonical harness: `Src/Common/FwAvalonia/FwAvaloniaTests/Path3BundleTests.cs`; +bundle contract provenance: +`openspec/changes/lexical-edit-avalonia-migration/coverage-map.md` §9. + +## 2. Evidence lanes and what each can prove + +| Lane | Tooling | Proves | Cannot prove | +| --- | --- | --- | --- | +| Semantic snapshot | IR `ToSnapshot()`, JSON per scenario | Binding, labels, editor kind, visibility, ghost state, focus order, WS metadata, accessibility identity | Typography, density, wrapping, native rendering | +| Visual/render | Avalonia.Headless Skia capture; legacy screenshots | Layout, density, fonts, wrapping | Why a field is missing (semantic lane explains that) | +| Workflow/accessibility | Avalonia.Headless input simulation; UIA2/FlaUI on realized windows | Focus movement, invoke paths, chooser reachability, keyboard shortcuts, native automation tree | Pixel fidelity | +| Performance | Timing harness + committed baselines | Budgets vs. measured legacy | Anything functional | + +Headless and desktop are different lanes: a headless smoke test is never a +UIA2 baseline, and desktop workflow claims need realized-window evidence. + +## 3. Evidence language (claim-downgrading taxonomy) + +When verifying task checkboxes or PR claims, scan the evidence text for +these words and downgrade the claim accordingly: + +- **substitute** — a different artifact stands in for the claimed one; + the claim is unproven. +- **placeholder** — data or metadata is fake; parity is not demonstrated. +- **skipped** — the test exists but did not run; no evidence. +- **future / planned** — work item, not evidence. +- **partial** — name exactly which axes are proven and which are not. + +A checked task whose evidence says any of the above is a review blocker: +either the evidence improves or the checkbox is unchecked. + +## 4. Forbidden symbols (engine isolation) + +Migrated-region production code must not reference, in any runtime path: + +- `System.Windows.Forms.Control` +- `DataTree`, `Slice`, `SliceFactory`, `RootSiteControl` +- `XmlView`, `BrowseViewer` +- `IVwRootBox`, `IVwEnv`, `IVwGraphics`, `IRenderEngine` +- `GraphiteEngineClass`, `UniscribeEngineClass` +- `GeckoWebBrowser`, `XWebBrowser`, `GeckofxHtmlToPdf` + +Enforced by `Src/Common/FwAvalonia/FwAvaloniaTests/EngineIsolationAuditTests.cs`. +When a migration discovers a new legacy symbol that must not leak, add it +to the audit test AND this list in the same PR. Custom linguistic services +(XAmple, spelling, parsers, ICU, encoding converters) may remain behind +explicit service seams when they do not own the render/editor surface. + +## 5. Performance budgets + +Budgets are measured, never estimated: + +1. Capture legacy timings with the characterization harness + (`Src/Common/Controls/DetailControls/DetailControlsTests/DataTreeRenderTests.cs`) + on a named machine profile; thresholds are committed in + `DataTreeTimingBaselines.json`. +2. Hold the Avalonia surface to within 20% of measured legacy total, or + record an explicitly accepted delta with justification in the region + manifest. +3. Measure cold vs. warm separately; IR compile latency must stay + deterministic and small; refresh-after-edit has its own baseline. +4. Always include the 150% DPI path and the large fixtures before + accepting a control choice. + +## 6. Artifact naming + +`{scenarioId}/{bundleId}/semantic.json`, `visual.legacy.png`, +`visual.avalonia.png`, `workflow.legacy.md`, `workflow.avalonia.md`, +`performance.json`, `failure-summary.md`. + +Snapshots must normalize away pixel bounds, transient generated names, +timestamps, machine paths, and culture-dependent ordering — see +`fieldworks-semantic-render-parity` for include/exclude rules. diff --git a/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/seam-catalog.md b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/seam-catalog.md new file mode 100644 index 0000000000..98337847db --- /dev/null +++ b/.claude/skills/fieldworks-winforms-to-avalonia-migration/references/seam-catalog.md @@ -0,0 +1,76 @@ +# Seam Catalog + +The seams that separate Avalonia UI from LCModel/xCore/WinForms. All +contracts live in `Src/Common/FwAvalonia/Seams/ISeams.cs` with +implementations in `SeamImplementations.cs` and tests in +`Src/Common/FwAvalonia/FwAvaloniaTests/SeamTests.cs`. Per-seam design docs +(current state, alternatives considered, required tests) live in +`openspec/changes/lexical-edit-avalonia-migration/avalonia-*.md`. + +Before inventing a new abstraction for a migration, check this table. If a +seam fits, reuse it. If none fits, add the new seam here (name, purpose, +rules, pivot trigger) in the same PR that introduces it. + +Contents: + +1. Seam table +2. Supporting seams +3. Pivot triggers (when to revisit a decision) + +## 1. Seam table + +| Seam | Purpose | Key rules | +| --- | --- | --- | +| `IEditSession` | Fenced LCModel undo-task lifecycle: Active → Saved/Canceled → Disposed | One undoable action per save; cancel rolls back without creating an undo action; writes outside a session are a bug | +| `IUndoRedoCoordinator` | Routes global undo/redo through the LCModel action handler | Control-local text undo stays local until commit; never a parallel committed-state history; refresh region after global undo/redo | +| `IValidationService` | Deterministic validation over immutable presentation snapshots | Focus-order error ordering; skip unmaterialized lazy items; localized message keys; only severity=Error blocks save; discard stale async results | +| `IXCoreCommandBridge` | Bridges xCore mediator command routing to Avalonia commands | Region-local commands first; shell-scope wiring happens in the shell phase, not per region | +| `IUiScheduler` | Thin UI-thread marshalling (`IsOnUiThread`, `Post`) | No hidden `Task.Run`; fakeable in tests; keeps threading visible at the seam | +| `IRegionLifetime` | Region disposal discipline | Idempotent disposal, late-callback suppression, event-handler cleanup; protects against async work completing after close | +| `ILexicalRefreshCoordinator` | Mirrors legacy `DoNotRefresh`/`RefreshListNeeded` gating (LT-22414) | Defer PropChanged fan-out during multi-field edits until commit/cancel; characterize legacy behavior before extending (`RefreshCoordinator.cs`) | +| `IRecordNavigationContext` | Bidirectional selection bridge with the xCore "current record" bus | Follow external navigation and publish selection back; never reach into PropertyTable directly from a region | +| `IFwClipboard` | Clipboard access without WinForms dependency | See `FwClipboardSeamTests.cs` | +| `IHostSurface` (focus API) | Host-side focus save/restore around WinForms dialogs | Pairs with the dialog-ownership rules (architecture-patterns.md §7) | + +## 2. Supporting seams + +- **View definition pipeline:** `IViewDefinitionImporter` / + `ViewDefinitionCompiler` / cache keyed by immutable source snapshot + (`ViewDefinitionCacheKey.cs`). Off-thread compilation, deterministic + output. +- **Region value provision:** the composer consumes value-provider style + seams so region models can be built and tested without LCModel or + WinForms. +- **Active-host contract:** `ActiveHostContract.cs` whitelists the only + approved adapters through which an active Avalonia host may touch legacy + infrastructure. +- **Drag/drop and sync-context hygiene:** `FwDragDrop.cs`, + `FinalizerSafeSynchronizationContext.cs`. + +## 3. Pivot triggers (when to revisit a decision) + +A decision below stands until its trigger fires. When a trigger fires, +record the re-evaluation outcome here and in the lessons ledger. + +- **Edit sessions:** adopt a staged-draft model only if fenced direct + LCModel sessions prove unacceptably complex or risky in practice. +- **Undo/redo:** add richer document-local undo only for a specific owned + control that needs it, still committing through LCModel. +- **Validation:** collapse to Avalonia-native validation only for isolated + dialogs with no LCModel/cross-object semantics. +- **UI scheduler / lifetime:** collapse wrappers that demonstrably provide + no test or architecture value. +- **TreeDataGrid:** re-evaluate for browse surfaces if it is relicensed + permissively (or SIL accepts a commercial license) AND upstream closes + the editing/automation gaps. +- **VirtualizingStackPanel:** escalate to a fully owned realization-window + virtualizer if scroll/expand or open-time budgets fail on the production + fixtures (253-slice detail, 10k-row browse). +- **TreeView ceiling (≤500 items):** raise/remove if a consumable Avalonia + release ships TreeView virtualization. +- **ItemsRepeater:** reconsider as the owned-control substrate if it is + un-deprecated with maintained virtualization. +- **Owned-control cost:** if owned controls overrun, re-open the + TreeDataGrid commercial option with measured cost as the baseline. +- **Stock-control accessibility:** if any adopted stock control fails an + accessibility gate, owning its automation peers becomes mandatory. diff --git a/.claude/skills/smart-screenshot-capture/SKILL.md b/.claude/skills/smart-screenshot-capture/SKILL.md index 3786b87b60..b616f1f6c3 100644 --- a/.claude/skills/smart-screenshot-capture/SKILL.md +++ b/.claude/skills/smart-screenshot-capture/SKILL.md @@ -62,6 +62,7 @@ For this repository, default to: - transient evidence: `Output/ManualEvidence//` - OpenSpec review evidence: `openspec/changes//evidence/manual-winapp/` +- Path 3 parity bundle evidence: `openspec/changes//evidence/parity//` - ad hoc screenshots: `Output/ManualEvidence/screenshots/` Create the folder if needed. Do not put scratch screenshots in committed @@ -73,6 +74,7 @@ Use sorted, descriptive names: - single capture: `-.png` - before/after: `01-before-.png`, `02-after-.png` +- Path 3 parity: `01-winforms-.png`, `02-avalonia-.png`, `03-diff-.png` - sequence: `step-01-.png`, `step-02-.png` - app tour: `-.png` - temporary fallback: `screenshot-YYYY-MM-DD-HHMMSS.png` @@ -121,6 +123,10 @@ Use multiple captures when one image cannot tell the story: screenshot; - comparison: capture both images, then run a screenshot diff when available. +For migration parity bundles, keep framing, DPI, zoom, and window size matched across WinForms and Avalonia captures whenever density, wrapping, or spacing is under review. + +For a Path 3 parity bundle, pair screenshots with the matching semantic snapshot and workflow/accessibility evidence for the same scenario id; a screenshot pair alone is not a full parity claim. + For sequences, keep the same target, window size, and framing across captures unless the task is specifically about responsive or layout behavior. diff --git a/.github/agents/fieldworks.avalonia-expert.agent.md b/.github/agents/fieldworks.avalonia-expert.agent.md index 8933db04a8..23895a1783 100644 --- a/.github/agents/fieldworks.avalonia-expert.agent.md +++ b/.github/agents/fieldworks.avalonia-expert.agent.md @@ -28,6 +28,20 @@ When you need Avalonia API details, patterns, or examples, you MUST use Context7 - Build: `./build.ps1` - Tests: `./test.ps1` +## Repo skills (read before designing anything) +The migration playbook and decided architecture live in skills under +`.claude/skills/` (picked up by Copilot and Claude Code alike): +- `fieldworks-winforms-to-avalonia-migration` — hub playbook; its + `references/architecture-patterns.md` and `references/seam-catalog.md` + document the decided patterns (typed IR, region composer, owned controls, + seams). Do not reinvent abstractions those files already settle. +- `fieldworks-avalonia-ui` — control/XAML/headless-test conventions and + canonical code to imitate. +- Supporting reviews: `fieldworks-ui-wiring-review`, + `fieldworks-uia2-parity-testing`, `fieldworks-semantic-render-parity`, + `fieldworks-localization-review`, `fieldworks-migration-scope-review`, + `fieldworks-managed-netfx-review`. + ## Avalonia development guidelines - Prefer MVVM patterns that are idiomatic for Avalonia. - Keep UI logic out of XAML code-behind where practical; use view models and bindings. diff --git a/.github/instructions/avalonia.instructions.md b/.github/instructions/avalonia.instructions.md new file mode 100644 index 0000000000..134546216a --- /dev/null +++ b/.github/instructions/avalonia.instructions.md @@ -0,0 +1,99 @@ +--- +applyTo: "**/*" +name: "avalonia.instructions" +description: "Guidance for FieldWorks Avalonia modules and the shared Preview Host" +--- + +# Avalonia Modules (FieldWorks) + +## Purpose & Scope +- Provide a consistent way to **create, build, test, and preview** Avalonia UI modules in FieldWorks. +- Applies to the Advanced Entry Avalonia work under `specs/010-advanced-entry-view/` and future Avalonia modules. +- This file covers mechanics (build, layout, logging, preview). The + migration playbook, decided architecture patterns, and parity/evidence + rules live in the skills under `.claude/skills/` — start with + `fieldworks-winforms-to-avalonia-migration` (hub) and + `fieldworks-avalonia-ui`. + +## Key Rules + +### Build & test (always use repo scripts) +- Build the repo using the traversal script: + - `./build.ps1` +- Run tests using the repo test runner: + - `./test.ps1` +- Do **not** rely on `dotnet build` for repo-wide builds; FieldWorks build targets include tasks that require full Visual Studio/MSBuild. + +### Project locations & naming +- Feature modules live under `Src//.Avalonia/`. + - Example: `Src/LexText/AdvancedEntry.Avalonia/` +- Shared Avalonia utilities live under `Src/Common/FwAvalonia/`. +- Preview tooling lives under `Src/Common/FwAvaloniaPreviewHost/`. + +### Solution + traversal integration (required) +For every new Avalonia module or tool: +- Add the project(s) to the traversal build so `./build.ps1` and `./test.ps1` naturally cover them: + - `FieldWorks.proj` +- Add the project(s) to the solution so developers can open/build/debug in Visual Studio: + - `FieldWorks.sln` + +### Logging (use FieldWorks diagnostics) +- Module logging must route through the existing FieldWorks diagnostics pipeline (`System.Diagnostics`, `TraceSwitch`, `EnvVarTraceListener`). +- Add a `TraceSwitch` entry for each module/component in the dev diagnostics config: + - `Src/Common/FieldWorks/FieldWorks.Diagnostics.dev.config` + +### Preview Host diagnostics (log file) +- The Preview Host writes startup errors and trace output to a log file next to the executable: + - `Output//FieldWorks.trace.log` (e.g. `Output/Debug/FieldWorks.trace.log`) +- To override the log path, set environment variable `FW_PREVIEW_TRACE_LOG` to a full file path. + +### Preview Host (fast UI iteration) +To preview UI without launching the full FieldWorks app, use the shared Preview Host. + +**How modules opt-in** +- Register the module using an assembly-level attribute: + - `FwPreviewModuleAttribute` in `Src/Common/FwAvalonia/Preview/` +- Provide an optional data provider implementing: + - `IFwPreviewDataProvider` + +**Run the preview** +- Use the agent script (build + run): + - `./scripts/Agent/Run-AvaloniaPreview.ps1 -Module advanced-entry -Data sample` +- Supported `-Data` modes depend on the module’s data provider; the current convention is: + - `empty` (minimal/default DataContext) + - `sample` (representative sample data) + +## Expected Structure (current) + +- Module: + - `Src/LexText/AdvancedEntry.Avalonia/` +- Shared utilities/contracts: + - `Src/Common/FwAvalonia/` + - `Diagnostics/` (logging shim) + - `Preview/` (module registration + data provider contracts) +- Preview host executable: + - `Src/Common/FwAvaloniaPreviewHost/` +- Launcher script: + - `scripts/Agent/Run-AvaloniaPreview.ps1` + +## Examples + +### Build everything (recommended) +```powershell +./build.ps1 +``` + +### Run tests +```powershell +./test.ps1 +``` + +### Preview the Advanced Entry module +```powershell +./scripts/Agent/Run-AvaloniaPreview.ps1 -Module advanced-entry -Data sample +``` + +## Notes & Constraints +- Avalonia modules should remain **detached from LCModel** for preview scenarios (use DTO/view-model sample data) to keep the Preview Host lightweight. +- Keep all user-visible strings localizable (use `.resx` patterns where applicable; do not hardcode translatable UI text). +- Treat any input that crosses managed/native boundaries as untrusted; sanitize and validate per repo security guidance. diff --git a/.github/prompts/opsx-apply.prompt.md b/.github/prompts/opsx-apply.prompt.md index e23ec64d14..fedf7b8b88 100644 --- a/.github/prompts/opsx-apply.prompt.md +++ b/.github/prompts/opsx-apply.prompt.md @@ -1,149 +1,9 @@ --- description: Implement tasks from an OpenSpec change (Experimental) +agent: "agent" +argument-hint: "Optional change name" --- -Implement tasks from an OpenSpec change. +Use the `openspec-apply-change` skill from `.claude/skills/openspec-apply-change/SKILL.md`. -**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. - -**Steps** - -1. **Select the change** - - If a name is provided, use it. Otherwise: - - Infer from conversation context if the user mentioned a change - - Auto-select if only one active change exists - - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select - - Always announce: "Using change: " and how to override (e.g., `/opsx:apply `). - -2. **Check status to understand the schema** - ```bash - openspec status --change "" --json - ``` - Parse the JSON to understand: - - `schemaName`: The workflow being used (e.g., "spec-driven") - - Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others) - -3. **Get apply instructions** - - ```bash - openspec instructions apply --change "" --json - ``` - - This returns: - - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema) - - Progress (total, complete, remaining) - - Task list with status - - Dynamic instruction based on current state - - **Handle states:** - - If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue` - - If `state: "all_done"`: congratulate, suggest archive - - Otherwise: proceed to implementation - -4. **Read context files** - - Read every file path listed under `contextFiles` from the apply instructions output. - The files depend on the schema being used: - - **spec-driven**: proposal, specs, design, tasks - - Other schemas: follow the contextFiles from CLI output - -5. **Show current progress** - - Display: - - Schema being used - - Progress: "N/M tasks complete" - - Remaining tasks overview - - Dynamic instruction from CLI - -6. **Implement tasks (loop until done or blocked)** - - For each pending task: - - Show which task is being worked on - - Make the code changes required - - Keep changes minimal and focused - - Mark task complete in the tasks file: `- [ ]` → `- [x]` - - Continue to next task - - **Pause if:** - - Task is unclear → ask for clarification - - Implementation reveals a design issue → suggest updating artifacts - - Error or blocker encountered → report and wait for guidance - - User interrupts - -7. **On completion or pause, show status** - - Display: - - Tasks completed this session - - Overall progress: "N/M tasks complete" - - If all done: suggest archive - - If paused: explain why and wait for guidance - -**Output During Implementation** - -``` -## Implementing: (schema: ) - -Working on task 3/7: -[...implementation happening...] -✓ Task complete - -Working on task 4/7: -[...implementation happening...] -✓ Task complete -``` - -**Output On Completion** - -``` -## Implementation Complete - -**Change:** -**Schema:** -**Progress:** 7/7 tasks complete ✓ - -### Completed This Session -- [x] Task 1 -- [x] Task 2 -... - -All tasks complete! You can archive this change with `/opsx:archive`. -``` - -**Output On Pause (Issue Encountered)** - -``` -## Implementation Paused - -**Change:** -**Schema:** -**Progress:** 4/7 tasks complete - -### Issue Encountered - - -**Options:** -1.