Skip to content

Stabilize QuickFiler conversation and metrics flows while syncing development fixes into main#107

Merged
drmoisan merged 27 commits into
mainfrom
development
Mar 27, 2026
Merged

Stabilize QuickFiler conversation and metrics flows while syncing development fixes into main#107
drmoisan merged 27 commits into
mainfrom
development

Conversation

@drmoisan

Copy link
Copy Markdown
Owner

Stabilize QuickFiler conversation and metrics flows while syncing development fixes into main

Summary

  • Fixes three user-facing QuickFiler failure paths: missing Email Time calendar data no longer throws in the metrics flow, the Right arrow key expands conversations again during keyboard navigation, and ConversationResolver no longer re-enters and throws when expanded conversation rows are empty.
  • Adds focused regression coverage across QuickFiler.Test for QfcHomeController, QfcCollectionController, QfcItemController, ConversationResolver, and EfcHomeController.
  • Hardens UtilitiesCS/OutlookObjects/Recipient/RecipientStatic.cs so unreadable Exchange sender/recipient properties fall back to safer mail/address-entry/property-accessor values.
  • Fixes UtilitiesSwordfish/Collections/ConcurrentObservableBase.cs so CollectionChanged relays the wrapper instance as the event sender, with matching regression coverage.
  • Carries forward contributor-tooling updates under .codex/ and .github/workflows/codex-web-setup-test.yml, plus the issue/plan/evidence/audit artifacts for the QuickFiler bug-fix tracks.

Why

  • Issue #97 documents that UtilitiesCS.Calendar.GetCalendar("Email Time", ...) can return null, which propagated into QuickFileMetrics_WRITE and GetMoveDiagnostics and caused a NullReferenceException when the Outlook calendar subfolder did not exist.
  • Issue #96 documents that QfcItemController.RegisterFocusAsyncActions() no longer registered a Keys.Right handler after the async keyboard-action migration, so the key press fell through to the focused WinForms control and activated the sender mailto: link instead of expanding the conversation.
  • Issue #103 documents a read-before-write ordering bug in ConversationResolver.LoadConversationInfoAsync(): UpdateUI(ConversationInfo.Expanded) ran before the local pair was assigned back to ConversationInfo, which re-entered the synchronous loader and threw InvalidOperationException when Count.Expanded == 0.
  • The issue and plan material for #103 also require a second constraint: when the expanded conversation is empty, the synchronous path should return a safe single-item fallback instead of throwing for a recoverable scenario.
  • The remaining shared-library and tooling changes in this range support the same overall goal of reducing regression risk on development before merging back to main.

What Changed

  • Core behavior / architecture

    • Updated QuickFiler/Controllers/QfcCollectionController.cs and QuickFiler/Controllers/QfcHomeController.cs to guard null calendar/appointment data in the QuickFiler metrics path described by issue #97.
    • Updated QuickFiler/Controllers/QfcItemController.cs to restore Keys.Right registration/removal in the async focus-action path so keyboard navigation can expand conversations again.
    • Updated QuickFiler/Helper Classes/ConversationResolver.cs so the async path assigns ConversationInfo = pair before invoking the UI callback, passes pair.Expanded directly, and returns a logged single-item fallback when Count.Expanded <= 0.
    • Updated QuickFiler/Controllers/EfcHomeController.cs to guard ExecuteMovesAsync against re-entrant cleanup and to adjust the metrics predicate.
    • Updated UtilitiesCS/OutlookObjects/Recipient/RecipientStatic.cs to tolerate unreadable Exchange sender/recipient properties via layered fallbacks.
    • Updated UtilitiesSwordfish/Collections/ConcurrentObservableBase.cs so collection-change subscribers receive the wrapper instance as the event sender.
  • Tooling / automation / CI / DevEx

    • Added .codex/agents/atomic-executor.toml, .codex/agents/atomic-planner.toml, .codex/agents/feature-reviewer.toml, and .codex/prompts/feature-review-remediate.md.
    • Updated .codex/codex-web-setup.plan.md, .codex/codex-web-setup.sh, and .github/workflows/codex-web-setup-test.yml.
    • Restored or normalized supporting project files including TaskMaster/AppGlobals/AppAutoFileObjects.cs, TaskMaster/Ribbon/RibbonExplorer.xml, TaskMaster/TaskMaster.csproj, and missing-serializable-list.json.
  • Tests

    • Added QuickFiler.Test/Controllers/EfcHomeControllerTests.cs, QuickFiler.Test/Controllers/QfcCollectionControllerTests.cs, QuickFiler.Test/Controllers/QfcItemControllerTests.cs, and QuickFiler.Test/Helper Classes/ConversationResolverTests.cs.
    • Expanded QuickFiler.Test/Controllers/QfcHomeControllerTests.cs and updated QuickFiler.Test/QuickFiler.Test.csproj.
    • Updated UtilitiesCS.Test/OutlookObjects/Recipient/RecipientStaticTests.cs, UtilitiesCS.Test/OutlookObjects/Table/OlToDoTable_Tests.cs, and UtilitiesCS.Test/UtilitiesCS.Test.csproj.
    • Added UtilitiesCS.Test/ReusableTypeClasses/Concurrent/Observable/Collection/ConcurrentObservableCollectionSenderTests.cs.
  • Docs / templates / agents

    • Added active feature folders and evidence under:
      • docs/features/active/2026-03-25-getmovediagnostics-null-guard-97/
      • docs/features/active/2026-03-25-quickfiler-gui-not-expanding-96/
      • docs/features/active/2026-03-26-conversation-info-updateui-ordering-103/
    • Added docs/features/potential/2026-03-25-quickfiler-gui-not-expanding.md.

Architecture / How It Fits Together

  • QuickFiler keyboard flow

    • KeyboardHandler dispatches registered async key actions.
    • QfcItemController.RegisterFocusAsyncActions() now registers Keys.Right.
    • Pressing Right routes into ToggleExpansionAsync(Enums.ToggleState.On) instead of falling through to the focused control.
  • QuickFiler metrics flow

    • The move operation reaches QuickFileMetrics_WRITE and GetMoveDiagnostics.
    • The null-guard fix now treats missing calendar/appointment objects as an edge case to skip safely rather than dereferencing olEmailCalendar.Items or olAppointment.Body.
  • Conversation loading flow

    • ConversationResolver.LoadConversationInfoAsync() builds the local pair, assigns ConversationInfo = pair, then calls UpdateUI(pair.Expanded).
    • ConversationResolver.LoadConversationInfo() now returns a single-item [MailHelper] fallback when Count.Expanded <= 0, which avoids the recoverable empty-conversation exception path described in issue #103.
  • Shared library behavior

    • RecipientStatic now wraps Exchange lookups with fallback resolution paths for sender/recipient data.
    • ConcurrentObservableBase<T> now preserves the wrapper-level sender contract for subscribers that cast the CollectionChanged sender back to the wrapper type.

Verification

Completed

  • Recorded canonical QA evidence for issue #96:
    • dotnet tool run csharpier format .EXIT_CODE: 0
    • pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/vscode/Invoke-VSBuild.ps1 -SolutionPath TaskMaster.sln -Configuration Debug -Platform "Any CPU" -EnableNETAnalyzers -EnforceCodeStyleInBuildEXIT_CODE: 0
    • pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/vscode/Invoke-VSBuild.ps1 -SolutionPath TaskMaster.sln -Configuration Debug -Platform "Any CPU" -EnableNullable -TreatWarningsAsErrorsEXIT_CODE: 0
    • & "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: 0
    • Recorded fail-before regression filter for QfcItemControllerEXIT_CODE: 1
  • Recorded QA evidence for issue #97:
    • Canonical feature evidence records format and analyzer passes.
    • PR #102 records nullable build success and an MSTest coverage run, with 14 failures called out there as pre-existing on origin/development.
  • Recorded QA evidence for issue #103:
    • dotnet tool run csharpier format .EXIT_CODE: 0
    • Analyzer build → EXIT_CODE: 0
    • Nullable build → EXIT_CODE: 0
    • vstest.console.exe "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll" /InIsolation /TestCaseFilter:"FullyQualifiedName~ConversationResolver"EXIT_CODE: 0
    • vstest.console.exe "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll" /InIsolation /EnableCodeCoverageEXIT_CODE: 0
  • No single repo-wide verification record is included in the context bundle for the entire origin/main..development range beyond the feature-specific evidence above.

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 -EnforceCodeStyleInBuild
  • pwsh -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~QfcItemController_KeyboardRegistration"
  • & "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 /EnableCodeCoverage

Backward Compatibility / Migration Notes

  • No public API signature changes are described in the context bundle.
  • ConversationResolver.LoadConversationInfo() now returns a single-item fallback when Count.Expanded <= 0 instead of throwing InvalidOperationException for that case.
  • QuickFiler keyboard behavior changes intentionally: Right arrow is handled by QfcItemController again and expands the selected conversation instead of falling through to the focused WinForms control.
  • QuickFiler metrics behavior changes intentionally: missing Email Time calendar data is now treated as a null-safe path instead of an exception path.
  • This range also introduces new contributor-tooling files under .codex/ and additional evidence/audit documentation under docs/features/active/....

Risks and Mitigations

  • Risk: Outlook-hosted UI behavior is difficult to prove purely from unit tests.
    • Mitigation: the range includes targeted regression tests plus captured fail-before evidence for the QuickFiler bugs.
  • Risk: The development..main comparison includes multiple merged PRs and supporting docs/tooling, which increases review surface.
    • Mitigation: review the source-code deltas first, then the tooling/docs changes separately using the guide below.
  • Risk: The new ConversationResolver fallback changes behavior for callers that previously observed an exception on empty expanded conversations.
    • Mitigation: the issue and plan explicitly define the fallback as the intended contract, and the regression tests were updated accordingly.
  • Risk: PR #102 records pre-existing MSTest failures on origin/development, so full-range verification may still need a clean re-run before merge.
    • Mitigation: rerun the recommended QA commands against the current branch head before final merge.

Review Guide

  • Start with docs/features/active/2026-03-25-quickfiler-gui-not-expanding-96/issue.md, docs/features/active/2026-03-25-getmovediagnostics-null-guard-97/issue.md, and docs/features/active/2026-03-26-conversation-info-updateui-ordering-103/issue.md for the bug statements.
  • Review QuickFiler/Controllers/QfcItemController.cs with QuickFiler.Test/Controllers/QfcItemControllerTests.cs for the Right-arrow registration fix.
  • Review QuickFiler/Controllers/QfcCollectionController.cs and QuickFiler/Controllers/QfcHomeController.cs with their tests for the metrics null-guard fix.
  • Review QuickFiler/Helper Classes/ConversationResolver.cs with QuickFiler.Test/Helper Classes/ConversationResolverTests.cs for the async ordering and empty-conversation fallback changes.
  • Review QuickFiler/Controllers/EfcHomeController.cs with QuickFiler.Test/Controllers/EfcHomeControllerTests.cs for the re-entrant cleanup and metrics-predicate adjustments.
  • Review UtilitiesCS/OutlookObjects/Recipient/RecipientStatic.cs with UtilitiesCS.Test/OutlookObjects/Recipient/RecipientStaticTests.cs, then UtilitiesSwordfish/Collections/ConcurrentObservableBase.cs with UtilitiesCS.Test/ReusableTypeClasses/Concurrent/Observable/Collection/ConcurrentObservableCollectionSenderTests.cs.
  • Finish with .codex/*, .github/workflows/codex-web-setup-test.yml, and the docs/features/active/... evidence/audit artifacts; these are useful context but are noisier than the product-code deltas.

Follow-ups

  • Manually validate QuickFiler in Outlook for:
    • Right-arrow conversation expansion
    • missing Email Time calendar handling
    • Junk E-mail conversation loading with empty expanded rows
  • Re-run the recommended QA loop for the current branch head if a merge to main requires a single consolidated verification record for the entire comparison range.
  • Consider separating future contributor-tooling/doc-evidence updates from product-code fixes when a narrower reviewer focus is preferred.

GitHub Auto-close

Related issues / PRs

drmoisan and others added 27 commits March 21, 2026 22:47
- catch COM failures when reading Exchange sender and recipient properties
- fall back to mail item, address entry, and property accessor values when Exchange data is unavailable
- add regression tests covering sender and recipient fallback behavior for unreadable Exchange users
(fix(recipient)): harden Outlook recipient fallback resolution
- Update ConcurrentObservableBase to raise CollectionChanged with the wrapper instance for relayed view-model and base-collection events
- Add regression tests covering add, remove, and concrete wrapper casting scenarios for ConcurrentObservableCollection subscribers
- Include the new sender regression test file in the UtilitiesCS.Test project

Refs: #87
…onversation load

- Add catch (OperationCanceledException) { throw; } before the broad
  catch (System.Exception) so mid-load cancellation is not suppressed
- Extract ConversationResolver.LoadAsync into protected virtual
  DoLoadConversationResolverCoreAsync to enable seam-based testing
  without WinForms infrastructure
- Guard PopulateConversationAsync against null ConversationResolver
  after a silently-swallowed load failure; add token check after load
- Add QfcItemControllerTests with four regression tests covering
  cancellation propagation and null-guard paths
…cleanup and fix metrics predicate

- Add `_isExecuting` volatile flag to drop re-entrant calls before any field access,
  preventing NullReferenceException when Cleanup() nulls _globals mid-await
- Capture _globals into a local before MoveToFolderAsync as defense-in-depth against
  concurrent Cleanup() racing the post-await continuation
- Fix inverted predicate in QuickFileMetrics_WRITE: `Count == 0` → `Count > 0` to
  write metrics for non-empty move results instead of empty ones
- Add EfcHomeControllerTests with three regression tests covering the re-entrancy guard
  and both empty-list and null-list paths through QuickFileMetrics_WRITE
…k-clean

chore/mixed branch excluded work clean
…failures

- replace recursive error-message property access with nameof() so invalid conversation state throws cleanly instead of overflowing the stack
- treat a zero row count as a valid initialized value by switching the Count cache sentinel to (-1,-1) and using an explicit initialization predicate
- add regression tests for the stack overflow and zero-count reload paths, and expose internals to QuickFiler.Test for deterministic coverage
…ite-loop

Bug/conversation resolver infinite loop
- In GetMoveDiagnostics, wrap olAppointment.Body access with `if (olAppointment is not null)` to eliminate NullReferenceException when the "Email Time" calendar subfolder is absent
- In QuickFileMetrics_WRITE, guard olEmailCalendar before .Items.Add() and propagate null olAppointment safely downstream
- Add regression tests covering both null paths in QfcCollectionControllerTests and QfcHomeControllerTests
- Capture Phase 0–2 baseline and QA gate evidence artifacts; add atomic-executor, atomic-planner, and feature-review skill definitions

Refs: #97
…rd-97-clean

fix(QuickFiler): guard null calendar and appointment in GetMoveDiagnostics #97
…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
…ordering-103

fix(QuickFiler): prevent ConversationResolver re-entry and add empty-conversation fallback
… conversation on focus

- Add Keys.Right → ToggleExpansionAsync() in RegisterFocusAsyncActions, restoring
  the handler omitted during the async migration
- Add corresponding Remove in UnregisterFocusAsyncActions to prevent stale handler
  accumulation across focus changes
- Add regression tests verifying Right-arrow is registered on focus and cleaned up
  on unfocus (QfcItemController_KeyboardRegistrationTests, P1-T1 and P1-T2)
- Promote feature to active folder with issue, plan, baseline, QA-gate, and
  regression evidence artifacts

Refs: #96
…g-96-clean

bug/quickfiler gui not expanding 96 clean
@drmoisan
drmoisan merged commit cb6a6ed into main Mar 27, 2026
4 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant