From e04a3671b01402c7dde54702621b7ab4481dfe90 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Tue, 17 Feb 2026 10:40:44 -0500 Subject: [PATCH] Modernize .NET tooling and enable AI workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - See SDK_MIGRATION.md for details - **64-bit only** architecture enforcement (x86/Win32 removed) - **Registration-free COM** implementation (Native + Managed) - **Unified launcher**: FieldWorks.exe replaced the historical LexText.exe stub across build, installer, and documentation - **MSBuild Traversal SDK** for declarative builds - **Test framework modernization** (RhinoMocks → Moq, NUnit 4 ready) - **Central Package Management (CPM)** via `Directory.Packages.props` - **Unified test runner** (`test.ps1`) for managed and native tests - **Stale DLL detection** via single-pass pre-build validation - **Installer validation tooling** with snapshot-based evidence collection - **Binding redirect cleanup** — eliminated manual `` entries - **Developer environment tooling** (Defender exclusions, dependency verification) - **AGENTS.md documentation convention** for AI agent and developer onboarding --- .GitHub/copilot-setup-steps.yml | 78 - .gitattributes | 3 + .github/AGENTS.md | 143 + .github/AI_GOVERNANCE.md | 71 + .github/BUILD_REQUIREMENTS.md | 79 + .github/agents/WinFormsExpert.agent.md | 628 + .github/agents/debug.agent.md | 79 + .github/agents/devils-advocate.agent.md | 41 + .../fieldworks.avalonia-expert.agent.md | 52 + .github/agents/fieldworks.cpp-expert.agent.md | 53 + .../agents/fieldworks.csharp-expert.agent.md | 69 + .../fieldworks.winforms-expert.agent.md | 59 + .github/chatmodes/coding-agent.chatmode.md | 44 + .../chatmodes/installer-engineer.chatmode.md | 19 + .../chatmodes/managed-engineer.chatmode.md | 24 + .github/chatmodes/native-engineer.chatmode.md | 22 + .../chatmodes/technical-writer.chatmode.md | 20 + .github/check_copilot_docs.py | 414 + .github/commit-guidelines.md | 34 + .github/context/codebase.context.md | 18 + .github/copilot-framework-tasks.md | 67 + .github/copilot_apply_updates.py | 163 + .github/copilot_cache.py | 46 + .github/copilot_change_utils.py | 109 + .github/copilot_doc_utils.py | 68 + .github/copilot_tree_hash.py | 90 + .github/dependabot.yml | 47 + .github/detect_copilot_needed.py | 259 + .github/instructions/build.instructions.md | 65 + .../instructions/debugging.instructions.md | 63 + ...rchitecture-good-practices.instructions.md | 279 + .../dotnet-framework.instructions.md | 113 + .../dotnet-upgrade.instructions.md | 287 + .../instructions/installer.instructions.md | 144 + .github/instructions/managed.instructions.md | 114 + .github/instructions/native.instructions.md | 69 + .github/instructions/repo.instructions.md | 14 + .github/instructions/security.instructions.md | 21 + .github/instructions/terminal.instructions.md | 34 + .github/instructions/testing.instructions.md | 94 + .github/memory.md | 10 + .github/migrate_copilot_format.py | 131 + .github/option3-plan.md | 50 + .github/plan_copilot_updates.py | 358 + .github/prompts/bugfix.prompt.md | 38 + .../prompts/copilot-folder-review.prompt.md | 29 + .../prompts/dotnet-best-practices.prompt.md | 84 + .../dotnet-design-pattern-review.prompt.md | 41 + .github/prompts/dotnet-upgrade.prompt.md | 115 + .github/prompts/feature-spec.prompt.md | 41 + .github/prompts/opsx-apply.prompt.md | 149 + .github/prompts/opsx-archive.prompt.md | 154 + .github/prompts/opsx-bulk-archive.prompt.md | 239 + .github/prompts/opsx-continue.prompt.md | 111 + .github/prompts/opsx-explore.prompt.md | 171 + .github/prompts/opsx-ff.prompt.md | 91 + .github/prompts/opsx-new.prompt.md | 66 + .github/prompts/opsx-onboard.prompt.md | 522 + .github/prompts/opsx-sync.prompt.md | 131 + .github/prompts/opsx-verify.prompt.md | 161 + .github/prompts/test-failure-debug.prompt.md | 22 + .github/pull_request_template.md | 18 + .github/recipes/add-dialog-xworks.md | 18 + .github/recipes/extend-cellar-schema.md | 18 + .github/scaffold_copilot_markdown.py | 319 + .../atlassian-readonly-skills/.env.example | 78 + .../atlassian-readonly-skills/REFERENCE.md | 598 + .../skills/atlassian-readonly-skills/SKILL.md | 569 + .../requirements.txt | 3 + .../scripts/__init__.py | 0 .../scripts/_common.py | 742 + .../scripts/bitbucket_commits.py | 166 + .../scripts/bitbucket_files.py | 190 + .../scripts/bitbucket_projects.py | 145 + .../scripts/bitbucket_pull_requests.py | 151 + .../scripts/confluence_comments.py | 89 + .../scripts/confluence_labels.py | 71 + .../scripts/confluence_pages.py | 96 + .../scripts/confluence_search.py | 102 + .../scripts/jira_agile.py | 292 + .../scripts/jira_issues.py | 68 + .../scripts/jira_links.py | 63 + .../scripts/jira_projects.py | 182 + .../scripts/jira_search.py | 166 + .../scripts/jira_users.py | 135 + .../scripts/jira_workflow.py | 108 + .../scripts/jira_worklog.py | 87 + .github/skills/atlassian-skills/.env.example | 78 + .github/skills/atlassian-skills/REFERENCE.md | 624 + .github/skills/atlassian-skills/SKILL.md | 740 + .../skills/atlassian-skills/requirements.txt | 3 + .../atlassian-skills/scripts/__init__.py | 7 + .../atlassian-skills/scripts/_common.py | 742 + .../scripts/bitbucket_commits.py | 166 + .../scripts/bitbucket_files.py | 190 + .../scripts/bitbucket_projects.py | 145 + .../scripts/bitbucket_pull_requests.py | 425 + .../scripts/confluence_comments.py | 140 + .../scripts/confluence_labels.py | 159 + .../scripts/confluence_pages.py | 257 + .../scripts/confluence_search.py | 102 + .../atlassian-skills/scripts/jira_agile.py | 415 + .../atlassian-skills/scripts/jira_issues.py | 332 + .../atlassian-skills/scripts/jira_links.py | 281 + .../atlassian-skills/scripts/jira_projects.py | 247 + .../atlassian-skills/scripts/jira_search.py | 166 + .../atlassian-skills/scripts/jira_users.py | 135 + .../atlassian-skills/scripts/jira_workflow.py | 170 + .../atlassian-skills/scripts/jira_worklog.py | 187 + .github/skills/beads/CLAUDE.md | 86 + .github/skills/beads/README.md | 121 + .github/skills/beads/SKILL.md | 100 + .../adr/0001-bd-prime-as-source-of-truth.md | 59 + .github/skills/beads/resources/AGENTS.md | 62 + .github/skills/beads/resources/ASYNC_GATES.md | 168 + .github/skills/beads/resources/BOUNDARIES.md | 469 + .../beads/resources/CHEMISTRY_PATTERNS.md | 197 + .../skills/beads/resources/CLI_REFERENCE.md | 702 + .../skills/beads/resources/DEPENDENCIES.md | 747 + .../beads/resources/INTEGRATION_PATTERNS.md | 407 + .../skills/beads/resources/ISSUE_CREATION.md | 139 + .github/skills/beads/resources/MOLECULES.md | 354 + .github/skills/beads/resources/PATTERNS.md | 341 + .../skills/beads/resources/RESUMABILITY.md | 207 + .github/skills/beads/resources/STATIC_DATA.md | 54 + .../skills/beads/resources/TROUBLESHOOTING.md | 489 + .github/skills/beads/resources/WORKFLOWS.md | 623 + .github/skills/beads/resources/WORKTREES.md | 94 + .github/skills/execute-implement/SKILL.md | 38 + .github/skills/jira-to-beads/SKILL.md | 95 + .../scripts/create_beads_from_jira.py | 336 + .../scripts/export_jira_assigned.py | 127 + .github/skills/openspec-apply-change/SKILL.md | 156 + .../skills/openspec-archive-change/SKILL.md | 114 + .github/skills/openspec-beads-import/SKILL.md | 191 + .../openspec-bulk-archive-change/SKILL.md | 246 + .../skills/openspec-continue-change/SKILL.md | 118 + .github/skills/openspec-explore/SKILL.md | 290 + .github/skills/openspec-ff-change/SKILL.md | 101 + .github/skills/openspec-new-change/SKILL.md | 74 + .github/skills/openspec-onboard/SKILL.md | 529 + .github/skills/openspec-sync-specs/SKILL.md | 138 + .../skills/openspec-verify-change/SKILL.md | 168 + .github/skills/plan-design/SKILL.md | 37 + .github/skills/powershell/SKILL.md | 57 + .github/skills/review/SKILL.md | 36 + .github/skills/session-workflow/SKILL.md | 261 + .github/skills/verify-test/SKILL.md | 36 + .github/src-catalog.md | 202 + .../organizational-copilot.template.md | 16 + .github/workflows/CI.yml | 233 +- .github/workflows/CommitMessage.yml | 94 +- .github/workflows/base-installer-cd.yml | 558 +- .github/workflows/check-whitespace.yml | 68 +- .github/workflows/copilot-docs-detect.yml | 34 + .github/workflows/copilot-setup-steps.yml | 139 + .github/workflows/link-check.yml | 23 + .github/workflows/lint-docs.yml | 63 + .github/workflows/openspec-validate.yml | 17 + .github/workflows/patch-installer-cd.yml | 128 +- .gitignore | 61 +- .serena/.gitignore | 2 + .serena/memories/architecture.md | 51 + .serena/memories/common_issues.md | 83 + .serena/memories/project_overview.md | 7 + .serena/memories/style_and_conventions.md | 8 + .serena/memories/suggested_commands.md | 9 + .serena/memories/task_completion.md | 7 + .serena/project.yml | 125 + .vscode/context7-configuration.json | 80 + .vscode/extensions.json | 22 + .vscode/launch.json | 193 +- .vscode/mcp.json | 20 + .vscode/settings.json | 127 + .vscode/tasks.json | 604 +- AGENTS.md | 176 + Bin/CollectUnit++Tests.cmd | 12 +- Bin/Mktstw.bat | 10 - Bin/RemakeFw.bat | 24 - Bin/Rhino/Rhino.Mocks.dll | Bin 315904 -> 0 bytes Bin/_EnsureRoot.bat | 17 - Bin/mkGenLib-tst.bat | 34 - Bin/mkGenLib.bat | 10 - Bin/mkaft.bat | 10 - Bin/mkall-tst.bat | 67 - Bin/mkdir-wrapper.cmd | 43 + Bin/mkdp.bat | 10 - Bin/mkecob.bat | 105 - Bin/mkfwk-tst.bat | 34 - Bin/mkfwk.bat | 10 - Bin/mkgrc.bat | 10 - Bin/mkgre.bat | 10 - Bin/mkhv.bat | 10 - Bin/mkhw.bat | 10 - Bin/mkhwt.bat | 10 - Bin/mkhwv.bat | 10 - Bin/mkhwx.bat | 10 - Bin/mklg-tst.bat | 34 - Bin/mklg.bat | 10 - Bin/mklgt.bat | 10 - Bin/mktlbs.bat | 16 - Bin/mktsth.bat | 13 - Bin/mktv.bat | 10 - Bin/mkvw-tst.bat | 34 - Bin/mkvw.bat | 10 - Bin/nmock/NMock.dll | Bin 53248 -> 0 bytes Bin/nmock/NMock.pdb | Bin 138752 -> 0 bytes Bin/nmock/src/LICENSE.txt | 32 - Bin/nmock/src/README.txt | 14 - Bin/nmock/src/build.bat | 2 - Bin/nmock/src/ccnet/ccnet-nmock.bat | 2 - Bin/nmock/src/ccnet/ccnet.config | 36 - Bin/nmock/src/continuousintegration.build | 24 - Bin/nmock/src/lib/nunit-console.exe | Bin 24576 -> 0 bytes Bin/nmock/src/lib/nunit-console.exe.config | 92 - Bin/nmock/src/lib/nunit.framework.dll | Bin 28672 -> 0 bytes Bin/nmock/src/lib/nunit.util.dll | Bin 81920 -> 0 bytes Bin/nmock/src/nmock.build | 68 - Bin/nmock/src/sample/build.build | 14 - Bin/nmock/src/sample/order/Notifier.cs | 36 - Bin/nmock/src/sample/order/Order.cs | 47 - Bin/nmock/src/sample/order/OrderProcessor.cs | 32 - .../src/sample/order/OrderProcessorTest.cs | 119 - Bin/nmock/src/sample/random/Weather.cs | 64 - Bin/nmock/src/sample/random/WeatherTest.cs | 110 - Bin/nmock/src/sample/sample.csproj | 141 - Bin/nmock/src/src/NMock.csproj | 194 - Bin/nmock/src/src/NMock/CallMethodOrder.cs | 63 - .../src/src/NMock/CallMethodWithParams.cs | 79 - .../src/NMock/CallMethodWithoutExpectation.cs | 41 - .../src/src/NMock/Constraints/Constraints.cs | 513 - .../src/src/NMock/Constraints/IConstraint.cs | 10 - .../src/src/NMock/Constraints/IsArrayEqual.cs | 50 - .../src/src/NMock/Dynamic/ClassGenerator.cs | 750 - .../src/src/NMock/Dynamic/InterfaceLister.cs | 44 - Bin/nmock/src/src/NMock/DynamicMock.cs | 241 - Bin/nmock/src/src/NMock/IInvocationHandler.cs | 12 - Bin/nmock/src/src/NMock/IMethod.cs | 23 - Bin/nmock/src/src/NMock/IMock.cs | 83 - Bin/nmock/src/src/NMock/IVerifiable.cs | 13 - Bin/nmock/src/src/NMock/Invocation.cs | 22 - Bin/nmock/src/src/NMock/Method.cs | 110 - Bin/nmock/src/src/NMock/MethodSignature.cs | 66 - Bin/nmock/src/src/NMock/Mock.cs | 381 - Bin/nmock/src/src/NMock/MockCall.cs | 148 - Bin/nmock/src/src/NMock/NMock.csproj | 168 - .../src/src/NMock/Remoting/MockServer.cs | 37 - .../src/src/NMock/Remoting/RemotingMock.cs | 22 - Bin/nmock/src/src/NMock/SingleMethod.cs | 42 - Bin/nmock/src/src/NMock/VerifyException.cs | 47 - Bin/nmock/src/src/NMock/build.build | 20 - Bin/nmock/src/src/src.csproj | 194 - .../test/NMock/Constraints/ConstraintsTest.cs | 423 - .../NMock/Constraints/IsArrayEqualTest.cs | 166 - .../test/NMock/Dynamic/ClassGeneratorTest.cs | 533 - .../test/NMock/Dynamic/InterfaceListerTest.cs | 183 - Bin/nmock/src/test/NMock/DynamicMockTest.cs | 353 - .../src/test/NMock/FastErrorHandlingTest.cs | 125 - Bin/nmock/src/test/NMock/MockTest.cs | 584 - Bin/nmock/src/test/NMock/NMockTests.csproj | 174 - .../src/test/NMock/Remoting/MockServerTest.cs | 36 - .../test/NMock/Remoting/RemotingMockTest.cs | 26 - .../src/test/NMock/VerifyExceptionTest.cs | 26 - Bin/nmock/src/test/NMock/build.build | 30 - Bin/nmock/src/test/test.csproj | 162 - Bin/nmock/src/tools/NAnt.Core.dll | Bin 106496 -> 0 bytes Bin/nmock/src/tools/NAnt.DotNetTasks.dll | Bin 32768 -> 0 bytes Bin/nmock/src/tools/NAnt.NUnit1Tasks.dll | Bin 32768 -> 0 bytes Bin/nmock/src/tools/NAnt.NUnitTasks.dll | Bin 9728 -> 0 bytes Bin/nmock/src/tools/NDoc.Core.dll | Bin 57344 -> 0 bytes Bin/nmock/src/tools/nant.exe | Bin 16384 -> 0 bytes Bin/nmock/src/tools/nunit.core.dll | Bin 61440 -> 0 bytes Bin/nmock/src/tools/nunit.framework.dll | Bin 28672 -> 0 bytes Bin/nunitforms/FormsTester.dll | Bin 17408 -> 0 bytes .../FormsTester/AmbiguousNameException.cs | 66 - .../source/FormsTester/ControlFinder.cs | 206 - .../FormsTester/ControlNotVisibleException.cs | 55 - .../source/FormsTester/ControlTester.cs | 379 - Bin/nunitforms/source/FormsTester/Finder.cs | 115 - .../source/FormsTester/FormCollection.cs | 117 - .../source/FormsTester/FormFinder.cs | 117 - .../FormsTestAssertionException.cs | 49 - .../source/FormsTester/FormsTester.csproj | 73 - .../source/FormsTester/ModalFormTester.cs | 230 - .../source/FormsTester/NUnitFormTest.cs | 219 - .../FormsTester/NoSuchControlException.cs | 55 - .../FormsTester/Properties/AssemblyInfo.cs | 68 - Bin/nunitforms/source/FormsTester/Win32.cs | 67 - Bin/nunitforms/source/FormsTester/readme.txt | 5 - Bin/testWrapper.cmd | 11 - Bin/wrapper.cmd | 13 - Bld/_init.mak | 23 +- Bld/_targ.mak | 15 +- Build/Agent/FwBuildEnvironment.psm1 | 291 + Build/Agent/FwBuildHelpers.psm1 | 327 + Build/Agent/GitHelpers.ps1 | 15 + Build/Agent/Preprocess-WixIncludes.ps1 | 109 + Build/Agent/Rebuild-TestProjects.ps1 | 136 + Build/Agent/Remove-StaleDlls.ps1 | 245 + Build/Agent/Run-VsTests.ps1 | 217 + Build/Agent/Setup-DefenderExclusions.ps1 | 390 + Build/Agent/Setup-FwBuildEnv.ps1 | 266 + Build/Agent/Setup-InstallerBuild.ps1 | 389 + Build/Agent/Setup-Serena.ps1 | 212 + Build/Agent/Verify-FwDependencies.ps1 | 258 + Build/Agent/check-and-fix-whitespace.ps1 | 9 + Build/Agent/check-and-fix-whitespace.sh | 6 + Build/Agent/check-whitespace.ps1 | 95 + Build/Agent/check-whitespace.sh | 72 + Build/Agent/commit-messages.ps1 | 35 + Build/Agent/commit-messages.sh | 32 + Build/Agent/fix-whitespace.ps1 | 60 + Build/Agent/fix-whitespace.sh | 36 + Build/Agent/lib_git.sh | 19 + Build/Agent/validate-test-exclusions.ps1 | 54 + Build/FieldWorks.proj | 40 - Build/FwBuildTasks.targets | 87 +- Build/Installer.Wix3.targets | 32 + Build/Installer.legacy.targets | 739 + Build/Installer.targets | 773 +- Build/InstallerBuild.proj | 35 + Build/LocalLibrary.targets | 4 +- Build/Localize.targets | 382 +- Build/NuGet.targets | 52 - Build/PackageRestore.targets | 573 + Build/RegFree.targets | 127 +- Build/SetupInclude.targets | 620 +- Build/SilVersions.props | 22 + Build/Src/Directory.Packages.props | 12 + Build/Src/FwBuildTasks/CollectTargets.cs | 477 +- Build/Src/FwBuildTasks/Directory.Build.props | 6 + Build/Src/FwBuildTasks/FwBuildTasks.csproj | 25 +- .../FwBuildTasksTests/ClouseauTests.cs | 142 +- .../FwBuildTasksTests/GoldEticToXliffTests.cs | 70 +- .../LocalizeFieldWorksTests.cs | 32 +- .../FwBuildTasksTests/LocalizeListsTests.cs | 54 +- .../NormalizeLocalesTests.cs | 6 +- .../FwBuildTasksTests/PoToXmlTests.cs | 478 +- .../FwBuildTasksTests/RegFreeCreatorTests.cs | 101 + .../FwBuildTasksTests/TaskTestUtils.cs | 19 + .../FwBuildTasksTests/WxsToWxiTests.cs | 10 +- .../FwBuildTasksTests/XliffToGoldEticTests.cs | 2 +- .../FwBuildTasksTests/XmlToPoTests.cs | 975 +- Build/Src/FwBuildTasks/Make.cs | 55 +- Build/Src/FwBuildTasks/README.md | 5 - Build/Src/FwBuildTasks/RegFree.cs | 300 +- Build/Src/FwBuildTasks/RegFreeCreator.cs | 754 +- Build/Src/FwBuildTasks/RegHelper.cs | 219 +- Build/Src/FwBuildTasks/Substitute.cs | 9 + Build/Src/FwBuildTasks/app.config | 2 +- Build/Src/NUnitReport/NUnitReport.csproj | 97 +- Build/Src/NativeBuild/NativeBuild.csproj | 81 + Build/Windows.targets | 240 +- Build/build | 22 - Build/build-recent | 43 - Build/build.bat | 72 - Build/build64.bat | 8 - Build/buildLocalLibraries.sh | 30 +- Build/mkall.targets | 845 +- Build/multitry | 14 - Build/nuget-common/packages.config | 98 - Build/nuget-windows/packages.config | 8 - Build/run-in-environ | 9 - Build/scripts/Invoke-CppTest.ps1 | 523 + Directory.Build.props | 201 + Directory.Build.targets | 28 + Directory.Packages.props | 156 + DistFiles/Aga.Controls.dll | Bin 149504 -> 0 bytes DistFiles/GAFAWSAnalysis.dll | Bin 28672 -> 0 bytes .../Configuration/Parts/Cellar.fwlayout | 4 + Docs/64bit-regfree-migration.md | 208 + Docs/CONTRIBUTING.md | 176 + Docs/agent-docs-refresh.md | 67 + Docs/architecture/data-migrations.md | 177 + Docs/architecture/dependencies.md | 134 + Docs/copilot-instructions-plan.md | 28 + Docs/copilot-refresh.md | 4 + Docs/core-developer-setup.md | 234 + Docs/installer-build-guide.md | 228 + Docs/mcp.md | 101 + Docs/traversal-sdk-migration.md | 227 + Docs/visual-studio-setup.md | 145 + Docs/vscode-stability-profile.md | 42 + Docs/workflows/pull-request-workflow.md | 190 + Docs/workflows/release-process.md | 168 + EVIDENCE_CRITERA.md | 141 + FLExInstaller/AGENTS.md | 52 + FLExInstaller/CustomComponents.wxi | 13 +- FLExInstaller/Directory.Packages.props | 10 + FLExInstaller/Overrides.wxi | 1 + FLExInstaller/wix6/AGENTS.md | 96 + FLExInstaller/wix6/CustomActionSteps.wxi | 4 + FLExInstaller/wix6/CustomComponents.wxi | 296 + FLExInstaller/wix6/CustomFeatures.wxi | 144 + FLExInstaller/wix6/FieldWorks.Bundle.wixproj | 93 + .../wix6/FieldWorks.Installer.wixproj | 166 + .../wix6/FieldWorks.OfflineBundle.wixproj | 91 + FLExInstaller/wix6/Fonts.wxi | 122 + FLExInstaller/wix6/Overrides.wxi | 19 + FLExInstaller/wix6/Redistributables.wxi | 35 + FLExInstaller/wix6/Shared/Base/Bundle.wxs | 315 + .../wix6/Shared/Base/BundleTheme.wxl | 42 + .../wix6/Shared/Base/BundleTheme.xml | 448 + .../wix6/Shared/Base/BundleThemeTemplate.wxi | 11 + .../wix6/Shared/Base/CustomAction.config | 31 + FLExInstaller/wix6/Shared/Base/Framework.wxs | 212 + .../wix6/Shared/Base/GICustomizeDlg.wxs | 54 + .../wix6/Shared/Base/GIInstallDirDlg.wxs | 36 + .../wix6/Shared/Base/GIProgressDlg.wxs | 46 + .../wix6/Shared/Base/GISetupTypeDlg.wxs | 36 + .../wix6/Shared/Base/GIWelcomeDlg.wxs | 32 + FLExInstaller/wix6/Shared/Base/KeyPathFix.xsl | 18 + .../wix6/Shared/Base/OfflineBundle.wxs | 330 + FLExInstaller/wix6/Shared/Base/TrueType.xsl | 21 + .../wix6/Shared/Base/WixUI_DialogFlow.wxs | 107 + .../wix6/Shared/Base/WixUI_en-us.wxl | 608 + .../wix6/Shared/Base/buildBaseInstaller.bat | 9 + FLExInstaller/wix6/Shared/Base/buildExe.bat | 14 + FLExInstaller/wix6/Shared/Base/buildMsi.bat | 9 + FLExInstaller/wix6/Shared/Base/setVars.bat | 10 + .../wix6/Shared/Base/signingProxy.bat | 33 + .../wix6/Shared/Common/CustomActionSteps.wxi | 5 + .../wix6/Shared/Common/CustomComponents.wxi | 61 + .../wix6/Shared/Common/CustomFeatures.wxi | 33 + .../wix6/Shared/Common/Overrides.wxi | 25 + .../wix6/Shared/Common/Redistributables.wxi | 35 + .../Shared/CustomActions/CustomActions.sln | 33 + .../CustomActions/ClassDiagram1.cd | 2 + .../CustomActions/ClosePromptForm.Designer.cs | 71 + .../CustomActions/ClosePromptForm.cs | 14 + .../CustomActions/ClosePromptForm.resx | 6052 ++++++ .../CustomActions/CustomAction.config | 31 + .../CustomActions/CustomAction.cs | 252 + .../CustomActions/CustomActions.csproj | 20 + .../CustomActions/CustomActions/Paratext.ico | Bin 0 -> 355574 bytes .../CustomActions/PromptCloseApplication.cs | 87 + .../CustomActions/Properties/AssemblyInfo.cs | 35 + .../Properties/Settings.Designer.cs | 35 + .../Properties/Settings.settings | 9 + .../CustomActions/CustomActions/RegUtils.cs | 279 + .../CustomActions/TextMessageForm.Designer.cs | 100 + .../CustomActions/TextMessageForm.cs | 15 + .../CustomActions/TextMessageForm.resx | 120 + .../CustomActions/WindowWrapper.cs | 15 + .../CustomActions/CustomActions/app.config | 15 + .../wix6/Shared/ProcRunner/ProcRunner.sln | 37 + .../Shared/ProcRunner/ProcRunner/App.config | 6 + .../ProcRunner/ProcRunner/ProcRunner.cs | 117 + .../ProcRunner/ProcRunner/ProcRunner.csproj | 9 + .../ProcRunner/Properties/AssemblyInfo.cs | 19 + .../Properties/Resources.Designer.cs | 63 + .../ProcRunner/Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 26 + .../ProcRunner/Properties/Settings.settings | 7 + .../ProcRunner/resources/working.gif | Bin 0 -> 15758 bytes .../Shared/ProcRunner/ProcRunner/working.gif | Bin 0 -> 15758 bytes FLExInstaller/wix6/Shared/resources/App.ico | Bin 0 -> 136486 bytes .../wix6/Shared/resources/Installer.ico | Bin 0 -> 94811 bytes .../wix6/Shared/resources/License.htm | 49 + .../wix6/Shared/resources/MsiBackground.bmp | Bin 0 -> 464614 bytes .../wix6/Shared/resources/bannrbmp.bmp | Bin 0 -> 114514 bytes FLExInstaller/wix6/Shared/resources/bg.png | Bin 0 -> 120 bytes .../wix6/Shared/resources/header-bg.png | Bin 0 -> 120 bytes FLExInstaller/wix6/Shared/resources/logo.png | Bin 0 -> 7955 bytes FLExInstaller/wix6/UpdateInfo.xml | 4 + FLExInstaller/wix6/UpdateInfoAlpha.xml | 304 + FLExInstaller/wix6/UpdateInfoBeta.xml | 304 + FW.6.0.ReSharper => FieldWorks.6.0.ReSharper | 0 FieldWorks.proj | 56 + FieldWorks.sln | 941 + ....DotSettings => FieldWorks.sln.DotSettings | 2 +- FieldWorks_9.3.5.1_x64.wxs | 7289 +++++++ Lib/Directory.Build.targets | 18 - Lib/debug/ECInterfaces.tlb | Bin 86836 -> 0 bytes Lib/debug/ICSharpCode.SharpZipLib.dll | Bin 192512 -> 0 bytes Lib/debug/ICSharpCode.SharpZipLib.pdb | Bin 652800 -> 0 bytes Lib/debug/ParserObject.lib | Bin 875246 -> 0 bytes Lib/debug/cport.lib | Bin 11464 -> 0 bytes Lib/debug/test.txt | Bin 0 -> 14 bytes Lib/debug/wrtXML.dll | Bin 405504 -> 0 bytes Lib/debug/xmlparse-utf16.lib | Bin 393314 -> 187624 bytes Lib/debug/xmlparse-utf16.pdb | Bin 69632 -> 0 bytes Lib/debug/xmlparse.lib | Bin 391422 -> 0 bytes Lib/debug/xmlparse.pdb | Bin 69632 -> 0 bytes Lib/release/unit++.pdb | Bin 0 -> 864256 bytes .../ConvertConsole/ConverterConsole.csproj | 148 +- Lib/src/Converter/Converter/Converter.csproj | 180 +- Lib/src/Converter/Convertlib/AssemblyInfo.cs | 3 +- .../Converter/Convertlib/ConvertLib.csproj | 135 +- .../FormLanguageSwitch.csproj | 121 +- .../ClassPropertySelector.Designer.cs | 2 +- .../ObjectBrowser/ClassPropertySelector.cs | 38 +- Lib/src/ObjectBrowser/FDOHelpers.cs | 140 + Lib/src/ObjectBrowser/ObjectBrowser.csproj | 199 +- Lib/src/ObjectBrowser/Program.cs | 4 +- Lib/src/ScrChecks/BuildInclude.targets | 27 +- Lib/src/ScrChecks/ScrChecks.csproj | 185 +- .../CapitalizationCheckSilUnitTest.cs | 108 +- .../CapitalizationCheckUnitTest.cs | 5 +- .../ScrChecksTests/ChapterVerseTests.cs | 154 +- .../ScrChecksTests/CharactersCheckUnitTest.cs | 27 +- .../MatchedPairsCheckUnitTest.cs | 11 +- .../MixedCapitalizationCheckUnitTest.cs | 31 +- .../PunctuationCheckUnitTest.cs | 124 +- .../QuotationCheckSilUnitTest.cs | 114 +- .../ScrChecksTests/QuotationCheckUnitTest.cs | 9 +- .../ScrChecksTests/RepeatedWordsCheckTests.cs | 18 +- .../RepeatedWordsCheckUnitTest.cs | 7 +- .../ScrChecksTests/ScrChecksTestBase.cs | 16 +- .../ScrChecksTests/ScrChecksTests.csproj | 163 +- ...ceFinalPunctCapitalizationCheckUnitTest.cs | 34 +- Lib/src/graphite2/graphite2.mak | 7 + Lib/src/graphite2/src/Collider.cpp | 5 +- Lib/src/unit++/GlobalSetup.cc | 1 + Lib/src/unit++/VS/unit++.vcxproj | 59 - Lib/src/unit++/main.cc | 9 + ReadMe.md | 102 +- SDK_MIGRATION.md | 582 + Setup-Developer-Machine.ps1 | 317 + Src/AppCore/AGENTS.md | 144 + Src/AppCore/Res/AfApp.rc | 3 +- Src/AppForTests.config | 91 +- Src/AssemblyInfoForTests.cs | 3 +- Src/CacheLight/AGENTS.md | 163 + Src/CacheLight/CacheLight.csproj | 207 +- .../CacheLightTests/AssemblyInfo.cs | 2 + .../CacheLightTests/CacheLightTests.csproj | 233 +- .../CacheLightTests/MetaDataCacheTests.cs | 138 +- .../CacheLightTests/RealDataCacheTests.cs | 86 +- Src/CacheLight/MetaDataCache.cs | 2 +- Src/Cellar/AGENTS.md | 99 + Src/Common/AGENTS.md | 52 + Src/Common/Controls/AGENTS.md | 52 + Src/Common/Controls/Design/AssemblyInfo.cs | 6 +- Src/Common/Controls/Design/Design.csproj | 236 +- .../Controls/DetailControls/AssemblyInfo.cs | 6 +- .../Controls/DetailControls/ButtonLauncher.cs | 2 +- .../Controls/DetailControls/DataTreeImages.cs | 2 +- .../DetailControls/DetailControls.csproj | 549 +- .../AtomicReferenceLauncherTests.cs | 5 +- .../DetailControlsTests/DataTreeTests.cs | 108 +- .../DetailControlsTests.csproj | 276 +- .../MorphTypeAtomicLauncherTests.cs | 165 + .../DetailControlsTests/SliceTests.cs | 10 +- .../DetailControlsTests/Test.fwlayout | 3 + .../DetailControlsTests/TestParts.xml | 21 + .../VectorReferenceLauncherTests.cs | 154 +- .../MSAReferenceComboBoxSlice.cs | 2 + .../DetailControls/MorphTypeAtomicLauncher.cs | 30 +- .../PossibilityAtomicReferenceView.cs | 2 - .../DetailControls/SemanticDomainsChooser.cs | 2 + Src/Common/Controls/DetailControls/Slice.cs | 2 +- .../DetailControls/VectorReferenceView.cs | 2 - .../Controls/FwControls/AssemblyInfo.cs | 6 +- .../Controls/FwControls/DropDownContainer.cs | 2 +- .../Controls/FwControls/FwControls.csproj | 563 +- .../FwControlsTests/AssemblyInfo.cs | 17 + .../CaseSensitiveListBoxTests.cs | 40 +- .../DummyPersistedFormManual.cs | 2 +- .../DummyPersistedFormWinDef.cs | 2 +- .../FwControlsTests/FwControlsTests.csproj | 272 +- .../FwControlsTests/FwSplitContainerTests.cs | 8 +- .../ObtainProjectMethodTests.cs | 4 +- .../FwControlsTests/PersistenceTest.cs | 32 +- .../FwControlsTests/ProgressDlgTests.cs | 4 +- .../FwControlsTests/TriStateTreeViewTests.cs | 120 +- .../Controls/FwControls/InformationBar.cs | 2 +- .../AssemblyInfo.cs | 27 +- .../FwControls/ProgressDialogImpl.Designer.cs | 22 +- .../FwControls/StatusBarProgressPanel.cs | 2 +- .../Controls/FwControls/TriStateTreeView.cs | 2 +- Src/Common/Controls/Widgets/AssemblyInfo.cs | 4 +- .../Widgets/DemoWidgets}/AssemblyInfo.cs | 27 +- Src/Common/Controls/Widgets/Widgets.csproj | 386 +- .../WidgetsTests/FontHeightAdjusterTests.cs | 22 +- .../Widgets/WidgetsTests/FwListBoxTests.cs | 29 +- .../FwMultilingualPropViewTests.cs | 2 +- .../Widgets/WidgetsTests/FwTextBoxTests.cs | 13 +- .../InnerLabeledMultiStringViewTests.cs | 24 +- .../Widgets/WidgetsTests/WidgetsTests.csproj | 271 +- Src/Common/Controls/XMLViews/AssemblyInfo.cs | 4 +- Src/Common/Controls/XMLViews/BrowseViewer.cs | 4 +- Src/Common/Controls/XMLViews/BulkEditBar.cs | 2 +- .../XMLViews/ColumnConfigureDialog.cs | 2 +- Src/Common/Controls/XMLViews/DhListView.cs | 2 + .../XMLViews/ReallySimpleListChooser.cs | 3 +- Src/Common/Controls/XMLViews/XMLViews.csproj | 500 +- .../XMLViews/XMLViewsTests/AssemblyInfo.cs | 58 + .../XMLViewsTests/ConfiguredExportTests.cs | 76 +- .../XMLViewsTests/LayoutMergerTests.cs | 24 +- .../TestColumnConfigureDialog.cs | 8 +- .../XMLViews/XMLViewsTests/TestLayoutMerge.cs | 2 +- .../XMLViewsTests/TestManyOneBrowse.cs | 193 +- .../XMLViewsTests/TestNeededPropertyInfo.cs | 14 +- .../XMLViewsTests/TestObjectListPublisher.cs | 16 +- .../XMLViewsTests/TestPartGenerate.cs | 50 +- .../XMLViewsTests/TestXmlViewsDataCache.cs | 10 +- .../XMLViewsTests/TestXmlViewsUtils.cs | 48 +- .../XMLViewsTests/XMLViewsTests.csproj | 369 +- .../XMLViewsTests/XmlBrowseViewBaseVcTests.cs | 2 +- .../Controls/XMLViews/XmlBrowseViewBaseVc.cs | 3 +- Src/Common/FieldWorks/AGENTS.md | 170 + Src/Common/FieldWorks/App.config | 97 +- .../FieldWorks/Branding}/LT.ico | Bin .../FieldWorks/Branding}/LT.png | Bin .../FieldWorks/Branding}/LT128.png | Bin .../FieldWorks/Branding}/LT64.png | Bin Src/Common/FieldWorks/BuildInclude.targets | 14 +- .../FieldWorks/FieldWorks.Diagnostics.config | 5 + .../FieldWorks.Diagnostics.dev.config | 17 + Src/Common/FieldWorks/FieldWorks.cs | 60 +- Src/Common/FieldWorks/FieldWorks.csproj | 425 +- Src/Common/FieldWorks/FieldWorks.exe.manifest | 28 + .../FieldWorksTests/FieldWorksTests.cs | 49 +- .../FieldWorksTests/FieldWorksTests.csproj | 208 +- .../FieldWorksTests/PaObjectsTests.cs | 6 +- .../FieldWorksTests/ProjectIDTests.cs | 141 +- Src/Common/FieldWorks/ProjectId.cs | 14 +- .../FieldWorks/Properties/AssemblyInfo.cs | 6 +- Src/Common/Filters/AGENTS.md | 157 + Src/Common/Filters/AssemblyInfo.cs | 2 +- Src/Common/Filters/Filters.csproj | 250 +- .../FiltersTests/DateTimeMatcherTests.cs | 171 +- .../Filters/FiltersTests/FiltersTests.csproj | 254 +- .../FiltersTests/FindResultsSorterTests.cs | 2 +- .../FiltersTests/RangeIntMatcherTests.cs | 10 +- .../Filters/FiltersTests/TestPersistence.cs | 88 +- Src/Common/Framework/AGENTS.md | 181 + Src/Common/Framework/AssemblyInfo.cs | 6 +- Src/Common/Framework/Framework.csproj | 378 +- .../Framework/FrameworkTests/AssemblyInfo.cs | 17 + .../FrameworkTests/FrameworkTests.csproj | 280 +- .../FrameworkTests/FwEditingHelperTests.cs | 333 +- .../Framework/FrameworkTests/SelInfoTests.cs | 140 +- Src/Common/Framework/FwRootSite.cs | 2 +- Src/Common/Framework/StylesXmlAccessor.cs | 6 +- Src/Common/FwUtils/AGENTS.md | 107 + Src/Common/FwUtils/DebugProcs.cs | 38 +- Src/Common/FwUtils/FwDirectoryFinder.cs | 163 +- Src/Common/FwUtils/FwRegistryHelper.cs | 4 +- Src/Common/FwUtils/FwUtils.cs | 66 + Src/Common/FwUtils/FwUtils.csproj | 385 +- .../FwUtils/FwUtilsTests/AlphaOutlineTests.cs | 36 +- .../FwUtils/FwUtilsTests/AssemblyInfo.cs | 19 + .../FwUtilsTests/AssemblySetupFixture.cs | 39 + .../CreateComObjectsFromManifestAttribute.cs | 2 +- .../InitializeFwRegistryHelperAttribute.cs | 42 +- .../CharEnumeratorForByteArrayTests.cs | 8 +- .../FwUtils/FwUtilsTests/DebugProcsTests.cs | 18 +- .../FwUtilsTests/DisposableObjectsSetTests.cs | 20 +- .../FwUtilsTests/DummyFwRegistryHelper.cs | 3 +- .../FwUtilsTests/FLExBridgeHelperTests.cs | 4 + .../FwUtilsTests/FwDirectoryFinderTests.cs | 12 +- .../FwUtils/FwUtilsTests/FwLinkArgsTests.cs | 240 +- .../FwUtilsTests/FwRegistryHelperTests.cs | 107 +- .../FwUtils/FwUtilsTests/FwUpdaterTests.cs | 92 +- .../FwUtils/FwUtilsTests/FwUtilsTests.csproj | 282 +- .../FwUtils/FwUtilsTests/IVwCacheDaTests.cs | 236 +- .../FwUtils/FwUtilsTests/ImagePictureTest.cs | 16 +- .../FwUtils/FwUtilsTests/InterfacesTests.cs | 36 +- .../ManagedPictureFactoryTests.cs | 6 +- .../FwUtils/FwUtilsTests/MatchedPairsTests.cs | 94 +- .../FwUtilsTests/MeasurementUtilsTest.cs | 99 +- .../FwUtilsTests/ParagraphCorrelationTests.cs | 34 +- .../FwUtils/FwUtilsTests/PubSubSystemTests.cs | 110 +- .../FwUtilsTests/QuotationMarksTests.cs | 178 +- .../FwUtils/FwUtilsTests/SimpleLoggerTests.cs | 4 +- .../FwUtils/FwUtilsTests/StringTableTests.cs | 53 +- .../FwUtilsTests/TempSFFileMakerTests.cs | 250 +- .../FwUtilsTests/TestFwStylesheetTests.cs | 32 +- .../FwUtils/FwUtilsTests/WavConverterTests.cs | 24 +- Src/Common/FwUtils/ManagedPictureFactory.cs | 1 + Src/Common/FwUtils/Properties/AssemblyInfo.cs | 6 +- Src/Common/FwUtils/StringTable.cs | 45 +- Src/Common/RootSite/AGENTS.md | 107 + Src/Common/RootSite/AssemblyInfo.cs | 4 +- Src/Common/RootSite/RootSite.cs | 3 - Src/Common/RootSite/RootSite.csproj | 317 +- .../RootSite/RootSiteTests/AssemblyInfo.cs | 17 + .../RootSiteTests/BasicViewTestsBase.cs | 2 +- .../RootSiteTests/CollectorEnvTests.cs | 2 +- .../RootSiteTests/MoreRootSiteTests.cs | 660 +- .../RootSiteTests/PrintRootSiteTests.cs | 46 +- .../RootSiteEditingHelperTests.cs | 12 +- .../RootSiteTests/RootSiteGroupTests.cs | 43 +- .../RootSiteTests/RootSiteTests.csproj | 321 +- .../RootSite/RootSiteTests/StVcTests.cs | 52 +- .../RootSiteTests/UndoTaskHelperTests.cs | 24 +- Src/Common/ScriptureUtils/AGENTS.md | 115 + Src/Common/ScriptureUtils/AssemblyInfo.cs | 4 +- .../ScriptureUtils/ScriptureProvider.cs | 93 +- .../ScriptureUtils/ScriptureUtils.csproj | 251 +- .../ScriptureUtilsTests/AssemblyInfo.cs | 18 + .../ParatextHelperTests.cs | 233 +- .../ScrReferencePositionComparerTests.cs | 36 +- .../ScriptureReferenceComparerTests.cs | 36 +- .../ScriptureUtilsTests.csproj | 271 +- Src/Common/SimpleRootSite/AGENTS.md | 176 + Src/Common/SimpleRootSite/AssemblyInfo.cs | 6 +- Src/Common/SimpleRootSite/EditingHelper.cs | 11 +- .../IbusRootSiteEventHandler.cs | 2 +- .../Properties/Resources.Designer.cs | 4 +- Src/Common/SimpleRootSite/SelectionHelper.cs | 2 +- Src/Common/SimpleRootSite/SimpleRootSite.cs | 46 +- .../SimpleRootSite/SimpleRootSite.csproj | 331 +- .../SimpleRootSiteTests/EditingHelperTests.cs | 50 +- .../IbusRootSiteEventHandlerTests.cs | 2195 -- .../IbusRootSiteEventHandlerTests_Simple.cs | 316 - .../RenderEngineFactoryTests.cs | 12 +- .../SelectionHelperTests.cs | 189 +- .../SimpleRootSiteTests/SimpleBasicView.cs | 2 +- .../SimpleRootSiteTests.csproj | 319 +- ...leRootSiteTests_IsSelectionVisibleTests.cs | 102 +- ...leRootSiteTests_ScrollSelectionIntoView.cs | 40 +- .../TsStringWrapperTests.cs | 2 +- Src/Common/SimpleRootSite/ViewInputManager.cs | 1 + Src/Common/UIAdapterInterfaces/AGENTS.md | 112 + .../UIAdapterInterfaces/AssemblyInfo.cs | 4 +- .../UIAdapterInterfaces.csproj | 215 +- Src/Common/ViewsInterfaces/AGENTS.md | 159 + Src/Common/ViewsInterfaces/AssemblyInfo.cs | 4 +- .../ViewsInterfaces/BuildInclude.targets | 78 +- .../ViewsInterfaces/ViewsInterfaces.csproj | 210 +- .../ExtraComInterfacesTests.cs | 22 +- .../Properties/AssemblyInfo.cs | 10 +- .../ViewsInterfacesTests.csproj | 167 +- .../ViewsInterfacesTests/VwGraphicsTests.cs | 74 +- .../ViewsInterfaces/VwPropertyStoreManaged.cs | 39 +- Src/CommonAssemblyInfoTemplate.cs | 12 +- Src/DbExtend/AGENTS.md | 94 + Src/DebugProcs/AGENTS.md | 113 + Src/DebugProcs/DebugProcs.vcxproj | 54 +- Src/Directory.Build.props | 38 + Src/Directory.Build.targets | 18 - Src/DocConvert/AGENTS.md | 80 + Src/FXT/AGENTS.md | 102 + Src/FXT/FxtDll/AssemblyInfo.cs | 4 +- Src/FXT/FxtDll/FxtDll.csproj | 211 +- Src/FXT/FxtDll/FxtDllTests/DumperTests.cs | 20 +- Src/FXT/FxtDll/FxtDllTests/FxtDllTests.csproj | 244 +- Src/FXT/FxtDll/FxtDllTests/FxtTestBase.cs | 2 +- .../FxtDllTests/StandFormatExportTests.cs | 16 +- Src/FXT/FxtExe/AssemblyInfo.cs | 4 +- Src/FXT/FxtExe/ConsoleLcmUI.cs | 88 + Src/FXT/FxtExe/FxtExe.csproj | 222 +- Src/FXT/FxtExe/NullThreadedProgress.cs | 77 + Src/FXT/FxtExe/main.cs | 79 +- Src/FdoUi/AGENTS.md | 108 + Src/FdoUi/AssemblyInfo.cs | 4 +- Src/FdoUi/FdoUi.csproj | 445 +- Src/FdoUi/FdoUiTests/FdoUiTests.cs | 12 +- Src/FdoUi/FdoUiTests/FdoUiTests.csproj | 193 +- Src/FdoUi/FwLcmUI.cs | 3 + Src/FwCoreDlgs/AGENTS.md | 108 + Src/FwCoreDlgs/AddCnvtrDlg.cs | 418 +- Src/FwCoreDlgs/AddCnvtrDlg.resx | 2 +- .../AdvancedScriptRegionVariantModel.cs | 1 - ...dvancedScriptRegionVariantView.Designer.cs | 105 +- Src/FwCoreDlgs/AssemblyInfo.cs | 4 +- Src/FwCoreDlgs/BackupProjectSettings.cs | 9 +- .../BackupRestore/BackupProjectPresenter.cs | 6 +- .../BackupRestore/RestoreProjectPresenter.cs | 2 + Src/FwCoreDlgs/CnvtrPropertiesCtrl.cs | 27 +- .../{ConverterTest.cs => ConverterTester.cs} | 12 +- ...onverterTest.resx => ConverterTester.resx} | 2 +- Src/FwCoreDlgs/FindCollectorEnv.cs | 123 +- Src/FwCoreDlgs/FwChooserDlg.cs | 2 +- .../FwCoreDlgControls/AssemblyInfo.cs | 4 +- .../FwCoreDlgControls/FontFeaturesButton.cs | 2 +- .../FwCoreDlgControls.csproj | 413 +- .../DefaultFontsControlTests.cs | 2 +- .../FwAttributesTests.cs | 12 +- .../FwCoreDlgControlsTests.csproj | 265 +- .../FwCoreDlgControlsTests/FwFontTabTests.cs | 7 +- .../FwCoreDlgControlsTests/StyleInfoTests.cs | 62 +- .../StylesComboTests.cs | 103 +- .../TestFontFeaturesButton.cs | 36 +- .../UpDownMeasureControlTests.cs | 332 +- .../FwCoreDlgControls/LocaleMenuButton.cs | 2 +- Src/FwCoreDlgs/FwCoreDlgs.csproj | 841 +- Src/FwCoreDlgs/FwCoreDlgs.resx | 56 +- .../AdvancedScriptRegionVariantModelTests.cs | 17 +- Src/FwCoreDlgs/FwCoreDlgsTests/App.config | 4 +- .../FwCoreDlgsTests/AssemblyInfo.cs | 77 + .../CnvtrPropertiesCtrlTests.cs | 315 +- .../FwCoreDlgsTests/FindCollectorEnvTests.cs | 30 +- .../FwCharacterCategorizerTests.cs | 20 +- .../FwCoreDlgsTests/FwCoreDlgsTests.csproj | 364 +- .../FwCoreDlgsTests/FwFindReplaceDlgTests.cs | 480 +- .../FwCoreDlgsTests/FwFontDialogTests.cs | 2 +- .../FwNewLangProjectModelTests.cs | 81 +- .../FwCoreDlgsTests/FwStylesDlgTests.cs | 20 +- .../FwWritingSystemSetupDlgTests.cs | 124 +- .../FwWritingSystemSetupModelTests.cs | 450 +- .../FwCoreDlgsTests/RealSplashScreenTests.cs | 2 +- .../RestoreProjectPresenterTests.cs | 63 +- .../ValidCharactersDlgTests.cs | 76 +- .../ViewHiddenWritingSystemsModelTests.cs | 22 +- Src/FwCoreDlgs/FwFindReplaceDlg.cs | 137 +- Src/FwCoreDlgs/FwHelpAbout.cs | 76 +- Src/FwCoreDlgs/FwNewLangProject.Designer.cs | 42 +- Src/FwCoreDlgs/FwNewLangProject.cs | 2 + Src/FwCoreDlgs/FwProjPropertiesDlg.cs | 2 +- Src/FwCoreDlgs/FwSplashScreen.cs | 2 +- Src/FwCoreDlgs/FwUserProperties.cs | 2 +- Src/FwCoreDlgs/FwWritingSystemSetupModel.cs | 2 - Src/FwCoreDlgs/MergeWritingSystemDlg.cs | 3 - .../PicturePropertiesDialog.Designer.cs | 75 +- Src/FwCoreDlgs/PicturePropertiesDialog.cs | 2 +- Src/FwCoreDlgs/ProjectLocationDlg.cs | 11 +- Src/FwCoreDlgs/RealSplashScreen.cs | 2 +- Src/FwParatextLexiconPlugin/AGENTS.md | 120 + Src/FwParatextLexiconPlugin/FdoLexicon.cs | 2 +- .../FwLexiconPlugin.cs | 2 +- .../FwParatextLexiconPlugin.csproj | 203 +- .../FdoLexiconTests.cs | 174 +- .../FwParatextLexiconPluginTests.csproj | 126 +- .../ParatextLexiconPluginThreadedProgress.cs | 8 +- .../Properties/AssemblyInfo.cs | 8 +- Src/FwResources/AGENTS.md | 107 + Src/FwResources/AssemblyInfo.cs | 4 +- Src/FwResources/FwResources.csproj | 285 +- Src/FwResources/ResourceHelper.cs | 2 - Src/GenerateHCConfig/AGENTS.md | 96 + Src/GenerateHCConfig/App.config | 11 +- Src/GenerateHCConfig/GenerateHCConfig.csproj | 118 +- Src/GenerateHCConfig/NullThreadedProgress.cs | 13 +- .../Properties/AssemblyInfo.cs | 4 +- Src/Generic/AGENTS.md | 155 + Src/Generic/Generic.vcxproj | 97 +- Src/Generic/Generic.vcxproj.filters | 2 +- Src/Generic/StackDumper.cpp | 70 +- Src/Generic/StackDumper.h | 2 + Src/Generic/StackDumperWin32.cpp | 253 +- Src/Generic/StackDumperWin64.cpp | 305 +- Src/Generic/StrUtil.cpp | 26 + Src/Generic/Test/TestGeneric.vcxproj | 312 +- Src/Generic/Util.cpp | 51 +- Src/InstallValidator/AGENTS.md | 88 + Src/InstallValidator/InstallValidator.csproj | 102 +- .../InstallValidatorTests.cs | 18 +- .../InstallValidatorTests.csproj | 113 +- .../WixInstallerArtifactsTests.cs | 190 + .../InstallerArtifactsTests.csproj | 36 + .../WixInstallerArtifactsTests.cs | 264 + .../Properties/AssemblyInfo.cs | 18 +- Src/Kernel/AGENTS.md | 106 + Src/Kernel/Kernel.vcxproj | 56 +- Src/LCMBrowser/AGENTS.md | 141 + Src/LCMBrowser/App.config | 19 +- Src/LCMBrowser/BuildInclude.targets | 2 +- Src/LCMBrowser/LCMBrowser.csproj | 278 +- Src/LCMBrowser/Properties/AssemblyInfo.cs | 4 +- Src/LexText/AGENTS.md | 57 + Src/LexText/Discourse/AGENTS.md | 178 + Src/LexText/Discourse/Discourse.csproj | 293 +- .../AdvancedMTDialogLogicTests.cs | 30 +- .../ConstChartRowDecoratorTests.cs | 38 +- .../ConstituentChartDatabaseTests.cs | 139 +- .../DiscourseTests/DiscourseExportTests.cs | 28 +- .../DiscourseTests/DiscourseTestHelper.cs | 70 +- .../DiscourseTests/DiscourseTests.csproj | 258 +- .../DiscourseTests/InMemoryLogicTest.cs | 222 +- .../DiscourseTests/InMemoryMoveEditTests.cs | 29 +- .../DiscourseTests/InMemoryMovedTextTests.cs | 40 +- .../DiscourseTests/InterlinRibbonTests.cs | 18 +- .../Discourse/DiscourseTests/LogicTest.cs | 142 +- .../DiscourseTests/NotifyChangeSpy.cs | 4 +- .../DiscourseTests/Properties/AssemblyInfo.cs | 29 +- .../Discourse/DiscourseTests/TestCCLogic.cs | 10 +- .../Discourse/Properties/AssemblyInfo.cs | 4 +- Src/LexText/FlexPathwayPlugin/AGENTS.md | 99 + Src/LexText/FlexPathwayPlugin/AssemblyInfo.cs | 6 +- .../FlexPathwayPlugin.csproj | 181 +- .../FlexPathwayPluginTests.cs | 24 +- .../FlexPathwayPluginTests.csproj | 183 +- .../FlexPathwayPluginTests/MyFoldersTest.cs | 10 +- Src/LexText/Interlinear/AGENTS.md | 172 + Src/LexText/Interlinear/AssemblyInfo.cs | 4 +- .../Interlinear/BIRDInterlinearImporter.cs | 3 +- .../Interlinear/ComplexConcMorphDlg.cs | 2 +- .../Interlinear/ComplexConcMorphDlg.resx | 20 +- Src/LexText/Interlinear/ComplexConcWordDlg.cs | 2 +- .../Interlinear/ComplexConcWordDlg.resx | 18 +- Src/LexText/Interlinear/ConcordanceControl.cs | 2 +- .../Interlinear/ConfigureInterlinDialog.cs | 9 +- Src/LexText/Interlinear/EditMorphBreaksDlg.cs | 2 + .../Interlinear/FilterAllTextsDialog.cs | 4 +- .../FlexInterlinModel/FlexInterlinear.cs | 2 + .../FocusBoxController.Designer.cs | 2 +- Src/LexText/Interlinear/ITextDll.csproj | 756 +- .../ITextDllTests/AddWordsToLexiconTests.cs | 110 +- .../ITextDllTests/BIRDFormatImportTests.cs | 323 +- .../ITextDllTests/ComboHandlerTests.cs | 48 +- .../ConfigureInterlinearDlgTests.cs | 40 +- .../GlossToolLoadsGuessContentsTests.cs | 12 +- .../ITextDllTests/ITextDllTests.csproj | 382 +- .../ImportInterlinearAnalysesTests.cs | 22 +- .../InterlinDocForAnalysisTests.cs | 79 +- .../ITextDllTests/InterlinLineChoicesTests.cs | 236 +- .../ITextDllTests/InterlinMasterTests.cs | 10 +- .../ITextDllTests/InterlinSfmImportTests.cs | 18 +- .../ITextDllTests/InterlinTaggingTests.cs | 21 +- .../ITextDllTests/InterlinearExporterTests.cs | 6 +- .../InterlinearTextRecordClerkTests.cs | 2 +- .../ITextDllTests/MorphemeBreakerTests.cs | 75 +- .../ITextDllTests/SandboxBaseTests.cs | 2 +- .../TextsTriStateTreeViewTests.cs | 61 +- .../ITextDllTests/WordBreakGuesserTests.cs | 20 +- .../ITextDllTests/XLingPaperExporterTests.cs | 2 +- Src/LexText/Interlinear/ImageHolder.cs | 2 +- Src/LexText/Interlinear/InterAreaBookmark.cs | 2 + .../Interlinear/InterlinDocForAnalysis.cs | 3 - Src/LexText/Interlinear/InterlinMaster.cs | 2 +- .../Interlinear/InterlinearExportDialog.cs | 1 - .../Interlinear/InterlinearExporter.cs | 2 +- .../Interlinear/InterlinearSfmImportWizard.cs | 1 - .../InterlinearTextsRecordClerk.cs | 2 - Src/LexText/Interlinear/LinguaLinksImport.cs | 3 +- .../Interlinear/SandboxBase.ComboHandlers.cs | 2 + Src/LexText/Interlinear/Sfm2FlexText.cs | 6 + Src/LexText/Interlinear/StatisticsView.cs | 2 + .../Interlinear/TextsTriStateTreeView.cs | 17 + Src/LexText/LexTextControls/AGENTS.md | 178 + Src/LexText/LexTextControls/AddNewSenseDlg.cs | 2 + Src/LexText/LexTextControls/AssemblyInfo.cs | 4 +- .../FeatureStructureTreeView.cs | 2 +- Src/LexText/LexTextControls/InsertEntryDlg.cs | 2 +- .../LexTextControls/InsertEntryDlg.resx | 5 +- .../LexImportWizardLanguage.cs | 2 +- .../LexTextControls/LexTextControls.csproj | 801 +- .../LexTextControlsTests/LexImportTests.cs | 6 +- .../LexTextControlsTests.csproj | 241 +- .../LexTextControlsTests/LiftExportTests.cs | 244 +- .../LiftMergerRelationTests.cs | 177 +- .../LexTextControlsTests/LiftMergerTests.cs | 950 +- .../MasterCategoryTests.cs | 53 +- .../MsaInflectionFeatureListDlgTests.cs | 28 +- Src/LexText/LexTextControls/LiftExporter.cs | 1 - Src/LexText/LexTextControls/LiftMerger.cs | 7 +- .../LexTextControls/LiftMergerRanges.cs | 6 +- .../LiftMergerSupportCodeAndClasses.cs | 1 - .../LexTextControls/MSAPopupTreeManager.cs | 1 - .../LexTextControls/MasterCategoryListDlg.cs | 2 +- .../MsaInflectionFeatureListDlg.cs | 2 +- .../LexTextControls/Sfm2FlexTextWords.cs | 10 +- Src/LexText/LexTextDll/AGENTS.md | 110 + Src/LexText/LexTextDll/AreaListener.cs | 2 +- Src/LexText/LexTextDll/AssemblyInfo.cs | 4 +- Src/LexText/LexTextDll/ImageHolder.cs | 2 +- Src/LexText/LexTextDll/LexTextApp.cs | 6 +- Src/LexText/LexTextDll/LexTextDll.csproj | 462 +- .../LexTextDllTests/AreaListenerTests.cs | 10 +- .../LexTextDllTests/LexTextDllTests.csproj | 186 +- Src/LexText/LexTextExe/LexText.cs | 31 - Src/LexText/LexTextExe/LexTextExe.csproj | 239 - Src/LexText/Lexicon/AGENTS.md | 169 + Src/LexText/Lexicon/AssemblyInfo.cs | 4 +- Src/LexText/Lexicon/FLExBridgeListener.cs | 1 - Src/LexText/Lexicon/ImageHolder.cs | 2 +- Src/LexText/Lexicon/LexEdDll.csproj | 612 +- .../LexEdDllTests/CircularRefBreakerTests.cs | 36 +- .../LexEdDllTests/GoldEticGuidFixerTests.cs | 11 +- .../LexEdDllTests/LexEdDllTests.csproj | 195 +- .../LexEntryChangeHandlerTests.cs | 21 +- .../LexEdDllTests/Properties/AssemblyInfo.cs | 30 +- .../ReversalEntryBulkEditTests.cs | 6 +- .../LexEdDllTests/ReversalEntryViewTests.cs | 14 +- .../SortReversalSubEntriesTests.cs | 10 +- Src/LexText/Lexicon/LexEntryImages.cs | 2 +- .../MsaInflectionFeatureListDlgLauncher.cs | 1 - .../Lexicon/ReversalIndexEntrySlice.cs | 2 +- Src/LexText/Lexicon/ReversalListener.cs | 2 + Src/LexText/Morphology/AGENTS.md | 165 + Src/LexText/Morphology/AssemblyInfo.cs | 6 +- .../Morphology/AssignFeaturesToPhonemes.cs | 1 - Src/LexText/Morphology/BasicIPASymbolSlice.cs | 2 - Src/LexText/Morphology/ConcordanceDlg.cs | 1 - Src/LexText/Morphology/ImageHolder.cs | 2 +- .../Morphology/InflAffixTemplateControl.cs | 1 - Src/LexText/Morphology/MEImages.cs | 2 +- Src/LexText/Morphology/MGA/AssemblyInfo.cs | 4 - Src/LexText/Morphology/MGA/MGA.csproj | 288 +- Src/LexText/Morphology/MGA/MGADialog.cs | 2 +- .../Morphology/MGA/MGATests/MGATests.cs | 38 +- .../Morphology/MGA/MGATests/MGATests.csproj | 227 +- .../Morphology/MorphologyEditorDll.csproj | 500 +- .../MorphologyEditorDllTests.csproj | 157 +- .../Properties/AssemblyInfo.cs | 29 +- .../RespellingTests.cs | 387 +- .../Morphology/OneAnalysisSandbox.Designer.cs | 2 +- Src/LexText/Morphology/RespellerDlg.cs | 11 +- Src/LexText/ParserCore/AGENTS.md | 194 + Src/LexText/ParserCore/AssemblyInfo.cs | 6 +- Src/LexText/ParserCore/ParserCore.csproj | 318 +- .../ParserCoreTests/HCLoaderTests.cs | 2 +- .../M3ToXAmpleTransformerTests.cs | 2 +- .../ParseFilerProcessingTests.cs | 28 +- .../ParserCoreTests/ParseWorkerTests.cs | 8 +- .../ParserCoreTests/ParserCoreTests.csproj | 242 +- .../ParserCoreTests/ParserReportTests.cs | 40 +- .../ParserCoreTests/XAmpleParserTests.cs | 8 +- Src/LexText/ParserCore/ParserWorker.cs | 2 - .../ParserCore/ParserXmlWriterExtensions.cs | 1 - .../Properties/AssemblyInfo.cs | 7 + .../XAmpleCOMWrapper/XAmpleCOMWrapper.vcxproj | 195 +- .../XAmpleManagedWrapper/AssemblyInfo.cs | 4 +- .../XAmpleManagedWrapper.csproj | 117 +- .../TestXAmpleDLLWrapper.cs | 10 +- .../TestXAmpleWrapper.cs | 4 +- .../XAmpleManagedWrapperTests.csproj | 114 +- Src/LexText/ParserCore/XAmpleParser.cs | 1 - Src/LexText/ParserUI/AGENTS.md | 174 + Src/LexText/ParserUI/AssemblyInfo.cs | 2 +- .../HCMaxCompoundRulesDlg.Designer.cs | 22 +- Src/LexText/ParserUI/ParserUI.csproj | 407 +- .../ParserUITests/ParserUITests.csproj | 193 +- .../WordGrammarDebuggingTests.cs | 2 +- Src/LexText/ParserUI/TryAWordDlg.cs | 3 +- Src/ManagedLgIcuCollator/AGENTS.md | 143 + Src/ManagedLgIcuCollator/LgIcuCollator.cs | 33 +- .../ManagedLgIcuCollator.csproj | 126 +- .../ManagedLgIcuCollatorTests.cs | 43 +- .../ManagedLgIcuCollatorTests.csproj | 142 +- Src/ManagedVwDrawRootBuffered/AGENTS.md | 116 + Src/ManagedVwDrawRootBuffered/AssemblyInfo.cs | 4 +- .../ManagedVwDrawRootBuffered.csproj | 131 +- .../VwDrawRootBuffered.cs | 339 +- Src/ManagedVwWindow/AGENTS.md | 117 + Src/ManagedVwWindow/AssemblyInfo.cs | 4 +- Src/ManagedVwWindow/ManagedVwWindow.cs | 1 + Src/ManagedVwWindow/ManagedVwWindow.csproj | 126 +- .../ManagedVwWindowTests.cs | 8 +- .../ManagedVwWindowTests.csproj | 149 +- Src/MigrateSqlDbs/AGENTS.md | 178 + Src/MigrateSqlDbs/MigrateSqlDbs.csproj | 220 +- Src/MigrateSqlDbs/Properties/AssemblyInfo.cs | 6 +- Src/Paratext8Plugin/AGENTS.md | 197 + .../ParaText8PluginTests/App.config | 41 +- .../Paratext8PluginTests.csproj | 134 +- .../ParatextDataIntegrationTests.cs | 4 +- Src/Paratext8Plugin/Paratext8Plugin.csproj | 102 +- .../Properties/AssemblyInfo.cs | 8 +- Src/ParatextImport/AGENTS.md | 127 + Src/ParatextImport/ParatextImport.csproj | 270 +- .../ParatextImportTests/AutoMergeTests.cs | 292 +- .../ParatextImportTests/BookMergerTests.cs | 6612 +++--- .../BookMergerTestsBase.cs | 2 +- .../ParatextImportTests/ClusterTests.cs | 144 +- .../ParatextImportTests/DiffTestHelper.cs | 269 +- .../ParatextImportTests/DifferenceTests.cs | 56 +- .../ImportTests/ImportStyleProxyTests.cs | 83 +- .../ParatextImportBtInterleaved.cs | 955 +- .../ParatextImportBtNonInterleaved.cs | 147 +- .../ImportTests/ParatextImportManagerTests.cs | 354 +- .../ParatextImportParatext6Tests.cs | 824 +- .../ImportTests/ParatextImportTests.cs | 1247 +- .../ImportTests/ParatextImportTestsBase.cs | 44 +- .../ParatextImportTests/ImportedBooksTests.cs | 16 +- .../ParatextImportTests.csproj | 305 +- .../ParatextImportTests/SCTextEnumTests.cs | 1222 +- .../SegmentedBtMergeTests.cs | 232 +- .../ParatextImportTests/VerseIteratorTests.cs | 30 +- Src/ParatextImport/Properties/AssemblyInfo.cs | 4 +- Src/ParatextImport/SCScriptureText.cs | 13 - Src/ParatextImport/SCTextEnum.cs | 21 + Src/ProjectUnpacker/AGENTS.md | 123 + Src/ProjectUnpacker/AssemblyInfo.cs | 2 +- Src/ProjectUnpacker/ProjectUnpacker.csproj | 218 +- Src/Transforms/AGENTS.md | 185 + Src/UnicodeCharEditor/AGENTS.md | 124 + Src/UnicodeCharEditor/App.config | 13 +- Src/UnicodeCharEditor/BuildInclude.targets | 2 +- Src/UnicodeCharEditor/CustomCharDlg.cs | 2 +- .../Properties/AssemblyInfo.cs | 4 +- .../UnicodeCharEditor.csproj | 237 +- .../PUAInstallerTests.cs | 273 +- .../UnicodeCharEditorTests.csproj | 183 +- Src/Utilities/AGENTS.md | 49 + .../ComManifestTestHost/BuildInclude.targets | 9 + .../ComManifestTestHost.csproj | 32 + Src/Utilities/ComManifestTestHost/Program.cs | 64 + Src/Utilities/FixFwData/AGENTS.md | 116 + Src/Utilities/FixFwData/FixFwData.csproj | 141 +- .../FixFwData/Properties/AssemblyInfo.cs | 6 +- Src/Utilities/FixFwDataDll/AGENTS.md | 186 + Src/Utilities/FixFwDataDll/ErrorFixer.cs | 10 +- .../FixFwDataDll/FixFwDataDll.csproj | 197 +- ...signer.cs => FixFwDataStrings.Designer.cs} | 98 +- .../{Strings.resx => FixFwDataStrings.resx} | 30 + Src/Utilities/FixFwDataDll/FwData.cs | 8 +- .../FixFwDataDll/Properties/AssemblyInfo.cs | 4 +- .../FixFwDataDll/WriteAllObjectsUtility.cs | 8 +- Src/Utilities/MessageBoxExLib/AGENTS.md | 131 + Src/Utilities/MessageBoxExLib/AssemblyInfo.cs | 2 +- .../MessageBoxExLib/MessageBoxExForm.cs | 2 +- .../MessageBoxExLib/MessageBoxExLib.csproj | 199 +- .../MessageBoxExLibTests/AssemblyInfo.cs | 2 + .../MessageBoxExLibTests.csproj | 213 +- .../MessageBoxExLibTests/Tests.cs | 32 +- Src/Utilities/Reporting/AGENTS.md | 102 + Src/Utilities/Reporting/ErrorReport.cs | 6 + .../{ErrorReport.resx => ErrorReporter.resx} | 0 Src/Utilities/Reporting/Reporting.csproj | 223 +- Src/Utilities/SfmStats/AGENTS.md | 91 + .../SfmStats/Properties/AssemblyInfo.cs | 8 +- Src/Utilities/SfmStats/SfmStats.csproj | 133 +- Src/Utilities/SfmToXml/AGENTS.md | 105 + Src/Utilities/SfmToXml/AssemblyInfo.cs | 6 +- .../SfmToXml/ConvertSFM/AssemblyInfo.cs | 58 + .../SfmToXml/ConvertSFM/ConvertSFM.csproj | 195 +- Src/Utilities/SfmToXml/Converter.cs | 9 +- Src/Utilities/SfmToXml/Sfm2Xml.csproj | 232 +- .../Sfm2XmlTests/Properties/AssemblyInfo.cs | 7 +- .../SfmToXml/Sfm2XmlTests/Sfm2XmlTests.csproj | 101 +- .../SfmToXml/XSLTTester/AssemblyInfo.cs | 58 + Src/Utilities/XMLUtils/AGENTS.md | 110 + Src/Utilities/XMLUtils/AssemblyInfo.cs | 6 +- Src/Utilities/XMLUtils/XMLUtils.csproj | 187 +- .../XMLUtils/XMLUtilsTests/AssemblyInfo.cs | 4 + .../XMLUtilsTests/XMLUtilsTests.csproj | 210 +- .../XMLUtils/XMLUtilsTests/XmlUtilsTest.cs | 4 +- Src/Utilities/XMLUtils/XmlUtils.cs | 223 +- Src/XCore/AGENTS.md | 135 + Src/XCore/AssemblyInfo.cs | 4 +- .../CommandBarLibrary/AssemblyInfo.cs | 8 + .../SidebarLibrary/AssemblyInfo.cs | 8 + Src/XCore/FlexUIAdapter/AGENTS.md | 98 + Src/XCore/FlexUIAdapter/AssemblyInfo.cs | 2 +- Src/XCore/FlexUIAdapter/FlexUIAdapter.csproj | 271 +- Src/XCore/FlexUIAdapter/PaneBar.cs | 2 +- Src/XCore/IconHolder.cs | 2 +- Src/XCore/MultiPane.cs | 2 +- Src/XCore/PaneBarContainer.cs | 1 - Src/XCore/SilSidePane/AGENTS.md | 101 + .../SilSidePane/Properties/AssemblyInfo.cs | 4 +- Src/XCore/SilSidePane/SilSidePane.csproj | 245 +- .../SilSidePaneTests/AssemblyInfo.cs | 14 + .../NavPaneOptionsDlgTests.cs | 35 +- .../SilSidePaneTests/OutlookBarButtonTests.cs | 10 +- .../SilSidePaneTests/SidePaneTests.cs | 58 +- .../SilSidePaneTests/SilSidePaneTests.csproj | 195 +- Src/XCore/SilSidePaneAdapter/AssemblyInfo.cs | 58 + Src/XCore/XCoreSample/AssemblyInfo.cs | 77 + Src/XCore/xCore.csproj | 355 +- Src/XCore/xCoreInterfaces/AGENTS.md | 123 + Src/XCore/xCoreInterfaces/AssemblyInfo.cs | 4 +- Src/XCore/xCoreInterfaces/Mediator.cs | 7 - .../xCoreInterfaces/xCoreInterfaces.csproj | 278 +- .../Properties/AssemblyInfo.cs | 7 +- .../PropertyTableTests.cs | 521 +- .../TestMessageSequencer.cs | 36 +- .../xCoreInterfacesTests.csproj | 184 +- .../xCoreOpenSourceAdapter}/AssemblyInfo.cs | 6 +- Src/XCore/xCoreTests/AGENTS.md | 92 + Src/XCore/xCoreTests/AssemblyInfo.cs | 58 + Src/XCore/xCoreTests/IncludeXmlTests.cs | 34 +- Src/XCore/xCoreTests/InventoryTests.cs | 108 +- Src/XCore/xCoreTests/xCoreTests.csproj | 274 +- Src/XCore/xWindow.cs | 2 +- Src/bldinc.h | 6 +- Src/views/AGENTS.md | 195 + Src/views/Test/TestNotifier.h | 138 +- Src/views/Test/TestViews.vcxproj | 462 +- Src/views/Test/TestVwRootBox.h | 12 +- Src/views/Test/TestVwSelection.h | 5 +- Src/views/Test/debug_main.cpp | 110 + Src/views/Test/early_log.cpp | 18 + Src/views/Test/testViews.cpp | 16 +- Src/views/Test/testViews.mak | 14 +- Src/views/Views.mak | 6 +- Src/views/Views.rc | 3 +- Src/views/VwRootBox.cpp | 73 +- Src/views/VwSelection.cpp | 11 +- .../lib/VwGraphicsReplayer/AssemblyInfo.cs | 2 +- .../VwGraphicsReplayer/VwGraphicsReplayer.cs | 4 +- .../VwGraphicsReplayer.csproj | 88 +- Src/views/views.vcxproj | 100 +- Src/views/views2008.vcproj | 382 - Src/xWorks/AGENTS.md | 168 + Src/xWorks/AddCustomFieldDlg.cs | 4 +- Src/xWorks/AssemblyInfo.cs | 6 +- Src/xWorks/ConfiguredLcmGenerator.cs | 2 +- Src/xWorks/CssGenerator.cs | 12 +- Src/xWorks/DTMenuHandler.cs | 2 - Src/xWorks/DataTreeImages.cs | 2 +- Src/xWorks/DictionaryConfigManager.cs | 10 +- Src/xWorks/DictionaryConfigurationDlg.cs | 26 +- Src/xWorks/DictionaryConfigurationMigrator.cs | 2 - .../FirstBetaMigrator.cs | 2 +- .../DetailsView.Designer.cs | 1 - .../GroupingOptionsView.cs | 1 - .../ListOptionsView.Designer.cs | 40 +- Src/xWorks/GeneratedHtmlViewer.cs | 2 +- Src/xWorks/ImageHolder.cs | 2 +- Src/xWorks/LcmJsonGenerator.cs | 1 - Src/xWorks/MacroListener.cs | 4 +- Src/xWorks/NotebookExportDialog.cs | 3 - Src/xWorks/RecordBrowseView.cs | 2 + Src/xWorks/RecordClerk.cs | 12 +- Src/xWorks/RecordClerkImages.cs | 2 +- Src/xWorks/RecordDocView.cs | 5 +- Src/xWorks/RecordEditView.cs | 4 +- Src/xWorks/SemanticDomainRdeTreeBarHandler.cs | 1 - Src/xWorks/UploadToWebonaryController.cs | 56 +- Src/xWorks/WebonaryLogViewer.cs | 2 +- Src/xWorks/XWorksViewBase.cs | 243 +- Src/xWorks/xWorks.csproj | 832 +- .../AllReversalEntriesRecordListTests.cs | 3 +- Src/xWorks/xWorksTests/ArchivingTests.cs | 2 +- Src/xWorks/xWorksTests/BulkEditBarTests.cs | 381 +- .../ConfigurableDictionaryNodeTests.cs | 100 +- .../ConfiguredLcmGeneratorTests.cs | 8 +- .../ConfiguredXHTMLGeneratorReversalTests.cs | 2 +- .../ConfiguredXHTMLGeneratorTests.cs | 208 +- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 211 +- Src/xWorks/xWorksTests/CustomListDlgTests.cs | 73 +- .../xWorksTests/DeleteCustomListTests.cs | 63 +- .../DictionaryConfigManagerTests.cs | 171 +- .../DictionaryConfigurationControllerTests.cs | 425 +- ...onaryConfigurationImportControllerTests.cs | 64 +- .../DictionaryConfigurationListenerTests.cs | 10 +- ...naryConfigurationManagerControllerTests.cs | 79 +- .../DictionaryConfigurationMigratorTests.cs | 48 +- .../FirstAlphaMigratorTests.cs | 228 +- .../FirstBetaMigratorTests.cs | 180 +- .../PreHistoricMigratorTests.cs | 395 +- .../DictionaryConfigurationModelTests.cs | 158 +- .../DictionaryConfigurationUtilsTests.cs | 11 +- .../DictionaryDetailsControllerTests.cs | 228 +- .../DictionaryExportServiceTests.cs | 63 +- .../xWorksTests/DictionaryNodeOptionsTests.cs | 54 +- .../DictionaryPublicationDecoratorTests.cs | 26 +- Src/xWorks/xWorksTests/ExportDialogTests.cs | 1414 +- .../HeadwordNumbersControllerTests.cs | 42 +- .../xWorksTests/InterestingTextsTests.cs | 76 +- .../xWorksTests/LcmJsonGeneratorTests.cs | 48 +- .../xWorksTests/LcmWordGeneratorTests.cs | 46 +- .../xWorksTests/ReversalIndexServicesTests.cs | 13 +- .../xWorksTests/TreeBarHandlerUtilsTests.cs | 16 +- .../UploadToWebonaryControllerTests.cs | 99 +- Src/xWorks/xWorksTests/XWorksAppTestBase.cs | 59 +- Src/xWorks/xWorksTests/XhtmlDocViewTests.cs | 28 +- Src/xWorks/xWorksTests/xWorksTests.csproj | 396 +- Test.runsettings | 80 + agent-build-fw.sh | 69 - build.ps1 | 547 + contracts/test-exclusion-api.yaml | 193 + environ | 146 - environ-other | 9 - environ-xulrunner | 22 - fw.code-workspace | 8 - generate_version.proj | 7 + nuget.config | 40 + openspec/AGENTS.md | 327 + openspec/MAKE_SPECS.md | 644 + .../.openspec.yaml | 2 + .../detail-controls-testability/design.md | 77 + .../detail-controls-class-diagram.mmd | 150 + .../detail-controls-testability/proposal.md | 39 + .../ui-framework/winforms-patterns/spec.md | 10 + .../detail-controls-architecture/spec.md | 38 + .../specs/detail-controls-testability/spec.md | 69 + .../detail-controls-testability/tasks.md | 40 + openspec/config.yaml | 65 + openspec/specs/README.md | 96 + .../architecture/build-deploy/build-phases.md | 99 + .../architecture/build-deploy/installer.md | 68 + .../architecture/build-deploy/localization.md | 40 + .../architecture/data-access/lcm-patterns.md | 41 + .../architecture/data-access/undo-redo.md | 39 + .../architecture/interop/com-contracts.md | 46 + .../architecture/interop/external-apis.md | 42 + .../architecture/interop/native-boundary.md | 43 + .../architecture/layers/dependency-graph.md | 41 + .../specs/architecture/layers/entry-points.md | 41 + .../specs/architecture/layers/layer-model.md | 42 + .../specs/architecture/testing/fixtures.md | 38 + .../architecture/testing/test-strategy.md | 40 + .../ui-framework/views-rendering.md | 44 + .../ui-framework/winforms-patterns.md | 43 + .../ui-framework/xcore-mediator.md | 41 + openspec/specs/configuration/lists.md | 43 + openspec/specs/configuration/projects.md | 44 + .../specs/configuration/writing-systems.md | 49 + openspec/specs/grammar/morphology/affixes.md | 43 + .../specs/grammar/morphology/allomorphs.md | 43 + .../specs/grammar/morphology/categories.md | 43 + .../specs/grammar/parsing/configuration.md | 49 + openspec/specs/grammar/parsing/rules.md | 43 + .../specs/grammar/parsing/troubleshooting.md | 43 + openspec/specs/grammar/sketch/generation.md | 43 + .../integration/collaboration/multi-user.md | 43 + .../integration/collaboration/send-receive.md | 43 + .../specs/integration/external/encoding.md | 44 + .../specs/integration/external/flextools.md | 44 + .../specs/integration/external/paratext.md | 51 + openspec/specs/lexicon/entries/creation.md | 43 + openspec/specs/lexicon/entries/relations.md | 44 + openspec/specs/lexicon/entries/structure.md | 50 + openspec/specs/lexicon/export/dictionary.md | 43 + openspec/specs/lexicon/export/lift.md | 43 + openspec/specs/lexicon/export/pathway.md | 43 + openspec/specs/lexicon/import/lift.md | 43 + openspec/specs/lexicon/import/sfm.md | 43 + openspec/specs/texts/analysis/concordance.md | 43 + openspec/specs/texts/analysis/discourse.md | 43 + openspec/specs/texts/analysis/tagging.md | 43 + openspec/specs/texts/export/formats.md | 43 + .../specs/texts/interlinear/annotation.md | 49 + openspec/specs/texts/interlinear/baseline.md | 43 + openspec/specs/texts/interlinear/import.md | 43 + preprocessed.xml | 17873 ++++++++++++++++ regen_midl.cmd | 32 + scripts/Agent/Collect-InstallerSnapshot.ps1 | 230 + .../Agent/Compare-InstallerEvidenceRuns.ps1 | 70 + scripts/Agent/Compare-InstallerSnapshots.ps1 | 180 + .../Agent/Compare-SpecInstallerEvidence.ps1 | 64 + scripts/Agent/Copy-LocalLcm.ps1 | 184 + scripts/Agent/Git-Search.ps1 | 237 + .../Guest/InstallerParityEvidence.Common.ps1 | 100 + .../Invoke-InstallerParityEvidence-Wix3.ps1 | 24 + .../Invoke-InstallerParityEvidence-Wix6.ps1 | 24 + .../Guest/Invoke-InstallerParityEvidence.ps1 | 34 + .../Invoke-InstallerUninstallEvidence.ps1 | 167 + .../Guest/Invoke-ManualInstallerEvidence.ps1 | 120 + scripts/Agent/Invoke-Installer.ps1 | 335 + scripts/Agent/Invoke-InstallerCheck.ps1 | 119 + scripts/Agent/Jira-Beads-FromSelection.ps1 | 58 + scripts/Agent/New-Wix6ParityFixPlan.ps1 | 125 + scripts/Agent/README.md | 28 + scripts/Agent/Read-FileContent.ps1 | 136 + scripts/Agent/Remove-FieldWorksAll.ps1 | 378 + scripts/Agent/Setup-Local-Localization.ps1 | 85 + scripts/GenerateAssemblyInfo/__init__.py | 9 + .../assembly_info_parser.py | 63 + .../audit_generate_assembly_info.py | 60 + scripts/GenerateAssemblyInfo/cli_args.py | 78 + .../convert_generate_assembly_info.py | 511 + scripts/GenerateAssemblyInfo/git_metadata.py | 108 + scripts/GenerateAssemblyInfo/git_restore.py | 70 + scripts/GenerateAssemblyInfo/history_diff.py | 92 + scripts/GenerateAssemblyInfo/models.py | 114 + .../GenerateAssemblyInfo/project_scanner.py | 216 + .../reflect_attributes.ps1 | 93 + scripts/GenerateAssemblyInfo/reporting.py | 96 + .../validate_generate_assembly_info.py | 310 + .../verify-performance-and-tests.ps1 | 58 + scripts/Installer/Invoke-InstallerWithLog.ps1 | 63 + scripts/Rename-WorktreeToBranch.ps1 | 194 + scripts/Setup-WorktreeBeads.ps1 | 129 + scripts/Setup-WorktreeColor.ps1 | 634 + scripts/Worktree-CreateFromBranch.ps1 | 432 + scripts/analyze_audit.py | 52 + scripts/audit_ignored_tests.py | 280 + scripts/audit_test_exclusions.py | 82 + scripts/convert_test_exclusions.py | 124 + scripts/enforce_x64_platform.py | 249 + scripts/git-utilities.ps1 | 173 + scripts/openspec/Propose-RefFixes.ps1 | 627 + scripts/openspec/Report-RefCoverage.ps1 | 170 + scripts/openspec/Sync-AgentsAnchors.ps1 | 236 + scripts/openspec/Validate-OpenSpecRefs.ps1 | 496 + scripts/regfree/FieldWorks.regfree.manifest | 36 + scripts/regfree/README.md | 27 + scripts/regfree/__init__.py | 1 + scripts/regfree/audit_com_usage.py | 270 + scripts/regfree/com_guids.json | 604 + scripts/regfree/common.py | 162 + scripts/regfree/extract_clsids.py | 165 + scripts/regfree/extract_com_guids.py | 203 + scripts/regfree/generate_app_manifests.py | 69 + scripts/regfree/generate_manifest.py | 76 + scripts/regfree/project_map.json | 68 + scripts/regfree/run-in-vm.ps1 | 116 + scripts/templates/settings.example.json | 10 + scripts/templates/tasks.template.json | 93 + scripts/test_exclusions/README.md | 28 + scripts/test_exclusions/__init__.py | 18 + scripts/test_exclusions/assembly_guard.ps1 | 77 + scripts/test_exclusions/converter.py | 108 + scripts/test_exclusions/escalation_writer.py | 77 + scripts/test_exclusions/models.py | 178 + scripts/test_exclusions/msbuild_parser.py | 138 + scripts/test_exclusions/repo_scanner.py | 200 + scripts/test_exclusions/report_writer.py | 64 + scripts/test_exclusions/validator.py | 87 + scripts/tests/__init__.py | 1 + scripts/tests/compare_ignoring_format.py | 164 + scripts/tests/conftest.py | 54 + scripts/tests/convert_nunit.py | 139 + scripts/tests/convert_rhinomock_to_moq.py | 166 + scripts/tests/fixtures/audit/README.md | 13 + .../audit/Src/Explicit/Explicit.csproj | 9 + .../Src/Explicit/ExplicitTests/FooTest.cs | 3 + .../fixtures/audit/Src/Missing/Missing.csproj | 5 + .../audit/Src/Missing/MissingTests/BazTest.cs | 3 + .../audit/Src/Wildcard/Helpers/HelperTests.cs | 3 + .../audit/Src/Wildcard/Wildcard.csproj | 9 + .../Src/Wildcard/WildcardTests/BarTest.cs | 3 + scripts/tests/nunit_converters.py | 455 + scripts/tests/nunit_fixers.py | 337 + scripts/tests/nunit_parsing.py | 261 + scripts/tests/test_exclusions/__init__.py | 1 + .../test_exclusions/test_assembly_guard.py | 68 + .../test_exclusions/test_audit_command.py | 95 + .../tests/test_exclusions/test_converter.py | 171 + .../test_models_and_scanner.py | 131 + .../test_exclusions/test_validator_command.py | 121 + scripts/tools/parse_msbuild_warnings.py | 97 + .../tools/remove_duplicate_assemblyinfo.py | 105 + scripts/tools/sync_copilot_instructions.py | 87 + scripts/toolshims/README.md | 47 + scripts/toolshims/environ.cmd | 6 + scripts/toolshims/py.cmd | 19 + scripts/toolshims/py.ps1 | 74 + scripts/validate_test_exclusions.py | 101 + test.ps1 | 578 + tests/Integration/RegFreeCom/README.md | 28 + .../RegFreeCom/test_audit_com_usage.py | 166 + vagrant/.gitignore | 1 - vagrant/Vagrantfile | 37 - 1422 files changed, 131182 insertions(+), 61103 deletions(-) delete mode 100644 .GitHub/copilot-setup-steps.yml create mode 100644 .github/AGENTS.md create mode 100644 .github/AI_GOVERNANCE.md create mode 100644 .github/BUILD_REQUIREMENTS.md create mode 100644 .github/agents/WinFormsExpert.agent.md create mode 100644 .github/agents/debug.agent.md create mode 100644 .github/agents/devils-advocate.agent.md create mode 100644 .github/agents/fieldworks.avalonia-expert.agent.md create mode 100644 .github/agents/fieldworks.cpp-expert.agent.md create mode 100644 .github/agents/fieldworks.csharp-expert.agent.md create mode 100644 .github/agents/fieldworks.winforms-expert.agent.md create mode 100644 .github/chatmodes/coding-agent.chatmode.md create mode 100644 .github/chatmodes/installer-engineer.chatmode.md create mode 100644 .github/chatmodes/managed-engineer.chatmode.md create mode 100644 .github/chatmodes/native-engineer.chatmode.md create mode 100644 .github/chatmodes/technical-writer.chatmode.md create mode 100644 .github/check_copilot_docs.py create mode 100644 .github/commit-guidelines.md create mode 100644 .github/context/codebase.context.md create mode 100644 .github/copilot-framework-tasks.md create mode 100644 .github/copilot_apply_updates.py create mode 100644 .github/copilot_cache.py create mode 100644 .github/copilot_change_utils.py create mode 100644 .github/copilot_doc_utils.py create mode 100644 .github/copilot_tree_hash.py create mode 100644 .github/dependabot.yml create mode 100644 .github/detect_copilot_needed.py create mode 100644 .github/instructions/build.instructions.md create mode 100644 .github/instructions/debugging.instructions.md create mode 100644 .github/instructions/dotnet-architecture-good-practices.instructions.md create mode 100644 .github/instructions/dotnet-framework.instructions.md create mode 100644 .github/instructions/dotnet-upgrade.instructions.md create mode 100644 .github/instructions/installer.instructions.md create mode 100644 .github/instructions/managed.instructions.md create mode 100644 .github/instructions/native.instructions.md create mode 100644 .github/instructions/repo.instructions.md create mode 100644 .github/instructions/security.instructions.md create mode 100644 .github/instructions/terminal.instructions.md create mode 100644 .github/instructions/testing.instructions.md create mode 100644 .github/memory.md create mode 100644 .github/migrate_copilot_format.py create mode 100644 .github/option3-plan.md create mode 100644 .github/plan_copilot_updates.py create mode 100644 .github/prompts/bugfix.prompt.md create mode 100644 .github/prompts/copilot-folder-review.prompt.md create mode 100644 .github/prompts/dotnet-best-practices.prompt.md create mode 100644 .github/prompts/dotnet-design-pattern-review.prompt.md create mode 100644 .github/prompts/dotnet-upgrade.prompt.md create mode 100644 .github/prompts/feature-spec.prompt.md create mode 100644 .github/prompts/opsx-apply.prompt.md create mode 100644 .github/prompts/opsx-archive.prompt.md create mode 100644 .github/prompts/opsx-bulk-archive.prompt.md create mode 100644 .github/prompts/opsx-continue.prompt.md create mode 100644 .github/prompts/opsx-explore.prompt.md create mode 100644 .github/prompts/opsx-ff.prompt.md create mode 100644 .github/prompts/opsx-new.prompt.md create mode 100644 .github/prompts/opsx-onboard.prompt.md create mode 100644 .github/prompts/opsx-sync.prompt.md create mode 100644 .github/prompts/opsx-verify.prompt.md create mode 100644 .github/prompts/test-failure-debug.prompt.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/recipes/add-dialog-xworks.md create mode 100644 .github/recipes/extend-cellar-schema.md create mode 100644 .github/scaffold_copilot_markdown.py create mode 100644 .github/skills/atlassian-readonly-skills/.env.example create mode 100644 .github/skills/atlassian-readonly-skills/REFERENCE.md create mode 100644 .github/skills/atlassian-readonly-skills/SKILL.md create mode 100644 .github/skills/atlassian-readonly-skills/requirements.txt create mode 100644 .github/skills/atlassian-readonly-skills/scripts/__init__.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/_common.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/bitbucket_commits.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/bitbucket_files.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/bitbucket_projects.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/bitbucket_pull_requests.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/confluence_comments.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/confluence_labels.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/confluence_pages.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/confluence_search.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/jira_agile.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/jira_issues.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/jira_links.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/jira_projects.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/jira_search.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/jira_users.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/jira_workflow.py create mode 100644 .github/skills/atlassian-readonly-skills/scripts/jira_worklog.py create mode 100644 .github/skills/atlassian-skills/.env.example create mode 100644 .github/skills/atlassian-skills/REFERENCE.md create mode 100644 .github/skills/atlassian-skills/SKILL.md create mode 100644 .github/skills/atlassian-skills/requirements.txt create mode 100644 .github/skills/atlassian-skills/scripts/__init__.py create mode 100644 .github/skills/atlassian-skills/scripts/_common.py create mode 100644 .github/skills/atlassian-skills/scripts/bitbucket_commits.py create mode 100644 .github/skills/atlassian-skills/scripts/bitbucket_files.py create mode 100644 .github/skills/atlassian-skills/scripts/bitbucket_projects.py create mode 100644 .github/skills/atlassian-skills/scripts/bitbucket_pull_requests.py create mode 100644 .github/skills/atlassian-skills/scripts/confluence_comments.py create mode 100644 .github/skills/atlassian-skills/scripts/confluence_labels.py create mode 100644 .github/skills/atlassian-skills/scripts/confluence_pages.py create mode 100644 .github/skills/atlassian-skills/scripts/confluence_search.py create mode 100644 .github/skills/atlassian-skills/scripts/jira_agile.py create mode 100644 .github/skills/atlassian-skills/scripts/jira_issues.py create mode 100644 .github/skills/atlassian-skills/scripts/jira_links.py create mode 100644 .github/skills/atlassian-skills/scripts/jira_projects.py create mode 100644 .github/skills/atlassian-skills/scripts/jira_search.py create mode 100644 .github/skills/atlassian-skills/scripts/jira_users.py create mode 100644 .github/skills/atlassian-skills/scripts/jira_workflow.py create mode 100644 .github/skills/atlassian-skills/scripts/jira_worklog.py create mode 100644 .github/skills/beads/CLAUDE.md create mode 100644 .github/skills/beads/README.md create mode 100644 .github/skills/beads/SKILL.md create mode 100644 .github/skills/beads/adr/0001-bd-prime-as-source-of-truth.md create mode 100644 .github/skills/beads/resources/AGENTS.md create mode 100644 .github/skills/beads/resources/ASYNC_GATES.md create mode 100644 .github/skills/beads/resources/BOUNDARIES.md create mode 100644 .github/skills/beads/resources/CHEMISTRY_PATTERNS.md create mode 100644 .github/skills/beads/resources/CLI_REFERENCE.md create mode 100644 .github/skills/beads/resources/DEPENDENCIES.md create mode 100644 .github/skills/beads/resources/INTEGRATION_PATTERNS.md create mode 100644 .github/skills/beads/resources/ISSUE_CREATION.md create mode 100644 .github/skills/beads/resources/MOLECULES.md create mode 100644 .github/skills/beads/resources/PATTERNS.md create mode 100644 .github/skills/beads/resources/RESUMABILITY.md create mode 100644 .github/skills/beads/resources/STATIC_DATA.md create mode 100644 .github/skills/beads/resources/TROUBLESHOOTING.md create mode 100644 .github/skills/beads/resources/WORKFLOWS.md create mode 100644 .github/skills/beads/resources/WORKTREES.md create mode 100644 .github/skills/execute-implement/SKILL.md create mode 100644 .github/skills/jira-to-beads/SKILL.md create mode 100644 .github/skills/jira-to-beads/scripts/create_beads_from_jira.py create mode 100644 .github/skills/jira-to-beads/scripts/export_jira_assigned.py create mode 100644 .github/skills/openspec-apply-change/SKILL.md create mode 100644 .github/skills/openspec-archive-change/SKILL.md create mode 100644 .github/skills/openspec-beads-import/SKILL.md create mode 100644 .github/skills/openspec-bulk-archive-change/SKILL.md create mode 100644 .github/skills/openspec-continue-change/SKILL.md create mode 100644 .github/skills/openspec-explore/SKILL.md create mode 100644 .github/skills/openspec-ff-change/SKILL.md create mode 100644 .github/skills/openspec-new-change/SKILL.md create mode 100644 .github/skills/openspec-onboard/SKILL.md create mode 100644 .github/skills/openspec-sync-specs/SKILL.md create mode 100644 .github/skills/openspec-verify-change/SKILL.md create mode 100644 .github/skills/plan-design/SKILL.md create mode 100644 .github/skills/powershell/SKILL.md create mode 100644 .github/skills/review/SKILL.md create mode 100644 .github/skills/session-workflow/SKILL.md create mode 100644 .github/skills/verify-test/SKILL.md create mode 100644 .github/src-catalog.md create mode 100644 .github/templates/organizational-copilot.template.md create mode 100644 .github/workflows/copilot-docs-detect.yml create mode 100644 .github/workflows/copilot-setup-steps.yml create mode 100644 .github/workflows/link-check.yml create mode 100644 .github/workflows/lint-docs.yml create mode 100644 .github/workflows/openspec-validate.yml create mode 100644 .serena/.gitignore create mode 100644 .serena/memories/architecture.md create mode 100644 .serena/memories/common_issues.md create mode 100644 .serena/memories/project_overview.md create mode 100644 .serena/memories/style_and_conventions.md create mode 100644 .serena/memories/suggested_commands.md create mode 100644 .serena/memories/task_completion.md create mode 100644 .serena/project.yml create mode 100644 .vscode/context7-configuration.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/mcp.json create mode 100644 .vscode/settings.json create mode 100644 AGENTS.md delete mode 100755 Bin/Mktstw.bat delete mode 100755 Bin/RemakeFw.bat delete mode 100644 Bin/Rhino/Rhino.Mocks.dll delete mode 100755 Bin/_EnsureRoot.bat delete mode 100755 Bin/mkGenLib-tst.bat delete mode 100755 Bin/mkGenLib.bat delete mode 100755 Bin/mkaft.bat delete mode 100755 Bin/mkall-tst.bat create mode 100644 Bin/mkdir-wrapper.cmd delete mode 100755 Bin/mkdp.bat delete mode 100755 Bin/mkecob.bat delete mode 100755 Bin/mkfwk-tst.bat delete mode 100755 Bin/mkfwk.bat delete mode 100755 Bin/mkgrc.bat delete mode 100755 Bin/mkgre.bat delete mode 100755 Bin/mkhv.bat delete mode 100755 Bin/mkhw.bat delete mode 100755 Bin/mkhwt.bat delete mode 100755 Bin/mkhwv.bat delete mode 100755 Bin/mkhwx.bat delete mode 100755 Bin/mklg-tst.bat delete mode 100755 Bin/mklg.bat delete mode 100755 Bin/mklgt.bat delete mode 100755 Bin/mktlbs.bat delete mode 100755 Bin/mktsth.bat delete mode 100755 Bin/mktv.bat delete mode 100755 Bin/mkvw-tst.bat delete mode 100755 Bin/mkvw.bat delete mode 100644 Bin/nmock/NMock.dll delete mode 100644 Bin/nmock/NMock.pdb delete mode 100644 Bin/nmock/src/LICENSE.txt delete mode 100644 Bin/nmock/src/README.txt delete mode 100755 Bin/nmock/src/build.bat delete mode 100755 Bin/nmock/src/ccnet/ccnet-nmock.bat delete mode 100644 Bin/nmock/src/ccnet/ccnet.config delete mode 100644 Bin/nmock/src/continuousintegration.build delete mode 100755 Bin/nmock/src/lib/nunit-console.exe delete mode 100644 Bin/nmock/src/lib/nunit-console.exe.config delete mode 100644 Bin/nmock/src/lib/nunit.framework.dll delete mode 100644 Bin/nmock/src/lib/nunit.util.dll delete mode 100644 Bin/nmock/src/nmock.build delete mode 100644 Bin/nmock/src/sample/build.build delete mode 100644 Bin/nmock/src/sample/order/Notifier.cs delete mode 100644 Bin/nmock/src/sample/order/Order.cs delete mode 100644 Bin/nmock/src/sample/order/OrderProcessor.cs delete mode 100644 Bin/nmock/src/sample/order/OrderProcessorTest.cs delete mode 100644 Bin/nmock/src/sample/random/Weather.cs delete mode 100644 Bin/nmock/src/sample/random/WeatherTest.cs delete mode 100644 Bin/nmock/src/sample/sample.csproj delete mode 100644 Bin/nmock/src/src/NMock.csproj delete mode 100644 Bin/nmock/src/src/NMock/CallMethodOrder.cs delete mode 100644 Bin/nmock/src/src/NMock/CallMethodWithParams.cs delete mode 100644 Bin/nmock/src/src/NMock/CallMethodWithoutExpectation.cs delete mode 100644 Bin/nmock/src/src/NMock/Constraints/Constraints.cs delete mode 100644 Bin/nmock/src/src/NMock/Constraints/IConstraint.cs delete mode 100644 Bin/nmock/src/src/NMock/Constraints/IsArrayEqual.cs delete mode 100644 Bin/nmock/src/src/NMock/Dynamic/ClassGenerator.cs delete mode 100644 Bin/nmock/src/src/NMock/Dynamic/InterfaceLister.cs delete mode 100644 Bin/nmock/src/src/NMock/DynamicMock.cs delete mode 100644 Bin/nmock/src/src/NMock/IInvocationHandler.cs delete mode 100644 Bin/nmock/src/src/NMock/IMethod.cs delete mode 100644 Bin/nmock/src/src/NMock/IMock.cs delete mode 100644 Bin/nmock/src/src/NMock/IVerifiable.cs delete mode 100644 Bin/nmock/src/src/NMock/Invocation.cs delete mode 100644 Bin/nmock/src/src/NMock/Method.cs delete mode 100644 Bin/nmock/src/src/NMock/MethodSignature.cs delete mode 100644 Bin/nmock/src/src/NMock/Mock.cs delete mode 100644 Bin/nmock/src/src/NMock/MockCall.cs delete mode 100644 Bin/nmock/src/src/NMock/NMock.csproj delete mode 100644 Bin/nmock/src/src/NMock/Remoting/MockServer.cs delete mode 100644 Bin/nmock/src/src/NMock/Remoting/RemotingMock.cs delete mode 100644 Bin/nmock/src/src/NMock/SingleMethod.cs delete mode 100644 Bin/nmock/src/src/NMock/VerifyException.cs delete mode 100644 Bin/nmock/src/src/NMock/build.build delete mode 100644 Bin/nmock/src/src/src.csproj delete mode 100644 Bin/nmock/src/test/NMock/Constraints/ConstraintsTest.cs delete mode 100644 Bin/nmock/src/test/NMock/Constraints/IsArrayEqualTest.cs delete mode 100644 Bin/nmock/src/test/NMock/Dynamic/ClassGeneratorTest.cs delete mode 100644 Bin/nmock/src/test/NMock/Dynamic/InterfaceListerTest.cs delete mode 100644 Bin/nmock/src/test/NMock/DynamicMockTest.cs delete mode 100644 Bin/nmock/src/test/NMock/FastErrorHandlingTest.cs delete mode 100644 Bin/nmock/src/test/NMock/MockTest.cs delete mode 100644 Bin/nmock/src/test/NMock/NMockTests.csproj delete mode 100644 Bin/nmock/src/test/NMock/Remoting/MockServerTest.cs delete mode 100644 Bin/nmock/src/test/NMock/Remoting/RemotingMockTest.cs delete mode 100644 Bin/nmock/src/test/NMock/VerifyExceptionTest.cs delete mode 100644 Bin/nmock/src/test/NMock/build.build delete mode 100644 Bin/nmock/src/test/test.csproj delete mode 100644 Bin/nmock/src/tools/NAnt.Core.dll delete mode 100644 Bin/nmock/src/tools/NAnt.DotNetTasks.dll delete mode 100644 Bin/nmock/src/tools/NAnt.NUnit1Tasks.dll delete mode 100644 Bin/nmock/src/tools/NAnt.NUnitTasks.dll delete mode 100644 Bin/nmock/src/tools/NDoc.Core.dll delete mode 100755 Bin/nmock/src/tools/nant.exe delete mode 100644 Bin/nmock/src/tools/nunit.core.dll delete mode 100644 Bin/nmock/src/tools/nunit.framework.dll delete mode 100644 Bin/nunitforms/FormsTester.dll delete mode 100644 Bin/nunitforms/source/FormsTester/AmbiguousNameException.cs delete mode 100644 Bin/nunitforms/source/FormsTester/ControlFinder.cs delete mode 100644 Bin/nunitforms/source/FormsTester/ControlNotVisibleException.cs delete mode 100644 Bin/nunitforms/source/FormsTester/ControlTester.cs delete mode 100644 Bin/nunitforms/source/FormsTester/Finder.cs delete mode 100644 Bin/nunitforms/source/FormsTester/FormCollection.cs delete mode 100644 Bin/nunitforms/source/FormsTester/FormFinder.cs delete mode 100644 Bin/nunitforms/source/FormsTester/FormsTestAssertionException.cs delete mode 100644 Bin/nunitforms/source/FormsTester/FormsTester.csproj delete mode 100644 Bin/nunitforms/source/FormsTester/ModalFormTester.cs delete mode 100644 Bin/nunitforms/source/FormsTester/NUnitFormTest.cs delete mode 100644 Bin/nunitforms/source/FormsTester/NoSuchControlException.cs delete mode 100644 Bin/nunitforms/source/FormsTester/Properties/AssemblyInfo.cs delete mode 100644 Bin/nunitforms/source/FormsTester/Win32.cs delete mode 100644 Bin/nunitforms/source/FormsTester/readme.txt delete mode 100644 Bin/testWrapper.cmd delete mode 100644 Bin/wrapper.cmd create mode 100644 Build/Agent/FwBuildEnvironment.psm1 create mode 100644 Build/Agent/FwBuildHelpers.psm1 create mode 100644 Build/Agent/GitHelpers.ps1 create mode 100644 Build/Agent/Preprocess-WixIncludes.ps1 create mode 100644 Build/Agent/Rebuild-TestProjects.ps1 create mode 100644 Build/Agent/Remove-StaleDlls.ps1 create mode 100644 Build/Agent/Run-VsTests.ps1 create mode 100644 Build/Agent/Setup-DefenderExclusions.ps1 create mode 100644 Build/Agent/Setup-FwBuildEnv.ps1 create mode 100644 Build/Agent/Setup-InstallerBuild.ps1 create mode 100644 Build/Agent/Setup-Serena.ps1 create mode 100644 Build/Agent/Verify-FwDependencies.ps1 create mode 100644 Build/Agent/check-and-fix-whitespace.ps1 create mode 100644 Build/Agent/check-and-fix-whitespace.sh create mode 100644 Build/Agent/check-whitespace.ps1 create mode 100644 Build/Agent/check-whitespace.sh create mode 100644 Build/Agent/commit-messages.ps1 create mode 100644 Build/Agent/commit-messages.sh create mode 100644 Build/Agent/fix-whitespace.ps1 create mode 100644 Build/Agent/fix-whitespace.sh create mode 100644 Build/Agent/lib_git.sh create mode 100644 Build/Agent/validate-test-exclusions.ps1 delete mode 100644 Build/FieldWorks.proj create mode 100644 Build/Installer.Wix3.targets create mode 100644 Build/Installer.legacy.targets create mode 100644 Build/InstallerBuild.proj delete mode 100644 Build/NuGet.targets create mode 100644 Build/PackageRestore.targets create mode 100644 Build/SilVersions.props create mode 100644 Build/Src/Directory.Packages.props create mode 100644 Build/Src/FwBuildTasks/Directory.Build.props create mode 100644 Build/Src/FwBuildTasks/FwBuildTasksTests/RegFreeCreatorTests.cs delete mode 100644 Build/Src/FwBuildTasks/README.md create mode 100644 Build/Src/NativeBuild/NativeBuild.csproj delete mode 100755 Build/build delete mode 100755 Build/build-recent delete mode 100755 Build/build.bat delete mode 100644 Build/build64.bat delete mode 100755 Build/multitry delete mode 100644 Build/nuget-common/packages.config delete mode 100644 Build/nuget-windows/packages.config delete mode 100755 Build/run-in-environ create mode 100644 Build/scripts/Invoke-CppTest.ps1 create mode 100644 Directory.Build.props create mode 100644 Directory.Build.targets create mode 100644 Directory.Packages.props delete mode 100644 DistFiles/Aga.Controls.dll delete mode 100644 DistFiles/GAFAWSAnalysis.dll create mode 100644 Docs/64bit-regfree-migration.md create mode 100644 Docs/CONTRIBUTING.md create mode 100644 Docs/agent-docs-refresh.md create mode 100644 Docs/architecture/data-migrations.md create mode 100644 Docs/architecture/dependencies.md create mode 100644 Docs/copilot-instructions-plan.md create mode 100644 Docs/copilot-refresh.md create mode 100644 Docs/core-developer-setup.md create mode 100644 Docs/installer-build-guide.md create mode 100644 Docs/mcp.md create mode 100644 Docs/traversal-sdk-migration.md create mode 100644 Docs/visual-studio-setup.md create mode 100644 Docs/vscode-stability-profile.md create mode 100644 Docs/workflows/pull-request-workflow.md create mode 100644 Docs/workflows/release-process.md create mode 100644 EVIDENCE_CRITERA.md create mode 100644 FLExInstaller/AGENTS.md create mode 100644 FLExInstaller/Directory.Packages.props create mode 100644 FLExInstaller/wix6/AGENTS.md create mode 100644 FLExInstaller/wix6/CustomActionSteps.wxi create mode 100644 FLExInstaller/wix6/CustomComponents.wxi create mode 100644 FLExInstaller/wix6/CustomFeatures.wxi create mode 100644 FLExInstaller/wix6/FieldWorks.Bundle.wixproj create mode 100644 FLExInstaller/wix6/FieldWorks.Installer.wixproj create mode 100644 FLExInstaller/wix6/FieldWorks.OfflineBundle.wixproj create mode 100644 FLExInstaller/wix6/Fonts.wxi create mode 100644 FLExInstaller/wix6/Overrides.wxi create mode 100644 FLExInstaller/wix6/Redistributables.wxi create mode 100644 FLExInstaller/wix6/Shared/Base/Bundle.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/BundleTheme.wxl create mode 100644 FLExInstaller/wix6/Shared/Base/BundleTheme.xml create mode 100644 FLExInstaller/wix6/Shared/Base/BundleThemeTemplate.wxi create mode 100644 FLExInstaller/wix6/Shared/Base/CustomAction.config create mode 100644 FLExInstaller/wix6/Shared/Base/Framework.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/GICustomizeDlg.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/GIInstallDirDlg.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/GIProgressDlg.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/GISetupTypeDlg.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/GIWelcomeDlg.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/KeyPathFix.xsl create mode 100644 FLExInstaller/wix6/Shared/Base/OfflineBundle.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/TrueType.xsl create mode 100644 FLExInstaller/wix6/Shared/Base/WixUI_DialogFlow.wxs create mode 100644 FLExInstaller/wix6/Shared/Base/WixUI_en-us.wxl create mode 100644 FLExInstaller/wix6/Shared/Base/buildBaseInstaller.bat create mode 100644 FLExInstaller/wix6/Shared/Base/buildExe.bat create mode 100644 FLExInstaller/wix6/Shared/Base/buildMsi.bat create mode 100644 FLExInstaller/wix6/Shared/Base/setVars.bat create mode 100644 FLExInstaller/wix6/Shared/Base/signingProxy.bat create mode 100644 FLExInstaller/wix6/Shared/Common/CustomActionSteps.wxi create mode 100644 FLExInstaller/wix6/Shared/Common/CustomComponents.wxi create mode 100644 FLExInstaller/wix6/Shared/Common/CustomFeatures.wxi create mode 100644 FLExInstaller/wix6/Shared/Common/Overrides.wxi create mode 100644 FLExInstaller/wix6/Shared/Common/Redistributables.wxi create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions.sln create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/ClassDiagram1.cd create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/ClosePromptForm.Designer.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/ClosePromptForm.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/ClosePromptForm.resx create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/CustomAction.config create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/CustomAction.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/CustomActions.csproj create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/Paratext.ico create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/PromptCloseApplication.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/Properties/AssemblyInfo.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/Properties/Settings.Designer.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/Properties/Settings.settings create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/RegUtils.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/TextMessageForm.Designer.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/TextMessageForm.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/TextMessageForm.resx create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/WindowWrapper.cs create mode 100644 FLExInstaller/wix6/Shared/CustomActions/CustomActions/app.config create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner.sln create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/App.config create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/ProcRunner.cs create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/ProcRunner.csproj create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/Properties/AssemblyInfo.cs create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/Properties/Resources.Designer.cs create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/Properties/Resources.resx create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/Properties/Settings.Designer.cs create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/Properties/Settings.settings create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/resources/working.gif create mode 100644 FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/working.gif create mode 100644 FLExInstaller/wix6/Shared/resources/App.ico create mode 100644 FLExInstaller/wix6/Shared/resources/Installer.ico create mode 100644 FLExInstaller/wix6/Shared/resources/License.htm create mode 100644 FLExInstaller/wix6/Shared/resources/MsiBackground.bmp create mode 100644 FLExInstaller/wix6/Shared/resources/bannrbmp.bmp create mode 100644 FLExInstaller/wix6/Shared/resources/bg.png create mode 100644 FLExInstaller/wix6/Shared/resources/header-bg.png create mode 100644 FLExInstaller/wix6/Shared/resources/logo.png create mode 100644 FLExInstaller/wix6/UpdateInfo.xml create mode 100644 FLExInstaller/wix6/UpdateInfoAlpha.xml create mode 100644 FLExInstaller/wix6/UpdateInfoBeta.xml rename FW.6.0.ReSharper => FieldWorks.6.0.ReSharper (100%) create mode 100644 FieldWorks.proj create mode 100644 FieldWorks.sln rename FW.sln.DotSettings => FieldWorks.sln.DotSettings (99%) create mode 100644 FieldWorks_9.3.5.1_x64.wxs delete mode 100644 Lib/Directory.Build.targets delete mode 100644 Lib/debug/ECInterfaces.tlb delete mode 100644 Lib/debug/ICSharpCode.SharpZipLib.dll delete mode 100644 Lib/debug/ICSharpCode.SharpZipLib.pdb delete mode 100644 Lib/debug/ParserObject.lib delete mode 100644 Lib/debug/cport.lib create mode 100644 Lib/debug/test.txt delete mode 100644 Lib/debug/wrtXML.dll delete mode 100644 Lib/debug/xmlparse-utf16.pdb delete mode 100644 Lib/debug/xmlparse.lib delete mode 100644 Lib/debug/xmlparse.pdb create mode 100644 Lib/release/unit++.pdb create mode 100644 Lib/src/ObjectBrowser/FDOHelpers.cs create mode 100644 SDK_MIGRATION.md create mode 100644 Setup-Developer-Machine.ps1 create mode 100644 Src/AppCore/AGENTS.md create mode 100644 Src/CacheLight/AGENTS.md create mode 100644 Src/CacheLight/CacheLightTests/AssemblyInfo.cs create mode 100644 Src/Cellar/AGENTS.md create mode 100644 Src/Common/AGENTS.md create mode 100644 Src/Common/Controls/AGENTS.md create mode 100644 Src/Common/Controls/DetailControls/DetailControlsTests/MorphTypeAtomicLauncherTests.cs create mode 100644 Src/Common/Controls/FwControls/FwControlsTests/AssemblyInfo.cs rename {Bin/nmock/src/src => Src/Common/Controls/FwControls/PredictiveProgressBarTestApp}/AssemblyInfo.cs (66%) rename {Bin/nmock/src/test => Src/Common/Controls/Widgets/DemoWidgets}/AssemblyInfo.cs (66%) create mode 100644 Src/Common/Controls/XMLViews/XMLViewsTests/AssemblyInfo.cs create mode 100644 Src/Common/FieldWorks/AGENTS.md rename Src/{LexText/LexTextExe => Common/FieldWorks/Branding}/LT.ico (100%) rename Src/{LexText/LexTextExe => Common/FieldWorks/Branding}/LT.png (100%) rename Src/{LexText/LexTextExe => Common/FieldWorks/Branding}/LT128.png (100%) rename Src/{LexText/LexTextExe => Common/FieldWorks/Branding}/LT64.png (100%) create mode 100644 Src/Common/FieldWorks/FieldWorks.Diagnostics.config create mode 100644 Src/Common/FieldWorks/FieldWorks.Diagnostics.dev.config create mode 100644 Src/Common/FieldWorks/FieldWorks.exe.manifest create mode 100644 Src/Common/Filters/AGENTS.md create mode 100644 Src/Common/Framework/AGENTS.md create mode 100644 Src/Common/Framework/FrameworkTests/AssemblyInfo.cs create mode 100644 Src/Common/FwUtils/AGENTS.md create mode 100644 Src/Common/FwUtils/FwUtilsTests/AssemblyInfo.cs create mode 100644 Src/Common/FwUtils/FwUtilsTests/AssemblySetupFixture.cs create mode 100644 Src/Common/RootSite/AGENTS.md create mode 100644 Src/Common/RootSite/RootSiteTests/AssemblyInfo.cs create mode 100644 Src/Common/ScriptureUtils/AGENTS.md create mode 100644 Src/Common/ScriptureUtils/ScriptureUtilsTests/AssemblyInfo.cs create mode 100644 Src/Common/SimpleRootSite/AGENTS.md delete mode 100644 Src/Common/SimpleRootSite/SimpleRootSiteTests/IbusRootSiteEventHandlerTests.cs delete mode 100644 Src/Common/SimpleRootSite/SimpleRootSiteTests/IbusRootSiteEventHandlerTests_Simple.cs create mode 100644 Src/Common/UIAdapterInterfaces/AGENTS.md create mode 100644 Src/Common/ViewsInterfaces/AGENTS.md create mode 100644 Src/DbExtend/AGENTS.md create mode 100644 Src/DebugProcs/AGENTS.md create mode 100644 Src/Directory.Build.props delete mode 100644 Src/Directory.Build.targets create mode 100644 Src/DocConvert/AGENTS.md create mode 100644 Src/FXT/AGENTS.md create mode 100644 Src/FXT/FxtExe/ConsoleLcmUI.cs create mode 100644 Src/FXT/FxtExe/NullThreadedProgress.cs create mode 100644 Src/FdoUi/AGENTS.md create mode 100644 Src/FwCoreDlgs/AGENTS.md rename Src/FwCoreDlgs/{ConverterTest.cs => ConverterTester.cs} (99%) rename Src/FwCoreDlgs/{ConverterTest.resx => ConverterTester.resx} (99%) create mode 100644 Src/FwCoreDlgs/FwCoreDlgsTests/AssemblyInfo.cs create mode 100644 Src/FwParatextLexiconPlugin/AGENTS.md create mode 100644 Src/FwResources/AGENTS.md create mode 100644 Src/GenerateHCConfig/AGENTS.md create mode 100644 Src/Generic/AGENTS.md create mode 100644 Src/InstallValidator/AGENTS.md create mode 100644 Src/InstallValidator/InstallValidatorTests/WixInstallerArtifactsTests.cs create mode 100644 Src/InstallValidator/InstallerArtifactsTests/InstallerArtifactsTests.csproj create mode 100644 Src/InstallValidator/InstallerArtifactsTests/WixInstallerArtifactsTests.cs create mode 100644 Src/Kernel/AGENTS.md create mode 100644 Src/LCMBrowser/AGENTS.md create mode 100644 Src/LexText/AGENTS.md create mode 100644 Src/LexText/Discourse/AGENTS.md create mode 100644 Src/LexText/FlexPathwayPlugin/AGENTS.md create mode 100644 Src/LexText/Interlinear/AGENTS.md create mode 100644 Src/LexText/LexTextControls/AGENTS.md create mode 100644 Src/LexText/LexTextDll/AGENTS.md delete mode 100644 Src/LexText/LexTextExe/LexText.cs delete mode 100644 Src/LexText/LexTextExe/LexTextExe.csproj create mode 100644 Src/LexText/Lexicon/AGENTS.md create mode 100644 Src/LexText/Morphology/AGENTS.md create mode 100644 Src/LexText/ParserCore/AGENTS.md create mode 100644 Src/LexText/ParserCore/PatrParserWrapper/Properties/AssemblyInfo.cs create mode 100644 Src/LexText/ParserUI/AGENTS.md create mode 100644 Src/ManagedLgIcuCollator/AGENTS.md create mode 100644 Src/ManagedVwDrawRootBuffered/AGENTS.md create mode 100644 Src/ManagedVwWindow/AGENTS.md create mode 100644 Src/MigrateSqlDbs/AGENTS.md create mode 100644 Src/Paratext8Plugin/AGENTS.md create mode 100644 Src/ParatextImport/AGENTS.md create mode 100644 Src/ProjectUnpacker/AGENTS.md create mode 100644 Src/Transforms/AGENTS.md create mode 100644 Src/UnicodeCharEditor/AGENTS.md create mode 100644 Src/Utilities/AGENTS.md create mode 100644 Src/Utilities/ComManifestTestHost/BuildInclude.targets create mode 100644 Src/Utilities/ComManifestTestHost/ComManifestTestHost.csproj create mode 100644 Src/Utilities/ComManifestTestHost/Program.cs create mode 100644 Src/Utilities/FixFwData/AGENTS.md create mode 100644 Src/Utilities/FixFwDataDll/AGENTS.md rename Src/Utilities/FixFwDataDll/{Strings.Designer.cs => FixFwDataStrings.Designer.cs} (62%) rename Src/Utilities/FixFwDataDll/{Strings.resx => FixFwDataStrings.resx} (83%) create mode 100644 Src/Utilities/MessageBoxExLib/AGENTS.md create mode 100644 Src/Utilities/MessageBoxExLib/MessageBoxExLibTests/AssemblyInfo.cs create mode 100644 Src/Utilities/Reporting/AGENTS.md rename Src/Utilities/Reporting/{ErrorReport.resx => ErrorReporter.resx} (100%) create mode 100644 Src/Utilities/SfmStats/AGENTS.md create mode 100644 Src/Utilities/SfmToXml/AGENTS.md create mode 100644 Src/Utilities/SfmToXml/ConvertSFM/AssemblyInfo.cs create mode 100644 Src/Utilities/SfmToXml/XSLTTester/AssemblyInfo.cs create mode 100644 Src/Utilities/XMLUtils/AGENTS.md create mode 100644 Src/Utilities/XMLUtils/XMLUtilsTests/AssemblyInfo.cs create mode 100644 Src/XCore/AGENTS.md create mode 100644 Src/XCore/ControlLibrary/CommandBarLibrary/AssemblyInfo.cs create mode 100644 Src/XCore/ControlLibrary/SidebarLibrary/AssemblyInfo.cs create mode 100644 Src/XCore/FlexUIAdapter/AGENTS.md create mode 100644 Src/XCore/SilSidePane/AGENTS.md create mode 100644 Src/XCore/SilSidePane/SilSidePaneTests/AssemblyInfo.cs create mode 100644 Src/XCore/SilSidePaneAdapter/AssemblyInfo.cs create mode 100644 Src/XCore/XCoreSample/AssemblyInfo.cs create mode 100644 Src/XCore/xCoreInterfaces/AGENTS.md rename Src/{LexText/LexTextExe => XCore/xCoreOpenSourceAdapter}/AssemblyInfo.cs (53%) create mode 100644 Src/XCore/xCoreTests/AGENTS.md create mode 100644 Src/XCore/xCoreTests/AssemblyInfo.cs create mode 100644 Src/views/AGENTS.md create mode 100644 Src/views/Test/debug_main.cpp create mode 100644 Src/views/Test/early_log.cpp delete mode 100644 Src/views/views2008.vcproj create mode 100644 Src/xWorks/AGENTS.md create mode 100644 Test.runsettings delete mode 100755 agent-build-fw.sh create mode 100644 build.ps1 create mode 100644 contracts/test-exclusion-api.yaml delete mode 100644 environ delete mode 100644 environ-other delete mode 100644 environ-xulrunner delete mode 100644 fw.code-workspace create mode 100644 generate_version.proj create mode 100644 nuget.config create mode 100644 openspec/AGENTS.md create mode 100644 openspec/MAKE_SPECS.md create mode 100644 openspec/changes/detail-controls-testability/.openspec.yaml create mode 100644 openspec/changes/detail-controls-testability/design.md create mode 100644 openspec/changes/detail-controls-testability/detail-controls-class-diagram.mmd create mode 100644 openspec/changes/detail-controls-testability/proposal.md create mode 100644 openspec/changes/detail-controls-testability/specs/architecture/ui-framework/winforms-patterns/spec.md create mode 100644 openspec/changes/detail-controls-testability/specs/detail-controls-architecture/spec.md create mode 100644 openspec/changes/detail-controls-testability/specs/detail-controls-testability/spec.md create mode 100644 openspec/changes/detail-controls-testability/tasks.md create mode 100644 openspec/config.yaml create mode 100644 openspec/specs/README.md create mode 100644 openspec/specs/architecture/build-deploy/build-phases.md create mode 100644 openspec/specs/architecture/build-deploy/installer.md create mode 100644 openspec/specs/architecture/build-deploy/localization.md create mode 100644 openspec/specs/architecture/data-access/lcm-patterns.md create mode 100644 openspec/specs/architecture/data-access/undo-redo.md create mode 100644 openspec/specs/architecture/interop/com-contracts.md create mode 100644 openspec/specs/architecture/interop/external-apis.md create mode 100644 openspec/specs/architecture/interop/native-boundary.md create mode 100644 openspec/specs/architecture/layers/dependency-graph.md create mode 100644 openspec/specs/architecture/layers/entry-points.md create mode 100644 openspec/specs/architecture/layers/layer-model.md create mode 100644 openspec/specs/architecture/testing/fixtures.md create mode 100644 openspec/specs/architecture/testing/test-strategy.md create mode 100644 openspec/specs/architecture/ui-framework/views-rendering.md create mode 100644 openspec/specs/architecture/ui-framework/winforms-patterns.md create mode 100644 openspec/specs/architecture/ui-framework/xcore-mediator.md create mode 100644 openspec/specs/configuration/lists.md create mode 100644 openspec/specs/configuration/projects.md create mode 100644 openspec/specs/configuration/writing-systems.md create mode 100644 openspec/specs/grammar/morphology/affixes.md create mode 100644 openspec/specs/grammar/morphology/allomorphs.md create mode 100644 openspec/specs/grammar/morphology/categories.md create mode 100644 openspec/specs/grammar/parsing/configuration.md create mode 100644 openspec/specs/grammar/parsing/rules.md create mode 100644 openspec/specs/grammar/parsing/troubleshooting.md create mode 100644 openspec/specs/grammar/sketch/generation.md create mode 100644 openspec/specs/integration/collaboration/multi-user.md create mode 100644 openspec/specs/integration/collaboration/send-receive.md create mode 100644 openspec/specs/integration/external/encoding.md create mode 100644 openspec/specs/integration/external/flextools.md create mode 100644 openspec/specs/integration/external/paratext.md create mode 100644 openspec/specs/lexicon/entries/creation.md create mode 100644 openspec/specs/lexicon/entries/relations.md create mode 100644 openspec/specs/lexicon/entries/structure.md create mode 100644 openspec/specs/lexicon/export/dictionary.md create mode 100644 openspec/specs/lexicon/export/lift.md create mode 100644 openspec/specs/lexicon/export/pathway.md create mode 100644 openspec/specs/lexicon/import/lift.md create mode 100644 openspec/specs/lexicon/import/sfm.md create mode 100644 openspec/specs/texts/analysis/concordance.md create mode 100644 openspec/specs/texts/analysis/discourse.md create mode 100644 openspec/specs/texts/analysis/tagging.md create mode 100644 openspec/specs/texts/export/formats.md create mode 100644 openspec/specs/texts/interlinear/annotation.md create mode 100644 openspec/specs/texts/interlinear/baseline.md create mode 100644 openspec/specs/texts/interlinear/import.md create mode 100644 preprocessed.xml create mode 100644 regen_midl.cmd create mode 100644 scripts/Agent/Collect-InstallerSnapshot.ps1 create mode 100644 scripts/Agent/Compare-InstallerEvidenceRuns.ps1 create mode 100644 scripts/Agent/Compare-InstallerSnapshots.ps1 create mode 100644 scripts/Agent/Compare-SpecInstallerEvidence.ps1 create mode 100644 scripts/Agent/Copy-LocalLcm.ps1 create mode 100644 scripts/Agent/Git-Search.ps1 create mode 100644 scripts/Agent/Guest/InstallerParityEvidence.Common.ps1 create mode 100644 scripts/Agent/Guest/Invoke-InstallerParityEvidence-Wix3.ps1 create mode 100644 scripts/Agent/Guest/Invoke-InstallerParityEvidence-Wix6.ps1 create mode 100644 scripts/Agent/Guest/Invoke-InstallerParityEvidence.ps1 create mode 100644 scripts/Agent/Guest/Invoke-InstallerUninstallEvidence.ps1 create mode 100644 scripts/Agent/Guest/Invoke-ManualInstallerEvidence.ps1 create mode 100644 scripts/Agent/Invoke-Installer.ps1 create mode 100644 scripts/Agent/Invoke-InstallerCheck.ps1 create mode 100644 scripts/Agent/Jira-Beads-FromSelection.ps1 create mode 100644 scripts/Agent/New-Wix6ParityFixPlan.ps1 create mode 100644 scripts/Agent/README.md create mode 100644 scripts/Agent/Read-FileContent.ps1 create mode 100644 scripts/Agent/Remove-FieldWorksAll.ps1 create mode 100644 scripts/Agent/Setup-Local-Localization.ps1 create mode 100644 scripts/GenerateAssemblyInfo/__init__.py create mode 100644 scripts/GenerateAssemblyInfo/assembly_info_parser.py create mode 100644 scripts/GenerateAssemblyInfo/audit_generate_assembly_info.py create mode 100644 scripts/GenerateAssemblyInfo/cli_args.py create mode 100644 scripts/GenerateAssemblyInfo/convert_generate_assembly_info.py create mode 100644 scripts/GenerateAssemblyInfo/git_metadata.py create mode 100644 scripts/GenerateAssemblyInfo/git_restore.py create mode 100644 scripts/GenerateAssemblyInfo/history_diff.py create mode 100644 scripts/GenerateAssemblyInfo/models.py create mode 100644 scripts/GenerateAssemblyInfo/project_scanner.py create mode 100644 scripts/GenerateAssemblyInfo/reflect_attributes.ps1 create mode 100644 scripts/GenerateAssemblyInfo/reporting.py create mode 100644 scripts/GenerateAssemblyInfo/validate_generate_assembly_info.py create mode 100644 scripts/GenerateAssemblyInfo/verify-performance-and-tests.ps1 create mode 100644 scripts/Installer/Invoke-InstallerWithLog.ps1 create mode 100644 scripts/Rename-WorktreeToBranch.ps1 create mode 100644 scripts/Setup-WorktreeBeads.ps1 create mode 100644 scripts/Setup-WorktreeColor.ps1 create mode 100644 scripts/Worktree-CreateFromBranch.ps1 create mode 100644 scripts/analyze_audit.py create mode 100644 scripts/audit_ignored_tests.py create mode 100644 scripts/audit_test_exclusions.py create mode 100644 scripts/convert_test_exclusions.py create mode 100644 scripts/enforce_x64_platform.py create mode 100644 scripts/git-utilities.ps1 create mode 100644 scripts/openspec/Propose-RefFixes.ps1 create mode 100644 scripts/openspec/Report-RefCoverage.ps1 create mode 100644 scripts/openspec/Sync-AgentsAnchors.ps1 create mode 100644 scripts/openspec/Validate-OpenSpecRefs.ps1 create mode 100644 scripts/regfree/FieldWorks.regfree.manifest create mode 100644 scripts/regfree/README.md create mode 100644 scripts/regfree/__init__.py create mode 100644 scripts/regfree/audit_com_usage.py create mode 100644 scripts/regfree/com_guids.json create mode 100644 scripts/regfree/common.py create mode 100644 scripts/regfree/extract_clsids.py create mode 100644 scripts/regfree/extract_com_guids.py create mode 100644 scripts/regfree/generate_app_manifests.py create mode 100644 scripts/regfree/generate_manifest.py create mode 100644 scripts/regfree/project_map.json create mode 100644 scripts/regfree/run-in-vm.ps1 create mode 100644 scripts/templates/settings.example.json create mode 100644 scripts/templates/tasks.template.json create mode 100644 scripts/test_exclusions/README.md create mode 100644 scripts/test_exclusions/__init__.py create mode 100644 scripts/test_exclusions/assembly_guard.ps1 create mode 100644 scripts/test_exclusions/converter.py create mode 100644 scripts/test_exclusions/escalation_writer.py create mode 100644 scripts/test_exclusions/models.py create mode 100644 scripts/test_exclusions/msbuild_parser.py create mode 100644 scripts/test_exclusions/repo_scanner.py create mode 100644 scripts/test_exclusions/report_writer.py create mode 100644 scripts/test_exclusions/validator.py create mode 100644 scripts/tests/__init__.py create mode 100644 scripts/tests/compare_ignoring_format.py create mode 100644 scripts/tests/conftest.py create mode 100644 scripts/tests/convert_nunit.py create mode 100644 scripts/tests/convert_rhinomock_to_moq.py create mode 100644 scripts/tests/fixtures/audit/README.md create mode 100644 scripts/tests/fixtures/audit/Src/Explicit/Explicit.csproj create mode 100644 scripts/tests/fixtures/audit/Src/Explicit/ExplicitTests/FooTest.cs create mode 100644 scripts/tests/fixtures/audit/Src/Missing/Missing.csproj create mode 100644 scripts/tests/fixtures/audit/Src/Missing/MissingTests/BazTest.cs create mode 100644 scripts/tests/fixtures/audit/Src/Wildcard/Helpers/HelperTests.cs create mode 100644 scripts/tests/fixtures/audit/Src/Wildcard/Wildcard.csproj create mode 100644 scripts/tests/fixtures/audit/Src/Wildcard/WildcardTests/BarTest.cs create mode 100644 scripts/tests/nunit_converters.py create mode 100644 scripts/tests/nunit_fixers.py create mode 100644 scripts/tests/nunit_parsing.py create mode 100644 scripts/tests/test_exclusions/__init__.py create mode 100644 scripts/tests/test_exclusions/test_assembly_guard.py create mode 100644 scripts/tests/test_exclusions/test_audit_command.py create mode 100644 scripts/tests/test_exclusions/test_converter.py create mode 100644 scripts/tests/test_exclusions/test_models_and_scanner.py create mode 100644 scripts/tests/test_exclusions/test_validator_command.py create mode 100644 scripts/tools/parse_msbuild_warnings.py create mode 100644 scripts/tools/remove_duplicate_assemblyinfo.py create mode 100644 scripts/tools/sync_copilot_instructions.py create mode 100644 scripts/toolshims/README.md create mode 100644 scripts/toolshims/environ.cmd create mode 100644 scripts/toolshims/py.cmd create mode 100644 scripts/toolshims/py.ps1 create mode 100644 scripts/validate_test_exclusions.py create mode 100644 test.ps1 create mode 100644 tests/Integration/RegFreeCom/README.md create mode 100644 tests/Integration/RegFreeCom/test_audit_com_usage.py delete mode 100644 vagrant/.gitignore delete mode 100644 vagrant/Vagrantfile diff --git a/.GitHub/copilot-setup-steps.yml b/.GitHub/copilot-setup-steps.yml deleted file mode 100644 index 263c54186b..0000000000 --- a/.GitHub/copilot-setup-steps.yml +++ /dev/null @@ -1,78 +0,0 @@ -# Reusable workflow to run msbuild tasks on Windows -# Call with: uses: ./.github/workflows/copilot-setup-steps.yml -name: "Copilot: Windows setup steps" -on: - workflow_call: - inputs: - msbuild_args: - required: false - type: string - description: 'Arguments to pass to msbuild (e.g., "FieldWorks.sln /t:RunTests /p:Configuration=Debug")' - # optional secrets or other inputs may be added - -jobs: - windows-setup: - runs-on: windows-latest - outputs: - msbuild-path: ${{ steps.find-msbuild.outputs.msbuild-path }} - - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Find MSBuild (vswhere) - id: find-msbuild - shell: pwsh - run: | - # Try to find msbuild in Visual Studio using vswhere; fallback to common paths - $vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" - if (Test-Path $vsWhere) { - $ms = & $vsWhere -latest -requires Microsoft.Component.MSBuild -products * -property installationPath - if ($ms) { - $msbuild = Join-Path $ms 'MSBuild\Current\Bin\MSBuild.exe' - if (-not (Test-Path $msbuild)) { - # older path - $msbuild = Join-Path $ms 'MSBuild\15.0\Bin\MSBuild.exe' - } - } - } - if (-not $msbuild) { - # fallback: try common Program Files paths - $candidates = @( - "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\MSBuild.exe", - "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin\MSBuild.exe", - "${env:ProgramFiles}\MSBuild\14.0\Bin\MSBuild.exe" - ) - foreach ($c in $candidates) { - if (Test-Path $c) { $msbuild = $c; break } - } - } - if (-not $msbuild) { - Write-Error "MSBuild not found on runner. Consider installing Visual Studio Build Tools in the runner image or use a self-hosted Windows runner." - exit 1 - } else { - Add-Content -Path $env:GITHUB_OUTPUT -Value "msbuild-path=$msbuild" - Write-Host "Found MSBuild at $msbuild" - } - - - name: Run msbuild (if args provided) - if: inputs.msbuild_args != '' && inputs.msbuild_args != null - shell: pwsh - run: | - $msbuildPath = "${{ steps.find-msbuild.outputs.msbuild-path }}" - if (-not $msbuildPath) { - Write-Error "MSBuild path not found" - exit 2 - } - Write-Host "Running MSBuild: $msbuildPath ${{ inputs.msbuild_args }}" - $args = @() - if ("${{ inputs.msbuild_args }}".Trim() -ne "") { - # Split on spaces, preserving quoted substrings - $args = [System.Management.Automation.PSParser]::Tokenize(${{ toJson(inputs.msbuild_args) }}, [ref]$null) | Where-Object { $_.Type -eq 'String' -or $_.Type -eq 'CommandArgument' } | ForEach-Object { $_.Content } - } - & $msbuildPath @args - if ($LASTEXITCODE -ne 0) { - Write-Error "MSBuild failed with exit code $LASTEXITCODE" - exit $LASTEXITCODE - } - Write-Host "Build completed successfully!" -ForegroundColor Green diff --git a/.gitattributes b/.gitattributes index 7f04265383..d7686b163c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -31,3 +31,6 @@ Bin/ilrepack-assemblies -whitespace *.json -whitespace *.js -whitespace Src/LexText/ParserCore/ParserCoreTests/**/*.txt -whitespace + +# Use bd merge for beads JSONL files +.beads/issues.jsonl merge=beads diff --git a/.github/AGENTS.md b/.github/AGENTS.md new file mode 100644 index 0000000000..47046a19a5 --- /dev/null +++ b/.github/AGENTS.md @@ -0,0 +1,143 @@ +# FieldWorks Agentic Instructions + +## Purpose & Scope +- Give AI coding agents a fast, reliable playbook for FieldWorks—what the repo contains, how to build/test, and how to keep documentation accurate. +- Assume nothing beyond this file and linked instructions; only search the repo when a referenced step fails or is missing. + +See `.github/AI_GOVERNANCE.md` for the documentation taxonomy and “source of truth” rules. + +## Repository Snapshot +- Product: FieldWorks (FLEx) — Windows-first linguistics suite maintained by SIL International. +- Languages & tech: C#, C++/CLI, native C++, WiX, PowerShell, XML, JSON, XAML/WinForms. +- Tooling: Visual Studio 2022 (Desktop workloads), MSBuild Traversal (`FieldWorks.proj`), WiX 3.14.x, NUnit-style tests, Crowdin localization. +- Docs: `ReadMe.md` → https://github.com/sillsdev/FwDocumentation/wiki for deep dives; `.github/src-catalog.md` + per-folder `AGENTS.md` describe Src/ layout. + +## Core Rules +- Prefer `./build.ps1`; avoid ad-hoc project builds that skip traversal ordering. +- Run tests relevant to your change before pushing; do not assume CI coverage. +- For bug fixes, default to TDD (Red-Green-Refactor): write a failing test first, then implement the minimal fix. +- If you need to pause current edits to stay test-first, use `git stash` to hold changes while implementing the tests to fail, then restore with `git stash apply`. +- Keep localization via `.resx` and respect `crowdin.json`; never hardcode translatable strings. +- Avoid COM/registry edits without a test plan. +- Stay within documented tooling—no surprise dependencies or scripts without updating instructions. +- **Terminal commands**: **ALWAYS use `scripts/Agent/` wrapper scripts** for git or file reading requiring pipes/filters. See `.github/instructions/terminal.instructions.md` for the transformation table. + +## Build & Test Essentials +- Prerequisites: install VS 2022 Desktop workloads, WiX 3.14.x (pre-installed on windows-latest), Git, LLVM/clangd + standalone OmniSharp (for Serena C++/C# support), and optional Crowdin CLI only when needed. +- Verify your environment: `.\Build\Agent\Verify-FwDependencies.ps1 -IncludeOptional` +- Common commands: + ```powershell + # Full traversal build (Debug/x64 defaults) + .\build.ps1 + + # Run tests + .\test.ps1 + ``` +- Tests: follow `.github/instructions/testing.instructions.md`; use VS Test Explorer or `vstest.console.exe` for managed tests. +- Installer edits must follow `.github/instructions/installer.instructions.md` plus WiX validation before PR. +- Installer builds: use `.\Build\Agent\Setup-InstallerBuild.ps1 -ValidateOnly` to check prerequisites, `-SetupPatch` for patch builds. + +## Workflow Shortcuts +| Task | Reference | +| --- | --- | +| Build/test rules | `.github/instructions/build.instructions.md`, `.github/instructions/testing.instructions.md` | +| Debugging | `.github/instructions/debugging.instructions.md` | +| Managed / Native / Installer guidance | `.github/instructions/managed.instructions.md`, `.github/instructions/native.instructions.md`, `.github/instructions/installer.instructions.md` | +| Security & PowerShell rules | `.github/instructions/security.instructions.md`, `.github/instructions/powershell.instructions.md` | +| Guidance governance | `.github/AI_GOVERNANCE.md` | +| **Agent wrapper scripts** | `scripts/Agent/` - build, test, and git helpers for auto-approval | +| Prompts & specs | `.github/prompts/*.prompt.md`, `.github/spec-templates/`, `.github/recipes/` | +| Chat modes | `.github/chatmodes/*.chatmode.md` | + +## Instruction & Prompt Expectations +- Instruction files live under `.github/instructions/` with `applyTo`, `name`, and `description` frontmatter only; keep content ≤ 200 lines with Purpose/Scope, Key Rules, Examples. +- Chat modes constrain role-specific behavior (managed/native/installer/technical-writer) and should be referenced when invoking agents. + +**Context7 Guidance:** When requesting API references, code examples, or library-specific patterns, consult Context7 first (for example, call `resolve-library-id` then `get-library-docs` or `search-code`). Prefer the Context7 libraries listed in `.vscode/context7-configuration.json` and include the resolved library ID in your prompt when possible. Context7 lookups are considered safe and are configured for auto-approval in this workspace. + +## AGENTS.md Maintenance +1. **Detect** stale folders: `python .github/detect_copilot_needed.py --strict --base origin/ --json .cache/copilot/detect.json`. +2. **Plan** diffs + reference groups: `python .github/plan_copilot_updates.py --detect-json .cache/copilot/detect.json --out .cache/copilot/diff-plan.json`. +3. **Scaffold** (optional) when a file drifts from the canonical layout: `python .github/scaffold_copilot_markdown.py --folders Src/`. +4. **Apply** the auto change-log from the planner: `python .github/copilot_apply_updates.py --plan .cache/copilot/diff-plan.json --folders Src/`. +5. **Edit narrative sections** using the planner JSON (change counts, commit log, `reference_groups`), keeping human guidance short and linking to subfolder docs where possible. +6. **Validate** with `python .github/check_copilot_docs.py --only-changed --fail` (or use `--paths Src/Foo/AGENTS.md` for targeted checks). +7. When documentation exceeds ~200 lines or acts as a parent index, migrate to `.github/templates/organizational-copilot.template.md` and keep the parent doc as a navigation index. +8. Run `.github/prompts/copilot-folder-review.prompt.md` with the updated plan slice to simulate an agent review before committing. + +## CI & Validation Requirements +- GitHub Actions workflows live under `.github/workflows/`; keep them passing. +- Local parity checks: + ```powershell + # Commit messages (gitlint) + python -m pip install --upgrade gitlint + git fetch origin + gitlint --ignore body-is-missing --commits origin/.. + + # Whitespace + git log --check --pretty=format:"---% h% s" origin/.. + git diff --check --cached + ``` +- Before PRs, ensure: + - Build + relevant tests succeed locally. + - Installer/config changes validated with WiX tooling. + - Analyzer/lint warnings addressed. + +### Build & Test Commands (ALWAYS use the scripts) +```powershell +# Build +.\build.ps1 +.\build.ps1 -Configuration Release +.\build.ps1 -BuildTests + +# Test +.\test.ps1 +.\test.ps1 -TestFilter "TestCategory!=Slow" +.\test.ps1 -TestProject "Src/Common/FwUtils/FwUtilsTests" +.\test.ps1 -NoBuild # Skip build, use existing binaries + +# Both scripts automatically: +# - Clean stale obj/ folders and conflicting processes +# - Set up VS environment +``` + +**DO NOT** use raw `msbuild` directly - let the scripts handle it. + +## Where to Make Changes +- Source: `Src/` contains managed/native projects—mirror existing patterns and keep tests near the code (`Src/.Tests`). +- Installer: `FLExInstaller/` with WiX artifacts. +- Shared headers/libs: `Include/`, `Lib/` (avoid committing large binaries unless policy allows). +- Localization: update `.resx` files; never edit `crowdin.json` unless you understand Crowdin flows. +- Build infrastructure: `Build/` + `Bld/` orchestrate targets/props—change sparingly and document impacts. + +## JIRA Integration + +**LT-prefixed tickets** (e.g., `LT-22382`) are JIRA issues from `https://jira.sil.org/`. + +⚠️ **NEVER browse to `jira.sil.org` URLs** - requires authentication. **ALWAYS use Python scripts:** + +```powershell +# Get issue details (inline Python) +python -c "import sys; sys.path.insert(0, '.github/skills/atlassian-readonly-skills/scripts'); from jira_issues import jira_get_issue; print(jira_get_issue('LT-22382'))" + +# Or export your assigned issues to JSON +python .github/skills/jira-to-beads/scripts/export_jira_assigned.py +``` + +| Scenario | Skill | +|----------|-------| +| Read issue details | `atlassian-readonly-skills` (default) | +| Create/update/comment | `atlassian-skills` (only when user explicitly requests) | +| Bulk import to Beads | `jira-to-beads` | + +See `/AGENTS.md` → "Atlassian / JIRA Skills" section for full configuration and details. + +## Confidence Checklist +- [ ] Prefer traversal builds over per-project compile hacks. +- [ ] Keep coding style aligned with `.editorconfig` and existing patterns. +- [ ] Validate installer/localization changes before PR. +- [ ] Record uncertainties with `FIXME()` and resolve them when evidence is available. +- [ ] Refer back to this guide whenever you need repo-wide ground truth. + + + diff --git a/.github/AI_GOVERNANCE.md b/.github/AI_GOVERNANCE.md new file mode 100644 index 0000000000..0c8777188b --- /dev/null +++ b/.github/AI_GOVERNANCE.md @@ -0,0 +1,71 @@ +# AI guidance governance + +## Purpose +This repo uses a **tool-agnostic, agent-first** documentation strategy: +- Component knowledge lives with the component (`Src/**/AGENTS.md`). +- A small set of scoped instruction files in `.github/instructions/` provides **prescriptive, enforceable constraints**. +- `.github/AGENTS.md` is the short “front door” that links to the right places. +- Agent definitions in `.github/agents/` and role chatmodes in `.github/chatmodes/` describe **behavior/persona**, not system architecture. + +## Source of truth +- **Component architecture & entry points**: `Src//AGENTS.md` +- **Repo-wide workflow** (how to build/test, safety constraints): `.github/AGENTS.md` +- **Non-negotiable rules** (security, terminal restrictions, installer rules, etc.): `.github/instructions/*.instructions.md` + +## No duplication rule +- Do not copy component descriptions into `.github/instructions/`. +- Do not restate rules in multiple places. Prefer linking. +- If a rule must be enforced by agents for a subtree, add a scoped `.instructions.md`; otherwise document it in the relevant `AGENTS.md`. + +## What goes where + +### `.github/AGENTS.md` +Use for: +- One-page onboarding for agents: build/test commands, repo constraints, and links. +- Pointers to the curated instruction set and the component docs. + +### `.github/instructions/*.instructions.md` +Use for: +- Prescriptive constraints that must be applied during editing/review. +- Cross-cutting rules that prevent expensive mistakes (security, terminal command restrictions, installer rules, managed/native boundary rules). + +**Curated keep set (intentionally small):** +- `build.instructions.md` +- `debugging.instructions.md` +- `installer.instructions.md` +- `managed.instructions.md` +- `native.instructions.md` +- `powershell.instructions.md` +- `repo.instructions.md` +- `security.instructions.md` +- `terminal.instructions.md` +- `testing.instructions.md` + +### `Src/**/AGENTS.md` +Use for: +- Where to start (entry points, key projects, typical workflows). +- Dependencies and cross-component links. +- Tests (where they live, how to run them). + +Baseline expectations for a component agent doc: +- **Where to start** (projects, primary entry points) +- **Dependencies** (other components/layers) +- **Tests** (test projects and the recommended `./test.ps1` invocation) + +### `.github/agents/` and `.github/chatmodes/` +Use for: +- Role definitions, boundaries, and tool preferences. +- Do not put component architecture here; link to the component `AGENTS.md`. + +## External standards alignment (post-2025) +- **AGENTS.md**: Supported as a simple, vendor-neutral instruction format by multiple tools (for example, Cursor’s project rules). Use plain Markdown with clear headings and concise rules. +- **MCP (Model Context Protocol)**: Use MCP for tool/data integration rather than vendor-specific plugins; MCP provides a standardized, versioned protocol for AI tool connectivity. + +## Adding a new scoped instruction file +Add a new `.github/instructions/.instructions.md` only when: +- The guidance is prescriptive (MUST/DO NOT), and +- It applies broadly or to a subtree, and +- It would be harmful if Copilot ignored it. + +Otherwise, update the appropriate `Src/**/AGENTS.md`. + diff --git a/.github/BUILD_REQUIREMENTS.md b/.github/BUILD_REQUIREMENTS.md new file mode 100644 index 0000000000..a307d4e186 --- /dev/null +++ b/.github/BUILD_REQUIREMENTS.md @@ -0,0 +1,79 @@ +# Build Requirements + +## Local Development + +### Full Build (C# + Native C++) + +To build the complete FieldWorks solution including native C++ components: + +**PowerShell (Recommended - auto-initializes VS environment):** +```powershell +.\build.ps1 +``` + +**Why?** Native components (DebugProcs, GenericLib, FwKernel, Views, graphite2) require: +- `nmake.exe` (from Visual Studio C++ Build Tools) +- C++ compiler toolchain +- Environment variables set by VsDevCmd.bat (VCINSTALLDIR, INCLUDE, LIB, etc.) + +**Note:** The PowerShell script (`build.ps1`) automatically initializes the Visual Studio environment using `vswhere.exe`. + +### Managed-Only Build (C# projects) + +If you only need to build C# projects and already have native artifacts from a previous build: + +```powershell +# Build only managed projects (skips native C++) +msbuild FieldWorks.proj /p:Configuration=Debug /p:Platform=x64 +``` + +## CI Builds + +GitHub Actions CI automatically configures the Developer environment using the `microsoft/setup-msbuild@v2` action. No manual setup is required. + +From `.github/workflows/CI.yml`: +```yaml +- name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 # Configures VS environment automatically + +- name: Build Debug and run tests + run: ./build.ps1 -Configuration Debug -Platform x64 +``` + +## Troubleshooting + +### Error: "nmake.exe could not be run" or "VCINSTALLDIR not set" + +**Cause:** Build script was run from a regular PowerShell/bash session instead of a Developer Command Prompt. + +**Solution:** +1. Close your current terminal +2. Open "Developer Command Prompt for VS 2022" or "Developer PowerShell for VS 2022" from the Start Menu +3. Navigate to the repository +4. Run the build script again + +### Error: "Missing FieldWorks build tasks assembly" + +**Cause:** FwBuildTasks.dll hasn't been built yet (typically on first build or after clean). + +**Solution:** The build scripts now automatically bootstrap FwBuildTasks. If this fails, manually build it first: +```powershell +msbuild Build/Src/FwBuildTasks/FwBuildTasks.csproj /t:Restore;Build /p:Configuration=Debug +``` + +## Build Script Features + +`build.ps1` includes: + +1. **Automatic FwBuildTasks bootstrap**: Builds build infrastructure before main build +2. **Environment validation**: Warns if Developer environment is not detected +3. **Package restoration**: Restores NuGet packages before build +4. **Traversal build**: Uses MSBuild Traversal SDK (FieldWorks.proj) for correct dependency ordering + +## Visual Studio Requirements + +- **Visual Studio 2022** (Community, Professional, or Enterprise) +- **Required Workloads:** + - .NET desktop development + - Desktop development with C++ +- **Optional:** WiX Toolset 3.14.1 (only for installer builds) diff --git a/.github/agents/WinFormsExpert.agent.md b/.github/agents/WinFormsExpert.agent.md new file mode 100644 index 0000000000..dd834bf330 --- /dev/null +++ b/.github/agents/WinFormsExpert.agent.md @@ -0,0 +1,628 @@ +--- +name: WinForms Expert +description: Support development of .NET (OOP) WinForms Designer compatible Apps. +#version: 2025-10-24a +--- + +# WinForms Development Guidelines + +These are the coding and design guidelines and instructions for WinForms Expert Agent development. +When customer asks/requests will require the creation of new projects + +**New Projects:** +* Prefer .NET 10+. Note: MVVM Binding requires .NET 8+. +* Prefer `Application.SetColorMode(SystemColorMode.System);` in `Program.cs` at application startup for DarkMode support (.NET 9+). +* Make Windows API projection available by default. Assume 10.0.22000.0 as minimum Windows version requirement. +```xml + net10.0-windows10.0.22000.0 +``` + +**Critical:** + +**📦 NUGET:** New projects or supporting class libraries often need special NuGet packages. +Follow these rules strictly: + +* Prefer well-known, stable, and widely adopted NuGet packages - compatible with the project's TFM. +* Define the versions to the latest STABLE major version, e.g.: `[2.*,)` + +**⚙️ Configuration and App-wide HighDPI settings:** *app.config* files are discouraged for configuration for .NET. +For setting the HighDpiMode, use e.g. `Application.SetHighDpiMode(HighDpiMode.SystemAware)` at application startup, not *app.config* nor *manifest* files. + +Note: `SystemAware` is standard for .NET, use `PerMonitorV2` when explicitly requested. + +**VB Specifics:** +- In VB, do NOT create a *Program.vb* - rather use the VB App Framework. +- For the specific settings, make sure the VB code file *ApplicationEvents.vb* is available. + Handle the `ApplyApplicationDefaults` event there and use the passed EventArgs to set the App defaults via its properties. + +| Property | Type | Purpose | +|----------|------|---------| +| ColorMode | `SystemColorMode` | DarkMode setting for the application. Prefer `System`. Other options: `Dark`, `Classic`. | +| Font | `Font` | Default Font for the whole Application. | +| HighDpiMode | `HighDpiMode` | `SystemAware` is default. `PerMonitorV2` only when asked for HighDPI Multi-Monitor scenarios. | + +--- + + +## 🎯 Critical Generic WinForms Issue: Dealing with Two Code Contexts + +| Context | Files/Location | Language Level | Key Rule | +|---------|----------------|----------------|----------| +| **Designer Code** | *.designer.cs*, inside `InitializeComponent` | Serialization-centric (assume C# 2.0 language features) | Simple, predictable, parsable | +| **Regular Code** | *.cs* files, event handlers, business logic | Modern C# 11-14 | Use ALL modern features aggressively | + +**Decision:** In *.designer.cs* or `InitializeComponent` → Designer rules. Otherwise → Modern C# rules. + +--- + +## 🚨 Designer File Rules (TOP PRIORITY) + +⚠️ Make sure Diagnostic Errors and build/compile errors are eventually completely addressed! + +### ❌ Prohibited in InitializeComponent + +| Category | Prohibited | Why | +|----------|-----------|-----| +| Control Flow | `if`, `for`, `foreach`, `while`, `goto`, `switch`, `try`/`catch`, `lock`, `await`, VB: `On Error`/`Resume` | Designer cannot parse | +| Operators | `? :` (ternary), `??`/`?.`/`?[]` (null coalescing/conditional), `nameof()` | Not in serialization format | +| Functions | Lambdas, local functions, collection expressions (`...=[]` or `...=[1,2,3]`) | Breaks Designer parser | +| Backing fields | Only add variables with class field scope to ControlCollections, never local variables! | Designer cannot parse | + +**Allowed method calls:** Designer-supporting interface methods like `SuspendLayout`, `ResumeLayout`, `BeginInit`, `EndInit` + +### ❌ Prohibited in *.designer.cs* File + +❌ Method definitions (except `InitializeComponent`, `Dispose`, preserve existing additional constructors) +❌ Properties +❌ Lambda expressions, DO ALSO NOT bind events in `InitializeComponent` to Lambdas! +❌ Complex logic +❌ `??`/`?.`/`?[]` (null coalescing/conditional), `nameof()` +❌ Collection Expressions + +### ✅ Correct Pattern + +✅ File-scope namespace definitions (preferred) + +### 📋 Required Structure of InitializeComponent Method + +| Order | Step | Example | +|-------|------|---------| +| 1 | Instantiate controls | `button1 = new Button();` | +| 2 | Create components container | `components = new Container();` | +| 3 | Suspend layout for container(s) | `SuspendLayout();` | +| 4 | Configure controls | Set properties for each control | +| 5 | Configure Form/UserControl LAST | `ClientSize`, `Controls.Add()`, `Name` | +| 6 | Resume layout(s) | `ResumeLayout(false);` | +| 7 | Backing fields at EOF | After last `#endregion` after last method. | `_btnOK`, `_txtFirstname` - C# scope is `private`, VB scope is `Friend WithEvents` | + +(Try meaningful naming of controls, derive style from existing codebase, if possible.) + +```csharp +private void InitializeComponent() +{ + // 1. Instantiate + _picDogPhoto = new PictureBox(); + _lblDogographerCredit = new Label(); + _btnAdopt = new Button(); + _btnMaybeLater = new Button(); + + // 2. Components + components = new Container(); + + // 3. Suspend + ((ISupportInitialize)_picDogPhoto).BeginInit(); + SuspendLayout(); + + // 4. Configure controls + _picDogPhoto.Location = new Point(12, 12); + _picDogPhoto.Name = "_picDogPhoto"; + _picDogPhoto.Size = new Size(380, 285); + _picDogPhoto.SizeMode = PictureBoxSizeMode.Zoom; + _picDogPhoto.TabStop = false; + + _lblDogographerCredit.AutoSize = true; + _lblDogographerCredit.Location = new Point(12, 300); + _lblDogographerCredit.Name = "_lblDogographerCredit"; + _lblDogographerCredit.Size = new Size(200, 25); + _lblDogographerCredit.Text = "Photo by: Professional Dogographer"; + + _btnAdopt.Location = new Point(93, 340); + _btnAdopt.Name = "_btnAdopt"; + _btnAdopt.Size = new Size(114, 68); + _btnAdopt.Text = "Adopt!"; + + // OK, if BtnAdopt_Click is defined in main .cs file + _btnAdopt.Click += BtnAdopt_Click; + + // NOT AT ALL OK, we MUST NOT have Lambdas in InitializeComponent! + _btnAdopt.Click += (s, e) => Close(); + + // 5. Configure Form LAST + AutoScaleDimensions = new SizeF(13F, 32F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(420, 450); + Controls.Add(_picDogPhoto); + Controls.Add(_lblDogographerCredit); + Controls.Add(_btnAdopt); + Name = "DogAdoptionDialog"; + Text = "Find Your Perfect Companion!"; + ((ISupportInitialize)_picDogPhoto).EndInit(); + + // 6. Resume + ResumeLayout(false); + PerformLayout(); +} + +#endregion + +// 7. Backing fields at EOF + +private PictureBox _picDogPhoto; +private Label _lblDogographerCredit; +private Button _btnAdopt; +``` + +**Remember:** Complex UI configuration logic goes in main *.cs* file, NOT *.designer.cs*. + +--- + +--- + +## Modern C# Features (Regular Code Only) + +**Apply ONLY to `.cs` files (event handlers, business logic). NEVER in `.designer.cs` or `InitializeComponent`.** + +### Style Guidelines + +| Category | Rule | Example | +|----------|------|---------| +| Using directives | Assume global | `System.Windows.Forms`, `System.Drawing`, `System.ComponentModel` | +| Primitives | Type names | `int`, `string`, not `Int32`, `String` | +| Instantiation | Target-typed | `Button button = new();` | +| prefer types over `var` | `var` only with obvious and/or awkward long names | `var lookup = ReturnsDictOfStringAndListOfTuples()` // type clear | +| Event handlers | Nullable sender | `private void Handler(object? sender, EventArgs e)` | +| Events | Nullable | `public event EventHandler? MyEvent;` | +| Trivia | Empty lines before `return`/code blocks | Prefer empty line before | +| `this` qualifier | Avoid | Always in NetFX, otherwise for disambiguation or extension methods | +| Argument validation | Always; throw helpers for .NET 8+ | `ArgumentNullException.ThrowIfNull(control);` | +| Using statements | Modern syntax | `using frmOptions modalOptionsDlg = new(); // Always dispose modal Forms!` | + +### Property Patterns (⚠️ CRITICAL - Common Bug Source!) + +| Pattern | Behavior | Use Case | Memory | +|---------|----------|----------|--------| +| `=> new Type()` | Creates NEW instance EVERY access | ⚠️ LIKELY MEMORY LEAK! | Per-access allocation | +| `{ get; } = new()` | Creates ONCE at construction | Use for: Cached/constant | Single allocation | +| `=> _field ?? Default` | Computed/dynamic value | Use for: Calculated property | Varies | + +```csharp +// ❌ WRONG - Memory leak +public Brush BackgroundBrush => new SolidBrush(BackColor); + +// ✅ CORRECT - Cached +public Brush BackgroundBrush { get; } = new SolidBrush(Color.White); + +// ✅ CORRECT - Dynamic +public Font CurrentFont => _customFont ?? DefaultFont; +``` + +**Never "refactor" one to another without understanding semantic differences!** + +### Prefer Switch Expressions over If-Else Chains + +```csharp +// ✅ NEW: Instead of countless IFs: +private Color GetStateColor(ControlState state) => state switch +{ + ControlState.Normal => SystemColors.Control, + ControlState.Hover => SystemColors.ControlLight, + ControlState.Pressed => SystemColors.ControlDark, + _ => SystemColors.Control +}; +``` + +### Prefer Pattern Matching in Event Handlers + +```csharp +// Note nullable sender from .NET 8+ on! +private void Button_Click(object? sender, EventArgs e) +{ + if (sender is not Button button || button.Tag is null) + return; + + // Use button here +} +``` + +## When designing Form/UserControl from scratch + +### File Structure + +| Language | Files | Inheritance | +|----------|-------|-------------| +| C# | `FormName.cs` + `FormName.Designer.cs` | `Form` or `UserControl` | +| VB.NET | `FormName.vb` + `FormName.Designer.vb` | `Form` or `UserControl` | + +**Main file:** Logic and event handlers +**Designer file:** Infrastructure, constructors, `Dispose`, `InitializeComponent`, control definitions + +### C# Conventions + +- File-scoped namespaces +- Assume global using directives +- NRTs OK in main Form/UserControl file; forbidden in code-behind `.designer.cs` +- Event _handlers_: `object? sender` +- Events: nullable (`EventHandler?`) + +### VB.NET Conventions + +- Use Application Framework. There is no `Program.vb`. +- Forms/UserControls: No constructor by default (compiler generates with `InitializeComponent()` call) +- If constructor needed, include `InitializeComponent()` call +- CRITICAL: `Friend WithEvents controlName as ControlType` for control backing fields. +- Strongly prefer event handlers `Sub`s with `Handles` clause in main code over `AddHandler` in file`InitializeComponent` + +--- + +## Classic Data Binding and MVVM Data Binding (.NET 8+) + +### Breaking Changes: .NET Framework vs .NET 8+ + +| Feature | .NET Framework <= 4.8.1 | .NET 8+ | +|---------|----------------------|---------| +| Typed DataSets | Designer supported | Code-only (not recommended) | +| Object Binding | Supported | Enhanced UI, fully supported | +| Data Sources Window | Available | Not available | + +### Data Binding Rules + +- Object DataSources: `INotifyPropertyChanged`, `BindingList` required, prefer `ObservableObject` from MVVM CommunityToolkit. +- `ObservableCollection`: Requires `BindingList` a dedicated adapter, that merges both change notifications approaches. Create, if not existing. +- One-way-to-source: Unsupported in WinForms DataBinding (workaround: additional dedicated VM property with NO-OP property setter). + +### Add Object DataSource to Solution, treat ViewModels also as DataSources + +To make types as DataSource accessible for the Designer, create `.datasource` file in `Properties\DataSources\`: + +```xml + + + MyApp.ViewModels.MainViewModel, MyApp.ViewModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + +``` + +Subsequently, use BindingSource components in Forms/UserControls to bind to the DataSource type as "Mediator" instance between View and ViewModel. (Classic WinForms binding approach) + +### New MVVM Command Binding APIs in .NET 8+ + +| API | Description | Cascading | +|-----|-------------|-----------| +| `Control.DataContext` | Ambient property for MVVM | Yes (down hierarchy) | +| `ButtonBase.Command` | ICommand binding | No | +| `ToolStripItem.Command` | ICommand binding | No | +| `*.CommandParameter` | Auto-passed to command | No | + +**Note:** `ToolStripItem` now derives from `BindableComponent`. + +### MVVM Pattern in WinForms (.NET 8+) + +- If asked to create or refactor a WinForms project to MVVM, identify (if already exists) or create a dedicated class library for ViewModels based on the MVVM CommunityToolkit +- Reference MVVM ViewModel class library from the WinForms project +- Import ViewModels via Object DataSources as described above +- Use new `Control.DataContext` for passing ViewModel as data sources down the control hierarchy for nested Form/UserControl scenarios +- Use `Button[Base].Command` or `ToolStripItem.Command` for MVVM command bindings. Use the CommandParameter property for passing parameters. + +- - Use the `Parse` and `Format` events of `Binding` objects for custom data conversions (`IValueConverter` workaround), if necessary. + +```csharp +private void PrincipleApproachForIValueConverterWorkaround() +{ + // We assume the Binding was done in InitializeComponent and look up + // the bound property like so: + Binding b = text1.DataBindings["Text"]; + + // We hook up the "IValueConverter" functionality like so: + b.Format += new ConvertEventHandler(DecimalToCurrencyString); + b.Parse += new ConvertEventHandler(CurrencyStringToDecimal); +} +``` +- Bind property as usual. +- Bind commands the same way - ViewModels are Data SOurces! Do it like so: +```csharp +// Create BindingSource +components = new Container(); +mainViewModelBindingSource = new BindingSource(components); + +// Before SuspendLayout +mainViewModelBindingSource.DataSource = typeof(MyApp.ViewModels.MainViewModel); + +// Bind properties +_txtDataField.DataBindings.Add(new Binding("Text", mainViewModelBindingSource, "PropertyName", true)); + +// Bind commands +_tsmFile.DataBindings.Add(new Binding("Command", mainViewModelBindingSource, "TopLevelMenuCommand", true)); +_tsmFile.CommandParameter = "File"; +``` + +--- + +## WinForms Async Patterns (.NET 9+) + +### Control.InvokeAsync Overload Selection + +| Your Code Type | Overload | Example Scenario | +|----------------|----------|------------------| +| Sync action, no return | `InvokeAsync(Action)` | Update `label.Text` | +| Async operation, no return | `InvokeAsync(Func)` | Load data + update UI | +| Sync function, returns T | `InvokeAsync(Func)` | Get control value | +| Async operation, returns T | `InvokeAsync(Func>)` | Async work + result | + +### ⚠️ Fire-and-Forget Trap + +```csharp +// ❌ WRONG - Analyzer violation, fire-and-forget +await InvokeAsync(() => await LoadDataAsync()); + +// ✅ CORRECT - Use async overload +await InvokeAsync(async (ct) => await LoadDataAsync(ct), outerCancellationToken); +``` + +### Form Async Methods (.NET 9+) + +- `ShowAsync()`: Completes when form closes. + Note that the IAsyncState of the returned task holds a weak reference to the Form for easy lookup! +- `ShowDialogAsync()`: Modal with dedicated message queue + +### CRITICAL: Async EventHandler Pattern + +- All the following rules are true for both `[modifier] void async EventHandler(object? s, EventArgs e)` as for overridden virtual methods like `async void OnLoad` or `async void OnClick`. +- `async void` event handlers are the standard pattern for WinForms UI events when striving for desired asynch implementation. +- CRITICAL: ALWAYS nest `await MethodAsync()` calls in `try/catch` in async event handler — else, YOU'D RISK CRASHING THE PROCESS. + +## Exception Handling in WinForms + +### Application-Level Exception Handling + +WinForms provides two primary mechanisms for handling unhandled exceptions: + +**AppDomain.CurrentDomain.UnhandledException:** +- Catches exceptions from any thread in the AppDomain +- Cannot prevent application termination +- Use for logging critical errors before shutdown + +**Application.ThreadException:** +- Catches exceptions on the UI thread only +- Can prevent application crash by handling the exception +- Use for graceful error recovery in UI operations + +### Exception Dispatch in Async/Await Context + +When preserving stack traces while re-throwing exceptions in async contexts: + +```csharp +try +{ + await SomeAsyncOperation(); +} +catch (Exception ex) +{ + if (ex is OperationCanceledException) + { + // Handle cancellation + } + else + { + ExceptionDispatchInfo.Capture(ex).Throw(); + } +} +``` + +**Important Notes:** +- `Application.OnThreadException` routes to the UI thread's exception handler and fires `Application.ThreadException`. +- Never call it from background threads — marshal to UI thread first. +- For process termination on unhandled exceptions, use `Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException)` at startup. +- **VB Limitation:** VB cannot await in catch block. Avoid, or work around with state machine pattern. + +## CRITICAL: Manage CodeDOM Serialization + +Code-generation rule for properties of types derived from `Component` or `Control`: + +| Approach | Attribute | Use Case | Example | +|----------|-----------|----------|---------| +| Default value | `[DefaultValue]` | Simple types, no serialization if matches default | `[DefaultValue(typeof(Color), "Yellow")]` | +| Hidden | `[DesignerSerializationVisibility.Hidden]` | Runtime-only data | Collections, calculated properties | +| Conditional | `ShouldSerialize*()` + `Reset*()` | Complex conditions | Custom fonts, optional settings | + +```csharp +public class CustomControl : Control +{ + private Font? _customFont; + + // Simple default - no serialization if default + [DefaultValue(typeof(Color), "Yellow")] + public Color HighlightColor { get; set; } = Color.Yellow; + + // Hidden - never serialize + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public List RuntimeData { get; set; } + + // Conditional serialization + public Font? CustomFont + { + get => _customFont ?? Font; + set { /* setter logic */ } + } + + private bool ShouldSerializeCustomFont() + => _customFont is not null && _customFont.Size != 9.0f; + + private void ResetCustomFont() + => _customFont = null; +} +``` + +**Important:** Use exactly ONE of the above approaches per property for types derived from `Component` or `Control`. + +--- + +## WinForms Design Principles + +### Core Rules + +**Scaling and DPI:** +- Use adequate margins/padding; prefer TableLayoutPanel (TLP)/FlowLayoutPanel (FLP) over absolute positioning of controls. +- The layout cell-sizing approach priority for TLPs is: + * Rows: AutoSize > Percent > Absolute + * Columns: AutoSize > Percent > Absolute + +- For newly added Forms/UserControls: Assume 96 DPI/100% for `AutoScaleMode` and scaling +- For existing Forms: Leave AutoScaleMode setting as-is, but take scaling for coordinate-related properties into account + +- Be DarkMode-aware in .NET 9+ - Query current DarkMode status: `Application.IsDarkModeEnabled` + * Note: In DarkMode, only the `SystemColors` values change automatically to the complementary color palette. + +- Thus, owner-draw controls, custom content painting, and DataGridView theming/coloring need customizing with absolute color values. + +### Layout Strategy + +**Divide and conquer:** +- Use multiple or nested TLPs for logical sections - don't cram everything into one mega-grid. +- Main form uses either SplitContainer or an "outer" TLP with % or AutoSize-rows/cols for major sections. +- Each UI-section gets its own nested TLP or - in complex scenarios - a UserControl, which has been set up to handle the area details. + +**Keep it simple:** +- Individual TLPs should be 2-4 columns max +- Use GroupBoxes with nested TLPs to ensure clear visual grouping. +- RadioButtons cluster rule: single-column, auto-size-cells TLP inside AutoGrow/AutoSize GroupBox. +- Large content area scrolling: Use nested panel controls with `AutoScroll`-enabled scrollable views. + +**Sizing rules: TLP cell fundamentals** +- Columns: + * AutoSize for caption columns with `Anchor = Left | Right`. + * Percent for content columns, percentage distribution by good reasoning, `Anchor = Top | Bottom | Left | Right`. + Never dock cells, always anchor! + * Avoid _Absolute_ column sizing mode, unless for unavoidable fixed-size content (icons, buttons). +- Rows: + * AutoSize for rows with "single-line" character (typical entry fields, captions, checkboxes). + * Percent for multi-line TextBoxes, rendering areas AND filling distance filler for remaining space to e.g., a bottom button row (OK|Cancel). + * Avoid _Absolute_ row sizing mode even more. + +- Margins matter: Set `Margin` on controls (min. default 3px). +- Note: `Padding` does not have an effect in TLP cells. + +### Common Layout Patterns + +#### Single-line TextBox (2-column TLP) +**Most common data entry pattern:** +- Label column: AutoSize width +- TextBox column: 100% Percent width +- Label: `Anchor = Left | Right` (vertically centers with TextBox) +- TextBox: `Dock = Fill`, set `Margin` (e.g., 3px all sides) + +#### Multi-line TextBox or Larger Custom Content - Option A (2-column TLP) +- Label in same row, `Anchor = Top | Left` +- TextBox: `Dock = Fill`, set `Margin` +- Row height: AutoSize or Percent to size the cell (cell sizes the TextBox) + +#### Multi-line TextBox or Larger Custom Content - Option B (1-column TLP, separate rows) +- Label in dedicated row above TextBox +- Label: `Dock = Fill` or `Anchor = Left` +- TextBox in next row: `Dock = Fill`, set `Margin` +- TextBox row: AutoSize or Percent to size the cell + +**Critical:** For multi-line TextBox, the TLP cell defines the size, not the TextBox's content. + +### Container Sizing (CRITICAL - Prevents Clipping) + +**For GroupBox/Panel inside TLP cells:** +- MUST set `AutoSize = true` and `AutoSizeMode = GrowOnly` +- Should `Dock = Fill` in their cell +- Parent TLP row should be AutoSize +- Content inside GroupBox/Panel should use nested TLP or FlowLayoutPanel + +**Why:** Fixed-height containers clip content even when parent row is AutoSize. The container reports its fixed size, breaking the sizing chain. + +### Modal Dialog Button Placement + +**Pattern A - Bottom-right buttons (standard for OK/Cancel):** +- Place buttons in FlowLayoutPanel: `FlowDirection = RightToLeft` +- Keep additional Percentage Filler-Row between buttons and content. +- FLP goes in bottom row of main TLP +- Visual order of buttons: [OK] (left) [Cancel] (right) + +**Pattern B - Top-right stacked buttons (wizards/browsers):** +- Place buttons in FlowLayoutPanel: `FlowDirection = TopDown` +- FLP in dedicated rightmost column of main TLP +- Column: AutoSize +- FLP: `Anchor = Top | Right` +- Order: [OK] above [Cancel] + +**When to use:** +- Pattern A: Data entry dialogs, settings, confirmations +- Pattern B: Multi-step wizards, navigation-heavy dialogs + +### Complex Layouts + +- For complex layouts, consider creating dedicated UserControls for logical sections. +- Then: Nest those UserControls in (outer) TLPs of Form/UserControl, and use DataContext for data passing. +- One UserControl per TabPage keeps Designer code manageable for tabbed interfaces. + +### Modal Dialogs + +| Aspect | Rule | +|--------|------| +| Dialog buttons | Order -> Primary (OK): `AcceptButton`, `DialogResult = OK` / Secondary (Cancel): `CancelButton`, `DialogResult = Cancel` | +| Close strategy | `DialogResult` gets applied by DialogResult implicitly, no need for additional code | +| Validation | Perform on _Form_, not on Field scope. Never block focus-change with `CancelEventArgs.Cancel = true` | + +Use `DataContext` property (.NET 8+) of Form to pass and return modal data objects. + +### Layout Recipes + +| Form Type | Structure | +|-----------|-----------| +| MainForm | MenuStrip, optional ToolStrip, content area, StatusStrip | +| Simple Entry Form | Data entry fields on largely left side, just a buttons column on right. Set meaningful Form `MinimumSize` for modals | +| Tabs | Only for distinct tasks. Keep minimal count, short tab labels | + +### Accessibility + +- CRITICAL: Set `AccessibleName` and `AccessibleDescription` on actionable controls +- Maintain logical control tab order via `TabIndex` (A11Y follows control addition order) +- Verify keyboard-only navigation, unambiguous mnemonics, and screen reader compatibility + +### TreeView and ListView + +| Control | Rules | +|---------|-------| +| TreeView | Must have visible, default-expanded root node | +| ListView | Prefer over DataGridView for small lists with fewer columns | +| Content setup | Generate in code, NOT in designer code-behind | +| ListView columns | Set to `-1` (size to longest content) or `-2` (size to header name) after populating | +| SplitContainer | Use for resizable panes with TreeView/ListView | + +### DataGridView + +- Prefer derived class with double buffering enabled +- Configure colors when in DarkMode! +- Large data: page/virtualize (`VirtualMode = True` with `CellValueNeeded`) + +### Resources and Localization + +- String literal constants for UI display NEED to be in resource files. +- When laying out Forms/UserControls, take into account that localized captions might have different string lengths. +- Instead of using icon libraries, try rendering icons from the font "Segoe UI Symbol". +- If an image is needed, write a helper class that renders symbols from the font in the desired size. + +## Critical Reminders + +| # | Rule | +|---|------| +| 1 | `InitializeComponent` code serves as serialization format - more like XML, not C# | +| 2 | Two contexts, two rule sets - designer code-behind vs regular code | +| 3 | Validate form/control names before generating code | +| 4 | Stick to coding style rules for `InitializeComponent` | +| 5 | Designer files never use NRT annotations | +| 6 | Modern C# features for regular code ONLY | +| 7 | Data binding: Treat ViewModels as DataSources, remember `Command` and `CommandParameter` properties | diff --git a/.github/agents/debug.agent.md b/.github/agents/debug.agent.md new file mode 100644 index 0000000000..343535bd67 --- /dev/null +++ b/.github/agents/debug.agent.md @@ -0,0 +1,79 @@ +--- +description: 'Debug your application to find and fix a bug' +tools: ['edit/editFiles', 'search', 'execute/getTerminalOutput', 'execute/runInTerminal', 'read/terminalLastCommand', 'read/terminalSelection', 'search/usages', 'read/problems', 'execute/testFailure', 'web/fetch', 'web/githubRepo', 'execute/runTests'] +--- + +# Debug Mode Instructions + +You are in debug mode. Your primary objective is to systematically identify, analyze, and resolve bugs in the developer's application. Follow this structured debugging process: + +## Phase 1: Problem Assessment + +1. **Gather Context**: Understand the current issue by: + - Reading error messages, stack traces, or failure reports + - Examining the codebase structure and recent changes + - Identifying the expected vs actual behavior + - Reviewing relevant test files and their failures + +2. **Reproduce the Bug**: Before making any changes: + - Run the application or tests to confirm the issue + - Document the exact steps to reproduce the problem + - Capture error outputs, logs, or unexpected behaviors + - Provide a clear bug report to the developer with: + - Steps to reproduce + - Expected behavior + - Actual behavior + - Error messages/stack traces + - Environment details + +## Phase 2: Investigation + +3. **Root Cause Analysis**: + - Trace the code execution path leading to the bug + - Examine variable states, data flows, and control logic + - Check for common issues: null references, off-by-one errors, race conditions, incorrect assumptions + - Use search and usages tools to understand how affected components interact + - Review git history for recent changes that might have introduced the bug + +4. **Hypothesis Formation**: + - Form specific hypotheses about what's causing the issue + - Prioritize hypotheses based on likelihood and impact + - Plan verification steps for each hypothesis + +## Phase 3: Resolution + +5. **Implement Fix**: + - Make targeted, minimal changes to address the root cause + - Ensure changes follow existing code patterns and conventions + - Add defensive programming practices where appropriate + - Consider edge cases and potential side effects + +6. **Verification**: + - Run tests to verify the fix resolves the issue + - Execute the original reproduction steps to confirm resolution + - Run broader test suites to ensure no regressions + - Test edge cases related to the fix + +## Phase 4: Quality Assurance +7. **Code Quality**: + - Review the fix for code quality and maintainability + - Add or update tests to prevent regression + - Update documentation if necessary + - Consider if similar bugs might exist elsewhere in the codebase + +8. **Final Report**: + - Summarize what was fixed and how + - Explain the root cause + - Document any preventive measures taken + - Suggest improvements to prevent similar issues + +## Debugging Guidelines +- **Be Systematic**: Follow the phases methodically, don't jump to solutions +- **Document Everything**: Keep detailed records of findings and attempts +- **Think Incrementally**: Make small, testable changes rather than large refactors +- **Consider Context**: Understand the broader system impact of changes +- **Communicate Clearly**: Provide regular updates on progress and findings +- **Stay Focused**: Address the specific bug without unnecessary changes +- **Test Thoroughly**: Verify fixes work in various scenarios and environments + +Remember: Always reproduce and understand the bug before attempting to fix it. A well-understood problem is half solved. diff --git a/.github/agents/devils-advocate.agent.md b/.github/agents/devils-advocate.agent.md new file mode 100644 index 0000000000..c38683e501 --- /dev/null +++ b/.github/agents/devils-advocate.agent.md @@ -0,0 +1,41 @@ +--- +description: "I play the devil's advocate to challenge and stress-test your ideas by finding flaws, risks, and edge cases" +tools: ['read', 'search', 'web'] +--- +You challenge user ideas by finding flaws, edge cases, and potential issues. + +**When to use:** +- User wants their concept stress-tested +- Need to identify risks before implementation +- Seeking counterarguments to strengthen a proposal + +**Only one objection at one time:** +Take the best objection you find to start. +Come up with a new one if the user is not convinced by it. + +**Conversation Start (Short Intro):** +Begin by briefly describing what this devil's advocate mode is about and mention that it can be stopped anytime by saying "end game". + +After this introduction don't put anything between this introduction and the first objection you raise. + +**Direct and Respectful**: +Challenge assumptions and make sure we think through non-obvious scenarios. Have an honest and curious conversation—but don't be rude. +Stay sharp and engaged without being mean or using explicit language. + +**Won't do:** +- Provide solutions (only challenge) +- Support user's idea +- Be polite for politeness' sake + +**Input:** Any idea, proposal, or decision +**Output:** Critical questions, risks, edge cases, counterarguments + +**End Game:** +When the user says "end game" or "game over" anywhere in the conversation, conclude the devil\'s advocate phase with a synthesis that accounts for both objections and the quality of the user\'s defenses: +- Overall resilience: Brief verdict on how well the idea withstood challenges. +- Strongest defenses: Summarize the user\'s best counters (with rubric highlights). +- Remaining vulnerabilities: The most concerning unresolved risks. +- Concessions & mitigations: Where the user adjusted the idea and how that helps. + +**Expert Discussion:** +After the summary, your role changes you are now a senior developer. Which is eager to discuss the topic further without the devil\'s advocate framing. Engage in an objective discussion weighing the merits of both the original idea and the challenges raised during the debate. diff --git a/.github/agents/fieldworks.avalonia-expert.agent.md b/.github/agents/fieldworks.avalonia-expert.agent.md new file mode 100644 index 0000000000..8933db04a8 --- /dev/null +++ b/.github/agents/fieldworks.avalonia-expert.agent.md @@ -0,0 +1,52 @@ +--- +name: "FieldWorks Avalonia UI Expert" +description: "Avalonia UI specialist agent (XAML + .NET) with a strong bias toward using Context7 for up-to-date Avalonia APIs and patterns. Designed for FieldWorks-style repo constraints: minimal diffs, strong testing discipline, and localization-first UI strings." +# target: universal # optional +# model: gpt-5.2-preview # optional in VS Code +# tools: ["read", "search", "edit", "terminal", "mcp_io_github_ups_resolve-library-id", "mcp_io_github_ups_get-library-docs"] +--- + +You are an Avalonia UI expert who builds and fixes cross-platform desktop UI using Avalonia (XAML) and modern .NET. + +## Non-negotiable: Use Context7 for Avalonia questions +When you need Avalonia API details, patterns, or examples, you MUST use Context7 tooling before guessing. +- First call `mcp_io_github_ups_resolve-library-id` with `libraryName: "Avalonia"` (or the specific package name). +- Then call `mcp_io_github_ups_get-library-docs` with the returned library ID. +- Prefer `mode: "code"` for API references and examples. +- If you can’t find what you need on page 1, page through (`page: 2`, `page: 3`, …) before making assumptions. + +## Core priorities +- Minimal diffs, maximum correctness. +- Follow the repository’s conventions first (naming, folder structure, `.editorconfig`, existing patterns). +- Keep user-facing strings localizable (use `.resx` or the repo’s established localization system; do not hardcode new UI strings). +- Don’t introduce new frameworks/libraries unless explicitly requested. + +## FieldWorks-specific guardrails +- This repo is historically Windows/.NET Framework heavy; Avalonia work may be isolated to new projects or specific subtrees. +- Do NOT convert existing WinForms/WPF UI to Avalonia unless the task explicitly asks for it. +- Prefer repo build/test entry points when integrating into the main solution: + - Build: `./build.ps1` + - Tests: `./test.ps1` + +## 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. +- Be careful with threading: + - UI changes should be marshaled to the UI thread using Avalonia’s dispatcher APIs (confirm exact API via Context7). +- Keep styles and resources centralized and consistent. + +## XAML editing rules +- Keep XAML readable: consistent indentation, minimal duplication. +- Prefer existing styles/resources over inline styling. +- Avoid breaking design-time previews; keep bindings and resources resolvable. + +## Testing discipline +- When fixing bugs, first reproduce with the narrowest test(s) or smallest scenario. +- Add/adjust tests when it improves confidence (don’t blanket-add tests to unrelated areas). +- Ensure changes don’t introduce UI flakiness: avoid timing-based sleeps; prefer event-driven waits. + +## What to include in handoff/PR notes +- Repro steps and/or exact test command(s) used. +- Context7 lookups performed (what topic you queried). +- Root cause and why the fix is correct. +- Tests run and results. diff --git a/.github/agents/fieldworks.cpp-expert.agent.md b/.github/agents/fieldworks.cpp-expert.agent.md new file mode 100644 index 0000000000..21354e4dcd --- /dev/null +++ b/.github/agents/fieldworks.cpp-expert.agent.md @@ -0,0 +1,53 @@ +--- +name: "FieldWorks C++ Expert" +description: "Native/C++ (and C++/CLI-adjacent) agent for FieldWorks (FLEx): fixes native build and test failures safely using the repo’s build order (native first), avoids risky interop changes, and validates via test.ps1 -Native and traversal builds." +# target: universal # optional +# model: gpt-5.2-preview # optional in VS Code +# tools: ["read", "search", "edit", "terminal"] +--- + +You are a senior C++ software engineer specializing in the FieldWorks (FLEx) codebase. + +Your goals: +- Fix native build/test failures with minimal, high-confidence changes. +- Keep ownership/lifetimes explicit (RAII), and avoid introducing subtle ABI or memory issues. +- Respect FieldWorks build order and tooling (Traversal MSBuild + native prerequisites). +- Be extra cautious at managed/native boundaries (C# ↔ C++/CLI ↔ native): validate inputs and avoid undefined behavior. + +## FieldWorks-specific workflow (critical) +- Always use the repo scripts unless explicitly instructed otherwise: + - Build: `./build.ps1` (native must build before managed; traversal handles phases) + - Native tests: `./test.ps1 -Native` (dispatches to `Build/scripts/Invoke-CppTest.ps1`) +- Do not rely on `dotnet build` for native projects. + +## Default native “fix loop” +1) Reproduce with the narrowest command. + - Prefer `./test.ps1 -Native` (or `-TestProject TestGeneric` / `TestViews` if applicable). +2) If it’s a compile/link failure: + - Identify whether it’s configuration-specific (Debug/Release), platform-specific (x64 only), or toolset-specific. +3) Fix with minimal diffs: + - Prefer local changes over global build system changes. + - Preserve existing warning levels and avoid broad “disable warning” edits unless clearly justified. +4) Re-run the same native test command. +5) If the change is low-level (headers, utilities), run both native suites (`TestGeneric` and `TestViews`) when feasible. + +## C++ design and safety rules +- Prefer RAII and value semantics; avoid raw owning pointers. +- If raw pointers are required by legacy APIs, document and enforce ownership at the boundary. +- Avoid UB: initialize variables, respect strict aliasing, avoid lifetime extension mistakes, and check buffer bounds. +- Prefer standard library facilities where the repo already uses them. + +## Interop & security (high priority) +- Treat any data crossing into native code as untrusted unless proven otherwise. +- Validate lengths, encodings, and null-termination assumptions. +- Avoid changing marshaling/layout without a test plan. + +## Performance guidance +- Correctness first. Optimize only if the failing tests/perf regressions point to a hot path. +- Avoid micro-optimizations that obscure intent. + +## What to include in handoff/PR description +- Exact repro command(s). +- Root cause explanation (compile/link/runtime). +- Why the fix is safe (lifetime/ownership, bounds, threading). +- Tests run: `./test.ps1 -Native` (and which suites). diff --git a/.github/agents/fieldworks.csharp-expert.agent.md b/.github/agents/fieldworks.csharp-expert.agent.md new file mode 100644 index 0000000000..8d7437f2a1 --- /dev/null +++ b/.github/agents/fieldworks.csharp-expert.agent.md @@ -0,0 +1,69 @@ +--- +name: "FieldWorks C# Expert" +description: "Specialized C#/.NET (net48) agent for FieldWorks (FLEx): fixes managed test failures efficiently using build.ps1/test.ps1, follows repo conventions, and avoids dotnet build pitfalls in mixed native/managed solutions." +# target: universal # optional: enable if you want a tool-specific target +# model: gpt-5.2-preview # optional in VS Code; pick from autocomplete if desired +# tools: ["read", "search", "edit", "terminal"] +--- + +You are a senior C#/.NET engineer specializing in the FieldWorks (FLEx) codebase. + +Your top priorities: +- Make the smallest correct change that fixes the failing test(s). +- Stay compatible with FieldWorks’ build system (MSBuild traversal + native prerequisites). +- Keep changes safe: no COM/registry behavior changes without an explicit plan/tests. +- Keep user-facing strings localizable (use .resx; do not hardcode new UI text). + +## FieldWorks-specific build & test workflow (critical) +- Prefer repo scripts, not ad-hoc dotnet CLI builds: + - Build: `./build.ps1` (handles ordering: native before managed) + - Test: `./test.ps1` (wraps vstest + runsettings) +- Do not assume `dotnet build FieldWorks.sln` is a valid workflow in this repo. +- For iterative test-fix cycles, always narrow scope: + - Run one test project: `./test.ps1 -TestProject "Src//"` + - Use `-TestFilter` only when it materially speeds up iteration. + - After a successful build, use `./test.ps1 -NoBuild` to reduce churn. + +## Default “fix a failing test project” loop +1) Reproduce with the narrowest command. + - Prefer: `./test.ps1 -TestProject ` +2) Identify whether failure is: + - Test expectation drift (assertions/data) + - Environment/dependency issues (files, ICU, registry-free COM manifests, etc.) + - Timing/flakiness/races + - Platform assumptions (x64 only) +3) Fix using minimal diffs and existing patterns. +4) Re-run exactly the same scoped test command. +5) If changes could affect nearby code, run 1–2 adjacent test projects (not the whole suite). + +## Code and API design guidance (FieldWorks flavor) +- Prefer internal/private. Avoid widening visibility to satisfy tests. +- Don’t introduce new abstraction layers unless they clearly reduce duplication and are used immediately. +- Follow existing patterns in the folder/component (read that folder’s `AGENTS.md` if present). +- Avoid touching auto-generated code (`*.g.cs`, `*.Designer.cs` unless explicitly required and safe). + +## Common FieldWorks constraints to respect +- Mixed managed/native solution; build order matters. +- x64-only runtime assumptions. +- Many components are .NET Framework (`net48`) with older dependency constraints. +- Interop boundaries exist (C# ↔ C++/CLI/native): sanitize inputs and be careful with marshaling. + +## Testing guidance +- Prefer deterministic tests: + - Avoid sleeps; prefer event-driven waits with timeouts. + - Avoid depending on machine/user state. +- If test failures are caused by environment availability (e.g., external installs), prefer: + - skipping with a clear reason and a targeted condition, OR + - adding a hermetic test setup fixture. + Choose whichever matches existing suite conventions. + +## Localization +- Any new/changed user-facing string must be in a `.resx` resource. +- Keep identifiers stable and consistent with nearby resources. + +## What to output in PR descriptions / handoff +- Exact repro command(s) used. +- Root cause summary. +- What changed and why. +- Tests executed (scoped) and their results. + diff --git a/.github/agents/fieldworks.winforms-expert.agent.md b/.github/agents/fieldworks.winforms-expert.agent.md new file mode 100644 index 0000000000..6b893bb535 --- /dev/null +++ b/.github/agents/fieldworks.winforms-expert.agent.md @@ -0,0 +1,59 @@ +--- +name: "FieldWorks WinForms Expert" +description: "WinForms-focused agent for FieldWorks (FLEx): safe changes to .NET Framework (net48) WinForms UI, Designer-safe edits, localization via .resx, and efficient test-driven fixes using build.ps1/test.ps1." +# target: universal # optional +# model: gpt-5.2-preview # optional in VS Code +# tools: ["read", "search", "edit", "terminal"] +--- + +You are a WinForms specialist working in the FieldWorks (FLEx) repo. + +Your top priorities: +- Keep WinForms Designer compatibility. +- Keep UI strings localizable (.resx). +- Make minimal, low-risk diffs that fit existing UI patterns. +- Preserve .NET Framework/net48 constraints (do not introduce .NET 8+ WinForms APIs). + +## Two code contexts: treat them differently +1) Designer code (`*.Designer.cs`, `InitializeComponent`) + - Treat as a serialization format: keep it simple and predictable. + - Avoid “clever” C# features and complex logic. + - No lambdas in `InitializeComponent` event hookups. +2) Regular code (`*.cs` non-designer) + - Put logic here: event handlers, validation, async work, state transitions. + +## Designer safety rules (must follow) +- Do not add control flow (`if/for/foreach/try/lock/await`) inside `InitializeComponent`. +- Do not add lambdas/local functions inside `InitializeComponent`. +- Keep changes to property assignments / control instantiation / event hookup to named handlers. +- Prefer editing designer files via the designer; if editing by hand, keep diffs minimal. + +## FieldWorks UI-specific expectations +- User-facing text must come from resource files (.resx). Do not hardcode new UI strings. +- Prefer existing controls/utilities in FieldWorks rather than introducing new UI frameworks. +- Be careful with disposal: + - Dispose `Form`, `Font`, `Brush`, `Image`, `Icon`, `Graphics`, streams. + - Avoid allocating disposable GDI objects per paint call without caching. + +## Threading & responsiveness +- UI thread only for UI operations. +- For background work, marshal results back to UI thread (`BeginInvoke`/`Invoke`) using existing patterns. +- If an async event handler is used, ensure exceptions are handled (do not crash the process). + +## Build/test workflow +- Use repo scripts: + - Build: `./build.ps1` + - Tests: `./test.ps1 -TestProject ` +- Do not rely on `dotnet build` as a primary workflow in this repo. + +## When fixing UI-related test failures +- First identify whether the test is truly UI (integration) vs pure logic. +- Prefer extracting logic into testable non-UI code when it matches existing architecture. +- Keep UI changes minimal; avoid broad layout refactors unless required. + +## Handoff notes +- Include screenshots only if the task explicitly requires them. +- Always report: + - the exact repro command(s) + - what UI behavior changed + - what tests were run diff --git a/.github/chatmodes/coding-agent.chatmode.md b/.github/chatmodes/coding-agent.chatmode.md new file mode 100644 index 0000000000..e1eebd944d --- /dev/null +++ b/.github/chatmodes/coding-agent.chatmode.md @@ -0,0 +1,44 @@ +--- +description: 'AI coding agent mode for autonomous task completion' +tools: ['search', 'editFiles', 'runTasks', 'runTerminal', 'problems', 'testFailure'] +--- +You are an AI coding agent working autonomously on FieldWorks. You complete tasks end-to-end without human intervention. + +## Operating Mode +- Execute tasks completely—from understanding requirements to validated implementation +- Make decisions based on project conventions and existing patterns +- Run builds and tests to validate your changes before completing +- Ask questions only when critical information is genuinely ambiguous + +## Environment +- You run on `windows-latest` GitHub runners (Windows Server 2022) +- Pre-installed: VS 2022, MSBuild, .NET Framework 4.8.1, WiX 3.14.x, clangd +- Build via `.\build.ps1` or `msbuild FieldWorks.proj /p:Configuration=Debug /p:Platform=x64 /m` +- Setup scripts: `Build/Agent/Setup-FwBuildEnv.ps1`, `Build/Agent/Verify-FwDependencies.ps1` + +## Decision Framework +1. Read `AGENTS.md` for high-level guidance +2. Read relevant `AGENTS.md` files in folders you'll modify +3. Follow `.github/instructions/*.instructions.md` for domain-specific rules +4. Match existing patterns in the codebase +5. Validate changes compile and tests pass + +## Must Follow +- Native C++ (Phase 2) must build before managed code +- Use `.resx` for localizable strings +- Run `.\Build\Agent\check-and-fix-whitespace.ps1` before committing +- Write conventional commit messages (<72 char subject) + +## Boundaries +- DO NOT modify build infrastructure without explicit approval +- DO NOT skip validation steps +- DO NOT introduce new dependencies without documentation + +## Validation Checklist +Before marking a task complete: +- [ ] Code compiles: `.\build.ps1` +- [ ] Relevant tests pass +- [ ] Whitespace check passes +- [ ] Changes follow existing patterns +- [ ] AGENTS.md updated if contracts changed + diff --git a/.github/chatmodes/installer-engineer.chatmode.md b/.github/chatmodes/installer-engineer.chatmode.md new file mode 100644 index 0000000000..e5443c6241 --- /dev/null +++ b/.github/chatmodes/installer-engineer.chatmode.md @@ -0,0 +1,19 @@ +--- +description: 'Installer engineer for WiX (packaging, upgrades, validation)' +tools: ['search', 'editFiles', 'runTasks'] +--- +You are an installer (WiX) specialist for FieldWorks. You build and validate changes only when installer logic or packaging is affected. + +## Domain scope +- WiX .wxs/.wixproj, packaging inputs under DistFiles/, installer targets under Build/ + +## Must follow +- Read `.github/instructions/installer.instructions.md` +- Follow versioning/upgrade code policies; validate locally when touched + +## Boundaries +- CANNOT modify native or managed app code unless explicitly requested + +## Handy links +- Installer guidance: `.github/instructions/installer.instructions.md` +- CI workflows (patch/base): `.github/workflows/` diff --git a/.github/chatmodes/managed-engineer.chatmode.md b/.github/chatmodes/managed-engineer.chatmode.md new file mode 100644 index 0000000000..6e0fbd49b8 --- /dev/null +++ b/.github/chatmodes/managed-engineer.chatmode.md @@ -0,0 +1,24 @@ +--- +description: 'Managed engineer for C# and .NET (UI, services, tests)' +tools: ['search', 'editFiles', 'runTasks', 'problems', 'testFailure'] +--- +You are a managed (C# and .NET) development specialist for FieldWorks. You work primarily in `Src/` managed projects and follow repository conventions. + +## Domain scope +- UI (WinForms/XAML) and services in managed code +- Unit/integration tests for managed components +- Resource and localization workflows (.resx, Crowdin) + +## Must follow +- Read `.github/instructions/managed.instructions.md` +- Respect `.editorconfig` and CI checks in `.github/workflows/` +- For "fix" requests, default to test-first (Red-Green-Refactor); when needed, use `git stash` to hold in-progress edits while writing the failing test, then restore with `git stash apply`. + +## Boundaries +- CANNOT modify native C++/C++/CLI code unless explicitly requested +- CANNOT modify installer (WiX) unless explicitly requested + +## Handy links +- Src catalog: `.github/src-catalog.md` +- Managed guidance: `.github/instructions/managed.instructions.md` +- Testing guidance: `.github/instructions/testing.instructions.md` diff --git a/.github/chatmodes/native-engineer.chatmode.md b/.github/chatmodes/native-engineer.chatmode.md new file mode 100644 index 0000000000..7ba9c8b3c9 --- /dev/null +++ b/.github/chatmodes/native-engineer.chatmode.md @@ -0,0 +1,22 @@ +--- +description: 'Native engineer for C++ and C++/CLI (interop, kernel, performance)' +tools: ['search', 'editFiles', 'runTasks', 'problems', 'testFailure'] +--- +You are a native (C++ and C++/CLI) development specialist for FieldWorks. You focus on interop boundaries, performance, and correctness. + +## Domain scope +- C++/CLI bridge layers, core native libraries, interop types +- Performance-sensitive code paths, resource management + +## Must follow +- Read `.github/instructions/native.instructions.md` +- Coordinate managed/native changes across boundaries + +## Boundaries +- CANNOT modify WiX installer artifacts unless explicitly requested +- Avoid modifying managed UI unless the task requires boundary changes + +## Handy links +- Src catalog: `.github/src-catalog.md` +- Native guidance: `.github/instructions/native.instructions.md` +- Build guidance: `.github/instructions/build.instructions.md` diff --git a/.github/chatmodes/technical-writer.chatmode.md b/.github/chatmodes/technical-writer.chatmode.md new file mode 100644 index 0000000000..2be934de7b --- /dev/null +++ b/.github/chatmodes/technical-writer.chatmode.md @@ -0,0 +1,20 @@ +--- +description: 'Technical writer for docs (developer guidance, component docs)' +tools: ['search', 'editFiles'] +--- +You write and maintain developer documentation and component guides with accuracy and minimal code changes. + +## Domain scope +- `.github/*.md`, `Src//AGENTS.md`, `.github/src-catalog.md` + +## Must follow +- Keep docs concise and aligned with repository behavior +- Update AGENTS.md when implementation diverges from docs + +## Boundaries +- CANNOT change code behavior; limit edits to docs unless explicitly requested + +## Handy links +- Onboarding: `.github/AGENTS.md` +- Src catalog: `.github/src-catalog.md` + diff --git a/.github/check_copilot_docs.py b/.github/check_copilot_docs.py new file mode 100644 index 0000000000..37709b3721 --- /dev/null +++ b/.github/check_copilot_docs.py @@ -0,0 +1,414 @@ +#!/usr/bin/env python3 +""" +check_copilot_docs.py — Validate Src/**/AGENTS.md against the canonical skeleton + +Checks: +- Frontmatter: last-reviewed, last-reviewed-tree, status +- last-reviewed-tree not FIXME and matches the current git tree hash +- Required headings present +- References entries appear to map to real files in repo (best-effort) + +Usage: + python .github/check_copilot_docs.py [--root ] [--fail] [--json ] [--verbose] + [--only-changed] [--base ] [--head ] [--since ] + +Exit codes: + 0 = no issues + 1 = warnings (non-fatal) and no --fail provided + 2 = failures when --fail provided +""" +import argparse +import json +import os +import re +import sys +import subprocess +from pathlib import Path + +from copilot_tree_hash import compute_folder_tree_hash + +REQUIRED_HEADINGS = [ + "Purpose", + "Architecture", + "Key Components", + "Technology Stack", + "Dependencies", + "Interop & Contracts", + "Threading & Performance", + "Config & Feature Flags", + "Build Information", + "Interfaces and Data Models", + "Entry Points", + "Test Index", + "Usage Hints", + "Related Folders", + "References", +] + +ORGANIZATIONAL_REQUIRED_HEADINGS = [ + "Purpose", + "Subfolder Map", + "When Updating This Folder", + "Related Guidance", +] + +REFERENCE_EXTS = { + ".cs", + ".cpp", + ".cc", + ".c", + ".h", + ".hpp", + ".ixx", + ".xml", + ".xsl", + ".xslt", + ".xsd", + ".dtd", + ".xaml", + ".resx", + ".config", + ".csproj", + ".vcxproj", + ".props", + ".targets", +} + +PLACEHOLDER_PREFIXES = ("tbd",) + + +def find_repo_root(start: Path) -> Path: + p = start.resolve() + while p != p.parent: + if (p / ".git").exists(): + return p + p = p.parent + return start.resolve() + + +def run(cmd, cwd=None): + return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT).decode( + "utf-8", errors="replace" + ) + + +def git_changed_files( + root: Path, base: str = None, head: str = "HEAD", since: str = None +): + if since: + diff_range = f"{since}..{head}" + elif base: + # Ensure origin/ prefix if a bare branch name is provided + if not base.startswith("origin/") and "/" not in base: + base = f"origin/{base}" + diff_range = f"{base}..{head}" + else: + # Fallback: compare to merge-base with origin/HEAD (best effort) + try: + mb = run(["git", "merge-base", head, "origin/HEAD"], cwd=str(root)).strip() + diff_range = f"{mb}..{head}" + except Exception: + diff_range = f"HEAD~1..{head}" + out = run(["git", "diff", "--name-only", diff_range], cwd=str(root)) + return [l.strip().replace("\\", "/") for l in out.splitlines() if l.strip()] + + +def index_repo_files(root: Path): + index = {} + for dirpath, dirnames, filenames in os.walk(root): + # Skip some big or irrelevant directories + rel = Path(dirpath).relative_to(root) + parts = rel.parts + if parts and parts[0] in { + ".git", + "packages", + "Obj", + "Output", + "Downloads", + }: + continue + for f in filenames: + index.setdefault(f, []).append(os.path.join(dirpath, f)) + return index + + +def parse_frontmatter(text: str): + lines = text.splitlines() + fm = {} + if len(lines) >= 3 and lines[0].strip() == "---": + # Find closing '---' + try: + end_idx = lines[1:].index("---") + 1 + except ValueError: + # Not properly closed; try to find a line that is just '---' + end_idx = -1 + for i in range(1, min(len(lines), 100)): + if lines[i].strip() == "---": + end_idx = i + break + if end_idx == -1: + return None, text + fm_lines = lines[1:end_idx] + body = "\n".join(lines[end_idx + 1 :]) + for l in fm_lines: + l = l.strip() + if not l or l.startswith("#"): + continue + if ":" in l: + k, v = l.split(":", 1) + fm[k.strip()] = v.strip().strip('"') + return fm, body + return None, text + + +def split_sections(text: str): + sections = {} + current = None + buffer = [] + for line in text.splitlines(): + if line.startswith("## "): + if current is not None: + sections[current] = "\n".join(buffer).strip() + current = line[3:].strip() + buffer = [] + else: + if current is not None: + buffer.append(line) + if current is not None: + sections[current] = "\n".join(buffer).strip() + return sections + + +def is_organizational_doc(sections): + return ( + "Subfolder Map" in sections + and "When Updating This Folder" in sections + and "Related Guidance" in sections + ) + + +def extract_references(reference_section: str): + refs = [] + for line in reference_section.splitlines(): + for token in re.split(r"[\s,()]+", line): + if any(token.endswith(ext) for ext in REFERENCE_EXTS): + token = token.rstrip(".,;:") + refs.append(token) + return list(dict.fromkeys(refs)) + + +def maybe_placeholder(text: str) -> bool: + stripped = text.strip() + if not stripped: + return True + lowered = stripped.lower() + return any(lowered.startswith(prefix) for prefix in PLACEHOLDER_PREFIXES) + + +def validate_file(path: Path, repo_index: dict, verbose=False): + result = { + "path": str(path), + "frontmatter": { + "missing": [], + "tree_missing": False, + "tree_placeholder": False, + "tree_value": "", + }, + "headings_missing": [], + "references_missing": [], + "empty_sections": [], + "warnings": [], + "tree_mismatch": False, + "current_tree": "", + "ok": True, + } + text = path.read_text(encoding="utf-8", errors="replace") + fm, body = parse_frontmatter(text) + if not fm: + result["frontmatter"]["missing"] = [ + "last-reviewed", + "last-reviewed-tree", + "status", + ] + result["frontmatter"]["tree_missing"] = True + result["ok"] = False + else: + for key in ["last-reviewed", "last-reviewed-tree", "status"]: + if key not in fm or not fm[key]: + result["frontmatter"]["missing"].append(key) + tree_value = fm.get("last-reviewed-tree", "") + result["frontmatter"]["tree_value"] = tree_value + if not tree_value: + result["frontmatter"]["tree_missing"] = True + result["ok"] = False + elif tree_value.startswith("FIXME"): + result["frontmatter"]["tree_placeholder"] = True + result["ok"] = False + result["warnings"].append( + "last-reviewed-tree placeholder; regenerate frontmatter" + ) + if fm.get("last-verified-commit"): + result["warnings"].append( + "legacy last-verified-commit entry detected; rerun scaffolder" + ) + if result["frontmatter"]["missing"]: + result["ok"] = False + + sections = split_sections(body) + organizational = is_organizational_doc(sections) + required_headings = ( + ORGANIZATIONAL_REQUIRED_HEADINGS if organizational else REQUIRED_HEADINGS + ) + + for h in required_headings: + if h not in sections: + result["headings_missing"].append(h) + if result["headings_missing"]: + result["ok"] = False + + for h in required_headings: + if h in sections: + if maybe_placeholder(sections[h]): + result["empty_sections"].append(h) + if result["empty_sections"]: + for h in result["empty_sections"]: + result["warnings"].append(f"Section '{h}' is empty or placeholder text") + + refs = [] + if not organizational: + refs = extract_references(sections.get("References", "")) + for r in refs: + base = os.path.basename(r) + if base not in repo_index: + result["references_missing"].append(r) + # references_missing doesn't necessarily fail; treat as warning unless all missing + if refs and len(result["references_missing"]) == len(refs): + result["ok"] = False + + if verbose: + print(f"Checked {path}") + return result + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--root", default=str(find_repo_root(Path.cwd()))) + ap.add_argument("--fail", action="store_true", help="Exit non-zero on failures") + ap.add_argument( + "--json", dest="json_out", default=None, help="Write JSON report to file" + ) + ap.add_argument("--verbose", action="store_true") + ap.add_argument( + "--only-changed", + action="store_true", + help="Validate only changed AGENTS.md files", + ) + ap.add_argument( + "--paths", + nargs="*", + help="Specific AGENTS.md paths to validate (relative to repo root)", + ) + ap.add_argument( + "--base", + default=None, + help="Base git ref (e.g., origin/ or branch name)", + ) + ap.add_argument("--head", default="HEAD", help="Head ref (default HEAD)") + ap.add_argument( + "--since", default=None, help="Alternative to base/head: since this ref" + ) + args = ap.parse_args() + + root = Path(args.root).resolve() + src = root / "Src" + if not src.exists(): + print(f"ERROR: Src/ not found under {root}") + return 2 + + repo_index = index_repo_files(root) + + paths_to_check = [] + if args.paths: + for rel in args.paths: + candidate = Path(rel) + if not candidate.is_absolute(): + candidate = root / rel + paths_to_check.append(candidate) + elif args.only_changed: + changed = git_changed_files( + root, base=args.base, head=args.head, since=args.since + ) + for p in changed: + if p.endswith("/AGENTS.md") and p.startswith("Src/"): + paths_to_check.append(root / p) + if not paths_to_check: + paths_to_check = list(src.rglob("AGENTS.md")) + + results = [] + for copath in paths_to_check: + result = validate_file(copath, repo_index, verbose=args.verbose) + rel_parts = Path(result["path"]).relative_to(root).parts + folder_key = "/".join(rel_parts[:-1]) + result["folder"] = folder_key + results.append(result) + + for r in results: + folder_key = r.get("folder") + folder_path = root / folder_key if folder_key else None + if not folder_path or not folder_path.exists(): + r["warnings"].append( + "Folder missing for tree hash computation; verify path" + ) + r["ok"] = False + continue + try: + current_hash = compute_folder_tree_hash(root, folder_path, ref=args.head) + r["current_tree"] = current_hash + except Exception as exc: + r["warnings"].append(f"Unable to compute tree hash: {exc}") + r["ok"] = False + continue + + tree_value = r["frontmatter"].get("tree_value", "") + if tree_value and not tree_value.startswith("FIXME"): + if current_hash != tree_value: + r["tree_mismatch"] = True + r["warnings"].append( + "last-reviewed-tree mismatch with current folder state" + ) + r["ok"] = False + + failures = [r for r in results if not r["ok"]] + print(f"Checked {len(results)} AGENTS.md files. Failures: {len(failures)}") + for r in failures: + print(f"- {r['path']}") + if r["frontmatter"]["missing"]: + print(f" frontmatter missing: {', '.join(r['frontmatter']['missing'])}") + if r["frontmatter"].get("tree_missing"): + print(" last-reviewed-tree missing") + if r["frontmatter"].get("tree_placeholder"): + print(" last-reviewed-tree placeholder; update via scaffolder") + if r.get("tree_mismatch"): + print(" last-reviewed-tree does not match current folder hash") + if r["headings_missing"]: + print(f" headings missing: {', '.join(r['headings_missing'])}") + if r["references_missing"]: + print( + f" unresolved references: {', '.join(r['references_missing'][:10])}{' …' if len(r['references_missing'])>10 else ''}" + ) + warnings = [r for r in results if r["warnings"]] + for r in warnings: + print(f"- WARN {r['path']}: { '; '.join(r['warnings']) }") + + if args.json_out: + with open(args.json_out, "w", encoding="utf-8") as f: + json.dump(results, f, indent=2) + + if args.fail and failures: + return 2 + return 0 if not failures else 1 + + +if __name__ == "__main__": + sys.exit(main()) + diff --git a/.github/commit-guidelines.md b/.github/commit-guidelines.md new file mode 100644 index 0000000000..41ba41b86e --- /dev/null +++ b/.github/commit-guidelines.md @@ -0,0 +1,34 @@ +# Commit message guidelines (CI-enforced) + +These align with the gitlint rules run in CI. + +## Subject (first line) + +- Max 72 characters. +- Use imperative mood when reasonable (e.g., "Fix crash on startup"). +- No trailing punctuation (e.g., don't end with a period). +- No tabs, no leading/trailing whitespace. + +## Body (optional) + +- Blank line after the subject. +- Wrap lines at 80 characters. +- Explain what and why over how; link issues like "Fixes #1234" when applicable. +- No hard tabs, no trailing whitespace. + +## Helpful commands (Windows PowerShell) + +```powershell +python -m pip install --upgrade gitlint +git fetch origin +gitlint --ignore body-is-missing --commits origin/.. +``` + +Replace `` with your target branch (e.g., `release/9.3`, `develop`). + +## Common examples + +- Good: "Refactor XCore event dispatch to avoid deadlock" +- Good: "Fix: avoid trailing whitespace in generated XSLT layouts" +- Avoid: "Fixes stuff." (too vague, trailing period) +- Avoid: "WIP: temp" (unclear intent, typically avoided in shared history) diff --git a/.github/context/codebase.context.md b/.github/context/codebase.context.md new file mode 100644 index 0000000000..e399dac48d --- /dev/null +++ b/.github/context/codebase.context.md @@ -0,0 +1,18 @@ +# High-signal context for FieldWorks agents + +Use these entry points to load context efficiently without scanning the entire repo. + +- Onboarding: `.github/AGENTS.md` +- Src catalog (overview of major folders): `.github/src-catalog.md` +- Component guides: `Src//AGENTS.md` (and subfolder AGENTS.md where present) +- Build system: `build.ps1`, `FieldWorks.proj`, `FieldWorks.sln`, `Build/InstallerBuild.proj` +- Installer: `FLExInstaller/` +- Testing: `test.ps1` +- Test data: `TestLangProj/` +- Localization: `crowdin.json`, `DistFiles/CommonLocalizations/` +- Documentation discipline: `Docs/agent-docs-refresh.md` (detect → plan workflow, agent doc skeleton) + +Tips +- Prefer top-level scripts or FieldWorks.sln over ad-hoc project builds +- Respect CI checks (commit messages, whitespace) before pushing + diff --git a/.github/copilot-framework-tasks.md b/.github/copilot-framework-tasks.md new file mode 100644 index 0000000000..c54e365dd3 --- /dev/null +++ b/.github/copilot-framework-tasks.md @@ -0,0 +1,67 @@ +# AI agent framework tasks + +This checklist tracks repository updates that improve AI workflows using agentic primitives, context engineering, and spec-first development. + +## Option 1 — Docs-first primitives (low effort, high ROI) + +- [x] Create domain instructions files: + - [x] .github/instructions/managed.instructions.md + - [x] .github/instructions/native.instructions.md + - [x] .github/instructions/installer.instructions.md + - [x] .github/instructions/testing.instructions.md + - [x] .github/instructions/build.instructions.md +- [x] Add role-scoped chat modes with tool boundaries: + - [x] .github/chatmodes/managed-engineer.chatmode.md + - [x] .github/chatmodes/native-engineer.chatmode.md + - [x] .github/chatmodes/installer-engineer.chatmode.md + - [x] .github/chatmodes/technical-writer.chatmode.md +- [x] Add context and memory anchors: + - [x] .github/context/codebase.context.md + - [x] .github/memory.md +- [x] Reference these entry points from onboarding: + - [x] Link instructions, chat modes, and context in .github/AGENTS.md + +## Option 2 — Agentic workflows + spec-first flow (moderate effort) + +- [ ] Prompts in .github/prompts/: + - [ ] feature-spec.prompt.md (spec → plan → implement with gates; uses spec-kit) + - [ ] bugfix.prompt.md (triage → RCA → fix plan → patch + tests) + - [ ] test-failure-debug.prompt.md (parse NUnit output → targeted fixes) +- [ ] Specification templates: + - [ ] .github/spec-templates/spec.md and plan.md (or link to spec-kit) + - [ ] .github/recipes/*.md playbooks for common tasks +- [ ] Fast inner-loop tasks: + - [ ] Extend .vscode/tasks.json: quick builds (managed/native), smoke tests, whitespace/gitlint + +## Option 3 — Outer-loop automation + MCP integration (higher effort) + +- [ ] Agent CLI/APM scaffolding: + - [ ] apm.yml: map scripts to prompts and declare MCP dependencies + - [ ] Document local usage: `apm install`, `apm run agent-feature-spec --param specFile=...` + - [ ] GH Action to run chosen prompt on PR, post summary/comments +- [ ] MCP servers & boundaries: + - [ ] Add GitHub MCP server and Filesystem MCP (pilot set); restrict by chat mode + - [ ] Capture list and policies in `.github/context/mcp.servers.md` +- [ ] CI governance: + - [ ] lint-docs job to verify AGENTS.md presence/links and src-catalog consistency + - [ ] prompt validation job to parse `.prompt.md` frontmatter/structure +- [ ] Security & secrets: + - [ ] Use least-privilege tokens (e.g., `secrets.AGENT_CLI_PAT`) + - [ ] Add a security review checklist for enabling new tools/servers +- [ ] Rollout strategy: + - [ ] Pilot a no-write prompt (`test-failure-debug.prompt.md`) on PRs + - [ ] Iterate then enable selective write-capable workflows + +See: `.github/option3-plan.md` for details. + +## Notes +- Keep instructions concise and domain-scoped (use `applyTo` when appropriate). +- Follow the canonical agent-doc skeleton described in `Docs/agent-docs-refresh.md` (detect → plan → validate workflow) and remove scaffold leftovers when editing docs. +- Prefer fast inner-loop build/test paths for agents; reserve installer builds for when necessary. + + +## small but high-impact extras +- [ ] Add mermaid diagrams in .github/docs/architecture.md showing component relationships (Cellar/Common/XCore/xWorks), so agents can parse text-based diagrams. +- [ ] Create tests.index.md that maps each major component to its test assemblies and common scenarios (fast lookup for agents). +- [ ] Enrich each AGENTS.md with section headers that match your instructions architecture: Responsibilities, Entry points, Dependencies, Tests, Pitfalls, Extension points. Agents recognize consistent structures quickly. +- [ ] Link your CI checks in the instructions: we already added commit/whitespace/build rules and a PR template—keep those links at the top of AGENTS.md. diff --git a/.github/copilot_apply_updates.py b/.github/copilot_apply_updates.py new file mode 100644 index 0000000000..6ddc60150e --- /dev/null +++ b/.github/copilot_apply_updates.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +"""Apply planner output to refresh auto sections in AGENTS.md files.""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Dict, List, Optional + +AUTO_START = "" +AUTO_END = "" + + +def read_plan(path: Path) -> Dict[str, object]: + return json.loads(path.read_text(encoding="utf-8")) + + +def select_entries(plan: Dict[str, object], folders: Optional[List[str]]) -> List[Dict[str, object]]: + entries = plan.get("folders", []) + if not folders: + return entries + want = {f.replace("\\", "/") for f in folders} + filtered = [] + for entry in entries: + folder = entry.get("folder") + if folder in want: + filtered.append(entry) + return filtered + + +def build_auto_block(entry: Dict[str, object], heading: str, max_files: int, max_commits: int) -> str: + counts = entry.get("change_counts", {}) + summary = ( + f"Files: {counts.get('total', 0)} (code={counts.get('code', 0)}, tests={counts.get('tests', 0)}, resources={counts.get('resources', 0)})" + ) + risk = entry.get("risk_score", "unknown") + recorded = entry.get("recorded_commit") or entry.get("diff_base") + lines = [AUTO_START, f"## {heading}", "", f"- Snapshot: {recorded}", f"- Risk: {risk}", f"- {summary}"] + + changes = entry.get("changes", []) + if changes: + lines.append("\n### File Highlights") + for change in changes[:max_files]: + status = change.get("status", "?") + rel = change.get("path", "") + kind = change.get("kind", "") + ext = change.get("ext", "") + lines.append(f"- {status} {rel} ({kind or 'code'} {ext})") + if len(changes) > max_files: + lines.append(f"- ... {len(changes) - max_files} more file(s)") + + log_entries = entry.get("commit_log", []) + if log_entries: + lines.append("\n### Recent Commits") + for commit in log_entries[:max_commits]: + short = commit.get("hash", "")[:8] + date = commit.get("date", "") + summary_line = commit.get("summary", "") + lines.append(f"- {short} {date} — {summary_line}") + if len(log_entries) > max_commits: + lines.append(f"- ... {len(log_entries) - max_commits} more commit(s)") + + prompts = entry.get("prompts", {}).get("doc-refresh", []) + if prompts: + lines.append("\n### Prompt seeds") + for prompt in prompts: + lines.append(f"- {prompt}") + + lines.append(AUTO_END) + lines.append("") + return "\n".join(lines) + + +def find_frontmatter_end(text: str) -> int: + lines = text.splitlines(keepends=True) + if not lines or not lines[0].strip().startswith("---"): + return 0 + for idx in range(1, min(len(lines), 200)): + if lines[idx].strip().startswith("---"): + return sum(len(line) for line in lines[: idx + 1]) + return 0 + + +def inject_block(text: str, block: str) -> str: + start = text.find(AUTO_START) + if start != -1: + end = text.find(AUTO_END, start) + if end == -1: + end = start + else: + end += len(AUTO_END) + before = text[:start].rstrip() + after = text[end:].lstrip() + pieces = [before, block, after] + return "\n\n".join(piece for piece in pieces if piece).strip() + "\n" + fm_end = find_frontmatter_end(text) + before = text[:fm_end].rstrip() + after = text[fm_end:].lstrip() + parts = [before, block, after] + return "\n\n".join(part for part in parts if part).strip() + "\n" + + +def apply_auto_block(copath: Path, block: str, dry_run: bool) -> bool: + if not copath.exists(): + return False + original = copath.read_text(encoding="utf-8", errors="replace") + updated = inject_block(original, block) + if updated == original: + return False + if dry_run: + return True + copath.write_text(updated, encoding="utf-8") + return True + + +def main() -> int: + ap = argparse.ArgumentParser(description="Inject auto change-log sections into AGENTS.md files") + ap.add_argument("--root", default=str(Path.cwd())) + ap.add_argument("--plan", default=".cache/copilot/diff-plan.json") + ap.add_argument("--folders", nargs="*", help="Subset of folders to update") + ap.add_argument("--heading", default="Change Log (auto)") + ap.add_argument("--max-files", type=int, default=25) + ap.add_argument("--max-commits", type=int, default=10) + ap.add_argument("--dry-run", action="store_true") + args = ap.parse_args() + + root = Path(args.root).resolve() + plan_path = (root / args.plan) if not Path(args.plan).is_absolute() else Path(args.plan) + if not plan_path.exists(): + print(f"Plan file not found: {plan_path}") + return 1 + + plan = read_plan(plan_path) + entries = select_entries(plan, args.folders) + if not entries: + print("No matching entries to apply.") + return 0 + + applied = 0 + for entry in entries: + copilot_rel = entry.get("copilot_path") + if not copilot_rel: + continue + copath = (root / copilot_rel).resolve() + block = build_auto_block(entry, args.heading, args.max_files, args.max_commits) + changed = apply_auto_block(copath, block, args.dry_run) + if changed: + applied += 1 + action = "(dry run)" if args.dry_run else "" + print(f"Updated {copath} {action}") + else: + print(f"No changes needed for {copath}") + + if args.dry_run: + print(f"Dry run complete. {applied} file(s) would change.") + else: + print(f"Applied auto blocks to {applied} file(s).") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/.github/copilot_cache.py b/.github/copilot_cache.py new file mode 100644 index 0000000000..4a86f595c2 --- /dev/null +++ b/.github/copilot_cache.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +"""Cache helpers for COPILOT planning scripts.""" +from __future__ import annotations + +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Dict, Optional + +ISO_FORMAT = "%Y-%m-%dT%H:%M:%SZ" + + +class CopilotCache: + def __init__(self, repo_root: Path) -> None: + self.repo_root = repo_root + self.cache_root = repo_root / ".cache" / "copilot" + self.diff_dir = self.cache_root / "diffs" + self.cache_root.mkdir(parents=True, exist_ok=True) + self.diff_dir.mkdir(parents=True, exist_ok=True) + + def _path_for_folder(self, folder: str) -> Path: + safe = folder.replace("\\", "/").replace("/", "__") + return self.diff_dir / f"{safe}.json" + + def load_folder(self, folder: str, recorded_tree: str, head_tree: str) -> Optional[Dict[str, Any]]: + path = self._path_for_folder(folder) + if not path.exists(): + return None + try: + data = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return None + if data.get("recorded_tree") == recorded_tree and data.get("current_tree") == head_tree: + return data + return None + + def save_folder(self, folder: str, payload: Dict[str, Any]) -> None: + payload = dict(payload) + payload["cached_at"] = datetime.now(timezone.utc).strftime(ISO_FORMAT) + path = self._path_for_folder(folder) + path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8") + + def clear_folder(self, folder: str) -> None: + path = self._path_for_folder(folder) + if path.exists(): + path.unlink() diff --git a/.github/copilot_change_utils.py b/.github/copilot_change_utils.py new file mode 100644 index 0000000000..ddbbbf96f0 --- /dev/null +++ b/.github/copilot_change_utils.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""Shared helpers for COPILOT automation scripts.""" +from __future__ import annotations + +import os +from dataclasses import dataclass +from typing import Dict, Iterable, List + +RESOURCE_EXTS = { + ".resx", + ".xaml", + ".xml", + ".xsd", + ".xsl", + ".xslt", + ".config", + ".json", +} + +TEST_TOKENS = ( + "/tests/", + "\\tests\\", + ".tests/", + ".tests\\", + ".tests.", +) + + +@dataclass +class ChangeClassification: + path: str + ext: str + kind: str # code, test, resource, or other + is_test: bool + is_resource: bool + + +def classify_path(rel_path: str) -> ChangeClassification: + """Classify a relative path into buckets used by risk scoring.""" + norm = rel_path.replace("\\", "/") + lower = norm.lower() + ext = os.path.splitext(lower)[1] + is_test = any(token in lower for token in TEST_TOKENS) or lower.endswith("tests.cs") + is_resource = ext in RESOURCE_EXTS + if is_test: + kind = "test" + elif is_resource: + kind = "resource" + else: + kind = "code" + return ChangeClassification(path=norm, ext=ext, kind=kind, is_test=is_test, is_resource=is_resource) + + +def summarize_paths(paths: Iterable[str]) -> Dict[str, int]: + """Return aggregate counts for a collection of relative paths.""" + counts = { + "total": 0, + "code": 0, + "tests": 0, + "resources": 0, + } + for rel in paths: + info = classify_path(rel) + counts["total"] += 1 + if info.kind == "test": + counts["tests"] += 1 + elif info.kind == "resource": + counts["resources"] += 1 + else: + counts["code"] += 1 + return counts + + +def compute_risk_score(counts: Dict[str, int]) -> str: + """Estimate a simple risk level based on change counts.""" + total = counts.get("total", 0) + if total == 0: + return "none" + code_changes = counts.get("code", 0) + test_changes = counts.get("tests", 0) + if code_changes >= 10 or (code_changes >= 5 and test_changes == 0): + return "high" + if total >= 5: + return "medium" + return "low" + + +def classify_with_status(entries: Iterable[str]) -> List[Dict[str, str]]: + """Split "status\tpath" lines into structured dictionaries.""" + results: List[Dict[str, str]] = [] + for raw in entries: + if not raw.strip(): + continue + parts = raw.split("\t", 1) + if len(parts) != 2: + continue + status, rel_path = parts + info = classify_path(rel_path) + results.append( + { + "status": status, + "path": info.path, + "kind": info.kind, + "ext": info.ext, + "is_test": info.is_test, + "is_resource": info.is_resource, + } + ) + return results diff --git a/.github/copilot_doc_utils.py b/.github/copilot_doc_utils.py new file mode 100644 index 0000000000..ddd36b6709 --- /dev/null +++ b/.github/copilot_doc_utils.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +"""Shared markdown helpers for COPILOT documents.""" +from __future__ import annotations + +from pathlib import Path +from typing import Tuple + +AUTO_START = "" +AUTO_END = "" +AUTO_PLACEHOLDER = """\n## Change Log (auto)\n\nThis section is populated by running:\n1. `python .github/plan_copilot_updates.py --folders `\n2. `python .github/copilot_apply_updates.py --folders `\n\nDo not edit this block manually; rerun the scripts above after code or doc updates.\n\n""" +AUTO_HINT_HEADING = "## References (auto-generated hints)" + + +def _split_frontmatter(text: str) -> Tuple[str, str]: + if not text.startswith("---\n"): + return "", text + end = text.find("\n---", 3) + if end == -1: + return "", text + end += len("\n---\n") + return text[:end], text[end:] + + +def ensure_auto_change_log_block(text: str) -> Tuple[str, bool]: + if AUTO_START in text and AUTO_END in text: + return text, False + front, body = _split_frontmatter(text) + before = front.strip() + after = body.strip() + pieces = [part for part in (before, AUTO_PLACEHOLDER.strip(), after) if part] + updated = "\n\n".join(pieces) + "\n" + return updated, True + + +def remove_legacy_auto_hint(text: str) -> Tuple[str, bool]: + if AUTO_HINT_HEADING not in text: + return text, False + start = text.find(AUTO_HINT_HEADING) + if start == -1: + return text, False + after_heading = text[start:] + end = after_heading.find("\n## ", len(AUTO_HINT_HEADING)) + if end == -1: + trimmed = text[:start].rstrip() + return (trimmed + "\n").rstrip() + "\n", True + end_index = start + end + updated = (text[:start].rstrip() + "\n\n" + text[end_index:].lstrip()).strip() + "\n" + return updated, True + + +def split_after_auto_block(text: str) -> Tuple[str, str]: + start = text.find(AUTO_START) + if start == -1: + return text, "" + end = text.find(AUTO_END, start) + if end == -1: + return text, "" + end += len(AUTO_END) + newline_idx = text.find("\n", end) + if newline_idx == -1: + newline_idx = len(text) + prefix = text[:newline_idx].rstrip() + "\n\n" + suffix = text[newline_idx:].lstrip() + return prefix, suffix + + +def load_template(path: Path) -> str: + return path.read_text(encoding="utf-8").strip() diff --git a/.github/copilot_tree_hash.py b/.github/copilot_tree_hash.py new file mode 100644 index 0000000000..17434a23b1 --- /dev/null +++ b/.github/copilot_tree_hash.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""Utility helpers for computing deterministic Src/ tree hashes. + +The goal is to capture the set of tracked files under a folder (excluding the +folder's AGENTS.md) and produce a stable digest that represents the code/data +state that documentation was written against. + +We hash the list of files paired with their git blob SHAs at the specified ref +(default HEAD). The working tree is not considered; callers should ensure they +run these helpers on a clean tree or handle dirty-state warnings separately. +""" +from __future__ import annotations + +import hashlib +import subprocess +from pathlib import Path +from typing import Iterable, Tuple + +__all__ = [ + "compute_folder_tree_hash", + "list_tracked_blobs", +] + + +def run(cmd: Iterable[str], cwd: Path) -> str: + """Run a subprocess and return stdout decoded as UTF-8.""" + return subprocess.check_output(cmd, cwd=str(cwd), stderr=subprocess.STDOUT).decode( + "utf-8", errors="replace" + ) + + +def list_tracked_blobs( + root: Path, folder: Path, ref: str = "HEAD" +) -> Iterable[Tuple[str, str]]: + """Yield (relative_path, blob_sha) for tracked files under ``folder``. + + ``ref`` defaults to ``HEAD``. ``folder`` must be inside ``root``. + ``AGENTS.md`` is excluded by design so the hash reflects code/data only. + """ + + rel = folder.relative_to(root).as_posix() + if not rel.startswith("Src/"): + raise ValueError(f"Folder must reside under Src/: {rel}") + + try: + output = run( + [ + "git", + "ls-tree", + "-r", + "--full-tree", + ref, + "--", + rel, + ], + cwd=root, + ) + except subprocess.CalledProcessError as exc: + raise RuntimeError( + f"Failed to list tracked files for {rel}: {exc.output.decode('utf-8', errors='replace')}" + ) from exc + + for line in output.splitlines(): + parts = line.split() + if len(parts) < 4: + continue + _, obj_type, blob_sha, *rest = parts + if obj_type != "blob": + continue + path = rest[-1] + if path.endswith("/AGENTS.md") or path == "AGENTS.md": + continue + yield path, blob_sha + + +def compute_folder_tree_hash(root: Path, folder: Path, ref: str = "HEAD") -> str: + """Compute a stable sha256 digest representing ``folder`` at ``ref``. + + The digest is the sha256 of ``"{relative_path}:{blob_sha}\n"`` for each + tracked file (sorted lexicographically) underneath ``folder`` excluding the + AGENTS.md documentation. When a folder has no tracked files besides + AGENTS.md the digest is the sha256 of the empty string. + """ + + items = sorted(list_tracked_blobs(root, folder, ref)) + digest = hashlib.sha256() + for rel_path, blob_sha in items: + digest.update(f"{rel_path}:{blob_sha}\n".encode("utf-8")) + return digest.hexdigest() + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..7e05640073 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,47 @@ +# Dependabot configuration for FieldWorks +# Keeps GitHub Actions and other dependencies up-to-date + +version: 2 +updates: + # GitHub Actions - check weekly for updates + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + commit-message: + prefix: "ci" + labels: + - "dependencies" + - "github-actions" + # Group minor/patch updates to reduce PR noise + groups: + actions-minor: + patterns: + - "*" + update-types: + - "minor" + - "patch" + + # NuGet packages - check weekly + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "weekly" + day: "tuesday" + commit-message: + prefix: "deps" + labels: + - "dependencies" + - "nuget" + # Ignore major version bumps by default (breaking changes) + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-major"] + groups: + nuget-minor: + patterns: + - "*" + update-types: + - "minor" + - "patch" diff --git a/.github/detect_copilot_needed.py b/.github/detect_copilot_needed.py new file mode 100644 index 0000000000..1ed1849827 --- /dev/null +++ b/.github/detect_copilot_needed.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +""" +detect_copilot_needed.py — Identify folders with code/config changes that likely require AGENTS.md updates. + +Intended for CI (advisory or failing), and for local pre-commit checks. + +Logic: +- Compute changed files between a base and head ref (or since a rev). +- Consider only changes under Src/** that match code/config extensions. +- Group by top-level folder: Src//... +- For each impacted folder, compare the folder's current git tree hash to the + `last-reviewed-tree` recorded in AGENTS.md and track whether the doc changed. +- Report folders whose hashes no longer match or whose docs are missing. +- Optionally validate changed AGENTS.md files with check_copilot_docs.py. + +Exit codes: + 0 = no issues (either no impacted folders or all have AGENTS.md changes, and validations passed) + 1 = advisory warnings (impacted folders without AGENTS.md updated), when --strict not set + 2 = strict failure when --strict is set and there are issues, or validation fails + +Examples: + python .github/detect_copilot_needed.py --base origin/release/9.3 --head HEAD --strict + python .github/detect_copilot_needed.py --since origin/release/9.3 +""" +import argparse +import json +import os +import subprocess +from pathlib import Path +from typing import Dict, Optional, Tuple + +from copilot_change_utils import compute_risk_score, summarize_paths +from copilot_tree_hash import compute_folder_tree_hash + +CODE_EXTS = { + ".cs", + ".cpp", + ".cc", + ".c", + ".h", + ".hpp", + ".ixx", + ".xml", + ".xsl", + ".xslt", + ".xsd", + ".dtd", + ".xaml", + ".resx", + ".config", + ".csproj", + ".vcxproj", + ".props", + ".targets", +} + + +def run(cmd, cwd=None): + return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT).decode( + "utf-8", errors="replace" + ) + + +def git_changed_files( + root: Path, base: str = None, head: str = "HEAD", since: str = None +): + if since: + diff_range = f"{since}..{head}" + elif base: + diff_range = f"{base}..{head}" + else: + # Fallback: compare to merge-base with origin/HEAD (best effort) + try: + mb = run(["git", "merge-base", head, "origin/HEAD"], cwd=str(root)).strip() + diff_range = f"{mb}..{head}" + except Exception: + diff_range = f"HEAD~1..{head}" + out = run(["git", "diff", "--name-only", diff_range], cwd=str(root)) + return [l.strip().replace("\\", "/") for l in out.splitlines() if l.strip()] + + +def top_level_src_folder(path: str): + # Expect paths like Src//... + parts = path.split("/") + if len(parts) >= 2 and parts[0] == "Src": + return "/".join(parts[:2]) # Src/Folder + return None + + +def parse_frontmatter(path: Path) -> Tuple[Optional[Dict[str, str]], str]: + if not path.exists(): + return None, "" + text = path.read_text(encoding="utf-8", errors="replace") + lines = text.splitlines() + if len(lines) >= 3 and lines[0].strip() == "---": + end_idx = -1 + for i in range(1, min(len(lines), 200)): + if lines[i].strip() == "---": + end_idx = i + break + if end_idx == -1: + return None, text + fm_lines = lines[1:end_idx] + fm: Dict[str, str] = {} + for l in fm_lines: + l = l.strip() + if not l or l.startswith("#"): + continue + if ":" in l: + k, v = l.split(":", 1) + fm[k.strip()] = v.strip().strip('"') + return fm, "\n".join(lines[end_idx + 1 :]) + return None, "" + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--root", default=str(Path.cwd())) + ap.add_argument( + "--base", default=None, help="Base git ref (e.g., origin/release/9.3)" + ) + ap.add_argument("--head", default="HEAD", help="Head ref (default HEAD)") + ap.add_argument( + "--since", default=None, help="Alternative to base/head: since this ref" + ) + ap.add_argument("--json", dest="json_out", default=None) + ap.add_argument( + "--validate-changed", + action="store_true", + help="Validate changed AGENTS.md with check_copilot_docs.py", + ) + ap.add_argument("--strict", action="store_true", help="Exit non-zero on issues") + args = ap.parse_args() + + root = Path(args.root).resolve() + changed = git_changed_files(root, base=args.base, head=args.head, since=args.since) + + impacted: Dict[str, set] = {} + copilot_changed = set() + for p in changed: + if p.endswith("/AGENTS.md"): + copilot_changed.add(p) + # Only care about Src/** files that look like code/config + if not p.startswith("Src/"): + continue + if p.endswith("/AGENTS.md"): + continue + _, ext = os.path.splitext(p) + if ext.lower() not in CODE_EXTS: + continue + folder = top_level_src_folder(p) + if folder: + impacted.setdefault(folder, set()).add(p) + + results = [] + issues = 0 + for folder, files in sorted(impacted.items()): + copath_rel = f"{folder}/AGENTS.md" + copath = root / copath_rel + folder_path = root / folder + doc_changed = copath_rel in copilot_changed + reasons = [] + recorded_hash: Optional[str] = None + fm, _ = parse_frontmatter(copath) + if not copath.exists(): + reasons.append("AGENTS.md missing") + elif not fm: + reasons.append("frontmatter missing") + else: + recorded_hash = fm.get("last-reviewed-tree") + if not recorded_hash or recorded_hash.startswith("FIXME"): + reasons.append("last-reviewed-tree missing or placeholder") + current_hash: Optional[str] = None + hash_error: Optional[str] = None + if folder_path.exists(): + try: + current_hash = compute_folder_tree_hash( + root, folder_path, ref=args.head + ) + except Exception as exc: # pragma: no cover - diagnostics only + hash_error = str(exc) + reasons.append("unable to compute tree hash") + else: + reasons.append("folder missing at head ref") + + counts = summarize_paths(files) + risk_level = compute_risk_score(counts) + + if current_hash and recorded_hash and current_hash == recorded_hash: + up_to_date = True + else: + up_to_date = False + if current_hash and recorded_hash and current_hash != recorded_hash: + reasons.append("tree hash mismatch") + if not doc_changed and not reasons: + # Defensive catch-all + reasons.append("AGENTS.md not updated") + + entry = { + "folder": folder, + "files_changed": sorted(files), + "copilot_path": copath_rel, + "copilot_changed": doc_changed, + "last_reviewed_tree": recorded_hash, + "current_tree": current_hash, + "status": "OK" if up_to_date else "STALE", + "reasons": reasons, + "change_counts": counts, + "risk_score": risk_level, + } + if hash_error: + entry["hash_error"] = hash_error + if not up_to_date: + issues += 1 + results.append(entry) + + # Optional validation for changed AGENTS.md files + validation_failures = [] + if args.validate_changed and copilot_changed: + try: + cmd = ["python", ".github/check_copilot_docs.py", "--fail"] + # Limit to changed files by setting CWD and relying on script to scan all; keep simple + run(cmd, cwd=str(root)) + except subprocess.CalledProcessError as e: + validation_failures.append(e.output.decode("utf-8", errors="replace")) + issues += 1 + + print(f"Impacted folders: {len(impacted)}") + for e in results: + if e["status"] == "OK": + detail = "hash aligned" + else: + detail = ", ".join(e["reasons"]) if e["reasons"] else "hash mismatch" + extras = "" + if e.get("risk_score"): + extras = f"; risk={e['risk_score']}" + print(f"- {e['folder']}: {e['status']} ({detail}{extras})") + + if validation_failures: + print("\nValidation failures from check_copilot_docs.py:") + for vf in validation_failures: + print(vf) + + if args.json_out: + json_path = Path(args.json_out) + if not json_path.is_absolute(): + json_path = Path.cwd() / json_path + json_path.parent.mkdir(parents=True, exist_ok=True) + with open(json_path, "w", encoding="utf-8") as f: + json.dump({"impacted": results}, f, indent=2) + + if args.strict and issues: + return 2 + return 0 if not issues else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/.github/instructions/build.instructions.md b/.github/instructions/build.instructions.md new file mode 100644 index 0000000000..5b902665b9 --- /dev/null +++ b/.github/instructions/build.instructions.md @@ -0,0 +1,65 @@ +--- +applyTo: "**/*" +name: "build.instructions" +description: "FieldWorks build guidelines and inner-loop tips" +--- +# Build guidelines (FieldWorks) + +## Purpose & Scope +This file documents the **supported** build workflow for FieldWorks. + +FieldWorks is **Windows-first** and **x64-only**. Use the repo scripts so build ordering (native before managed) is correct. + +## Quick start (PowerShell) +```powershell +# Full traversal build (Debug/x64 defaults) +.\build.ps1 + +# Release build +.\build.ps1 -Configuration Release +``` + +## Non-negotiable rules +- Use `.\build.ps1` for builds and `.\test.ps1` for tests. +- Avoid ad-hoc `msbuild`/`dotnet build` invocations unless you are explicitly debugging build infrastructure. +- Do not change COM/registry behavior without an explicit plan and tests. + +## Warnings policy +- Treat *compiler* warnings as errors by default (fix warnings; don’t suppress them). +- Keep warning output **visible**. Avoid “make it quiet” changes that hide warnings without resolving the cause. +- Some MSBuild warnings are expected today due to external dependencies (notably `MSB3277`/`MSB3243` assembly version conflicts). These warnings are documented as informational in `Directory.Build.props`. + - Do **not** attempt to suppress these with warning filters; if the dependency landscape changes, update the documentation and reassess. +- Other MSBuild warnings (for example `MSB3245`/`MSB3246` unresolved references / bad images) are **actionable**: fix the underlying reference or generation issue rather than suppressing the warning. + +## Why build.ps1 +FieldWorks uses an MSBuild traversal (`FieldWorks.proj`) with ordered phases. + +Key ordering constraint: +- **Native C++ must build first** (Phase 2) because later managed code-generation depends on native artifacts. + +## Outputs +- Build outputs: `Output//` (for example `Output/Debug/`) +- Intermediate outputs: `Obj//` (centralized; do not rely on per-project `obj/` folders) + +## Worktrees and concurrent builds +This repo supports multiple concurrent builds across git worktrees. Prefer the scripts because they handle environment setup and avoid cross-worktree process conflicts. + +## Troubleshooting (common) + +### “Native artifacts missing” / code-generation failures +Re-run the scripted build: +```powershell +.\build.ps1 +``` +If the failure persists, check the first error (later ones often cascade) and confirm native Phase 2 succeeds. + +### Stale intermediates +Prefer re-running `.\build.ps1` first (it performs cleanup needed for this repo). If you must do a hard reset, delete `Output/` and `Obj/`, then run `.\build.ps1` again. + +## References +- Traversal order: `FieldWorks.proj` +- Build infrastructure: `Build/` +- Tests: `.github/instructions/testing.instructions.md` + +## CLARIFICATIONS_NEEDED +- Confirm whether any MSBuild warning codes other than `MSB3277`/`MSB3243` are considered acceptable, and under what conditions. diff --git a/.github/instructions/debugging.instructions.md b/.github/instructions/debugging.instructions.md new file mode 100644 index 0000000000..fa5b2dfffe --- /dev/null +++ b/.github/instructions/debugging.instructions.md @@ -0,0 +1,63 @@ +--- +applyTo: "**/*" +name: "debugging.instructions" +description: "Runtime debugging and tracing guidance for FieldWorks" +--- + +# Debugging and Tracing + +## Purpose & Scope +- Enable developers to capture actionable runtime diagnostics in Debug builds. +- Cover trace switches, listeners, and crash evidence collection for FieldWorks desktop. + +## Quick Start: Enable Trace Log +1) Copy the snippet below into `Output/Debug/FieldWorks.exe.config` (or create it by copying `Src/AppForTests.config`). +2) Ensure `%temp%` is writable; set `AssertUiEnabled=false` to avoid modal assertion dialogs. +3) Restart `FieldWorks.exe`; traces will append to `%temp%/FieldWorks.trace.log`. + +```xml + + + + + + + + + + + + + + +``` +- Switch levels: 0 Off, 1 Error, 2 Warning, 3 Info, 4 Verbose. +- Log file path is configurable via `logfilename`; environment variable expansion (`%temp%`) is supported by `EnvVarTraceListener`. + +## Trace Switch Reference +- `ShowPendingMsgs`: XCore pending message queue tracing. +- `XCore.Mediator_InvokeTrace`: Mediator invoke and job queue tracing. +- `XWorks_Timing`: Timing hooks in xWorks (RecordList, etc.). +- `XWorks_LinkListener`: LinkListener diagnostics. +- Additional Trace.WriteLine hooks exist in Debug-only helpers (`DebugProcs`, etc.). + +## Assertions and UI +- `EnvVarTraceListener` honors `AssertUiEnabled` and `AssertExceptionEnabled` environment variables. +- Set `AssertUiEnabled=false` to suppress modal assertion dialogs; exceptions are thrown only if `assertexceptionenabled` remains `true`. + +## Dev switch (auto config) +- FieldWorks now supports a swappable diagnostics config via `FieldWorks.Diagnostics.config`. +- Default is quiet. `build.ps1` now enables the dev diagnostics config automatically for Debug builds unless you override `/p:UseDevTraceConfig`. You can also force it via `UseDevTraceConfig=true`, by setting environment variable `FW_TRACE_LOG` before the build, or by passing `-TraceCrashes` to `build.ps1`; the dev diagnostics file is copied as `FieldWorks.Diagnostics.config` in the output. +- Dev log location: `Output/Debug/FieldWorks.trace.log` (relative to the app folder) so it’s easy to collect alongside binaries. +- Dev config logs to `%temp%/FieldWorks.trace.log` and turns on the core switches above. Edit `Src/Common/FieldWorks/FieldWorks.Diagnostics.dev.config` to change log path or switches. + +## Crash Evidence +- Check `%LOCALAPPDATA%\CrashDumps` for `FieldWorks.exe.*.dmp` (enable Windows Error Reporting local dumps if missing). +- Windows Event Viewer → Windows Logs → Application contains `.NET Runtime` and `Application Error` entries. + +## Proposed Improvements (dev-only) +- Add `Docs/FieldWorks.trace.sample.config` with the snippet above for easy reuse. +- Introduce a dev flag (`UseDevTraceConfig=true` or `FW_TRACE_LOG` env var) that copies the dev diagnostics file next to `FieldWorks.exe` in Debug builds so tracing is on by default for local runs. +- Document standard trace switches in `Docs/logging.md` and keep `EnvVarTraceListener` as the default listener for dev traces. diff --git a/.github/instructions/dotnet-architecture-good-practices.instructions.md b/.github/instructions/dotnet-architecture-good-practices.instructions.md new file mode 100644 index 0000000000..793c163739 --- /dev/null +++ b/.github/instructions/dotnet-architecture-good-practices.instructions.md @@ -0,0 +1,279 @@ +--- +description: "DDD and .NET architecture guidelines" +applyTo: '**/*.cs,**/*.csproj,**/Program.cs,**/*.razor' +--- + +# DDD Systems & .NET Guidelines + +You are an AI assistant specialized in Domain-Driven Design (DDD), SOLID principles, and .NET good practices for software Development. Follow these guidelines for building robust, maintainable systems. + +## MANDATORY THINKING PROCESS + +**BEFORE any implementation, you MUST:** + +1. **Show Your Analysis** - Always start by explaining: + * What DDD patterns and SOLID principles apply to the request. + * Which layer(s) will be affected (Domain/Application/Infrastructure). + * How the solution aligns with ubiquitous language. + * Security and compliance considerations. +2. **Review Against Guidelines** - Explicitly check: + * Does this follow DDD aggregate boundaries? + * Does the design adhere to the Single Responsibility Principle? + * Are domain rules encapsulated correctly? + * Will tests follow the `MethodName_Condition_ExpectedResult()` pattern? + * Are Coding domain considerations addressed? + * Is the ubiquitous language consistent? +3. **Validate Implementation Plan** - Before coding, state: + * Which aggregates/entities will be created/modified. + * What domain events will be published. + * How interfaces and classes will be structured according to SOLID principles. + * What tests will be needed and their naming. + +**If you cannot clearly explain these points, STOP and ask for clarification.** + +## Core Principles + +### 1. **Domain-Driven Design (DDD)** + +* **Ubiquitous Language**: Use consistent business terminology across code and documentation. +* **Bounded Contexts**: Clear service boundaries with well-defined responsibilities. +* **Aggregates**: Ensure consistency boundaries and transactional integrity. +* **Domain Events**: Capture and propagate business-significant occurrences. +* **Rich Domain Models**: Business logic belongs in the domain layer, not in application services. + +### 2. **SOLID Principles** + +* **Single Responsibility Principle (SRP)**: A class should have only one reason to change. +* **Open/Closed Principle (OCP)**: Software entities should be open for extension but closed for modification. +* **Liskov Substitution Principle (LSP)**: Subtypes must be substitutable for their base types. +* **Interface Segregation Principle (ISP)**: No client should be forced to depend on methods it does not use. +* **Dependency Inversion Principle (DIP)**: Depend on abstractions, not on concretions. + +### 3. **.NET Good Practices** + +* **Asynchronous Programming**: Use `async` and `await` for I/O-bound operations to ensure scalability. +* **Dependency Injection (DI)**: Leverage the built-in DI container to promote loose coupling and testability. +* **LINQ**: Use Language-Integrated Query for expressive and readable data manipulation. +* **Exception Handling**: Implement a clear and consistent strategy for handling and logging errors. +* **Modern C# Features**: Utilize modern language features (e.g., records, pattern matching) to write concise and robust code. + +### 4. **Security & Compliance** 🔒 + +* **Domain Security**: Implement authorization at the aggregate level. +* **Financial Regulations**: PCI-DSS, SOX compliance in domain rules. +* **Audit Trails**: Domain events provide a complete audit history. +* **Data Protection**: LGPD compliance in aggregate design. + +### 5. **Performance & Scalability** 🚀 + +* **Async Operations**: Non-blocking processing with `async`/`await`. +* **Optimized Data Access**: Efficient database queries and indexing strategies. +* **Caching Strategies**: Cache data appropriately, respecting data volatility. +* **Memory Efficiency**: Properly sized aggregates and value objects. + +## DDD & .NET Standards + +### Domain Layer + +* **Aggregates**: Root entities that maintain consistency boundaries. +* **Value Objects**: Immutable objects representing domain concepts. +* **Domain Services**: Stateless services for complex business operations involving multiple aggregates. +* **Domain Events**: Capture business-significant state changes. +* **Specifications**: Encapsulate complex business rules and queries. + +### Application Layer + +* **Application Services**: Orchestrate domain operations and coordinate with infrastructure. +* **Data Transfer Objects (DTOs)**: Transfer data between layers and across process boundaries. +* **Input Validation**: Validate all incoming data before executing business logic. +* **Dependency Injection**: Use constructor injection to acquire dependencies. + +### Infrastructure Layer + +* **Repositories**: Aggregate persistence and retrieval using interfaces defined in the domain layer. +* **Event Bus**: Publish and subscribe to domain events. +* **Data Mappers / ORMs**: Map domain objects to database schemas. +* **External Service Adapters**: Integrate with external systems. + +### Testing Standards + +* **Test Naming Convention**: Use `MethodName_Condition_ExpectedResult()` pattern. +* **Unit Tests**: Focus on domain logic and business rules in isolation. +* **Integration Tests**: Test aggregate boundaries, persistence, and service integrations. +* **Acceptance Tests**: Validate complete user scenarios. +* **Test Coverage**: Minimum 85% for domain and application layers. + +### Development Practices + +* **Event-First Design**: Model business processes as sequences of events. +* **Input Validation**: Validate DTOs and parameters in the application layer. +* **Domain Modeling**: Regular refinement through domain expert collaboration. +* **Continuous Integration**: Automated testing of all layers. + +## Implementation Guidelines + +When implementing solutions, **ALWAYS follow this process**: + +### Step 1: Domain Analysis (REQUIRED) + +**You MUST explicitly state:** + +* Domain concepts involved and their relationships. +* Aggregate boundaries and consistency requirements. +* Ubiquitous language terms being used. +* Business rules and invariants to enforce. + +### Step 2: Architecture Review (REQUIRED) + +**You MUST validate:** + +* How responsibilities are assigned to each layer. +* Adherence to SOLID principles, especially SRP and DIP. +* How domain events will be used for decoupling. +* Security implications at the aggregate level. + +### Step 3: Implementation Planning (REQUIRED) + +**You MUST outline:** + +* Files to be created/modified with justification. +* Test cases using `MethodName_Condition_ExpectedResult()` pattern. +* Error handling and validation strategy. +* Performance and scalability considerations. + +### Step 4: Implementation Execution + +1. **Start with domain modeling and ubiquitous language.** +2. **Define aggregate boundaries and consistency rules.** +3. **Implement application services with proper input validation.** +4. **Adhere to .NET good practices like async programming and DI.** +5. **Add comprehensive tests following naming conventions.** +6. **Implement domain events for loose coupling where appropriate.** +7. **Document domain decisions and trade-offs.** + +### Step 5: Post-Implementation Review (REQUIRED) + +**You MUST verify:** + +* All quality checklist items are met. +* Tests follow naming conventions and cover edge cases. +* Domain rules are properly encapsulated. +* Financial calculations maintain precision. +* Security and compliance requirements are satisfied. + +## Testing Guidelines + +### Test Structure + +```csharp +[Fact(DisplayName = "Descriptive test scenario")] +public void MethodName_Condition_ExpectedResult() +{ + // Setup for the test + var aggregate = CreateTestAggregate(); + var parameters = new TestParameters(); + + // Execution of the method under test + var result = aggregate.PerformAction(parameters); + + // Verification of the outcome + Assert.NotNull(result); + Assert.Equal(expectedValue, result.Value); +} +``` + +### Domain Test Categories + +* **Aggregate Tests**: Business rule validation and state changes. +* **Value Object Tests**: Immutability and equality. +* **Domain Service Tests**: Complex business operations. +* **Event Tests**: Event publishing and handling. +* **Application Service Tests**: Orchestration and input validation. + +### Test Validation Process (MANDATORY) + +**Before writing any test, you MUST:** + +1. **Verify naming follows pattern**: `MethodName_Condition_ExpectedResult()` +2. **Confirm test category**: Which type of test (Unit/Integration/Acceptance). +3. **Check domain alignment**: Test validates actual business rules. +4. **Review edge cases**: Includes error scenarios and boundary conditions. + +## Quality Checklist + +**MANDATORY VERIFICATION PROCESS**: Before delivering any code, you MUST explicitly confirm each item: + +### Domain Design Validation + +* **Domain Model**: "I have verified that aggregates properly model business concepts." +* **Ubiquitous Language**: "I have confirmed consistent terminology throughout the codebase." +* **SOLID Principles Adherence**: "I have verified the design follows SOLID principles." +* **Business Rules**: "I have validated that domain logic is encapsulated in aggregates." +* **Event Handling**: "I have confirmed domain events are properly published and handled." + +### Implementation Quality Validation + +* **Test Coverage**: "I have written comprehensive tests following `MethodName_Condition_ExpectedResult()` naming." +* **Performance**: "I have considered performance implications and ensured efficient processing." +* **Security**: "I have implemented authorization at aggregate boundaries." +* **Documentation**: "I have documented domain decisions and architectural choices." +* **.NET Best Practices**: "I have followed .NET best practices for async, DI, and error handling." + +### Financial Domain Validation + +* **Monetary Precision**: "I have used `decimal` types and proper rounding for financial calculations." +* **Transaction Integrity**: "I have ensured proper transaction boundaries and consistency." +* **Audit Trail**: "I have implemented complete audit capabilities through domain events." +* **Compliance**: "I have addressed PCI-DSS, SOX, and LGPD requirements." + +**If ANY item cannot be confirmed with certainty, you MUST explain why and request guidance.** + +### Monetary Values + +* Use `decimal` type for all monetary calculations. +* Implement currency-aware value objects. +* Handle rounding according to financial standards. +* Maintain precision throughout calculation chains. + +### Transaction Processing + +* Implement proper saga patterns for distributed transactions. +* Use domain events for eventual consistency. +* Maintain strong consistency within aggregate boundaries. +* Implement compensation patterns for rollback scenarios. + +### Audit and Compliance + +* Capture all financial operations as domain events. +* Implement immutable audit trails. +* Design aggregates to support regulatory reporting. +* Maintain data lineage for compliance audits. + +### Financial Calculations + +* Encapsulate calculation logic in domain services. +* Implement proper validation for financial rules. +* Use specifications for complex business criteria. +* Maintain calculation history for audit purposes. + +### Platform Integration + +* Use system standard DDD libraries and frameworks. +* Implement proper bounded context integration. +* Maintain backward compatibility in public contracts. +* Use domain events for cross-context communication. + +**Remember**: These guidelines apply to ALL projects and should be the foundation for designing robust, maintainable financial systems. + +## CRITICAL REMINDERS + +**YOU MUST ALWAYS:** + +* Show your thinking process before implementing. +* Explicitly validate against these guidelines. +* Use the mandatory verification statements. +* Follow the `MethodName_Condition_ExpectedResult()` test naming pattern. +* Confirm financial domain considerations are addressed. +* Stop and ask for clarification if any guideline is unclear. + +**FAILURE TO FOLLOW THIS PROCESS IS UNACCEPTABLE** - The user expects rigorous adherence to these guidelines and code standards. diff --git a/.github/instructions/dotnet-framework.instructions.md b/.github/instructions/dotnet-framework.instructions.md new file mode 100644 index 0000000000..9b796f612a --- /dev/null +++ b/.github/instructions/dotnet-framework.instructions.md @@ -0,0 +1,113 @@ +--- +description: 'Guidance for working with .NET Framework projects. Includes project structure, C# language version, NuGet management, and best practices.' +applyTo: '**/*.csproj, **/*.cs' +--- + +# .NET Framework Development + +## Build and Compilation Requirements +- Always use `msbuild /t:rebuild` to build the solution or projects instead of `dotnet build` + +## Project File Management + +### Non-SDK Style Project Structure +.NET Framework projects use the legacy project format, which differs significantly from modern SDK-style projects: + +- **Explicit File Inclusion**: All new source files **MUST** be explicitly added to the project file (`.csproj`) using a `` element + - .NET Framework projects do not automatically include files in the directory like SDK-style projects + - Example: `` + +- **No Implicit Imports**: Unlike SDK-style projects, .NET Framework projects do not automatically import common namespaces or assemblies + +- **Build Configuration**: Contains explicit `` sections for Debug/Release configurations + +- **Output Paths**: Explicit `` and `` definitions + +- **Target Framework**: Uses `` instead of `` + - Example: `v4.7.2` + +## NuGet Package Management +- Installing and updating NuGet packages in .NET Framework projects is a complex task requiring coordinated changes to multiple files. Therefore, **do not attempt to install or update NuGet packages** in this project. +- Instead, if changes to NuGet references are required, ask the user to install or update NuGet packages using the Visual Studio NuGet Package Manager or Visual Studio package manager console. +- When recommending NuGet packages, ensure they are compatible with .NET Framework or .NET Standard 2.0 (not only .NET Core or .NET 5+). + +## C# Language Version is 7.3 +- This project is limited to C# 7.3 features only. Please avoid using: + +### C# 8.0+ Features (NOT SUPPORTED): + - Using declarations (`using var stream = ...`) + - Await using statements (`await using var resource = ...`) + - Switch expressions (`variable switch { ... }`) + - Null-coalescing assignment (`??=`) + - Range and index operators (`array[1..^1]`, `array[^1]`) + - Default interface methods + - Readonly members in structs + - Static local functions + - Nullable reference types (`string?`, `#nullable enable`) + +### C# 9.0+ Features (NOT SUPPORTED): + - Records (`public record Person(string Name)`) + - Init-only properties (`{ get; init; }`) + - Top-level programs (program without Main method) + - Pattern matching enhancements + - Target-typed new expressions (`List list = new()`) + +### C# 10+ Features (NOT SUPPORTED): + - Global using statements + - File-scoped namespaces + - Record structs + - Required members + +### Use Instead (C# 7.3 Compatible): + - Traditional using statements with braces + - Switch statements instead of switch expressions + - Explicit null checks instead of null-coalescing assignment + - Array slicing with manual indexing + - Abstract classes or interfaces instead of default interface methods + +## Environment Considerations (Windows environment) +- Use Windows-style paths with backslashes (e.g., `C:\path\to\file.cs`) +- Use Windows-appropriate commands when suggesting terminal operations +- Consider Windows-specific behaviors when working with file system operations + +## Common .NET Framework Pitfalls and Best Practices + +### Async/Await Patterns +- **ConfigureAwait(false)**: Always use `ConfigureAwait(false)` in library code to avoid deadlocks: + ```csharp + var result = await SomeAsyncMethod().ConfigureAwait(false); + ``` +- **Avoid sync-over-async**: Don't use `.Result` or `.Wait()` or `.GetAwaiter().GetResult()`. These sync-over-async patterns can lead to deadlocks and poor performance. Always use `await` for asynchronous calls. + +### DateTime Handling +- **Use DateTimeOffset for timestamps**: Prefer `DateTimeOffset` over `DateTime` for absolute time points +- **Specify DateTimeKind**: When using `DateTime`, always specify `DateTimeKind.Utc` or `DateTimeKind.Local` +- **Culture-aware formatting**: Use `CultureInfo.InvariantCulture` for serialization/parsing + +### String Operations +- **StringBuilder for concatenation**: Use `StringBuilder` for multiple string concatenations +- **StringComparison**: Always specify `StringComparison` for string operations: + ```csharp + string.Equals(other, StringComparison.OrdinalIgnoreCase) + ``` + +### Memory Management +- **Dispose pattern**: Implement `IDisposable` properly for unmanaged resources +- **Using statements**: Always wrap `IDisposable` objects in using statements +- **Avoid large object heap**: Keep objects under 85KB to avoid LOH allocation + +### Configuration +- **Use ConfigurationManager**: Access app settings through `ConfigurationManager.AppSettings` +- **Connection strings**: Store in `` section, not `` +- **Transformations**: Use web.config/app.config transformations for environment-specific settings + +### Exception Handling +- **Specific exceptions**: Catch specific exception types, not generic `Exception` +- **Don't swallow exceptions**: Always log or re-throw exceptions appropriately +- **Use using for disposable resources**: Ensures proper cleanup even when exceptions occur + +### Performance Considerations +- **Avoid boxing**: Be aware of boxing/unboxing with value types and generics +- **String interning**: Use `string.Intern()` judiciously for frequently used strings +- **Lazy initialization**: Use `Lazy` for expensive object creation +- **Avoid reflection in hot paths**: Cache `MethodInfo`, `PropertyInfo` objects when possible diff --git a/.github/instructions/dotnet-upgrade.instructions.md b/.github/instructions/dotnet-upgrade.instructions.md new file mode 100644 index 0000000000..e384dc3742 --- /dev/null +++ b/.github/instructions/dotnet-upgrade.instructions.md @@ -0,0 +1,287 @@ +--- +name: ".NET Framework Upgrade Specialist" +description: "Specialized agent for comprehensive .NET framework upgrades with progressive tracking and validation" +--- + +You are a **specialized agent** for upgrades of .NET Framework. Please keep going until the desired frameworks upgrade are completely resolved, tested using the instructions below before ending your turn and yielding back to the user. + +Your thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough. + +You **MUST iterate** and keep going until the problem is solved. + +# .NET Project Upgrade Instructions + +This document provides structured guidance for upgrading a multi-project .NET solution to a higher framework version (e.g., .NET 6 → .NET 8). Upgrade this repository to the latest supported **.NET Core**, **.NET Standard**, or **.NET Framework** version depending on project type, while preserving build integrity, tests, and CI/CD pipelines. +Follow the steps **sequentially** and **do not attempt to upgrade all projects at once**. + +## Preparation +1. **Identify Project Type** + - Inspect each `*.csproj`: + - `netcoreapp*` → **.NET Core / .NET (modern)** + - `netstandard*` → **.NET Standard** + - `net4*` (e.g., net472) → **.NET Framework** + - Note the current target and SDK. + +2. **Select Target Version** + - **.NET (Core/Modern)**: Upgrade to the latest LTS (e.g., `net8.0`). + - **.NET Standard**: Prefer migrating to **.NET 6+** if possible. If staying, target `netstandard2.1`. + - **.NET Framework**: Upgrade to at least **4.8**, or migrate to .NET 6+ if feasible. + +3. **Review Release Notes & Breaking Changes** + - [.NET Core/.NET Upgrade Docs](https://learn.microsoft.com/dotnet/core/whats-new/) + - [.NET Framework 4.x Docs](https://learn.microsoft.com/dotnet/framework/whats-new/) + +--- + +## 1. Upgrade Strategy +1. Upgrade **projects sequentially**, not all at once. +2. Start with **independent class library projects** (least dependencies). +3. Gradually move to projects with **higher dependencies** (e.g., APIs, Azure Functions). +4. Ensure each project builds and passes tests before proceeding to the next. +5. Post Builds are successfull **only after success completion** update the CI/CD files + +--- + +## 2. Determine Upgrade Sequence +To identify dependencies: +- Inspect the solution’s dependency graph. +- Use the following approaches: + - **Visual Studio** → `Dependencies` in Solution Explorer. + - **dotnet CLI** → run: + ```bash + dotnet list .csproj reference + ``` + - **Dependency Graph Generator**: + ```bash + dotnet msbuild .sln /t:GenerateRestoreGraphFile /p:RestoreGraphOutputPath=graph.json + ``` + Inspect `graph.json` to see the dependency order. + +--- + +## 3. Analyze Each Project +For each project: +1. Open the `*.csproj` file. + Example: + ```xml + + + net6.0 + + + + + + + ``` + +2. Check for: + - `TargetFramework` → Change to the desired version (e.g., `net8.0`). + - `PackageReference` → Verify if each NuGet package supports the new framework. + - Run: + ```bash + dotnet list package --outdated + ``` + Update packages: + ```bash + dotnet add package --version + ``` + +3. If `packages.config` is used (legacy), migrate to `PackageReference`: + ```bash + dotnet migrate + ``` + + +4. Upgrade Code Adjustments +After analyzing the nuget packages, review code for any required changes. + +### Examples +- **System.Text.Json vs Newtonsoft.Json** + ```csharp + // Old (Newtonsoft.Json) + var obj = JsonConvert.DeserializeObject(jsonString); + + // New (System.Text.Json) + var obj = JsonSerializer.Deserialize(jsonString); +IHostBuilder vs WebHostBuilder + +csharp +Copy code +// Old +IWebHostBuilder builder = new WebHostBuilder(); + +// New +IHostBuilder builder = Host.CreateDefaultBuilder(args); +Azure SDK Updates + +csharp +Copy code +// Old (Blob storage SDK v11) +CloudBlobClient client = storageAccount.CreateCloudBlobClient(); + +// New (Azure.Storage.Blobs) +BlobServiceClient client = new BlobServiceClient(connectionString); + + +--- + +## 4. Upgrade Process Per Project +1. Update `TargetFramework` in `.csproj`. +2. Update NuGet packages to versions compatible with the target framework. +3. After upgrading and restoring the latest DLLs, review code for any required changes. +4. Rebuild the project: + ```bash + dotnet build .csproj + ``` +5. Run unit tests if any: + ```bash + dotnet test + ``` +6. Fix build or runtime issues before proceeding. + + +--- + +## 5. Handling Breaking Changes +- Review [.NET Upgrade Assistant](https://learn.microsoft.com/dotnet/core/porting/upgrade-assistant) suggestions. +- Common issues: + - Deprecated APIs → Replace with supported alternatives. + - Package incompatibility → Find updated NuGet or migrate to Microsoft-supported library. + - Configuration differences (e.g., `Startup.cs` → `Program.cs` in .NET 6+). + + +--- + +## 6. Validate End-to-End +After all projects are upgraded: +1. Rebuild entire solution. +2. Run all automated tests (unit, integration). +3. Deploy to a lower environment (UAT/Dev) for verification. +4. Validate: + - APIs start without runtime errors. + - Logging and monitoring integrations work. + - Dependencies (databases, queues, caches) connect as expected. + + +--- + +## 7. Tools & Automation +- **.NET Upgrade Assistant**(Optional): + ```bash + dotnet tool install -g upgrade-assistant + upgrade-assistant upgrade .sln``` + +- **Upgrade CI/CD Pipelines**: + When upgrading .NET projects, remember that build pipelines must also reference the correct SDK, NuGet versions, and tasks. + a. Locate pipeline YAML files + - Check common folders such as: + - .azuredevops/ + - .pipelines/ + - Deployment/ + - Root of the repo (*.yml) + +b. Scan for .NET SDK installation tasks + Look for tasks like: + - task: UseDotNet@2 + inputs: + version: + + or + displayName: Use .NET Core sdk + +c. Update SDK version to match the upgraded framework + Replace the old version with the new target version. + Example: + - task: UseDotNet@2 + displayName: Use .NET SDK + inputs: + version: + includePreviewVersions: true # optional, if upgrading to a preview release + +d. Update NuGet Tool version if required + Ensure the NuGet installer task matches the upgraded framework’s needs. + Example: + - task: NuGetToolInstaller@0 + displayName: Use NuGet + inputs: + versionSpec: + checkLatest: true + +e. Validate the pipeline after updates + - Commit changes to a feature branch. + - Trigger a CI build to confirm: + - The YAML is valid. + - The SDK is installed successfully. + - Projects restore, build, and test with the upgraded framework. + +--- + +## 8. Commit Plan +- Always work on the specified branch or branch provided in context, if no branch specified create a new branch (`upgradeNetFramework`). +- Commit after each successful project upgrade. +- If a project fails, rollback to the previous commit and fix incrementally. + + +--- + +## 9. Final Deliverable +- Fully upgraded solution targeting the desired framework version. +- Updated documentation of upgraded dependencies. +- Test results confirming successful build & execution. + +--- + + +## 10. Upgrade Checklist (Per Project) + +Use this table as a sample to track the progress of the upgrade across all projects in the solution and add this in the PullRequest + +| Project Name | Target Framework | Dependencies Updated | Builds Successfully | Tests Passing | Deployment Verified | Notes | +|--------------|------------------|-----------------------|---------------------|---------------|---------------------|-------| +| Project A | ☐ net8.0 | ☐ | ☐ | ☐ | ☐ | | +| Project B | ☐ net8.0 | ☐ | ☐ | ☐ | ☐ | | +| Project C | ☐ net8.0 | ☐ | ☐ | ☐ | ☐ | | + +> ✅ Mark each column as you complete the step for every project. + +## 11. Commit & PR Guidelines + +- Use a **single PR per repository**: + - Title: `Upgrade to .NET [VERSION]` + - Include: + - Updated target frameworks. + - NuGet upgrade summary. + - Provide test results as summarized above. +- Tag with `breaking-change` if APIs were replaced. + +## 12. Multi-Repo Execution (Optional) + +For organizations with multiple repositories: +1. Store this `instructions.md` in a central upgrade template repo. +2. Provide SWE Agent / Cursor with: + ``` + Upgrade all repositories to latest supported .NET versions following instructions.md + ``` +3. Agent should: + - Detect project type per repo. + - Apply the appropriate upgrade path. + - Open PRs for each repo. + + +## 🔑 Notes & Best Practices + +- **Prefer Migration to Modern .NET** + If on .NET Framework or .NET Standard, evaluate moving to .NET 6/8 for long-term support. +- **Automate Tests Early** + CI/CD should block merges if tests fail. +- **Incremental Upgrades** + Large solutions may require upgrading one project at a time. + + ### ✅ Example Agent Prompt + + > Upgrade this repository to the latest supported .NET version following the steps in `dotnet-upgrade-instructions.md`. + > Detect project type (.NET Core, Standard, or Framework) and apply the correct migration path. + > Ensure all tests pass and CI/CD workflows are updated. + +--- diff --git a/.github/instructions/installer.instructions.md b/.github/instructions/installer.instructions.md new file mode 100644 index 0000000000..447f4ce993 --- /dev/null +++ b/.github/instructions/installer.instructions.md @@ -0,0 +1,144 @@ +--- +applyTo: "FLExInstaller/**" +name: "installer.instructions" +description: "FieldWorks installer (WiX) development guidelines" +--- +# Installer development guidelines (WiX 3 default, WiX 6 opt-in) + +## Purpose & Scope +Guidance for building, validating, and debugging the FieldWorks installer for both toolsets: MSI (`FieldWorks.msi`) and bootstrapper bundle (`FieldWorksBundle.exe`). + +## Key facts (FieldWorks repo) +- WiX 3 inputs live in `FLExInstaller/` (legacy batch pipeline; default). +- WiX 6 projects live in `FLExInstaller/wix6/`: + - `FieldWorks.Installer.wixproj` (MSI) + - `FieldWorks.Bundle.wixproj` (bundle) +- WiX 3 builds require the **Visual Studio WiX Toolset v3 extension** so `Wix.CA.targets` is available under the MSBuild extensions path. +- Builds are orchestrated via `Build/InstallerBuild.proj` (preferred entrypoints are `./build.ps1` and `./test.ps1`). +- Default installer artifacts (Debug) land under: + - WiX 3: `FLExInstaller\bin\x64\Debug\en-US\FieldWorks.msi` + - WiX 3: `FLExInstaller\bin\x64\Debug\FieldWorksBundle.exe` + - WiX 6: `FLExInstaller\wix6\bin\x64\Debug\en-US\FieldWorks.msi` + - WiX 6: `FLExInstaller\wix6\bin\x64\Debug\FieldWorksBundle.exe` + +## Build commands +```powershell +# Preferred: traversal build wrapper (Debug, WiX 3 default) +./build.ps1 -BuildInstaller + +# WiX 6 opt-in +./build.ps1 -BuildInstaller -InstallerToolset Wix6 + +# Direct MSBuild (equivalent to the orchestration target) +msbuild Build/InstallerBuild.proj /t:BuildInstaller /p:Configuration=Debug /p:Platform=x64 + +# Direct MSBuild (WiX 6 opt-in) +msbuild Build/InstallerBuild.proj /t:BuildInstaller /p:Configuration=Debug /p:Platform=x64 /p:InstallerToolset=Wix6 +``` + +## Debugging flow: “double-click does nothing” +When a bundle/MSI exits with no UI, assume one of: +1) immediate process exit (argument parsing, prerequisite detection, policy) +2) crash before UI (bad BA init, missing dependency, load failure) +3) UI suppressed (quiet/passive mode, elevation issue) + +Work from most observable → deepest inspection: + +### 1) Run via the repo helper script (recommended) +Use the agent-friendly wrapper script to run the installer and collect logs in a deterministic evidence folder: +```powershell +# Bundle (default path for the chosen configuration/platform) +.\scripts\Agent\Invoke-Installer.ps1 -InstallerType Bundle -Configuration Debug -Platform x64 + +# Bundle with extra args and additional temp-log capture (useful for chained package logs) +.\scripts\Agent\Invoke-Installer.ps1 -InstallerType Bundle -Configuration Release -Arguments @('/passive') -IncludeTempLogs + +# MSI (direct Windows Installer engine, with /l*v logging) +.\scripts\Agent\Invoke-Installer.ps1 -InstallerType Msi -Configuration Debug -Platform x64 + +# Custom path (if you're testing a copied artifact) +.\scripts\Agent\Invoke-Installer.ps1 -InstallerType Bundle -InstallerPath 'C:\Path\To\FieldWorksBundle.exe' +``` + +Notes: +- The script writes evidence under `Output\InstallerEvidence\\` and prints the primary log path. +- `-IncludeTempLogs` copies common related logs from `%TEMP%` that were written during the run. + +### 1a) Manual commands (when you need full control) +Create an evidence folder and run explicitly: +```powershell +$e = Join-Path $env:TEMP ('FwInstallerEvidence\\' + (Get-Date -Format yyyy-MM-dd)) +$null = New-Item -ItemType Directory -Force -Path $e + +# Bundle (preferred for end-user scenario, WiX 3 default) +.\FLExInstaller\bin\x64\Debug\FieldWorksBundle.exe /log "$e\bundle.log" + +# Bundle (WiX 6 opt-in) +.\FLExInstaller\wix6\bin\x64\Debug\FieldWorksBundle.exe /log "$e\bundle.log" + +# MSI (direct Windows Installer engine, WiX 3 default) +msiexec /i .\FLExInstaller\bin\x64\Debug\en-US\FieldWorks.msi /l*v "$e\msi-install.log" + +# MSI (WiX 6 opt-in) +msiexec /i .\FLExInstaller\wix6\bin\x64\Debug\en-US\FieldWorks.msi /l*v "$e\msi-install.log" +``` + +Notes: +- Windows Installer requires the target log directory to exist; if logging fails, create the folder first. + +### 2) Check Windows Event Viewer and crash dumps +If there is still “nothing”, check for an early crash: +- Event Viewer → Windows Logs → Application + - `.NET Runtime` and `Application Error` + - `MsiInstaller` events for MSI failures +- Crash dumps: `%LOCALAPPDATA%\CrashDumps\` for `*.dmp` related to the bundle or `msiexec.exe`. + +### 3) Re-run with reduced noise (optional) +MSI UI levels can hide dialogs; explicitly request UI: +```powershell +# Full UI (if authored, WiX 3 default): +msiexec /i .\FLExInstaller\bin\x64\Debug\en-US\FieldWorks.msi /qf /l*v C:\Temp\FwInstallerEvidence\msi-ui.log + +# Full UI (WiX 6 opt-in): +msiexec /i .\FLExInstaller\wix6\bin\x64\Debug\en-US\FieldWorks.msi /qf /l*v C:\Temp\FwInstallerEvidence\msi-ui.log + +# Basic UI (progress only, WiX 3 default): +msiexec /i .\FLExInstaller\bin\x64\Debug\en-US\FieldWorks.msi /qb /l*v C:\Temp\FwInstallerEvidence\msi-basic.log + +# Basic UI (WiX 6 opt-in): +msiexec /i .\FLExInstaller\wix6\bin\x64\Debug\en-US\FieldWorks.msi /qb /l*v C:\Temp\FwInstallerEvidence\msi-basic.log +``` + +### 4) If the bundle starts but install fails: differentiate bundle vs MSI +- Bundle log shows prerequisite detection, downloads, and chaining decisions. +- MSI log shows property resolution, component/file install, registry writes, custom action execution. + +### 5) If custom actions are suspected +- Search the MSI log for: + - `Return value 3` (classic MSI failure marker) + - `CustomAction` lines and the action name that failed +- Prefer reproducing with a single variable change at a time (different App/Data dirs; feature set; upgrade vs clean). + +## WiX v6 build-time diagnostics (authoring/build) +- WiX v6 is used via SDK-style `.wixproj` with MSBuild properties. +- Useful knobs live on the project (or can be passed on the command line) such as: + - `DefineConstants` (preprocessor variables) + - `SuppressIces` / `Ices` / `SuppressValidation` (validation control) + - `VerboseOutput` (more build output) + - `*AdditionalOptions` properties to pass arbitrary `wix.exe` args + +## Harvesting note (WiX v6) +- FieldWorks currently uses Heat (via `WixToolset.Heat` NuGet) to generate harvested `.wxs` inputs. +- Heat emits a deprecation warning (HEAT5149) and is expected to go away in WiX v7; treat this as technical debt to retire. + +## Spec-backed verification (what to capture) +- Verification matrix: `specs/001-wix-v6-migration/verification-matrix.md` +- Golden install checklist: `specs/001-wix-v6-migration/golden-install-checklist.md` +- Recommended evidence folder convention: `C:\Temp\FwInstallerEvidence\YYYY-MM-DD\` + +## References +- Windows Installer logging & command line: + - https://learn.microsoft.com/windows/win32/msi/windows-installer-logging + - https://learn.microsoft.com/windows/win32/msi/command-line-options +- WiX v6 MSBuild SDK concepts/properties: + - https://docs.firegiant.com/wix/tools/msbuild/ diff --git a/.github/instructions/managed.instructions.md b/.github/instructions/managed.instructions.md new file mode 100644 index 0000000000..b8c7039fb0 --- /dev/null +++ b/.github/instructions/managed.instructions.md @@ -0,0 +1,114 @@ +--- +applyTo: "**/*.{cs,xaml,config,resx}" +name: "managed.instructions" +description: "FieldWorks managed (.NET/C#) development guidelines" +--- + +# Managed development guidelines for C# and .NET + +## Purpose & Scope +This file describes conventions, deterministic requirements, and best practices for managed (.NET/C#) development in FieldWorks. + +## Platform Support +- **Windows only**: FieldWorks targets .NET Framework 4.8 on Windows x64. +- **Mono is deprecated**: Mono/Linux support was removed. When you encounter `Platform.IsMono` checks, `TODO-Linux` comments, or other Mono-specific code, remove it when practical. Do not add new Mono-specific code paths. + +## Context loading +- Review `.github/src-catalog.md` and `Src//AGENTS.md` for component responsibilities and entry points. +- Follow localization patterns (use .resx resources; avoid hardcoded UI strings). Crowdin sync is configured via `crowdin.json`. + +## Deterministic requirements +- Threading: UI code must run on the UI thread; prefer async patterns for long-running work. Avoid deadlocks; do not block the UI. +- Exceptions: Fail fast for unrecoverable errors; log context. Avoid swallowing exceptions. +- Encoding: Favor UTF-16/UTF-8; be explicit at interop boundaries; avoid locale-dependent APIs. +- Tests: Co-locate unit/integration tests under `Src/.Tests` (NUnit patterns are common). Keep tests deterministic and portable. +- Resources: Place images/strings in resource files; avoid absolute paths; respect `.editorconfig`. + +## TDD default for C# fixes +- For bug fixes and behavior changes, default to Red-Green-Refactor: + 1) add or update a test that fails first, + 2) implement the smallest production change to pass, + 3) refactor while keeping tests green. +- If you already have partial code edits and need to focus on test-first flow, use `git stash` to hold changes while implementing the tests to fail, then bring changes back with `git stash apply`. +- Favor fast, isolated tests (no infrastructure dependencies) and keep one behavior per test when practical. +- Prefer AAA structure (Arrange, Act, Assert) and descriptive names (`Method_Scenario_ExpectedResult`). + +## Key Rules +- Use existing patterns for localization, unit tests, and avoid runtime-incompatible behaviors. +- Keep public APIs stable and documented with XML docs. +- **AssemblyInfo Policy**: + - All managed projects must link `Src/CommonAssemblyInfo.cs` via ``. + - Set `false` to prevent SDK duplicate attribute errors. + - Restore and maintain project-specific `AssemblyInfo*.cs` files if custom attributes are required. + - Use `scripts/GenerateAssemblyInfo/validate_generate_assembly_info.py` to verify compliance. + +## Test exclusion conversion playbook (Pattern A standard) +- Always prefer explicit `Tests/**` exclusions. For nested test folders add matching explicit entries (for example `Component/ComponentTests/**`). +- Audit current state before making changes: + ```powershell + python -m scripts.audit_test_exclusions + ``` + Inspect the resulting CSV/JSON plus the generated `Output/test-exclusions/mixed-code.json` and Markdown issue templates under `Output/test-exclusions/escalations/` before touching `.csproj` files. +- Convert projects in deterministic batches using dry-run mode first: + ```powershell + python -m scripts.convert_test_exclusions --input Output/test-exclusions/report.json --batch-size 15 --dry-run + ``` + Remove `--dry-run` once you are satisfied with the diff. The converter rewrites only the targeted SDK-style projects and inserts the explicit `` + `` pairs. +- Typical conversion (Pattern B ➜ Pattern A): + ```xml + + + + + + + + + + + + ``` +- After each batch, rerun the audit command so `patternType` values and `ValidationIssue` records stay current, then update `Directory.Build.props` comments and any affected `Src/**/AGENTS.md` files to reflect the new pattern. + +## Mixed-code escalation workflow +- Use `scripts/test_exclusions/escalation_writer.py` outputs (stored under `Output/test-exclusions/escalations/`) to open the pre-filled GitHub issue template for each project. Attach: + - The audit/validator excerpts showing the mixed folders. + - A short summary of the blocking files and the owning team/contact. + - A proposed remediation plan (e.g., split helpers into a dedicated test project). +- Track the escalation link inside your working notes/PR description so reviewers can confirm every mixed-code violation has an owner before merging conversions. + +## Test exclusion validation checklist +- Run the validator CLI locally for every PR touching exclusions: + ```powershell + python -m scripts.validate_test_exclusions --fail-on-warning --json-report Output/test-exclusions/validator.json + ``` + This enforces “Pattern A only”, ensures all detected test folders are excluded, and fails on mixed-code records or CS0436 parsing hits. +- Use the Agent wrapper when running in CI or automation: + ```powershell + pwsh Build/Agent/validate-test-exclusions.ps1 -FailOnWarning + ``` + The wrapper chains the Python validator, MSBuild invocation, and CS0436 log parsing so agent runs match local expectations. +- Guard against leaked test types before publishing artifacts: + ```powershell + pwsh scripts/test_exclusions/assembly_guard.ps1 -Assemblies "Output/Debug/**/*.dll" + ``` + The guard loads each assembly and fails when any type name ends in `Test`/`Tests`. Include the log in release sign-off packages. +- Document the validation evidence (validator JSON, PowerShell transcript, assembly guard output) in the PR description alongside the rerun audit results. + +## Examples +```csharp +// Minimal example of public API with XML docs +/// Converts foo to bar +public Bar Convert(Foo f) { ... } +``` + +## Structured output +- Public APIs include XML docs; keep namespaces consistent. +- Include minimal tests (happy path + one edge case) when modifying behavior. +- Follow existing project/solution structure; avoid creating new top-level patterns without consensus. + +## References +- Build: `msbuild FieldWorks.sln /m /p:Configuration=Debug` +- Tests: Use Test Explorer or `dotnet test` for SDK-style; NUnit console for .NET Framework assemblies. +- Localization: See `DistFiles/CommonLocalizations/` and `crowdin.json`. + diff --git a/.github/instructions/native.instructions.md b/.github/instructions/native.instructions.md new file mode 100644 index 0000000000..497c11c3ab --- /dev/null +++ b/.github/instructions/native.instructions.md @@ -0,0 +1,69 @@ +--- +applyTo: "**/*.{cpp,h,hpp,cc,ixx,def}" +name: "native.instructions" +description: "FieldWorks native (C++/C++-CLI) development guidelines" +--- + +# Native development guidelines for C++ and C++/CLI + +## Purpose & Scope +This file outlines conventions and patterns for native C++/C++-CLI code used in FieldWorks, including interop rules and build-specific requirements. + +## Context loading +- Review `Src//AGENTS.md` for managed/native boundaries and interop contracts. +- Include/Lib paths are injected by build props/targets; avoid ad-hoc project configs. + +## Deterministic requirements +- Memory & RAII: Prefer smart pointers and RAII for resource management. +- Interop boundaries: Define clear marshaling rules (strings/arrays/structs). Avoid throwing exceptions across managed/native boundaries; translate appropriately. +- SAL annotations and warnings: Use SAL where feasible; keep warning level strict; fix warnings, don’t suppress casually. +- Encoding: Be explicit about UTF-8/UTF-16 conversions; do not rely on locale defaults. +- Threading: Document thread-affinity for UI and shared objects. + +## Structured output +- Header hygiene: Minimize transitive includes; prefer forward declarations where reasonable. +- ABI stability: Avoid breaking binary interfaces used by C# or other native modules without coordinated changes. +- Tests: Favor deterministic unit tests; isolate filesystem/registry usage. + +## Key Rules +- Enforce RAII and prefer checked operations for buffer management. +- Keep interop marshaling rules well documented and ensure managed tests exist for early validation. + +## Native C++ Tests + +### Test Framework +Native C++ tests use the **Unit++** framework (legacy). Test projects: +- `Src/Generic/Test/` → `testGenericLib.exe` +- `Src/views/Test/` → `TestViews.exe` + +### Building Tests +Build from VS Developer Command Prompt: +```cmd +cd Src\Generic\Test +nmake /nologo BUILD_CONFIG=Debug BUILD_TYPE=d BUILD_ROOT=%CD%\..\..\..\ BUILD_ARCH=x64 /f testGenericLib.mak +``` + +### vcxproj Project Structure +The vcxproj files are "Makefile" projects (`ConfigurationType=Makefile`) that: +- Wrap nmake invocations via `NMakeBuildCommandLine` +- Require environment variables: `BUILD_ROOT`, `BUILD_CONFIG`, `BUILD_TYPE`, `BUILD_ARCH` +- Cannot be built via plain `msbuild` without VS toolchain in PATH + +### Test Dependencies +- Main native libraries (Generic.lib, DebugProcs.dll) must be built first +- ICU 70 DLLs required at runtime +- `Bin/CollectUnit++Tests.cmd` generates test registration code + +### Future Direction +Migration to GoogleTest is planned for better IDE integration. See `specs/007-test-modernization-vstest/native-migration-plan.md`. + +## Examples +```cpp +// Prefer RAII +std::unique_ptr buf = std::make_unique(size); +``` + +## References +- Build: Use top-level solution/scripts to ensure props/targets are loaded. +- Interop: Coordinate with corresponding managed components in `Src/`. + diff --git a/.github/instructions/repo.instructions.md b/.github/instructions/repo.instructions.md new file mode 100644 index 0000000000..cddb75eb91 --- /dev/null +++ b/.github/instructions/repo.instructions.md @@ -0,0 +1,14 @@ +--- +applyTo: "**/*" +name: "repo.instructions" +description: "High-level repository rules that assist AI coding agents and automated code review." +--- + +# FieldWorks: Repo-wide Guidance (short) + +## Purpose & Scope +Provide clear, concise, and enforceable rules that help AI coding agents and automated code review offer relevant suggestions and reviews. + +## Rules (high-impact, short) +- Prefer the repository top-level build (`.\build.ps1`) and solution (`FieldWorks.sln`) for full builds. +- Keep localization consistent: use `.resx` and follow `crowdin.json` for crowdin integration. diff --git a/.github/instructions/security.instructions.md b/.github/instructions/security.instructions.md new file mode 100644 index 0000000000..114ecb3636 --- /dev/null +++ b/.github/instructions/security.instructions.md @@ -0,0 +1,21 @@ +--- +applyTo: "**/*" +name: "security.instructions" +description: "Security expectations and OWASP-derived rules for code changes in FieldWorks" +--- + +# Security & Secure-by-default + +## Purpose & Scope +- Provide concise, actionable security checks that reviewers and agents should enforce. + +## Rules +- Sanitize all untrusted input before passing to native boundaries or COM APIs. +- Avoid hardcoded secrets or credentials; enforce use of env vars or secrets stores. +- Review changes that touch interop layers (C# `<->` C++/CLI) for buffer handling / marshaling issues. + +## Automated Checks +- Run static analyzers for C# & C++ where possible; surface results in CI. + +## Examples & Quick Checks +- For C++ code manipulating buffers, prefer usage of checked APIs and unit tests that validate boundaries. diff --git a/.github/instructions/terminal.instructions.md b/.github/instructions/terminal.instructions.md new file mode 100644 index 0000000000..9d91a98ba6 --- /dev/null +++ b/.github/instructions/terminal.instructions.md @@ -0,0 +1,34 @@ +--- +applyTo: "**/*" +name: "terminal.instructions" +description: "Terminal command patterns for auto-approval in FieldWorks" +--- + +# Terminal Commands + +Commands with pipes (`|`), `&&`, or `2>&1` require manual approval. Use `scripts/Agent/` wrappers instead. + +**MCP-first:** When the `ps-tools` MCP server is running, prefer MCP tools (`Git-Search`, `Read-FileContent`, `Invoke-AgentTask`, `build`, `test`, agent tools) instead of direct terminal commands. Use wrappers only when MCP is unavailable. + +## Transformations + +| ❌ Blocked | ✅ Use Instead | +|-----------|----------------| +| `git log \| head -20` | `.\scripts\Agent\Git-Search.ps1 -Action log -HeadLines 20` | +| `git show ref:file \| head -50` | `.\scripts\Agent\Git-Search.ps1 -Action show -Ref "ref" -Path "file" -HeadLines 50` | +| `git diff ref -- path` | `.\scripts\Agent\Git-Search.ps1 -Action diff -Ref "ref" -Path "path"` | +| `Get-Content file \| Select -First N` | `.\scripts\Agent\Read-FileContent.ps1 -Path "file" -HeadLines N` | +| `Get-Content file \| Select-String pat` | `.\scripts\Agent\Read-FileContent.ps1 -Path "file" -Pattern "pat"` | + +## Scripts + +| Script | Purpose | +|--------|---------| +| `Git-Search.ps1` | git show/diff/log/grep/blame | +| `Read-FileContent.ps1` | File reading with filtering | + +**Build/test**: Run `.\build.ps1` or `.\test.ps1` directly—they're auto-approvable. +## Beads CLI (auto-approvable patterns) +- `br` and `bv --robot-*` commands are auto-approvable **when they do not use pipes, `&&`, or redirection**. +- Prefer `--json`/`--robot` output and parse in the agent instead of piping to `jq`. +- `br sync --flush-only` does **not** run git; you must run git commands separately. \ No newline at end of file diff --git a/.github/instructions/testing.instructions.md b/.github/instructions/testing.instructions.md new file mode 100644 index 0000000000..6cfc77a5e6 --- /dev/null +++ b/.github/instructions/testing.instructions.md @@ -0,0 +1,94 @@ +--- +applyTo: "**/*.{cs,cpp,h}" +name: "testing.instructions" +description: "FieldWorks testing guidelines (unit/integration)" +--- +# Testing guidelines + +## Purpose & Scope +Guidance for writing and running deterministic unit and integration tests for FieldWorks. +**CRITICAL**: Always use the provided PowerShell scripts (`test.ps1`, `build.ps1`) to run tests. These scripts environment setup, and dependency management automatically. + +## Core Rules +1. **Always use scripts**: Never run `vstest.console.exe`, `nmake`, or `msbuild` directly for testing unless debugging the build system itself. +2. **Build First**: Ensure tests are built before running. `test.ps1` builds by default unless `-NoBuild` is passed. + +## Running Tests (Managed) + +Use `.\test.ps1` for all managed (C#) tests. + +```powershell +# Run all tests (builds first) +.\test.ps1 + +# Run specific project +.\test.ps1 -TestProject "Src/Common/FwUtils/FwUtilsTests" + +# Run with filter +.\test.ps1 -TestFilter "TestCategory!=Slow" + +# Run without rebuilding (faster iteration) +.\test.ps1 -NoBuild -TestProject "FwUtilsTests" +``` + +## Running Tests (Native C++) + +Use `.\test.ps1 -Native` for native (C++) tests. This wraps `Build/scripts/Invoke-CppTest.ps1`. + +```powershell +# Build and run TestGeneric (default) +.\test.ps1 -Native + +# Build and run TestViews +.\test.ps1 -Native -TestProject TestViews + +# Run without rebuilding +.\test.ps1 -Native -TestProject TestViews -NoBuild +``` + +## Building Tests + +Tests are built automatically by `test.ps1`. To build explicitly without running: + +```powershell +# Build all managed tests +.\build.ps1 -BuildTests + +# Build native tests (via Invoke-CppTest backend) +.\Build\scripts\Invoke-CppTest.ps1 -Action Build -TestProject TestViews +``` + +## Script Architecture + +The testing infrastructure relies on shared PowerShell modules for consistency: +- **`test.ps1`**: Main entry point. Dispatches to VSTest (managed) or `Invoke-CppTest.ps1` (native). +- **`Build/scripts/Invoke-CppTest.ps1`**: Backend for native C++ tests (MSBuild/NMake). +- **`Build/Agent/FwBuildHelpers.psm1`**: Shared logic for VS environment, and process cleanup. + +## Debugging & Logs +- **Logs**: Build logs are written to `Output/Build.log` (if configured) or standard output. +- **Verbosity**: Use `-Verbosity detailed` with `test.ps1` for more output. +- **Results**: Managed test results (TRX) are saved to `Output//TestResults/`. + +## Writing Tests +- **Managed**: Use NUnit 3.x. See `Src/Common/FwUtils/FwUtilsTests` for examples. +- **Native**: Use Unit++. See `Src/Generic/Test/TestGeneric` for examples. +- **Determinism**: Tests must be hermetic. Avoid external state. + +## TDD workflow for "fix it" requests +- Prefer Red-Green-Refactor for managed fixes: + 1) write the failing test, + 2) make the minimal code change to pass, + 3) refactor with tests green. +- If working tree changes are in progress, use `git stash` to hold changes while implementing the tests to fail, then restore with `git stash apply`. +- Keep tests fast, isolated, repeatable, and self-checking; avoid infrastructure in unit tests. +- Use one Act per test when practical, and prefer parameterized tests over loops/conditionals inside tests. + +## Troubleshooting +- **"File not found"**: Ensure you built with `-BuildTests` (or let `test.ps1` do it). +- **Native Crash**: Native tests are fragile. Use `printf` debugging if the debugger is unavailable. + +## References +- **Test Configuration**: `Test.runsettings` for VSTest settings +- **Test Data**: `TestLangProj/` for integration test data +- **Quickstart**: `specs/007-test-modernization-vstest/quickstart.md` for detailed instructions diff --git a/.github/memory.md b/.github/memory.md new file mode 100644 index 0000000000..f44046a989 --- /dev/null +++ b/.github/memory.md @@ -0,0 +1,10 @@ +# FieldWorks agent memory (curated) + +Use this file to capture decisions and pitfalls that help future agent sessions. +Keep it concise and high-value. + +- Managed ↔ Native boundaries must be coordinated. Avoid throwing exceptions across the boundary; marshal explicitly. +- UI strings should come from .resx; avoid hardcoded user-visible text (Crowdin is configured). +- Prefer CI-style build scripts for reproducibility; installer builds are slow—run only when needed. +- Integration tests often rely on `TestLangProj/`; keep data deterministic. +- Keep `.editorconfig` and CI checks in mind: trailing whitespace, final newline, commit message format. diff --git a/.github/migrate_copilot_format.py b/.github/migrate_copilot_format.py new file mode 100644 index 0000000000..f549f16075 --- /dev/null +++ b/.github/migrate_copilot_format.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +"""One-time helper to migrate AGENTS.md files to the new auto change-log format.""" +from __future__ import annotations + +import argparse +import re +from pathlib import Path +from typing import List, Tuple + +AUTO_START = "" +AUTO_END = "" +AUTO_PLACEHOLDER = """ +## Change Log (auto) + +This section is populated by running: +1. `python .github/plan_copilot_updates.py --folders ` +2. `python .github/copilot_apply_updates.py --folders ` + +Do not edit this block manually; rerun the scripts above after code or doc updates. + +""" + +AUTO_HINT_HEADING = "## References (auto-generated hints)" +SECTION_RE = re.compile(r"^## ", re.MULTILINE) + + +def find_frontmatter_end(text: str) -> int: + if not text.startswith("---\n"): + return 0 + idx = text.find("\n---", 3) + if idx == -1: + return 0 + return idx + len("\n---\n") + + +def remove_auto_hint_section(text: str) -> Tuple[str, bool]: + if AUTO_HINT_HEADING not in text: + return text, False + start = text.find(AUTO_HINT_HEADING) + if start == -1: + return text, False + match = SECTION_RE.search(text, start + len(AUTO_HINT_HEADING)) + end = match.start() if match else len(text) + updated = (text[:start].rstrip() + "\n\n" + text[end:].lstrip()).strip() + "\n" + return updated, True + + +def ensure_auto_block(text: str) -> Tuple[str, bool]: + if AUTO_START in text and AUTO_END in text: + return text, False + fm_end = find_frontmatter_end(text) + before = text[:fm_end].rstrip() + after = text[fm_end:].lstrip() + pieces = [part for part in [before, AUTO_PLACEHOLDER.strip(), after] if part] + updated = "\n\n".join(pieces) + "\n" + return updated, True + + +def migrate_file(path: Path, dry_run: bool) -> Tuple[bool, List[str]]: + text = path.read_text(encoding="utf-8", errors="replace") + notes: List[str] = [] + text, removed = remove_auto_hint_section(text) + if removed: + notes.append("removed auto hints section") + text, inserted = ensure_auto_block(text) + if inserted: + notes.append("inserted auto change-log placeholder") + if not notes: + return False, [] + if not dry_run: + path.write_text(text, encoding="utf-8") + return True, notes + + +def resolve_folders(root: Path, folders: List[str], include_all: bool) -> List[Path]: + targets = set() + for folder in folders: + rel = folder.replace("\\", "/") + if rel.startswith("Src/"): + targets.add(rel) + else: + abs_path = Path(folder) + if not abs_path.is_absolute(): + abs_path = (root / folder).resolve() + rel = abs_path.relative_to(root).as_posix() + targets.add(rel) + if include_all or not targets: + for copilot in root.glob("Src/**/AGENTS.md"): + targets.add(copilot.parent.relative_to(root).as_posix()) + paths = [] + for rel in sorted(targets): + copilot = root / rel / "AGENTS.md" + if copilot.exists(): + paths.append(copilot) + return paths + + +def main() -> int: + ap = argparse.ArgumentParser(description="Migrate AGENTS.md files to new auto-block format") + ap.add_argument("--root", default=str(Path.cwd())) + ap.add_argument("--folders", nargs="*", default=[], help="Specific Src/ entries") + ap.add_argument("--all", action="store_true", help="Process every AGENTS.md under Src/") + ap.add_argument("--dry-run", action="store_true") + args = ap.parse_args() + + root = Path(args.root).resolve() + paths = resolve_folders(root, args.folders, args.all) + if not paths: + print("No AGENTS.md files matched the criteria.") + return 0 + + changed = 0 + for copilot_path in paths: + updated, notes = migrate_file(copilot_path, args.dry_run) + if updated: + changed += 1 + action = "DRY" if args.dry_run else "UPDATED" + print(f"[{action}] {copilot_path}: {', '.join(notes)}") + else: + print(f"[SKIP] {copilot_path}: already compliant") + + if args.dry_run: + print(f"Dry run complete. {changed} file(s) would change.") + else: + print(f"Migration complete. {changed} file(s) updated.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/.github/option3-plan.md b/.github/option3-plan.md new file mode 100644 index 0000000000..a90d9c97bf --- /dev/null +++ b/.github/option3-plan.md @@ -0,0 +1,50 @@ +# Option 3 plan: Outer-loop automation and MCP integration (pilot later) + +This plan is mothballed for now. It captures the steps to bring our agent workflows into CI/CD with safe tool boundaries. + +## Goals +- Run selected prompts reliably in CI (e.g., spec validation, test failure triage) +- Use least-privilege MCP tools per role/chat mode +- Package agent primitives for sharing and repeatability + +## Steps + +### 1) Agent CLI and APM scaffold +- Add `apm.yml` with scripts mapping to our prompts (e.g., `agent-feature-spec` → feature-spec.prompt.md) +- Include MCP dependencies (e.g., `ghcr.io/github/github-mcp-server`) +- Document local usage in README: `apm install`, `apm run agent-feature-spec --param specFile=...` + +### 2) GitHub Action to run a prompt on PR +- Create `.github/workflows/agent-workflow.yml` +- Matrix run for selected scripts (e.g., spec validation, debug mode) +- Permissions: `pull-requests: write`, `contents: read`, `models: read` +- Post results as PR comments or check summaries + +### 3) MCP servers and boundaries +- Start with GitHub MCP server for PR/issue context and Filesystem MCP for repo search +- Restrict tools by chat mode (e.g., installer mode cannot edit native code) +- Maintain a curated list in `.github/context/mcp.servers.md` (to be created when piloting) + +### 4) Security and secrets +- Use `secrets.AGENT_CLI_PAT` for agent CLI (if needed) +- Principle of least privilege for tokens and tool scopes +- Add a security review checklist for new tools/servers + +### 5) Governance and validation +- Add a `lint-docs` CI job to verify presence and links for: + - `.github/instructions/*.instructions.md` + - `Src/*/AGENTS.md` + - `.github/src-catalog.md` +- Add a `prompt-validate` job: checks frontmatter structure for `.prompt.md` + +### 6) Rollout strategy +- Pilot a single prompt (e.g., `test-failure-debug.prompt.md`) that makes no file edits and only posts analysis +- Gather feedback and iterate before enabling write-capable workflows + +## References +- `.github/AGENTS.md` (entry points) +- `.github/prompts/` (agent workflows) +- `.github/instructions/` (domain rules) +- `.github/chatmodes/` (role boundaries) +- `.github/context/` and `.github/memory.md` (signals and decisions) + diff --git a/.github/plan_copilot_updates.py b/.github/plan_copilot_updates.py new file mode 100644 index 0000000000..bbd8c429de --- /dev/null +++ b/.github/plan_copilot_updates.py @@ -0,0 +1,358 @@ +#!/usr/bin/env python3 +"""Plan focused AGENTS.md refreshes using cached diffs.""" +from __future__ import annotations + +import argparse +import json +import subprocess +from datetime import datetime, timezone +import os +from pathlib import Path +from typing import Dict, List, Optional, Sequence, Tuple + +FILE_GROUPS = { + "Project files": {".csproj", ".vcxproj", ".props", ".targets"}, + "Key C# files": {".cs"}, + "Key C++ files": {".cpp", ".cc", ".c"}, + "Key headers": {".h", ".hpp", ".ixx"}, + "Data contracts/transforms": { + ".xml", + ".xsl", + ".xslt", + ".xsd", + ".dtd", + ".xaml", + ".resx", + ".config", + }, +} +def collect_reference_groups(folder: Path, root: Path, limit_per_group: int = 25) -> Dict[str, List[str]]: + + groups: Dict[str, List[str]] = {k: [] for k in FILE_GROUPS} + skip = {"obj", "bin", "packages", "output", "downloads"} + for dirpath, dirnames, filenames in os.walk(folder): + rel_parts = {p.lower() for p in Path(dirpath).parts} + if rel_parts & skip: + continue + for filename in filenames: + ext = os.path.splitext(filename)[1].lower() + for group_name, exts in FILE_GROUPS.items(): + if ext in exts: + rel = Path(dirpath, filename).relative_to(root) + groups[group_name].append(str(rel).replace("\\", "/")) + break + for key in groups: + groups[key] = sorted(groups[key])[:limit_per_group] + return groups + +from copilot_cache import CopilotCache +from copilot_change_utils import ( + classify_with_status, + compute_risk_score, + summarize_paths, +) +from copilot_tree_hash import compute_folder_tree_hash + + +def run_git(cmd: Sequence[str], cwd: Path) -> str: + return subprocess.check_output(cmd, cwd=str(cwd), stderr=subprocess.STDOUT).decode( + "utf-8", errors="replace" + ) + + +def parse_frontmatter(path: Path) -> Tuple[Optional[Dict[str, str]], str]: + if not path.exists(): + return None, "" + text = path.read_text(encoding="utf-8", errors="replace") + lines = text.splitlines() + if len(lines) < 3 or lines[0].strip() != "---": + return None, text + end_idx = -1 + for idx in range(1, min(len(lines), 200)): + if lines[idx].strip() == "---": + end_idx = idx + break + if end_idx == -1: + return None, text + fm: Dict[str, str] = {} + for raw in lines[1:end_idx]: + stripped = raw.strip() + if not stripped or stripped.startswith("#"): + continue + if ":" in stripped: + key, value = stripped.split(":", 1) + fm[key.strip()] = value.strip().strip('"') + body = "\n".join(lines[end_idx + 1 :]) + return fm, body + + +def read_detect_json(path: Optional[Path]) -> List[Dict[str, object]]: + if not path: + return [] + data = json.loads(path.read_text(encoding="utf-8")) + return data.get("impacted", []) + + +def find_matching_commit( + root: Path, folder_rel: str, folder_path: Path, target_hash: Optional[str], head: str, limit: int +) -> Optional[str]: + if not target_hash or target_hash.startswith("FIXME"): + return None + try: + revs = run_git( + [ + "git", + "rev-list", + "--max-count", + str(limit), + head, + "--", + folder_rel, + ], + root, + ).splitlines() + except subprocess.CalledProcessError: + return None + for commit in revs: + try: + digest = compute_folder_tree_hash(root, folder_path, ref=commit) + except Exception: + continue + if digest == target_hash: + return commit + return None + + +def determine_fallback_base(root: Path, head: str) -> str: + try: + mb = run_git(["git", "merge-base", head, "origin/HEAD"], root).strip() + if mb: + return mb + except Exception: + pass + return f"{head}~1" + + +def git_diff_lines(root: Path, base: str, head: str, folder_rel: str) -> List[str]: + try: + output = run_git( + [ + "git", + "diff", + "--name-status", + f"{base}..{head}", + "--", + folder_rel, + ], + root, + ) + except subprocess.CalledProcessError as exc: + raise RuntimeError(exc.output.decode("utf-8", errors="replace")) from exc + return [line.strip() for line in output.splitlines() if line.strip()] + + +def git_log(root: Path, base: Optional[str], head: str, folder_rel: str) -> List[Dict[str, str]]: + if base: + rev_range = f"{base}..{head}" + else: + rev_range = head + try: + output = run_git( + [ + "git", + "log", + "--date=iso", + "--pretty=format:%H\t%ad\t%s", + rev_range, + "--", + folder_rel, + ], + root, + ) + except subprocess.CalledProcessError: + return [] + entries = [] + for line in output.splitlines(): + parts = line.split("\t", 2) + if len(parts) != 3: + continue + entries.append({"hash": parts[0], "date": parts[1], "summary": parts[2]}) + return entries + + +def build_prompts(folder: str, counts: Dict[str, int], risk: str) -> Dict[str, List[str]]: + total = counts.get("total", 0) + code = counts.get("code", 0) + tests = counts.get("tests", 0) + resources = counts.get("resources", 0) + summary = ( + f"{total} files changed (code={code}, tests={tests}, resources={resources}); risk={risk}." + ) + return { + "doc-refresh": [ + f"Update AGENTS.md for {folder}. Prioritize Purpose/Architecture sections using planner data.", + f"Highlight API or UI updates, then confirm Usage/Test sections reflect {summary}", + "Finish with verification notes and TODOs for manual testing.", + ] + } + + +def build_plan_entry( + root: Path, + folder_rel: str, + head: str, + fallback_base: str, + cache: CopilotCache, + refresh_cache: bool, + search_limit: int, +) -> Optional[Dict[str, object]]: + folder_path = root / folder_rel + if not folder_path.exists(): + return None + copilot_path = folder_path / "AGENTS.md" + fm, _ = parse_frontmatter(copilot_path) + if fm is None: + fm = {} + recorded_tree = fm.get("last-reviewed-tree") + notes: List[str] = [] + if not recorded_tree or recorded_tree.startswith("FIXME"): + notes.append("missing last-reviewed-tree") + try: + current_tree = compute_folder_tree_hash(root, folder_path, ref=head) + except Exception as exc: + notes.append(f"unable to compute current tree: {exc}") + current_tree = None + + cache_entry: Optional[Dict[str, object]] = None + if not refresh_cache and recorded_tree and current_tree: + cache_entry = cache.load_folder(folder_rel, recorded_tree, current_tree) + if cache_entry: + cache_entry = dict(cache_entry) + cache_entry.setdefault("notes", []).extend(notes) + cache_entry["from_cache"] = True + return cache_entry + + recorded_commit = find_matching_commit( + root, folder_rel, folder_path, recorded_tree, head, search_limit + ) + if not recorded_commit and recorded_tree: + notes.append("recorded hash commit not found (consider increasing --search-limit)") + diff_base = recorded_commit or fallback_base + diff_lines = git_diff_lines(root, diff_base, head, folder_rel) + classified = classify_with_status(diff_lines) + counts = summarize_paths([item["path"] for item in classified]) + status_counts: Dict[str, int] = {} + for item in classified: + status_counts[item["status"]] = status_counts.get(item["status"], 0) + 1 + risk = compute_risk_score(counts) + commit_log = git_log(root, recorded_commit or fallback_base, head, folder_rel) + + entry = { + "folder": folder_rel, + "copilot_path": str(copilot_path.relative_to(root)), + "recorded_tree": recorded_tree, + "current_tree": current_tree, + "recorded_commit": recorded_commit, + "diff_base": diff_base, + "risk_score": risk, + "change_counts": counts, + "status_counts": status_counts, + "changes": classified, + "commit_log": commit_log, + "notes": notes, + "prompts": build_prompts(folder_rel, counts, risk), + "from_cache": False, + "reference_groups": collect_reference_groups(folder_path, root), # Add reference groups + } + cache.save_folder(folder_rel, entry) + return entry + + +def resolve_folders( + root: Path, + detect_entries: List[Dict[str, object]], + folders: Optional[List[str]], + include_all: bool, +) -> List[str]: + targets = set() + if folders: + for folder in folders: + rel = folder.replace("\\", "/") + if rel.startswith("Src/"): + targets.add(rel) + else: + abs_path = Path(folder) + if not abs_path.is_absolute(): + abs_path = (root / folder).resolve() + rel = abs_path.relative_to(root).as_posix() + targets.add(rel) + elif detect_entries: + for entry in detect_entries: + folder = entry.get("folder") + status = entry.get("status") + if folder and status != "OK": + targets.add(str(folder)) + if include_all: + for path in sorted(root.glob("Src/**/AGENTS.md")): + targets.add(path.parent.relative_to(root).as_posix()) + return sorted(targets) + + +def main() -> int: + ap = argparse.ArgumentParser(description="Plan AGENTS.md updates with cached diffs") + ap.add_argument("--root", default=str(Path.cwd())) + ap.add_argument("--head", default="HEAD") + ap.add_argument("--detect-json", help="Path to detect_copilot_needed --json output") + ap.add_argument("--folders", nargs="*", help="Explicit Src/ paths") + ap.add_argument("--all", action="store_true", help="Plan for every AGENTS.md folder") + ap.add_argument("--out", default=".cache/copilot/diff-plan.json") + ap.add_argument("--fallback-base", help="Fallback git ref if recorded hash commit is unknown") + ap.add_argument("--refresh-cache", action="store_true") + ap.add_argument("--search-limit", type=int, default=800, help="Max commits to scan per folder") + args = ap.parse_args() + + root = Path(args.root).resolve() + detect_entries = read_detect_json(Path(args.detect_json)) if args.detect_json else [] + targets = resolve_folders(root, detect_entries, args.folders, args.all) + if not targets: + print("No folders to plan. Provide --folders, --all, or --detect-json input.") + return 0 + + fallback_base = args.fallback_base or determine_fallback_base(root, args.head) + cache = CopilotCache(root) + entries: List[Dict[str, object]] = [] + + for folder in targets: + entry = build_plan_entry( + root, + folder, + args.head, + fallback_base, + cache, + args.refresh_cache, + args.search_limit, + ) + if entry: + entries.append(entry) + print( + f"Planned {folder}: {entry['change_counts'].get('total', 0)} files, risk={entry['risk_score']}" + ) + else: + print(f"Skipped {folder} (unable to build entry)") + + output_path = (root / args.out) if not Path(args.out).is_absolute() else Path(args.out) + output_path.parent.mkdir(parents=True, exist_ok=True) + payload = { + "generated_at": datetime.now(timezone.utc).isoformat(), + "head": args.head, + "fallback_base": fallback_base, + "folders": entries, + } + output_path.write_text(json.dumps(payload, indent=2), encoding="utf-8") + print(f"Wrote plan with {len(entries)} folder(s) to {output_path}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/.github/prompts/bugfix.prompt.md b/.github/prompts/bugfix.prompt.md new file mode 100644 index 0000000000..2a601ded6e --- /dev/null +++ b/.github/prompts/bugfix.prompt.md @@ -0,0 +1,38 @@ +# Bugfix workflow (triage → RCA → fix) + +You are an expert FieldWorks engineer. Triage and fix a defect with a validation gate before code changes. + +## Inputs +- failure description or issue link: ${issue} +- logs or stack trace (optional): ${logs} + +## Triage +1) Summarize the failure and affected components +2) Reproduce locally if possible; capture steps or failing test +3) Identify recent changes that could be related + +## Root cause analysis (RCA) +- Hypothesize likely causes (3 candidates) and quick tests to confirm/deny +- Note any managed/native or installer boundary implications + +## Validation gate (STOP) +Do not change files yet. Present: +- Root cause hypothesis and evidence +- Proposed fix (minimal diff) and test changes +- Risk assessment and fallback plan + +Wait for approval before proceeding. + +## Implementation +- Apply the minimal fix aligned with repository conventions +- Ensure localization, threading, and interop rules are respected + +## Tests +- Add/adjust tests to reproduce the original failure and verify the fix +- Prefer deterministic tests; update `TestLangProj/` data only if necessary + +## Handoff checklist +- [ ] Build and local tests pass +- [ ] Commit messages conform to gitlint rules +- [ ] AGENTS.md updated if behavior/contract changed + diff --git a/.github/prompts/copilot-folder-review.prompt.md b/.github/prompts/copilot-folder-review.prompt.md new file mode 100644 index 0000000000..d75b7c5c32 --- /dev/null +++ b/.github/prompts/copilot-folder-review.prompt.md @@ -0,0 +1,29 @@ +# Agent folder review helper + +You are an AI coding agent, reviewing a single FieldWorks folder after documentation updates. Stay focused on the provided folder; do not roam the repo. + +## Inputs +- folder path: ${folder} +- planner JSON snippet (from `.cache/copilot/diff-plan.json`): ${planJson} +- AGENTS.md path: ${agentsFile} +- optional diff summary or PR notes: ${extraContext} + +## Review Goals +1. Confirm AGENTS.md reflects the code/resource changes described in the planner JSON. +2. Flag missing coverage (sections lacking updates, tests absent for code changes, resources not referenced, etc.). +3. List concrete follow-up steps for humans to finish the refresh. + +## Process +1. Load planner data and note high-risk areas (file counts, risk score, commits). +2. Read the AGENTS.md sections most impacted (Purpose, Architecture, Key Components, Usage, Tests). +3. Compare planner insights vs. current text; note mismatches or TODOs. +4. Summarize observations with explicit action items and open questions. + +## Output format +- `status`: `pass`, `warn`, or `block` based on doc coverage. +- `summary`: 3–5 bullet points describing what changed and whether the doc captured it. +- `follow-ups`: numbered list of actionable tasks for humans. +- `questions`: optional list for reviewers/maintainers. + +Keep the response concise (≤ 400 words) and avoid ownership language. Focus on actionable behaviors and verification steps. + diff --git a/.github/prompts/dotnet-best-practices.prompt.md b/.github/prompts/dotnet-best-practices.prompt.md new file mode 100644 index 0000000000..cad0f15e98 --- /dev/null +++ b/.github/prompts/dotnet-best-practices.prompt.md @@ -0,0 +1,84 @@ +--- +agent: 'agent' +description: 'Ensure .NET/C# code meets best practices for the solution/project.' +--- +# .NET/C# Best Practices + +Your task is to ensure .NET/C# code in ${selection} meets the best practices specific to this solution/project. This includes: + +## Documentation & Structure + +- Create comprehensive XML documentation comments for all public classes, interfaces, methods, and properties +- Include parameter descriptions and return value descriptions in XML comments +- Follow the established namespace structure: {Core|Console|App|Service}.{Feature} + +## Design Patterns & Architecture + +- Use primary constructor syntax for dependency injection (e.g., `public class MyClass(IDependency dependency)`) +- Implement the Command Handler pattern with generic base classes (e.g., `CommandHandler`) +- Use interface segregation with clear naming conventions (prefix interfaces with 'I') +- Follow the Factory pattern for complex object creation. + +## Dependency Injection & Services + +- Use constructor dependency injection with null checks via ArgumentNullException +- Register services with appropriate lifetimes (Singleton, Scoped, Transient) +- Use Microsoft.Extensions.DependencyInjection patterns +- Implement service interfaces for testability + +## Resource Management & Localization + +- Use ResourceManager for localized messages and error strings +- Separate LogMessages and ErrorMessages resource files +- Access resources via `_resourceManager.GetString("MessageKey")` + +## Async/Await Patterns + +- Use async/await for all I/O operations and long-running tasks +- Return Task or Task from async methods +- Use ConfigureAwait(false) where appropriate +- Handle async exceptions properly + +## Testing Standards + +- Use MSTest framework with FluentAssertions for assertions +- Follow AAA pattern (Arrange, Act, Assert) +- Use Moq for mocking dependencies +- Test both success and failure scenarios +- Include null parameter validation tests + +## Configuration & Settings + +- Use strongly-typed configuration classes with data annotations +- Implement validation attributes (Required, NotEmptyOrWhitespace) +- Use IConfiguration binding for settings +- Support appsettings.json configuration files + +## Semantic Kernel & AI Integration + +- Use Microsoft.SemanticKernel for AI operations +- Implement proper kernel configuration and service registration +- Handle AI model settings (ChatCompletion, Embedding, etc.) +- Use structured output patterns for reliable AI responses + +## Error Handling & Logging + +- Use structured logging with Microsoft.Extensions.Logging +- Include scoped logging with meaningful context +- Throw specific exceptions with descriptive messages +- Use try-catch blocks for expected failure scenarios + +## Performance & Security + +- Use C# 12+ features and .NET 8 optimizations where applicable +- Implement proper input validation and sanitization +- Use parameterized queries for database operations +- Follow secure coding practices for AI/ML operations + +## Code Quality + +- Ensure SOLID principles compliance +- Avoid code duplication through base classes and utilities +- Use meaningful names that reflect domain concepts +- Keep methods focused and cohesive +- Implement proper disposal patterns for resources diff --git a/.github/prompts/dotnet-design-pattern-review.prompt.md b/.github/prompts/dotnet-design-pattern-review.prompt.md new file mode 100644 index 0000000000..13ade4c082 --- /dev/null +++ b/.github/prompts/dotnet-design-pattern-review.prompt.md @@ -0,0 +1,41 @@ +--- +agent: 'agent' +description: 'Review the C#/.NET code for design pattern implementation and suggest improvements.' +--- +# .NET/C# Design Pattern Review + +Review the C#/.NET code in ${selection} for design pattern implementation and suggest improvements for the solution/project. Do not make any changes to the code, just provide a review. + +## Required Design Patterns + +- **Command Pattern**: Generic base classes (`CommandHandler`), `ICommandHandler` interface, `CommandHandlerOptions` inheritance, static `SetupCommand(IHost host)` methods +- **Factory Pattern**: Complex object creation service provider integration +- **Dependency Injection**: Primary constructor syntax, `ArgumentNullException` null checks, interface abstractions, proper service lifetimes +- **Repository Pattern**: Async data access interfaces provider abstractions for connections +- **Provider Pattern**: External service abstractions (database, AI), clear contracts, configuration handling +- **Resource Pattern**: ResourceManager for localized messages, separate .resx files (LogMessages, ErrorMessages) + +## Review Checklist + +- **Design Patterns**: Identify patterns used. Are Command Handler, Factory, Provider, and Repository patterns correctly implemented? Missing beneficial patterns? +- **Architecture**: Follow namespace conventions (`{Core|Console|App|Service}.{Feature}`)? Proper separation between Core/Console projects? Modular and readable? +- **.NET Best Practices**: Primary constructors, async/await with Task returns, ResourceManager usage, structured logging, strongly-typed configuration? +- **GoF Patterns**: Command, Factory, Template Method, Strategy patterns correctly implemented? +- **SOLID Principles**: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion violations? +- **Performance**: Proper async/await, resource disposal, ConfigureAwait(false), parallel processing opportunities? +- **Maintainability**: Clear separation of concerns, consistent error handling, proper configuration usage? +- **Testability**: Dependencies abstracted via interfaces, mockable components, async testability, AAA pattern compatibility? +- **Security**: Input validation, secure credential handling, parameterized queries, safe exception handling? +- **Documentation**: XML docs for public APIs, parameter/return descriptions, resource file organization? +- **Code Clarity**: Meaningful names reflecting domain concepts, clear intent through patterns, self-explanatory structure? +- **Clean Code**: Consistent style, appropriate method/class size, minimal complexity, eliminated duplication? + +## Improvement Focus Areas + +- **Command Handlers**: Validation in base class, consistent error handling, proper resource management +- **Factories**: Dependency configuration, service provider integration, disposal patterns +- **Providers**: Connection management, async patterns, exception handling and logging +- **Configuration**: Data annotations, validation attributes, secure sensitive value handling +- **AI/ML Integration**: Semantic Kernel patterns, structured output handling, model configuration + +Provide specific, actionable recommendations for improvements aligned with the project's architecture and .NET best practices. diff --git a/.github/prompts/dotnet-upgrade.prompt.md b/.github/prompts/dotnet-upgrade.prompt.md new file mode 100644 index 0000000000..7d41864258 --- /dev/null +++ b/.github/prompts/dotnet-upgrade.prompt.md @@ -0,0 +1,115 @@ +--- +name: ".NET Upgrade Analysis Prompts" +description: "Ready-to-use prompts for comprehensive .NET framework upgrade analysis and execution" +--- + # Project Discovery & Assessment + - name: "Project Classification Analysis" + prompt: "Identify all projects in the solution and classify them by type (`.NET Framework`, `.NET Core`, `.NET Standard`). Analyze each `.csproj` for its current `TargetFramework` and SDK usage." + + - name: "Dependency Compatibility Review" + prompt: "Review external and internal dependencies for framework compatibility. Determine the upgrade complexity based on dependency graph depth." + + - name: "Legacy Package Detection" + prompt: "Identify legacy `packages.config` projects needing migration to `PackageReference` format." + + # Upgrade Strategy & Sequencing + - name: "Project Upgrade Ordering" + prompt: "Recommend a project upgrade order from least to most dependent components. Suggest how to isolate class library upgrades before API or Azure Function migrations." + + - name: "Incremental Strategy Planning" + prompt: "Propose an incremental upgrade strategy with rollback checkpoints. Evaluate the use of **Upgrade Assistant** or **manual upgrades** based on project structure." + + - name: "Progress Tracking Setup" + prompt: "Generate an upgrade checklist for tracking build, test, and deployment readiness across all projects." + + # Framework Targeting & Code Adjustments + - name: "Target Framework Selection" + prompt: "Suggest the correct `TargetFramework` for each project (e.g., `net8.0`). Review and update deprecated SDK or build configurations." + + - name: "Code Modernization Analysis" + prompt: "Identify code patterns needing modernization (e.g., `WebHostBuilder` → `HostBuilder`). Suggest replacements for deprecated .NET APIs and third-party libraries." + + - name: "Async Pattern Conversion" + prompt: "Recommend conversion of synchronous calls to async where appropriate for improved performance and scalability." + + # NuGet & Dependency Management + - name: "Package Compatibility Analysis" + prompt: "Analyze outdated or incompatible NuGet packages and suggest compatible versions. Identify third-party libraries that lack .NET 8 support and provide migration paths." + + - name: "Shared Dependency Strategy" + prompt: "Recommend strategies for handling shared dependency upgrades across projects. Evaluate usage of legacy packages and suggest alternatives in Microsoft-supported namespaces." + + - name: "Transitive Dependency Review" + prompt: "Review transitive dependencies and potential version conflicts after upgrade. Suggest resolution strategies for dependency conflicts." + + # CI/CD & Build Pipeline Updates + - name: "Pipeline Configuration Analysis" + prompt: "Analyze YAML build definitions for SDK version pinning and recommend updates. Suggest modifications for `UseDotNet@2` and `NuGetToolInstaller` tasks." + + - name: "Build Pipeline Modernization" + prompt: "Generate updated build pipeline snippets for .NET 8 migration. Recommend validation builds on feature branches before merging to main." + + - name: "CI Automation Enhancement" + prompt: "Identify opportunities to automate test and build verification in CI pipelines. Suggest strategies for continuous integration validation." + + # Testing & Validation + - name: "Build Validation Strategy" + prompt: "Propose validation checks to ensure the upgraded solution builds and runs successfully. Recommend automated test execution for unit and integration suites post-upgrade." + + - name: "Service Integration Verification" + prompt: "Generate validation steps to verify logging, telemetry, and service connectivity. Suggest strategies for verifying backward compatibility and runtime behavior." + + - name: "Deployment Readiness Check" + prompt: "Recommend UAT deployment verification steps before production rollout. Create comprehensive testing scenarios for upgraded components." + + # Breaking Change Analysis + - name: "API Deprecation Detection" + prompt: "Identify deprecated APIs or removed namespaces between target versions. Suggest automated scanning using `.NET Upgrade Assistant` and API Analyzer." + + - name: "API Replacement Strategy" + prompt: "Recommend replacement APIs or libraries for known breaking areas. Review configuration changes such as `Startup.cs` → `Program.cs` refactoring." + + - name: "Regression Testing Focus" + prompt: "Suggest regression testing scenarios focused on upgraded API endpoints or services. Create test plans for critical functionality validation." + + # Version Control & Commit Strategy + - name: "Branching Strategy Planning" + prompt: "Recommend branching strategy for safe upgrade with rollback capability. Generate commit templates for partial and complete project upgrades." + + - name: "PR Structure Optimization" + prompt: "Suggest best practices for creating structured PRs (`Upgrade to .NET [Version]`). Identify tagging strategies for PRs involving breaking changes." + + - name: "Code Review Guidelines" + prompt: "Recommend peer review focus areas (build, test, and dependency validation). Create checklists for effective upgrade reviews." + + # Documentation & Communication + - name: "Upgrade Documentation Strategy" + prompt: "Suggest how to document each project's framework change in the PR. Propose automated release note generation summarizing upgrades and test results." + + - name: "Stakeholder Communication" + prompt: "Recommend communicating version upgrades and migration timelines to consumers. Generate documentation templates for dependency updates and validation results." + + - name: "Progress Tracking Systems" + prompt: "Suggest maintaining an upgrade summary dashboard or markdown checklist. Create templates for tracking upgrade progress across multiple projects." + + # Tools & Automation + - name: "Upgrade Tool Selection" + prompt: "Recommend when and how to use: `.NET Upgrade Assistant`, `dotnet list package --outdated`, `dotnet migrate`, and `graph.json` dependency visualization." + + - name: "Analysis Script Generation" + prompt: "Generate scripts or prompts for analyzing dependency graphs before upgrading. Propose AI-assisted prompts for agents to identify upgrade issues automatically." + + - name: "Multi-Repository Validation" + prompt: "Suggest how to validate automation output across multiple repositories. Create standardized validation workflows for enterprise-scale upgrades." + + # Final Validation & Delivery + - name: "Final Solution Validation" + prompt: "Generate validation steps to confirm the final upgraded solution passes all validation checks. Suggest production deployment verification steps post-upgrade." + + - name: "Deployment Readiness Confirmation" + prompt: "Recommend generating final test results and build artifacts. Create a checklist summarizing completion across projects (builds/tests/deployment)." + + - name: "Release Documentation" + prompt: "Generate a release note summarizing framework changes and CI/CD updates. Create comprehensive upgrade summary documentation." + +--- diff --git a/.github/prompts/feature-spec.prompt.md b/.github/prompts/feature-spec.prompt.md new file mode 100644 index 0000000000..7e33e78884 --- /dev/null +++ b/.github/prompts/feature-spec.prompt.md @@ -0,0 +1,41 @@ +# Feature implementation from specification + +You are an expert FieldWorks engineer. Implement a feature using a spec-first, validation-gated workflow. Do not modify files until after the validation gate is approved. + +## Inputs +- spec file: ${specFile} + +## Context loading +1) Read the spec at ${specFile} +2) Skim `.github/src-catalog.md` and relevant `Src//AGENTS.md` guides +3) Check build/test constraints in `.github/instructions/*.instructions.md` + +## Plan +- Identify impacted components (managed/native/installer) +- List files to add/modify, and any cross-boundary implications +- Outline tests (unit/integration) and data needed from `TestLangProj/` + +## Validation gate (STOP) +Do not change files yet. Present: +- Summary of the change +- Affected components and risks +- Test strategy (coverage and edge cases) +- Rollback considerations + +Wait for approval before proceeding. + +## Implementation +- Make minimal, incremental changes aligned with the approved plan +- Follow localization and resource patterns (.resx; avoid hardcoded strings) +- Keep interop boundaries explicit (marshaling rules) + +## Tests +- Add/modify tests near affected components +- Ensure deterministic outcomes; avoid relying on external state + +## Handoff checklist +- [ ] Code compiles and local build passes +- [ ] Tests added/updated and pass locally +- [ ] AGENTS.md updated if architecture meaningfully changed +- [ ] `.github/src-catalog.md` updated if folder purpose changed + diff --git a/.github/prompts/opsx-apply.prompt.md b/.github/prompts/opsx-apply.prompt.md new file mode 100644 index 0000000000..494e10e990 --- /dev/null +++ b/.github/prompts/opsx-apply.prompt.md @@ -0,0 +1,149 @@ +--- +description: Implement tasks from an OpenSpec change (Experimental) +--- + +Implement tasks from an OpenSpec change. + +**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: + - Context 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 the files listed in `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.