Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
0162567
docs(418): add feature folder and wire SVGControl.Test into solution
drmoisan Aug 4, 2026
a569565
docs(418): add cross-machine handoff for VSTO-enabled host
drmoisan Aug 4, 2026
296eac9
docs(418): record Phase 0 re-baseline and plan revisions to v0.6
drmoisan Aug 4, 2026
82badeb
fix(SVGControl): eliminate NullReferenceException from SvgRenderer co…
drmoisan Aug 4, 2026
ea10611
test(svg-renderer): complete Phase 2 final QC loop
drmoisan Aug 5, 2026
a62391f
fix(svg-renderer): restore exception containment in the assembly reso…
drmoisan Aug 5, 2026
ad60882
docs(418): record reaudit artifacts for remediation cycle 1
drmoisan Aug 5, 2026
9a71c26
docs(418): update cycle-2 plan HEAD refs and five accuracy fixes
drmoisan Aug 5, 2026
04569d9
docs(418): gate cycle-2 plan on tree invariants, not a pinned HEAD SHA
drmoisan Aug 5, 2026
9b49a44
docs(418): correct a false exclusion ground in the cycle-2 plan
drmoisan Aug 5, 2026
dc00cf1
docs(418): record the plan-rationale-is-evidence lesson from cycle 2
drmoisan Aug 5, 2026
69e675d
fix(svg-control-test): add the ExCSS reference so tests are order-ind…
drmoisan Aug 5, 2026
db8b59f
docs(418): record the cycle-2 reaudit; blocking count 2 -> 1
drmoisan Aug 5, 2026
215a6f7
docs(418): AC-11 verified by the maintainer; G-9 exception authorized
drmoisan Aug 6, 2026
24284a5
docs(418): correct a false claim in the AC-11 capture; commit the G-9…
drmoisan Aug 6, 2026
9734356
docs(418): complete the AC-11 runbook environment fields
drmoisan Aug 6, 2026
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
10 changes: 9 additions & 1 deletion .claude/agent-memory/atomic-executor/MEMORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Atomic Executor Memory Index

- [Plan rationale clauses are evidence](project_418_plan_rationale_clauses_are_evidence.md) — #418 cycle 2 needed 3 preflight passes; all 3 blockers were unmeasured world-state claims in prose (clean-tree, csharpier scope, obj/ cache read as a live project), never in the fix
- [#418 500-line gate vs mandated plan content](project_418_500line_gate_vs_plan_content.md) — P1-T19 unsatisfiable: ~193 new lines into 146 headroom, tightening only reached 547; per-block logger.Error+Trace.TraceError clauses block centralizing; delta = extract pure helpers to a new file
- [Invoke-MSTest.ps1 dies on a single test assembly](project_418_invoke_mstest_single_assembly_bug.md) — StrictMode Latest + `.Count` on a scalar String throws before vstest runs; use a direct vstest.console.exe call with the script's own arg list
- [Incremental build makes a vacuous baseline](project_incremental_build_vacuous_baseline.md) — Invoke-VSBuild only does /t:Build + timestamp-based up-to-date checks ignore /p: changes → nullable/analyzer baseline returns EXIT 0 with 0 CoreCompile; add a supplementary /t:Rebuild to enumerate diagnostics
- [#400 CompleteOpenAsync unreachable recovery catch](project_400_completeopenasync_unreachable_recovery_catch.md) — P5-T185..204: CompleteOpenAsync d__16 153-156 is dead code (dispatcher.Report swallows sink exceptions), can't reach >=90% test-only; blocks T202-T204, escalated; 17-class gate deadlocks PopupControlDispatch testhost, retry after clearing runners (passed 5th try)
- [Coverage delta: reproduce the baseline's counting method](project_coverage_delta_reproduce_baseline_counting_method.md) — Cobertura repeats lines under `<method>` AND class-level `<lines>`; deduped vs all-descendant give ~2x-different package/class denominators (SVGControl 1838 vs 3500) → false denominator-change escalation
- [csharpier pipe-files is a non-enforcing gate](project_csharpier_pipefiles_nonenforcing_gate.md) — `csharpier pipe-files`+re-hash-unmodified-file enforces nothing; use `csharpier check`/`format`; #400 P5 tests balloon past 500 lines under genuine format (size new files AFTER format, not hand-written count)

- [#398 test-split gate gotchas](project_398_test_split_gate_gotchas.md) — pre-existing CS2002 duplicate PercentageFormatterTests Compile (latent til recompile, out of scope, don't Rebuild-to-verify); /EnableCodeCoverage has no branch% + .coverage merges to empty cobertura → use Cobertura-runsettings variant + single report-level JaCoCo counter; cobertura run needs MSTest Workers=4 or TryAddValuesAsync times out ~22s
Expand Down Expand Up @@ -31,6 +36,7 @@
- [TaskController (#297) unit-test gotchas](project_taskvisualization_taskcontroller_test_gotchas.md) — ApplyChanges hangs over Moq (mark exempt); get-only MailItem.TaskSubject throws MissingMethodException; STA harness needs TableLayoutPanel parenting + NavTips warmup; C# 7.3 test project
- [MSTest [DoNotParallelize] overlaps the parallel bucket](project_mstest_donotparallelize_overlaps_parallel_bucket.md) — #292: a [DoNotParallelize] null-baseline reader still sees parallel-bucket writers; fix = mark every writer too
- [FluentAssertions Equal(params) has no because](project_fluentassertions_equal_params_no_because.md) — a trailing reason on .Equal(...) becomes an extra expected element, fails GREEN-on-HEAD; use .Equal(new[]{...}) or move reason to .HaveCount(n, reason)
- [Missing VSTO runtime breaks baseline gates](project_missing_vsto_runtime_breaks_baseline_gates.md) — absent Office Tools v4.0.Utilities => 4x CS0234 in ThisAddIn.Designer.cs on both solution gates; TaskMaster.Test/UtilitiesCS.Test never build so repo coverage reads ~25% not ~71%
- [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 beyond scope lock; Moq mocks auto-implement
- [Project Build/Test Env](project_build_test_env.md) — git-bash 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
Expand All @@ -53,7 +59,9 @@
- [QFC #227 coverage tooling](project_qfc227_coverage_tooling.md) — vstest + Cobertura runsettings (Format under Configuration + ExcludeFromCodeCoverage attribute-exclude) is the reliable numeric per-class path; .coverage not offline-convertible here
- [Theme/FolderPredictor seam retrofit gotchas (#227 cycle-3)](project_theme_folderpredictor_seam_retrofit_gotchas.md) — new required field + shared parameterless-ctor test-double builder = silent regression (fix: inject non-executing dispatcher); FolderPredictor.InitAsync FromField is COM-bound, FromArrayOrString is COM-free
- [#227 cycle-4 ToggleFocus genuine-execution gotchas](project_qfc227_cycle4_toggle_focus_genuine_test_gotchas.md) — QuickFiler.Test lacks refs to ObjectListView/WebView2.WinForms (use Activator.CreateInstance(field.FieldType)); ToggleFocus wraps a nested ToggleTips Invoke → viewer sees Invoke twice
- [vstest TestCaseFilter OR-vs-pipe + fresh-worktree bootstrap](project_vstest_testcasefilter_or_operator_and_env_setup.md) — vstest 18.7.0 rejects `OR` in /TestCaseFilter, needs `|`; fresh worktree needs Install-RepoDotNetSdk.ps1 (pwsh7) + Invoke-Restore.ps1 first
- [vstest TestCaseFilter OR-vs-pipe + fresh-worktree bootstrap](project_vstest_testcasefilter_or_operator_and_env_setup.md) — vstest 18.7.0 rejects `OR` in /TestCaseFilter, needs `|`; fresh worktree needs Install-RepoDotNetSdk.ps1 (pwsh7) + `dotnet tool restore` + global `dotnet-coverage` + Invoke-Restore.ps1 first
- [New sln member surfaces MSB3277](project_new_sln_member_surfaces_msb3277_pin_divergence.md) — wiring an unbuilt legacy test project into the sln emits a brand-new MSB3277 when its packages.config pins diverge from the ProjectReference'd production project; only fixable in packages.config/.csproj, never a .cs file
- [Legacy csproj: no transitive compile refs](project_legacy_csproj_no_transitive_compile_refs.md) — non-SDK ProjectReference doesn't flow the referenced project's package types to csc (CS0012 despite copy-local DLL); tests naming a third-party type need their own `<Reference>` + packages.config entry
- [UtilitiesCS.Test parallelism flakiness](project_utilitiescs_test_parallelism_flakiness.md) — full-suite timing tests time out (~22s) under default parallelism + coverage instrumentation; lower MSTest Workers to 4 via /Settings runsettings for a deterministic green gate
- [Cobertura runsettings <Attributes> override](project_cobertura_runsettings_attributes_override.md) — a custom <CodeCoverage> block replaces the collector's default <Attributes> excludes, silently disabling [ExcludeFromCodeCoverage]; re-add the block. csharpier v1 also formats packages.config XML
- [QfcDatamodel BackgroundWorker async-void IsBusy race](project_qfc_backgroundworker_async_void_race.md) — Worker_DoWork is async void; IsBusy flips false instantly, so a synchronous post-RunWorkerAsync IsBusy assert is context-dependent — use WorkerSupportsCancellation instead
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: 418-500line-gate-vs-plan-content
description: Issue #418 P1-T19 — plan mandated ~193 net new lines in a 354-line file with 146 lines of headroom, so the 500-line gate was unsatisfiable; per-block logging acceptance clauses blocked centralization
metadata:
type: project
---

An atomic plan can mandate more new code than its target file's 500-line headroom allows, making its
own "tighten until compliant" task unsatisfiable. Check the arithmetic during preflight: sum the
estimated line cost of every production task and compare against `500 - <current file length>`.

**Why:** Issue #418 `[P1-T19]` required `SVGControl/SvgRenderer.cs` <= 500 lines. The file was 354
lines (146 headroom), but `[P1-T10]`–`[P1-T18]` mandated a parse-failure boundary, three public
parse members, two rewritten constructors, two pure probe helpers, and an `AssemblyResolve`
strategy-3 block. First pass landed at 603; a full tightening pass reached only 547. csharpier
reformats from the AST, so the post-format count is stable — hand-compressing further does not help.

Three tightening levers were blocked by the plan's own acceptance clauses:
- `[P1-T11]` required its `catch (Exception` block to literally contain both `logger.Error` and
`Trace.TraceError`, and `[P1-T14]` required *both* byte-array constructors to contain both calls.
Routing all four sites through one helper saves only ~4 lines and breaks three clauses.
- Chaining the 4-arg constructor to the 3-arg one saves ~16 lines but breaks `[P1-T14]` and makes the
log record name the wrong signature.
- Deleting pre-existing members (unreferenced private `AddMargins`, 19 lines) exceeds a mandate to
"tighten the added code".

**How to apply:** When the gate is unreachable, report `SCOPE_EXCEEDED` with the measured count and a
concrete plan delta rather than bending an acceptance clause. Here the clean delta was extracting the
two *pure* helpers (`TryGetDirectoryFromCodeBase`, `GetProbeDirectories` — 50 lines) into a new
`SVGControl/SvgAssemblyProbe.cs`, projecting 497 lines. That needs the Scope Lock amended to add the
new file *plus* `SVGControl/SVGControl.csproj` for its `<Compile Include>` (legacy non-SDK, no glob;
see [[project_legacy_csproj_no_transitive_compile_refs]]). Stop before the test-authoring tasks: the
delta moves which type the helpers live on, so tests written first would bind to a surface that moves.

Related: [[project_csharpier_pipefiles_nonenforcing_gate]] (size new files AFTER formatting).
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: 418-invoke-mstest-single-assembly-bug
description: scripts/vscode/Invoke-MSTest.ps1 throws "property 'Count' cannot be found" whenever discovery finds exactly ONE test assembly, because StrictMode Latest rejects .Count on a scalar String
metadata:
type: project
---

`scripts/vscode/Invoke-MSTest.ps1` cannot run a single-project scope. Line 115 evaluates
`$testAssemblies.Count` while line 77 sets `Set-StrictMode -Version Latest`. When
`Get-ChildItem ... | Select-Object -ExpandProperty FullName` matches exactly one file it returns a
scalar `System.String`, and StrictMode `Latest` rejects `.Count` on a scalar, so the script dies with
`The property 'Count' cannot be found on this object` before reaching vstest.

**Why:** Issue #418 `[P1-T9]`/`[P1-T23]` commanded
`Invoke-MSTest.ps1 -SearchRoot SVGControl.Test`, which discovers exactly one assembly
(`SVGControl.Test/bin/Debug/SVGControl.Test.dll`) and therefore can never execute. `-SearchRoot .`
finds nine assemblies (an array), so the Phase 0 baseline and repo-wide runs are unaffected — which is
why this stayed latent.

Verified in isolation:
```
pwsh -NoProfile -Command "Set-StrictMode -Version Latest; $s='one'; $s.Count" -> throws
pwsh -NoProfile -Command "Set-StrictMode -Version Latest; $a=@('one','two'); $a.Count" -> 2
```

**How to apply:** If the script is outside your Scope Lock, do not edit it. Run the faithful
equivalent by calling `vstest.console.exe` with the argument list the script's own pure
`Get-VsTestArgumentList` builds — assemblies + `/Settings:scripts/vscode/TaskMaster.cli.runsettings` +
`/InIsolation` + `/TestCaseFilter:TestCategory!=LiveOutlook` — and record both the failed plan command
and the equivalent in the evidence artifact. Resolve `vstest.console.exe` via
`vswhere -latest -products * -find 'Common7\IDE\Extensions\TestPlatform\vstest.console.exe'`. Invoke
through `pwsh` rather than git-bash so `/`-prefixed switches are not path-mangled. The real fix, when
in scope, is `@($testAssemblies).Count`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: plan-rationale-clauses-are-evidence
description: "#418 cycle 2 took 3 preflight passes; all 3 blockers were unmeasured world-state claims in plan prose, never in the fix — measure any rationale an artifact must reproduce"
metadata:
type: project
---

In #418 remediation cycle 2 (a two-line `.csproj`/`packages.config` change) preflight took three passes.
Every blocker was a claim the plan made *about the world*, not a defect in the fix, the commands, or the
acceptance clauses:

1. Header asserted "working tree clean" when two tracked `.claude/agent-memory/feature-review/` files were
modified — which made `[P0-T5]`'s halt clause fire at task 5 and `[P1-T7]`'s "exactly two tracked files"
unsatisfiable.
2. `[P1-T2]` justified a single-line `packages.config` entry with "csharpier formats only `*.cs`". False:
`.csharpierignore` excludes `*.csproj`/`*.props`/`*.targets` but **not** `packages.config`, and that file
is visibly csharpier-reflowed. Conclusion (stay single-line) was right for a different reason — width.
3. `[P0-T9]` required the executor to *record* that `UtilitiesSwordfish.Test`'s project file is
`UtilitiesSwordfish.NET.Test.csproj`. That directory holds only `bin/` and `obj/`; `git ls-files` returns
zero. The planner had read `obj/…csproj.AssemblyReference.cache` and inferred a live project from build
residue that outlived the tear-down commit (#308).

**Why:** a rationale clause that a task orders an artifact to reproduce is not commentary — it becomes
evidence in the audit trail, and a reaudit that checks it reopens the cycle over prose. Pattern 2 and 3
share a shape: wrong supporting fact, right conclusion, so nothing fails at runtime and only a disk check
catches it.

**How to apply:** during preflight, verify every factual assertion a task requires an artifact to state, not
just the commands and paths. During execution, re-measure such clauses at the point of writing rather than
transcribing plan prose. Treat `obj/` and `bin/` contents as residue, never as proof a project exists —
`git ls-files <dir>` is the authoritative check. Ten directories match `*.Test` in this repo but only nine
test assemblies exist; `UtilitiesSwordfish.Test` is stale untracked output.

Related: [[project_bom_grep_anchor_false_negative]] (another measure-don't-assume trap),
[[project_cobertura_runsettings_attributes_override]] (csharpier v1 formats packages.config XML).
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: coverage-delta-reproduce-baseline-counting-method
description: A coverage-delta task must reproduce whatever per-<line> counting method the baseline artifact used (deduped vs all-descendant), or package/class before-after numbers differ ~2x and look like a catastrophic regression
metadata:
type: project
---

When a plan task says "compare the post-change `SVGControl` package figure against the baseline's
`1412 / 3266`", read *how* the baseline artifact arrived at its denominator before computing yours.
Cobertura repeats every statement line twice — once under `<class><methods><method><lines><line>` and
once under the class-level `<class><lines><line>` — so two defensible methods give roughly 2x-different
package and class denominators:

- **Deduped / class-level only** (`$class.lines.line`): `SVGControl` reads 853/1838 = 46.41%.
- **All `<line>` descendants** (`$node.SelectNodes('.//line')`): `SVGControl` reads 1648/3500 = 47.09%.

The #418 baseline used the **all-descendant** method (its per-class rows sum exactly to 3266, and
`SvgRenderer` 264/422 is 211 statement lines doubled). Computing the post-change figure the deduped way
would have reported 1838 total against a 3266 baseline — a denominator that appears to have *halved*,
which is nonsense and would have triggered a false `COVERAGE_DENOMINATOR_CHANGE` escalation.

Per-`<method>` figures are unaffected: a `<method>` node has exactly one `<lines>` child, so
`line-rate` and `.//line` counts inside a method are honest either way. Only package- and class-level
rollups diverge.

**Why:** #418 `[P2-T9]` gated a `>= 90%` newly-added-member rule on `<method>` `line-rate` (safe) while
also requiring package/class before-after deltas (method-sensitive). Verifying the baseline's method
first turned an apparent package regression into the true result: 1412/3266 = 43.23% -> 1648/3500 =
47.09%, an improvement.

**How to apply:** Before writing any coverage-delta artifact, sanity-check the baseline by recomputing
one of its own recorded rows from the current XML structure and confirming your script reproduces the
shape (e.g. that the baseline's per-class denominators sum to its package denominator). State the
counting method explicitly in the delta artifact so the next reader can reproduce it. The repo-wide
root `<coverage>` attributes are already deduped and match a per-package all-descendant sum in this
repo, so repo-level figures need no adjustment. See also
[[csharp-canonical-coverage-artifact-conversion]] and
[[dotnet-coverage-denominator-nondeterminism]].
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ metadata:
**Why:** In issue #400 (QuickFiler folder-selector drop-down), ~20 prior P5 CSharpier evidence artifacts used exactly this pattern, so the committed P5 production/test files were never actually CSharpier-clean. When P5-T154 ran genuine `csharpier format` (CSharpier 1.3.0, no `.csharpierrc`, default width 100), 8 of 10 files changed and two coverage test files blew past the hard 500-line limit: `BreadcrumbDropDownOpenCoordinatorTests.cs` 395->514 and `BreadcrumbPopupBoundaryCoverageTests.cs` 479->562. The prior batches sized those files to <=480 using their UNFORMATTED line counts. This created an unsatisfiable conflict (CSharpier-clean AND <=500) that only a plan revision (split into partial-class pairs + new includes) can resolve — P5-T154..T160 were left blocked.

**How to apply:** For any TaskMaster CSharpier gate, verify with `csharpier check <files>` (exit 1 == not formatted) or `csharpier format <files>` (writes in place), never `pipe-files`+re-hash. When sizing new/edited .cs files against the 500-line limit, measure the count AFTER `csharpier format`, not the hand-written count. Reformatting can both expand (arg lists broken across lines) and shrink (blank-line removal) files. See also [[vs18-build-toolchain-paths]].

**`format`'s summary line is a processed count, not a rewritten count.** `csharpier format .` prints `Formatted 1466 files in 1257ms.` even when it rewrites nothing, so a plan task demanding "Files reformatted: 0" cannot be satisfied from that line. Prove zero rewrites by content comparison instead — capture line counts (or hashes) of the files you touched before the run and re-measure after, and corroborate with a following `csharpier check .` at exit 0. csharpier also does **not** reflow comments, so a comment-line budget you count by hand survives formatting unchanged (used to hold `SvgRenderer.cs` at 497 against a 500 cap in #418 `[P2-T1]`).
Loading
Loading