Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions QuickFiler.Test/Controllers/QfcHomeControllerMetricsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,15 @@ public async Task WriteMetricsAsync_UsesInjectedClock_ForDateAndTimeStamps()
var fake = FixedClock();
controller.TimeProvider = fake;
var expectedLocal = fake.GetLocalNow().LocalDateTime;
// Issue #742: the oracle must name the invariant culture explicitly. An uncultured
// ToString(format) here resolves the "/" and ":" placeholders against the operator's
// locale, exactly as the pre-fix production code did, so the two would agree under any
// culture and the assertion could not detect the defect it is meant to pin.
var expectedDataLineBeg =
expectedLocal.ToString("MM/dd/yyyy") + "," + expectedLocal.ToString("HH:mm") + ",";
expectedLocal.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture)
+ ","
+ expectedLocal.ToString("HH:mm", CultureInfo.InvariantCulture)
+ ",";

// Act
await controller.WriteMetricsAsync("metrics.csv");
Expand Down Expand Up @@ -274,8 +281,13 @@ public void QuickFileMetrics_WRITE_UsesInjectedClock_ForDataLine()
var fake = FixedClock();
controller.TimeProvider = fake;
var expectedLocal = fake.GetLocalNow().LocalDateTime;
// Issue #742: see the note in the sibling test above. The oracle names the invariant
// culture so it cannot silently track a culture-dependent production rendering.
var expectedDataLineBeg =
expectedLocal.ToString("MM/dd/yyyy") + "," + expectedLocal.ToString("HH:mm") + ",";
expectedLocal.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture)
+ ","
+ expectedLocal.ToString("HH:mm", CultureInfo.InvariantCulture)
+ ",";

// Act
controller.QuickFileMetrics_WRITE("metrics.csv");
Expand Down
461 changes: 461 additions & 0 deletions QuickFiler.Test/Controllers/QuickFilerInvariantCultureIssue742Tests.cs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions QuickFiler.Test/QuickFiler.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@
<Compile Include="Controllers\QfcItemController.InitializationTests.Part2.cs" />
<Compile Include="Controllers\QfcItemController.InitializationTests.Part3.cs" />
<Compile Include="Controllers\QfcItemController.ViewerSetupTests.cs" />
<Compile Include="Controllers\QuickFilerInvariantCultureIssue742Tests.cs" />
<Compile Include="Controllers\QfcItemController.EventHandlersTests.cs" />
<Compile Include="Controllers\QfcItemController.SearchFocusRegressionTests.cs" />
<Compile Include="Controllers\QfcItemController.SearchDismissalTests.cs" />
Expand Down
8 changes: 4 additions & 4 deletions QuickFiler/Controllers/EfcHomeController.Metrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ List<MailItemHelper> moved
return Array.Empty<string>();
}

var curDateText = currentDateTime.ToString("MM/dd/yyyy");
var curTimeText = currentDateTime.ToString("HH:mm");
var curDateText = currentDateTime.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture);
var curTimeText = currentDateTime.ToString("HH:mm", CultureInfo.InvariantCulture);
var dataLineBeg = curDateText + "," + curTimeText + ",";

var duration = elapsedSeconds;
Expand All @@ -115,8 +115,8 @@ List<MailItemHelper> moved
+ $",SingleSorted,{durationText},{durationMinutesText},"
+ $"{QfcCollectionController.xComma(itemInfo.ToRecipientsName)},"
+ $"{QfcCollectionController.xComma(itemInfo.SenderName)},Email,{folderText},"
+ $"{itemInfo.SentDate.ToString("MM/dd/yyyy")},"
+ $"{itemInfo.SentDate.ToString("HH:mm:ss")}"
+ $"{itemInfo.SentDate.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture)},"
+ $"{itemInfo.SentDate.ToString("HH:mm:ss", CultureInfo.InvariantCulture)}"
)
.ToArray();
}
Expand Down
5 changes: 3 additions & 2 deletions QuickFiler/Controllers/EfcItemController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
Expand Down Expand Up @@ -604,12 +605,12 @@ public string Sender

public string SentDate
{
get => _itemInfo.SentDate.ToString("MM/dd/yyyy");
get => _itemInfo.SentDate.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture);
}

public string SentTime
{
get => _itemInfo.SentDate.ToString("HH:mm");
get => _itemInfo.SentDate.ToString("HH:mm", CultureInfo.InvariantCulture);
}

public string Subject
Expand Down
10 changes: 7 additions & 3 deletions QuickFiler/Controllers/QfcCollectionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -232,7 +233,10 @@ internal bool TryGetMoveReadiness(out string notifications)
strNotifications
+ grp.ItemController.ItemNumber
+ " "
+ grp.ItemController.Mail.SentOn.ToString("MM/dd/yyyy")
+ grp.ItemController.Mail.SentOn.ToString(
"MM/dd/yyyy",
CultureInfo.InvariantCulture
)
+ " "
+ grp.ItemController.Mail.Subject
+ Environment.NewLine;
Expand Down Expand Up @@ -1293,7 +1297,7 @@ public void ToggleExpansionStyle(int itemIndex, Enums.ToggleState desiredState)
var c = _itemGroups[itemIndex].ItemController;
var msg =
$"Cannot expand item with index {itemIndex} because UI is not active.\n"
+ $"Controller for message \"{c.ItemHelper.Subject} sent on {c.ItemHelper.SentDate.ToString("MM/dd/yyyy")} at {c.ItemHelper.SentDate.ToString("HH:mm")} "
+ $"Controller for message \"{c.ItemHelper.Subject} sent on {c.ItemHelper.SentDate.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture)} at {c.ItemHelper.SentDate.ToString("HH:mm", CultureInfo.InvariantCulture)} "
+ $"by {c.ItemHelper.SenderName} has a value of {c.IsActiveUI} for {nameof(c.IsActiveUI)}";
throw new InvalidOperationException(msg);
}
Expand Down Expand Up @@ -2299,7 +2303,7 @@ ref AppointmentItem olAppointment
var dataLine =
$"{dataLineBeg} {xComma(qf.ItemHelper.Subject)},QuickFiled,{durationText},{durationMinutesText},";
dataLine +=
$"{xComma(qf.ItemHelper.ToRecipientsName)},{xComma(qf.ItemHelper.SenderName)},Email,{xComma(qf.SelectedFolder)},{qf.ItemHelper.SentDate.ToString("MM/dd/yyyy")},{qf.ItemHelper.SentDate.ToString("HH:mm")}";
$"{xComma(qf.ItemHelper.ToRecipientsName)},{xComma(qf.ItemHelper.SenderName)},Email,{xComma(qf.SelectedFolder)},{qf.ItemHelper.SentDate.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture)},{qf.ItemHelper.SentDate.ToString("HH:mm", CultureInfo.InvariantCulture)}";

strOutput[k] = dataLine;
}
Expand Down
10 changes: 7 additions & 3 deletions QuickFiler/Controllers/QfcHomeController.Metrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ public void QuickFileMetrics_WRITE(string filename)
//var curDateText = DateTime.Now.ToString("MM/dd/yyyy");
//var curTimeText = DateTime.Now.ToString("hh:mm");
//dataLineBeg = curDateText + "," + curTimeText + ",";
dataLineBeg = $"{now:MM/dd/yyyy},{now:HH:mm},";
dataLineBeg =
now.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture)
+ ","
+ now.ToString("HH:mm", CultureInfo.InvariantCulture)
+ ",";

if (!Globals.FS.SpecialFolders.TryGetValue("MyDocuments", out var folderRoot))
{
Expand Down Expand Up @@ -122,9 +126,9 @@ public async Task WriteMetricsAsync(string filename)

// Create a line of comma seperated valued to store data
var now = TimeProvider.GetLocalNow().LocalDateTime;
curDateText = now.ToString("MM/dd/yyyy");
curDateText = now.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture);

curTimeText = now.ToString("HH:mm");
curTimeText = now.ToString("HH:mm", CultureInfo.InvariantCulture);

dataLineBeg = curDateText + "," + curTimeText + ",";

Expand Down
3 changes: 2 additions & 1 deletion QuickFiler/Controllers/QfcItemController.ViewerSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
Expand Down Expand Up @@ -473,6 +474,6 @@ private void DetachWebResourceRequestedHandler()
}

internal string GetItemSummary() =>
$"Subject: {ItemHelper.Subject} sent on {ItemHelper.SentDate.ToString("MM/dd/yyyy")} at {ItemHelper.SentDate.ToString("HH:mm")} by {ItemHelper.SenderName}";
$"Subject: {ItemHelper.Subject} sent on {ItemHelper.SentDate.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture)} at {ItemHelper.SentDate.ToString("HH:mm", CultureInfo.InvariantCulture)} by {ItemHelper.SenderName}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Baseline CSharpier Check (issue #742, [P0-T4])

Timestamp: 2026-09-14T01-58

Command: `pwsh -NoProfile -Command 'dotnet tool run csharpier check .'`

EXIT_CODE: 0

Output Summary: `Checked 1634 files in 4554ms.` CSharpier reported no formatting diff anywhere in the
tree. This is the read-only baseline captured before any write-mode formatter runs in this plan, so
the Phase 5 `csharpier format` / `csharpier check` gate ([P5-T1]) measures only drift introduced by
this change rather than pre-existing drift.

Acceptance: none stated by the task; this is a baseline capture only.
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Baseline Discovery-Count Controls (issue #742, [P0-T9])

Timestamp: 2026-09-14T02-05

All eleven commands below were run against the unmodified tree, before [P1-T1] created or modified
any Write Set file. Branch HEAD at capture time: `6d6da116f9084db5da0b2f25d80941c41c5fbf61`.

Zero-match reading convention: `git grep -c PATTERN -- path` prints no line at all, and exits `1`,
when `path` has zero matching lines; it never prints a `0`.

Every command below was executed with `git -C <worktree>` from the item worktree
`C:/Users/DanMoisan/repos/TaskMaster-wt/bugs-2026-09-11-item-742`. The `-C` form is an invocation
detail of this execution environment and does not change any pattern or pathspec.

---

## Control 1 — uncultured `ToString` sweep across the five production files

Command: `git grep -c '[.]ToString[(]@\?"[^"]*[:/.\-][^"]*"[)]' -- QuickFiler/Controllers/QfcHomeController.Metrics.cs QuickFiler/Controllers/EfcHomeController.Metrics.cs QuickFiler/Controllers/QfcItemController.ViewerSetup.cs QuickFiler/Controllers/QfcCollectionController.cs QuickFiler/Controllers/EfcItemController.cs`

EXIT_CODE: 0

Output Summary:

```
QuickFiler/Controllers/EfcHomeController.Metrics.cs:4
QuickFiler/Controllers/EfcItemController.cs:2
QuickFiler/Controllers/QfcCollectionController.cs:3
QuickFiler/Controllers/QfcHomeController.Metrics.cs:4
QuickFiler/Controllers/QfcItemController.ViewerSetup.cs:1
```

Per-file distribution 4 / 4 / 1 / 3 / 2 (14 matching lines total), exactly as the plan and
`spec.md` state. The `@\?` escape is load-bearing: `git grep` runs in basic-regular-expression mode,
in which a bare `?` is a literal character rather than an optional-atom quantifier.

## Control 2 — interpolated specifier token

Command: `git grep -c '[{]now:' -- QuickFiler/Controllers/QfcHomeController.Metrics.cs`

EXIT_CODE: 0

Output Summary: `QuickFiler/Controllers/QfcHomeController.Metrics.cs:1` — expected 1.

## Control 3 — pre-existing InvariantCulture sites, QfcHomeController.Metrics.cs

Command: `git grep -c 'CultureInfo[.]InvariantCulture' -- QuickFiler/Controllers/QfcHomeController.Metrics.cs`

EXIT_CODE: 0

Output Summary: `QuickFiler/Controllers/QfcHomeController.Metrics.cs:4` — expected 4.

## Control 4 — pre-existing InvariantCulture sites, EfcHomeController.Metrics.cs

Command: `git grep -c 'CultureInfo[.]InvariantCulture' -- QuickFiler/Controllers/EfcHomeController.Metrics.cs`

EXIT_CODE: 0

Output Summary: `QuickFiler/Controllers/EfcHomeController.Metrics.cs:2` — expected 2.

## Control 5 — `Compile Include` count in the test project file

Command: `git grep -c -F 'Compile Include' -- QuickFiler.Test/QuickFiler.Test.csproj`

EXIT_CODE: 0

Output Summary: `QuickFiler.Test/QuickFiler.Test.csproj:176` — expected 176 per the coordinator
amendment dated 2026-09-14, which re-derived this figure after items 871, 873 and 877 each added an
unrelated `<Compile Include>` entry to this file. The plan body's original figure of 172 is
superseded.

## Control 6 — new test file not yet referenced by the project file

Command: `git grep -c -F 'QuickFilerInvariantCultureIssue742Tests' -- QuickFiler.Test/QuickFiler.Test.csproj`

EXIT_CODE: 1

Output Summary: prints no line — expected: prints no line, exits 1.

## Control 7 — no `System.Globalization` using directive, QfcItemController.ViewerSetup.cs

Command: `git grep -c -F 'using System.Globalization;' -- QuickFiler/Controllers/QfcItemController.ViewerSetup.cs`

EXIT_CODE: 1

Output Summary: prints no line — expected: prints no line, exits 1.

## Control 8 — no `System.Globalization` using directive, QfcCollectionController.cs

Command: `git grep -c -F 'using System.Globalization;' -- QuickFiler/Controllers/QfcCollectionController.cs`

EXIT_CODE: 1

Output Summary: prints no line — expected: prints no line, exits 1.

## Control 9 — no `System.Globalization` using directive, EfcItemController.cs

Command: `git grep -c -F 'using System.Globalization;' -- QuickFiler/Controllers/EfcItemController.cs`

EXIT_CODE: 1

Output Summary: prints no line — expected: prints no line, exits 1.

## Control 10 — self-referential uncultured test oracle, both occurrences

Command: `git grep -c -F 'expectedLocal.ToString("MM/dd/yyyy") + "," + expectedLocal.ToString("HH:mm") + ",";' -- QuickFiler.Test/Controllers/QfcHomeControllerMetricsTests.cs`

EXIT_CODE: 0

Output Summary: `QuickFiler.Test/Controllers/QfcHomeControllerMetricsTests.cs:2` — expected 2, the
identical expected-value construction in the two sibling test methods.

## Control 11 — no InvariantCulture in the metrics test file

Command: `git grep -c -F 'CultureInfo.InvariantCulture' -- QuickFiler.Test/Controllers/QfcHomeControllerMetricsTests.cs`

EXIT_CODE: 1

Output Summary: prints no line — expected: prints no line, exits 1.

---

Acceptance: this artifact exists, records all eleven commands with `Timestamp:`, `Command:`,
`EXIT_CODE:` and `Output Summary:`, every observed figure equals the figure stated by the task
(with control 5 read against the coordinator's amended value of 176), and it was written before
[P1-T1], which is the first task in this plan that creates or modifies any Write Set file.

This artifact is the baseline evidence artifact required by the Acceptance Criteria item covering
the pre-fix discovery-count figures.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# ExcludeFromCodeCoverage Confirmation (issue #742, [P0-T10])

Timestamp: 2026-09-14T02-05

Command: `git grep -c -F '[ExcludeFromCodeCoverage]' -- QuickFiler/Controllers/QfcCollectionController.cs QuickFiler/Controllers/EfcItemController.cs`

EXIT_CODE: 0

Output Summary:

```
QuickFiler/Controllers/EfcItemController.cs:1
QuickFiler/Controllers/QfcCollectionController.cs:1
```

Acceptance: the command prints exactly one matching line for each of the two files and `EXIT_CODE`
is 0 — satisfied. `git grep` orders its output by pathspec resolution rather than by argument
order, so `EfcItemController.cs` precedes `QfcCollectionController.cs` here; both required lines are
present with a count of 1 each.

This confirms the coverage-signal framing used by [P0-T8] and [P5-T4]: both classes carry a
type-level `[ExcludeFromCodeCoverage]` attribute, are therefore absent from the Cobertura report,
and their quality signal is the named-test pass/fail state rather than a coverage percentage. This
change does not add, remove, or modify either attribute.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Baseline MSBuild Analyzer State (issue #742, [P0-T5])

Timestamp: 2026-09-14T02-00

Command: `pwsh -NoProfile -Command '$out = msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true 2>&1; $exit = $LASTEXITCODE; $out | Select-String -Pattern "^\s*\d+ Error\(s\)\s*$" | ForEach-Object { $_.Line.Trim() }; Write-Output "EXITCODE=$exit"'`

EXIT_CODE: 0

Output Summary: transcribed summary line `0 Error(s)`, exit code `0`. The same command additionally
printed `0 Warning(s)`, recorded here for completeness rather than as a baseline floor.

Acceptance: none stated by the task; this is a baseline capture only.

## Pre-existing cold-worktree blocker encountered and resolved before this measurement

The first run of this exact command in this worktree printed `2 Error(s)` and exited `1`. The two
errors were:

```
CSC : error CS0006: Metadata file '..\packages\Meziantou.Analyzer.3.0.203\analyzers\dotnet\roslyn5.0\cs\Meziantou.Analyzer.dll' could not be found [...\VBFunctions\VBFunctions.csproj]
CSC : error CS0006: Metadata file '..\packages\Meziantou.Analyzer.3.0.203\analyzers\dotnet\roslyn5.0\cs\Meziantou.Analyzer.dll' could not be found [...\UtilitiesCS\UtilitiesCS.csproj]
```

Diagnosis: an `<Analyzer Include>` HintPath skew that predates this branch. Fifteen of sixteen
first-party project files name `Meziantou.Analyzer.3.0.203` in their `<Analyzer Include>` item while
`packages.config`, the `<Import>` and the `<Error Condition>` guard in the same files all name
`3.0.235`, which is the version `nuget restore` installs. `TaskMaster.csproj` alone already names
`3.0.235`. Only two errors surface because the two failing projects are upstream of every other
project in the graph, so the rest are skipped rather than compiled.

Verification that this is pre-existing and not introduced by this change:

- `git grep -c -F 'Meziantou.Analyzer.3.0.203' origin/main -- UtilitiesCS/UtilitiesCS.csproj VBFunctions/VBFunctions.csproj QuickFiler/QuickFiler.csproj QuickFiler.Test/QuickFiler.Test.csproj` printed `1` for each of the four paths, so `origin/main` carries the identical skew.
- `git diff --name-only origin/main...HEAD -- "*.csproj" "*.config" "*.props" "*.targets"` printed no line, so this branch has modified no project or package file.

Remedy applied (provisioning only, no tracked file edited):

`pwsh -NoProfile -Command 'nuget install Meziantou.Analyzer -Version 3.0.203 -OutputDirectory packages -DependencyVersion Ignore'` — exit code `0`.

This provisions the HintPath-named analyzer version into the repository's `packages/` tree, which
`git check-ignore -v` confirms is ignored at `.gitignore:191` (`**/[Pp]ackages/*`). After the
install, `git status --porcelain --untracked-files=all -- "*.csproj" "*.config" "*.props" "*.targets" "packages"`
printed no line, confirming zero tracked files changed. The mandated msbuild command was then re-run
unmodified and produced the `0 Error(s)` result recorded above.

This is the same class of cold-worktree provisioning as the repo-local .NET SDK install recorded in
`toolchain-bootstrap.2026-09-12T16-09.md`. It edits no source file, no project file, and nothing
belonging to any sibling work item, and it is outside this change's Write Set because it changes no
tracked file at all.
Loading
Loading