From 8f34f8ef45d188f02ea19caef3c6e2b610f1a4ab Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Mon, 20 Jul 2026 13:53:15 -0400 Subject: [PATCH 1/3] fix(folder-selection): make fallback folder selection index bounds-safe - Guard AssignFolderComboBox fallback selection against single-item list - Guard PopulateAndSelectFolder fallback selection against single-item list - Add regression tests for single-item, no-predetermined-match case Refs: #392 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01A2Yht8Rg83Um38RaVyV1sA --- .../QfcItemController.FolderHandlingTests.cs | 54 ++- .../QfcItemController.FolderHandling.cs | 7 +- .../analyzer-baseline.2026-07-20T13-25.md | 34 ++ ...ndidate-defect-surface.2026-07-20T13-10.md | 27 ++ .../csharpier-baseline.2026-07-20T13-15.md | 4 + .../git-baseline-state.2026-07-20T13-08.md | 4 + .../minor-audit-scope.2026-07-20T13-07.md | 15 + .../nullable-baseline.2026-07-20T13-35.md | 6 + .../baseline/phase0-instructions-read.md | 87 ++++ ...test-coverage-baseline.2026-07-20T13-45.md | 42 ++ .../ac-status-final-392.2026-07-20T14-55.md | 41 ++ .../ac-status-final-392.2026-07-20T15-15.md | 37 ++ .../ac-status-phase1-392.2026-07-20T14-15.md | 24 + ...ac-closure-summary-392.2026-07-20T14-55.md | 21 + ...ac-closure-summary-392.2026-07-20T15-15.md | 17 + .../other/root-cause-392.2026-07-20T13-50.md | 35 ++ .../analyzer-final-392.2026-07-20T14-24.md | 9 + ...overage-conversion-392.2026-07-20T14-50.md | 38 ++ .../coverage-delta-392.2026-07-20T14-38.md | 55 +++ .../csharpier-final-392.2026-07-20T14-20.md | 28 ++ ...or-audit-readiness-392.2026-07-20T15-00.md | 72 +++ ...or-audit-readiness-392.2026-07-20T15-20.md | 72 +++ .../nullable-final-392.2026-07-20T14-28.md | 25 ++ .../nullable-final-392.2026-07-20T15-10.md | 39 ++ .../regression-check-392.2026-07-20T14-42.md | 7 + ...est-coverage-final-392.2026-07-20T14-32.md | 30 ++ .../fail-before-392.2026-07-20T14-05.md | 19 + .../pass-after-392.2026-07-20T14-10.md | 9 + ...eted-no-regression-392.2026-07-20T14-13.md | 13 + .../issue.md | 85 ++++ .../plan.2026-07-20T12-59.md | 421 ++++++++++++++++++ 31 files changed, 1358 insertions(+), 19 deletions(-) create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/analyzer-baseline.2026-07-20T13-25.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/candidate-defect-surface.2026-07-20T13-10.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/csharpier-baseline.2026-07-20T13-15.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/git-baseline-state.2026-07-20T13-08.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/minor-audit-scope.2026-07-20T13-07.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/nullable-baseline.2026-07-20T13-35.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/phase0-instructions-read.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/vstest-coverage-baseline.2026-07-20T13-45.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392.2026-07-20T14-55.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392.2026-07-20T15-15.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-phase1-392.2026-07-20T14-15.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392.2026-07-20T14-55.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392.2026-07-20T15-15.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/root-cause-392.2026-07-20T13-50.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392.2026-07-20T15-00.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392.2026-07-20T15-20.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392.2026-07-20T14-28.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/regression-check-392.2026-07-20T14-42.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/fail-before-392.2026-07-20T14-05.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/pass-after-392.2026-07-20T14-10.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/plan.2026-07-20T12-59.md diff --git a/QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs b/QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs index 68fffc4d..d7923ade 100644 --- a/QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs +++ b/QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs @@ -14,10 +14,7 @@ namespace QuickFiler.Controllers.Tests { - /// - /// Folder-handling cluster tests (research §5.2). Covers the pure static folder-selection seam - /// PopulateAndSelectFolder edge cases and the AssignFolderComboBox guard behavior. - /// + /// Folder-handling cluster tests (research §5.2): PopulateAndSelectFolder seam edge cases and AssignFolderComboBox guard behavior. [TestClass] public class QfcItemController_FolderHandlingTests { @@ -89,12 +86,23 @@ public void PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection() } } - /// - /// Builds a with a known FolderArray without touching - /// Outlook COM. The single-arg FolderPredictor(Outlook.Application) constructor performs - /// no COM work; seeding the private _folderList backing field makes the lazy - /// FolderArray getter return that list directly. - /// + [TestMethod] + public void PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing() + { + // Single suggestion, no predetermined match: bounds-safe fallback selects index 0 (#392). + var folders = new[] { @"\\A\only" }; + using (var comboBox = new ComboBox()) + { + Action act = () => + QfcItemController.PopulateAndSelectFolder(comboBox, folders, null); + + act.Should().NotThrow(); + comboBox.SelectedIndex.Should().Be(0); + (comboBox.SelectedItem as string).Should().Be(@"\\A\only"); + } + } + + /// Builds a with a known FolderArray by seeding the private _folderList field, without touching Outlook COM. private static FolderPredictor BuildFolderHandlerWithArray(params string[] folders) { var ctor = typeof(FolderPredictor) @@ -461,13 +469,7 @@ public void AssignFolderComboBox_WhenFolderHandlerNull_DoesNotTouchViewer() var mock = new Mock(); mock.SetupGet(v => v.InvokeRequired).Returns(false); var controller = new FolderController(); - typeof(QfcItemController) - .GetField( - "_itemViewer", - System.Reflection.BindingFlags.NonPublic - | System.Reflection.BindingFlags.Instance - ) - .SetValue(controller, mock.Object); + SetPrivate(controller, "_itemViewer", mock.Object); // Act controller.AssignFolderComboBox(); @@ -476,5 +478,23 @@ public void AssignFolderComboBox_WhenFolderHandlerNull_DoesNotTouchViewer() mock.Verify(v => v.SetFolderItems(It.IsAny()), Times.Never()); mock.Verify(v => v.SetFolderSelectedIndex(It.IsAny()), Times.Never()); } + + [TestMethod] + public void AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero() + { + // Single suggestion, no predetermined match: viewer selects index 0, not 1 (#392). + var mock = new Mock(); + mock.SetupGet(v => v.InvokeRequired).Returns(false); + mock.Setup(v => v.GetSelectedFolder()).Returns(@"\\A\only"); + var controller = new FolderController(); + SetPrivate(controller, "_itemViewer", mock.Object); + SetPrivate(controller, "_folderHandler", BuildFolderHandlerWithArray(@"\\A\only")); + + controller.AssignFolderComboBox(); + + mock.Verify(v => v.SetFolderSelectedIndex(0), Times.Once()); + mock.Verify(v => v.SetFolderSelectedIndex(1), Times.Never()); + controller.SelectedFolder.Should().Be(@"\\A\only"); + } } } diff --git a/QuickFiler/Controllers/QfcItemController.FolderHandling.cs b/QuickFiler/Controllers/QfcItemController.FolderHandling.cs index a09ca4ab..059fe83f 100644 --- a/QuickFiler/Controllers/QfcItemController.FolderHandling.cs +++ b/QuickFiler/Controllers/QfcItemController.FolderHandling.cs @@ -199,7 +199,9 @@ public void AssignFolderComboBox() } else { - _itemViewer.SetFolderSelectedIndex(1); + _itemViewer.SetFolderSelectedIndex( + _folderHandler.FolderArray.Length == 1 ? 0 : 1 + ); } _selectedFolder = _itemViewer.GetSelectedFolder(); } @@ -225,7 +227,8 @@ string predeterminedFolder int predeterminedIndex = string.IsNullOrEmpty(predeterminedFolder) ? -1 : comboBox.Items.IndexOf(predeterminedFolder); - comboBox.SelectedIndex = predeterminedIndex >= 0 ? predeterminedIndex : 1; + comboBox.SelectedIndex = + predeterminedIndex >= 0 ? predeterminedIndex : (folderArray.Length == 1 ? 0 : 1); return comboBox.SelectedItem as string; } } diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/analyzer-baseline.2026-07-20T13-25.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/analyzer-baseline.2026-07-20T13-25.md new file mode 100644 index 00000000..b76c190f --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/analyzer-baseline.2026-07-20T13-25.md @@ -0,0 +1,34 @@ +Timestamp: 2026-07-20T13-25 +Command: `MSBuild.exe TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true /m` (VS18 Community full-framework MSBuild at `C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\amd64\MSBuild.exe`; not the dotnet-core SDK MSBuild) +EXIT_CODE: 0 +Output Summary: Build succeeded. 80 Warning(s), 0 Error(s). Time Elapsed 00:00:10.19. + +## Pre-restore / pre-build environment note (recorded for auditability) + +Before this successful run, two blocking environment issues were resolved (neither required editing any scope-locked or in-scope-lock-forbidden project file): + +1. NuGet packages were not restored in this fresh worktree. Ran + `nuget.exe restore TaskMaster.sln` (nuget.exe located at + `C:\Users\DanMoisan\AppData\Local\Microsoft\WinGet\Packages\Microsoft.NuGet_Microsoft.Winget.Source_8wekyb3d8bbwe\nuget.exe`). + 170 packages restored to the (gitignored) `packages/` folder. + +2. After restore, `VBFunctions.csproj` and `UtilitiesCS.csproj` still failed with `CSC : error CS0006` + because their hardcoded `` paths reference older analyzer package versions + (`Meziantou.Analyzer.3.0.101`, `SonarAnalyzer.CSharp.10.27.0.140913`, + `Microsoft.CodeAnalysis.BannedApiAnalyzers.3.3.4`) than what `packages.config` now declares + (`3.0.123`, `10.29.0.143774`, `5.6.0` respectively). This mismatch was introduced by the most + recent commit on this branch, `1e5ada71 (chore): update packages`, which bumped + `packages.config` versions across ~48 files but did not update the corresponding hardcoded + `` paths in `VBFunctions.csproj`/`UtilitiesCS.csproj`. This is a pre-existing + repo-wide defect, unrelated to issue #392 and out of this plan's Scope-Lock (no + `UtilitiesCS`/`VBFunctions` project file may be touched by this plan). Because `QuickFiler.csproj` + and `QuickFiler.Test.csproj` (in scope) both depend on `UtilitiesCS.csproj`, this defect + transitively blocked the entire toolchain, including this plan's in-scope build/test targets. + Resolved without editing any project file by installing the three specific older analyzer + package versions still referenced by the stale `` paths into the (gitignored) + `packages/` folder via `nuget.exe install -Version -OutputDirectory packages`, + so both the old (csproj-referenced) and new (packages.config-declared) analyzer package versions + coexist on disk side by side. No `.csproj`/`.config`/source file was modified to achieve this. + +This defect and its non-project-file-modifying workaround are recorded here for audit transparency; +they are pre-existing/out-of-scope and are not fixed at the source (csproj) level by this plan. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/candidate-defect-surface.2026-07-20T13-10.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/candidate-defect-surface.2026-07-20T13-10.md new file mode 100644 index 00000000..d9fca1a3 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/candidate-defect-surface.2026-07-20T13-10.md @@ -0,0 +1,27 @@ +Timestamp: 2026-07-20T13-10 + +## Candidate defect-surface baseline notes (capture only; diagnosis conclusion is P1-T1's job) + +Verbatim citations from the plan's Confirmed Facts section: + +- `QuickFiler/Controllers/QfcItemController.FolderHandling.cs:161-206` — `AssignFolderComboBox()`: + when `_folderHandler.FolderArray.Length > 0` (line 170) and no predetermined-folder match is + present/selected, the `else` branch (lines 200-203) unconditionally calls + `_itemViewer.SetFolderSelectedIndex(1)` (line 202) regardless of `FolderArray.Length`. + +- `QuickFiler/Viewers/BreadcrumbBridgeCoordinator.cs:127` — `SelectRow(int index)` forwards the + index unchanged into `BreadcrumbStateModel.SelectRow`. + +- `UtilitiesCS/OutlookObjects/Folder/BreadcrumbStateModel.cs:233-246` — `SelectRow(int index)` + validates `index` is `-1` or in `[0, RowCount-1]` and throws `ArgumentOutOfRangeException` for a + single-row model (`RowCount == 1`) when `index == 1`. This validation is correct defensive + behavior and must NOT be changed by this plan. + +## Source verification performed in this session + +- `QfcItemController.FolderHandling.cs` line 202: confirmed literal `_itemViewer.SetFolderSelectedIndex(1);` inside the `else` branch (lines 200-203) of `AssignFolderComboBox()`. +- `QfcItemController.FolderHandling.cs` line 228: confirmed literal `comboBox.SelectedIndex = predeterminedIndex >= 0 ? predeterminedIndex : 1;` inside the static `PopulateAndSelectFolder(...)` helper (lines 217-230). +- `BreadcrumbBridgeCoordinator.cs` line 127 (file lines 118-129 region, `SelectRow` method at line 125): confirmed `SelectRow(int index)` forwards `index` unchanged into `_router.SelectRow(index)` (which routes to `BreadcrumbStateModel.SelectRow`). +- `BreadcrumbStateModel.cs` lines 233-246: confirmed `SelectRow(int index)` throws `ArgumentOutOfRangeException` when `index < -1 || index >= _rows.Count`, with message `"Row selection requires -1 or an index in [0, {_rows.Count - 1}]."` — this matches the issue.md stack trace exactly (`Row selection requires -1 or an index in [0, 0].`) and is confirmed correct defensive behavior, out of scope-lock for modification. + +No conclusion is drawn here beyond the plan's pre-recorded Confirmed Facts; the diagnosis conclusion is deferred to P1-T1. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/csharpier-baseline.2026-07-20T13-15.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/csharpier-baseline.2026-07-20T13-15.md new file mode 100644 index 00000000..fef65e1f --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/csharpier-baseline.2026-07-20T13-15.md @@ -0,0 +1,4 @@ +Timestamp: 2026-07-20T13-15 +Command: csharpier check . (v1.3.0 subcommand syntax; equivalent to plan's `dotnet tool run csharpier --check .`) +EXIT_CODE: 1 +Output Summary: 32 pre-existing formatting errors across 1406 checked files (mostly `packages.config` missing trailing newline and `app.config` attribute-wrapping differences in unrelated projects: ToDoModel.Test, UtilitiesCS, UtilitiesCS.Test, VBFunctions.Test, and others). This is pre-existing baseline noise, not introduced by this plan. Neither in-scope file (`QuickFiler/Controllers/QfcItemController.FolderHandling.cs` or `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs`) appears in the error list — both are already CSharpier-clean at baseline. "Checked 1406 files in 1851ms." diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/git-baseline-state.2026-07-20T13-08.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/git-baseline-state.2026-07-20T13-08.md new file mode 100644 index 00000000..6654aaa4 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/git-baseline-state.2026-07-20T13-08.md @@ -0,0 +1,4 @@ +Timestamp: 2026-07-20T13-08 +Command: git rev-parse --abbrev-ref HEAD && git rev-parse --short HEAD +EXIT_CODE: 0 +Output Summary: Branch is `bug/folder-combobox-fallback-index-out-of-range-392`; HEAD short SHA is `1e5ada71`. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/minor-audit-scope.2026-07-20T13-07.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/minor-audit-scope.2026-07-20T13-07.md new file mode 100644 index 00000000..8c1a11bc --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/minor-audit-scope.2026-07-20T13-07.md @@ -0,0 +1,15 @@ +Timestamp: 2026-07-20T13-07 + +## Verification of minor-audit requirements boundary for issue #392 + +Source file inspected: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md` + +- Confirmed `issue.md` line 12 contains: `- Work Mode: minor-audit`. +- Confirmed `issue.md` contains an explicit `## Acceptance Criteria` heading at line 74, listing exactly five checkbox items AC-1 through AC-5 (lines 76-80), all currently `- [ ]` (unchecked) at baseline. +- Only the `## Acceptance Criteria` section (lines 74-80) is treated as the authoritative AC source for this minor-audit plan, per `acceptance-criteria-tracking` and the plan's Requirements Boundary section. No other `issue.md` checkbox section (e.g. "Logs / Screenshots", "Proposed Fix / Validation Ideas", "Next Step") is treated as acceptance criteria. +- Confirmed `spec.md` and `user-story.md` are absent from the feature folder + `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/`. Directory listing at baseline contained only `issue.md` and `plan.2026-07-20T12-59.md` (evidence subfolders were created during Phase 0 execution). + +Command: `ls "docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/"` +EXIT_CODE: 0 +Output Summary: Directory contained `issue.md` and `plan.2026-07-20T12-59.md` only (no `spec.md`, no `user-story.md`). Fail-closed condition (unexpected spec.md/user-story.md presence) does NOT apply. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/nullable-baseline.2026-07-20T13-35.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/nullable-baseline.2026-07-20T13-35.md new file mode 100644 index 00000000..de817df9 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/nullable-baseline.2026-07-20T13-35.md @@ -0,0 +1,6 @@ +Timestamp: 2026-07-20T13-35 +Command: `MSBuild.exe TaskMaster.sln /t:Rebuild /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true /m` (VS18 Community full-framework MSBuild; `/t:Rebuild` used instead of `/t:Build` because the nullable gate requires a forced full recompilation to surface nullable-flow warnings on files that an incremental build would otherwise skip, per prior-session repo guidance) +EXIT_CODE: 1 +Output Summary: Build FAILED. 0 Warning(s), 34 Error(s). All 34 nullable errors (`CS8618`, `CS8600`, `CS8601`, `CS8602`, `CS8603`, `CS8625`) are attributed exclusively to `SVGControl.csproj` (a vendored third-party WinForms control library, not touched by this plan and outside the Scope-Lock). No error is attributed directly to `QuickFiler.csproj`, `QuickFiler.Test.csproj`, or `UtilitiesCS.csproj` source. `UtilitiesCS.csproj`, `QuickFiler.csproj`, `QuickFiler.Test.csproj`, and other first-party projects report `-- FAILED` only because they hold a project reference (directly or transitively) to the failing `SVGControl.csproj`, not because of their own nullable violations. This is a pre-existing, out-of-scope, vendored-project nullable-debt condition (previously observed across multiple prior sessions per repo agent memory; the specific error count is known to fluctuate between sessions and is re-verified here rather than assumed). Fixing `SVGControl.csproj`'s nullable debt is out of this plan's Scope-Lock and is not attempted. + +This baseline failure is a pre-existing repo condition, not a change introduced by this plan, and does not indicate a defect in the in-scope files (`QuickFiler/Controllers/QfcItemController.FolderHandling.cs`, `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs`). The plan's later final-QC nullable step (P2-T3) is expected to hit the same pre-existing SVGControl failure with the same error count (no regression), which will be verified explicitly at that step. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/phase0-instructions-read.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/phase0-instructions-read.md new file mode 100644 index 00000000..fbc33b51 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/phase0-instructions-read.md @@ -0,0 +1,87 @@ +Timestamp: 2026-07-20T13-05 + +Policy Order: CLAUDE.md (all sections) → General Code Change Policy → General Unit Test Policy → C# Code Change Policy → C# Unit Test Policy + +Files read (in order): +1. `CLAUDE.md` +2. `.claude/rules/general-code-change.md` +3. `.claude/rules/general-unit-test.md` +4. `.claude/rules/csharp.md` + +## CLAUDE.md — Policy Compliance Order (quoted verbatim) + +``` +## Policy Compliance Order + +The four core policies below are embedded directly in this file and apply to every session without requiring explicit skill loads. Apply them in this order: + +1. This file (CLAUDE.md) — all sections +2. General Code Change Policy (§ below) +3. General Unit Test Policy (§ below) +4. For C#: C# Code Change Policy (§ below) and C# Unit Test Policy (§ below) +``` + +## general-code-change.md — Mandatory Toolchain Loop (quoted verbatim) + +``` +## Mandatory Toolchain Loop + +Run the full seven-stage toolchain in this exact order and repeat until all stages pass in a single pass: + +1. **Formatting** (e.g., Black, Prettier, CSharpier, Invoke-Formatter) +2. **Linting** (e.g., Ruff, ESLint, PSScriptAnalyzer, .NET analyzers) +3. **Type checking** (e.g., Pyright, TSC, nullable analysis; skip for PowerShell) +4. **Architecture-boundary tests** (e.g., dependency-cruiser, NetArchTest.Rules) +5. **Unit tests** (e.g., Pytest, Vitest, MSTest, Pester) including property-based tests where applicable per `quality-tiers.md` +6. **Contract / schema compatibility checks** (e.g., oasdiff, schema-snapshot diff) +7. **Integration tests** + +**Restart from step 1** if any stage fails or auto-fixes any files. Do not stop the loop until all seven stages complete without errors in a single pass. + +Mutation testing and golden tests run in pre-merge or nightly pipelines, not the per-commit loop. +``` + +Note: for this C# minor-audit plan, the concrete toolchain command sequence actually executed is the CUT3/C#7 four-step sequence (format → lint → type-check → test) as specified in CLAUDE.md's "C# Toolchain" section and `.claude/rules/csharp.md`'s "Toolchain" section; the general seven-stage loop's architecture-boundary/contract/integration stages have no C#-specific command defined in this repo's C# policy sections and are not separately invoked by this plan. + +## general-unit-test.md — Coverage Requirements (quoted verbatim) + +``` +## Coverage Requirements + +- **Line coverage must remain >= 85% across all tiers (T1–T4).** +- **Branch coverage must remain >= 75% across all tiers (T1–T4).** +- Code changes or refactors must not reduce coverage for the lines that were changed. +- Tier-specific lower coverage thresholds are not used in this repository. See `.claude/rules/quality-tiers.md` for the full tier system. +- Coverage is a supporting metric, not the sole quality gate. Untested critical behavior is not acceptable even if the overall percentage looks good. +- Configure coverage tooling to exclude test files (e.g., `tests/`) so metrics reflect application code, not tests. +- Type-only / interface-only modules with no executable behavior may be omitted from coverage measurement. Examples: Python `Protocol`-only modules consumed only under `TYPE_CHECKING`, TypeScript interface/type-only files, and C# interface-only files. Such modules legitimately report 0% executable coverage and may be excluded from measurement. This is a clarification only; it does not lower any coverage threshold. +``` + +Note: per the plan's "Coverage Floor Used By This Plan" section, this plan uses issue.md AC-5's explicit >= 90% new/changed-code coverage figure (consistent with CLAUDE.md's C# Unit Test Policy repo-wide 80% / new-code 90% figures) for the new/changed-code coverage gate in this minor-audit scope; the wider repo-floor conflict noted in the plan is out of scope here. + +## csharp.md — Toolchain and Testing Standards (quoted verbatim) + +``` +## Toolchain + +1. **Formatting — CSharpier**: All C# source files must be formatted with CSharpier. Do not use `dotnet format`. Command: `dotnet tool run csharpier .` or `csharpier .` +2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Command: `msbuild .sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` +3. **Type Checking — Nullable Analysis**: Enable nullable reference types and fail on warnings. Command: `msbuild .sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true` +4. **Testing — MSTest + Moq + FluentAssertions**: Run tests with: `vstest.console.exe /EnableCodeCoverage` + +Run the toolchain in order: format → lint → type-check → test. Restart from step 1 if any step fails or changes files. +``` + +``` +## Testing Standards + +- Use **MSTest** (`Microsoft.VisualStudio.TestTools.UnitTesting`) as the test framework. +- Use **Moq** for mocking. +- Prefer **FluentAssertions** for assertions; use MSTest `Assert` only when FluentAssertions is not practical. +- Use `[TestClass]` and `[TestMethod]` attributes. +- Follow Arrange–Act–Assert structure. +- No external dependencies in unit tests. +- Repository-wide line coverage must remain >= 80%. +- Any new module, class, or method must reach >= 90% coverage. +- Coverage regression on changed lines is a blocking finding. +``` diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/vstest-coverage-baseline.2026-07-20T13-45.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/vstest-coverage-baseline.2026-07-20T13-45.md new file mode 100644 index 00000000..c0ebf280 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/vstest-coverage-baseline.2026-07-20T13-45.md @@ -0,0 +1,42 @@ +Timestamp: 2026-07-20T13-45 +Command: `dotnet-coverage collect -f cobertura -s coverage-exclude-deedle.xml -o baseline-coverage.cobertura.xml -- vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation` +(equivalent, coverage-format-explicit version of the plan's stated +`vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /EnableCodeCoverage`; `dotnet-coverage` +wrapping was used to obtain a numeric, parseable Cobertura line/branch-rate report instead of the +binary `.coverage` format that `/EnableCodeCoverage` alone produces, per established repo tooling +pattern. `/InIsolation` is required for this Moq-based test assembly per prior-session repo guidance.) +EXIT_CODE: 0 +Output Summary: +- Total tests: 539. Passed: 539. Failed: 0. Total time: 6.5993 seconds. +- A first attempt without the module-exclude settings file produced 3 spurious failures + (`InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing`, + `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker`, + `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop`) with + `System.TypeInitializationException` -> `Deedle.Reflection` -> `System.Security.VerificationException: + Operation could destabilize the runtime` — a known, pre-existing coverage-instrumentation artifact of + byte-rewriting the Deedle/F# assemblies (documented in repo agent memory across prior sessions), not a + real regression and unrelated to the in-scope files. Passing a `-s` settings XML that excludes + `.*Deedle.*` and `.*FSharp\.Core.*` from instrumentation (module-path exclude only, no test/production + source change) eliminated the spurious failures; the reported `539 passed, 0 failed` result above is + from that corrected run. +- Repository-wide totals (all instrumented first- and third-party assemblies, not the coverage-gate + denominator): line-rate 0.2443166285770522 (24.43%), branch-rate 0.12617514101692204 (12.62%). This + raw aggregate is expected to be low because it includes large third-party/vendored packages + (log4net, System.Linq.Async, System.Interactive, FluentAssertions, Mono.Reflection, + Microsoft.IO.RecyclableMemoryStream) with 0% or near-0% coverage; it is recorded here for reference + only, not as the plan's coverage gate figure. +- `QuickFiler` package (production assembly containing the in-scope files): line-rate + 0.7366649404453651 (73.67%), branch-rate 0.645326504481434 (64.53%). +- `QuickFiler.Test` package: line-rate 0.955163387655155 (95.52%), branch-rate 0.9252199413489736 + (92.52%). +- Class-level baseline for `QuickFiler.Controllers.QfcItemController` sourced from + `QfcItemController.FolderHandling.cs` specifically: line-rate 0.9154929577464789 (91.55%), + branch-rate 0.7105263157894737 (71.05%). +- Method-level baseline for the two in-scope methods (from the same class/file): + - `AssignFolderComboBox()`: line-rate 0.8846153846153846 (88.46%), branch-rate 0.8571428571428571 + (85.71%). + - `PopulateAndSelectFolder(System.Windows.Forms.ComboBox, string[], string)`: line-rate 1 (100%), + branch-rate 1 (100%). + +These class/method-level figures are the baseline reference used by P2-T5's changed-line coverage +delta comparison. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392.2026-07-20T14-55.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392.2026-07-20T14-55.md new file mode 100644 index 00000000..3fa45470 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392.2026-07-20T14-55.md @@ -0,0 +1,41 @@ +Timestamp: 2026-07-20T14-55 + +## AC-5 check-off decision for issue #392 + +AC-5 text: "The full C# toolchain passes in order (CSharpier format, .NET analyzers build, nullable +build, MSTest via vstest.console.exe) with zero regressions, and new/changed code meets the >= 90% +coverage target." + +Component-by-component status: +- CSharpier format: PASS for the two Scope-Lock-authorized files (see + `evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md`). Repo-wide pre-existing formatting + noise (32 files) is unchanged from baseline and out of Scope-Lock. +- .NET analyzers build: PASS, EXIT_CODE 0 (see `evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md`). +- Nullable build: **FAIL, EXIT_CODE 1** (see `evidence/qa-gates/nullable-final-392.2026-07-20T14-28.md`). + All 34 errors are confined to `SVGControl.csproj` (a vendored third-party control library), a + byte-for-byte reproduction of the pre-existing baseline failure documented in + `evidence/baseline/nullable-baseline.2026-07-20T13-35.md`. No error is attributable to either + Scope-Lock-authorized file. Fixing this would require modifying `SVGControl.csproj`, which this + plan's Scope-Lock explicitly forbids ("No other file may be changed by this plan"). +- MSTest via vstest.console.exe: PASS, 541/541 tests passed, 0 failed (see + `evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md`). +- Zero regressions: PASS (see `evidence/qa-gates/regression-check-392.2026-07-20T14-42.md`). +- New/changed code >= 90% coverage: PASS, 100% observed (see + `evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md`). + +**Decision: AC-5 is left unchecked (`- [ ]`)** in `issue.md`, per `acceptance-criteria-tracking`'s +rule "Leave unmet items unchecked: If an AC item cannot be fully delivered or verified, leave it as +`- [ ]` and document the gap." Five of the six named toolchain/coverage components pass; the +nullable-build component fails for a pre-existing, out-of-scope, vendored-project reason unrelated +to this bugfix and not resolvable within this plan's Scope-Lock. This is recorded here as an +explicit, auditable gap rather than an overstated pass. + +## Final AC status for issue #392 + +| AC | Status | Backing evidence | +|---|---|---| +| AC-1 | `[x]` Checked | `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`, `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` | +| AC-2 | `[x]` Checked | `evidence/other/root-cause-392.2026-07-20T13-50.md`, same fail-before/pass-after evidence | +| AC-3 | `[x]` Checked | `evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md` | +| AC-4 | `[x]` Checked | `evidence/other/root-cause-392.2026-07-20T13-50.md`, same fail-before/pass-after evidence | +| AC-5 | `[ ]` NOT checked — partial | Five of six components pass (see table above); nullable-build component fails on pre-existing, out-of-scope `SVGControl.csproj` vendored debt, confirmed no-regression vs. baseline. | diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392.2026-07-20T15-15.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392.2026-07-20T15-15.md new file mode 100644 index 00000000..a70b98a4 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392.2026-07-20T15-15.md @@ -0,0 +1,37 @@ +Timestamp: 2026-07-20T15-15 + +## AC-5 check-off (revised) for issue #392 + +The plan of record was revised (P2-T3 amended) and `issue.md`'s AC-5 text was amended by the +orchestrator on 2026-07-20 with an explicit scope note: nullable enforcement is scoped to +first-party projects per `.claude/rules/csharp.md`; pre-existing, byte-identical-to-baseline +vendored `SVGControl.csproj` nullable errors are explicitly non-blocking and tracked separately in +`docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`. + +Component-by-component status under the amended wording: +- CSharpier format: PASS for the two Scope-Lock-authorized files (`evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md`). +- .NET analyzers build: PASS, EXIT_CODE 0 (`evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md`). +- Nullable build (first-party scope): PASS under the amended AC-5 wording. Full-recompile run + (`evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md`) shows EXIT_CODE 1 overall, but the + error-set comparison against the P0-T11 baseline shows **zero NEW errors** and **zero errors + attributable to first-party in-scope files** — all 34 errors are byte-identical-to-baseline, + confined to vendored `SVGControl.csproj`, and are explicitly excluded from this AC's scope per the + amended wording. +- MSTest via vstest.console.exe: PASS, 541/541 tests passed, 0 failed (`evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md`). +- Zero regressions: PASS (`evidence/qa-gates/regression-check-392.2026-07-20T14-42.md`). +- New/changed code >= 90% coverage: PASS, 100% observed (`evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md`). + +**Decision: AC-5 is checked off (`- [x]`)** in `issue.md`. All six components pass under the amended, +first-party-scoped wording. + +## Final AC status for issue #392 + +| AC | Status | Backing evidence | +|---|---|---| +| AC-1 | `[x]` Checked | `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`, `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` | +| AC-2 | `[x]` Checked | `evidence/other/root-cause-392.2026-07-20T13-50.md`, same fail-before/pass-after evidence | +| AC-3 | `[x]` Checked | `evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md` | +| AC-4 | `[x]` Checked | `evidence/other/root-cause-392.2026-07-20T13-50.md`, same fail-before/pass-after evidence | +| AC-5 | `[x]` Checked | `evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md`, `evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md`, `evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md` (error-set comparison), `evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md`, `evidence/qa-gates/regression-check-392.2026-07-20T14-42.md`, `evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md`, `evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md` | + +All 5 acceptance criteria are now checked off. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-phase1-392.2026-07-20T14-15.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-phase1-392.2026-07-20T14-15.md new file mode 100644 index 00000000..803410b0 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-phase1-392.2026-07-20T14-15.md @@ -0,0 +1,24 @@ +Timestamp: 2026-07-20T14-15 + +## AC check-off (Phase 1) for issue #392 + +In `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md`, +`## Acceptance Criteria` section, changed from `- [ ]` to `- [x]`: + +- AC-1: backed by + `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md` (fail-before) and + `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` (pass-after). No temporary files + or external dependencies used (real `System.Windows.Forms.ComboBox` and `Mock` only). +- AC-2: backed by `evidence/other/root-cause-392.2026-07-20T13-50.md` (diagnosis), + `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`, + `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md`. Production fix at + `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` line ~200-204 (P1-T5). +- AC-3: backed by + `evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md` (6/6 pre-existing + tests still pass unchanged). +- AC-4: backed by `evidence/other/root-cause-392.2026-07-20T13-50.md`, + `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`, + `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md`. Production fix at + `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` line ~227-230 (P1-T6). + +AC-5 remains unchecked (`- [ ]`) pending Phase 2 final QC. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392.2026-07-20T14-55.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392.2026-07-20T14-55.md new file mode 100644 index 00000000..594d494d --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392.2026-07-20T14-55.md @@ -0,0 +1,21 @@ +Timestamp: 2026-07-20T14-55 + +## AC Closure Summary — Issue #392 + +| AC | Text | Status | Backing evidence path(s) | +|---|---|---|---| +| AC-1 | Deterministic MSTest regression test reproduces the defect and fails before the fix; passes after; no temp files/external dependencies | Checked | `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`; `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` | +| AC-2 | `AssignFolderComboBox` no longer throws for single-entry `FolderArray` with no predetermined match; selects index 0 | Checked | `evidence/other/root-cause-392.2026-07-20T13-50.md`; `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`; `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` | +| AC-3 | Existing multi-suggestion index-1 behavior and predetermined-preselect behavior preserved | Checked | `evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md` | +| AC-4 | `PopulateAndSelectFolder` applies the same bounds-safe fallback | Checked | `evidence/other/root-cause-392.2026-07-20T13-50.md`; `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`; `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` | +| AC-5 | Full C# toolchain passes in order; zero regressions; new/changed code >= 90% coverage | **NOT fully checked (partial)** | `evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md`; `evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md`; `evidence/qa-gates/nullable-final-392.2026-07-20T14-28.md` (FAIL, pre-existing out-of-scope `SVGControl` vendored nullable debt, no regression); `evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md`; `evidence/qa-gates/regression-check-392.2026-07-20T14-42.md`; `evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md`; `evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md`; `evidence/issue-updates/ac-status-final-392.2026-07-20T14-55.md` (decision rationale) | + +4 of 5 acceptance criteria are fully checked off. AC-5 is left unchecked because its nullable-build +component fails due to a pre-existing, out-of-scope, vendored-project (`SVGControl.csproj`) +nullable-reference-type debt condition, confirmed byte-for-byte identical to the P0-T11 baseline (no +regression introduced by this plan). This plan's Scope-Lock explicitly forbids modifying +`SVGControl.csproj` or any file other than `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` +and `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs`, so this gap cannot be +resolved within this plan's authorized scope. All other AC-5 components (CSharpier format on +in-scope files, .NET analyzers build, MSTest execution, zero test regressions, and >= 90% +new/changed-code coverage) pass. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392.2026-07-20T15-15.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392.2026-07-20T15-15.md new file mode 100644 index 00000000..5bafcf08 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392.2026-07-20T15-15.md @@ -0,0 +1,17 @@ +Timestamp: 2026-07-20T15-15 + +## AC Closure Summary — Issue #392 (revised, all AC checked) + +| AC | Text | Status | Backing evidence path(s) | +|---|---|---|---| +| AC-1 | Deterministic MSTest regression test reproduces the defect and fails before the fix; passes after; no temp files/external dependencies | Checked | `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`; `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` | +| AC-2 | `AssignFolderComboBox` no longer throws for single-entry `FolderArray` with no predetermined match; selects index 0 | Checked | `evidence/other/root-cause-392.2026-07-20T13-50.md`; `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`; `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` | +| AC-3 | Existing multi-suggestion index-1 behavior and predetermined-preselect behavior preserved | Checked | `evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md` | +| AC-4 | `PopulateAndSelectFolder` applies the same bounds-safe fallback | Checked | `evidence/other/root-cause-392.2026-07-20T13-50.md`; `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md`; `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` | +| AC-5 (amended 2026-07-20 by orchestrator, first-party-scoped nullable wording) | Full C# toolchain passes in order; zero regressions relative to Phase 0 baseline; new/changed code >= 90% coverage; vendored `SVGControl.csproj` nullable errors byte-identical to baseline are non-blocking (tracked in `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`) | Checked | `evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md`; `evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md`; `evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md` (error-set comparison: 0 new, 0 first-party); `evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md`; `evidence/qa-gates/regression-check-392.2026-07-20T14-42.md`; `evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md`; `evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md`; `evidence/issue-updates/ac-status-final-392.2026-07-20T15-15.md` | + +All 5 acceptance criteria are fully checked off. AC-5's nullable-build component is satisfied under +the amended, first-party-scoped wording: the P2-T3 error-set comparison against the P0-T11 baseline +confirms zero new errors and zero errors attributable to any first-party project; all 34 errors are +byte-identical-to-baseline and confined to the vendored `SVGControl.csproj`, which is out of this +plan's Scope-Lock and out of the amended AC-5's enforcement scope. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/root-cause-392.2026-07-20T13-50.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/root-cause-392.2026-07-20T13-50.md new file mode 100644 index 00000000..44976ed0 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/root-cause-392.2026-07-20T13-50.md @@ -0,0 +1,35 @@ +Timestamp: 2026-07-20T13-50 + +## Root Cause Diagnosis for Issue #392 + +(a) `QfcItemController.FolderHandling.cs:202` — `AssignFolderComboBox()`'s `else` branch (lines +200-203, guarded by `_folderHandler?.FolderArray?.Length > 0` at line 170) unconditionally calls +`_itemViewer.SetFolderSelectedIndex(1);` whenever no predetermined-folder match is present or +selected, regardless of how many suggestions `_folderHandler.FolderArray` actually contains. When +`FolderArray.Length == 1`, index 1 is out of range for the single-row breadcrumb model. + +(b) `QfcItemController.FolderHandling.cs:228` — the static `PopulateAndSelectFolder(...)` helper +contains the identical unguarded fallback: +`comboBox.SelectedIndex = predeterminedIndex >= 0 ? predeterminedIndex : 1;`. A WinForms +`ComboBox` populated with exactly one item also rejects `SelectedIndex = 1` (this is confirmed by +the existing test `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection`, which demonstrates +the same class of out-of-range throw for the zero-item case). + +(c) `BreadcrumbStateModel.SelectRow(int index)` (`UtilitiesCS/OutlookObjects/Folder/BreadcrumbStateModel.cs:233-246`) +validates that `index` is `-1` or within `[0, _rows.Count - 1]` and throws +`ArgumentOutOfRangeException` otherwise. Verified in this session: the exact exception message +format (`"Row selection requires -1 or an index in [0, {_rows.Count - 1}]."`) matches the +issue.md-reported stack trace verbatim for a single-row model (`RowCount == 1`, thrown message +`"Row selection requires -1 or an index in [0, 0]."`, actual value 1). This is correct defensive +validation protecting the breadcrumb pipeline's invariant and is explicitly NOT modified by this +plan. + +(d) **Primary root-cause statement:** Both call sites in `QfcItemController.FolderHandling.cs` +((a) `AssignFolderComboBox()` line 202, and (b) the static `PopulateAndSelectFolder` helper line +228) select a hardcoded fallback index of `1` whenever no predetermined folder is preselected, +without checking how many suggestions are actually present. The fix is to clamp the fallback +selection index to `0` when exactly one suggestion exists, and retain `1` only when two or more +suggestions exist. `BreadcrumbStateModel.SelectRow`'s bounds validation is correct and is not the +defect; the defect is entirely in the two caller sites identified above. This satisfies the +diagnosis prerequisite for AC-1 (regression test authored against this diagnosed defect), AC-2 +(`AssignFolderComboBox` fix), and AC-4 (`PopulateAndSelectFolder` fix). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md new file mode 100644 index 00000000..0cc09579 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md @@ -0,0 +1,9 @@ +Timestamp: 2026-07-20T14-24 +Command: `MSBuild.exe TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true /m` +EXIT_CODE: 0 +Output Summary: Build succeeded. 46 Warning(s), 0 Error(s). Time Elapsed 00:00:04.68. No error +regression relative to the P0-T10 baseline (80 Warning(s), 0 Error(s) — the lower warning count here +reflects incremental-build scope after the earlier full builds, not a functional change). Zero +errors both at baseline and final, consistent with the analyzer gate passing throughout. No new +analyzer diagnostics attributable to the two Scope-Lock-authorized files were observed in the build +output. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md new file mode 100644 index 00000000..465057b3 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md @@ -0,0 +1,38 @@ +Timestamp: 2026-07-20T14-50 +Command: Python conversion script (inline, scratchpad-only, not committed to the repo): parse +`final-coverage.cobertura.xml` (P2-T4's `dotnet-coverage collect -f cobertura` output), dedup lines +per `(sourcefile, line-number)` within each class, scope to first-party assemblies only +`{QuickFiler, UtilitiesCS, TaskMaster, TaskVisualization, ToDoModel, Tags, SVGControl}` (test +assemblies `QuickFiler.Test`/`UtilitiesCS.Test`/etc. and third-party/vendored libraries +`FluentAssertions`, `log4net`, `Mono.Reflection`, `Microsoft.IO.RecyclableMemoryStream`, +`System.Linq.Async`, `System.Interactive` excluded), emit JaCoCo-format `` +`counter` elements (`type="LINE"` and `type="BRANCH"`, aggregated bottom-up at class/package/report +level) to `artifacts/csharp/coverage.xml`, mirroring the established conversion pattern recorded in +`docs/features/active/2026-07-16-quickfiler-breadcrumb-webview2-351/evidence/qa-gates/coverage-conversion.2026-07-18T10-55.md`. +EXIT_CODE: 0 +Output Summary: +- `artifacts/csharp/coverage.xml` written in JaCoCo format (report/package/class `counter` + elements), regenerated from the P2-T4 final Cobertura output. +- Report-level LINE counter totals: covered=27,063, missed=139,470 (total 166,533) => 16.25% + first-party line coverage under the single-suite (`QuickFiler.Test` only) measurement scope. +- Report-level BRANCH counter totals: covered=5,604, missed=35,604 (total 41,208) => 13.60% branch + coverage. Unlike the referenced prior-feature conversion (which reported 0/0 BRANCH counters + because that dotnet-coverage run carried no `condition-coverage` attributes), this run's Cobertura + XML does carry `branch="True"`/`condition-coverage="X% (covered/total)"` attributes on branch + lines, so BRANCH counters here are populated from real condition-coverage data, not left at 0/0. +- Verification: the hook `.claude/hooks/validate-feature-review-coverage.ps1`'s own + `Get-JacocoRepoCoverage`/`Get-JacocoBranchCoverage` XPath logic + (`//counter[@type="LINE"]` / `//counter[@type="BRANCH"]`, summed across all matching nodes) was + independently re-run against the generated `artifacts/csharp/coverage.xml` and reproduced the + identical 16.25% / 13.6% figures, confirming the multi-level (class + package + report) counter + nesting sums to a ratio-preserving result (a constant multiplicative factor cancels in the + covered/total ratio) and that the hook will read this file correctly. +- Scope note (matches the referenced prior-feature pattern): the 16.25% figure under-represents the + repository floor because first-party assemblies not exercised by the single `QuickFiler.Test` suite + run here (`TaskVisualization`, `ToDoModel`, `Tags` — each 0% under this scope, each covered by its + own suite in PR CI) and the large `UtilitiesCS` assembly (7.48% under this scope, exercised + primarily by `UtilitiesCS.Test`, not run here) drag the aggregate down. Per-package figures for the + directly exercised assembly are `QuickFiler` 73.68% line / 64.62% branch (matching P2-T4's + per-package Cobertura figures exactly). Repo-wide aggregation across all first-party test suites is + deferred to PR CI per established repo practice (policy-audit §5.4); no assembly was cherry-picked + out of the first-party scope to inflate this figure. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md new file mode 100644 index 00000000..3912cb96 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md @@ -0,0 +1,55 @@ +Timestamp: 2026-07-20T14-38 + +## Coverage delta: baseline (P0-T12) vs. post-change (P2-T4), scoped to `QfcItemController.FolderHandling.cs` + +| Scope | Baseline line-rate | Post-change line-rate | Baseline branch-rate | Post-change branch-rate | +|---|---|---|---|---| +| `QuickFiler` package (whole assembly) | 73.67% | 73.68% | 64.53% | 64.62% | +| `QfcItemController.FolderHandling.cs` (class, file-scoped) | 91.55% | 91.89% | 71.05% | 73.81% | +| `AssignFolderComboBox()` method | 88.46% | 89.29% | 85.71% | 87.5% | +| `PopulateAndSelectFolder(...)` method | 100% | 100% | 100% | 100% | + +**No regression on any of the above rows** — every post-change figure is greater than or equal to +its corresponding baseline figure. + +## New/changed-code coverage (the two edited statements, per AC-5's >= 90% target) + +Changed lines (per `git diff`, P1-T5 and P1-T6): +- `QfcItemController.FolderHandling.cs:201-205` — the `else` branch of `AssignFolderComboBox()`, + containing the new `_folderHandler.FolderArray.Length == 1 ? 0 : 1` conditional + (source spans 202-204; the compiler emits reported Cobertura sequence points on lines 201, 203, + 204, 205 for this block — line 202 alone, an incomplete partial expression line, has no + independent sequence point). +- `QfcItemController.FolderHandling.cs:230-231` — the new + `predeterminedIndex >= 0 ? predeterminedIndex : (folderArray.Length == 1 ? 0 : 1)` conditional + assignment (the compiler emits the sequence point on line 231; line 230 alone has no independent + sequence point). + +Post-change Cobertura per-line data for these reported sequence points (from +`final-coverage.cobertura.xml`): + +``` + + + + + +``` + +All 5 reported lines show `hits="1"` — **100% line coverage on the new/changed code**, exceeding the +>= 90% target from AC-5. + +Each ternary's two logical paths (`FolderArray.Length == 1` vs. `> 1`) are independently exercised by +tests (this Cobertura conversion does not expose branch-level granularity on these specific lines, +so line-hit evidence is supplemented with the specific tests that drive each path): +- `Length == 1` path: `PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing` + and `AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero` (both new, + P1-T2/P1-T3). +- `Length > 1` (else) path: `PopulateAndSelectFolder_AllMissingPredetermined_SelectsIndexOne` and + `AssignFolderComboBox_WhenNoPredeterminedFolder_SelectsTopSuggestionViaViewer` (both pre-existing, + unchanged). + +## Explicit PASS/FAIL Statement + +- No regression on changed lines: **PASS**. +- >= 90% coverage on new/changed code: **PASS** (100% observed). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md new file mode 100644 index 00000000..5fabb14a --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md @@ -0,0 +1,28 @@ +Timestamp: 2026-07-20T14-20 +Command: `csharpier format .` (equivalent to plan's `dotnet tool run csharpier .`), followed by `csharpier check .` (non-mutating re-verification) +EXIT_CODE: 0 (format run); 1 (subsequent check run — see explanation below; this is the pre-existing baseline formatting-noise exit code, not a failure introduced by this plan) +Output Summary: +- `csharpier format .` reported "Formatted 1406 files in 2969ms" and exited 0. `git status` after this + run showed it had rewritten 31 pre-existing, out-of-scope `app.config`/`packages.config` files + across unrelated projects (SVGControl, Tags, Tags.Test, TaskMaster, TaskMaster.Test, TaskTree, + TaskTree.Test, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS, + UtilitiesCS.Test, VBFunctions.Test, plus QuickFiler/QuickFiler.Test's own `app.config`/ + `packages.config`) — exactly the same 32 pre-existing formatting errors already documented at + baseline in `evidence/baseline/csharpier-baseline.2026-07-20T13-15.md`. This conflicts with this + plan's own Scope-Lock ("No other file may be changed by this plan"; only + `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` and + `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` are authorized for + modification). Per this plan's Scope-Lock (the more specific, overriding constraint for this + minor-audit bugfix), the out-of-scope config-file changes were reverted via + `git checkout -- ` for each of the 31 affected non-authorized files, leaving only the two + Scope-Lock-authorized files (already csharpier-clean from earlier per-file formatting during + Phase 1) as the sole tracked modifications. +- A subsequent non-mutating `csharpier check .` (run after the revert) confirms: 32 pre-existing + errors remain (unchanged from the P0-T9 baseline count — no regression, no improvement, no new + unformatted files), and neither `QfcItemController.FolderHandling.cs` nor + `QfcItemController.FolderHandlingTests.cs` appears in the error list. Both in-scope files are + fully CSharpier-formatted. "Checked 1406 files in 2540ms." +- No further Phase 2 restart is triggered: this command's only *authorized* file-changing effect + (formatting the two in-scope files) had already converged to a stable, clean state before this + step ran (they were formatted incrementally during Phase 1 edits), so no in-scope file changed as + a result of this step. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392.2026-07-20T15-00.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392.2026-07-20T15-00.md new file mode 100644 index 00000000..051b17dc --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392.2026-07-20T15-00.md @@ -0,0 +1,72 @@ +Timestamp: 2026-07-20T15-00 + +## Minor-audit readiness evidence for issue #392 + +### Phase 0 artifacts (all present) +- `evidence/baseline/phase0-instructions-read.md` +- `evidence/baseline/minor-audit-scope.2026-07-20T13-07.md` +- `evidence/baseline/git-baseline-state.2026-07-20T13-08.md` +- `evidence/baseline/candidate-defect-surface.2026-07-20T13-10.md` +- `evidence/baseline/csharpier-baseline.2026-07-20T13-15.md` +- `evidence/baseline/analyzer-baseline.2026-07-20T13-25.md` +- `evidence/baseline/nullable-baseline.2026-07-20T13-35.md` +- `evidence/baseline/vstest-coverage-baseline.2026-07-20T13-45.md` + +### Phase 1 artifacts (all present) +- `evidence/other/root-cause-392.2026-07-20T13-50.md` (diagnosis) +- `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md` (regression tests authored and confirmed failing) +- `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` (fix confirmed) +- `evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md` (no regression) +- `evidence/issue-updates/ac-status-phase1-392.2026-07-20T14-15.md` (AC-1..AC-4 check-off) + +### Phase 2 artifacts (all present, including the P2-T7 JaCoCo conversion) +- `evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md` +- `evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md` +- `evidence/qa-gates/nullable-final-392.2026-07-20T14-28.md` +- `evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md` +- `evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md` +- `evidence/qa-gates/regression-check-392.2026-07-20T14-42.md` +- `evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md` +- `evidence/issue-updates/ac-status-final-392.2026-07-20T14-55.md` +- `evidence/other/ac-closure-summary-392.2026-07-20T14-55.md` + +### Command-bearing task execution status +Every command-bearing task in this plan was executed and recorded with a numeric `EXIT_CODE` (no +`SKIPPED` outcomes were used anywhere in this plan): +- P0-T9 csharpier baseline: EXIT_CODE 1 (pre-existing baseline formatting noise, documented) +- P0-T10 analyzer baseline: EXIT_CODE 0 +- P0-T11 nullable baseline: EXIT_CODE 1 (pre-existing SVGControl vendored debt, documented) +- P0-T12 coverage baseline: EXIT_CODE 0 +- P1-T4 fail-before: EXIT_CODE 1 (expected per `[expect-fail]`) +- P1-T7 pass-after: EXIT_CODE 0 +- P1-T8 targeted no-regression: EXIT_CODE 0 +- P2-T1 csharpier final: EXIT_CODE 0 (format run) / EXIT_CODE 1 (subsequent check run, same + pre-existing baseline noise, unchanged count) +- P2-T2 analyzer final: EXIT_CODE 0 +- P2-T3 nullable final: EXIT_CODE 1 (byte-for-byte identical to baseline, pre-existing SVGControl + vendored debt, no regression, documented as an unresolved gap — see below) +- P2-T4 coverage final: EXIT_CODE 0 +- P2-T7 JaCoCo conversion: EXIT_CODE 0 + +### AC-1 through AC-5 check-off status in `issue.md` +- AC-1: `[x]` Checked. +- AC-2: `[x]` Checked. +- AC-3: `[x]` Checked. +- AC-4: `[x]` Checked. +- AC-5: `[ ]` **NOT checked.** Five of the six named toolchain/coverage components pass (CSharpier + format on in-scope files, .NET analyzers build, MSTest execution with zero regressions, and >= 90% + new/changed-code coverage — 100% observed). The nullable-build component fails with EXIT_CODE 1, + confined entirely to pre-existing, out-of-scope, vendored `SVGControl.csproj` nullable-reference-type + debt (34 errors, byte-for-byte identical to the P0-T11 baseline — confirmed no regression). This + plan's Scope-Lock forbids modifying `SVGControl.csproj`, so this gap cannot be resolved within this + plan's authorized scope. See `evidence/issue-updates/ac-status-final-392.2026-07-20T14-55.md` and + `evidence/other/ac-closure-summary-392.2026-07-20T14-55.md` for full rationale. + +### Overall readiness verdict +**Partial / remediation-relevant escalation required for AC-5's nullable-build component only.** The +defect described in issue #392 is fixed, verified by regression tests (fail-before/pass-after), +verified not to regress any of the 539 pre-existing tests, and verified to have >= 90% (100%) +coverage on the new/changed code. The sole outstanding gap is a pre-existing, out-of-scope, +vendored-project nullable-debt condition unrelated to this bugfix, which this plan's own Scope-Lock +prevents fixing. This is escalated for the orchestrator/maintainer's awareness rather than resolved +by an out-of-scope change or a silently-relaxed acceptance criterion. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392.2026-07-20T15-20.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392.2026-07-20T15-20.md new file mode 100644 index 00000000..8c09072f --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392.2026-07-20T15-20.md @@ -0,0 +1,72 @@ +Timestamp: 2026-07-20T15-20 + +## Minor-audit readiness evidence for issue #392 (final, post-plan-revision) + +### Phase 0 artifacts (all present) +- `evidence/baseline/phase0-instructions-read.md` +- `evidence/baseline/minor-audit-scope.2026-07-20T13-07.md` +- `evidence/baseline/git-baseline-state.2026-07-20T13-08.md` +- `evidence/baseline/candidate-defect-surface.2026-07-20T13-10.md` +- `evidence/baseline/csharpier-baseline.2026-07-20T13-15.md` +- `evidence/baseline/analyzer-baseline.2026-07-20T13-25.md` +- `evidence/baseline/nullable-baseline.2026-07-20T13-35.md` +- `evidence/baseline/vstest-coverage-baseline.2026-07-20T13-45.md` + +### Phase 1 artifacts (all present) +- `evidence/other/root-cause-392.2026-07-20T13-50.md` (diagnosis) +- `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md` (regression tests authored and confirmed failing) +- `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` (fix confirmed) +- `evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md` (no regression) +- `evidence/issue-updates/ac-status-phase1-392.2026-07-20T14-15.md` (AC-1..AC-4 check-off) + +### Phase 2 artifacts (all present, including the P2-T7 JaCoCo conversion and the revised P2-T3 error-set comparison) +- `evidence/qa-gates/csharpier-final-392.2026-07-20T14-20.md` +- `evidence/qa-gates/analyzer-final-392.2026-07-20T14-24.md` +- `evidence/qa-gates/nullable-final-392.2026-07-20T14-28.md` (original attempt, superseded) +- `evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md` (revised: full-recompile error-set comparison against P0-T11 baseline — 0 new errors, 0 first-party errors) +- `evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md` +- `evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md` +- `evidence/qa-gates/regression-check-392.2026-07-20T14-42.md` +- `evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md` +- `evidence/issue-updates/ac-status-final-392.2026-07-20T14-55.md` (original, superseded) +- `evidence/issue-updates/ac-status-final-392.2026-07-20T15-15.md` (revised, current) +- `evidence/other/ac-closure-summary-392.2026-07-20T14-55.md` (original, superseded) +- `evidence/other/ac-closure-summary-392.2026-07-20T15-15.md` (revised, current) + +### Command-bearing task execution status +Every command-bearing task in this plan was executed and recorded with a numeric `EXIT_CODE` (no +`SKIPPED` outcomes were used anywhere in this plan): +- P0-T9 csharpier baseline: EXIT_CODE 1 (pre-existing baseline formatting noise, documented) +- P0-T10 analyzer baseline: EXIT_CODE 0 +- P0-T11 nullable baseline: EXIT_CODE 1 (34 errors, all confined to vendored `SVGControl.csproj`) +- P0-T12 coverage baseline: EXIT_CODE 0 +- P1-T4 fail-before: EXIT_CODE 1 (expected per `[expect-fail]`) +- P1-T7 pass-after: EXIT_CODE 0 +- P1-T8 targeted no-regression: EXIT_CODE 0 +- P2-T1 csharpier final: EXIT_CODE 0 (format run) / EXIT_CODE 1 (subsequent non-mutating check run, same pre-existing baseline noise, unchanged count) +- P2-T2 analyzer final: EXIT_CODE 0 +- P2-T3 nullable final (revised): EXIT_CODE 1 overall, but error-set comparison confirms 0 NEW errors + and 0 first-party-attributable errors relative to the P0-T11 baseline (34/34 identical, all + confined to vendored `SVGControl.csproj`) — satisfies the amended acceptance criterion +- P2-T4 coverage final: EXIT_CODE 0 +- P2-T7 JaCoCo conversion: EXIT_CODE 0 + +### AC-1 through AC-5 check-off status in `issue.md` +- AC-1: `[x]` Checked. +- AC-2: `[x]` Checked. +- AC-3: `[x]` Checked. +- AC-4: `[x]` Checked. +- AC-5: `[x]` Checked, under the amended (2026-07-20, orchestrator) first-party-scoped nullable + wording. All six named components pass: CSharpier format (in-scope files), .NET analyzers build + (EXIT_CODE 0), nullable build (0 new / 0 first-party errors vs. baseline), MSTest execution + (541/541 passed), zero test regressions, and >= 90% new/changed-code coverage (100% observed). + +### Overall readiness verdict +**PASS.** The defect described in issue #392 is fixed in both call sites +(`AssignFolderComboBox()` and the static `PopulateAndSelectFolder` helper), verified by two new +regression tests (fail-before/pass-after), verified not to regress any of the 539 pre-existing +tests (541/541 pass post-fix), and verified to have 100% coverage on the new/changed code (exceeding +the >= 90% target). All five acceptance criteria (AC-1 through AC-5) are checked off in `issue.md`. +The only residual toolchain artifact is 34 pre-existing, vendored `SVGControl.csproj` nullable +errors, confirmed byte-identical to the Phase 0 baseline and explicitly out of the amended AC-5's +first-party-scoped enforcement per `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392.2026-07-20T14-28.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392.2026-07-20T14-28.md new file mode 100644 index 00000000..932c5f97 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392.2026-07-20T14-28.md @@ -0,0 +1,25 @@ +Timestamp: 2026-07-20T14-28 +Command: `MSBuild.exe TaskMaster.sln /t:Rebuild /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true /m` +EXIT_CODE: 1 +Output Summary: Build FAILED. 0 Warning(s), 34 Error(s) — identical in count and attribution to the +P0-T11 baseline. All 34 errors are attributed exclusively to `SVGControl.csproj` (a vendored +third-party WinForms control library, not authorized for modification by this plan's Scope-Lock). +No error is attributed to `QuickFiler.csproj`, `QuickFiler.Test.csproj`, or any source line in +`QfcItemController.FolderHandling.cs` or `QfcItemController.FolderHandlingTests.cs`. This is a +byte-for-byte reproduction of the pre-existing, out-of-scope, vendored nullable-debt condition +documented in `evidence/baseline/nullable-baseline.2026-07-20T13-35.md`: **no regression** was +introduced by this plan's P1-T5/P1-T6 fix or its new tests. + +## Disposition (this task's literal EXIT_CODE: 0 acceptance criterion) + +This task's stated acceptance text requires `EXIT_CODE: 0`. That literal outcome was not achieved, +and per the plan's stated restart rule ("if this command fails, fix and restart Phase 2 from +P2-T1"), a fix would require modifying `SVGControl.csproj` or its source files to resolve pre-existing +nullable-reference-type violations in vendored code — which this plan's Scope-Lock explicitly +forbids ("No other file may be changed by this plan"). Restarting Phase 2 without such a fix would +reproduce this identical, unrelated failure indefinitely rather than converging. Per the atomic +executor's non-blocking-after-execution-begins mandate, this task is not looped further; the P2-T3 +checkbox is left unchecked in the plan (verification against the literal `EXIT_CODE: 0` criterion did +not pass) and this gap is escalated explicitly in the plan-completion report rather than being +silently marked as passing. All other Phase 2 steps proceed, since none of them depend on +`SVGControl.csproj` building successfully under the nullable-strict flag. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md new file mode 100644 index 00000000..4f903e0a --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md @@ -0,0 +1,39 @@ +Timestamp: 2026-07-20T15-10 +Command: `MSBuild.exe TaskMaster.sln /t:Rebuild /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true` (revised P2-T3 command, full-recompile-to-full-recompile comparison per the amended task text and `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`) +EXIT_CODE: 1 +Output Summary: Build FAILED. 0 Warning(s), 34 Error(s). + +## Error-set comparison against the P0-T11 baseline artifact + +Baseline source: `evidence/baseline/nullable-baseline.2026-07-20T13-35.md` (raw log: +`nullable-baseline.txt`, command `MSBuild.exe TaskMaster.sln /t:Rebuild ... /p:Nullable=enable +/p:TreatWarningsAsErrors=true`, EXIT_CODE 1, 34 Error(s)). + +Extracted the full set of `(,): error CS####: ` diagnostic lines from both +the baseline log and this final-run log (normalized, deduplicated, sorted) and diffed them: + +- Baseline error-set size: 34 unique diagnostics. +- Final error-set size: 34 unique diagnostics. +- **NEW errors (final minus baseline): 0** (`comm -13 baseline final` produced no output). +- **Resolved errors (baseline minus final): 0** (`comm -23 baseline final` produced no output). +- The two error sets are byte-for-byte identical. + +## First-party attribution check + +Searched the final run's error output for any `error CS` line NOT attributed to +`SVGControl.csproj`: **0 matches**. All 34 errors are attributed exclusively to +`SVGControl.csproj` (a vendored third-party WinForms control library). No error is attributable to +`QuickFiler.csproj`, `QuickFiler.Test.csproj`, `UtilitiesCS.csproj`, or any other first-party +project. + +## Disposition under the amended AC-5 scope note + +Per the amended P2-T3 acceptance and the AC-5 scope note in `issue.md` (amended 2026-07-20 by +orchestrator): nullable enforcement is scoped to first-party projects per `.claude/rules/csharp.md` +(analyzers/nullable are wired to first-party projects only; vendored projects are excluded). The +pre-existing 34 nullable errors in vendored `SVGControl.csproj` are confirmed byte-identical to the +P0-T11 baseline (see comparison above) and are tracked separately in +`docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`. Per this +amended, explicit scope: **zero NEW errors relative to baseline** and **zero errors attributable to +first-party in-scope files** — both conditions are met. This task's acceptance criterion is +satisfied. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/regression-check-392.2026-07-20T14-42.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/regression-check-392.2026-07-20T14-42.md new file mode 100644 index 00000000..d448aa9d --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/regression-check-392.2026-07-20T14-42.md @@ -0,0 +1,7 @@ +Timestamp: 2026-07-20T14-42 +Command: `comm -23 ` (set difference over the test-name lists extracted from the P0-T12 baseline run and the P2-T4 final run) +EXIT_CODE: 0 +Output Summary: Baseline (P0-T12): 539 tests, 539 passed, 0 failed. Final (P2-T4): 541 tests, 541 +passed, 0 failed (541 = 539 baseline + 2 new regression tests from P1-T2/P1-T3). The set-difference +command produced zero output, confirming every test name that passed at baseline is present in the +final-passed set. Total pass count increased from 539 to 541 (did not decrease). No test regressed. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md new file mode 100644 index 00000000..28dc0108 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md @@ -0,0 +1,30 @@ +Timestamp: 2026-07-20T14-32 +Command: `dotnet-coverage collect -f cobertura -s coverage-exclude-deedle.xml -o final-coverage.cobertura.xml -- vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation` +(equivalent, coverage-format-explicit version of the plan's stated +`vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /EnableCodeCoverage`, matching the +same tooling pattern and Deedle/FSharp module-exclude settings used for the P0-T12 baseline, for a +directly comparable numeric result) +EXIT_CODE: 0 +Output Summary: +- Total tests: 541. Passed: 541. Failed: 0. Total time: 6.8048 seconds. (541 = 539 baseline tests + + 2 new regression tests added in P1-T2/P1-T3.) +- Repository-wide totals (reference only, not the coverage-gate denominator): line-rate + 0.2444818576379399 (24.45%), branch-rate 0.12631494740210392 (12.63%). Consistent with baseline + (24.43%/12.62%; the small increase reflects the two new tests and the fix's added conditional + logic). +- `QuickFiler` package: line-rate 0.7367671800181183 (73.68%), branch-rate 0.6462324393358876 + (64.62%). Baseline: 73.67%/64.53%. No regression; branch coverage improved slightly. +- `QuickFiler.Test` package: line-rate 0.9552276559865093 (95.52%), branch-rate 0.9252199413489736 + (92.52%). Unchanged from baseline. +- Class-level coverage for `QuickFiler.Controllers.QfcItemController` sourced from + `QfcItemController.FolderHandling.cs`: line-rate 0.918918918918919 (91.89%), branch-rate + 0.7380952380952381 (73.81%). Baseline: 91.55%/71.05%. No regression; both line and branch coverage + improved. +- Method-level coverage for the two fixed methods: + - `AssignFolderComboBox()`: line-rate 0.8928571428571429 (89.29%), branch-rate 0.875 (87.5%). + Baseline: 88.46%/85.71%. Improved (the new conditional branch is now exercised by both the + existing multi-suggestion test and the new single-suggestion test). + - `PopulateAndSelectFolder(System.Windows.Forms.ComboBox, string[], string)`: line-rate 1 (100%), + branch-rate 1 (100%). Unchanged from baseline (already fully covered; the new conditional branch + is exercised by the new `PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_...` test and the + pre-existing multi-item/predetermined tests). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/fail-before-392.2026-07-20T14-05.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/fail-before-392.2026-07-20T14-05.md new file mode 100644 index 00000000..014ea16b --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/fail-before-392.2026-07-20T14-05.md @@ -0,0 +1,19 @@ +Timestamp: 2026-07-20T14-05 +Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation /TestCaseFilter:"FullyQualifiedName~PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing|FullyQualifiedName~AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero"` +(preceded by `MSBuild.exe TaskMaster.sln /t:QuickFiler_Test /p:Configuration=Debug /p:Platform="Any CPU"`, EXIT_CODE 0, to compile the two new test methods before running them; `/InIsolation` added per prior-session repo guidance for Moq-based test assemblies) +EXIT_CODE: 1 +Output Summary: 2 failed, 0 passed, as expected before the fix. +- `PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing` failed with + an unhandled `System.ArgumentOutOfRangeException: InvalidArgument=Value of '1' is not valid for + 'SelectedIndex'` thrown from `System.Windows.Forms.ComboBox.set_SelectedIndex`, raised inside + `QfcItemController.PopulateAndSelectFolder` at `QfcItemController.FolderHandling.cs:228` — exactly + the diagnosed defect (unguarded `predeterminedIndex >= 0 ? predeterminedIndex : 1` fallback with a + single-item combo box). +- `AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero` failed on + `mock.Verify(v => v.SetFolderSelectedIndex(0), Times.Once())`: the Moq exception log shows the + actual invocation was `IItemViewer.SetFolderSelectedIndex(1)` — exactly the diagnosed defect at + `QfcItemController.FolderHandling.cs:202` (unconditional `SetFolderSelectedIndex(1)` regardless of + `FolderArray.Length`). + +This satisfies AC-1's fail-before requirement: both regression tests reproduce the defect and fail, +for the reasons diagnosed in P1-T1, before any production-code change. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/pass-after-392.2026-07-20T14-10.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/pass-after-392.2026-07-20T14-10.md new file mode 100644 index 00000000..f428b404 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/pass-after-392.2026-07-20T14-10.md @@ -0,0 +1,9 @@ +Timestamp: 2026-07-20T14-10 +Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation /TestCaseFilter:"FullyQualifiedName~PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing|FullyQualifiedName~AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero"` +(preceded by `MSBuild.exe TaskMaster.sln /t:QuickFiler_Test /p:Configuration=Debug /p:Platform="Any CPU"`, EXIT_CODE 0, 17 Warning(s), 0 Error(s), to rebuild with the P1-T5/P1-T6 production fix) +EXIT_CODE: 0 +Output Summary: 2 passed, 0 failed. Total time: 1.2911 seconds. +- `PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing`: Passed. +- `AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero`: Passed. + +This satisfies AC-1's pass-after requirement, AC-2, and AC-4. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md new file mode 100644 index 00000000..a85b85c2 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md @@ -0,0 +1,13 @@ +Timestamp: 2026-07-20T14-13 +Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation /TestCaseFilter:"FullyQualifiedName~PopulateAndSelectFolder_ExactMatchAtIndexZero|FullyQualifiedName~PopulateAndSelectFolder_AllMissingPredetermined|FullyQualifiedName~PopulateAndSelectFolder_EmptyArray|FullyQualifiedName~AssignFolderComboBox_WhenNoPredeterminedFolder|FullyQualifiedName~AssignFolderComboBox_WhenPredeterminedFolderPresent|FullyQualifiedName~AssignFolderComboBox_WhenFolderHandlerNull"` +EXIT_CODE: 0 +Output Summary: Total tests: 6. Passed: 6. Failed: 0. Total time: 1.3160 seconds. +- `PopulateAndSelectFolder_ExactMatchAtIndexZero_SelectsIndexZero`: Passed (predetermined-preselect path unchanged). +- `PopulateAndSelectFolder_AllMissingPredetermined_SelectsIndexOne`: Passed (multi-suggestion index-1 fallback unchanged, `FolderArray.Length == 3`). +- `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection`: Passed (empty-array-throws behavior unchanged; not modified by this plan per Scope-Lock). +- `AssignFolderComboBox_WhenNoPredeterminedFolder_SelectsTopSuggestionViaViewer`: Passed (multi-suggestion index-1 fallback via `AssignFolderComboBox` unchanged, `FolderArray.Length == 3`). +- `AssignFolderComboBox_WhenPredeterminedFolderPresent_PreselectsThatFolder`: Passed (predetermined-preselect path via `AssignFolderComboBox` unchanged). +- `AssignFolderComboBox_WhenFolderHandlerNull_DoesNotTouchViewer`: Passed (null-handler guard unchanged). + +Confirms the pre-existing multi-suggestion (index-1), predetermined-preselect, empty-array-throws, +and null-handler-guard tests all still pass unchanged after the P1-T5/P1-T6 fix. Satisfies AC-3. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md new file mode 100644 index 00000000..996711bd --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md @@ -0,0 +1,85 @@ +# folder-combobox-fallback-index-out-of-range (Issue #392) + +- Date captured: 2026-07-20 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/folder-combobox-fallback-index-out-of-range/ (Issue #392) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #392 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/392 +- Last Updated: 2026-07-20 +- Work Mode: minor-audit + +## Summary + +`QfcItemController.AssignFolderComboBox` unconditionally selects folder-suggestion index 1 when no predetermined folder matches. When the folder suggestion list contains exactly one entry, the breadcrumb pipeline (`BreadcrumbBridgeCoordinator.SelectRow` -> `BreadcrumbStateModel.SelectRow`) rejects the out-of-range index and throws `System.ArgumentOutOfRangeException`, aborting the QuickFiler load sequence. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Runtime: .NET Framework 4.8 VSTO add-in (TaskMaster / QuickFiler) +- Command/flags used: Outlook ribbon action "QuickFiler High Confidence" (`RibbonViewer.QuickFilerHighConfidence_Click`) +- Data source or fixture: Live mailbox item whose folder predictor returns a single folder suggestion + +## Steps to Reproduce + +1. Launch QuickFiler in high-confidence mode from the TaskMaster ribbon. +2. Load an email whose `FolderPredictor` produces a `FolderArray` with exactly one entry and no predetermined folder match. +3. `QfcCollectionController.LoadSecondaryAsync` invokes `QfcItemController.AssignFolderComboBox`, which calls `_itemViewer.SetFolderSelectedIndex(1)`. + +## Expected Behavior + +With a single available suggestion, the top (index 0) suggestion is selected and loading completes without error. + +## Actual Behavior + +`System.ArgumentOutOfRangeException` is thrown from `BreadcrumbStateModel.SelectRow` (UtilitiesCS) and propagates up through `QfcCollectionController.LoadSecondaryAsync` / `QfcFormController.LoadItemsAsync` / `QfcHomeController.LaunchAsync`, aborting the QuickFiler load: + +``` +Message=Row selection requires -1 or an index in [0, 0]. +Parameter name: index +Actual value was 1. + at UtilitiesCS.OutlookObjects.Folder.BreadcrumbStateModel.SelectRow(Int32 index) in BreadcrumbStateModel.cs:line 237 + at QuickFiler.Viewers.BreadcrumbBridgeCoordinator.SelectRow(Int32 index) in BreadcrumbBridgeCoordinator.cs:line 127 + at QuickFiler.Controllers.QfcItemController.AssignFolderComboBox() in QfcItemController.FolderHandling.cs:line 204 +``` + +## Logs / Screenshots + +- [x] Attached minimal logs or screenshot +- Snippet: full stack trace captured in the exception details above (source: user-supplied crash report, 2026-07-20). + +## Impact / Severity + +- [ ] Blocker +- [x] High +- [ ] Medium +- [ ] Low + +High: the exception aborts the entire QuickFiler high-confidence load path whenever any loaded item has exactly one folder suggestion. + +## Suspected Cause / Notes + +- `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` line 202: `_itemViewer.SetFolderSelectedIndex(1)` assumes at least two suggestion rows exist. +- The same unguarded index-1 fallback exists in the retained static helper `PopulateAndSelectFolder` (same file, line 228): `comboBox.SelectedIndex = predeterminedIndex >= 0 ? predeterminedIndex : 1;` — a WinForms `ComboBox` with one item also rejects `SelectedIndex = 1`. +- `BreadcrumbStateModel.SelectRow` validation is correct defensive behavior; the defect is in the callers. + +## Proposed Fix / Validation Ideas + +- [x] Unit coverage areas: clamp the fallback index to the available row count (`rows > 1 ? 1 : 0`; `-1`/no-op when empty) in `AssignFolderComboBox` and mirror the guard in `PopulateAndSelectFolder`; add MSTest regression tests for single-entry and empty suggestion lists. +- [ ] Integration scenario to retest: QuickFiler high-confidence load with a single-suggestion item. +- [x] Manual verification notes: re-run the ribbon "QuickFiler High Confidence" action against an item with one suggestion after the fix. + +## Acceptance Criteria + +- [x] AC-1: A deterministic MSTest regression test reproduces the defect (fallback selection with exactly one folder suggestion) and fails before the fix; the same test passes after the fix. No temporary files or external dependencies are used. +- [x] AC-2: `QfcItemController.AssignFolderComboBox` no longer throws `ArgumentOutOfRangeException` when `FolderArray` has exactly one entry and no predetermined folder matches: it selects index 0 (the only suggestion) instead of index 1. +- [x] AC-3: Existing multi-suggestion behavior is preserved: with two or more suggestions and no predetermined match, index 1 remains selected; with a predetermined folder present in the list, that folder remains preselected. +- [x] AC-4: The retained static helper `PopulateAndSelectFolder` applies the same bounds-safe fallback so a single-item combo box does not throw. +- [x] AC-5: The full C# toolchain passes in order (CSharpier format, .NET analyzers build, nullable build, MSTest via vstest.console.exe) with zero regressions relative to the Phase 0 baseline, and new/changed code meets the >= 90% coverage target. Scope note (amended 2026-07-20 by orchestrator, before feature review): nullable enforcement is scoped to first-party projects per `.claude/rules/csharp.md` (analyzers/nullable are wired to first-party projects only; vendored projects are excluded). The 34 pre-existing nullable errors in vendored `SVGControl.csproj` are byte-identical to the Phase 0 baseline, are not enforced by CI (MSBuild incremental skip), and are tracked separately in `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`; they do not gate this bug fix. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [x] Move to active fix folder / branch diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/plan.2026-07-20T12-59.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/plan.2026-07-20T12-59.md new file mode 100644 index 00000000..e19c1028 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/plan.2026-07-20T12-59.md @@ -0,0 +1,421 @@ +# folder-combobox-fallback-index-out-of-range (Plan) + +- **Issue:** #392 +- **Issue URL:** https://github.com/drmoisan/TaskMaster/issues/392 +- **Parent (optional):** none +- **Owner:** drmoisan +- **Last Updated:** 2026-07-20T12-59 +- **Status:** Draft +- **Version:** 1.0 +- **Work Mode:** minor-audit +- **Requirements Source:** `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md` (`## Acceptance Criteria`, AC-1..AC-5) +- **Feature folder (``):** `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392` +- **Timestamp token:** every `` placeholder below MUST be substituted with the real ISO-8601 + timestamp (`yyyy-MM-ddTHH-mm`) at the moment the artifact is written, per + `evidence-and-timestamp-conventions`. + +**Fail-closed evidence rule:** Include explicit baseline artifact tasks, final-QA artifact tasks, +and coverage-comparison tasks for each in-scope language when policy requires coverage. If any +required baseline artifact, QA artifact, or coverage-comparison artifact is missing, the audit +verdict must be BLOCKED or INCOMPLETE, never PASS. + +**Evidence accounting rule:** Record the expected artifact path or location in each +evidence-producing task. Do not mark evidence-backed work complete without the artifact. + +## Requirements Boundary + +This minor-audit plan uses only +`docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md` as the +requirements source. Acceptance criteria are limited to the checkbox items (AC-1..AC-5) under that +file's explicit `## Acceptance Criteria` section (lines 74-80). `spec.md` and `user-story.md` are +not required for minor-audit mode; confirmed absent from the feature folder at plan time (only +`issue.md` and this plan file exist there). If either appears later, that is a fail-closed +condition and must be reported, not silently ignored. + +All evidence must be written under +`docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence//`. +No non-canonical path (e.g. `artifacts/baselines/`, `artifacts/qa/`, `artifacts/coverage/`, +`artifacts/evidence/`) is used for evidence anywhere in this plan. The one exception is the +repo-wide feature-review coverage-gate **input** at `artifacts/csharp/coverage.xml`, which is a +documented non-evidence tooling path (see Correction note below), not a duplicate/alternate +evidence location. + +## Correction to Upstream Delegation Instructions (format only, not evidence path) + +The delegation prompt instructed generating `artifacts/csharp/coverage.xml` in **Cobertura** +format. That is corrected here: `.claude/hooks/validate-feature-review-coverage.ps1` parses +`artifacts/csharp/coverage.xml` as **JaCoCo** XML (`//counter[@type="LINE"]`, +`//counter[@type="BRANCH"]`), not Cobertura. Producing Cobertura at that path would be silently +misread as zero coverage by the feature-review gate. Phase 2 therefore plans a Cobertura→JaCoCo +**conversion** (mirroring the established pattern used in `2026-07-16-quickfiler-breadcrumb-webview2-351`, +evidence `evidence/qa-gates/coverage-conversion.2026-07-18T10-55.md`), not a raw copy of the vstest +Cobertura output. `artifacts/csharp/` itself is not a forbidden evidence path — it is the gate's +tooling-input path, distinct from `/evidence//` outputs — so no +`EVIDENCE_LOCATION_OVERRIDE_REJECTED` applies to this path; only the stated file **format** is +corrected. + +## Coverage Floor Used By This Plan + +`issue.md` AC-5 explicitly states the applicable target for this feature: "new/changed code meets +the >= 90% coverage target" as part of the full C# toolchain pass. This plan uses that explicit +AC-5 figure for new/changed-code coverage. (Note for the record, not a plan blocker: this repo +currently carries an unresolved wider conflict between CLAUDE.md's repo-wide 80%/new-code 90% +coverage floor and `.claude/rules/general-unit-test.md`'s uniform 85%/75% line/branch floor with no +tier exception — see prior flag in atomic-planner memory. AC-5's explicit 90% new-code figure is +unambiguous for this plan's scope and is used as-is; the wider repo-floor conflict is out of scope +for this minimal-audit bugfix and is not resolved here.) + +## Confirmed Facts (from source inspection, recorded for the Phase 1 diagnosis task) + +- `QuickFiler/Controllers/QfcItemController.FolderHandling.cs:161-206` — `AssignFolderComboBox()`: + when `_folderHandler.FolderArray.Length > 0` (line 170) and no predetermined-folder match is + present/selected, the `else` branch (lines 200-203) unconditionally calls + `_itemViewer.SetFolderSelectedIndex(1)` (line 202) regardless of `FolderArray.Length`. +- `QuickFiler/Viewers/BreadcrumbBridgeCoordinator.cs:127` — `SelectRow(int index)` forwards the + index unchanged into `BreadcrumbStateModel.SelectRow`. +- `UtilitiesCS/OutlookObjects/Folder/BreadcrumbStateModel.cs:233-246` — `SelectRow(int index)` + validates `index` is `-1` or in `[0, RowCount-1]` and throws `ArgumentOutOfRangeException` for a + single-row model (`RowCount == 1`) when `index == 1`. This validation is correct defensive + behavior and must NOT be changed by this plan. +- `QuickFiler/Controllers/QfcItemController.FolderHandling.cs:217-230` — the retained static helper + `PopulateAndSelectFolder(ComboBox, string[], string)`: line 228, + `comboBox.SelectedIndex = predeterminedIndex >= 0 ? predeterminedIndex : 1;`, has the identical + unguarded index-1 fallback; a WinForms `ComboBox` populated with exactly one item throws + `ArgumentOutOfRangeException` when `SelectedIndex` is set to `1` (confirmed by the existing test + `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection` in + `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs:72-90`, which demonstrates + the same out-of-range throw for zero items). +- `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` is the established test + home for both surfaces: it already contains `[TestClass] QfcItemController_FolderHandlingTests` + with `PopulateAndSelectFolder_*` tests (real `ComboBox`, no mocking) and + `AssignFolderComboBox_*` tests (`Mock` via the `FolderController` test subclass, the + `BuildFolderHandlerWithArray(params string[])` helper, and the private `SetPrivate` reflection + helper). This file is already ``-wired in + `QuickFiler.Test/QuickFiler.Test.csproj:99`; no csproj change is required for this plan's new + tests. +- `AssignFolderComboBox`'s `_itemViewer` is a `Mock` in existing tests, so the real + `BreadcrumbStateModel` bounds check is not exercised through that seam; the true + `ArgumentOutOfRangeException` repro is only directly observable through the pure, unmocked + `PopulateAndSelectFolder` static helper. The `AssignFolderComboBox` regression test therefore + verifies the corrected mock interaction (`SetFolderSelectedIndex(0)` instead of `(1)`) rather than + an exception, which is sufficient to prove AC-2 at the controller-call level. + +## Scope-Lock — files this plan authorizes changing + +Production (modify only; no new production files): +- `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` (233 lines pre-change; stays well + under the 500-line limit after the two small conditional edits) + +Tests (modify only; file already ``-wired, no csproj change required): +- `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` (481 lines pre-change; + verify it remains <= 500 lines after the two new test methods are added — see P1-T2/P1-T3 + acceptance criteria) + +No other file may be changed by this plan. No `spec.md`/`user-story.md` edits. No +`BreadcrumbStateModel`, `BreadcrumbBridgeCoordinator`, or other UtilitiesCS file may be touched. + +--- + +### Phase 0 — Baseline Capture + +- [x] [P0-T1] Read `CLAUDE.md` in full (policy reading order position 1). + - Acceptance: file read in this session; its Policy Compliance Order section is quoted verbatim + in the P0-T5 evidence artifact. + +- [x] [P0-T2] Read `.claude/rules/general-code-change.md` (policy reading order position 2). + - Acceptance: file read; its Mandatory Toolchain Loop section quoted in the P0-T5 evidence + artifact. + +- [x] [P0-T3] Read `.claude/rules/general-unit-test.md` (policy reading order position 3). + - Acceptance: file read; its Coverage Requirements section quoted in the P0-T5 evidence artifact. + +- [x] [P0-T4] Read `.claude/rules/csharp.md` (policy reading order positions 4-5, C# Code Change + Policy and C# Unit Test Policy consolidated). + - Acceptance: file read; its Toolchain and Testing Standards sections quoted in the P0-T5 + evidence artifact. + +- [x] [P0-T5] Write the Phase 0 policy-read evidence artifact to + `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/phase0-instructions-read.md`. + - Acceptance: file exists and contains `Timestamp:`, `Policy Order:` (the exact ordered list + "CLAUDE.md (all sections) → General Code Change Policy → General Unit Test Policy → C# Code + Change Policy → C# Unit Test Policy"), and an explicit list of the four files read in P0-T1 + through P0-T4, in order. + +- [x] [P0-T6] Verify the minor-audit requirements boundary for issue #392. + - Files: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md` + (and confirm absence of `spec.md`/`user-story.md` in the same folder). + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/minor-audit-scope..md`. + - Acceptance: evidence confirms `issue.md` contains `- Work Mode: minor-audit`, contains an + explicit `## Acceptance Criteria` section listing AC-1..AC-5, treats only that section as the + AC source, and records that `spec.md`/`user-story.md` are absent from the feature folder + (fail-closed if either is unexpectedly present). + +- [x] [P0-T7] Record baseline git state (current branch name and `HEAD` short SHA via + `git rev-parse --abbrev-ref HEAD` and `git rev-parse --short HEAD`). + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/git-baseline-state..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:` + stating the branch name and SHA. + +- [x] [P0-T8] Record candidate-defect-surface baseline notes citing the Confirmed Facts file:line + list above (capture only, no new conclusion — the diagnosis conclusion is P1-T1's job). + - Files: `QuickFiler/Controllers/QfcItemController.FolderHandling.cs`, + `QuickFiler/Viewers/BreadcrumbBridgeCoordinator.cs`, + `UtilitiesCS/OutlookObjects/Folder/BreadcrumbStateModel.cs`. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/candidate-defect-surface..md`. + - Acceptance: artifact lists, verbatim, the file:line citations from this plan's Confirmed Facts + section for the two unguarded index-1 fallback sites and the validating + `BreadcrumbStateModel.SelectRow` bounds check. + +- [x] [P0-T9] Run the baseline C# formatting command. + - Command: `dotnet tool run csharpier --check .` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/csharpier-baseline..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:` + stating whether any files are currently unformatted. + +- [x] [P0-T10] Run the baseline C# analyzer build command. + - Command: `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/analyzer-baseline..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:` with + the warning/error count. + +- [x] [P0-T11] Run the baseline C# nullable build command. + - Command: `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/nullable-baseline..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:` with + the warning/error count. + +- [x] [P0-T12] Run the baseline MSTest coverage command for `QuickFiler.Test`. + - Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /EnableCodeCoverage` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/baseline/vstest-coverage-baseline..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:` with + total tests, pass/fail counts, and the numeric baseline line-coverage percentage for + `QuickFiler.dll`. + +--- + +### Phase 1 — Constrained Small-Path Implementation + +Delegated to the C# small-path implementation engineer via `atomic-executor`. Follows the repo +Bugfix Workflow: (a) diagnose and document root cause, (b) author failing regression tests first, +(c) implement the minimal targeted fix in both affected call sites, (d) verify no regression, (e) +check off satisfied AC items in `issue.md`. + +- [x] [P1-T1] Diagnose and document the confirmed root cause. + - Files: `QuickFiler/Controllers/QfcItemController.FolderHandling.cs`, + `QuickFiler/Viewers/BreadcrumbBridgeCoordinator.cs`, + `UtilitiesCS/OutlookObjects/Folder/BreadcrumbStateModel.cs`. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/root-cause-392..md`. + - Acceptance: evidence cites file:line for (a) `AssignFolderComboBox()`'s unconditional + `SetFolderSelectedIndex(1)` at `QfcItemController.FolderHandling.cs:202`; (b) the identical + unguarded fallback in the static `PopulateAndSelectFolder` helper at + `QfcItemController.FolderHandling.cs:228`; (c) confirmation that + `BreadcrumbStateModel.SelectRow` (`BreadcrumbStateModel.cs:233-246`) is correct defensive + validation and is explicitly NOT to be modified; (d) a single confirmed primary root-cause + statement: both call sites must clamp the fallback index to `0` when exactly one suggestion + exists and to `1` only when two or more suggestions exist. This satisfies the diagnosis + prerequisite for AC-1/AC-2/AC-4. + +- [x] [P1-T2] [expect-fail] Author a failing regression test + `PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing` in + `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs`, alongside the existing + `PopulateAndSelectFolder_*` tests. + - Test body: use a real `System.Windows.Forms.ComboBox` (mirroring + `PopulateAndSelectFolder_AllMissingPredetermined_SelectsIndexOne`), with + `folders = new[] { @"\\A\only" }` and `predeterminedFolder: null`. Assert + `Action act = () => QfcItemController.PopulateAndSelectFolder(comboBox, folders, null); + act.Should().NotThrow();` then assert + `comboBox.SelectedIndex.Should().Be(0)` and the returned selected string equals + `@"\\A\only"`. No live Outlook process, no temporary files. + - Precondition: P1-T1 complete. + - Acceptance: new test method exists in the file; file remains <= 500 lines after the addition. + +- [x] [P1-T3] [expect-fail] Author a failing regression test + `AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero` in + `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs`, alongside the existing + `AssignFolderComboBox_*` tests. + - Test body: mirror `AssignFolderComboBox_WhenNoPredeterminedFolder_SelectsTopSuggestionViaViewer` + exactly (same `Mock` setup, same `FolderController` subclass, same + `BuildFolderHandlerWithArray` helper) but seed the handler with exactly one folder + (`BuildFolderHandlerWithArray(@"\\A\only")`) and no predetermined folder. Assert + `mock.Verify(v => v.SetFolderSelectedIndex(0), Times.Once())` and + `mock.Verify(v => v.SetFolderSelectedIndex(1), Times.Never())`, and + `controller.SelectedFolder.Should().Be(@"\\A\only")` (with `mock.Setup(v => + v.GetSelectedFolder()).Returns(@"\\A\only")`). + - Precondition: P1-T1 complete. + - Acceptance: new test method exists in the file; file remains <= 500 lines after the addition + (verify explicitly; if the two new tests push the file over 500 lines, this is a blocking + condition to resolve within this same file — no new file/partial split is authorized by this + plan's Scope-Lock). + +- [x] [P1-T4] [expect-fail] Run both new regression tests via `vstest.console.exe` and confirm both + currently fail (fail-before evidence), before any production-code change. + - Precondition: P1-T2 and P1-T3 complete; no production code changed yet. + - Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /TestCaseFilter:"FullyQualifiedName~PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing|FullyQualifiedName~AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero"` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/fail-before-392..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, a non-zero `EXIT_CODE:`, and + `Output Summary:` stating `2 failed` (the `PopulateAndSelectFolder` test failing with an + unhandled `ArgumentOutOfRangeException`, the `AssignFolderComboBox` test failing on the + `SetFolderSelectedIndex(0)` verify). This satisfies AC-1's fail-before requirement. + +- [x] [P1-T5] Implement the minimal fix in `AssignFolderComboBox()`'s `else` branch in + `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` (line 202): replace the + unconditional `_itemViewer.SetFolderSelectedIndex(1);` with a bounds-safe selection that calls + `SetFolderSelectedIndex(0)` when `_folderHandler.FolderArray.Length == 1` and + `SetFolderSelectedIndex(1)` when `_folderHandler.FolderArray.Length > 1` (the enclosing + `_folderHandler?.FolderArray?.Length > 0` guard at line 170 already excludes the empty-array + case; do not restructure that guard). + - Precondition: P1-T4 confirmed both new tests failing. + - Acceptance: exactly one conditional expression changed inside the `else` branch at line + ~200-203; no other line in `AssignFolderComboBox()` changes; `git diff` for + `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` shows only this substitution plus + the P1-T6 change below. Satisfies AC-2. + +- [x] [P1-T6] Implement the mirrored minimal fix in the static `PopulateAndSelectFolder(...)` + helper in `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` (line 228): replace + `comboBox.SelectedIndex = predeterminedIndex >= 0 ? predeterminedIndex : 1;` with a bounds-safe + fallback that selects `0` when `folderArray.Length == 1` and `1` when `folderArray.Length > 1`, + preserving the existing predetermined-folder preselection (`predeterminedIndex >= 0`) unchanged. + - Precondition: P1-T4 confirmed both new tests failing. + - Acceptance: exactly one line changed; `git diff` for + `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` for this method shows only this + substitution; the empty-array case (`folderArray.Length == 0`, no predetermined match) is + unchanged and continues to throw via the real WinForms `ComboBox` (no behavior change is + required or authorized for that pre-existing, separately-tested case). Satisfies AC-4. + +- [x] [P1-T7] Re-run the two new regression tests from P1-T2/P1-T3 alone and confirm both now pass. + - Precondition: P1-T5 and P1-T6 complete. + - Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /TestCaseFilter:"FullyQualifiedName~PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing|FullyQualifiedName~AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero"` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/pass-after-392..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE: 0`, and `Output Summary:` + stating `2 passed, 0 failed`. Satisfies AC-1's pass-after requirement, AC-2, and AC-4. + +- [x] [P1-T8] Run the targeted regression suite for the existing multi-suggestion and + predetermined-folder-preselect tests to confirm no regression. + - Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /TestCaseFilter:"FullyQualifiedName~PopulateAndSelectFolder_ExactMatchAtIndexZero|FullyQualifiedName~PopulateAndSelectFolder_AllMissingPredetermined|FullyQualifiedName~PopulateAndSelectFolder_EmptyArray|FullyQualifiedName~AssignFolderComboBox_WhenNoPredeterminedFolder|FullyQualifiedName~AssignFolderComboBox_WhenPredeterminedFolderPresent|FullyQualifiedName~AssignFolderComboBox_WhenFolderHandlerNull"` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/targeted-no-regression-392..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE: 0`, and `Output Summary:` + listing pass counts per test name, confirming the pre-existing multi-suggestion (index-1), + predetermined-preselect, empty-array-throws, and null-handler-guard tests all still pass + unchanged. Satisfies AC-3. + +- [x] [P1-T9] Check off satisfied AC items (AC-1 through AC-4) in `issue.md`'s + `## Acceptance Criteria` section per `acceptance-criteria-tracking`, citing the Phase 1 evidence + artifacts backing each. + - Files: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md`. + - Evidence mirror: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-phase1-392..md`. + - Acceptance: only AC-1 through AC-4 under `## Acceptance Criteria` are changed from `- [ ]` to + `- [x]`, each backed by the corresponding Phase 1 evidence artifact path; AC-5 remains + unchecked pending Phase 2. + +--- + +### Phase 2 — Final QC Loop + +Unconditional full C# toolchain, run in order. If any step fails or changes files, restart this +phase from P2-T1. No `SKIPPED` outcomes; no IN_SCOPE/OUT_OF_SCOPE branches. + +- [x] [P2-T1] Run the final C# formatting command. + - Command: `dotnet tool run csharpier .` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/csharpier-final-392..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:`; if + this command changes files, restart Phase 2 from P2-T1. + +- [x] [P2-T2] Run the final C# analyzer build command. + - Command: `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/analyzer-final-392..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE: 0`, and `Output Summary:`; + if this command fails, fix and restart Phase 2 from P2-T1. + +- [x] [P2-T3] Run the final C# nullable build command. + - Command: `msbuild TaskMaster.sln /t:Rebuild /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true` + - Rationale for `/t:Rebuild` (not `/t:Build`): the P0-T11 baseline was captured with `/t:Rebuild` + because MSBuild's incremental up-to-date check otherwise skips `CoreCompile` after P2-T2's + build, which would vacuously pass both the vendored-exclusion comparison and — worse — skip + re-verifying nullable flow on the modified first-party file. A full-recompile-to-full-recompile + comparison is required (see + `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`). + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/nullable-final-392..md`. + - Acceptance: the command must run and the artifact must record `Timestamp:`, `Command:`, + `EXIT_CODE:`, and `Output Summary:` including an error-set comparison against the P0-T11 + baseline artifact. Acceptance = zero NEW errors relative to the P0-T11 baseline AND zero errors + attributable to first-party in-scope files. Pre-existing vendored `SVGControl.csproj` errors + that are identical to the P0-T11 baseline are explicitly non-blocking, per the AC-5 scope note + (amended 2026-07-20 by orchestrator) in `issue.md` and + `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`. If any NEW + error appears, or any error is attributable to a first-party project, fix and restart Phase 2 + from P2-T1. + +- [x] [P2-T4] Run the final full-suite MSTest coverage command for `QuickFiler.Test` via + `vstest.console.exe` with `/EnableCodeCoverage`, producing a Cobertura report. + - Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /EnableCodeCoverage` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/vstest-coverage-final-392..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE: 0`, and `Output Summary:` + with total tests, pass/fail counts, and the numeric post-change line-coverage percentage for + `QuickFiler.dll`; if this command fails, fix and restart Phase 2 from P2-T1. + +- [x] [P2-T5] Compare the baseline coverage (P0-T12) against the post-change coverage (P2-T4) for + `QuickFiler/Controllers/QfcItemController.FolderHandling.cs`'s changed lines and confirm no + regression and >= 90% coverage on the new/changed code (per AC-5's explicit target). + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-delta-392..md`. + - Acceptance: artifact contains baseline coverage %, post-change coverage %, and changed-line + coverage % for `QfcItemController.FolderHandling.cs`, with an explicit PASS/FAIL statement on + "no regression on changed lines" and ">= 90% coverage on new/changed code." If either check + fails, the outcome is remediation-required, not PASS. + +- [x] [P2-T6] Verify no other test regressed by comparing the baseline (P0-T12) and final (P2-T4) + full-suite results by test name/class. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/regression-check-392..md`. + - Acceptance: artifact confirms every test that passed at baseline still passes, and the total + pass count did not decrease. + +- [x] [P2-T7] Convert the P2-T4 Cobertura output to the JaCoCo-format canonical coverage-gate input + at `artifacts/csharp/coverage.xml` (report/package/class `counter` elements), scoped to + first-party assemblies only (excluding vendored/third-party and `*.Test` assemblies), mirroring + the established conversion pattern recorded in + `docs/features/active/2026-07-16-quickfiler-breadcrumb-webview2-351/evidence/qa-gates/coverage-conversion.2026-07-18T10-55.md`. + Also write a canonical evidence mirror of the resulting counter totals. + - Evidence: `artifacts/csharp/coverage.xml` (JaCoCo, tooling-input path, not an evidence + duplicate) and + `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-conversion-392..md`. + - Acceptance: `artifacts/csharp/coverage.xml` exists in JaCoCo format (not Cobertura) with + `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:` recorded in the evidence mirror, + including the report-level LINE and BRANCH counter totals used to satisfy the feature-review + coverage gate. + +- [x] [P2-T8] Check off AC-5 in `issue.md`'s `## Acceptance Criteria` section and record the final + AC closure summary. + - Files: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md`. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/ac-status-final-392..md` + and `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/ac-closure-summary-392..md`. + - Acceptance: AC-5 is changed from `- [ ]` to `- [x]` when the amended AC-5 wording (baseline-scoped + nullable gate: zero regression relative to the P0-T11 baseline, first-party projects clean; + pre-existing vendored `SVGControl.csproj` errors do not gate this fix) is satisfied by the + recorded P2-T1 through P2-T7 evidence; the closure summary lists AC-1 through AC-5 each mapped + to its exact backing evidence artifact path(s) from Phases 1 and 2. + +- [x] [P2-T9] Record final minor-audit readiness evidence for issue #392. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/minor-audit-readiness-392..md`. + - Acceptance: evidence confirms Phase 0 artifacts exist, Phase 1 diagnosis/regression-test/fix + evidence exists, Phase 2 QC artifacts exist (including the P2-T7 JaCoCo conversion), every + command-bearing task has an executed numeric `EXIT_CODE`, and AC-1 through AC-5 are checked off + in `issue.md`. AC-1 through AC-5 may be recorded as all checked under the amended AC-5 wording + (baseline-scoped nullable gate, first-party projects only), and the evidence must cite the + vendored-nullable exclusion rationale: the P2-T3 error-set comparison against the P0-T11 + baseline, and `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`. + +--- + +## Acceptance Criteria Coverage Map (for preflight cross-check) + +- AC-1 (deterministic MSTest regression test reproduces the defect and fails before the fix, passes + after; no temp files/external dependencies) → P1-T2 (author), P1-T4 (fail-before), P1-T5/P1-T6 + (fix), P1-T7 (pass-after). +- AC-2 (`AssignFolderComboBox` no longer throws for a single-entry `FolderArray` with no + predetermined match; selects index 0) → P1-T3 (author), P1-T4 (fail-before), P1-T5 (fix), P1-T7 + (pass-after). +- AC-3 (existing multi-suggestion index-1 behavior and predetermined-preselect behavior preserved) + → P1-T8 (targeted no-regression run). +- AC-4 (`PopulateAndSelectFolder` applies the same bounds-safe fallback) → P1-T2 (author), P1-T4 + (fail-before), P1-T6 (fix), P1-T7 (pass-after). +- AC-5 (full C# toolchain passes in order; zero regressions; new/changed code >= 90% coverage) → + P2-T1 through P2-T7. From 8a1b7b98b7d12dac69fd1bee5d5f109d4095c3c6 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Mon, 20 Jul 2026 14:39:06 -0400 Subject: [PATCH 2/3] test(folder-selection): add InvokeRequired branch-coverage test - Covers PopulateFolderComboBox marshaling when InvokeRequired is true - Raises QfcItemController.FolderHandlingTests branch coverage above 75% floor - Includes feature-review audit and remediation evidence Refs: #392 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01A2Yht8Rg83Um38RaVyV1sA --- .claude/agent-memory/feature-review/MEMORY.md | 1 + ...ted-package-does-not-always-clear-floor.md | 38 +++ .../QfcItemController.FolderHandlingTests.cs | 50 ++- .../code-review.2026-07-20T18-00.md | 50 +++ ...emediation-cycle1-note.2026-07-20T18-48.md | 37 +++ .../branch-gap-analysis.2026-07-20T18-20.md | 70 +++++ .../other/file-size-check.2026-07-20T18-27.md | 19 ++ ...e-disposition-decision.2026-07-20T18-17.md | 54 ++++ ...diation-analyzer-final.2026-07-20T18-32.md | 7 + ...on-coverage-conversion.2026-07-20T18-42.md | 31 ++ ...diation-coverage-delta.2026-07-20T18-44.md | 29 ++ ...iation-csharpier-final.2026-07-20T18-30.md | 19 ++ ...diation-nullable-final.2026-07-20T18-35.md | 28 ++ ...iation-reaudit-handoff.2026-07-20T18-50.md | 49 +++ ...ation-regression-check.2026-07-20T18-46.md | 18 ++ ...-vstest-coverage-final.2026-07-20T18-40.md | 13 + .../new-branch-test-pass.2026-07-20T18-25.md | 11 + .../coverage-baseline.2026-07-20T18-15.md | 32 ++ .../git-baseline-state.2026-07-20T18-08.md | 6 + .../minor-audit-scope.2026-07-20T18-07.md | 29 ++ ...ase0-instructions-read.2026-07-20T18-05.md | 110 +++++++ .../feature-audit.2026-07-20T18-00.md | 79 +++++ .../issue.md | 2 + .../policy-audit.2026-07-20T18-00.md | 292 ++++++++++++++++++ .../remediation-inputs.2026-07-20T18-00.md | 60 ++++ .../remediation-plan.2026-07-20T18-00.md | 283 +++++++++++++++++ 26 files changed, 1391 insertions(+), 26 deletions(-) create mode 100644 .claude/agent-memory/feature-review/project_rescoping-to-instrumented-package-does-not-always-clear-floor.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T18-00.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/remediation-cycle1-note.2026-07-20T18-48.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/branch-gap-analysis.2026-07-20T18-20.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/file-size-check.2026-07-20T18-27.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-analyzer-final.2026-07-20T18-32.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-csharpier-final.2026-07-20T18-30.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-nullable-final.2026-07-20T18-35.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-reaudit-handoff.2026-07-20T18-50.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-regression-check.2026-07-20T18-46.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/new-branch-test-pass.2026-07-20T18-25.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/coverage-baseline.2026-07-20T18-15.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/git-baseline-state.2026-07-20T18-08.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/minor-audit-scope.2026-07-20T18-07.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/phase0-instructions-read.2026-07-20T18-05.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T18-00.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T18-00.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-inputs.2026-07-20T18-00.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-plan.2026-07-20T18-00.md diff --git a/.claude/agent-memory/feature-review/MEMORY.md b/.claude/agent-memory/feature-review/MEMORY.md index 0491ed1d..288e3ca2 100644 --- a/.claude/agent-memory/feature-review/MEMORY.md +++ b/.claude/agent-memory/feature-review/MEMORY.md @@ -38,3 +38,4 @@ - [partial remediation still fails new-code floor](project_partial-remediation-new-code-floor-still-fails-209.md) — #209 R4: extracting one testable seam from a native-engine adapter raised coverage 0%->7.7%, real progress but still far below 85%/90%; recompute against the actual floor, don't trust a remediation-plan's weak ">0%" acceptance bar; recommend a maintainer exemption decision when the residual is architecturally irreducible - [ScoDictionaryNew swap: enumeration order + removal API](project_scodictionary-new-enumeration-order-and-removal-api.md) — swordfish-removal epic (F1 #306..F5): swap silently changes insertion->concurrent enumeration order (needs tie-breaks) and .Remove->.TryRemove in consumers; Static.Deserialize+Serialize() only, never the globals converter path; no SerializeAsync - [epic-child-twodot-diff-divergence-noise](project_epic-child-twodot-diff-divergence-noise.md) — reviewing an epic child vs the integration branch: use three-dot merge-base diff or sibling merges show as spurious adds/deletes (#307) +- [rescoping to instrumented package doesn't always clear floor](project_rescoping-to-instrumented-package-does-not-always-clear-floor.md) — #392: unlike #328's StoresWrapper win, QuickFiler's own instrumented-package figure (73.68%/64.62%) is itself pre-existing sub-floor; always re-check against the floor after rescoping instead of assuming it always rescues the number diff --git a/.claude/agent-memory/feature-review/project_rescoping-to-instrumented-package-does-not-always-clear-floor.md b/.claude/agent-memory/feature-review/project_rescoping-to-instrumented-package-does-not-always-clear-floor.md new file mode 100644 index 00000000..59e5afdf --- /dev/null +++ b/.claude/agent-memory/feature-review/project_rescoping-to-instrumented-package-does-not-always-clear-floor.md @@ -0,0 +1,38 @@ +--- +name: rescoping-to-instrumented-package-does-not-always-clear-floor +description: Unlike #328's StoresWrapper precedent, #392's QuickFiler package still fails the 85%/75% floor after rescoping the canonical JaCoCo artifact to the one actually-instrumented package +metadata: + type: project +--- + +The established correction pattern from [[project_csharp-canonical-jacoco-includes-uninstrumented-assemblies]] +(re-scope a canonical `artifacts/csharp/coverage.xml` aggregate to only the first-party assemblies +whose own dedicated test project actually ran in the local `dotnet-coverage` collection) does **not** +always rescue the number above the 85% line / 75% branch floor. On issue #328 +(`outlook-store-exclusion`), re-scoping the six-package aggregate down to the three instrumented +assemblies raised the figure from 70.45%/67.11% to 85.71%/79.34% — clearing the floor. On issue #392 +(`folder-combobox-fallback-index-out-of-range`), the single instrumented package (`QuickFiler`, the +only one with its own `.Test` project in a `QuickFiler.Test`-only local collection) was itself +measured at 73.68% line / 64.62% branch — still below the floor. The raw baseline for that same +package (captured before the #392 fix) was 73.67%/64.53%, virtually identical, confirming the gap is +a genuine, broad, pre-existing under-coverage condition in `QuickFiler`'s WinForms/UI surface, not a +scope-measurement artifact this time. + +**Why:** Rescoping only removes distortion from packages that were never run in a given local +collection (they read 0% because they're unmeasured, not because they're actually uncovered). It does +not change the true measured coverage of the one package that *was* run. Whether the corrected number +clears the floor is a fact about that specific package's real coverage, not a property of the +correction technique — do not assume rescoping is always sufficient just because it worked on a prior +review. + +**How to apply:** After re-scoping to the instrumented package(s), always compare the corrected figure +against the 85%/75% floor explicitly before writing PASS. If it still fails, do not attempt to further +shrink scope to chase a passing number (that would cross into narrowing / cherry-picking, which is +prohibited). Instead: (1) compare against that package's own pre-change baseline to confirm whether the +gap is pre-existing or a regression, (2) if pre-existing and broad (spans many unrelated classes), +route to remediation as a maintainer-disposition decision (ratified exception, analogous to the +[[project_partial-remediation-new-code-floor-still-fails-209]] pattern, or a dedicated coverage-uplift +task) rather than a code fix inside a minor-audit bug-fix cycle, and (3) if a narrower, marginal gap +exists at the class/file level (e.g., #392's `QfcItemController.FolderHandling.cs` at 73.81% branch, +only 1.19 points under floor), it may be closeable with 1-2 targeted tests within the already-touched +file — recommend that as a separate, smaller remediation task from the broader package-wide gap. diff --git a/QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs b/QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs index d7923ade..4957e52e 100644 --- a/QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs +++ b/QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs @@ -32,14 +32,12 @@ public void PopulateAndSelectFolder_ExactMatchAtIndexZero_SelectsIndexZero() var folders = new[] { @"\\A\predetermined", @"\\A\suggestion1", @"\\A\suggestion2" }; using (var comboBox = new ComboBox()) { - // Act var selected = QfcItemController.PopulateAndSelectFolder( comboBox, folders, predeterminedFolder: @"\\A\predetermined" ); - // Assert comboBox.SelectedIndex.Should().Be(0); selected.Should().Be(@"\\A\predetermined"); } @@ -52,14 +50,12 @@ public void PopulateAndSelectFolder_AllMissingPredetermined_SelectsIndexOne() var folders = new[] { @"\\A\header", @"\\A\top", @"\\A\second" }; using (var comboBox = new ComboBox()) { - // Act var selected = QfcItemController.PopulateAndSelectFolder( comboBox, folders, predeterminedFolder: @"\\A\not-present" ); - // Assert comboBox.SelectedIndex.Should().Be(1); selected.Should().Be(@"\\A\top"); } @@ -73,7 +69,6 @@ public void PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection() var folders = Array.Empty(); using (var comboBox = new ComboBox()) { - // Act Action act = () => QfcItemController.PopulateAndSelectFolder( comboBox, @@ -81,7 +76,6 @@ public void PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection() predeterminedFolder: null ); - // Assert act.Should().Throw(); } } @@ -182,10 +176,8 @@ public void LoadFolderHandler_WhenVarListNull_InvokesFactoryWithItemHelperAndFro }; SetPrivate(controller, "_folderPredictorFactory", factory); - // Act controller.LoadFolderHandler(); - // Assert capturedGlobals.Should().BeSameAs(globals); capturedObjItem.Should().BeSameAs(helper); capturedOptions.Should().Be(FolderPredictor.InitOptions.FromField); @@ -221,10 +213,8 @@ public void LoadFolderHandler_WhenVarListProvided_InvokesFactoryWithArrayOrStrin }; SetPrivate(controller, "_folderPredictorFactory", factory); - // Act controller.LoadFolderHandler(varList); - // Assert capturedGlobals.Should().BeSameAs(globals); capturedObjItem.Should().BeSameAs(varList); capturedOptions.Should().Be(FolderPredictor.InitOptions.FromArrayOrString); @@ -262,10 +252,8 @@ public async Task LoadFolderHandlerAsync_WhenVarListNull_InvokesFactoryWithExpec }; SetPrivate(controller, "_folderPredictorFactory", factory); - // Act Func act = () => controller.LoadFolderHandlerAsync(CancellationToken.None); - // Assert await act.Should().ThrowAsync(); capturedGlobals.Should().BeSameAs(globals); capturedObjItem.Should().BeSameAs(helper); @@ -297,11 +285,9 @@ public async Task LoadFolderHandlerAsync_WhenVarListProvided_InvokesFactoryWithA }; SetPrivate(controller, "_folderPredictorFactory", factory); - // Act Func act = () => controller.LoadFolderHandlerAsync(CancellationToken.None, varList); - // Assert await act.Should().ThrowAsync(); capturedGlobals.Should().BeSameAs(globals); capturedObjItem.Should().BeSameAs(varList); @@ -328,10 +314,8 @@ public async Task LoadFolderHandlerAsync_WhenPrimaryFactoryThrowsArgumentNull_In SetPrivate(controller, "_folderPredictorFactory", primaryFactory); SetPrivate(controller, "_folderPredictorEmptyFactory", emptyFactory); - // Act Func act = () => controller.LoadFolderHandlerAsync(CancellationToken.None); - // Assert await act.Should().NotThrowAsync(); QfcItemControllerTestSupport .GetField(controller, "_folderHandler") @@ -360,13 +344,35 @@ public void PopulateFolderComboBox_WhenFactorySucceeds_LoadsHandlerAndAssignsCom > factory = (g, o, opt) => returned; SetPrivate(controller, "_folderPredictorFactory", factory); - // Act controller.PopulateFolderComboBox(); - // Assert viewer.Verify(v => v.SetFolderItems(It.IsAny()), Times.Once()); } + [TestMethod] + public void PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke() + { + // InvokeRequired true: PopulateFolderComboBox marshals via Invoke, not the else branch. + var viewer = new Mock(); + viewer.SetupGet(v => v.InvokeRequired).Returns(true); + var controller = new FolderController(); + SetPrivate(controller, "_itemViewer", viewer.Object); + SetPrivate(controller, "_globals", new Mock().Object); + controller.ItemHelper = new MailItemHelper(); + var returned = BuildFolderHandlerWithArray(@"\\A\one"); + Func< + IApplicationGlobals, + object, + FolderPredictor.InitOptions, + FolderPredictor + > factory = (g, o, opt) => returned; + SetPrivate(controller, "_folderPredictorFactory", factory); + + controller.PopulateFolderComboBox(); + + viewer.Verify(v => v.Invoke(It.IsAny()), Times.Once()); + } + [TestMethod] public async Task PopulateFolderComboBoxAsync_WhenFactorySucceeds_DispatchesAssignFolderComboBoxThroughViewerDispatcher() { @@ -396,10 +402,8 @@ public async Task PopulateFolderComboBoxAsync_WhenFactorySucceeds_DispatchesAssi > factory = (g, o, opt) => returned; SetPrivate(controller, "_folderPredictorFactory", factory); - // Act await controller.PopulateFolderComboBoxAsync(CancellationToken.None, varList); - // Assert viewer.Verify(v => v.SetFolderItems(It.IsAny()), Times.Once()); } finally @@ -424,10 +428,8 @@ public void AssignFolderComboBox_WhenNoPredeterminedFolder_SelectsTopSuggestionV BuildFolderHandlerWithArray(@"\\A\header", @"\\A\top", @"\\A\second") ); - // Act controller.AssignFolderComboBox(); - // Assert mock.Verify(v => v.SetFolderItems(It.IsAny()), Times.Once()); mock.Verify(v => v.SetFolderSelectedIndex(1), Times.Once()); mock.Verify(v => v.SetFolderSelectedItem(It.IsAny()), Times.Never()); @@ -452,10 +454,8 @@ public void AssignFolderComboBox_WhenPredeterminedFolderPresent_PreselectsThatFo BuildFolderHandlerWithArray(@"\\A\header", @"\\A\top", @"\\A\chosen") ); - // Act controller.AssignFolderComboBox(); - // Assert mock.Verify(v => v.SetFolderSelectedItem(@"\\A\chosen"), Times.Once()); mock.Verify(v => v.SetFolderSelectedIndex(It.IsAny()), Times.Never()); controller.SelectedFolder.Should().Be(@"\\A\chosen"); @@ -471,10 +471,8 @@ public void AssignFolderComboBox_WhenFolderHandlerNull_DoesNotTouchViewer() var controller = new FolderController(); SetPrivate(controller, "_itemViewer", mock.Object); - // Act controller.AssignFolderComboBox(); - // Assert mock.Verify(v => v.SetFolderItems(It.IsAny()), Times.Never()); mock.Verify(v => v.SetFolderSelectedIndex(It.IsAny()), Times.Never()); } diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T18-00.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T18-00.md new file mode 100644 index 00000000..69b76544 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T18-00.md @@ -0,0 +1,50 @@ +# Code Review — folder-combobox-fallback-index-out-of-range (Issue #392) + +- Timestamp: 2026-07-20T18-00 +- Reviewer: feature-review (initial audit) +- Range: `bd43572498474be89d80e1f9620dffb132ade377..8f34f8ef45d188f02ea19caef3c6e2b610f1a4ab` +- Files reviewed: `QuickFiler/Controllers/QfcItemController.FolderHandling.cs`, + `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` + +## Executive Summary + +The change fixes Issue #392 with a minimal, targeted two-line clamp at each of the two pre-existing +fallback-selection call sites (`AssignFolderComboBox()` and the static `PopulateAndSelectFolder` +helper): when exactly one folder suggestion is available and no predetermined folder matches, index 0 +is selected instead of the previously-hardcoded index 1, which threw `ArgumentOutOfRangeException` via +`BreadcrumbStateModel.SelectRow`. The fix is scoped tightly to the two named files, matches the +existing code style, and is backed by two new MSTest/Moq/FluentAssertions regression tests plus +re-verification of six pre-existing tests covering multi-suggestion and predetermined-match behavior. +No blocking findings. Two low-severity findings (duplication, file-size headroom) and one +informational note (a pre-existing, unchanged, and now explicitly test-documented gap in the unused +static helper's empty-array handling) are recorded below. + +## Findings Table + +| Severity | File | Location | Finding | Recommendation | Rationale | Evidence | +|---|---|---|---|---|---|---| +| Low | QuickFiler/Controllers/QfcItemController.FolderHandling.cs | Lines 202-204 and 230-231 | The fallback clamp ` == 1 ? 0 : 1` is duplicated verbatim across `AssignFolderComboBox()` and `PopulateAndSelectFolder(...)` rather than factored into one shared helper. | Extract a small private static helper, e.g. `ResolveFallbackIndex(int suggestionCount) => suggestionCount == 1 ? 0 : 1;`, and call it from both sites. | General Code Change Policy §Reusability: "Factor out logic that is clearly reusable into small methods or pure functions. Avoid copy-paste." The duplication is currently harmless (2 lines, identical semantics) but is exactly the kind of copy-paste the policy asks to avoid, and a future change to the fallback rule (e.g. clamping for 0 suggestions too) would need to be applied in two places. | `git diff bd435724..8f34f8ef -- QuickFiler/Controllers/QfcItemController.FolderHandling.cs` | +| Low | QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs | Whole file | The test file grew from 480 to exactly 500 lines, landing precisely at the repository's 500-line file-size limit with zero headroom. | Consider splitting `PopulateAndSelectFolder` and `AssignFolderComboBox` test clusters into two files (mirroring the production file's two-method structure) before the next test addition, rather than waiting for a future PR to hit the limit. | General Code Change Policy §Module & File Structure: "Do not exceed 500 lines for any one file." The file does not exceed the limit today, but is at the boundary with no margin. | `wc -l QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` -> 500 | +| Info | QuickFiler/Controllers/QfcItemController.FolderHandling.cs | Line 231 (`PopulateAndSelectFolder`) | The static helper's ternary only special-cases `Length == 1`; when `folderArray.Length == 0` it still falls to the `else` branch and selects index 1, which a WinForms `ComboBox` with zero items rejects. This is unchanged, pre-existing behavior (not introduced or worsened by this branch) and currently unreachable in production, since the sole production caller (`AssignFolderComboBox`) guards with `_folderHandler?.FolderArray?.Length > 0` before calling the equivalent inline logic, and `PopulateAndSelectFolder` itself has no other production caller in this repository. The new test `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection` explicitly documents this residual throw rather than silently leaving it undiscovered. | No action required for this fix (out of the minor-audit Scope-Lock and AC-4's literal text, which only requires the single-suggestion case to be bounds-safe). If `PopulateAndSelectFolder` ever gains a production caller that does not pre-guard against an empty array, extend the fallback clamp to also special-case `Length == 0` (e.g., return `-1`/no-op) at that time. | General Code Change Policy §Design Principles (fail-fast) does not apply retroactively to unrelated pre-existing gaps outside the Scope-Lock; documented here for future-maintainer awareness only. | `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs:69-86` (test `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection`, asserting the throw) | + +## Positive Observations + +- The fix correctly reuses the pre-existing `SetPrivate` test helper (reflection-based private-field + injection) in `AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero` + instead of re-inlining the `BindingFlags` reflection call that a sibling test used before this + change — this is itself a small deduplication improvement over the pre-existing test style. +- Both new tests follow Arrange-Act-Assert with clear, descriptive names and inline comments that + reference the issue number (`#392`), satisfying the "Document Intent" requirement of the General + Unit Test Policy. +- No production caller, public API signature, or COM/Outlook Interop boundary was touched; the change + is confined to a pure conditional-index calculation, consistent with the Bugfix Workflow's "minimal, + targeted fix" requirement. +- The fix does not introduce any new nullable-reference-type surface, and the executor's nullable-gate + evidence confirms zero first-party nullable diagnostics attributable to either changed file. + +## Scope Confirmation + +Findings above are scoped to the full feature-vs-base diff +(`bd43572498474be89d80e1f9620dffb132ade377..8f34f8ef45d188f02ea19caef3c6e2b610f1a4ab`), not to any +plan/task/phase subset. The only two non-Markdown files in this diff are the production and test file +reviewed above; no other source file (any language) was changed. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/remediation-cycle1-note.2026-07-20T18-48.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/remediation-cycle1-note.2026-07-20T18-48.md new file mode 100644 index 00000000..e5329eb7 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/remediation-cycle1-note.2026-07-20T18-48.md @@ -0,0 +1,37 @@ +Timestamp: 2026-07-20T18-48 + +## Remediation Cycle 1 Closure Note for Issue #392 + +No AC checkbox changes were made in this remediation cycle — all five acceptance criteria (AC-1 +through AC-5) were already `[x]` checked prior to this cycle and remain unaffected (this is a +coverage-only remediation cycle triggered by `quality-tiers.md`'s uniform 75% branch-coverage floor, +not by any AC gap). + +**R1 (class-level branch-coverage closure): PASS.** `QfcItemController.FolderHandling.cs`'s +class-level branch coverage was raised from 73.81% to 76.19% (>= 75% floor) by adding one new +MSTest test, `PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke`, +exercising a previously-uncovered, pre-existing branch inside `PopulateFolderComboBox`'s +`InvokeRequired` guard clause. No production code was changed; no existing test was weakened. +Verified with zero regression: `evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md`. + +**R2 (`QuickFiler` package-wide and canonical repo-wide coverage gaps): RESOLVED AS SCOPE_CHANGE.** +Tracked in open GitHub issue #136 (*Feature: quickfiler-80-per-file-coverage*). Full disposition +rationale recorded at `evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md`. + +## Backing evidence index + +- `evidence/remediation-baseline/phase0-instructions-read.2026-07-20T18-05.md` +- `evidence/remediation-baseline/minor-audit-scope.2026-07-20T18-07.md` +- `evidence/remediation-baseline/git-baseline-state.2026-07-20T18-08.md` +- `evidence/remediation-baseline/coverage-baseline.2026-07-20T18-15.md` +- `evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md` +- `evidence/other/branch-gap-analysis.2026-07-20T18-20.md` +- `evidence/regression-testing/new-branch-test-pass.2026-07-20T18-25.md` +- `evidence/other/file-size-check.2026-07-20T18-27.md` +- `evidence/qa-gates/remediation-csharpier-final.2026-07-20T18-30.md` +- `evidence/qa-gates/remediation-analyzer-final.2026-07-20T18-32.md` +- `evidence/qa-gates/remediation-nullable-final.2026-07-20T18-35.md` +- `evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md` +- `evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md` +- `evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md` +- `evidence/qa-gates/remediation-regression-check.2026-07-20T18-46.md` diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/branch-gap-analysis.2026-07-20T18-20.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/branch-gap-analysis.2026-07-20T18-20.md new file mode 100644 index 00000000..a4db2994 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/branch-gap-analysis.2026-07-20T18-20.md @@ -0,0 +1,70 @@ +Timestamp: 2026-07-20T18-20 + +## Branch-gap analysis for `QfcItemController.FolderHandling.cs` + +Class-level branch coverage (P0-T8 re-baseline): 73.81% (branch-rate 0.7380952380952381, complexity +42). To reach >= 75%, at least one additional previously-uncovered branch condition must be +exercised (closing one branch raises the rate to approximately 76.19%, comfortably above the 75% +floor — computed as (covered+1)/total assuming a small integer denominator consistent with the +observed rate). + +### Candidate uncovered/partially-covered lines (from P0-T8's per-line Cobertura data) + +| Line | Method | hits | branch | condition-coverage | +|---|---|---|---|---| +| 36 | `LoadFolderHandler` | 1 | True | 50% (4/8) | +| 49 | `LoadFolderHandler` | 1 | True | 50% (4/8) | +| **139** | **`PopulateFolderComboBox`** | **1** | **True** | **50% (1/2)** | +| 140-142 | `PopulateFolderComboBox` | 0 | False | (unreached lines inside the `InvokeRequired` true-branch) | +| 164 | `AssignFolderComboBox` | 1 | True | 50% (1/2) | +| 165-167 | `AssignFolderComboBox` | 0 | False | (unreached lines inside the `InvokeRequired` true-branch) | +| 170 | `AssignFolderComboBox` | 1 | True | 83.33% (5/6) | + +### Selected target: line 139, `PopulateFolderComboBox` + +```csharp +public void PopulateFolderComboBox(object varList = null) +{ + LoadFolderHandler(varList); + + if (_itemViewer.InvokeRequired) // line 139 — TRUE branch never exercised (0 hits on 140-142) + { + _itemViewer.Invoke(() => AssignFolderComboBox()); // lines 140-142 + } + else + { + AssignFolderComboBox(); // line 145 — exercised by the existing + } // PopulateFolderComboBox_WhenFactorySucceeds_... test +} +``` + +### Rationale + +- **Smallest, most self-contained addition**: `PopulateFolderComboBox` is a short, already-public, + already-tested method (one existing test, + `PopulateFolderComboBox_WhenFactorySucceeds_LoadsHandlerAndAssignsComboFromViewer`, already covers + the `InvokeRequired == false` / `else` branch at line 145). Only the `InvokeRequired == true` + branch (lines 140-142) is unexercised. No new production code path is implied — this simply + exercises an existing, pre-#392, previously-untested branch. +- **No production behavior change**: the fix requires zero edits to + `QfcItemController.FolderHandling.cs`; only a new test is added. +- **Established pattern already in the same test project**: the identical + `InvokeRequired == true` / `viewer.Verify(v => v.Invoke(It.IsAny()), Times.Once())` + pattern is already used in `QfcItemController.ViewerSetupTests.cs`, + `QfcItemController.ConversationTests.cs`, `QfcItemController.FocusAndThemeTests.cs`, + `QfcItemController.SeamCoreTests.cs`, and `QfcItemController.NavigationTests.cs` — this is a + well-established, low-risk seam-verification idiom in this codebase, not a novel test technique. +- **Alternative considered and rejected**: line 164 in `AssignFolderComboBox` has the identical + `InvokeRequired == true` / recursive-`Invoke`-then-`return` shape and would equally close the gap. + `PopulateFolderComboBox` was chosen because it is the simpler of the two call sites (a single + guard clause with no downstream state to additionally assert on), minimizing the size and risk of + the new test. Only one new test is authored in P1-T3 (the plan authorizes "1-2"); a second test + for line 164 is not needed to clear the 75% floor and is not added, per the plan's "smallest ... + addition" instruction. + +### Expected effect + +Exercising line 139's true branch (lines 140-142) is expected to raise the class-level branch-rate +from 73.81% to approximately 76.19% (one additional branch-condition covered out of the same +denominator), which clears the >= 75% floor with headroom. This will be confirmed numerically by +P2-T4/P2-T5/P2-T6 after the new test is authored (P1-T3) and passes (P1-T4). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/file-size-check.2026-07-20T18-27.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/file-size-check.2026-07-20T18-27.md new file mode 100644 index 00000000..d2855fd9 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/file-size-check.2026-07-20T18-27.md @@ -0,0 +1,19 @@ +Timestamp: 2026-07-20T18-27 +Command: `wc -l "QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs"` +EXIT_CODE: 0 +Output Summary: 498 lines — within the <= 500-line limit. + +## How the zero-headroom constraint was satisfied (no file split, no weakened test) + +The file entered this remediation cycle at exactly 500 lines (zero headroom, per +`policy-audit.2026-07-20T18-00.md` Section 4). Per the plan's explicit instruction, room was made by +trimming redundant inline comments — specifically, 26 bare, purely-structural `// Act` / `// Assert` +comment-header lines (which added no information beyond what the code's existing blank-line +separation and Arrange/Act/Assert ordering already communicate) were removed from 12 pre-existing +tests, reducing the file to 474 lines. No test's assertion, name, mock setup, or verified behavior +was changed by this removal (`git diff` shows only comment-line deletions for those 12 tests — see +`evidence/regression-testing/new-branch-test-pass.2026-07-20T18-25.md`). The one new test, +`PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke` (24 lines including +its `[TestMethod]` attribute and surrounding blank lines, after CSharpier formatting), was then added, +bringing the file to 498 lines after formatting. No new file was created; no existing test was +weakened, deleted, or had its assertions altered to make room. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md new file mode 100644 index 00000000..603491c9 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md @@ -0,0 +1,54 @@ +Timestamp: 2026-07-20T18-17 + +## R2 maintainer-disposition record (documentation only; no code change) + +This artifact records, verbatim, the disposition already made by the orchestrator for the R2 items +identified in `remediation-inputs.2026-07-20T18-00.md`: + +**Disposition: SCOPE_CHANGE** + +The following two coverage gaps are dispositioned as `SCOPE_CHANGE`, not fixed within this +remediation cycle: + +1. `QuickFiler` package-wide coverage: 73.68% line / 64.62% branch (floor 85%/75%). +2. Canonical repo-wide artifact (`artifacts/csharp/coverage.xml`): 16.25% line / 13.60% branch (raw + six-package aggregate, distorted because only `QuickFiler.Test` ran in this local collection). + +**Citations backing this disposition:** + +(a) The `human_interaction` `scope_change` entry recorded by the orchestrator in +`orchestrator-state` (session-local, gitignored checkpoint), which names this exact gap and cites +open GitHub issue #136 (*Feature: quickfiler-80-per-file-coverage*) as the tracking vehicle for +closing the `QuickFiler` package-wide floor. This remediation cycle does not reopen or duplicate +that tracked work. + +(b) The `#328` `StoreWrapper` branch-floor exception precedent: in that prior cycle, a maintainer +ratified a similar pre-existing, broad, unrelated-to-the-immediate-fix branch-coverage shortfall as +an explicit, documented exception rather than requiring the fix's Scope-Lock to expand to +remediate the entire class/assembly. This remediation cycle's R2 disposition follows the same +ratification pattern: a narrow bug fix's Scope-Lock is not expanded to chase a pre-existing, +package-wide coverage floor unrelated to the two lines the fix touches. + +(c) CLAUDE.md's COM/VSTO testable-denominator exemption language (General Unit Test Policy → +Coverage and Scenarios → "COM/VSTO/WinForms coverage exemption"): the 80% floor applies to the +testable denominator after excluding VSTO/WinForms/Interop-bound classes that cannot be +unit-tested without a live Outlook process or a live WinForms message loop. `QuickFiler`'s +package-wide shortfall is concentrated in exactly this category of WinForms/UI surface (per +`remediation-inputs.2026-07-20T18-00.md` item 2's own characterization: "broad, pre-existing +under-coverage across the `QuickFiler` assembly's WinForms/UI surface, unrelated to the two lines +this bug fix touches"). + +(d) The true all-first-party repo-wide coverage figure is measured by the PR CI full-suite run +(which exercises `Tags.Test`, `TaskVisualization.Test`, `ToDoModel.Test`, `UtilitiesCS.Test`, and +`QuickFiler.Test` together), not by this single-project local `dotnet-coverage` collection. The +16.25%/13.60% canonical-artifact figure recorded in this cycle is a known local-collection artifact +(only `QuickFiler.Test` ran), not the repo's true PR-gate figure. This is documented, not +cherry-picked: no assembly's evidence is omitted or excluded from the local collection's scope to +inflate the percentage. + +## Effect on AC-5 + +This disposition, combined with the R1 code fix (P1-T2 through P1-T5, verified in Phase 2), fully +satisfies R2. No further R2 task exists in this plan. The `issue.md` AC-5 scope note (amended +2026-07-20 by orchestrator) remains the authoritative, checked-off record; this artifact is the +feature-folder evidence mirror of that same disposition for this remediation cycle's traceability. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-analyzer-final.2026-07-20T18-32.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-analyzer-final.2026-07-20T18-32.md new file mode 100644 index 00000000..3e7ba609 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-analyzer-final.2026-07-20T18-32.md @@ -0,0 +1,7 @@ +Timestamp: 2026-07-20T18-32 +Command: `MSBuild.exe TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true /m` +EXIT_CODE: 0 +Output Summary: Build succeeded. 0 Warning(s), 0 Error(s). Time Elapsed 00:00:01.12 (incremental +build; QuickFiler.Test/QuickFiler had already been rebuilt in P1-T4). Zero analyzer diagnostics +attributable to either Scope-Lock file (`QfcItemController.FolderHandling.cs`, +`QfcItemController.FolderHandlingTests.cs`). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md new file mode 100644 index 00000000..923710c9 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md @@ -0,0 +1,31 @@ +Timestamp: 2026-07-20T18-42 +Command: Python conversion script (same script/logic used in the original cycle's +`evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md`; scratchpad-only, not committed): +parse `remediation-final-coverage.cobertura.xml` (P2-T4's output), dedup lines per +`(sourcefile, line-number)` within each class, scope to first-party assemblies only +`{QuickFiler, UtilitiesCS, TaskMaster, TaskVisualization, ToDoModel, Tags, SVGControl}`, emit +JaCoCo-format `` `counter` elements to `artifacts/csharp/coverage.xml`. +EXIT_CODE: 0 +Output Summary: +- `artifacts/csharp/coverage.xml` regenerated in JaCoCo format. +- Report-level LINE counter totals: covered=27,072, missed=46,487 (using this run's raw totals: + covered=9,024, missed=46,487 for the reduced denominator variant reported by the script; see the + hook-equivalent verification below for the report-wide summed total) => 16.26% first-party line + coverage under the single-suite (`QuickFiler.Test` only) measurement scope. +- Report-level BRANCH counter totals => 13.61% branch coverage. +- `QuickFiler`-package-level LINE/BRANCH: line-rate 0.7371554290151417 (73.72%), branch-rate + 0.6468710089399745 (64.69%) — matches P2-T4's package-level Cobertura figures. +- `QfcItemController.FolderHandling.cs` class-level LINE/BRANCH counters (JaCoCo): + `` (95.95%), + `` (76.19%) — matches P2-T4's class-level + Cobertura figures exactly (missed+covered=42 branches, 32/42=76.19% >= 75% floor). +- Verification: `.claude/hooks/validate-feature-review-coverage.ps1`'s + `Get-JacocoRepoCoverage`/`Get-JacocoBranchCoverage` XPath logic + (`//counter[@type="LINE"]` / `//counter[@type="BRANCH"]`, summed across all matching nodes) was + independently re-run against the regenerated `artifacts/csharp/coverage.xml` and reproduced + 16.26% line / 13.61% branch, confirming the file parses correctly and the hook will read it as + expected. +- Scope note (unchanged from the original cycle, per the R2 SCOPE_CHANGE disposition): this + raw-aggregate figure under-represents the true repo-wide floor because only `QuickFiler.Test` ran + in this local collection (`TaskVisualization`, `ToDoModel`, `Tags` each report 0% here, each + covered by their own suite in PR CI). This is documented, not cherry-picked. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md new file mode 100644 index 00000000..e110733b --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md @@ -0,0 +1,29 @@ +Timestamp: 2026-07-20T18-44 + +## Coverage delta: this cycle's baseline (P0-T8) vs. final (P2-T4/P2-T5) for `QfcItemController.FolderHandling.cs` + +| Metric | P0-T8 baseline | P2-T4/P2-T5 final | Delta | +|---|---|---|---| +| Class-level line-rate | 91.89% | 95.95% | +4.06 points | +| Class-level branch-rate | 73.81% | 76.19% | +2.38 points | + +Raw JaCoCo counters (final): `` (74 total lines), +`` (42 total branches). + +## Explicit PASS/FAIL statement + +- **Class-level branch coverage >= 75%: PASS** (76.19% >= 75%, closing the 1.19-point original gap + with 1.19 points of additional headroom). +- **No regression on any previously-covered line/branch: PASS.** Method-level comparison: + - `LoadFolderHandler`: line 100% (unchanged), branch 55.56% (unchanged — not targeted by this + cycle). + - `PopulateFolderComboBox`: baseline line 70%/branch 50% -> final line 100%/branch 100% (both + improved; the new test exercises the previously-uncovered `InvokeRequired == true` branch). + - `AssignFolderComboBox`: baseline line 89.29%/branch 87.5% -> unchanged (not targeted by this + cycle; re-verified unchanged below via P2-T7). + - `PopulateAndSelectFolder`: baseline line 100%/branch 100% -> unchanged (not targeted). + - No method or line shows a decrease from the P0-T8 baseline to the P2-T4/P2-T5 final + measurement. + +Both gates read PASS. Per the plan's acceptance criterion, this cycle proceeds to P2-T7 (no restart +to Phase 1 required). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-csharpier-final.2026-07-20T18-30.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-csharpier-final.2026-07-20T18-30.md new file mode 100644 index 00000000..26f7571c --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-csharpier-final.2026-07-20T18-30.md @@ -0,0 +1,19 @@ +Timestamp: 2026-07-20T18-30 +Command: `csharpier format .` then `csharpier check .` (v1.3.0 subcommand syntax; equivalent to plan's `dotnet tool run csharpier .` / `dotnet tool run csharpier --check .`) +EXIT_CODE: 0 (both format and check runs) +Output Summary: +- `csharpier format .`: "Formatted 1406 files in 1003ms." `git status` after this run showed only + two tracked-file changes: `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` + (the Scope-Lock-authorized file, already formatted correctly from the P1-T3 edit — no further + change from this run) and `.claude/agent-memory/feature-review/MEMORY.md` (a pre-existing, + unrelated single-line addition from the prior feature-review pass, not a `.cs` file and not + touched by CSharpier). No out-of-scope `.cs`/config-file rewrite occurred this time. +- `csharpier check .`: "Checked 1406 files in 3032ms." **0 errors** — the 32 pre-existing + `app.config`/`packages.config` formatting errors documented in the original cycle's baseline + (`evidence/baseline/csharpier-baseline.2026-07-20T13-15.md`) are now resolved, confirmed + attributable to an unrelated upstream commit already on this branch's history + (`78e847ec style: apply csharpier formatting to dependabot config changes`), not to any action + taken in this remediation cycle. +- Both Scope-Lock files (`QfcItemController.FolderHandling.cs`, + `QfcItemController.FolderHandlingTests.cs`) are format-clean. No new format failure on any file + was introduced by this cycle. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-nullable-final.2026-07-20T18-35.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-nullable-final.2026-07-20T18-35.md new file mode 100644 index 00000000..2851246d --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-nullable-final.2026-07-20T18-35.md @@ -0,0 +1,28 @@ +Timestamp: 2026-07-20T18-35 +Command: `MSBuild.exe TaskMaster.sln /t:Rebuild /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true` +EXIT_CODE: 1 +Output Summary: Build FAILED. 0 Warning(s), 34 Error(s). + +## Error-set comparison against the original cycle's baseline (P0-T11 of `plan.2026-07-20T12-59.md`) + +Extracted the full set of `(,): error CS####: ` diagnostic lines from +both the original cycle's baseline log and this remediation cycle's final-run log (normalized, +deduplicated, sorted) and diffed them: + +- Original baseline error-set size: 34 unique diagnostics. +- Remediation final error-set size: 34 unique diagnostics. +- **NEW errors (final minus baseline): 0** (`comm -13` produced no output). +- **Resolved errors (baseline minus final): 0** (`comm -23` produced no output). +- The two error sets are byte-for-byte identical. + +## First-party attribution check + +Searched the final run's error output for any `error CS` line NOT attributed to +`SVGControl.csproj`: **0 matches**. All 34 errors are attributed exclusively to +`SVGControl.csproj` (vendored). No error is attributable to `QuickFiler.csproj`, +`QuickFiler.Test.csproj`, or any other first-party project. + +## Disposition (per amended AC-5 scope note and `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`) + +Acceptance = zero NEW errors relative to the original baseline AND zero errors attributable to +first-party in-scope files. Both conditions are met. This task's acceptance criterion is satisfied. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-reaudit-handoff.2026-07-20T18-50.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-reaudit-handoff.2026-07-20T18-50.md new file mode 100644 index 00000000..007f1575 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-reaudit-handoff.2026-07-20T18-50.md @@ -0,0 +1,49 @@ +Timestamp: 2026-07-20T18-50 + +## Reaudit handoff record — Issue #392, Remediation Cycle 1 + +**Executor capability note:** `atomic-executor` (this agent) does not have a subagent-delegation +tool available in its current toolset (Read, Grep, Glob, Edit, Write, Bash, and the bundled PoshQC +MCP tools only — no `Task`/agent-invocation tool). This task's deliverable is therefore the +handoff evidence artifact below, recording that a `feature-review` reaudit is required and +precisely what it must independently re-verify. Actually invoking `feature-review` is an +orchestrator-level action that must follow this executor's completion, not an action this executor +can perform itself. + +### Reaudit scope required + +`feature-review` must write `policy-audit..md`, `code-review..md`, and +`feature-audit..md` (and, if any finding remains, a new `remediation-inputs..md` +opening cycle 2) using an exit timestamp distinct from this cycle's entry timestamp +(`2026-07-20T18-00`). The reaudit must independently re-verify: + +(a) **The class-level branch-coverage floor is met (P2-T6).** `QfcItemController.FolderHandling.cs` +class-level branch coverage is now 76.19% (>= 75% floor), up from 73.81% at this cycle's entry. +Backing evidence: `evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md`. + +(b) **The P1-T1 R2 maintainer-disposition record is present and adequate.** The `QuickFiler` +package-wide (73.72%/64.69%) and canonical repo-wide artifact (16.26%/13.61%) coverage gaps remain +below the 85%/75% floor and are dispositioned `SCOPE_CHANGE`, tracked in open GitHub issue #136. +Backing evidence: `evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md`. + +(c) **All five original acceptance criteria (AC-1 through AC-5) remain PASS with no regression +introduced by this remediation cycle's own changes (P2-T7).** No AC checkbox was changed in this +cycle (all were already `[x]` at entry); 542/542 tests pass (up from 541, zero regressions). +Backing evidence: `evidence/qa-gates/remediation-regression-check.2026-07-20T18-46.md`, +`evidence/issue-updates/remediation-cycle1-note.2026-07-20T18-48.md`. + +### Full evidence index for this cycle + +See `evidence/issue-updates/remediation-cycle1-note.2026-07-20T18-48.md` for the complete backing +artifact list. + +### Toolchain state at handoff + +- CSharpier: clean (Scope-Lock files). `evidence/qa-gates/remediation-csharpier-final.2026-07-20T18-30.md` +- .NET analyzers: EXIT_CODE 0, 0 errors. `evidence/qa-gates/remediation-analyzer-final.2026-07-20T18-32.md` +- Nullable: EXIT_CODE 1 overall, but 0 new errors / 0 first-party errors vs. the original baseline + (34/34 identical, confined to vendored `SVGControl.csproj`). + `evidence/qa-gates/remediation-nullable-final.2026-07-20T18-35.md` +- Tests: 542/542 passed. `evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md` +- `artifacts/csharp/coverage.xml`: regenerated, JaCoCo format, parses correctly. + `evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md` diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-regression-check.2026-07-20T18-46.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-regression-check.2026-07-20T18-46.md new file mode 100644 index 00000000..14de4a18 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-regression-check.2026-07-20T18-46.md @@ -0,0 +1,18 @@ +Timestamp: 2026-07-20T18-46 +Command: `comm -23 ` +EXIT_CODE: 0 +Output Summary: Original cycle reference set: 541 passed test names, 0 failed (sourced from the +original cycle's Phase 2 final coverage run, `evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md` +— this is the most recent full-suite pass-count/name set prior to this remediation cycle; the +plan's P2-T7 acceptance text cites "541 tests" for this comparison, which matches this evidence +file's count precisely, though the plan's filename reference (`vstest-coverage-baseline..md`) +literally names the original cycle's Phase 0 baseline artifact, which recorded 539 tests. This +executor used the 541-test set explicitly named by the plan's own acceptance text as the +comparison reference, since it is both the correct "no regression from the most recent verified +state" reference and the count the plan itself cites). + +Remediation cycle final (P2-T4): 542 passed, 0 failed (541 + 1 new test from P1-T3). + +The set-difference command produced zero output, confirming every one of the 541 original-cycle +test names is present in this remediation cycle's 542-name passed set. Total pass count increased +from 541 to 542 (did not decrease). No test regressed. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md new file mode 100644 index 00000000..c429ce5b --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md @@ -0,0 +1,13 @@ +Timestamp: 2026-07-20T18-40 +Command: `dotnet-coverage collect -f cobertura -s coverage-exclude-deedle.xml -o remediation-final-coverage.cobertura.xml -- vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation` +EXIT_CODE: 0 +Output Summary: +- Total tests: 542. Passed: 542. Failed: 0. Total time: 7.7651 seconds. (542 = 541 original-cycle + baseline + 1 new regression test from P1-T3.) +- Class-level coverage for `QuickFiler.Controllers.QfcItemController` sourced from + `QfcItemController.FolderHandling.cs`: **line-rate 0.9594594594594594 (95.95%), branch-rate + 0.7619047619047619 (76.19%)**. Baseline (P0-T8, this cycle): 91.89%/73.81%. Both line and branch + coverage improved; branch coverage now clears the >= 75% floor. +- `QuickFiler` package: line-rate 0.7371554290151417 (73.72%), branch-rate 0.6468710089399745 + (64.69%). Baseline: 73.68%/64.62% — virtually unchanged (consistent with the R2 SCOPE_CHANGE + disposition; this cycle did not attempt to close the package-wide gap). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/new-branch-test-pass.2026-07-20T18-25.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/new-branch-test-pass.2026-07-20T18-25.md new file mode 100644 index 00000000..ac48e168 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/new-branch-test-pass.2026-07-20T18-25.md @@ -0,0 +1,11 @@ +Timestamp: 2026-07-20T18-25 +Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation /TestCaseFilter:"FullyQualifiedName~PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke"` +(preceded by `MSBuild.exe TaskMaster.sln /t:QuickFiler_Test /p:Configuration=Debug /p:Platform="Any CPU"`, EXIT_CODE 0, 1 Warning(s), 0 Error(s)) +EXIT_CODE: 0 +Output Summary: Total tests: 1. Passed: 1. Failed: 0. Total time: 1.2643 seconds. The new test +`PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke` passes, confirming +`_itemViewer.Invoke(It.IsAny())` is called exactly once when `InvokeRequired` is `true`, +exercising the previously-untested true-branch of `PopulateFolderComboBox`'s guard clause (line +139/140-142 in `QfcItemController.FolderHandling.cs`). No existing test's assertions, names, or +behavior were modified in this change (see `git diff` — only comment-line removals and this one new +test method were added). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/coverage-baseline.2026-07-20T18-15.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/coverage-baseline.2026-07-20T18-15.md new file mode 100644 index 00000000..b5d9d685 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/coverage-baseline.2026-07-20T18-15.md @@ -0,0 +1,32 @@ +Timestamp: 2026-07-20T18-15 +Command: `dotnet-coverage collect -f cobertura -s coverage-exclude-deedle.xml -o remediation-baseline-coverage.cobertura.xml -- vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation` +(preceded by `MSBuild.exe TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, EXIT_CODE 0, 75 Warning(s), 0 Error(s), after resolving a fresh-session package-restore gap — see note below) +EXIT_CODE: 0 +Output Summary: +- Total tests: 541. Passed: 541. Failed: 0. Total time: 7.7568 seconds. +- Class-level coverage for `QuickFiler.Controllers.QfcItemController` sourced from + `QfcItemController.FolderHandling.cs`: **line-rate 0.918918918918919 (91.89%), branch-rate + 0.7380952380952381 (73.81%)** — matches the expected starting point from + `policy-audit.2026-07-20T18-00.md` Section 5.2 exactly (re-measured, not assumed). +- Method-level breakdown: `LoadFolderHandler` line 100%/branch 55.56%; `PopulateFolderComboBox` + line 70%/branch 50%; `AssignFolderComboBox` line 89.29%/branch 87.5%; `PopulateAndSelectFolder` + line 100%/branch 100%. + +## Pre-collection environment note (recorded for auditability) + +This is a fresh session turn; the local NuGet `packages/` folder (gitignored) needed +re-verification before the Debug build succeeded. Beyond the three analyzer packages and `log4net` +already documented in the original cycle's baseline evidence, a systematic scan (comparing every +`packages\\` path referenced by any `.csproj` file against what is actually present under +`packages/`) found 15 additional stale-referenced package versions missing: +`AngleSharp.1.4.0`, `Castle.Core.5.1.1`, `FluentAssertions.6.12.0`, `MSTest.TestAdapter.3.1.1`, +`MSTest.TestFramework.3.1.1`, `Microsoft.Extensions.TimeProvider.Testing.9.10.0`, +`Microsoft.Graph.5.105.0`, `Moq.4.20.69`, `OpenTelemetry.PersistentStorage.Abstractions.1.1.0`, +`OpenTelemetry.PersistentStorage.FileSystem.1.1.0`, `Std.UriTemplate.2.0.8`, +`System.Reactive.6.1.0`, `System.Runtime.CompilerServices.Unsafe.6.0.0`, +`System.Threading.Tasks.Extensions.4.5.4`, `altcover.8.6.45`. All 15 were installed via +`nuget.exe install -Version -OutputDirectory packages` without editing any +`.csproj`/`.config` file — the same non-project-file-modifying workaround pattern documented in the +original cycle's `evidence/baseline/analyzer-baseline.2026-07-20T13-25.md`. This is a pre-existing, +out-of-scope, repo-wide packages.config/csproj-hint-path drift from the `1e5ada71 (chore): update +packages` commit, unrelated to this remediation cycle's Scope-Lock. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/git-baseline-state.2026-07-20T18-08.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/git-baseline-state.2026-07-20T18-08.md new file mode 100644 index 00000000..8135f2fd --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/git-baseline-state.2026-07-20T18-08.md @@ -0,0 +1,6 @@ +Timestamp: 2026-07-20T18-08 +Command: git rev-parse --abbrev-ref HEAD && git rev-parse --short HEAD +EXIT_CODE: 0 +Output Summary: Branch is `bug/folder-combobox-fallback-index-out-of-range-392`; HEAD short SHA is +`8f34f8ef` (the original cycle's fix, commit "fix(folder-selection): make fallback folder selection +index bounds-safe", is now on HEAD; this remediation cycle builds on top of it). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/minor-audit-scope.2026-07-20T18-07.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/minor-audit-scope.2026-07-20T18-07.md new file mode 100644 index 00000000..40d58240 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/minor-audit-scope.2026-07-20T18-07.md @@ -0,0 +1,29 @@ +Timestamp: 2026-07-20T18-07 + +## Verification of minor-audit Scope-Lock boundary for remediation cycle 1 (issue #392) + +Directory listing of `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/` +confirms the following `.md` files exist: `issue.md`, `plan.2026-07-20T12-59.md`, +`policy-audit.2026-07-20T18-00.md`, `code-review.2026-07-20T18-00.md`, +`feature-audit.2026-07-20T18-00.md`, `remediation-inputs.2026-07-20T18-00.md`, +`remediation-plan.2026-07-20T18-00.md`. **No `spec.md` and no `user-story.md`** are present — +confirms `issue.md` remains the sole AC source for this minor-audit cycle, unchanged from the +original cycle. + +`issue.md`'s `Work Mode: minor-audit` marker (line 12) and its `## Acceptance Criteria` section +(AC-1 through AC-5) are unchanged from the original cycle; all five ACs are already `- [x]` checked +and are not reopened by this remediation cycle (per the remediation plan's Work Mode note). + +Scope-Lock file list for this remediation cycle (unchanged from the original fix scope): +- `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` (R1: coverage-only addition, no new + production code path, no behavior change) +- `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` (R1: 1-2 new `[TestMethod]` + additions only, no new file) + +No other production file may be changed by this cycle. This matches the remediation plan's +Scope-Lock section verbatim. + +Command: `ls docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/*.md` +EXIT_CODE: 0 +Output Summary: 7 markdown files present in the feature folder; neither `spec.md` nor `user-story.md` +among them. Scope-Lock boundary confirmed unchanged from the original cycle. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/phase0-instructions-read.2026-07-20T18-05.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/phase0-instructions-read.2026-07-20T18-05.md new file mode 100644 index 00000000..98d07db6 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/phase0-instructions-read.2026-07-20T18-05.md @@ -0,0 +1,110 @@ +Timestamp: 2026-07-20T18-05 + +Policy Order: CLAUDE.md (all sections) → General Code Change Policy → General Unit Test Policy → C# Code Change Policy → C# Unit Test Policy → quality-tiers.md (uniform coverage floor) + +Files read (in order): +1. `CLAUDE.md` +2. `.claude/rules/general-code-change.md` +3. `.claude/rules/general-unit-test.md` +4. `.claude/rules/csharp.md` and `.claude/rules/quality-tiers.md` + +## CLAUDE.md — Policy Compliance Order (quoted verbatim) + +``` +## Policy Compliance Order + +The four core policies below are embedded directly in this file and apply to every session without requiring explicit skill loads. Apply them in this order: + +1. This file (CLAUDE.md) — all sections +2. General Code Change Policy (§ below) +3. General Unit Test Policy (§ below) +4. For C#: C# Code Change Policy (§ below) and C# Unit Test Policy (§ below) +``` + +## general-code-change.md — Mandatory Toolchain Loop / File Size Limit (quoted verbatim) + +``` +## Mandatory Toolchain Loop + +Run the full seven-stage toolchain in this exact order and repeat until all stages pass in a single pass: + +1. **Formatting** (e.g., Black, Prettier, CSharpier, Invoke-Formatter) +2. **Linting** (e.g., Ruff, ESLint, PSScriptAnalyzer, .NET analyzers) +3. **Type checking** (e.g., Pyright, TSC, nullable analysis; skip for PowerShell) +4. **Architecture-boundary tests** (e.g., dependency-cruiser, NetArchTest.Rules) +5. **Unit tests** (e.g., Pytest, Vitest, MSTest, Pester) including property-based tests where applicable per `quality-tiers.md` +6. **Contract / schema compatibility checks** (e.g., oasdiff, schema-snapshot diff) +7. **Integration tests** + +**Restart from step 1** if any stage fails or auto-fixes any files. Do not stop the loop until all seven stages complete without errors in a single pass. + +Mutation testing and golden tests run in pre-merge or nightly pipelines, not the per-commit loop. +``` + +``` +## File Size Limit + +- No production code, test code, or reusable script file may exceed **500 lines**. +- Exceptions: temporary throwaway scripts created and deleted within an agent session; raw text fixtures for language-processing test data; Markdown documentation files. +``` + +## general-unit-test.md — Coverage Requirements (quoted verbatim) + +``` +## Coverage Requirements + +- **Line coverage must remain >= 85% across all tiers (T1–T4).** +- **Branch coverage must remain >= 75% across all tiers (T1–T4).** +- Code changes or refactors must not reduce coverage for the lines that were changed. +- Tier-specific lower coverage thresholds are not used in this repository. See `.claude/rules/quality-tiers.md` for the full tier system. +- Coverage is a supporting metric, not the sole quality gate. Untested critical behavior is not acceptable even if the overall percentage looks good. +- Configure coverage tooling to exclude test files (e.g., `tests/`) so metrics reflect application code, not tests. +- Type-only / interface-only modules with no executable behavior may be omitted from coverage measurement. Examples: Python `Protocol`-only modules consumed only under `TYPE_CHECKING`, TypeScript interface/type-only files, and C# interface-only files. Such modules legitimately report 0% executable coverage and may be excluded from measurement. This is a clarification only; it does not lower any coverage threshold. +``` + +## csharp.md — Toolchain and Testing Standards (quoted verbatim) + +``` +## Toolchain + +1. **Formatting — CSharpier**: All C# source files must be formatted with CSharpier. Do not use `dotnet format`. Command: `dotnet tool run csharpier .` or `csharpier .` +2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Command: `msbuild .sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` +3. **Type Checking — Nullable Analysis**: Enable nullable reference types and fail on warnings. Command: `msbuild .sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true` +4. **Testing — MSTest + Moq + FluentAssertions**: Run tests with: `vstest.console.exe /EnableCodeCoverage` + +Run the toolchain in order: format → lint → type-check → test. Restart from step 1 if any step fails or changes files. +``` + +``` +## Testing Standards + +- Use **MSTest** (`Microsoft.VisualStudio.TestTools.UnitTesting`) as the test framework. +- Use **Moq** for mocking. +- Prefer **FluentAssertions** for assertions; use MSTest `Assert` only when FluentAssertions is not practical. +- Use `[TestClass]` and `[TestMethod]` attributes. +- Follow Arrange–Act–Assert structure. +- No external dependencies in unit tests. +- Repository-wide line coverage must remain >= 80%. +- Any new module, class, or method must reach >= 90% coverage. +- Coverage regression on changed lines is a blocking finding. +``` + +## quality-tiers.md — Uniform-vs-Tier-Dependent Gate Matrix (quoted verbatim) + +``` +## Uniform-vs-Tier-Dependent Gate Matrix + +Per Authoritative Decision #2, line and branch coverage thresholds are uniform across all tiers. Other gates remain tier-dependent. + +### Uniform across all tiers (T1–T4) + +- Format check: 100% pass. +- Lint errors: 0. +- Type errors: 0. +- Architecture violations: 0. +- Line coverage: >= 85%. +- Branch coverage: >= 75%. +- No regression on changed lines. +``` + +Note: this remediation cycle is triggered specifically by the `quality-tiers.md` uniform 75% branch-coverage floor (`QfcItemController.FolderHandling.cs` class-level branch coverage measured at 73.81% in the original cycle's final QC), reconciled against CLAUDE.md's separate 80%/90% repo-wide/new-code figures per the plan's stated scope (R1 targets the class-level branch floor specifically). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T18-00.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T18-00.md new file mode 100644 index 00000000..9367d430 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T18-00.md @@ -0,0 +1,79 @@ +# Feature Audit — folder-combobox-fallback-index-out-of-range (Issue #392) + +- Timestamp: 2026-07-20T18-00 +- Reviewer: feature-review (initial audit) +- Work Mode: `minor-audit` + +## Scope and Baseline + +- Base branch (resolved): `main` @ `bd43572498474be89d80e1f9620dffb132ade377`. +- Head: `8f34f8ef45d188f02ea19caef3c6e2b610f1a4ab`. +- Audit scope: the full branch diff vs the merge-base (feature-vs-base), not any plan/task/phase + subset. Confirmed via `git diff --numstat bd435724..8f34f8ef`: 2 changed `.cs` files (1 production, + 1 test), 29 added Markdown files (plan, issue, evidence). +- Acceptance-criteria source (per `minor-audit` marker in `issue.md`): the explicit + `## Acceptance Criteria` section of `issue.md` only (AC-1 through AC-5). No `spec.md` or + `user-story.md` exist in this feature folder, consistent with `minor-audit` mode. +- Evidence: production/test diffs read directly (`git diff bd435724..8f34f8ef -- `); executor + QA-gate and regression-testing evidence under `evidence/qa-gates/` and `evidence/regression-testing/` + read as pre-existing (not re-run); canonical C# coverage artifact `artifacts/csharp/coverage.xml` + parsed directly. + +## Acceptance Criteria Inventory + +From `issue.md` `## Acceptance Criteria`: + +- AC-1: A deterministic MSTest regression test reproduces the defect (fallback selection with exactly + one folder suggestion) and fails before the fix; the same test passes after the fix. No temporary + files or external dependencies are used. +- AC-2: `QfcItemController.AssignFolderComboBox` no longer throws `ArgumentOutOfRangeException` when + `FolderArray` has exactly one entry and no predetermined folder matches: it selects index 0 (the + only suggestion) instead of index 1. +- AC-3: Existing multi-suggestion behavior is preserved: with two or more suggestions and no + predetermined match, index 1 remains selected; with a predetermined folder present in the list, that + folder remains preselected. +- AC-4: The retained static helper `PopulateAndSelectFolder` applies the same bounds-safe fallback so a + single-item combo box does not throw. +- AC-5: The full C# toolchain passes in order (CSharpier format, .NET analyzers build, nullable build, + MSTest via vstest.console.exe) with zero regressions relative to the Phase 0 baseline, and + new/changed code meets the >= 90% coverage target. Scope note (amended 2026-07-20 by orchestrator, + before feature review): nullable enforcement is scoped to first-party projects per + `.claude/rules/csharp.md`; the 34 pre-existing nullable errors in vendored `SVGControl.csproj` are + byte-identical to the Phase 0 baseline, not enforced by CI, tracked separately, and do not gate this + bug fix. + +## Acceptance Criteria Evaluation + +| AC | Verdict | Evidence | +|---|---|---| +| AC-1 | PASS | `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md` (targeted run of the two new tests, EXIT_CODE 1, expected-fail tagged) and `pass-after-392.2026-07-20T14-10.md` (same targeted run after the fix, EXIT_CODE 0). No temp files or external dependencies used (grep of the changed test file confirms no `Path.GetTemp*`, no network/DB calls). | +| AC-2 | PASS | `git diff bd435724..8f34f8ef -- QuickFiler/Controllers/QfcItemController.FolderHandling.cs` shows `_itemViewer.SetFolderSelectedIndex(_folderHandler.FolderArray.Length == 1 ? 0 : 1)` replacing the hardcoded `SetFolderSelectedIndex(1)`. Verified by new test `AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero`, which asserts `mock.Verify(v => v.SetFolderSelectedIndex(0), Times.Once())` and `mock.Verify(v => v.SetFolderSelectedIndex(1), Times.Never())`. | +| AC-3 | PASS | `evidence/regression-testing/targeted-no-regression-392.2026-07-20T14-13.md` (EXIT_CODE 0) re-runs six pre-existing tests covering exact predetermined match, all-missing-predetermined (index-1 fallback with 2+ suggestions), empty array, no-predetermined-folder, predetermined-folder-present, and null-folder-handler paths; all pass unchanged. | +| AC-4 | PASS | `git diff` shows `PopulateAndSelectFolder`'s `comboBox.SelectedIndex = predeterminedIndex >= 0 ? predeterminedIndex : (folderArray.Length == 1 ? 0 : 1)`. New test `PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing` asserts no `ArgumentOutOfRangeException`, `SelectedIndex == 0`, and the correct selected item text. | +| AC-5 | PASS (on its literal terms; see coverage caveat below) | Toolchain: format PASS, analyzers PASS (0 errors), tests PASS (541/541, 0 regressions per `evidence/qa-gates/regression-check-392.2026-07-20T14-42.md` set-difference check), nullable build reproduces the byte-identical pre-existing 34-error vendored-`SVGControl.csproj` condition (0 new, 0 first-party errors per `evidence/qa-gates/nullable-final-392.2026-07-20T15-10.md`), matching the amended scope note. New/changed-code coverage: 100% line coverage on all 5 reported Cobertura sequence points for the changed lines (`evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md`), exceeding the >= 90% target. **Caveat**: AC-5's text does not itself require repo-wide or package-level coverage floors; the separate, broader repository coverage policy (uniform 85%/75% floor per `.claude/rules/quality-tiers.md`) is evaluated independently in `policy-audit.2026-07-20T18-00.md` Section 5, where it is FAIL at the package and repo-wide scopes (pre-existing, not caused by this fix). That finding does not fail AC-5 itself but is tracked as a separate policy-audit remediation item. | + +## Summary + +All five acceptance criteria in `issue.md` are met on their literal terms, backed by concrete +evidence: two new regression tests demonstrably fail before and pass after the fix, the fix is applied +identically at both fallback-selection call sites, existing multi-suggestion and predetermined-match +behavior is unchanged, and the full C# toolchain passes with zero regressions relative to baseline +(the nullable gate's literal EXIT_CODE remains 1, but is confirmed byte-identical to a pre-existing, +out-of-scope, vendored condition per the AC-5 scope note). The residual open item is a +policy-level (not AC-level) coverage-floor gap at the `QuickFiler` package and canonical repo-wide +scopes, confirmed pre-existing and unrelated to this fix, routed to remediation via +`remediation-inputs.2026-07-20T18-00.md`. + +### Acceptance Criteria Status +- Source: `issue.md` (`## Acceptance Criteria`, AC-1 through AC-5) +- Total AC items: 5 +- Checked off (delivered): 5 +- Remaining (unchecked): 0 +- Items remaining: none + +## Acceptance Criteria Check-off + +All five AC items are already checked off (`[x]`) in `issue.md`, authored by the executor during plan +execution. This audit independently confirms each PASS verdict above against the cited evidence; no +checkbox text was modified and no additional check-offs were required (all were already `[x]` prior to +this review). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md index 996711bd..365dd1b5 100644 --- a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md @@ -79,6 +79,8 @@ High: the exception aborts the entire QuickFiler high-confidence load path whene - [x] AC-4: The retained static helper `PopulateAndSelectFolder` applies the same bounds-safe fallback so a single-item combo box does not throw. - [x] AC-5: The full C# toolchain passes in order (CSharpier format, .NET analyzers build, nullable build, MSTest via vstest.console.exe) with zero regressions relative to the Phase 0 baseline, and new/changed code meets the >= 90% coverage target. Scope note (amended 2026-07-20 by orchestrator, before feature review): nullable enforcement is scoped to first-party projects per `.claude/rules/csharp.md` (analyzers/nullable are wired to first-party projects only; vendored projects are excluded). The 34 pre-existing nullable errors in vendored `SVGControl.csproj` are byte-identical to the Phase 0 baseline, are not enforced by CI (MSBuild incremental skip), and are tracked separately in `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`; they do not gate this bug fix. +Remediation Cycle 1 closure note (2026-07-20, no AC checkbox changes; all five ACs above were already `[x]` before this cycle and are unaffected): R1 closed `QfcItemController.FolderHandling.cs`'s class-level branch-coverage gap (73.81% -> 76.19%, clearing the uniform >= 75% floor in `.claude/rules/quality-tiers.md`) by adding one new test exercising a previously-uncovered, pre-existing branch, with zero production-code change and zero regression (542/542 tests passing, up from 541). R2 (the `QuickFiler` package-wide and canonical repo-wide coverage gaps) is dispositioned `SCOPE_CHANGE`, tracked in open GitHub issue #136 (*Feature: quickfiler-80-per-file-coverage*). Full evidence index: `evidence/issue-updates/remediation-cycle1-note.2026-07-20T18-48.md`. + ## Next Step - [x] Promote to GitHub issue (bug-report template) diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T18-00.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T18-00.md new file mode 100644 index 00000000..0dfccc32 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T18-00.md @@ -0,0 +1,292 @@ +# Policy Compliance Audit — folder-combobox-fallback-index-out-of-range (Issue #392) + +- Timestamp: 2026-07-20T18-00 +- Reviewer: feature-review (initial audit) +- Base branch (resolved): `main` +- Merge-base SHA: `bd43572498474be89d80e1f9620dffb132ade377` +- Head SHA: `8f34f8ef45d188f02ea19caef3c6e2b610f1a4ab` +- Work Mode: `minor-audit` (AC source: explicit `## Acceptance Criteria` section of `issue.md`) +- Scope: full branch diff vs merge-base (feature-vs-base). Not narrowed to any plan, task, or phase. + +## Executive Summary + +The branch fixes Issue #392: `QfcItemController.AssignFolderComboBox` and the retained static helper +`PopulateAndSelectFolder` unconditionally selected folder-suggestion index 1, which throws +`ArgumentOutOfRangeException` (via `BreadcrumbStateModel.SelectRow`) when exactly one suggestion is +present. Both call sites now clamp the fallback to index 0 when `FolderArray.Length == 1`. Two new +MSTest regression tests were added; the executor's evidence documents fail-before / pass-after / +no-regression verification (541/541 tests passing) and 100% line coverage on the five reported +Cobertura sequence points spanning the new/changed lines. + +Overall verdict: **PARTIAL (remediation required, coverage only)**. The C# toolchain (format, +analyzers, tests) passes; the nullable gate reproduces a pre-existing, byte-identical 34-error +condition confined to vendored `SVGControl.csproj` (dispositioned, not a regression). All five +acceptance criteria are met on their literal terms. However, mandatory C# coverage checks below the +uniform 85%/75% floor (`.claude/rules/quality-tiers.md`) are **FAIL** at three scopes: the touched +class's branch rate, the `QuickFiler` package, and the canonical repo-wide artifact. All three are +confirmed pre-existing conditions (baseline figures are materially unchanged or the change is a net +improvement) and not caused or worsened by this bug fix, but they are real, unratified sub-floor +conditions that require an explicit maintainer disposition or a follow-on coverage-uplift task before +this policy area can read PASS. See Section 5. + +## 1. Scope and Baseline + +- Range audited: `bd43572498474be89d80e1f9620dffb132ade377..8f34f8ef45d188f02ea19caef3c6e2b610f1a4ab`. +- Merge-base recomputed independently: `git merge-base HEAD origin/main` = `bd43572498474be89d80e1f9620dffb132ade377` (matches the supplied base). +- Changed languages with files in the branch diff: **C# only** (2 `.cs` files: 1 production, 1 test). + No TypeScript, Python, or PowerShell files changed. 29 Markdown files (plan, issue, evidence) were + also added; these are docs/evidence, not a changed language for coverage purposes. +- Coverage enforcement is therefore mandatory for C# and only C#. + +### 1.1 PR-context summary correction (recurring C#-as-docs misclassification) + +`artifacts/pr_context.summary.txt`'s "Changed files overview" reported `Core logic changes: 0 files` +and classified all 31 changed files as docs/evidence. This is the known generator misclassification +(recorded in prior feature reviews of this repo): `git diff --numstat` confirms 2 changed `.cs` files +(`QuickFiler/Controllers/QfcItemController.FolderHandling.cs` +5/-2, +`QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` +37/-17). The summary was +corrected in place (annotated `CORRECTED BY feature-review 2026-07-20`), adding a `Core logic changes: +2 files` bullet list with the two space-free `.cs` paths in the generator's own `(+N/-N)` format so +downstream language-detection tooling recognizes C# as a changed language. C# is treated as an +in-scope changed language and its coverage is enforced below regardless of the original overview. + +## 2. Toolchain Compliance (C#) + +Evidence source: executor QA-gate artifacts under +`docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/`. These +are read as pre-existing evidence per the feature-review workflow (msbuild/vstest are not available +on this review host; the code under test is unchanged since these artifacts were produced, confirmed +by `git status` showing a clean working tree at HEAD). + +| Stage | Command | Evidence | Exit | Verdict | +|---|---|---|---|---| +| Format | `csharpier format .` then `csharpier check .` | csharpier-final-392.2026-07-20T14-20.md | 0 (format) / 1 (check, pre-existing 32-file config-noise, unchanged from baseline; both in-scope files clean) | PASS | +| Lint / analyzers | `MSBuild.exe TaskMaster.sln /t:Build ... /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` | analyzer-final-392.2026-07-20T14-24.md | 0 | PASS | +| Type / nullable | `MSBuild.exe TaskMaster.sln /t:Rebuild ... /p:Nullable=enable /p:TreatWarningsAsErrors=true` | nullable-final-392.2026-07-20T15-10.md | 1 (34 errors, byte-identical error-set to the P0-T11 baseline, all attributed exclusively to vendored `SVGControl.csproj`; 0 new, 0 first-party) | PASS (dispositioned; see AC-5 scope note below) | +| Tests | `dotnet-coverage collect -f cobertura ... -- vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation` | vstest-coverage-final-392.2026-07-20T14-32.md | 0 (541/541 passed, 0 failed) | PASS | + +Nullable disposition: the AC-5 scope note in `issue.md` (amended 2026-07-20 by orchestrator, before +feature review) explicitly scopes nullable enforcement to first-party projects per +`.claude/rules/csharp.md` (analyzers/nullable wired to first-party projects only; vendored projects +excluded). The 34 errors are confirmed byte-identical to the pre-existing baseline and are tracked +separately in `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`. +Independently re-verified: `git show bd435724:QuickFiler/Controllers/QfcItemController.FolderHandling.cs` +and the test file's baseline revision contain no nullable-annotation changes, consistent with the +executor's error-set-diff claim of zero new/first-party errors. + +## 3. General Code Change Policy + +| Requirement | Verdict | Evidence | +|---|---|---| +| Simplicity first | PASS | The fix is a single ternary clamp (`FolderArray.Length == 1 ? 0 : 1`) at each of the two existing fallback sites; no new abstraction introduced. | +| Reusability | PASS with note | The identical `Length == 1 ? 0 : 1` clamp is duplicated across `AssignFolderComboBox` and `PopulateAndSelectFolder` rather than factored into one shared helper. Low-severity, non-blocking; see code-review Finding CR-1. | +| Separation of concerns | PASS | No I/O, COM, or UI framework logic was added; the WinForms-bound `PopulateAndSelectFolder` remains pure of `InvokeRequired` marshaling as before. | +| Error handling / fail-fast | PASS | No new try/catch or swallowed exceptions; the fix removes an unguarded invalid-index condition rather than catching the resulting exception. | +| Backward compatibility | PASS | Existing multi-suggestion (`Length > 1`) and predetermined-folder behavior is unchanged (ternary's `else` branch is the pre-existing constant `1`); verified via `targeted-no-regression-392` evidence (6 pre-existing tests, all pass). | +| Public API compatibility | PASS | No signature changes to `AssignFolderComboBox()` or `PopulateAndSelectFolder(...)`. | +| File size <= 500 lines | PASS | See Section 4. | + +## 4. File-Size Compliance + +Independently verified line counts at HEAD (`wc -l` / `awk 'END{print NR}'`, cross-checked): + +- `QuickFiler/Controllers/QfcItemController.FolderHandling.cs`: 235 lines (baseline 232, +3). Within + the 500-line limit. +- `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs`: **500 lines exactly** + (baseline 480, +20). At, but not exceeding, the 500-line limit. Verdict: PASS (boundary case; a + reviewer should note this file has no further growth headroom before the limit is breached — see + code-review Finding CR-2). + +No other production or test file was changed by this branch (all other 29 changed files are Markdown +evidence/plan/issue documents, exempt from the 500-line limit per the General Code Change Policy's +"Markdown documentation files" exception). + +## 5. Coverage Verification (C#) + +Mandatory for C# (changed files present). The canonical review coverage artifact path for C# is +`artifacts/csharp/coverage.xml` (JaCoCo counter form, read by +`.claude/hooks/validate-feature-review-coverage.ps1`'s `Get-JacocoRepoCoverage` / +`Get-JacocoBranchCoverage`). + +### 5.1 Canonical artifact presence and scope + +`artifacts/csharp/coverage.xml` is PRESENT on this review host, generated per +`evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md` from the P2-T4 +`final-coverage.cobertura.xml` (single collection: only `QuickFiler.Test.dll` was run under +`dotnet-coverage`). The artifact aggregates six first-party packages +(`QuickFiler`, `SVGControl`, `Tags`, `TaskVisualization`, `ToDoModel`, `UtilitiesCS`); only `QuickFiler` +was actually instrumented by a dedicated test project in this collection. `Tags`, `TaskVisualization`, +and `ToDoModel` read 0.00%/0.00% because no dedicated test project for those assemblies ran in this +collection (not because their production code is uncovered — they are exercised by their own suites in +PR CI). `SVGControl` (11.63%/6.68%) and `UtilitiesCS` (7.48%/7.79%) show only incidental partial +coverage from classes referenced transitively by `QuickFiler.Test`'s run, not from their own dedicated +suites. + +Unlike a prior feature review in this repo (`outlook-store-exclusion-328`) where re-scoping the +aggregate to the actually-instrumented package cleared the floor, re-scoping here does **not** rescue +the number: `QuickFiler` — the one package with a genuine dedicated-suite run in this collection — is +itself measured at 73.68% line / 64.62% branch, both below the 85%/75% floor. The corrected/rescoped +figure is reported below alongside the raw six-package aggregate; the artifact itself was **not** +edited, because no legitimate re-scoping clears the floor this time (edits would misrepresent, not +correct, the measurement). + +- Raw six-package aggregate (as delivered): LINE 9021/55511 = 16.25%; BRANCH 1868/13736 = 13.60%. + Independently recomputed via `//counter[@type="LINE"]` / `//counter[@type="BRANCH"]` XPath summation + (the hook's own parser logic) against `artifacts/csharp/coverage.xml` — reproduces 16.25%/13.60% + exactly. +- Corrected (instrumented-only, `QuickFiler` package): LINE 5693/7727 = 73.68%; BRANCH 1012/1566 = + 64.62%. Both below the 85% line / 75% branch floor. + +### 5.2 Per-class and per-method verification (feature-evidence Cobertura) + +Sourced from `evidence/qa-gates/coverage-delta-392.2026-07-20T14-38.md` and +`evidence/qa-gates/vstest-coverage-final-392.2026-07-20T14-32.md` (executor's post-change Cobertura +run, not re-run here): + +| Scope | Line coverage | Branch coverage | Line floor 85% | Branch floor 75% | +|---|---|---|---|---| +| `QuickFiler` package (whole assembly) | 73.68% | 64.62% | FAIL | FAIL | +| `QfcItemController.FolderHandling.cs` (class, file-scoped) | 91.89% | 73.81% | PASS | FAIL (marginal, -1.19pt) | +| `AssignFolderComboBox()` method | 89.29% | 87.5% | PASS | PASS | +| `PopulateAndSelectFolder(...)` method | 100% | 100% | PASS | PASS | + +No regression on any row: every post-change figure is greater than or equal to its baseline +counterpart (baseline: package 73.67%/64.53%; class 91.55%/71.05%; `AssignFolderComboBox` 88.46%/85.71%; +`PopulateAndSelectFolder` 100%/100%, per `coverage-delta-392.2026-07-20T14-38.md`). + +### 5.3 New-code and changed-line coverage + +Changed lines (per `git diff`): the `else` branch of `AssignFolderComboBox()` +(`QfcItemController.FolderHandling.cs:201-205`) and the `PopulateAndSelectFolder` conditional +assignment (`:230-231`). Post-change Cobertura reports `hits="1"` on all 5 reported sequence points +(lines 201, 203, 204, 205, 231) — **100% line coverage on the new/changed code**, exceeding the >= 90% +target cited in AC-5. Both logical paths of each new ternary (`Length == 1` and the `else`/`> 1` path) +are independently exercised by named tests (2 new: `PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing`, +`AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero`; 2 pre-existing, +re-verified: `PopulateAndSelectFolder_AllMissingPredetermined_SelectsIndexOne`, +`AssignFolderComboBox_WhenNoPredeterminedFolder_SelectsTopSuggestionViaViewer`). Verdict: PASS. +Changed-line regression: none (Section 5.2). Verdict: PASS. + +### 5.4 Repo-wide C# coverage + +Neither the raw six-package aggregate (16.25%/13.60%) nor the corrected instrumented-only scope +(`QuickFiler` only, 73.68%/64.62%) clears the 85% line / 75% branch floor. This differs from the +`outlook-store-exclusion-328` precedent, where the corrected scope cleared the floor; here it does +not. The true all-first-party repo-wide figure (aggregated across every first-party assembly's own +dedicated test suite, as CI would run) is not computable from this single local +`QuickFiler.Test`-only collection. **Verdict: FAIL** on every locally-measurable repo-wide scope. +Disposition: this is a pre-existing condition — the baseline `QuickFiler` package figure (73.67%/64.53%, +per `evidence/baseline/vstest-coverage-baseline.2026-07-20T13-45.md`) is virtually identical to the +post-change figure (73.68%/64.62%), so this bug fix neither introduced nor worsened the gap. The gap +predates this branch and reflects broad, pre-existing under-coverage across `QuickFiler`'s WinForms/UI +surface, unrelated to the two lines this bug fix touches. Closing it is disproportionate scope for a +minor-audit bug fix and is routed to remediation as a maintainer-disposition item (Section 5.6; +`remediation-inputs.2026-07-20T18-00.md`), not a code task for this fix. + +### 5.5 Modified-file branch coverage — marginal, pre-existing, no regression + +`QfcItemController.FolderHandling.cs` class-level branch coverage is 73.81%, 1.19 points below the 75% +floor. This is a marginal, pre-existing gap, not a regression: baseline was 71.05% (already below +floor before #392); the fix's own new lines are 100% line-covered and both new ternary branches are +independently test-exercised (Section 5.3). The residual sub-floor branch percentage stems from other, +unrelated pre-existing branches elsewhere in the same partial-class file that this minor-audit bug fix +did not touch. Verdict: FAIL (floor), dispositioned as pre-existing with net improvement +(71.05% -> 73.81%), no changed-line regression. Routed to remediation as an optional, narrowly-scoped +task (add 1-2 targeted tests for an existing uncovered branch in the same already-in-scope file) — see +`remediation-inputs.2026-07-20T18-00.md`. + +### 5.6 C# coverage verdict (summary row) + +**C# coverage: FAIL.** New-code and changed-line coverage both PASS (100% / no regression). Three +sub-floor conditions are open, all confirmed pre-existing and not caused by this change: (a) modified +class-level branch coverage 73.81% (floor 75%, marginal -1.19pt gap, closeable via a narrow test +addition); (b) `QuickFiler` package-wide line/branch coverage 73.68%/64.62% (floor 85%/75%, broad +pre-existing gap, disproportionate to fix in this cycle); (c) canonical repo-wide artifact +16.25%/13.60% (floor 85%/75%, distorted by an incomplete single-project local collection, requires +CI-level full-suite aggregation to measure accurately). None of the three is a ratified exception yet +(no `human_interaction`/`orchestrator-state` scope-change record exists for this feature, unlike the +`StoreWrapper` precedent in #328). Remediation is triggered; see +`remediation-inputs.2026-07-20T18-00.md` and `remediation-plan.2026-07-20T18-00.md`. + +### 5.7 Coverage exemptions applied + +None. No `[ExcludeFromCodeCoverage]` attribute and no `coverage.config`/`.csproj` coverage-exclude was +added or modified by this branch (verified: `git diff` touches only the two named `.cs` files). + +## 6. Unit Test Policy + +| Requirement | Verdict | Evidence | +|---|---|---| +| Framework MSTest | PASS | New tests use `[TestClass]`/`[TestMethod]` (class already `[TestClass]`). | +| Moq for mocking | PASS | `Mock` used in `AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero`. | +| FluentAssertions | PASS | `.Should()` assertions in both new tests (`act.Should().NotThrow<...>()`, `comboBox.SelectedIndex.Should().Be(0)`, `mock.Verify(...)`, `controller.SelectedFolder.Should().Be(...)`). | +| Determinism | PASS | No `Thread.Sleep`, `Task.Delay`, `DateTime.Now/UtcNow`, temp files, or `new Random()` in the changed test file (grep clean). No external dependencies; `ComboBox` is disposed via `using`. | +| Scenario completeness | PASS | New tests cover the single-suggestion positive case at both call sites; pre-existing tests (re-verified, unchanged) cover multi-suggestion, predetermined-match, empty-array (documents the pre-existing unrelated empty-array throw), and null-handler no-op paths. | +| Test file size <= 500 | PASS (at boundary) | 500 lines exactly (baseline 480, +20); see Section 4 and code-review Finding CR-2. | +| Test-file line-count regression | PASS | Grew 480 -> 500 lines; within the 500-line limit, not a regression per se (net addition of 2 new tests, doc-comment trimming, and reuse of the existing `SetPrivate` helper in place of inline reflection). | +| Independence / isolation | PASS | Each new test constructs its own `ComboBox`/`Mock`/`FolderController`; no shared mutable state between tests. | + +## 7. Additional Policy Rules + +### 7.1 Evidence Location Compliance + +All evidence artifacts for this feature are written under the canonical +`docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence//` +tree (`evidence/baseline/`, `evidence/regression-testing/`, `evidence/qa-gates/`, +`evidence/issue-updates/`, `evidence/other/`). A scan of the branch diff +(`git diff --name-only bd435724..8f34f8ef`) for files under `artifacts/baselines/`, `artifacts/qa/`, +`artifacts/evidence/`, or `artifacts/coverage/` returned zero matches. `scripts/*/validate_evidence_locations.py` +does not exist in this repository (checked via file search); the manual `git diff` scan above is the +substitute check. Verdict: PASS. Note: `artifacts/csharp/coverage.xml` is the canonical review +coverage-artifact location (read by the coverage-validation hook), not a feature-evidence location, +and is not part of the tracked branch diff (untracked, gitignored). + +### 7.2 modified-workflow-needs-green-run + +`git diff --name-only bd435724..8f34f8ef` contains no files matching `.github/workflows/**`, +`scripts/benchmarks/**`, or `.github/actions/**`. The rule does not fire; no green-run evidence is +required. Verdict: PASS (rule not triggered). + +## Rejected Scope Narrowing + +The delegating prompt for this review explicitly stated "Scope determination is your responsibility +per your scope invariant" and did not attempt to narrow scope to any plan, task, phase, or file +subset. No caller instruction marked any language's coverage as "plan scope only," "out of scope," +"informational only," or equivalent. The trailing `DIRECTIVE: PREFLIGHT VALIDATION ONLY` convention +referenced in shared skills is standard planner/executor handoff text and was not present in, or +applicable to, this delegation. No scope-narrowing instruction was detected or rejected in this +review cycle. + +## Appendix A — Coverage Verdict Checklist + +- C# — canonical artifact `artifacts/csharp/coverage.xml`: present; raw six-package aggregate FAIL + (16.25% line / 13.60% branch); corrected instrumented-only (`QuickFiler`) scope also FAIL + (73.68% line / 64.62% branch). +- C# — new-code / changed-line coverage: PASS (100% on all 5 reported new/changed sequence points; no + regression). +- C# — touched non-exempt class (`QfcItemController.FolderHandling.cs`) line coverage: PASS (91.89%). +- C# — touched non-exempt class branch coverage: FAIL (73.81%, floor 75%, dispositioned pre-existing + with net improvement, no changed-line regression). +- C# — repo-wide first-party line coverage (best-available local scope): FAIL (73.68% instrumented / + 16.25% raw aggregate). +- C# — repo-wide first-party branch coverage (best-available local scope): FAIL (64.62% instrumented / + 13.60% raw aggregate). +- TypeScript / Python / PowerShell: zero changed files on the branch; coverage enforcement not + required for these languages. + +## Appendix B — Command Reference + +Commands used or cited during this review (check-only, no mutation of source): + +- `git merge-base HEAD origin/main` — recomputed base SHA `bd43572498474be89d80e1f9620dffb132ade377`. +- `git diff --numstat bd435724..8f34f8ef` — enumerated the full branch diff (2 `.cs`, 29 `.md`). +- `git diff bd435724..8f34f8ef -- ` — read production and test diffs. +- `wc -l` / `awk 'END{print NR}' ` — file-size checks at HEAD and at the merge-base. +- Python `xml.etree.ElementTree` parse of `artifacts/csharp/coverage.xml` — independent re-derivation + of report-level and package-level `LINE`/`BRANCH` counters (reproduces the executor's + 16.25%/13.60% raw figures and the 73.68%/64.62% `QuickFiler`-only rescoped figures). +- `git show bd435724: | wc -l` — baseline line counts for the two touched files. +- Executor toolchain evidence (not re-run here): `csharpier-final-392`, `analyzer-final-392`, + `nullable-final-392` (both revisions), `vstest-coverage-final-392`, `coverage-delta-392`, + `coverage-conversion-392`, `regression-check-392`, `fail-before-392`, `pass-after-392`, + `targeted-no-regression-392` under `evidence/qa-gates/` and `evidence/regression-testing/`. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-inputs.2026-07-20T18-00.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-inputs.2026-07-20T18-00.md new file mode 100644 index 00000000..5ac604b6 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-inputs.2026-07-20T18-00.md @@ -0,0 +1,60 @@ +# Remediation Inputs — folder-combobox-fallback-index-out-of-range (Issue #392) + +- Timestamp: 2026-07-20T18-00 +- Entry cycle: 1 +- Source audit: `policy-audit.2026-07-20T18-00.md` Section 5, `feature-audit.2026-07-20T18-00.md` AC-5 caveat +- Trigger: mandatory C# coverage checks below the uniform 85% line / 75% branch floor + (`.claude/rules/quality-tiers.md`), unratified. + +## Why remediation is triggered + +All five acceptance criteria for Issue #392 pass. The C# toolchain passes (format, analyzers, tests; +nullable reproduces a dispositioned pre-existing vendored-project condition). The fix's own new/changed +lines are 100% line-covered and both new logical branches are test-exercised. However, three coverage +scopes read below the repository's mandatory 85%/75% floor, and none carries an existing maintainer +ratification (unlike the `StoreWrapper` branch-floor exception ratified for issue #328): + +1. **`QfcItemController.FolderHandling.cs` class-level branch coverage: 73.81%** (floor 75%, gap + 1.19 points). Baseline was 71.05% (already below floor); this fix improved it, did not regress it. +2. **`QuickFiler` package-wide coverage: 73.68% line / 64.62% branch** (floor 85%/75%). Baseline was + 73.67%/64.53% — virtually unchanged; this is broad, pre-existing under-coverage across the + `QuickFiler` assembly's WinForms/UI surface, unrelated to the two lines this bug fix touches. +3. **Canonical repo-wide artifact (`artifacts/csharp/coverage.xml`): 16.25% line / 13.60% branch** + (raw six-package aggregate) — distorted because only `QuickFiler.Test` ran in this local + collection; `Tags`, `TaskVisualization`, and `ToDoModel` were not exercised by their own suites here. + Re-scoping to the one actually-instrumented package (`QuickFiler`) still yields item 2 above, which + also fails the floor (unlike a prior feature review in this repo where re-scoping cleared the + floor). + +## Enumerated fix list + +| # | Item | File(s) | Expected outcome | Verification command | +|---|---|---|---|---| +| R1 | Close the marginal class-level branch-coverage gap (item 1) | `QuickFiler/Controllers/QfcItemController.FolderHandling.cs`, `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` | Add 1-2 targeted MSTest tests exercising an existing, currently-uncovered branch already inside `QfcItemController.FolderHandling.cs` (not a new file, not a new production code path) to raise class-level branch coverage from 73.81% to >= 75%, with zero regression on any currently-covered line/branch. | `dotnet-coverage collect -f cobertura -s coverage-exclude-deedle.xml -o -coverage.cobertura.xml -- vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation`, then recompute class-level `line-rate`/`branch-rate` for `QfcItemController.FolderHandling.cs` and diff against the 91.89%/73.81% baseline captured in `policy-audit.2026-07-20T18-00.md` Section 5.2. | +| R2 | Obtain an explicit maintainer disposition decision for the `QuickFiler` package-wide and canonical repo-wide coverage gaps (items 2-3) | none (documentation/ratification only) | Either (a) ratify the pre-existing `QuickFiler` package coverage gap as a documented exception analogous to the `StoreWrapper` precedent (issue #328), recorded via a `human_interaction` / `orchestrator-state` scope-change entry, with the requirement that the true all-first-party repo-wide figure is measured by the PR CI full-suite run; or (b) schedule a dedicated, separately-scoped coverage-uplift task for `QuickFiler` (outside this minor-audit bug fix's Scope-Lock). Do not attempt to close this gap inside this remediation cycle by adding broad, unrelated test coverage across `QuickFiler`'s WinForms/UI surface — that is disproportionate scope creep for a minor-audit fix. | N/A (decision/documentation task, not a command). | + +## Do-not-do list + +- Do not modify `.claude/rules/*` or any policy document to weaken the 85%/75% coverage floor. +- Do not add a `coverage.config` or `.csproj` coverage-exclude for `QuickFiler` or any of its classes + to artificially raise the measured percentage — no production source file may be excluded from + coverage measurement. +- Do not expand the fix's Scope-Lock beyond `QfcItemController.FolderHandling.cs` and its test file to + chase the package-wide `QuickFiler` floor (item 2) inside this cycle; that is R2's job, not R1's. +- Do not silently mark AC-5 or any policy-audit coverage row as PASS without the corresponding + remediation task closing the gap or a recorded maintainer ratification. +- Do not weaken or delete the two new regression tests, the pre-existing re-verified tests, or the + `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection` test that documents the pre-existing, + unrelated empty-array gap (code-review Finding CR-3/Info). + +## Pointer to audit artifacts + +- `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T18-00.md` + (Section 5, coverage findings; Appendix A, coverage verdict checklist) +- `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T18-00.md` + (Findings CR-1, CR-2, CR-3) +- `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T18-00.md` + (AC-5 coverage caveat) +- Target remediation plan: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-plan.2026-07-20T18-00.md` + (to be authored/refined by `atomic-planner` per `remediation-handoff-atomic-planner`; this + feature-review pass creates the target plan file with the R1/R2 shape above as the starting point). diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-plan.2026-07-20T18-00.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-plan.2026-07-20T18-00.md new file mode 100644 index 00000000..6087bc5b --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/remediation-plan.2026-07-20T18-00.md @@ -0,0 +1,283 @@ +# Remediation Plan — folder-combobox-fallback-index-out-of-range (Issue #392), Cycle 1 (R1) + +- **Timestamp:** 2026-07-20T18-00 +- **Entry cycle:** 1 +- **Work Mode:** `minor-audit` (AC source: `issue.md` `## Acceptance Criteria` only; AC-1 through + AC-5 are already all `[x]` and are not reopened by this cycle — see Phase 2) +- **Trigger:** `remediation-inputs.2026-07-20T18-00.md`, sourced from + `policy-audit.2026-07-20T18-00.md` Section 5 (coverage findings R1/R2), `code-review.2026-07-20T18-00.md` + (Findings CR-1/CR-2/CR-3, informational — no action required by this cycle), and + `feature-audit.2026-07-20T18-00.md` (AC-5 coverage caveat). +- **Feature folder (``):** `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392` +- **Timestamp token:** every `` placeholder below MUST be substituted with the real ISO-8601 + timestamp (`yyyy-MM-ddTHH-mm`) at the moment the artifact is written, per + `evidence-and-timestamp-conventions`. + +**Fail-closed evidence rule:** Include explicit baseline artifact tasks, final-QC artifact tasks, +and a coverage delta/threshold task for the in-scope language (C#). If any required baseline, QC, +or coverage-comparison artifact is missing, the remediation verdict must be BLOCKED or INCOMPLETE, +never PASS. + +**Evidence accounting rule:** Each evidence-producing task names its exact artifact path under +`docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence//`. +Do not mark an evidence-backed task complete without the artifact on disk. The sole non-evidence +exception is the repo-wide coverage-gate tooling input at `artifacts/csharp/coverage.xml` +(JaCoCo), which is documented as a non-canonical-evidence tooling path, not a duplicate evidence +location (see the main plan's Correction note). + +## Scope-Lock (reiterated, not reopened) + +This cycle may modify only: +- `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` (R1: add coverage only where an + existing, pre-#392 branch is currently uncovered — no new production code path, no behavior + change). +- `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` (R1: 1-2 new `[TestMethod]` + additions only — **no new file**; splitting this file into two is explicitly NOT authorized in + this cycle even though `code-review` Finding CR-2 notes it is at the 500-line boundary). + +No other production file may be changed. `spec.md`/`user-story.md` are confirmed absent from the +feature folder and remain out of scope for this minor-audit AC source. R2 is documentation-only (no +code, no policy edits, no coverage excludes, no package-wide test additions). + +## Do-Not-Do List (from `remediation-inputs.2026-07-20T18-00.md`, reiterated) + +- Do not modify `.claude/rules/*` or any policy document to weaken the 85%/75% coverage floor. +- Do not add a `coverage.config` or `.csproj` coverage-exclude for `QuickFiler` or any of its + classes to artificially raise the measured percentage. +- Do not expand the Scope-Lock beyond the two named files to chase the package-wide `QuickFiler` + floor (that is R2's disposition, not R1's code task). +- Do not silently mark AC-5 or any policy-audit coverage row as PASS without the corresponding + remediation task closing the gap (R1) or a recorded maintainer disposition (R2). +- Do not weaken or delete the two Phase-1 (original cycle) regression tests, the six re-verified + pre-existing tests, or `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection` (the test + that documents the pre-existing, unrelated, out-of-scope empty-array gap per code-review Finding + CR-3/Info). + +## R1/R2 Disposition Map (scope decisions already made by the orchestrator — encoded, not reopened) + +- **R1** (`QfcItemController.FolderHandling.cs` class-level branch coverage 73.81% vs 75% floor, + `policy-audit.2026-07-20T18-00.md` Section 5.2/5.5): IN SCOPE. Closed by Phase 1 (P1-T2..P1-T5) + and verified by Phase 2's coverage delta (P2-T6). +- **R2** (`QuickFiler` package-wide 73.68%/64.62% and canonical repo-wide artifact 16.25%/13.60%, + Section 5.1/5.4/5.6): RESOLVED AS `SCOPE_CHANGE`, no code work. Tracked in open GitHub issue #136 + (*Feature: quickfiler-80-per-file-coverage*). The orchestrator has already recorded a + `human_interaction` `scope_change` disposition in `orchestrator-state`, citing issue #136, + `remediation-inputs.2026-07-20T18-00.md` option (b), and CLAUDE.md's COM/VSTO testable-denominator + exemption language. This plan's only obligation is the single documentation task P1-T1, which + records that disposition verbatim as feature-folder evidence. + +--- + +### Phase 0 — Remediation Baseline Capture + +- [x] [P0-T1] Read `CLAUDE.md` in full (policy reading order position 1). + - Acceptance: file read in this session; its Policy Compliance Order section is quoted verbatim + in the P0-T5 evidence artifact. + +- [x] [P0-T2] Read `.claude/rules/general-code-change.md` (policy reading order position 2). + - Acceptance: file read; its Mandatory Toolchain Loop / File Size Limit sections quoted in the + P0-T5 evidence artifact. + +- [x] [P0-T3] Read `.claude/rules/general-unit-test.md` (policy reading order position 3). + - Acceptance: file read; its Coverage Requirements section quoted in the P0-T5 evidence artifact. + +- [x] [P0-T4] Read `.claude/rules/csharp.md` and `.claude/rules/quality-tiers.md` (policy reading + order positions 4-5, plus the uniform 85%/75% coverage-tier rule that triggered this cycle). + - Acceptance: both files read; the Toolchain/Testing Standards section of `csharp.md` and the + Uniform-vs-Tier-Dependent Gate Matrix of `quality-tiers.md` are quoted in the P0-T5 evidence + artifact. + +- [x] [P0-T5] Write the Phase 0 policy-read evidence artifact to + `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/phase0-instructions-read..md`. + - Acceptance: file exists and contains `Timestamp:`, `Policy Order:`, and the explicit list of + the four files read in P0-T1 through P0-T4, in order. + +- [x] [P0-T6] Verify the minor-audit Scope-Lock boundary for this remediation cycle. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/minor-audit-scope..md`. + - Acceptance: artifact confirms `spec.md`/`user-story.md` remain absent from the feature folder, + and confirms the Scope-Lock file list above (`QfcItemController.FolderHandling.cs` and + `QfcItemController.FolderHandlingTests.cs` only, no new files) is unchanged from the original + cycle's fix scope. + +- [x] [P0-T7] Record remediation baseline git state (current branch, `HEAD` short SHA via + `git rev-parse --abbrev-ref HEAD` and `git rev-parse --short HEAD`). + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/git-baseline-state..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:` + stating the branch name and SHA. + +- [x] [P0-T8] Re-run the C# coverage collection for `QuickFiler.Test` and record the class-level + `QfcItemController.FolderHandling.cs` line/branch rates as this cycle's re-baseline. + - Command: `dotnet-coverage collect -f cobertura -s coverage-exclude-deedle.xml -o -coverage.cobertura.xml -- vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/remediation-baseline/coverage-baseline..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:` with + the numeric class-level line/branch rate for `QfcItemController.FolderHandling.cs`. Expected + starting point (per `policy-audit.2026-07-20T18-00.md` Section 5.2): 91.89% line / 73.81% + branch. Do not assume these figures are unchanged without re-measuring — this re-baseline may + run at a later point in time than the original audit. + +--- + +### Phase 1 — Constrained Remediation Implementation (R1 Code + R2 Documentation) + +- [x] [P1-T1] Record the R2 maintainer-disposition evidence artifact (documentation only; no code + change; not gated on P0-T8). + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/coverage-disposition-decision..md`. + - Acceptance: artifact records, verbatim, the disposition already made by the orchestrator: the + `QuickFiler` package-wide (73.68%/64.62%) and canonical repo-wide artifact (16.25%/13.60% raw + six-package aggregate) coverage gaps are `SCOPE_CHANGE`, tracked in open GitHub issue #136 + (*Feature: quickfiler-80-per-file-coverage*), citing (a) the recorded `human_interaction` + `scope_change` entry in `orchestrator-state`, (b) the `#328` `StoreWrapper` branch-floor + exception precedent as the analogous ratification pattern, (c) CLAUDE.md's COM/VSTO + testable-denominator exemption language, and (d) that the true all-first-party repo-wide figure + is measured by the PR CI full-suite run (not this single-project local collection). This + satisfies R2 in full; no further R2 task exists in this plan. + +- [x] [P1-T2] Identify, from the class-level Cobertura per-line data captured in P0-T8, the + specific existing (pre-#392) branch(es) in `QfcItemController.FolderHandling.cs` that remain + uncovered and are the smallest, most self-contained addition to close the 73.81% -> >= 75% gap. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/branch-gap-analysis..md`. + - Acceptance: artifact records the identified line(s)/branch(es) with file:line citations and the + rationale for why exercising them is expected to close the gap, with no production behavior + change implied. + +- [x] [P1-T3] Add 1-2 new `[TestMethod]`s to + `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` exercising the branch(es) + identified in P1-T2, using MSTest + Moq (for any collaborator mocking) + FluentAssertions, + consistent with the file's existing test style. + - Precondition: P1-T2 complete. + - Acceptance: no existing test's assertions, names, or behavior are modified (the six + re-verified pre-existing tests and `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection` + are untouched); no new production code path is introduced in `QfcItemController.FolderHandling.cs` + (this task adds tests only, not production code); no new file is created. + +- [x] [P1-T4] Verify the new test(s) from P1-T3 pass and do not weaken any existing assertion. + - Precondition: P1-T3 complete. + - Command: `vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation /TestCaseFilter:"FullyQualifiedName~"` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/regression-testing/new-branch-test-pass..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE: 0`, and `Output Summary:` + stating the new test(s) passed. + +- [x] [P1-T5] Confirm `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` remains + within the 500-line limit after the P1-T3 addition. + - Command: `wc -l "QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs"` (baseline + 500 lines exactly per `policy-audit.2026-07-20T18-00.md` Section 4, zero headroom). + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/other/file-size-check..md`. + - Acceptance: file is <= 500 lines. Per this cycle's Scope-Lock, splitting the file into two + (code-review Finding CR-2's suggestion) is NOT authorized in this cycle: if the P1-T3 addition + would exceed 500 lines, the addition must be made to fit within the existing file (e.g., by + trimming redundant inline comments/blank lines elsewhere in the file, not by shortening + assertions or weakening any test) rather than creating a new file. If 500 lines cannot be met + without a new file or without weakening a test, halt this task and escalate to the coordinator + for an explicit Scope-Lock amendment before proceeding — do not silently split the file or + silently exceed the limit. + +--- + +### Phase 2 — Final QC Loop, Coverage Delta, and Reaudit Handoff + +Unconditional full C# toolchain, run in order. If any step fails or changes files, restart this +phase from P2-T1. No `SKIPPED` outcomes; no IN_SCOPE/OUT_OF_SCOPE branches. + +- [x] [P2-T1] Run the final C# formatting command. + - Command: `dotnet tool run csharpier .` then `dotnet tool run csharpier --check .` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-csharpier-final..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:`; the + two Scope-Lock files must be format-clean. Pre-existing, unrelated config-noise failures on + other files (unchanged from the original cycle's baseline) are non-blocking; any NEW + format failure on a file outside that pre-existing noise set is blocking and requires a + restart from P2-T1. + +- [x] [P2-T2] Run the final C# analyzer build command. + - Command: `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-analyzer-final..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE: 0`, and `Output Summary:`; + zero analyzer diagnostics attributable to the two Scope-Lock files. If this command fails, fix + and restart Phase 2 from P2-T1. + +- [x] [P2-T3] Run the final C# nullable build command. + - Command: `msbuild TaskMaster.sln /t:Rebuild /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true` + - Rationale for `/t:Rebuild` (not `/t:Build`): established in the main plan's P2-T3 — MSBuild's + incremental up-to-date check otherwise skips `CoreCompile`, which would vacuously pass the + error-set comparison below. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-nullable-final..md`. + - Acceptance: the command must run and the artifact must record `Timestamp:`, `Command:`, + `EXIT_CODE:`, and `Output Summary:` including an error-set comparison against the original + cycle's `evidence/baseline/nullable-baseline..md` (P0-T11 of `plan.2026-07-20T12-59.md`, + 34 errors, all in vendored `SVGControl.csproj`). Acceptance = zero NEW errors relative to that + baseline AND zero errors attributable to first-party in-scope files, per the amended AC-5 scope + note in `issue.md` and `docs/features/potential/2026-07-07-ci-nullable-check-skipped-vendored-projects.md`. + If any NEW error appears, or any error is attributable to a first-party project, fix and restart + Phase 2 from P2-T1. + +- [x] [P2-T4] Run the final full-suite MSTest coverage command for `QuickFiler.Test`. + - Command: `dotnet-coverage collect -f cobertura -s coverage-exclude-deedle.xml -o -coverage.cobertura.xml -- vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation` + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-vstest-coverage-final..md`. + - Acceptance: artifact contains `Timestamp:`, `Command:`, `EXIT_CODE: 0`, and `Output Summary:` + with total tests, pass/fail counts (baseline 541 plus any new tests from P1-T3, all passing), + and the numeric post-change class-level line/branch rate for `QfcItemController.FolderHandling.cs`. + If this command fails, fix and restart Phase 2 from P2-T1. + +- [x] [P2-T5] Regenerate the JaCoCo-format canonical coverage-gate input at + `artifacts/csharp/coverage.xml` from the P2-T4 Cobertura output, using the established conversion + pattern recorded in `evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md` (from the + original cycle). + - Evidence: `artifacts/csharp/coverage.xml` (JaCoCo, tooling-input path, not an evidence + duplicate) and + `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-conversion..md`. + - Acceptance: `artifacts/csharp/coverage.xml` exists in JaCoCo format with `Timestamp:`, + `Command:`, `EXIT_CODE:`, and `Output Summary:` recorded in the evidence mirror, including the + report-level, `QuickFiler`-package-level, and `QfcItemController.FolderHandling.cs` + class-level LINE/BRANCH counter totals. + +- [x] [P2-T6] Compute the coverage delta: the P0-T8 baseline (91.89% line / 73.81% branch) versus + this phase's final class-level rate for `QfcItemController.FolderHandling.cs` (from P2-T4/P2-T5). + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-coverage-delta..md`. + - Acceptance: artifact contains an explicit statement: **class-level branch coverage >= 75%: + PASS/FAIL**; **no regression on any previously-covered line/branch: PASS/FAIL**. If either + reads FAIL, this is remediation-required, not PASS: restart Phase 1 from P1-T2 with a + different branch target rather than proceeding to P2-T7. + +- [x] [P2-T7] Verify no other test regressed by comparing the original cycle's baseline + (`evidence/baseline/vstest-coverage-baseline..md`, 541 tests) and this cycle's final (P2-T4) + full-suite results by test name/class. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-regression-check..md`. + - Acceptance: artifact confirms every test that passed at the original baseline still passes, and + the total pass count did not decrease (it may only increase, by the P1-T3 addition). + +- [x] [P2-T8] Update `issue.md` with a remediation-cycle closure note. + - Files: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/issue.md`. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/issue-updates/remediation-cycle1-note..md`. + - Acceptance: no AC checkbox changes are made (all five ACs are already `[x]` and are unaffected + by this coverage-only remediation cycle); the evidence note summarizes the R1 branch-coverage + closure (P2-T6 PASS) and the R2 scope-change disposition (P1-T1), each cited to its backing + artifact path. + +- [x] [P2-T9] Hand off to `feature-review` for reaudit. + - Evidence: `docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/evidence/qa-gates/remediation-reaudit-handoff..md`. + - Acceptance: `feature-review` writes `policy-audit..md`, `code-review..md`, and + `feature-audit..md` (and, if any finding remains, a new + `remediation-inputs..md` opening cycle 2) using an exit timestamp distinct from this + cycle's entry timestamp (`2026-07-20T18-00`); the handoff evidence records that the reaudit is + required to independently re-verify: (a) the class-level branch-coverage floor is met (P2-T6), + (b) the P1-T1 R2 maintainer-disposition record is present and adequate, and (c) all five + original acceptance criteria (AC-1 through AC-5) remain PASS with no regression introduced by + this remediation cycle's own changes (P2-T7). + +--- + +## R1/R2 Traceability Map (for preflight cross-check) + +- R1 (class-level branch coverage 73.81% -> >= 75%) → P0-T8 (re-baseline), P1-T2 (gap analysis), + P1-T3 (new tests), P1-T4 (pass verification), P1-T5 (file-size gate), P2-T4/P2-T5 (final + measurement), P2-T6 (explicit PASS/FAIL delta gate). +- R2 (`QuickFiler` package-wide and canonical repo-wide gaps, `SCOPE_CHANGE`) → P1-T1 (sole + documentation task), P2-T8 (closure note cites P1-T1), P2-T9 (reaudit re-verifies P1-T1 is + adequate). +- No-regression obligation (all five original ACs stay PASS) → P2-T2, P2-T3, P2-T7, P2-T9. + +## Preflight Note + +This plan has not yet been preflighted by `atomic-executor` under +`DIRECTIVE: PREFLIGHT VALIDATION ONLY`. Per `remediation-handoff-atomic-planner`, execution must +not begin until `PREFLIGHT: ALL CLEAR` is returned; if `PREFLIGHT: REVISIONS REQUIRED` is returned, +`atomic-planner` (not `atomic-executor` or `feature-review`) revises this same file in place. From 7573d3d59b6da858b0c7886d1b59c0eccacafaa2 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Mon, 20 Jul 2026 15:01:37 -0400 Subject: [PATCH 3/3] docs(392): add R4 re-audit artifacts confirming PASS verdict Refs: #392 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01A2Yht8Rg83Um38RaVyV1sA --- .claude/agent-memory/feature-review/MEMORY.md | 1 + ...tion-verifies-scope-change-ratification.md | 31 ++ .../code-review.2026-07-20T19-30.md | 54 +++ .../feature-audit.2026-07-20T19-30.md | 74 ++++ .../policy-audit.2026-07-20T19-30.md | 373 ++++++++++++++++++ 5 files changed, 533 insertions(+) create mode 100644 .claude/agent-memory/feature-review/project_orchestrator-state-human-interaction-verifies-scope-change-ratification.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T19-30.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T19-30.md create mode 100644 docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T19-30.md diff --git a/.claude/agent-memory/feature-review/MEMORY.md b/.claude/agent-memory/feature-review/MEMORY.md index 288e3ca2..f3a7fd9d 100644 --- a/.claude/agent-memory/feature-review/MEMORY.md +++ b/.claude/agent-memory/feature-review/MEMORY.md @@ -39,3 +39,4 @@ - [ScoDictionaryNew swap: enumeration order + removal API](project_scodictionary-new-enumeration-order-and-removal-api.md) — swordfish-removal epic (F1 #306..F5): swap silently changes insertion->concurrent enumeration order (needs tie-breaks) and .Remove->.TryRemove in consumers; Static.Deserialize+Serialize() only, never the globals converter path; no SerializeAsync - [epic-child-twodot-diff-divergence-noise](project_epic-child-twodot-diff-divergence-noise.md) — reviewing an epic child vs the integration branch: use three-dot merge-base diff or sibling merges show as spurious adds/deletes (#307) - [rescoping to instrumented package doesn't always clear floor](project_rescoping-to-instrumented-package-does-not-always-clear-floor.md) — #392: unlike #328's StoresWrapper win, QuickFiler's own instrumented-package figure (73.68%/64.62%) is itself pre-existing sub-floor; always re-check against the floor after rescoping instead of assuming it always rescues the number +- [orchestrator-state human_interaction verifies scope_change ratification](project_orchestrator-state-human-interaction-verifies-scope-change-ratification.md) — #392 R4: cross-check a feature-evidence "maintainer ratified this" claim against the actual `human_interaction.requirements` block in `artifacts/orchestration/orchestrator-state.json` (gitignored, not in the diff) before downgrading a sub-floor FAIL to non-blocking diff --git a/.claude/agent-memory/feature-review/project_orchestrator-state-human-interaction-verifies-scope-change-ratification.md b/.claude/agent-memory/feature-review/project_orchestrator-state-human-interaction-verifies-scope-change-ratification.md new file mode 100644 index 00000000..8ff5bc20 --- /dev/null +++ b/.claude/agent-memory/feature-review/project_orchestrator-state-human-interaction-verifies-scope-change-ratification.md @@ -0,0 +1,31 @@ +--- +name: orchestrator-state-human-interaction-verifies-scope-change-ratification +description: For a coverage-gap SCOPE_CHANGE disposition, cross-check the feature-folder evidence narrative against artifacts/orchestration/orchestrator-state.json's human_interaction.requirements block before accepting the ratification as adequate +metadata: + type: project +--- + +On issue #392's remediation cycle 1 (R4 re-audit), the R2 disposition +(`evidence/qa-gates/coverage-disposition-decision..md`) claimed a maintainer `scope_change` +ratification for a pre-existing `QuickFiler` package-wide coverage gap, citing open GitHub issue +#136 and the `#328` `StoreWrapper` precedent. Rather than accepting the evidence file's narrative at +face value, I independently read `artifacts/orchestration/orchestrator-state.json`'s +`human_interaction.requirements` array and confirmed a well-formed entry: `response: "scope_change"` +(valid enum member per [[project_taskmaster-validator-memories-are-cross-repo]]'s companion rule +`.claude/rules/orchestrator-state.md`), non-empty `resolution` text citing the same issue number and +precedent, and a `resolved_at` timestamp. This is what elevates a sub-floor coverage finding from +"unratified FAIL, remediation required" (cycle 1's original verdict) to "ratified, non-blocking +exception" (cycle-1-re-audit's verdict) — matching the `#328` `StoreWrapper` pattern exactly. + +**Why:** A feature-folder Markdown evidence file is authored by the same executor/orchestrator being +reviewed and could assert a ratification that was never actually recorded in the checkpoint. The +`orchestrator-state.json` `human_interaction` block is the authoritative, schema-checked record (per +`.claude/rules/orchestrator-state.md`'s invariants: non-empty `requirements`, `response` in +`{scope_change, exception, halt}`, and `runbook_path` required only for `exception`). Treat the +Markdown evidence narrative as a claim and the checkpoint JSON as the verification. + +**How to apply:** Whenever a re-audit evidence file asserts a maintainer ratification/exemption for +an open coverage or policy gap, read `artifacts/orchestration/orchestrator-state.json` directly (it +is gitignored/session-local, so it will not appear in the branch diff) and confirm the +`human_interaction` entry exists, matches the claimed disposition, and is shape-valid before +downgrading a FAIL from blocking to non-blocking in the policy audit. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T19-30.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T19-30.md new file mode 100644 index 00000000..9aa2b564 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/code-review.2026-07-20T19-30.md @@ -0,0 +1,54 @@ +# Code Review — folder-combobox-fallback-index-out-of-range (Issue #392) + +- Timestamp: 2026-07-20T19-30 +- Reviewer: feature-review (remediation re-audit, cycle 1, R4) +- Range: `bd43572498474be89d80e1f9620dffb132ade377..8a1b7b98b7d12dac69fd1bee5d5f109d4095c3c6` +- Files reviewed: `QuickFiler/Controllers/QfcItemController.FolderHandling.cs`, + `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` + +## Executive Summary + +Remediation cycle 1 added exactly one new MSTest test +(`PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke`) to close the +marginal class-level branch-coverage gap flagged in `code-review.2026-07-20T18-00.md` / +`policy-audit.2026-07-20T18-00.md`, and trimmed 26 purely-structural `// Act`/`// Assert` +comment-header lines from 12 pre-existing tests to keep the file under the 500-line limit while +adding the new test. No production code changed in this cycle (the production file's diff is +byte-identical to cycle 1's original fix). No blocking findings. Both cycle-1 low-severity findings +(CR-1 duplication, CR-2 file-size headroom) are addressed or unchanged in status below; the cycle-1 +informational note (CR-3, the unused static helper's empty-array gap) is unaffected and still +applies. + +## Findings Table + +| Severity | File | Location | Finding | Recommendation | Rationale | Evidence | +|---|---|---|---|---|---|---| +| Low (carried forward, unresolved) | QuickFiler/Controllers/QfcItemController.FolderHandling.cs | Lines 202-204 and 230-231 | The fallback clamp ` == 1 ? 0 : 1` remains duplicated verbatim across `AssignFolderComboBox()` and `PopulateAndSelectFolder(...)`; this remediation cycle did not touch production code, so the finding from `code-review.2026-07-20T18-00.md` (CR-1) is unchanged. | Unchanged: extract a small private static helper (e.g. `ResolveFallbackIndex(int suggestionCount)`) and call it from both sites, at a future opportunity — not required for this cycle's coverage-only scope. | General Code Change Policy §Reusability. Non-blocking; carried forward for tracking. | `git diff bd435724..8a1b7b98 -- QuickFiler/Controllers/QfcItemController.FolderHandling.cs` (identical to cycle 1's diff). | +| Info (resolved) | QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs | Whole file | Cycle 1's CR-2 (file at exactly 500 lines, zero headroom) is resolved: the file is now 498 lines. Room was made by removing 26 bare `// Act`/`// Assert` comment-header lines from 12 pre-existing tests (verified via diff: comment-line deletions only, no assertion/name/behavior change) rather than by splitting the file or weakening a test. | No action required. If further tests are added to this file, consider the two-file split contemplated in CR-2 before the limit is reached again, since headroom is now only 2 lines. | General Code Change Policy §Module & File Structure (500-line limit); this is a disclosed, verifiable technique that preserves test documentation (AAA structure remains visible via blank-line separation and descriptive test names) while reclaiming line budget. | `wc -l QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` -> 498; `git diff 8f34f8ef..8a1b7b98` shows only comment deletions plus the one new test. | +| Info (unaffected, carried forward) | QuickFiler/Controllers/QfcItemController.FolderHandling.cs | Line 231 (`PopulateAndSelectFolder`) | Cycle 1's CR-3 (the static helper's ternary does not special-case an empty `folderArray`, and would select an invalid index 1 if ever called with zero items) is unaffected by this cycle — no production code changed. | Unchanged from cycle 1: no action required for this bug fix's scope; extend the guard only if `PopulateAndSelectFolder` ever gains a production caller that does not pre-guard against an empty array. | Documented for future-maintainer awareness only; still out of Scope-Lock. | Unchanged from cycle 1: `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` test `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection`. | + +## Positive Observations + +- The new test follows the established `InvokeRequired == true` / `viewer.Verify(v => + v.Invoke(It.IsAny()), Times.Once())` idiom already used identically in five sibling test + files in this project (`QfcItemController.ViewerSetupTests.cs`, + `QfcItemController.ConversationTests.cs`, `QfcItemController.FocusAndThemeTests.cs`, + `QfcItemController.SeamCoreTests.cs`, `QfcItemController.NavigationTests.cs`), per the branch-gap + analysis evidence — consistent, low-risk reuse of an existing project convention rather than a + novel technique. +- The remediation is minimal and precisely targeted: zero production-code changes, one new test, + closing exactly the floor gap identified in the prior audit with no unrelated scope expansion. +- The comment-trimming technique used to preserve file-size headroom is disclosed and independently + verifiable (a straightforward `git diff` confirms only comment-line deletions), rather than being + silently absorbed into the diff. +- Test independence is preserved: the new test constructs its own `Mock`, + `Mock`, and `FolderController`, with no shared mutable state with any other + test in the file. + +## Scope Confirmation + +Findings above are scoped to the full feature-vs-base diff +(`bd43572498474be89d80e1f9620dffb132ade377..8a1b7b98b7d12dac69fd1bee5d5f109d4095c3c6`), covering both +the original fix commit (`8f34f8ef`) and the remediation commit (`8a1b7b98`), not any plan/task/phase +subset. The only two non-Markdown files in this diff are the production and test file reviewed above +and in the original cycle's code review; no other source file (any language) was changed. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T19-30.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T19-30.md new file mode 100644 index 00000000..1c202f51 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/feature-audit.2026-07-20T19-30.md @@ -0,0 +1,74 @@ +# Feature Audit — folder-combobox-fallback-index-out-of-range (Issue #392) + +- Timestamp: 2026-07-20T19-30 +- Reviewer: feature-review (remediation re-audit, cycle 1, R4) +- Work Mode: `minor-audit` + +## Scope and Baseline + +- Base branch (resolved): `main` @ `bd43572498474be89d80e1f9620dffb132ade377`. +- Head: `8a1b7b98b7d12dac69fd1bee5d5f109d4095c3c6` (two commits ahead of merge-base: `8f34f8ef` fix, + `8a1b7b98` remediation). +- Audit scope: the full branch diff vs the merge-base (feature-vs-base), not any plan/task/phase + subset. Confirmed via `git diff --numstat bd435724..8a1b7b98`: 2 changed `.cs` files (1 production, + 1 test; unchanged count from the original audit), 53 added/modified Markdown files. +- Acceptance-criteria source (per `minor-audit` marker in `issue.md`): the explicit + `## Acceptance Criteria` section of `issue.md` only (AC-1 through AC-5). No `spec.md` or + `user-story.md` exist in this feature folder. +- Evidence: production/test diffs read directly; remediation-cycle QA-gate evidence under + `evidence/qa-gates/remediation-*` and `evidence/remediation-baseline/`; canonical C# coverage + artifact `artifacts/csharp/coverage.xml` (regenerated this cycle) parsed directly; the + `human_interaction` `scope_change` record in `artifacts/orchestration/orchestrator-state.json`. + +## Acceptance Criteria Inventory + +Unchanged from the original audit (`feature-audit.2026-07-20T18-00.md`). From `issue.md` +`## Acceptance Criteria`: + +- AC-1: A deterministic MSTest regression test reproduces the defect and fails before the fix; the + same test passes after the fix. No temporary files or external dependencies are used. +- AC-2: `QfcItemController.AssignFolderComboBox` no longer throws `ArgumentOutOfRangeException` when + `FolderArray` has exactly one entry and no predetermined folder matches: it selects index 0 instead + of index 1. +- AC-3: Existing multi-suggestion behavior is preserved. +- AC-4: The retained static helper `PopulateAndSelectFolder` applies the same bounds-safe fallback. +- AC-5: The full C# toolchain passes with zero regressions relative to the Phase 0 baseline, and + new/changed code meets the >= 90% coverage target, with the amended first-party-scoped nullable + note. + +## Acceptance Criteria Evaluation + +| AC | Verdict | Evidence | +|---|---|---| +| AC-1 | PASS (unchanged) | `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md` / `pass-after-392.2026-07-20T14-10.md`. Not affected by this remediation cycle (production code is byte-identical to cycle 1). | +| AC-2 | PASS (unchanged) | `git diff` shows the same clamp introduced in cycle 1, unmodified. | +| AC-3 | PASS (unchanged) | Pre-existing tests re-verified again in this cycle's full 542-test run with zero regressions. | +| AC-4 | PASS (unchanged) | Same clamp in `PopulateAndSelectFolder`, unmodified. | +| AC-5 | PASS | Toolchain: format PASS (independently reproduced 0 errors via `csharpier check .` on this review host), analyzers PASS (0 errors), tests PASS (542/542, 0 regressions — up from 541 per `evidence/qa-gates/remediation-regression-check.2026-07-20T18-46.md`), nullable reproduces the same byte-identical pre-existing 34-error vendored condition (0 new, 0 first-party). New/changed-code coverage: 100% on the original fix's lines (unchanged from cycle 1). **Additionally resolved this cycle**: the touched class's branch-coverage floor (73.81% -> 76.19%, per `evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md`, independently re-verified against the regenerated canonical artifact), which was an open policy-audit finding (not an AC gap) in the original cycle. The separate, broader `QuickFiler` package-wide/repo-wide coverage gap remains below floor but is now formally ratified as a `scope_change` exception (see `policy-audit.2026-07-20T19-30.md` Section 5.4); this does not affect AC-5's own literal PASS status, which was already met in the original cycle on its own terms. | + +## Summary + +All five acceptance criteria in `issue.md` remain PASS, unchanged from the original audit and +unaffected by this coverage-only remediation cycle (no production code was modified). The one +policy-level (not AC-level) finding open after the original audit — the touched class's marginal +branch-coverage floor gap (73.81%, floor 75%) — is now closed (76.19%), independently re-verified +against the regenerated canonical `artifacts/csharp/coverage.xml`. The separate, broader `QuickFiler` +package-wide/canonical repo-wide coverage gap remains below floor but is now properly ratified via a +`human_interaction` `scope_change` record citing open GitHub issue #136, the `#328` `StoreWrapper` +precedent, and CLAUDE.md's COM/VSTO testable-denominator exemption — consistent with established repo +practice for pre-existing, unrelated-to-the-fix coverage shortfalls. **Zero blocking findings remain +in this cycle.** + +### Acceptance Criteria Status +- Source: `issue.md` (`## Acceptance Criteria`, AC-1 through AC-5) +- Total AC items: 5 +- Checked off (delivered): 5 +- Remaining (unchecked): 0 +- Items remaining: none + +## Acceptance Criteria Check-off + +All five AC items remain checked off (`[x]`) in `issue.md`; no checkbox state changed in this +remediation cycle (verified: this coverage-only cycle did not touch any AC checkbox, per +`evidence/issue-updates/remediation-cycle1-note.2026-07-20T18-48.md`, and this audit's independent +re-verification confirms all five remain PASS). No checkbox text was modified. diff --git a/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T19-30.md b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T19-30.md new file mode 100644 index 00000000..b368ca80 --- /dev/null +++ b/docs/features/active/2026-07-20-folder-combobox-fallback-index-out-of-range-392/policy-audit.2026-07-20T19-30.md @@ -0,0 +1,373 @@ +# Policy Compliance Audit — folder-combobox-fallback-index-out-of-range (Issue #392) + +- Timestamp: 2026-07-20T19-30 +- Reviewer: feature-review (remediation re-audit, cycle 1, R4; structurally repaired in place per + MCP validator feedback — content and PASS verdict unchanged from the first write of this file) +- Base branch (resolved): `main` +- Merge-base SHA: `bd43572498474be89d80e1f9620dffb132ade377` +- Head SHA: `8a1b7b98b7d12dac69fd1bee5d5f109d4095c3c6` (two commits ahead of merge-base: + `8f34f8ef` fix, `8a1b7b98` remediation) +- Work Mode: `minor-audit` (AC source: explicit `## Acceptance Criteria` section of `issue.md`) +- Scope: full branch diff vs merge-base (feature-vs-base). Not narrowed to any plan, task, or phase. + +## Executive Summary + +This is a re-audit after remediation cycle 1, which addressed both open items from +`policy-audit.2026-07-20T18-00.md` Section 5: + +- **R1** — the marginal class-level branch-coverage gap in + `QfcItemController.FolderHandling.cs` (73.81%, floor 75%) is closed: one new MSTest test + (`PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke`) exercises a + previously-uncovered, pre-existing branch with zero production-code change, raising class-level + coverage to **95.95% line / 76.19% branch**, independently re-verified against the regenerated + canonical `artifacts/csharp/coverage.xml`. +- **R2** — the `QuickFiler` package-wide (73.72%/64.69%) and canonical repo-wide artifact + (16.26%/13.61%) coverage gaps remain below the 85%/75% floor, unchanged in substance from cycle 1, + but are now properly **ratified** via a `human_interaction` `scope_change` entry in + `artifacts/orchestration/orchestrator-state.json`, citing open GitHub issue #136 + (*quickfiler-80-per-file-coverage*), the `#328` `StoreWrapper` precedent, and CLAUDE.md's + COM/VSTO/WinForms testable-denominator exemption — the same ratification pattern used for the + `StoreWrapper` exception in issue #328. + +The production and test diff for the two Scope-Lock files is unchanged from cycle 1 except for one +added test method (verified: `git diff bd435724..8a1b7b98 -- QuickFiler/Controllers/QfcItemController.FolderHandling.cs` +is byte-identical to cycle 1's diff; the test file's only substantive change beyond comment removal +is the one new test). All five acceptance criteria remain PASS, unaffected by this coverage-only +cycle. The toolchain passes (format, analyzers, tests; nullable reproduces the same dispositioned +pre-existing vendored condition). 542/542 tests pass, up from 541, with zero regressions. + +**Overall verdict: PASS (go). Blocking findings: 0.** One item — the `QuickFiler` package-wide and +canonical repo-wide coverage gap — remains below the 85%/75% floor and is recorded as FAIL on its raw +numeric verdict, but is a ratified, documented pre-existing exception (not introduced or worsened by +this branch; baseline was 73.68%/64.62%, virtually unchanged at 73.72%/64.69%), consistent with the +`#328` `StoreWrapper` precedent, and does not block this cycle's exit gate. + +**C# coverage verdict:** touched-class coverage is PASS (95.95% line / 76.19% branch); package-wide and repo-wide coverage is FAIL on the raw numeric floor check, dispositioned as a ratified, non-blocking, pre-existing exception per Section 8. + +- Changed languages with files in the branch diff: **C# only** (2 `.cs` files: 1 production, 1 test). + TypeScript, Python, and PowerShell have zero changed files on this branch; their coverage rows + below are recorded `N/A - out of scope` per policy, not omitted. + +## 1. General Unit Test Policy Compliance + +| Requirement | Verdict | Evidence | +|---|---|---| +| Independence | PASS | The new test constructs its own `Mock`, `Mock`, and `FolderController`; no shared mutable state with any other test in the file. | +| Isolation | PASS | The new test targets a single unit of behavior (`PopulateFolderComboBox`'s `InvokeRequired == true` marshaling branch). | +| Fast execution | PASS | 542/542 tests complete in 7.7651 seconds (`evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md`). | +| Determinism | PASS | No `Thread.Sleep`, `Task.Delay`, `DateTime.Now/UtcNow`, temp files, or `new Random()` in the new or modified test code (grep clean on the changed test file). | +| Readability / maintainability | PASS | Descriptive test name (`PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke`) and an inline comment stating the scenario and expected outcome. | +| Coverage — repo-wide >= 85% line / 75% branch | FAIL at package/repo-wide scope, PASS at touched-class scope | See Section 5 (Test Coverage Detail) for the full breakdown and disposition. | +| Scenario completeness | PASS | The new test closes a previously-untested `InvokeRequired == true` marshaling branch, an established idiom already used identically in five sibling test files in this project (per `evidence/other/branch-gap-analysis.2026-07-20T18-20.md`). | +| Arrange-Act-Assert structure | PASS | The new test follows Arrange (mock/controller setup) - Act (`controller.PopulateFolderComboBox()`) - Assert (`viewer.Verify(...)`) ordering, delineated by blank lines. | +| External dependencies / mocks | PASS | Moq (`Mock`, `Mock`) used to isolate the unit under test; no network, database, or filesystem access. | +| No temporary files | PASS | Grep of the changed test file confirms no `Path.GetTemp*` or filesystem writes. | +| Test file location | PASS | `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs` mirrors the production path `QuickFiler/Controllers/QfcItemController.FolderHandling.cs` under the existing `QuickFiler.Test` tree; no colocation in `src/`-equivalent production trees. | + +## 2. General Code Change Policy Compliance + +| Requirement | Verdict | Evidence | +|---|---|---| +| Simplicity first | PASS | No production code changed this cycle; the original fix remains a single ternary clamp at each of the two pre-existing fallback sites. | +| Reusability | PASS with carried-forward note | The ` == 1 ? 0 : 1` clamp remains duplicated across `AssignFolderComboBox` and `PopulateAndSelectFolder` (unchanged from cycle 1's code-review Finding CR-1); non-blocking, tracked for future cleanup. | +| Extensibility / public API compatibility | PASS | No signature changes to `AssignFolderComboBox()` or `PopulateAndSelectFolder(...)`. | +| Separation of concerns | PASS | No I/O, COM, or UI framework logic was added or changed this cycle. | +| Error handling / fail-fast | PASS | No new try/catch or swallowed exceptions. | +| Backward compatibility | PASS | Existing multi-suggestion and predetermined-folder behavior is unchanged and re-verified (542/542 tests pass). | +| File size <= 500 lines | PASS | `QuickFiler/Controllers/QfcItemController.FolderHandling.cs`: 235 lines (unchanged). `QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs`: 498 lines (was exactly 500 at cycle-1 entry with zero headroom; 26 purely-structural `// Act`/`// Assert` comment-header lines were removed from 12 pre-existing tests — verified via diff to be comment-only deletions, no assertion/name/behavior change — then the one new test was added, netting 498 lines, now with 2 lines of headroom). | +| I/O boundary isolation | PASS | No disk/network/API code added; `PopulateAndSelectFolder` remains pure WinForms logic with no `InvokeRequired` marshaling, as before. | +| Dependency approval | PASS | No new dependency added; Moq/MSTest/FluentAssertions already approved for this repo. | + +## 3. Language-Specific Code Change Policy Compliance + +Language in scope: C# only. Evidence source: executor QA-gate artifacts under `evidence/qa-gates/remediation-*` (this cycle). +Independently re-verified where practical on this review host. + +| Stage | Command | Evidence | Exit | Verdict | +|---|---|---|---|---| +| Formatting (CSharpier) | `csharpier format .` then `csharpier check .` | `evidence/qa-gates/remediation-csharpier-final.2026-07-20T18-30.md` | 0 (both runs) | PASS — independently reproduced: `csharpier check .` run directly on this review host (csharpier 1.3.0) returns "Checked 1406 files in 2616ms" with **0 errors**, confirming the evidence's claim. | +| Linting / .NET analyzers | `MSBuild.exe TaskMaster.sln /t:Build ... /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` | `evidence/qa-gates/remediation-analyzer-final.2026-07-20T18-32.md` | 0 | PASS | +| Type checking / nullable | `MSBuild.exe TaskMaster.sln /t:Rebuild ... /p:Nullable=enable /p:TreatWarningsAsErrors=true` | `evidence/qa-gates/remediation-nullable-final.2026-07-20T18-35.md` | 1 (34 errors, byte-identical error-set to the original P0-T11 baseline, all attributed exclusively to vendored `SVGControl.csproj`; 0 new, 0 first-party) | PASS (dispositioned; unchanged from cycle 1's AC-5 scope note — nullable enforcement scoped to first-party projects per `.claude/rules/csharp.md`) | + +**Evidence-quality note (non-blocking):** cycle 1's baseline (`evidence/baseline/csharpier-baseline.2026-07-20T13-15.md`, +captured 2026-07-20T13-15) recorded 32 pre-existing `app.config`/`packages.config` formatting errors; +this cycle's evidence attributes their disappearance to upstream commit `78e847ec` ("style: apply +csharpier formatting to dependabot config changes"). That commit is confirmed to already be an +ancestor of the merge-base (`git merge-base --is-ancestor 78e847ec bd435724` succeeds), meaning it +predates both the merge-base and the original 13:15 baseline capture — so it cannot be the direct +cause of a change observed only between 13:15 and 18:30 on the same checkout. The practical fact +(0 errors, independently reproduced on this review host) is not in question; the causal attribution +in the evidence narrative is imprecise, most likely reflecting a working-tree/tool-state difference +between the two capture times rather than a defect in this branch. This does not change the PASS +verdict. + +No production `.cs`, `.csproj`, `.props`, or `.targets` file was changed in this remediation cycle. + +## 4. Language-Specific Unit Test Policy Compliance + +Language in scope: C# only. + +| Requirement | Verdict | Evidence | +|---|---|---| +| Framework: MSTest | PASS | The new test uses `[TestMethod]` inside the existing `[TestClass] QfcItemController_FolderHandlingTests`. | +| Mocking: Moq | PASS | `Mock`, `Mock` used in the new test. | +| Assertions: FluentAssertions preferred, MSTest-style acceptable when FluentAssertions is not practical | PASS | The new test uses `viewer.Verify(v => v.Invoke(It.IsAny()), Times.Once())` (Moq-idiomatic verification), consistent with sibling tests in the same file and project that verify mock interactions the same way. | +| MSTest style (`[TestClass]`/`[TestMethod]`) | PASS | Confirmed. | +| Test-file line-count regression | PASS | Net +18 lines cumulative from merge-base (61 added / 43 removed); the removed lines are comment-only (verified via diff). | +| No test weakened or deleted | PASS | `git diff 8f34f8ef..8a1b7b98` shows the only non-comment change is the addition of one new test method; all pre-existing assertions, names, and mock setups are byte-identical. | + +## 5. Test Coverage Detail + +Mandatory for C# (changed files present). Canonical artifact: `artifacts/csharp/coverage.xml` +(JaCoCo, regenerated this cycle at `evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md`), +independently re-parsed on this review host (Python `xml.etree.ElementTree`). + +**Coverage Metrics by Language:** + +| Language | Files Changed | Tests | Test Result | Baseline Coverage | Post-Change Coverage | New Code Coverage | +|----------|--------------|-------|-------------|-------------------|---------------------|-------------------| +| C# | 2 files | 542 tests | PASS 542 pass, 0 fail | 91.89% lines, 73.81% branch (touched class) | 95.95% lines, 76.19% branch (touched class) | 100% | + +(TypeScript, Python, and PowerShell rows are omitted per the template note — zero changed files for +those languages on this branch; see the `### Coverage Evidence Checklist` below for the explicit +`N/A - out of scope` record.) + +### Coverage Metrics — Multi-Scope Breakdown (supplementary detail) + +The single required table row above summarizes the touched class only. This repo's coverage policy +(`.claude/rules/quality-tiers.md`) also requires evaluating package-wide and repo-wide C# coverage; +the richer breakdown below is supplementary detail supporting the Section 5 findings and is not a +substitute for the required table above. + +| Language | Scope | Baseline Coverage | Post-Change Coverage | New Code Coverage | Disposition | +|---|---|---|---|---|---| +| C# | `QfcItemController.FolderHandling.cs` (touched class) | 91.89% line / 73.81% branch | 95.95% line / 76.19% branch | 100% line (original fix's 5 sequence points) | PASS | +| C# | `QuickFiler` package (whole assembly) | 73.67% line / 64.53% branch | 73.72% line / 64.69% branch | N/A - no new file added | FAIL (floor), dispositioned ratified non-blocking exception | +| C# | Canonical artifact, raw six-package aggregate | 16.25% line / 13.60% branch | 16.26% line / 13.61% branch | N/A - no new file added | FAIL (floor), dispositioned ratified non-blocking exception (single-suite local-collection artifact; see Section 8) | +| TypeScript | N/A - out of scope | N/A - out of scope | N/A - out of scope | N/A - out of scope | N/A - out of scope (0 changed files on this branch) | +| Python | N/A - out of scope | N/A - out of scope | N/A - out of scope | N/A - out of scope | N/A - out of scope (0 changed files on this branch) | +| PowerShell | N/A - out of scope | N/A - out of scope | N/A - out of scope | N/A - out of scope | N/A - out of scope (0 changed files on this branch) | + +Independent re-derivation (Python `xml.etree.ElementTree` against `artifacts/csharp/coverage.xml`): +report-level raw six-package aggregate LINE 9024/55511 = 16.26%, BRANCH 1869/13736 = 13.61% (matches +the executor's evidence exactly); `QuickFiler` package LINE 5696/7727 = 73.72%, BRANCH 1013/1566 = +64.69% (matches exactly); `QfcItemController.FolderHandling.cs` class-level entry (matched by +`sourcefilename`) `` = 95.95%, `` = 76.19% (matches exactly). + +### 1.2.1 Per-Language Coverage Comparison + +- C#: Baseline: 91.89% lines -> Post-change: 95.95% lines. Change: +4.06% lines. New/changed-code coverage: 100%. Disposition: PASS. Evidence: `evidence/qa-gates/remediation-coverage-delta.2026-07-20T18-44.md`, `evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md` (touched class `QfcItemController.FolderHandling.cs`; branch coverage 73.81% -> 76.19%, +2.38%, also clearing the 75% floor). +- C# package-wide (`QuickFiler` package, whole assembly, supplementary detail): Baseline: 73.67% line + / 64.53% branch (pre-fix). Post-change: 73.72% line / 64.69% branch. Change: +0.05% line, +0.16% + branch (no regression; small net improvement from the one new test). New/changed-code coverage: + N/A - no new file added to this package. Disposition: FAIL floor, dispositioned as ratified + non-blocking pre-existing exception (Section 8). Evidence: + `evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md`, + `evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md`, + `artifacts/orchestration/orchestrator-state.json` `human_interaction.requirements[0]`. +- C# repo-wide (canonical artifact, raw six-package aggregate, supplementary detail): Baseline: + 16.25% line / 13.60% branch. Post-change: 16.26% line / 13.61% branch. Change: +0.01% line, +0.01% + branch (no regression). New/changed-code coverage: N/A - no new file added. Disposition: FAIL + floor, dispositioned as ratified non-blocking pre-existing exception; the raw aggregate is a + single-suite (`QuickFiler.Test` only) local-collection artifact, not the true PR-CI repo-wide + figure (Section 8). Evidence: + `evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md`. +- TypeScript: Baseline: N/A. Post-change: N/A. Change: N/A. New/changed-code coverage: N/A. + Disposition: N/A. Evidence: N/A — no TypeScript files changed on this branch + (`git diff --numstat bd435724..8a1b7b98` shows 0 `.ts`/`.tsx` files). +- Python: Baseline: N/A. Post-change: N/A. Change: N/A. New/changed-code coverage: N/A. Disposition: + N/A. Evidence: N/A — no Python files changed on this branch. +- PowerShell: Baseline: N/A. Post-change: N/A. Change: N/A. New/changed-code coverage: N/A. + Disposition: N/A. Evidence: N/A — no PowerShell files changed on this branch. + +### 1.2.2 (comparison-bullet scan terminator) + +(Section boundary marker; no further per-language comparison bullets follow this heading.) + +### Coverage Evidence Checklist + +- C# baseline coverage artifact: `evidence/remediation-baseline/coverage-baseline.2026-07-20T18-15.md` + (this cycle's entry baseline) and cycle 1's `evidence/qa-gates/coverage-conversion-392.2026-07-20T14-50.md` + (original fix baseline). +- C# post-change coverage artifact: `artifacts/csharp/coverage.xml` (regenerated this cycle) and + `evidence/qa-gates/remediation-coverage-conversion.2026-07-20T18-42.md`. +- TypeScript baseline coverage artifact: N/A - out of scope (0 `.ts`/`.tsx` files changed on this branch). +- TypeScript post-change coverage artifact: N/A - out of scope (0 `.ts`/`.tsx` files changed on this branch). +- PowerShell baseline coverage artifact: N/A - out of scope (0 `.ps1`/`.psm1` files changed on this branch). +- PowerShell post-change coverage artifact: N/A - out of scope (0 `.ps1`/`.psm1` files changed on this branch). +- Per-language comparison summary: C# is the only changed language on this branch; its coverage + comparison is recorded in full above (Section 5, `### 1.2.1`). TypeScript, Python, and PowerShell + have zero changed files on this branch (confirmed via `git diff --numstat bd435724..8a1b7b98`, + which enumerates exactly 2 `.cs` files and 53 `.md` files); their rows are recorded `N/A - out of + scope` rather than omitted. + +## 6. Test Execution Metrics + +| Metric | Value | Evidence | +|---|---|---| +| Total tests executed | 542 | `evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md` | +| Passed | 542 | Same | +| Failed | 0 | Same | +| Skipped | 0 | Same | +| Test-count delta vs. prior cycle | +1 (541 -> 542; the one new regression test from R1) | Same | +| Total execution time | 7.7651 seconds | Same | +| No-regression check | PASS — every one of the 541 original-cycle test names is present in this cycle's 542-name passed set (`comm -23` set-difference produced zero output) | `evidence/qa-gates/remediation-regression-check.2026-07-20T18-46.md` | +| Fail-before / pass-after (original fix, unaffected this cycle) | PASS | `evidence/regression-testing/fail-before-392.2026-07-20T14-05.md` (EXIT_CODE 1), `evidence/regression-testing/pass-after-392.2026-07-20T14-10.md` (EXIT_CODE 0) | +| New-test targeted run (R1) | PASS (EXIT_CODE 0) | `evidence/regression-testing/new-branch-test-pass.2026-07-20T18-25.md` | + +## 7. Code Quality Checks + +| Check | Result | Evidence | +|---|---|---| +| Formatting scan | 0 errors (2 Scope-Lock files clean) | `evidence/qa-gates/remediation-csharpier-final.2026-07-20T18-30.md`; independently reproduced on this review host | +| Analyzer scan | 0 errors, 0 new warnings attributable to Scope-Lock files | `evidence/qa-gates/remediation-analyzer-final.2026-07-20T18-32.md` | +| Nullable scan | 0 new, 0 first-party errors (34 pre-existing vendored errors, dispositioned) | `evidence/qa-gates/remediation-nullable-final.2026-07-20T18-35.md` | +| File-size scan | 0 files over the 500-line limit | Section 2 | +| Duplication scan | 1 low-severity, non-blocking finding carried forward (CR-1, fallback-clamp duplication) | `code-review.2026-07-20T19-30.md` Findings Table | +| Workflow change scan | 0 files matching `.github/workflows/**`, `scripts/benchmarks/**`, `.github/actions/**` | `git diff --name-only bd435724..8a1b7b98` | +| Evidence location scan | 0 files under `artifacts/baselines/`, `artifacts/qa/`, `artifacts/evidence/`, `artifacts/coverage/` | `git diff --name-only bd435724..8a1b7b98` | + +## 8. Gaps and Exceptions + +**Ratified exception — `QuickFiler` package-wide and canonical repo-wide C# coverage.** Both remain +below the 85%/75% floor (Section 5). This is dispositioned as a ratified, documented pre-existing +exception, independently confirmed via: + +- `artifacts/orchestration/orchestrator-state.json` `human_interaction.requirements[0]`: `id: + quickfiler-package-coverage-disposition`, `response: scope_change`, `resolved_at: + 2026-07-20T19:20:00Z`, citing GitHub issue #136 (*quickfiler-80-per-file-coverage*), the `#328` + `StoreWrapper` precedent, and CLAUDE.md's COM/VSTO/WinForms testable-denominator exemption. The + block satisfies the shape required by `.claude/rules/orchestrator-state.md` (non-empty + `requirements` list; `response` in the valid enum `{scope_change, exception, halt}`). +- `evidence/qa-gates/coverage-disposition-decision.2026-07-20T18-17.md` — the feature-folder evidence + mirror of the same disposition, with the same citations. +- No regression: baseline `QuickFiler` package figure (73.67%/64.53%, pre-fix) is virtually identical + to this cycle's figure (73.72%/64.69%) — a small net improvement, not a worsening. +- No production source file is excluded from coverage measurement to produce this figure; no + `coverage.config` or `.csproj` coverage-exclude was added or modified. + +This mirrors the `#328` `StoreWrapper` branch-floor exception precedent exactly: a pre-existing, +broad, unrelated-to-the-immediate-fix coverage shortfall is ratified rather than requiring the +minor-audit fix's Scope-Lock to expand into unrelated WinForms/UI code. The true all-first-party +repo-wide figure is measured by the PR CI full-suite run, per the disposition's citation. + +**Carried-forward, non-blocking finding — fallback-clamp duplication (CR-1).** Unchanged from cycle +1; see `code-review.2026-07-20T19-30.md`. + +**Carried-forward, informational note — `PopulateAndSelectFolder` empty-array gap (CR-3).** +Unchanged from cycle 1, unaffected by this cycle (no production code modified); see +`code-review.2026-07-20T19-30.md`. + +**Evidence-quality note — csharpier baseline-resolution attribution.** See Section 3. Non-blocking. + +**Incidental scope note — agent-memory files in the diff.** `git diff --name-status` shows +`.claude/agent-memory/feature-review/MEMORY.md` (modified) and +`.claude/agent-memory/feature-review/project_rescoping-to-instrumented-package-does-not-always-clear-floor.md` +(added) inside this branch's diff — version-controlled reviewer-memory artifacts written by the +cycle-1 `feature-review` pass and swept into the `8a1b7b98` remediation commit alongside the intended +remediation changes. Markdown, non-production, exempt from the 500-line and coverage rules; no policy +or source file was affected. Not a finding. + +## 9. Summary of Changes + +- Range audited: `bd43572498474be89d80e1f9620dffb132ade377..8a1b7b98b7d12dac69fd1bee5d5f109d4095c3c6` + (two commits: `8f34f8ef` fix, `8a1b7b98` remediation). +- Merge-base recomputed independently: `git merge-base HEAD origin/main` = + `bd43572498474be89d80e1f9620dffb132ade377` (matches the supplied base; unchanged from cycle 1). +- Changed languages with files in the branch diff: C# only (2 `.cs` files: 1 production, 1 test; + unchanged file count from cycle 1). 53 Markdown files (plan, issue, evidence, and two committed + agent-memory files) were also added/modified. +- `git status` shows a clean working tree at HEAD; the code under test is unchanged since the + remediation evidence was produced. +- **PR-context summary correction (recurring C#-as-docs misclassification, again):** the refreshed + `artifacts/pr_context.summary.txt` again reported `Core logic changes: 0 files`, repeating the same + generator misclassification found and corrected in cycle 1 (`pr_context` artifacts do not + auto-update after a remediation commit and were freshly regenerated for this cycle, reproducing the + same defect rather than inheriting the prior correction). `git diff --numstat` confirms 2 changed + `.cs` files, unchanged in count from cycle 1. The summary was corrected in place (annotated + `CORRECTED BY feature-review 2026-07-20 (R4 re-audit)`) with space-free `.cs` paths in the + generator's `(+N/-N)` format so downstream language-detection recognizes C# as a changed language. +- Production code: byte-identical to cycle 1 (verified via `git diff bd435724..8a1b7b98 -- + QuickFiler/Controllers/QfcItemController.FolderHandling.cs`). Test code: one new test method added + (`PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke`), plus 26 + comment-only line removals from 12 pre-existing tests (disclosed, diff-verified). + +## 10. Compliance Verdict + +**Overall verdict: PASS (go). Blocking findings: 0.** + +- General Unit Test Policy Compliance: PASS. +- General Code Change Policy Compliance: PASS. +- Language-Specific (C#) Code Change Policy Compliance: PASS (nullable dispositioned per amended + AC-5 scope note). +- Language-Specific (C#) Unit Test Policy Compliance: PASS. +- Test Coverage Detail: PASS at the touched-class scope (95.95% line / 76.19% branch, both clear + floor); FAIL at the `QuickFiler` package-wide and canonical repo-wide scopes, dispositioned as a + ratified, non-blocking, pre-existing exception (Section 8) consistent with the `#328` precedent. +- Test Execution Metrics: PASS (542/542, zero regressions). +- Code Quality Checks: PASS (one carried-forward, non-blocking low-severity finding). +- Gaps and Exceptions: one ratified exception (ledgered), two carried-forward non-blocking findings, + one evidence-quality note, one incidental scope note — none blocking. + +**Remediation trigger: not triggered.** No new `remediation-inputs..md` is produced for +this cycle; zero blocking findings remain. + +## Rejected Scope Narrowing + +The coordinator's re-audit delegation explicitly stated "execute the full `feature-review-workflow` +SKILL contract end-to-end again, same inputs as your original review, no scope narrowing" and did not +attempt to narrow scope to any plan, task, phase, or file subset. No caller instruction marked any +language's coverage as "plan scope only," "out of scope" (as a narrowing instruction — the `N/A - out +of scope` rows above reflect a factual absence of changed files for those languages, not an +instructed narrowing), "informational only," or equivalent. No scope-narrowing instruction was +detected or rejected in this review cycle. + +## Appendix A: Test Inventory + +Tests directly relevant to this fix and this remediation cycle, in +`QuickFiler.Test/Controllers/QfcItemController.FolderHandlingTests.cs`: + +| Test | Status this cycle | Purpose | +|---|---|---| +| `PopulateAndSelectFolder_SingleItemNoPredeterminedMatch_SelectsIndexZeroWithoutThrowing` | Pre-existing (cycle 1), re-verified | AC-1/AC-4: single-suggestion fallback selects index 0 without throwing. | +| `AssignFolderComboBox_WhenSingleSuggestionNoPredeterminedMatch_SelectsIndexZero` | Pre-existing (cycle 1), re-verified | AC-1/AC-2: single-suggestion fallback selects index 0 via the viewer seam. | +| `PopulateAndSelectFolder_ExactMatchAtIndexZero` | Pre-existing, re-verified | AC-3: predetermined-folder match is preselected. | +| `PopulateAndSelectFolder_AllMissingPredetermined_SelectsIndexOne` | Pre-existing, re-verified | AC-3: multi-suggestion, no predetermined match, index 1 fallback preserved. | +| `PopulateAndSelectFolder_EmptyArray_ThrowsOnIndexOneSelection` | Pre-existing, re-verified | Documents the pre-existing, unrelated empty-array throw in the unused static helper (CR-3). | +| `AssignFolderComboBox_WhenNoPredeterminedFolder_SelectsTopSuggestionViaViewer` | Pre-existing, re-verified | AC-3: multi-suggestion fallback via the viewer seam. | +| `AssignFolderComboBox_WhenPredeterminedFolderPresent_SelectsPreDeterminedFolder` | Pre-existing, re-verified | AC-3: predetermined-folder match via the viewer seam. | +| `AssignFolderComboBox_WhenFolderHandlerNull_NoOps` | Pre-existing, re-verified | Guard-clause behavior when `_folderHandler` is unset. | +| `PopulateFolderComboBox_WhenFactorySucceeds_LoadsHandlerAndAssignsComboFromViewer` | Pre-existing, re-verified | Covers the `InvokeRequired == false` / `else` branch of `PopulateFolderComboBox`. | +| `PopulateFolderComboBox_WhenInvokeRequired_MarshalsAssignFolderComboBoxViaInvoke` | **New this cycle (R1)** | Closes the `InvokeRequired == true` branch-coverage gap identified in cycle 1's audit; raises class-level branch coverage 73.81% -> 76.19%. | + +Full suite: 542 tests total (541 pre-existing across the `QuickFiler.Test` project + 1 new this +cycle), 542 passed, 0 failed (`evidence/qa-gates/remediation-vstest-coverage-final.2026-07-20T18-40.md`). + +## Appendix B: Toolchain Commands Reference + +- `git merge-base HEAD origin/main` — recomputed base SHA `bd43572498474be89d80e1f9620dffb132ade377`. +- `git diff --numstat bd435724..8a1b7b98` — enumerated the full branch diff (2 `.cs`, 53 `.md`). +- `git diff bd435724..8a1b7b98 -- ` / `git diff 8f34f8ef..8a1b7b98 -- ` — read the + cumulative and remediation-only diffs. +- `wc -l ` — file-size checks at HEAD. +- `csharpier check .` (v1.3.0) — independently reproduced 0 errors on this review host. +- `git merge-base --is-ancestor 78e847ec bd435724` — confirmed the cited commit predates the + merge-base (Section 3 evidence-quality note). +- Python `xml.etree.ElementTree` parse of `artifacts/csharp/coverage.xml` — independent re-derivation + of report-level, `QuickFiler`-package-level, and `QfcItemController.FolderHandling.cs` + class-level LINE/BRANCH counters (Section 5). +- `python3 -c "import json; ..."` against `artifacts/orchestration/orchestrator-state.json` — + confirmed the `human_interaction` `scope_change` ratification record (Section 8). +- `MSBuild.exe TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" + /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true /m` — analyzer gate (executor evidence). +- `MSBuild.exe TaskMaster.sln /t:Rebuild /p:Configuration=Debug /p:Platform="Any CPU" + /p:Nullable=enable /p:TreatWarningsAsErrors=true` — nullable gate (executor evidence). +- `dotnet-coverage collect -f cobertura -s coverage-exclude-deedle.xml -o + remediation-final-coverage.cobertura.xml -- vstest.console.exe + QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /InIsolation` — test + coverage collection (executor + evidence). +- Executor toolchain evidence (not re-run except csharpier, above): `remediation-csharpier-final`, + `remediation-analyzer-final`, `remediation-nullable-final`, `remediation-vstest-coverage-final`, + `remediation-coverage-conversion`, `remediation-coverage-delta`, `remediation-regression-check` + under `evidence/qa-gates/`.