Skip to content

docs(repo): Tests/ nullable-cleanup plan — 96 warnings, 3-slice post-tag lane (gated) - #710

Merged
jsboige merged 1 commit into
masterfrom
docs/tests-nullable-cleanup-plan
Jul 5, 2026
Merged

docs(repo): Tests/ nullable-cleanup plan — 96 warnings, 3-slice post-tag lane (gated)#710
jsboige merged 1 commit into
masterfrom
docs/tests-nullable-cleanup-plan

Conversation

@jsboige

@jsboige jsboige commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Plan-only doc for the post-v0.9.0-tag tech-debt lane (dispatch awhj8g TERTIAIRE). No code change, no execution during freeze.

Inventory (code=truth on 34c7702c): dotnet build Tests.csproj --no-incremental96 source-level warnings across 20 files.

Category Count Codes
Nullable-flow 90 CS8625 (22), CS8618 (20), CS8602 (16), CS8620 (14), CS8600 (6), CS8619 (4), CS8604 (4), CS8603 (2), CS8605 (2)
Member hiding 4 CS0108
Dead store 2 CS0219

Top files: UpdateTableFromRecordsTests (20), PdfAlternateFaceAndBackContractTests (14) = 35% of total.

Recommended approach — 3 slices, each a separate PR with full test re-run after (a wrong ? on a test entity can silently change CsvHelper null/missing-field behavior — CLAUDE.md fragility):

  • Slice A (CS0108+CS0219, 6): mechanical, ~15 min, 1 PR.
  • Slice B (CS8618, 20): constructor-init, case-by-case ?/= null!/required, ~1h, ~3 PRs.
  • Slice C (CS86xx flow, 70): assertion/arrange judgment, ~3-4h, ~8-10 PRs.

Total: ~5h, ~12-14 PRs, spread across post-tag idle ticks.

Explicit NONOS:

  • ❌ No <Nullable>enable</Nullable> project-wide flip (would surface hundreds of SUT warnings — exploding blast radius).
  • ❌ No blanket #pragma warning disable (hides the signal).

Why not during freeze: risk of silently changing test behavior (CsvHelper null/missing-field distinction), low value for v0.9.0 (warnings pre-existing, don't block build, don't affect assets). Bundle with the SGEN/MSB3073 tooling warning deferred from #706 in the same post-tag window.

Plan-only: 0 code change, 0 build, 0 test run. Reproducible via dotnet build on 34c7702c.

Relates: dispatch awhj8g (TERTIAIRE), #587 (converter zero-warning), #706 (SYSLIB0014 + SGEN deferral). Base 34c7702c.

🤖 Generated with Claude Code

…tag lane (gated)

Plan-only doc for the post-v0.9.0-tag tech-debt lane dispatched by ai-01 (awhj8g tertiaire):
"documente le plan nullable-cleanup Tests/ (CS86xx) en docs/repo/, plan gated, PAS d'exécution
pendant freeze."

Empirical inventory on master 34c7702 (dotnet build Tests.csproj --no-incremental): 96 source-level
warnings across 20 files. Breakdown:
- CS8625 (22) null-to-non-nullable
- CS8618 (20) non-nullable property uninitialized
- CS8602 (16) possible-null dereference
- CS8620 (14) null to generic type param
- CS8600 (6), CS8619 (4), CS8604 (4), CS8603 (2), CS8605 (2) nullable-flow
- CS0108 (4) member hiding, CS0219 (2) dead store
By category: 90 nullable-flow (CS86xx) + 4 hiding + 2 dead-store. Top files: UpdateTableFromRecordsTests
(20), PdfAlternateFaceAndBackContractTests (14) — 35% of total.

Recommended approach: 3 slices, smallest-blast-radius first, each a separate PR with full test
re-run after (a wrong ? annotation on a test entity can silently change CsvHelper behavior — the
exact fragility CLAUDE.md documents):
- Slice A (CS0108+CS0219, 6 warnings): mechanical, ~15 min, 1 PR.
- Slice B (CS8618, 20): constructor-init, ?/= null!/required case-by-case, ~1h, ~3 PRs.
- Slice C (CS86xx flow, 70): assertion/arrange judgment, ~3-4h, ~8-10 PRs.
Estimated total ~5h, ~12-14 PRs, spread across post-tag idle ticks.

Explicit NONOS:
- No <Nullable>enable</Nullable> project-wide flip (would surface hundreds of SUT warnings,
  exploding blast radius).
- No blanket #pragma warning disable (hides the signal).

Why not during freeze: risk of silently changing test behavior (CsvHelper null/missing-field
distinction), low value for v0.9.0 (warnings pre-existing, don't block build, don't affect assets).
Bundle with the SGEN/MSB3073 tooling warning deferred from PR #706 in the same post-tag window.

Plan-only: 0 code change, 0 build, 0 test run. Reproducible via dotnet build on 34c7702.

Dispatch awhj8g (TERTIAIRE). Base 34c7702.

Co-authored-by: Claude-Code <noreply@anthropic.com>
@jsboige
jsboige merged commit c929be9 into master Jul 5, 2026
3 checks passed
@jsboige
jsboige deleted the docs/tests-nullable-cleanup-plan branch July 5, 2026 23:30
jsboige added a commit that referenced this pull request Jul 6, 2026
…agick.NET deferred-bug) (#719)

Read-only inventory of the 5 skipped tests in the AssetConverter test suite. Dispatch ynv05a
idle-de-secours ("review #133-adjacent test skips"). Base master bdba45d. 0 code change (release
freeze), dotnet test + read sources only.

Empirical dotnet test on bdba45d: 593 total = 587 pass / 1 fail / 5 skip. The 1 fail = permanent
known-fail OwlE2E...XmlRoundTrip (OWLSharp round-trip, #133). The 5 skips split cleanly:

- 4/5 = GSheetSync tests, hardcoded [Fact(Skip="Manual only — requires OAuth + network access")],
  credentials at jsboige-machine-specific G:\Mon Drive path + GDrive network. Legitimate infra-gated
  skips (CLAUDE.md "Pending: OAuth credentials"). GSheet logic covered by 77 unit tests in CI. Minor
  post-tag suggestion: convert unconditional Skip to conditional (run iff creds file exists) — same
  test-hygiene window as #710.

- 1/5 = SvgConversionIntegrationTests.TryAutomateSvgConversion... [Fact(Skip="Temporairement
  désactivé pour isoler le crash Magick.NET")] — the ONLY skip hiding a real bug. Skipped 2025-08-01
  (commit 6bd802f, ~11 months ago) under a stale "temporary" label. Tests the Freeplane-process SVG
  path (TryAutomateSvgConversion, via reflection). Flag for post-tag: production switched to FreeMind
  Batik SVGs (PR #565, April 2026), so the Freeplane path may be (a) dead code post-Batik, (b) a still-
  used fallback with a latent Magick.NET crash, or (c) a test-isolation artifact. Un-skip + reproduce
  resolves it. Bundled with #710 nullable + SGEN/MSB3073 post-tag lane.

Test-count drift note: CLAUDE.md snapshots 584 total (578/1/5) at 2026-07-05; empirical on bdba45d
= 593 total (587/1/5) — 9 new passing tests from recent merges (#715 + others). Skip/fail unchanged.

Read-only: 0 code change, 0 Cards/ write, 0 build mutation, no skipped test executed. Reproducible
via dotnet test on bdba45d.

Dispatch ynv05a (idle-de-secours). Base bdba45d.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…path replaced, dispatch h2utyb) (#722)

Root-cause investigation of the #719-flagged test skip (SvgConversionIntegrationTests...
TryAutomateSvgConversion, "Temporairement désactivé pour isoler le crash Magick.NET", skipped
2025-08-01). Dispatch h2utyb SECONDARY (ai-01 2026-07-06 07:09). Base master 7ebeda1. Docs only,
0 write Cards/, 0 code change.

Verdict: the Magick.NET crash is OBSOLETE — no longer reproducible. The code path that crashed
(Freeplane + Magick SVG rasterization) was REPLACED: TryAutomateSvgConversion now delegates to
TryFreeMindSvgExport (FreeMind GUI process + SendKeys, commit 46d6cd9), and production SVG
rendering moved to Batik (PR #565, April 2026). Evidence (code=truth): grep Magick
FallacyMindMapDocumentConfig.cs = 1 token = the dead 'using ImageMagick;' at line 19, ZERO usages;
the method body uses Process.Start + SendKeys, not MagickImage. This resolves #719 flag question
(a): the Magick dependency in the SVG path is dead.

Magick.NET itself is healthy and load-bearing ELSEWHERE (unrelated to SVG): ImageHelper.cs (image
loading, new MagickImage) + PdfManager.cs (PDF image collections, MagickImageCollection). Only its
former presence in the SVG path was the problem, and that presence is gone.

Fix options (post-tag, bundled with #710/#719 test-hygiene lane): (1) reclassify skip as
infra-gated "requires FreeMind GUI" (recommended, accurate reason, preserves local smoke-test
value); (2) delete (Magick premise obsolete, path covered by Batik tests + #715); (3) retarget to
headless Batik (most work, overlaps #715). In all 3, the stale Magick skip reason must be replaced.
Minor cleanup: remove the dead 'using ImageMagick;' (line 19). No dedicated issue — folds into #719.

No live repro run: the test sets FreeMindPath="" so TryAutomateSvgConversion hits the FreeMind-not-
found early return (returns false), never reaching Magick (there is none). Current failure mode if
un-skipped = "FreeMind not found" assertion fail, NOT a Magick crash. Code analysis is conclusive.

Read-only: 0 write Cards/, 0 code change, 0 test execution. Reproducible via grep on
FallacyMindMapDocumentConfig.cs + git log on the skip line.

Dispatch h2utyb (SECONDARY). Base 7ebeda1.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
… CS8603 in MmGeneratorTests) (#726)

Re-verification of the #710 nullable-cleanup baseline on master 9f52446 (dispatch h2utyb idle-de-
secours: "CS warnings beyond #710's 96"). Found +2 raw warnings = 1 new DISTINCT nullable-flow
warning introduced by PR #715 (fix #665 Virtue ar/fa/zh localization, commit 699580a).

Drift: MmGeneratorTests.cs:281 CS8603 (possible-null return). #715 added a CSV path-finder helper
(walks 12 parent dirs for Argumentum Virtues - Taxonomy.csv, returns null at line 281 if not found);
method returns non-nullable string → CS8603. Confirmed by git blame (lines 274-282 all = 699580a)
and diff vs 34c7702 (helper absent at #710 base — those lines held a FamilleExpression assertion).

MmGeneratorTests.cs: was 6 warnings (#710 top-8), now 7 raw (3 pre-existing CS8602 + 1 new CS8603).
Main converter project stays 0 CS (zero-warning, #587). Distinct-warnings on 9f52446 = 49; raw/echo
factor consistent across both measurements (96→98 = apples-to-apples raw-line count).

Impact on the plan: negligible — new warning is in Slice C scope (CS86xx nullable-flow),
MmGeneratorTests.cs already in §1 top-8. Post-tag Slice C file-by-file pass picks it up; no slice
re-scoping. Fix is low-risk: helper legitimately returns null → string? return type is the honest
annotation.

Read-only: 0 code change, 0 Cards/ write, 0 build mutation. Reproducible via dotnet build Tests.csproj
--no-incremental on 9f52446. Keeps the #710 baseline current so the post-tag lane runs against an
accurate inventory (96→98, +1 distinct).

Dispatch h2utyb (idle-de-secours). Base 9f52446.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…zero behavioral risk) (#727)

Executes Slice A of the #710 nullable-cleanup plan (#726 drift, dispatch x0ykyn PRIMARY).
Mechanical fixes only — no nullable-flow annotation, no SUT change, no CsvHelper-mapped entity.

Fixes (3 distinct warnings, 6 raw with build-echo):
- CS0108 ×2 (member hiding): EqualityComparerFactoryContractTests.cs:54-55 — TrackedDelegates.GetHashCode/Equals
  hide object.GetHashCode()/Equals(). Intentional (delegate-tracker, not a real comparer). Added `new` keyword.
- CS0219 ×1 (dead store): PdfDisposeContractTests.cs:67 — `var tracker = (DisposableTracker)null!;` assigned
  but never read (the test asserts on rec.Created[0], not the local). Removed the dead declaration.

DoD (per dispatch + #710 §2):
- dotnet build Tests.csproj --no-incremental: CS0108 4→0, CS0219 2→0. Total CS warnings 98→92 (-6).
  0 build errors. Files targeted (EqualityComparerFactoryContractTests, PdfDisposeContractTests) now
  free of CS0108/CS0219 (the remaining CS86xx in them are Slice C scope).
- dotnet test: 587 pass / 1 fail (OWL #133 permanent known-fail) / 5 skip (the #719 inventorially
  skips). 0 regression. Baseline 593 total preserved.

Why Slice A first (#710 §2 sequencing): CS0108 + CS0219 are the smallest-blast-radius, pure-
mechanical warnings. `new` on an intentional hide + removing an unread local cannot change runtime
behavior or CsvHelper mapping — the exact fragility CLAUDE.md / #710 §3 warn about is untouched
(no nullable annotation on a test entity, no missing-field vs null-field drift). Slices B (CS8618)
and C (CS86xx flow) follow in separate PRs with full re-test after each.

Base 240511c (post #726 merge). 0 Cards/ write, 0 rendering code change.

Dispatch x0ykyn (PRIMARY, Slice A). Base 240511c.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…arnings, synthetic entities) (#728)

Executes Slice B of the #710 nullable-cleanup plan (dispatch x0ykyn PRIMARY, after Slice A #727).
Fixes the 10 distinct CS8618 warnings (20 raw with build-echo) — non-nullable property uninitialized
in constructor — across 5 test-fixture files.

Discipline (#710 §2): all 10 are SYNTHETIC test entities / SUT doubles (not production domain entities
like Fallacy/Rule). The fix is `= string.Empty;` for plain string properties set by object initializer,
and `= null!;` for (a) the `init` property (Sample.Id) and (b) the CsvBase-derived ContractEntity
("logically required but set later by CsvHelper load" — the test-fixture idiom from #710 §2).

CSVHELPER SAFETY (#710 §3 risk, CLAUDE.md): NO type is changed to nullable — every fix keeps the
property type as non-nullable `string` and only adds a default initializer. CsvHelper keys on the
property TYPE + the ClassMap, not on C# default initializers, so mapping behavior (missing-field vs
null-field distinction) is byte-identical. The CsvBaseStrict + CsvParser test suites (which exercise
the real CsvHelper load path) both pass unchanged after the fix — direct confirmation there is no
missing/null-field drift.

Fixes:
- EqualityComparerFactoryContractTests.cs:45 Sample.Id (init) → `= null!;`
- ScribanGeneratorTests.cs:11,12 FallacyRecord.Name/Description → `= string.Empty;`
- CsvBaseStrictContractTests.cs:47,48 ContractEntity.Required/Optional (CsvBase-mapped) → `= null!;`
- CsvParserTests.cs:17,18 FallacyRecord.Name/Description → `= string.Empty;`
- InterpolateCacheTests.cs:18,19,24 TypeA.Text/Family + TypeB.Text → `= string.Empty;`

DoD:
- dotnet build Tests.csproj --no-incremental: CS8618 20→0. Total CS warnings 92→78 (-14). 0 errors.
- dotnet test: 587 pass / 1 fail (OWL #133 permanent) / 5 skip (#719). 0 regression — CsvBaseStrict,
  CsvParser, Interpolate, Scriban suites all green (the CsvHelper-mapping-risk suites).

Cumulative (Slice A #727 + this Slice B): 98→78 warnings (-20). Slice C (CS86xx nullable-flow,
~68 remaining) follows in separate PRs with full re-test after each — that slice touches assertions/
arrange with intentional null-passing, needs the most judgment.

Base 240511c. 0 Cards/ write, 0 rendering code change.

Dispatch x0ykyn (PRIMARY, Slice B). Base 240511c.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…ssert null (13 warnings) (#729)

First sub-lot of Slice C (CS86xx nullable-flow) of the #710 nullable-cleanup plan
(dispatch x0ykyn PRIMARY; follows Slice A #727 + Slice B #728). Slice C is the riskiest slice
per #710 §2 — it touches arrange/assert code with intentional null-passing — so it is shipped in
small, cohesive, per-area sub-lots with a full re-test after each, rather than one big bang.

This sub-lot: 13 distinct CS86xx warnings across 2 files in WebBasedGenerator/, both test-only
arrange/assert null-handling. NEITHER file touches CsvHelper mapping or parsing code, so the
CLAUDE.md missing/null-field fragility class is not in scope.

UpdateTableFromRecordsTests.cs (9 warnings) — DataSetInfo.UpdateTableFromRecords contract suite
(DataTable round-trip, GSheetSync-adjacent; no CsvHelper):
- L26  CS8619 `table.PrimaryKey = new[] { table.Columns[PrimaryKey]! }` — the column was just
       Add()'d on the prior line, so the indexer's nullable annotation is provably non-null here.
- L36  CS8625 `Record(string pk, string? textEn = null, string? descEn = null)` — the body already
       null-checks both (`if (textEn != null)`), so `string?` is the honest param type.
- L72/95/118/168 CS8625 `new object[] { "1", "old", null! }` — the null is the desc cell value;
       DataTable stores it as DBNull. Suppression is honest (runtime-legal, annotation-strict).
- L134/157/158 CS8602 `Rows.Find("x")!["col"]` — the prior line asserts
       `.Should().NotBeNull()`; the `!` acknowledges the assertion the compiler can't track.

PdfAlternateFaceAndBackContractTests.cs (4 warnings) — PdfManager.OrderImagesForAlternateFaceAndBack
contract suite (recto-verso ordering, pure; no CSV):
- L52/77/87/110 CS8620 — all `Cards(("Fx", null))` passing null-back to exercise the prod
       IsNullOrEmpty guard ("Rules have no back"). ONE helper-signature change fixes all four:
       `Cards(params (string front, string? back)[] specs)` makes the tuple's back honestly
       nullable (the tests deliberately pass null), with a single `s.back!` suppression at the
       CardImages.Back assignment documenting that prod's entity annotates Back non-nullable but
       tolerates a null Back at runtime via the guard. This is the intentional null-passing idiom
       #710 §2 calls out for test arrange — documented inline in the helper comment.

DoD (measured on this branch, base master 240511c):
- dotnet build Tests.csproj --no-incremental: the 2 target files emit 0 CS86xx (13→0). 0 errors.
- dotnet test: 587 pass / 1 fail (OWL #133 permanent) / 5 skip (#719). 0 regression —
  UpdateTableFromRecords + PdfAlternateFaceAndBack suites all green.

CsvHelper safety (#710 §3): NOT IN SCOPE — neither file is a CsvHelper-mapped entity or exercises
the CSV load path. The CsvBaseStrict + CsvParser suites (the mapping-risk suites) are untouched.

Slice C remaining: 19 distinct CS86xx across ~12 files (CsvGridConversion, MmGenerator incl. the
#715-introduced CS8603 at L281, SvgConversion×2, SvgDisambiguation, SvgPostProcessing, OwlE2E,
FallacyLinkFallback×3, LoggerMarkup, UtilityExtensions, PKHierarchical, ImageFileGenerator×2,
SimpleImageGenerator). These touch parsing/OWL/mindmap code paths and need closer per-line reading;
follow in subsequent sub-lots with the same re-test-after-each discipline.

Base 240511c. 0 Cards/ write, 0 rendering code change.

Dispatch x0ykyn (PRIMARY, Slice C-1). Base 240511c.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…s + #715 CS8603 (4 warnings) (#730)

Second sub-lot of Slice C (CS86xx nullable-flow) of the #710 nullable-cleanup plan
(dispatch x0ykyn PRIMARY; follows Slice A #727, Slice B #728, Slice C-1 #729).

This sub-lot closes the dispatch's explicit ask — "corrige le CS8603 de #715" — by fixing all
4 CS86xx in MmGeneratorTests.cs (the mindmap-generation test fixture), including the CS8603 that
#715 (commit 699580a, #665 Virtue ar/fa/zh loc) introduced at L281 (documented in the #726 drift
note as the 96→98 regression). The file is test-only and exercises the real mindmap-generation
code path (FallacyMindMapDocumentConfig / VirtueMindMapDocumentConfig via FreeMindPath="" headless).

Fixes (4 warnings, all low-risk post-assert suppression or honest nullable return):

- L86  CS8602 `xmlDoc.Root!.Name.LocalName` — XDocument.Parse of the test's well-formed .mm yields
       a non-null Root; the `!` documents that invariant. (No prior Root assert, hence the `!`.)
- L138 CS8602 `xmlDoc.Root!.Element("node")!.Elements("node")` — both derefs are guarded by the
       two FluentAssertions `NotBeNull` on the immediately preceding lines (L136 Root, L137 node).
- L176 CS8602 `xmlDoc.Root!.Element("node")!.Elements("node")` — same pattern, guarded by L174+L175.
- L273/281 CS8603 `private static string? FindRepoVirtuesCsv()` returning `null` — the #715 path-finder
       helper legitimately returns null when the Virtues taxonomy CSV is not found walking up from
       the bin dir. `string?` is the honest return type. The sole caller (L299) asserts
       `csvPath.Should().NotBeNull(...)` on L300 before passing it to LoadVirtuesFromPathAsync on L302;
       `csvPath!` there acknowledges that assertion (the compiler can't track FluentAssertions).

#715 CS8603 root cause (code=truth): git blame L274-282 = 699580a (#715); the helper was added to
locate the committed Virtues CSV for the new native-script ar/fa/zh test, declared `string` (non-null)
with a `return null;` fallback — a classic CS8618/CS8630-class mismatch the #710 sweep now catches.

DoD (measured on this branch, base master 240511c):
- dotnet build Tests.csproj --no-incremental: MmGeneratorTests emits 0 CS86xx (4→0). 0 errors.
- dotnet test --filter MmGeneratorTests: 11/11 pass (incl. the 3 ar/fa/zh InlineData #715 cases).
- dotnet test (full): 587 pass / 1 fail (OWL #133 permanent) / 5 skip (#719). 0 regression.

CsvHelper safety (#710 §3): NOT IN SCOPE — MmGeneratorTests exercises the mindmap-generation path
(.mm XML via XDocument), not the CsvHelper CSV load path. The mapping-risk suites are untouched.

Slice C remaining: 15 distinct CS86xx across ~11 files (CsvGridConversion, SvgConversion×2,
SvgDisambiguation, SvgPostProcessing, OwlE2E, FallacyLinkFallback×3, LoggerMarkup,
UtilityExtensions, PKHierarchical, ImageFileGenerator×2, SimpleImageGenerator). Follow in
subsequent sub-lots.

Base 240511c. 0 Cards/ write, 0 rendering code change.

Dispatch x0ykyn (PRIMARY, Slice C-2 — closes the #715 CS8603 call-out). Base 240511c.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…er (4 warnings) (#731)

Third sub-lot of Slice C (CS86xx nullable-flow) of the #710 nullable-cleanup plan
(dispatch x0ykyn PRIMARY; follows Slice A #727, B #728, C-1 #729, C-2 #730).

Cohesive sub-lot by area: all 4 remaining CS86xx in the MindmapGeneration SVG cluster
(3 files). This cluster sits in CLAUDE.md "Known Fragile Area #1 — SVG disambiguation" territory,
so each fix is the most conservative honest annotation, not a logic change.

Fixes (4 warnings):

- SvgConversionIntegrationTests.cs L59-60 (CS8602 method-null deref + CS8605 nullable unbox):
  `GetMethod` returns `MethodInfo?` and `Invoke` returns `object?`. The test is `[Fact(Skip=...)]`
  (the #719 Magick.NET skip — root-caused OBSOLETE by #722: the path was replaced by FreeMind GUI
  + Batik). Added `Assert.NotNull(method)` + `method!.Invoke(...)!` so the cast is honest. The
  skipped test never runs; the annotation just satisfies the compiler. NO behavioral change.
- SvgDisambiguationContractTests.cs L37 (CS8625): `G(string text, string id = null)` →
  `G(string text, string? id = null)`. The body already null-checks (`if (id != null)`) before
  `SetAttributeValue`, so `string?` is the honest param type. Caller fixture sites pass null to
  build <g> elements without an id — intentional.
- SvgPostProcessingTests.cs L33 (CS8604): `Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!`
  — GetDirectoryName returns `string?` (Location can in theory be empty); in a running test the
  assembly location is non-null, so the `!` documents that invariant.

DoD (measured on this branch, base master 240511c):
- dotnet build Tests.csproj --no-incremental: the 3 target files emit 0 CS86xx (4→0). 0 errors.
- dotnet test --filter MindmapGeneration: 85 pass / 1 skip (the Magick test, expected). 0 regression.
- dotnet test (full): 587 pass / 1 fail (OWL #133 permanent) / 5 skip (#719). 0 regression.

CsvHelper safety (#710 §3): NOT IN SCOPE — none of these files is a CsvHelper-mapped entity or
exercises the CSV load path. The mapping-risk suites are untouched.

Slice C remaining: 11 distinct CS86xx across 8 files (CsvGridConversion, ImageFileGenerator×2,
SimpleImageGenerator, OwlE2E, FallacyLinkFallback×3, LoggerMarkup, UtilityExtensions,
PKHierarchical). Follow in subsequent sub-lots.

Base 240511c. 0 Cards/ write, 0 rendering code change.

Dispatch x0ykyn (PRIMARY, Slice C-3). Base 240511c.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…warnings) (#732)

Fourth sub-lot of Slice C (CS86xx nullable-flow) of the #710 nullable-cleanup plan
(dispatch x0ykyn PRIMARY; follows Slice A #727, B #728, C-1 #729, C-2 #730, C-3 #731).

Cohesive sub-lot by area: all 3 remaining CS86xx in ImageGeneration/ (2 files).
None is a CsvHelper-mapped entity or exercises the CSV load path.

Fixes (3 warnings):

- ImageFileGeneratorTests.cs L65 (CS8604): `Path.GetDirectoryName(fakeImagePath)!` — GetDirectoryName
  returns `string?`; the fake-image path is built by ImageHelper from a known config so the dir part
  is non-null. `!` documents that invariant.
- ImageFileGeneratorTests.cs L166 (CS8600): `result = null!` — the ConcurrentDictionary is the
  classic closure-capture pattern: declared null, assigned inside the `Action act = () => result =
  sut.GenerateDocumentImages(...)`, then asserted `result.Should().NotBeNull()` at L172 before use.
  `null!` is the honest initializer.
- SimpleImageGeneratorTests.cs L40-43 (CS8602): `JsonSerializer.Deserialize` returns
  `AssetConverterConfig?`. Added `config.Should().NotBeNull(...)` (committed test-config.json must
  deserialize) + `config!.BaseTargetDirectoryName`. The FluentAssertions guard + `!` together satisfy
  the flow analysis (0 residual CS8603 at the `return config;`).

DoD (measured on this branch, base master 240511c):
- dotnet build Tests.csproj --no-incremental: the 2 target files emit 0 CS86xx (3→0). 0 errors.
- dotnet test --filter ImageGeneration: 8/8 pass. 0 regression.
- dotnet test (full): 587 pass / 1 fail (OWL #133 permanent) / 5 skip (#719). 0 regression.

CsvHelper safety (#710 §3): NOT IN SCOPE — neither file is a CsvHelper-mapped entity or exercises
the CSV load path.

Slice C remaining: 8 distinct CS86xx across 6 files (CsvGridConversion, OwlE2E,
FallacyLinkFallback×3, LoggerMarkup, UtilityExtensions, PKHierarchical). Follow in subsequent sub-lots.

Base 240511c. 0 Cards/ write, 0 rendering code change.

Dispatch x0ykyn (PRIMARY, Slice C-4). Base 240511c.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
… null (3 warnings) (#733)

Fifth sub-lot of Slice C (CS86xx nullable-flow) of the #710 nullable-cleanup plan
(dispatch x0ykyn PRIMARY; follows Slice A #727, B #728, C-1 #729, C-2 #730, C-3 #731, C-4 #732).

Single-signature fix: 3 CS8625 (L115/135/152) all stem from `SetLink(f, linkProp, null)` where the
helper was declared `SetLink(Fallacy f, string propName, string value)`. The null is INTENTIONAL —
the FallacyLinkFallback test suite deliberately clears a non-source language link to exercise the
mind-map link fallback cascade (non-source null → EN → FR source-floor → null floor). `string? value`
is the honest param type; the body `SetValue(f, value)` accepts object so null is runtime-legal.

This is the fragile-area link-fallback contract pinned by this suite (CLAUDE.md mind-map
localization) — the annotation changes nothing about the cascade behavior, only the declared
parameter type. Direct confirmation: all 29 FallacyLinkFallback tests pass unchanged.

DoD (measured on this branch, base master 240511c):
- dotnet build Tests.csproj --no-incremental: FallacyLinkFallback emits 0 CS86xx (3→0). 0 errors.
- dotnet test --filter FallacyLinkFallback: 29/29 pass.
- dotnet test (full): 587 pass / 1 fail (OWL #133 permanent) / 5 skip (#719). 0 regression.

CsvHelper safety (#710 §3): NOT IN SCOPE — FallacyLinkFallback exercises the link-fallback cascade
via reflection on Fallacy entities, not the CsvHelper CSV load path.

Slice C remaining: 5 distinct CS86xx across 5 files (CsvGridConversion, OwlE2E, LoggerMarkup,
UtilityExtensions, PKHierarchical) — singletons, bundle into a final C-final lot.

Base 240511c. 0 Cards/ write, 0 rendering code change.

Dispatch x0ykyn (PRIMARY, Slice C-5). Base 240511c.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…omplete (#710) (#734)

Final sub-lot of Slice C (CS86xx nullable-flow) of the #710 nullable-cleanup plan
(dispatch x0ykyn PRIMARY; follows Slice A #727, B #728, C-1 #729, C-2 #730, C-3 #731, C-4 #732,
C-5 #733). This completes Slice C: all 32 distinct CS86xx in Tests/ are addressed across the
7 sub-lots C-1..C-final (merged set), leaving 0 CS86xx once all slice PRs are merged.

The 5 singletons (one warning per file, 5 different areas):

- CsvGridConversionContractTests.cs L176 (CS8625): `new List<object> { "a", null!, "c" }` — the null
  cell is INTENTIONAL (test name "GridToCsv_NullCells_BecomeEmptyStrings", pins the `cell?.ToString()
  ?? ""` guard). `null!` is the honest suppression (runtime-legal null cell, annotation-strict).
- OwlE2EGenerationValidationTests.cs L60 (CS8603): `private static string? ResolveRepoFile(...)` —
  path-finder helper legitimately returns null when the ontology file isn't found walking up from
  the bin dir (same idiom as #715 FindRepoVirtuesCsv). The sole caller (L47) already null-checks and
  throws FileNotFoundException (L48-54); the throw guarantees `path` non-null at L55 FromFile, so no
  caller `!` needed.
- LoggerMarkupSafetyTests.cs L58 (CS8600): `string output = null!;` — closure-capture pattern
  (assigned in the lambda at L59, asserted at L62). `null!` is the honest initializer.
- UtilityExtensionsLayoutTests.cs L142 (CS8600): `((string)null!).PathIsUrl()` — the null is
  INTENTIONAL (test name "PathIsUrl_Returns_False_For_Null", pins the null-guard so a regression
  that dropped it would NRE). The `null!` documents that intent.
- PKHierarchicalGroupingTests.cs L25 (CS8619): `.Select(record => record["path"].ToString()!)` —
  the path key is always set by BuildRecords (L22), so ToString on the boxed string is non-null;
  `!` documents that invariant for the ToHashSet<string>() return type.

DoD (measured on this branch, base master 240511c):
- dotnet build Tests.csproj --no-incremental: the 5 target files emit 0 CS86xx (5→0). 0 errors.
- dotnet test (targeted GSheetSync+Ontology+Utility+PKHierarchical): 52 pass / 1 fail
  (OWL #133 permanent known-fail, pre-existing — not a regression; the 3 OTHER OwlE2E tests pass).
- dotnet test (full): 587 pass / 1 fail (OWL #133) / 5 skip (#719). 0 regression.

CsvHelper safety (#710 §3): NOT IN SCOPE — none of these 5 files is a CsvHelper-mapped entity or
exercises the CSV load path (GSheet grid round-trip, OWL ontology load via OwlAdapter, Logger
markup, path utility, PK grouping reflection).

#710 plan status once all 8 PR (#727-733 + this) merged: Tests/ CS86xx from 98 raw (49 distinct)
to 0. Slice A (6 raw CS0108/CS0219) + Slice B (20 raw CS8618) + Slice C (32 distinct CS86xx) = the
full 98-raw baseline. The #715 drift (CS8603, 96→98) closed at C-2.

Base 240511c. 0 Cards/ write, 0 rendering code change.

Dispatch x0ykyn (PRIMARY, Slice C-final — Slice C complete). Base 240511c.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jul 6, 2026
…nings (#736)

Follow-on to the #710 nullable-cleanup (plan complete, 8 PR #727-734 merged). Dispatch `5czj9v`
[2] SECONDAIRE. Clears the 3 residual xUnit-analyzer warnings in Tests/ (the nullable family hit 0
in #710; these are the remaining non-CS analyzer warnings the build emits).

Fixes (3 distinct warnings, all additive annotation or async — no logic change):

- HarvestManagerExpectedImageCountTests.cs L56 (xUnit1012): `string rsstyle` → `string? rsstyle`.
  The Theory has `[InlineData(10, 3, null, 10)]` (L52) deliberately passing null rsstyle to pin the
  "null rsstyle = no grouping" branch of ComputeExpectedImageCount (mirrors CardPen). The null is
  INTENTIONAL test input; `string?` is the honest param type.
- LocalizedFileNameContractTests.cs L30 (xUnit1012): `string fileName` → `string? fileName`. The
  Theory has `[InlineData(null)]` (L28) pinning the GetLocalizedFileName guard clause (null/empty
  returned as-is). Same intentional-null-test-input idiom.
- OwlAdapterRegressionTests.cs L306+L319 (xUnit1031): `public void` → `public async Task` +
  `adapter.ToFileAsync(...).Wait()` → `await adapter.ToFileAsync(...)`. xUnit1031 flags blocking
  Task.Wait() in test methods; the fix is the canonical async/await. The method tests OWL2XML
  serialization to a temp file (no sync-context → no deadlock risk either way); async is the clean
  fix the analyzer asks for. Test name unchanged (ToFileAsync_...).

DoD (measured on this branch, base master 9ed2e78):
- dotnet build Tests.csproj --no-incremental: xUnit warnings 3→0. 0 errors.
- dotnet test --filter (HarvestManagerExpected|LocalizedFileName|OwlAdapterRegression): 41/41 pass.
- dotnet test (full): 587 pass / 1 fail (OWL #133 permanent) / 5 skip (#719). 0 regression.

Residual (irréductible-infra, hors-scope Tests/ DoD): MSB3073 ×1 distinct on the MAIN project
(Argumentum.AssetConverter.csproj, not Tests) — the Microsoft.XmlSerializer.Generator PackageReference
(L25) sgen target exits code 1 at build. This is a main-project build-infra warning, not a Tests/
code warning; the DoD scope is "dotnet build Tests → 0 warning (ou irréductible documenté)". It
appears in the Tests build log only via the Tests→main project dependency. Suppressing it would
require touching the main csproj (out of scope for this tests-only lane; main project is
zero-CS-warning stable per #587 and the SGEN package may be load-bearing for ExtendedXmlSerializer).
Follow-up: separate main-project investigation to decide suppress (`<XmlSerializerGenerator...>` off)
vs document as irréductible-infra.

Base 9ed2e78. 0 Cards/ write, 0 rendering code change.

Dispatch 5czj9v [2] SECONDAIRE. Base 9ed2e78.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude-Code <noreply@anthropic.com>
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