Skip to content

Nullable remediation: UtilitiesCS/ReusableTypeClasses (#366)#380

Merged
drmoisan merged 9 commits into
epic/utilitiescs-nullable-remediation-integrationfrom
feature/utilitiescs-nullable-reusabletypes-366
Jul 20, 2026
Merged

Nullable remediation: UtilitiesCS/ReusableTypeClasses (#366)#380
drmoisan merged 9 commits into
epic/utilitiescs-nullable-remediation-integrationfrom
feature/utilitiescs-nullable-reusabletypes-366

Conversation

@drmoisan

@drmoisan drmoisan commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Nullable remediation: UtilitiesCS/ReusableTypeClasses (#366)

Epic child of utilitiescs-nullable-remediation (Wave 1). Base branch: epic/utilitiescs-nullable-remediation-integration.

Status: COMPLETE — ready to merge

Per-file #nullable enable opt-in remediation for the 51 in-scope reusable base/value types under UtilitiesCS/ReusableTypeClasses/, plus the maintainer-ratified where TKey : notnull generic-parameter-list constraint on the three truly-generic dictionary bases and its epic-authorized propagation to four cross-child NewtonsoftHelpers consumers. Annotation and null-safety only; no behavior changes.

What this delivers

Per-file #nullable enable across all 51 in-scope files (Batches 1–8): trivial leaves / EventArgs / observers / interfaces, standalone value/utility types, matrices, timed actions, locking core, concurrent-observable bases, SmartSerializable family + ConfigController, and the Serializable wrappers. Each file carries a #nullable enable pragma and reaches zero CS86xx / zero CS8714 under the per-file pragma gate. Changes are annotation and null-safety only: nullable ? annotations, guard clauses, justified !, unconstrained-generic T?/out TValue?, and default!/= null! for reflection/deserialization-populated fields (with // set by deserialization comments). No behavior changes, no refactors, no file splits, no post-condition attributes (net481 lacks the polyfills), no record/init conversion.

Ratified where TKey : notnull constraint ([P6-T2], RATIFIED 2026-07-19)

The maintainer ratified adding where TKey : notnull to the public generic parameter list of the dictionary base types. Factual correction applied during execution: ScoDictionaryStatic is a NON-generic static class of Type extension methods, so the constraint is mechanically inapplicable there. Net effect: the constraint applies to the three truly-generic basesConcurrentObservableDictionary, ScoDictionaryNew, and ScDictionary. ScBag (ConcurrentBag<T>-based) and ConcurrentObservableBag are correctly left unconstrained.

On net481 the BCL ConcurrentDictionary<TKey, TValue> reference assemblies are not nullable-annotated, so the constraint emits zero CS8714 against the base derivations in isolation; it is adopted as forward-looking public-contract hygiene. ConcurrentDictionary already rejects null keys at runtime, so the constraint is IL-metadata / compile-time-contract only, with no runtime behavior change (AC3/AC5).

Epic-authorized four-file cross-child waiver (Option A'')

Because sibling child #367 (UtilitiesCS/NewtonsoftHelpers, already merged onto the integration branch) is #nullable enable and declares wrapper/converter types with unconstrained TKey, constraining the bases cascades CS8714 into #367-owned consumers. The epic layer (which owns cross-child boundaries) authorized a bounded waiver adding exactly one where TKey : notnull line to each of four #367-owned NewtonsoftHelpers files, and nothing else:

  1. UtilitiesCS/NewtonsoftHelpers/WrapperScoDictionary.cs
  2. UtilitiesCS/NewtonsoftHelpers/ScoDictionaryConverter.cs
  3. UtilitiesCS/NewtonsoftHelpers/WrapperScDictionary.cs
  4. UtilitiesCS/NewtonsoftHelpers/ScDictionaryConverter.cs (no o; distinct from Auto hide inactive branches #2)

An assembly-wide grep confirms the cross-child consumer set is closed at these four: each generic base with cross-child consumers has exactly a Wrapper and a Converter (a symmetric closed set). WrapperPeopleScoDictionaryNew.cs and PeopleScoConverter.cs are verified safe (concrete type arguments / commented-out reference) and are not touched. Full authorization and empirical evidence: docs/features/active/utilitiescs-nullable-reusabletypes/evidence/other/cs8714-notnull-ratification.md.

Verification

  • Isolated-cluster per-file nullable pragma gate (msbuild UtilitiesCS/UtilitiesCS.csproj /t:Rebuild /p:TreatWarningsAsErrors=true, no global /p:Nullable=enable): 0 CS86xx, 0 CS8714 across the 51 cluster files including the four waived NewtonsoftHelpers lines.
  • CSharpier: clean second pass.
  • Analyzer/code-style build (msbuild TaskMaster.sln ... /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true): succeeds, 0 errors.
  • Tests: 5702 MSTest passing, no regression (AC3).
  • Coverage: line 83.88% / branch 76.35% (baseline 83.79% / 76.36%); annotation-only, no changed-line regression (AC4).
  • UtilitiesCS.csproj: no <Nullable> element (AC2).

Solution-wide gate deviation (expected cross-child fan-in)

The solution-wide pragma-gate rebuild (msbuild TaskMaster.sln /t:Rebuild /p:TreatWarningsAsErrors=true) exits non-zero because ~148 pre-existing CS86xx originate in sibling-owned #nullable enable files (EmailIntelligence/**, OutlookObjects/**) plus two vendored SVGControl CS0649. Zero solution-wide errors originate in a #366 file. The operative gate for #366 is the isolated-cluster result above; solution-wide zero is the Wave-2 CI capstone (#376) obligation. Documented in the feature-review artifacts.

Acceptance criteria

  • AC1 (zero CS86xx under the per-file pragma): satisfied for all 51 files.
  • AC2 (no <Nullable> element): satisfied.
  • AC3 (no behavior change; tests pass): satisfied (5702 pass).
  • AC4 (no changed-line coverage regression): satisfied.
  • AC5 (behavior-compatible public signatures): satisfied (additive nullable annotations + ratified additive constraint only).
  • AC6 (non-opted-in files not cross-blocked): satisfied.

Feature-review: policy-audit, code-review, and feature-audit all PASS; blocking_count 0.

Refs #366.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv

drmoisan and others added 9 commits July 19, 2026 21:15
Per-file `#nullable enable` opt-in remediation for 32 reusable base/value
types under UtilitiesCS/ReusableTypeClasses/ (Batches 1-5: trivial leaves,
value/utility types, matrices, timed actions, locking core). Each file
carries a `#nullable enable` pragma and reaches zero CS86xx under the
per-file pragma gate. Annotation and null-safety only: nullable `?`
annotations, guard clauses, justified `!`, and unconstrained-generic
`T?`/`default!` fields. No behavior changes, no refactors, no file splits,
no post-condition attributes (net481 lacks the polyfills), no record/init
conversion. No `<Nullable>` element added to UtilitiesCS.csproj (AC2).

Verification (cluster, isolated-compile methodology): 0 CS86xx, 0 CS8714
across the 32 files under their pragmas; csharpier clean; analyzers build
succeeds (75 warnings = baseline, 0 errors); 5702 MSTest tests pass with
no regression; line coverage 83.8% / branch 76.4% (unchanged within
rounding, annotations are IL-invariant).

Batch 6-8 and the CS8714 `where TKey : notnull` public-contract change on
the four generic dictionary bases are HALTED pending maintainer
ratification (see evidence/other/cs8714-notnull-ratification.md, BLOCKED).
Phases 6-9 are out of scope for this commit.

Epic child of utilitiescs-nullable-remediation (Wave 0). Refs #366.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Atomic-executor memory: on net481 the ConcurrentDictionary reference
assemblies are not nullable-annotated, so `where TKey : notnull` is not
required to clear CS8714 under the per-file pragma gate; and the pragma
gate must be measured with the isolated-compile methodology because the
whole-project TWAE build carries pre-existing unrelated errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Orchestrator memory: (1) TaskMaster lacks the Python orchestrator-state
validator, so the SubagentStop completion hook uses the portable PowerShell
path (no require-complete) — a blocked child can terminate and open its PR
with blocked_reason=none and steps 5-8 non-pending; (2) rebasing an epic
child onto an advanced integration tip conflicts only on the shared
agent-memory MEMORY.md index (resolve as a union), and pr_context must be
regenerated from the real diff in an isolated agent worktree.

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

Nullable-annotate the 5 Batch 6 concurrent-observable base files under per-file
#nullable enable (ConcurrentObservableBag, ConcurrentObservableCollection[.Serialization],
ConcurrentObservableDictionary, ObservableDictionary; DictionaryChangedEventArgs newValue/oldValue
made TValue?). Zero CS86xx / zero CS8714 for the cluster (isolated pragma gate); 5702 tests pass.

Apply the maintainer-RATIFIED (2026-07-19T22:14:30Z) `where TKey : notnull` public generic-parameter-list
constraint to ConcurrentObservableDictionary<TKey, TValue>. The ConcurrentBag<T>-based
ConcurrentObservableBag is NOT constrained.

Cross-child two-file scope waiver (epic-orchestrator authorized 2026-07-19T23:10:00Z, Option A
extended to two files): to keep the ratified public contract internally consistent, add EXACTLY ONE
`where TKey : notnull` line to EACH of two #367-owned NewtonsoftHelpers consumers that otherwise emit
CS8714 once ConcurrentObservableDictionary is constrained, and nothing else:
  - UtilitiesCS/NewtonsoftHelpers/WrapperScoDictionary.cs   -> WrapperScoDictionary<TDerived, TKey, TValue>
  - UtilitiesCS/NewtonsoftHelpers/ScoDictionaryConverter.cs -> ScoDictionaryConverter<TDerived, TKey, TValue>
The cascade is statically bounded at these two files; PeopleScoConverter.cs (commented-out reference)
and WrapperPeopleScoDictionaryNew.cs (concrete type args) are SAFE and are NOT edited. A third
cross-child consumer would require re-escalation to the epic layer (none surfaced). Additive constraint
only; no runtime behavior change (ConcurrentDictionary already rejects null keys on net481).

Refs #366. Epic: utilitiescs-nullable-remediation.

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

Per-file #nullable enable annotation of the 7 Batch 7 files (NewSmartSerializableConfig,
SmartSerializableBase, SmartSerializable, SmartSerializableStatic, SmartSerializableNonTyped,
SmartSerializableLoader, ConfigController). Reflection-populated fields become `= null!`
(// set by deserialization); uninitialized events become nullable; an inherited CS8766
mismatch on SmartSerializable<T>.DeserializeObject was conformed to the interface `T` return
with a justified `!` rather than editing the out-of-scope interface.

Zero CS86xx/CS87xx attributable to the 7 Batch 7 files under the isolated per-file pragma gate
(whole-assembly nonzero exit is the pre-existing out-of-scope CS0618/CS0168 baseline only).
5702 tests pass; line 83.79% / branch 76.34%. Annotation-only: no record/init conversion, no
file split (SmartSerializable.cs and SmartSerializableBase.cs remain single files), no
NewtonsoftHelpers or exempt-WinForms file touched, no <Nullable> element added to the csproj.

Refs #366. Epic: utilitiescs-nullable-remediation.

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

Per-file #nullable enable annotation of the 7 Batch 8 files (SerializableList, ScBag,
ScoDictionaryStatic, ScoDictionaryNew, SloLinkedList, SloStack, ScDictionary). Serialization
round-trip fields become `= null!` (// set by deserialization); SloLinkedList NotImplementedException
interface stubs annotated to the interface nullable contract without bodies. Zero CS86xx and zero
CS8714 attributable to the 7 files under the isolated per-file pragma gate; 5702 tests pass;
line 83.79% / branch 76.34%. Annotation-only: no record/init, no file split (SerializableList.cs
remains single), no NewtonsoftHelpers/exempt-WinForms touch, no <Nullable> element added.

Ratified `where TKey : notnull` constraint status for Batch 8 bases:
  - ScoDictionaryNew<TKey, TValue>: APPLIED (cascade fully absorbed by the two already-waived
    #367 files WrapperScoDictionary.cs + ScoDictionaryConverter.cs; 0 CS8714).
  - ScoDictionaryStatic: NOT APPLICABLE — it is a non-generic static class of Type extension
    methods with no TKey (the plan's "four generic bases" wording is inaccurate for this file);
    reaches 0/0 regardless.
  - ScDictionary<TKey, TValue>: DEFERRED / NOT APPLIED. Applying the constraint surfaces a THIRD
    cross-child CS8714 in UtilitiesCS/NewtonsoftHelpers/WrapperScDictionary.cs(18) — a #367-owned
    consumer distinct from the two epic-authorized waiver files. Per the AUTHORIZED PROGRAM
    third-consumer clause, the child orchestrator STOPPED and re-escalated to the epic layer rather
    than widen the waiver unilaterally. The tree is green WITHOUT this constraint (net481 no-op).

[P8-T1] complete; [P8-T2..T4] pending the epic-layer decision on the ScDictionary constraint.

Refs #366. Epic: utilitiescs-nullable-remediation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
Epic layer authorized extending the ratified `where TKey : notnull`
cross-child waiver to a THIRD #367-owned NewtonsoftHelpers consumer,
UtilitiesCS/NewtonsoftHelpers/WrapperScDictionary.cs (one constraint
line on WrapperScDictionary<TDerived,TKey,TValue>), triggered by
constraining the fourth ratified base ScDictionary<TKey,TValue>.

Three-file waiver total (one `where TKey : notnull` line each):
  1. WrapperScoDictionary.cs   (Batch 6)
  2. ScoDictionaryConverter.cs (Batch 6)
  3. WrapperScDictionary.cs    (A', Batch 8 completion)

Factual correction: ScoDictionaryStatic is a non-generic static class
of Type extension methods (no TKey); the ratified "four generic bases"
wording is inapplicable there. The constraint applies to the three
truly generic bases (ConcurrentObservableDictionary, ScoDictionaryNew,
ScDictionary). ScBag remains unconstrained.

Revises plan tasks [P8-T2], [P9-T9], [P9-T10] and appends the OPTION
A-PRIME resolution section to the cs8714-notnull-ratification dossier.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
…our-file A'' cross-child waiver)

Applies `where TKey : notnull` to the third truly-generic base ScDictionary and propagates it to
the four #367-owned NewtonsoftHelpers consumers under the epic-authorized four-file waiver
(A''): WrapperScoDictionary.cs, ScoDictionaryConverter.cs, WrapperScDictionary.cs,
ScDictionaryConverter.cs. Cross-child consumer set is closed at four (Wrapper+Converter per base).
P9-T3 solution-wide count carries expected cross-child fan-in from sibling files (capstone #376).

Batch 8 isolated-cluster pragma gate: 0 CS86xx / 0 CS8714 in the #366 cluster including the four
waived NewtonsoftHelpers lines. Tests 5702/5702 pass; line coverage 83.79% -> 83.88% (no
changed-line regression). AC1-AC6 checked off in issue.md. Phase 8/9 final-QC evidence recorded.

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 four-file A'' waiver
completion. blocking_count 0; solution-wide CS86xx recorded as expected
cross-child fan-in (capstone #376), sub-85% repo-wide line coverage
dispositioned non-blocking (pre-existing baseline, AC4 no-regression PASS).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXCu1xPtNbJs2HwKxgS9Bv
@drmoisan
drmoisan force-pushed the feature/utilitiescs-nullable-reusabletypes-366 branch from 580c7c9 to 120365b Compare July 20, 2026 02:30
@drmoisan drmoisan changed the title Nullable remediation: UtilitiesCS/ReusableTypeClasses Batches 1-5 (#366) [BLOCKED: maintainer ratification] Nullable remediation: UtilitiesCS/ReusableTypeClasses (#366) Jul 20, 2026
@drmoisan
drmoisan merged commit bfcdb39 into epic/utilitiescs-nullable-remediation-integration Jul 20, 2026
@drmoisan
drmoisan deleted the feature/utilitiescs-nullable-reusabletypes-366 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