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
3 changes: 3 additions & 0 deletions .claude/agent-memory/atomic-executor/MEMORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Atomic Executor Memory Index

- [dotnet-coverage denominator nondeterminism](project_dotnet_coverage_denominator_nondeterminism.md) — Invoke-MSTestWithCoverage repo line-rate swings (47% vs 81%) from double-counted denominator; re-baseline via git-stash, trust per-class rates
- [IApplicationGlobals member forces implementers](project_iapplicationglobals_member_forces_implementers.md) — adding an IApplicationGlobals member breaks 7 hand-written test-double stubs (QuickFiler/TaskMaster/UtilitiesCS .Test) beyond scope lock; Moq mocks auto-implement

- [Project Build/Test Env](project_build_test_env.md) — git-bash toolchain quirks: MSBuild dash-switches, MSYS_NO_PATHCONV for vstest, csharpier v1 syntax, forced-nullable Rebuild + Debug-restore, legacy csproj Compile includes, IVT for Moq, C# 7.3 in QuickFiler.Test
- [Outlook `Action` ambiguity](project_outlook_action_ambiguity.md) — bare non-generic `Action` is CS0104-ambiguous in Outlook-interop files; use `System.Action` (Action<MailItem> is fine)
- [init/record struct fails CS0518 on net48](project_record_struct_isexternalinit_netfx.md) — ANY init accessor (positional record, record struct, or explicit { get; init; }) needs IsExternalInit (absent on this net48 target, no polyfill); use constructor-initialized readonly struct with get-only props
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
name: dotnet-coverage-denominator-nondeterminism
description: Invoke-MSTestWithCoverage repo-wide line-rate is nondeterministic across runs due to dotnet-coverage double-counting the instrumented denominator; re-baseline via git-stash for a trustworthy delta
metadata:
type: project
---

The repo coverage path `scripts/vscode/Invoke-MSTestWithCoverage.ps1` (dotnet-coverage collect wrapping vstest over all 7 `*.Test.dll`, Workers=0) can emit a WILDLY different repo-wide `line-rate` between runs of the SAME code, because dotnet-coverage instruments all runtime-loaded modules and its cross-assembly merge is order/parallelism-sensitive and sometimes DOUBLE-COUNTS lines.

Concrete #261 F1 observation: one baseline run reported 47.16% with `lines-valid=180246` (UtilitiesCS package showed an implausible 141,188 valid lines); a clean re-measure of the exact same pre-change tree reported 81.02% with `lines-valid=97933`. The ~98k denominator is the correct de-duplicated value; the 180k run was the double-count anomaly.

**Why:** dotnet-coverage merge nondeterminism inflates the denominator, halving the apparent coverage. The per-CLASS line-rate for touched files stays stable and correct regardless.

**How to apply:**
- Never trust a single repo-wide coverage number for a no-regression delta. Run coverage at least twice and confirm the denominator (`lines-valid`) reproduces.
- For an apples-to-apples baseline-vs-postchange delta, `git stash push -u` the code changes (NOT the plan/evidence .md), rebuild, re-run coverage for a clean baseline, then `git stash pop` and rebuild. This gives both measurements under the same (correct) denominator.
- Rely on per-class coverage (parse the Cobertura `<class>` line hits with a small Python/awk script) for new-code >=90% and no-regression proof — it is stable when the overall percentage is not.
- Related: [[project_qfc227_coverage_tooling]], [[project_coverage_firstparty_denominator_method]], [[project_utilitiescs_test_parallelism_flakiness]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
name: iapplicationglobals-member-forces-implementers
description: Adding a member to IApplicationGlobals forces edits to ~7 hand-written test-double implementers beyond any scope lock; Moq mocks auto-implement and need no change
metadata:
type: project
---

`IApplicationGlobals` (UtilitiesCS/Interfaces/IGlobals/IApplicationGlobals.cs) has multiple hand-written concrete implementers in test projects. Adding any new interface member breaks compilation of ALL of them, forcing edits beyond a plan's scope lock.

Known hand-written implementers (as of #261, 2026-07-07):
- QuickFiler.Test: FakeApplicationGlobals in EfcHomeControllerLifecycleTests.cs, EfcHomeControllerMetricsTests.cs, EfcHomeControllerTests.cs (member style `=> null;`)
- TaskMaster.Test: StubApplicationGlobals in AppOlObjectsCoverageTests.cs, AppOlObjectsTests.cs (`=> throw new NotSupportedException();`), AppToDoObjectsTestDoubles.cs (`=> throw new NotSupportedException();`)
- UtilitiesCS.Test: StubGlobals in EmailIntelligence/EmailDataMiner_TestSupport.cs (`=> throw new NotImplementedException();`)

**Why:** these are `: IApplicationGlobals` classes, not Moq mocks. `Mock<IApplicationGlobals>` / `Mock.Of<IApplicationGlobals>()` auto-implement new members (default/null) and need NO edit.

**How to apply:** when a plan adds a member to IApplicationGlobals but its scope lock omits these 7 files, add a minimal member to each matching that file's existing style. This is a mechanically-necessary consequence of the interface change (complete-and-escalate past preflight), not replanning — record it as a scope addition. A planner SHOULD list these 7 files in the scope lock up front.
1 change: 1 addition & 0 deletions .claude/agent-memory/feature-review/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
- [TaskMaster validator memories are cross-repo](project_taskmaster-validator-memories-are-cross-repo.md) — the `validate_orchestration_artifacts`/heading-template memories describe a different repo (mix-calculator/drm-copilot); TaskMaster's only real gate is `validate-feature-review-coverage.ps1` (3-path advertisement + per-language coverage-row PASS/FAIL/no-narrowing check)
- [Stale caller-supplied merge-base](project_stale-caller-merge-base.md) — #244 cycle 2: caller SHA was one merged PR behind actual `main`; always recompute via `git merge-base HEAD origin/main`, don't trust the supplied value
- [modified-workflow green-run gate is manual](project_modified-workflow-green-run-manual-check.md) — #267: Test-ModifiedWorkflowNeedsGreenRun.ps1 doesn't exist in TaskMaster; check via `git diff --name-only` for `.github/workflows/**`, not the summary's truncated top-10 overview bullets
- [same-commit differing-outcome flake check](project_same-commit-differing-outcome-flake-check.md) — #261 cycle 1: two evidence runs at the identical SHA disagreeing on one test's outcome proves environment-flake, not regression; corroborate "pre-existing failure" claims this way before writing PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
name: same-commit-differing-outcome-flake-check
description: technique for confirming a failing test is environment-flaky (not code-caused) when two runs at the identical commit disagree
metadata:
type: project
---

When a full-suite run shows a failure and the caller claims it's "pre-existing," don't just trust
a prose claim — look for two evidence runs recorded at the *identical* commit SHA with *different*
pass/fail outcomes for that one test. If found, that is direct proof the failure is a function of
local environment/COM-server state, not of any code diff, because there is zero code difference
between the two runs.

Example (#261 F1 remediation cycle 1): the entry-cycle audit ran the full suite at commit `88366ad4`
and reported 0 failures; the remediation cycle's own Phase-0 baseline re-ran the full suite at the
same commit `88366ad4` and reported 1 failure (`LiveHookup_OnSta_CompletesAndDoesNotBlockStaBeyondThreshold`,
a live-Outlook COM/STA integration test). Same SHA, different outcome, zero code delta -> environment-
dependent, not a regression. Also cross-check the diff's changed-file list to confirm the failing
test's file (and its dependency path) isn't touched at all.

**How to apply:** whenever a policy/feature audit needs to disposition a "pre-existing failure"
claim, don't stop at "the baseline evidence doc says it failed before too" — check whether that
baseline run was actually at the pre-feature merge-base or merely at an earlier point within the
same feature's commits, and look for a same-SHA outcome mismatch across the review's own evidence
files as corroboration before writing PASS/not-Blocking.
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ public Task LoadAsync(bool parallel)
public IAppItemEngines Engines => null;

public IntelligenceConfig IntelRes => null;

public IStoreDisableService StoreDisable => null;
}

private sealed class FakeFileSystemFolderPaths : IFileSystemFolderPaths
Expand Down
2 changes: 2 additions & 0 deletions QuickFiler.Test/Controllers/EfcHomeControllerMetricsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ public Task LoadAsync(bool parallel)
public IAppItemEngines Engines => null;

public IntelligenceConfig IntelRes => null;

public IStoreDisableService StoreDisable => null;
}

private sealed class FakeFileSystemFolderPaths : IFileSystemFolderPaths
Expand Down
2 changes: 2 additions & 0 deletions QuickFiler.Test/Controllers/EfcHomeControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ public Task LoadAsync(bool parallel)
public IAppItemEngines Engines => null;

public global::UtilitiesCS.EmailIntelligence.IntelligenceConfig IntelRes => null;

public global::UtilitiesCS.IStoreDisableService StoreDisable => null;
}
}
}
2 changes: 2 additions & 0 deletions TaskMaster.Test/AppGlobals/AppOlObjectsCoverageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ private sealed class StubApplicationGlobals : IApplicationGlobals
public IAppItemEngines Engines => throw new NotSupportedException();

public IntelligenceConfig IntelRes => IntelResInstance;

public IStoreDisableService StoreDisable => throw new NotSupportedException();
}

private sealed class StubIntelligenceConfig : IntelligenceConfig
Expand Down
2 changes: 2 additions & 0 deletions TaskMaster.Test/AppGlobals/AppOlObjectsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ private sealed class StubApplicationGlobals : IApplicationGlobals
public IAppItemEngines Engines => throw new NotSupportedException();

public IntelligenceConfig IntelRes => IntelResInstance;

public IStoreDisableService StoreDisable => throw new NotSupportedException();
}

private sealed class StubIntelligenceConfig : IntelligenceConfig
Expand Down
2 changes: 2 additions & 0 deletions TaskMaster.Test/AppGlobals/AppToDoObjectsTestDoubles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ public StubApplicationGlobals(IFileSystemFolderPaths fs, IOlObjects ol)
public IAppItemEngines Engines => throw new NotSupportedException();

public IntelligenceConfig IntelRes => throw new NotSupportedException();

public IStoreDisableService StoreDisable => throw new NotSupportedException();
}

internal sealed class ReflectionRealProxy : RealProxy
Expand Down
7 changes: 7 additions & 0 deletions TaskMaster/AppGlobals/ApplicationGlobals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using UtilitiesCS;
using UtilitiesCS.EmailIntelligence;
using UtilitiesCS.HelperClasses;
using UtilitiesCS.OutlookObjects.Store;
using UtilitiesCS.Threading;

namespace TaskMaster
Expand Down Expand Up @@ -112,6 +113,9 @@ protected internal virtual void LoadBasicMethod()
_events = new AppEvents(this);
_quickFilerSettings = new AppQuickFilerSettings();
Engines = new AppItemEngines(this);
// why: issue #261. Constructed here (before the async store-load phase) because the
// service reads Globals.Ol.StoresWrapper lazily per call and never caches the model.
_storeDisableService = new StoreDisableService(this);
stopwatch.Stop();
_loadBasicElapsed = stopwatch.Elapsed;
}
Expand Down Expand Up @@ -419,6 +423,9 @@ public void LoadWhenIdle()
private AppOlObjects _olObjects;
public IOlObjects Ol => _olObjects;

private IStoreDisableService _storeDisableService;
public IStoreDisableService StoreDisable => _storeDisableService;

private AppToDoObjects _toDoObjects;
public IToDoObjects TD => _toDoObjects;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public StubGlobals(
public IAppItemEngines Engines => throw new NotImplementedException();

public IntelligenceConfig IntelRes => throw new NotImplementedException();

public IStoreDisableService StoreDisable => throw new NotImplementedException();
}

private sealed class StubFileSystemFolderPaths : IFileSystemFolderPaths
Expand Down
Loading