Skip to content

feat(365): nullable-enable Folder/Store cluster (Wave 1, utilitiescs-nullable-remediation) - #388

Merged
drmoisan merged 20 commits into
epic/utilitiescs-nullable-remediation-integrationfrom
feature/utilitiescs-nullable-outlook-folder-store-365
Jul 19, 2026
Merged

feat(365): nullable-enable Folder/Store cluster (Wave 1, utilitiescs-nullable-remediation)#388
drmoisan merged 20 commits into
epic/utilitiescs-nullable-remediation-integrationfrom
feature/utilitiescs-nullable-outlook-folder-store-365

Conversation

@drmoisan

Copy link
Copy Markdown
Owner

Summary

  • Epic child (Wave 1) of utilitiescs-nullable-remediation: brings UtilitiesCS/OutlookObjects/Folder/ (including MsgToMime/) and UtilitiesCS/OutlookObjects/Store/ to zero CS86xx/CS87xx nullable diagnostics under a per-file #nullable enable opt-in pragma, with TreatWarningsAsErrors. No project-level <Nullable> element was added to UtilitiesCS.csproj; enforcement remains per-file only.
  • 63 files received the #nullable enable pragma and nullable annotations/guards/justified !; 18 already-opted-in files were re-verified clean with no edits; the 2 Designer-generated files (DisabledStoresViewer.Designer.cs, StoreWrapperViewer.Designer.cs) remain non-opted-in per repository convention.
  • Annotation and null-safety only: no behavior changes, no refactors, no API redesign. Both partial-class groups (FolderPredictor.cs + FolderPredictor.IFolderSearchHandler.cs; StoresWrapper.cs + StoresWrapper.Filtering.cs) were remediated together with a consistent nullable shape for shared members.

Closes #365.

Test plan

  • Full UtilitiesCS.Test suite: 4511/4511 passing, no regressions.
  • Scoped nullable pragma gate (msbuild UtilitiesCS/UtilitiesCS.csproj /t:Rebuild ... /p:TreatWarningsAsErrors=true /p:BuildProjectReferences=false): zero CS86xx/CS87xx across all 63 remediated + 18 verify-only files.
  • Full-solution pragma gate (msbuild TaskMaster.sln /t:Rebuild ... /p:TreatWarningsAsErrors=true, no /p:Nullable=enable): only 2 pre-existing CS0649 errors in the vendored SVGControl project remain (sibling feature Feature: utilitiescs-nullable-svgcontrol #368's scope; confirmed pre-existing at the merge-base and outside this feature's Folder/Store diff).
  • Coverage: line 65.30% -> 65.31%, branch 61.32% -> 61.35% (no regression); changed-line coverage 96.97% (96/99); first-party UtilitiesCS package 88.75% line / 82.55% branch.
  • CSharpier formatting and analyzer/code-style build clean on the final pass.
  • AC1-AC7 all evaluated PASS by independent feature-review (blocking_count = 0); checked off in spec.md and user-story.md.

Notes for reviewers

  • This PR targets the epic integration branch epic/utilitiescs-nullable-remediation-integration, not main. Per this epic's CI configuration, ci.yml triggers only on PRs to main/development, so no required checks run against this PR by design.
  • Full evidence trail (baseline, per-batch gates, regression tests, final QC, AC status summary) is committed under docs/features/active/utilitiescs-nullable-outlook-folder-store/evidence/. Policy/code/feature audit artifacts are committed at the feature-folder root (policy-audit.2026-07-19T12-52.md, code-review.2026-07-19T12-52.md, feature-audit.2026-07-19T12-52.md).
  • Pre-existing exceptions flagged, not fixed here (out of scope): FolderPredictor.cs (983 lines), FolderScorer.cs (664 lines), and "FolderWrapper .cs" (532 lines, literal trailing space in filename, not renamed) remain single files per policy; a pre-existing analyzer package/version mismatch in the committed UtilitiesCS.csproj/packages.config was worked around locally (gitignored packages/ folder only, no tracked file changed) and is flagged for the maintainer.

drmoisan and others added 20 commits July 19, 2026 11:09
…ation

Records baseline file inventory (83 files: 18 already-enabled, 2 Designer, 63
targets), csharpier-clean state, analyzer build (0 errors), nullable pragma gate
(0 CS86xx; pre-existing SVGControl CS0649 and UtilitiesCS CS0618/CS0168 warning
debt documented as out-of-scope), and 4511 passing tests at 65.30% line / 61.32%
branch coverage. Confirms AC2 baseline (no <Nullable> in csproj) and that all 6
Wave-0 upstream files already carry #nullable enable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Adds #nullable enable to 14 Folder interface/enum/DTO files and applies the
interface contract shapes: IFolderHandleResolver.TryResolve(node?, out folder?),
IFolderHierarchyProvider.ResolveLeafKeyAsync -> Task<FolderTreeNodeKey?>, and
IFolderSearchHandler.FindFolder default-null optional params. Zero CS86xx;
4511/4511 tests pass; coverage unchanged (65.30% line / 61.32% branch).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
…ers (P2)

Adds #nullable enable to 10 files: FolderTreeNodeKey (IEquatable nullable
overrides), FolderTreeRequest/FolderTreeSelectionOverlay (nullable enumerables/
node), and the five IEqualityComparer implementations (nullable Equals params).
FolderWrapperNodeComparer needed three behavior-identical null-safety guard
refinements. Zero CS86xx; 4511/4511 tests pass; coverage 65.32% line / 61.35%
branch (no regression).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Adds #nullable enable to 7 snapshot files with nullable parentKey/ParentKey,
TryGetNode(out node?), nullable FindByPath/GetArchiveRoot/CreateNode returns,
nullable _highlighted, and FolderHierarchyBuilder nullable locals. Also widens
IOutlookFolderHierarchyReader.ReadFoldersAsync clocks to nullable to match the
null-tolerant FolderTreeSnapshotBuilder. Zero CS86xx; 4511/4511 tests pass;
coverage 65.29% line / 61.34% branch (no regression).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Pragma-enables all 8 F3 files (P4-T1) and annotates the 4 group-a files:
FolderConverter (string? ToFsFolderpath), FolderNavigator (Folder? return),
FolderMinimalWrapper and FolderWrapper .cs (nullable COM fields/props, string?
Name/RelativePath, nullable-by-design Globals, nullable PropertyChanged event).
Nullable-string lazies use new Lazy<string?> since #363 ToLazy has where T:class.
Zero CS86xx for the 4 group-a files; 4511/4511 tests pass; coverage unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Initializes _roots, makes PropertyChanged event nullable, forgives COM
OlFolder.Folders derefs and selections.Contains(RelativePath!). Zero CS86xx;
4511/4511 tests pass; coverage 65.30% line / 61.35% branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Adds nullable predictions array and cast-with-as locals (string[]? folders,
AddArray(string[]?)). FolderScoring struct fields verified set at all sites.
Zero CS86xx; 4511/4511 tests pass; coverage 65.30% line / 61.34% branch. Not split.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
… T11-T14)

Remediates FolderPredictor.cs (974 lines, not split) + its
IFolderSearchHandler partial together (AC7): nullable GetFolder/CreateFolder/
InputFoldername returns, nullable optional params matching the interface shape,
Regex?/List<string>? fields, and null! partial-init for the navigation-only ctor
so Suggestions/_globals stay non-null for the interface contract. Zero CS86xx
across all of UtilitiesCS (17 verify-only Folder files confirmed clean, P4-T14);
4511/4511 tests pass; coverage 65.30% line / 61.35% branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Annotates OutlookFolderHierarchyProvider (Task<FolderTreeNodeKey?> ResolveLeafKey)
and OutlookFolderTreeService (nullable state fields/event, nullable request/snapshot
pipeline params). Refines the request-nullable contract chain across
BuildSnapshotAsync/ReadFoldersAsync and FolderTreeSnapshotChangedEventArgs.
Zero CS86xx; 4511/4511 tests pass; coverage 65.31% line / 61.35% branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Pragma-enables IDisabledStoresViewer, IStoreWrapperViewer, DisabledStoreRow;
keeps WinForms control props and BindRows non-null by contract, sets event-handler
senders to object?, and initializes DisabledStoreRow string props to string.Empty.
Zero CS86xx; 4511/4511 tests pass; coverage 65.30% line / 61.32% branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Annotates StoreIdentity (nullable Resolve params), StoreFilterAttribution
(documented-nullable Decide/FormatLine params), StoreLockupAttribution,
StoreWrapperInitProbe. StoreRehookResult verify-only confirmed clean (P7-T5).
Zero CS86xx; 4511/4511 tests pass; coverage 65.29% line / 61.33% branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Makes Init/Restore-populated properties nullable (DisplayName, StoreId,
InnerStore, Inbox, RootFolder, UserEmailAddress, GlobalAddressBook),
GetSmtpAddressFromStore returns string?, InnerStore! at Init COM derefs.
Zero CS86xx; 4511/4511 tests pass; coverage 65.30% line / 61.33% branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Remediates StoresWrapper.cs + StoresWrapper.Filtering.cs together (AC7): nullable
Globals/Stores, nullable primitive-read locals, consistent static/instance
StoreIsIncluded nullable params. StoreDisableService: nullable rehook ctor param,
StoresWrapper? GetModelOrNull. Zero CS86xx; 4511/4511 tests pass; coverage 65.3%.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Annotates OutlookFolderHandleResolver (TryResolve node?/folder?),
OutlookFolderHierarchyReader (IOutlookFolderAdapter? GetRootFolder, nullable
request/clock params threaded through), OutlookFolderNotificationSink (nullable
events/_handler, string? storeId in FolderTreeNotification, 499 lines not split),
MAPIMethods + WpfDispatcherYield (pragma only). Zero CS86xx; 4511/4511 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
StoreWrapperController: nullable viewer-bound optional fields, = null! for
lifecycle-set Viewer/Model/Current/FsConverter, IList<string?> DisplayNames,
nullable SelectFolder/SelectFsFolder returns; confirmed the CS8625 disable/restore
pair still needed. DisabledStoresController: nullable-by-design Viewer. Cross-batch:
StoreWrapper configurable props nullable. Zero CS86xx; 4511/4511 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
StoreWrapperViewer (nullable-by-design Controller, object? senders) and
DisabledStoresViewer (object? sender). Designer siblings left non-opted-in per
convention. Cross-batch: two controller event-handler senders widened to object?.
Zero CS86xx; 4511/4511 tests pass; coverage 65.30% line / 61.34% branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
…CS8766)

The final comprehensive nullable gate (full CS8xxx range) caught a CS8766 that
the per-batch CS86xx grep missed: OutlookStoreAdapter.GetRootFolder returns
IOutlookFolderAdapter? but the nested IOutlookStoreAdapter interface declared it
non-null. Widened the interface to match the impl.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
csharpier clean; analyzers 0 errors/0 warnings; scoped nullable gate zero
CS86xx/CS87xx across all 63 remediated + 18 verify-only files; 4511/4511 tests
pass; overall coverage 65.30%->65.31% (no regression), changed-line coverage
96.97%. Verified AC2 (no <Nullable>), AC5 (signature compat), AC6 (no
post-condition attrs/records), AC7 (partial groups together), scope guards (no
splits, filename intact), upstream contracts stable. Checks off AC1-AC7 in
spec.md and user-story.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Policy audit, code review, and feature audit for the Folder/Store
nullable-enable remediation. All AC1-AC7 evaluated PASS; zero blocking
findings. Repo-wide C# coverage below the 85/75 uniform floor is
dispositioned non-blocking (pre-existing legacy VSTO/COM debt, ratified
CLAUDE.md exemption, first-party UtilitiesCS package above floor, no
regression).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
@drmoisan
drmoisan merged commit 3dfe339 into epic/utilitiescs-nullable-remediation-integration Jul 19, 2026
@drmoisan
drmoisan deleted the feature/utilitiescs-nullable-outlook-folder-store-365 branch July 21, 2026 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant