fix(QuickFiler): prevent ConversationResolver re-entry and add empty-conversation fallback#104
Merged
drmoisan merged 1 commit intoMar 26, 2026
Conversation
…mpty expansions - assign ConversationInfo before UpdateUI and pass pair.Expanded directly to avoid lazy getter re-entry in LoadConversationInfoAsync - return a logged single-item MailHelper fallback when Count.Expanded is zero and update ConversationResolver regression tests for the new behavior - capture issue, plan, baseline evidence, QA gates, and final policy/feature audits for the Issue #103 fix Refs: #103
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(QuickFiler): prevent ConversationResolver re-entry and add empty-conversation fallback
Summary
ConversationResolverconversation-loading path soLoadConversationInfoAsync()assignsConversationInfobefore UI updates and passespair.Expandeddirectly, avoiding a lazy-property re-entry that could trigger the synchronous loader.Count.Expanded == 0so the conversation panel can return a single-item result instead of throwingInvalidOperationExceptionfor recoverable cases such as filtered Junk E-mail conversations.QuickFiler.Test/Helper Classes/ConversationResolverTests.csto cover the ordering/fallback behavior and keep the ConversationResolver regression suite green.docs/features/active/2026-03-26-conversation-info-updateui-ordering-103/.Why
The captured issue states that
LoadConversationInfoAsync()calledUpdateUI(ConversationInfo.Expanded)before assigning the localpairback toConversationInfo. That ordering caused the lazy getter to re-enterLoadConversationInfo()synchronously, and whenCount.Expanded == 0the synchronous path threwInvalidOperationException.The issue and plan both describe two required constraints for the fix:
ConversationInfo = pairbefore any UI read of the conversation payloadLoadConversationInfo()when the expanded conversation is empty, rather than throwing for a recoverable scenarioThis change implements those two behaviors and records the supporting validation artifacts for Issue #103.
What Changed
Core behavior / architecture
QuickFiler/Helper Classes/ConversationResolver.csso the async conversation-loading path cachespairbefore invokingUpdateUI.pair.Expandeddirectly instead of re-readingConversationInfo.Expanded.MailHelperpair with an error log entry.Tests
QuickFiler.Test/Helper Classes/ConversationResolverTests.csto cover the read-before-write regression and the zero-expanded fallback behavior.ConversationResolvertest filter path used by the QA evidence.Docs / templates / agents
issue.md,plan.2026-03-26T18-43.md, fail-before evidence, QA gate outputs, and audit artifacts underdocs/features/active/2026-03-26-conversation-info-updateui-ordering-103/.Architecture / How It Fits Together
ConversationResolverhas two relevant flows described in the issue and plan:Async path
pairConversationInfo = pairUpdateUI(pair.Expanded)Sync path
Count.Expanded <= 0, it now returns a logged single-item fallback containingMailHelperTogether, those changes remove the read-before-write loop that previously caused the async path to fall back into the synchronous loader at the wrong time.
Verification
Completed
Format
dotnet tool run csharpier format .EXIT_CODE: 0Lint / analyzer build
pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/vscode/Invoke-VSBuild.ps1 -SolutionPath TaskMaster.sln -Configuration Debug -Platform "Any CPU" -EnableNETAnalyzers -EnforceCodeStyleInBuildEXIT_CODE: 0Nullable / type-check build
pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/vscode/Invoke-VSBuild.ps1 -SolutionPath TaskMaster.sln -Configuration Debug -Platform "Any CPU" -EnableNullable -TreatWarningsAsErrorsEXIT_CODE: 0Targeted regression tests
& "C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll" /InIsolation /TestCaseFilter:"FullyQualifiedName~ConversationResolver"EXIT_CODE: 0QuickFiler test suite with coverage
& "C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll" /InIsolation /EnableCodeCoverageEXIT_CODE: 0Fail-before evidence
ConversationInfo cannot be loaded if Df cannot be resolvedfromQuickFiler.Helper_Classes.ConversationResolver.LoadConversationInfo()before the fix.Recommended
dotnet tool run csharpier format .pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/vscode/Invoke-VSBuild.ps1 -SolutionPath TaskMaster.sln -Configuration Debug -Platform "Any CPU" -EnableNETAnalyzers -EnforceCodeStyleInBuildpwsh -NoProfile -ExecutionPolicy Bypass -File scripts/vscode/Invoke-VSBuild.ps1 -SolutionPath TaskMaster.sln -Configuration Debug -Platform "Any CPU" -EnableNullable -TreatWarningsAsErrors& "C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll" /InIsolation /TestCaseFilter:"FullyQualifiedName~ConversationResolver"& "C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll" /InIsolation /EnableCodeCoverageBackward Compatibility / Migration Notes
Count.Expanded == 0, the synchronous path now returns a single-item fallback instead of throwingInvalidOperationException.Risks and Mitigations
Risk: the async ordering fix could still miss an indirect property read in future edits.
ConversationResolverregression suite is recorded and should be rerun for future changes in this area.Risk: callers may implicitly expect an exception from the synchronous zero-expanded path.
Risk: the Outlook/VSTO scenario is difficult to reproduce purely through unit tests.
Review Guide
docs/features/active/2026-03-26-conversation-info-updateui-ordering-103/issue.mdfor the bug statement and expected behavior.docs/features/active/2026-03-26-conversation-info-updateui-ordering-103/plan.2026-03-26T18-43.mdfor the intended fix sequence and acceptance mapping.QuickFiler/Helper Classes/ConversationResolver.csfor the async assignment ordering and sync fallback behavior.QuickFiler.Test/Helper Classes/ConversationResolverTests.csfor the updated regression coverage.docs/features/active/2026-03-26-conversation-info-updateui-ordering-103/evidence/qa-gates/if you want to confirm the recorded command outputs.Follow-ups
issue.mdto confirm the conversation panel now loads without error.artifacts/pr_context.summary.txtandartifacts/pr_context.appendix.txtas the canonical sources.GitHub Auto-close
Related issues / PRs