From c18fd2eae6281597b660bc59926a344bf90a1bf2 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 8 Aug 2026 20:46:12 -0400 Subject: [PATCH 1/9] docs(#505): planning artifacts and Phase 0 baseline evidence --- .../agent-memory/atomic-executor/MEMORY.md | 56 +- ...ject_incremental_build_vacuous_baseline.md | 18 +- ...koverage_cobertura_postprocessing_shape.md | 39 + ...sing_vsto_runtime_breaks_baseline_gates.md | 15 +- .../project_vs18_build_toolchain_paths.md | 1 + .claude/agent-memory/atomic-planner/MEMORY.md | 1 + ...ject_505_toggle_state_guards_plan_seams.md | 22 + .claude/agent-memory/prd-feature/MEMORY.md | 1 + ...roject_522_nullable_typecheck_deviation.md | 12 + .../agent-memory/task-researcher/MEMORY.md | 1 + .../project_ribbon_toggle_state_guards_505.md | 21 + .../csharpier-check.2026-08-08T20-43.md | 27 + .../file-line-counts.2026-08-08T20-46.md | 56 ++ .../baseline/git-state.2026-08-08T20-41.md | 43 ++ .../msbuild-analyzers.2026-08-08T20-45.md | 57 ++ .../msbuild-typecheck.2026-08-08T20-46.md | 41 + .../nuget-restore.2026-08-08T20-42.md | 27 + .../phase0-inputs-read.2026-08-08T20-40.md | 54 ++ .../baseline/phase0-instructions-read.md | 43 ++ .../preexisting-failures.2026-08-08T20-45.md | 38 + .../tests-with-coverage.2026-08-08T20-44.md | 62 ++ ...toolchain-availability.2026-08-08T20-37.md | 29 + .../issue.md | 196 +++++ .../plan.2026-08-08T19-22.md | 387 ++++++++++ ...bon-engine-toggle-state-guards-research.md | 530 +++++++++++++ .../spec.md | 712 ++++++++++++++++++ 26 files changed, 2455 insertions(+), 34 deletions(-) create mode 100644 .claude/agent-memory/atomic-executor/project_koverage_cobertura_postprocessing_shape.md create mode 100644 .claude/agent-memory/atomic-planner/project_505_toggle_state_guards_plan_seams.md create mode 100644 .claude/agent-memory/prd-feature/project_522_nullable_typecheck_deviation.md create mode 100644 .claude/agent-memory/task-researcher/project_ribbon_toggle_state_guards_505.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/csharpier-check.2026-08-08T20-43.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/file-line-counts.2026-08-08T20-46.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/git-state.2026-08-08T20-41.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/msbuild-analyzers.2026-08-08T20-45.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/msbuild-typecheck.2026-08-08T20-46.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/nuget-restore.2026-08-08T20-42.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/phase0-inputs-read.2026-08-08T20-40.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/phase0-instructions-read.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/preexisting-failures.2026-08-08T20-45.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/tests-with-coverage.2026-08-08T20-44.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/toolchain-availability.2026-08-08T20-37.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/issue.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/plan.2026-08-08T19-22.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/research/2026-08-08T19-30-ribbon-engine-toggle-state-guards-research.md create mode 100644 docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/spec.md diff --git a/.claude/agent-memory/atomic-executor/MEMORY.md b/.claude/agent-memory/atomic-executor/MEMORY.md index 81524fe79..109d6c715 100644 --- a/.claude/agent-memory/atomic-executor/MEMORY.md +++ b/.claude/agent-memory/atomic-executor/MEMORY.md @@ -3,24 +3,24 @@ ## Plan validation & gates - [Verify line citations with numbered output](feedback_verify_line_citations_with_numbered_output.md) — never hand-count from a `sed` window; a wrong #438 advisory got applied and corrupted 3 correct citations in plan + spec - [Self-derived gate thresholds are blind](project_preflight_selfderived_gate_thresholds_are_blind.md) — a "count >= floor" gate whose floor comes from the runs it validates is deflation-blind + scope-incommensurable; use git-enumeration + `/ListTests` existence proofs -- [Merge-base diff gates need a commit cadence](project_preflight_mergebase_diff_gates_need_commit_cadence.md) — `..HEAD` gates are vacuous while HEAD == merge-base and unsatisfiable once HEAD is ahead; on a later cycle scope-audit via `git show --numstat --format= HEAD` +- [Merge-base diff gates need a commit cadence](project_preflight_mergebase_diff_gates_need_commit_cadence.md) — `..HEAD` gates are vacuous while HEAD == merge-base; plan an explicit commit task - [Inserted plan tasks force renumbering](project_plan_task_ids_digit_only_forces_renumbering.md) — suffixed IDs (`P3-T5a`) fail validation; say "insert + renumber downstream", then verify defs-vs-mentions mechanically - [Plan rationale clauses are evidence](project_418_plan_rationale_clauses_are_evidence.md) — #418 needed 3 preflight passes; all blockers were unmeasured world-state claims in prose, never in the fix - [Tracked agent-memory breaks unscoped git gates](project_agent_memory_tracked_breaks_unscoped_git_gates.md) — `.claude/agent-memory/**` is tracked + dirty at branch head; every git diff/status/grep gate needs an explicit pathspec or it is unsatisfiable / false-positive - [#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); per-block logging clauses block centralizing; delta = extract helpers to a new file -- [#376 capstone scope-expansion layers](project_376_capstone_scope_expansion_layers.md) — 5 escalated layers past P2-T17 resolved via the 3 authorized patterns; stop-condition never triggered -- [#207 Hook() redesign breaks AppEventsTests](project_207_hook_redesign_breaks_appeventstests.md) — readiness-gate Hook() fails an out-of-scope test asserting superseded ordering; needs a plan revision, not a test weakening -- [ApplicationGlobalsTests.cs at 500-line ceiling](project_appglobalstests_at_500_line_ceiling.md) — file is exactly 500 lines; any plan adding a Testable* override must extract first -- [Swordfish F5 test misclassification](project_swordfish_f5_test_misclassification.md) — #308 mislabeled two clean-base tests "direct-Swordfish"; verify using/namespace before treating a removal as Swordfish-only +- [#207 Hook() redesign breaks AppEventsTests](project_207_hook_redesign_breaks_appeventstests.md) — needs a plan revision, not a test weakening +- [ApplicationGlobalsTests.cs at 500-line ceiling](project_appglobalstests_at_500_line_ceiling.md) — exactly 500 lines; adding a Testable* override requires extraction first +- [#376 capstone scope-expansion layers](project_376_capstone_scope_expansion_layers.md) — 5 escalated layers resolved via the 3 authorized patterns +- [Swordfish F5 test misclassification](project_swordfish_f5_test_misclassification.md) — verify using/namespace before treating a removal as Swordfish-only ## Build / toolchain environment -- [Project Build/Test Env](project_build_test_env.md) — git-bash quirks: MSBuild dash-switches, MSYS_NO_PATHCONV, csharpier v1 syntax, forced-nullable Rebuild + Debug-restore, legacy csproj Compile includes, IVT for Moq, C# 7.3 in QuickFiler.Test +- [Project Build/Test Env](project_build_test_env.md) — git-bash quirks (MSBuild switches, MSYS_NO_PATHCONV), csharpier v1 syntax, legacy csproj Compile includes, IVT for Moq - [VS18 build/test toolchain paths](project_vs18_build_toolchain_paths.md) — use VS **18** full-framework msbuild.exe (not .dotnet-sdk, dies on binary resx MSB3822); nuget.exe restore; dotnet-coverage needs `--` separator - [Repo-local SDK install + nullable Rebuild](project_repo_sdk_and_nullable_rebuild.md) — .dotnet-sdk install needs pwsh7; csharpier check/format subcommands; nullable debt scope NOT stable across sessions — re-verify which csproj errors come from -- [vstest TestCaseFilter OR-vs-pipe + fresh-worktree bootstrap](project_vstest_testcasefilter_or_operator_and_env_setup.md) — vstest rejects `OR`, needs `|`; fresh worktree needs Install-RepoDotNetSdk.ps1 + `dotnet tool restore` + global `dotnet-coverage` + Invoke-Restore.ps1 +- [vstest TestCaseFilter OR-vs-pipe + fresh-worktree bootstrap](project_vstest_testcasefilter_or_operator_and_env_setup.md) — vstest rejects `OR`, needs `|`; fresh worktree needs restore + global `dotnet-coverage` - [Analyzer version skew on fresh worktree](project_analyzer_version_skew_fresh_worktree.md) — analyzer build can fail CS0006 when csproj `` HintPaths diverge from packages.config; compare the two before blaming restore - [SecurityCodeScan incompatible with Roslyn 5.6](project_securitycodescan_roslyn56_incompat.md) — SecurityCodeScan.VS2019 5.6.7 throws CS8032/YamlDotNet under VS18, breaking TWAE; Meziantou/Roslynator need roslyn-version subfolders -- [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; TaskMaster.Test/UtilitiesCS.Test never build so repo coverage reads ~25% not ~71% +- [Missing VSTO runtime breaks baseline gates](project_missing_vsto_runtime_breaks_baseline_gates.md) — HISTORICAL, not reproducing 2026-08-08; build before citing - [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 new MSB3277 when packages.config pins diverge; fix 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 package types to csc (CS0012 despite copy-local DLL); tests need their own `` + packages.config entry - [sln/csproj edits: preserve CRLF](project_sln_csproj_edit_crlf_preserve.md) — git-bash `sed -i` strips CRLF from TaskMaster.sln (churn + BOM loss); use Edit or `perl -0777` w/ explicit `\r\n` @@ -36,12 +36,12 @@ - [Compile-time red needs body-level refs](project_compile_red_needs_body_level_references.md) — a missing type in a method SIGNATURE suppresses body binding, so an `[expect-fail]` task requiring N named CS0246s reports only 1; construct the types inline in test bodies ## Test execution & isolation -- [Tests must mock GUI; no visible window](feedback_tests_must_mock_gui_no_visible_window.md) — user saw a window pop up during a QuickFiler.Test run; new tests must use headless seams (Mock, injected show/focus delegates, host-neutral types), never Form.Show/Application.Run +- [Tests must mock GUI; no visible window](feedback_tests_must_mock_gui_no_visible_window.md) — use headless seams (mocked viewers, injected show/focus delegates), never Form.Show/Application.Run - [WinFormsPumpHost tests are load-flaky](project_winformspumphost_tests_load_flaky.md) — QfcItemController_InitializationTests fail with "window handle has been created"/60s timeouts when the box is CPU-saturated; re-run when load drops, don't treat as a red baseline - [vstest /InIsolation + FilePathHelper serialization](project_vstest_isolation_and_filepathhelper_serialization.md) — Moq assemblies need /InIsolation (else STTE Setup FileNotFound); FilePathHelper.FilePath is "" default but null after JSON deserialize - [Invoke-MSTest.ps1 dies on a single test assembly](project_418_invoke_mstest_single_assembly_bug.md) — StrictMode + `.Count` on a scalar String throws before vstest runs; call vstest.console.exe directly with the script's arg list - [Timed-out MSTest leaves detached runner](project_timedout_mstest_leaves_detached_runner.md) — leaves a pwsh runner respawning testhosts → user.config hangs; kill the pwsh runner too, verify 0, rerun >=8min -- [Sibling-worktree shared-tooling hazard](project_sibling_worktree_shared_tooling_hazard.md) — a concurrent agent in another worktree crashes your testhost + clobbers /tmp logs; use session scratchpad, trust bash `$?`, never touch others' processes +- [Sibling-worktree shared-tooling hazard](project_sibling_worktree_shared_tooling_hazard.md) — a concurrent agent elsewhere crashes your testhost + clobbers /tmp logs; use the session scratchpad - [Concurrent executor in same worktree](project_concurrent_executor_same_worktree.md) — two executors on one worktree corrupt shared files; detect via mtime progression during your own turn; STOP, don't stash/race - [UtilitiesCS.Test parallelism flakiness](project_utilitiescs_test_parallelism_flakiness.md) — timing tests time out (~22s) under default parallelism + coverage; lower MSTest Workers to 4 via /Settings for a deterministic gate - [MSTest [DoNotParallelize] overlaps the parallel bucket](project_mstest_donotparallelize_overlaps_parallel_bucket.md) — a [DoNotParallelize] null-baseline reader still sees parallel-bucket writers; mark every writer too @@ -55,40 +55,32 @@ - [Coverage delta: reproduce the baseline's counting method](project_coverage_delta_reproduce_baseline_counting_method.md) — Cobertura repeats lines under `` AND class ``; deduped vs all-descendant give ~2x denominators → false escalation - [First-party coverage denominator method (#197)](project_coverage_firstparty_denominator_method.md) — production-only rate = per-`` count across ALL deduped packages INCLUDING vendored Swordfish/SVGControl; reproduces 71.73% - [dotnet-coverage denominator nondeterminism](project_dotnet_coverage_denominator_nondeterminism.md) — repo line-rate swings (47% vs 81%) from double-counted denominator; re-baseline via git-stash, trust per-class rates -- [C# canonical coverage artifact conversion](project_csharp_canonical_coverage_artifact_conversion.md) — hook reads artifacts/csharp/coverage.xml as JaCoCo; convert feature Cobertura; first-party aggregate under-counts from uninstrumented assemblies — defer repo-wide to PR CI +- [Koverage Cobertura post-processing shape](project_koverage_cobertura_postprocessing_shape.md) — Invoke-MSTestWithCoverage rewrites filenames with `\`, pre-merges per-file `` nodes, strips test packages, recomputes root attrs; forward-slash queries match nothing +- [C# canonical coverage artifact conversion](project_csharp_canonical_coverage_artifact_conversion.md) — hook reads artifacts/csharp/coverage.xml as JaCoCo (85% floor); defer repo-wide to PR CI - [Cobertura runsettings `` override](project_cobertura_runsettings_attributes_override.md) — a custom `` block replaces the default `` excludes, silently disabling [ExcludeFromCodeCoverage]; re-add it - [Changed-line coverage: Cobertura hits vs MS-coverage partial](project_changed_line_coverage_cobertura_vs_mscoverage_partial.md) — null-guard throws read "partially covered" in MS.CodeCoverage XML but hits=1 in Cobertura; use Cobertura for >=90% proofs - [QFC #227 coverage tooling](project_qfc227_coverage_tooling.md) — vstest + Cobertura runsettings (Format under Configuration + attribute-exclude) is the reliable per-class path; .coverage not offline-convertible here - [#398 test-split gate gotchas](project_398_test_split_gate_gotchas.md) — pre-existing CS2002 duplicate Compile (latent, out of scope); /EnableCodeCoverage has no branch% + .coverage merges to empty cobertura → use Cobertura-runsettings variant - [ExcludeFromCodeCoverage on partial class = CS0579](project_excludefromcodecoverage_partial_class_cs0579.md) — annotate a partial type ONCE, not both parts, or the build breaks with duplicate-attribute CS0579 -- [#400 CompleteOpenAsync unreachable recovery catch](project_400_completeopenasync_unreachable_recovery_catch.md) — d__16 153-156 is dead code (dispatcher.Report swallows sink exceptions), can't reach >=90%; escalated; 17-class gate deadlocks testhost, retry after clearing runners -- [Swordfish-removal epic: incidental vendored-coverage side effect](project_swordfish_removal_epic_incidental_coverage_sideeffect.md) — deleting a ScoXxx wrapper drops incidental UtilitiesSwordfish coverage; non-blocking, expect it on every F-slice -- [TaskVisualization #298 ScoCollection + live-bridge exemptions](project_taskvis_scocollection_and_livebridge_exemptions.md) — ScoCollection forces a Swordfish ProjectReference on test assemblies; a default-factory live-form bridge must be method-level exempt +- Closed one-offs (low reuse): [#400 dead recovery catch](project_400_completeopenasync_unreachable_recovery_catch.md), [Swordfish vendored-coverage side effect](project_swordfish_removal_epic_incidental_coverage_sideeffect.md), [#298 ScoCollection/live-bridge exemptions](project_taskvis_scocollection_and_livebridge_exemptions.md), [#328 OlObjectsProxy](project_328_rebuild_threading_olobjectsproxy_conflict.md) ## Nullable / C# language -- [Nullable per-file pragma gate mechanics](project_nullable_pragma_gate_mechanics.md) — HISTORICAL: solution-wide TWAE once aborted on vendored SVGControl CS0649 + UtilitiesCS CS0618/CS0168, needing an isolated `UtilitiesCS.csproj -t:Rebuild -p:BuildProjectReferences=false` + grep CS86xx. Re-verified 2026-08-08 (#507) with CI's own command (`/t:Rebuild`, TWAE, NO `/p:Nullable=enable`): genuinely EXIT 0 / 0 errors, so the enforced gate really does pass. The 195 UtilitiesCS + 219 TaskMaster error population appears only when `/p:Nullable=enable` is added, which no gate does — see [[project-507-nullconditional-return-triggers-cs8603-under-genuine-nullable-check]]. Still use `/t:Rebuild` (not `/t:Build`) so the pass is not vacuous. Supersedes the #364, epic, and net481-mechanics variants. +- [Nullable per-file pragma gate mechanics](project_nullable_pragma_gate_mechanics.md) — CI's command (`/t:Rebuild`, TWAE, NO `/p:Nullable=enable`) genuinely passes EXIT 0; the 195+219 error population appears only under the forced flag. Use `/t:Rebuild` so the pass is not vacuous - [CLAUDE.md nullable command != the CI gate](project_507_nullconditional_return_triggers_cs8603_under_genuine_nullable_check.md) — ci.yml omits `/p:Nullable=enable` and relies on per-file `#nullable` pragmas; forced-flag CS86xx in an unannotated file is NOT a blocker. Repro the CI command before failing an AC - [Nullable remediation annotation patterns](project_nullable_remediation_annotation_patterns.md) — net481 has no post-condition attrs; struct `= default!`; `.ToString()!` for string cells; IsNullOrEmpty overload gotcha; `x!.M()` for defensive flow-state -- [Nullable annotation CS8632 scoping](project_nullable_annotation_cs8632_scoping.md) — `Type?` in nullable-disabled projects emits CS8632; wrap in `#nullable enable annotations`/`restore annotations`, not whole-file -- [init/record struct fails CS0518 on net48](project_record_struct_isexternalinit_netfx.md) — ANY init accessor needs IsExternalInit (absent on net48, no polyfill); use a constructor-initialized readonly struct with get-only props -- [CS8714 does not fire on net481](project_nullable_cs8714_not_on_net481.md) — ConcurrentDictionary-derived unconstrained-TKey types emit 0 CS8714 (net481 BCL lacks notnull); `where TKey : notnull` is forward-looking -- [#366 notnull cascades past WrapperScoDictionary](project_366_notnull_cascades_beyond_wrapperscodictionary.md) — the ratified `where TKey : notnull` also forces the constraint onto ScoDictionaryConverter; fix = the same one-line constraint there -- [#366 ScDictionary constraint cascades to a 4th file](project_366_scdictionary_constraint_cascades_to_fourth_file.md) — cascades CS8714 into ScDictionaryConverter.cs beyond the 3-file waiver; STOP + re-escalate, don't widen -- [#366 Batch7 T? return triggers CS8766](project_366_batch7_tnullable_return_cs8766.md) — `T?` on a class-constrained generic implementing a null-oblivious interface member emits CS8766; conform to `T` + justified `!`, don't edit the interface -- [#372 email-classifier nullable patterns](project_372_email_classifier_nullable_patterns.md) — engine props post-ctor `null!`; factory returns `T?`; `.Class!` cascade; `(await Deserialize())!`; DTO `= null!` adds a coverage line -- [#371 OutlookObjects nullable lessons](project_371_outlookobjects_nullable_lessons.md) — a public-signature nullable change regresses OTHER nullable files in the same assembly; keep public tuples non-null w/ `!`; lazy-field CS8618 → `Lazy?` -- [#375 residuals nullable gotchas](project_375_residuals_nullable_gotchas.md) — CS8644 inherited-interface mismatch fixed with a `#nullable disable` island on the class-decl line; full-solution Rebuild cleans SVGControl.dll → isolated CS0006 +- [Nullable annotation CS8632 scoping](project_nullable_annotation_cs8632_scoping.md) — `Type?` in nullable-disabled projects emits CS8632; wrap in `#nullable enable annotations`, not whole-file +- [init/record struct fails CS0518 on net48](project_record_struct_isexternalinit_netfx.md) — no IsExternalInit polyfill; use a ctor-initialized readonly struct with get-only props +- [CS8714 does not fire on net481](project_nullable_cs8714_not_on_net481.md) — net481 BCL lacks notnull, so `where TKey : notnull` is forward-looking only +- Nullable-epic per-issue notes (closed epic, low reuse): [#366 notnull cascade](project_366_notnull_cascades_beyond_wrapperscodictionary.md), [#366 4th file](project_366_scdictionary_constraint_cascades_to_fourth_file.md), [#366 CS8766](project_366_batch7_tnullable_return_cs8766.md), [#372](project_372_email_classifier_nullable_patterns.md), [#371](project_371_outlookobjects_nullable_lessons.md), [#375](project_375_residuals_nullable_gotchas.md) - [Outlook `Action`/`Exception` ambiguity](project_outlook_action_ambiguity.md) — bare `Action` AND bare `Exception` are CS0104-ambiguous in Outlook-interop files; use `System.Action`/`System.Exception` (surfaces only at analyzer/type-check build) ## Component-specific gotchas -- [#349 breadcrumb WebView2 gotchas](project_349_breadcrumb_webview2_gotchas.md) — a retyped Designer field breaks reflection-injected tests (inject a router); aggregate async d__ classes for >=90% proofs; QuickFiler.Test is Newtonsoft-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 Invoke -- [Theme/FolderPredictor seam retrofit gotchas (#227 cycle-3)](project_theme_folderpredictor_seam_retrofit_gotchas.md) — new required field + shared parameterless-ctor builder = silent regression (inject a non-executing dispatcher); InitAsync FromField is COM-bound -- [ObjectListView TreeListView headless selection](project_objectlistview_treelistview_headless_selection.md) — SelectedObject/SelectedIndex need a native handle; headless can't select — cache the node via SelectionChanged; ListBox→TreeListView retype breaks `new ListBox()` -- [QfcDatamodel BackgroundWorker async-void IsBusy race](project_qfc_backgroundworker_async_void_race.md) — Worker_DoWork is async void so IsBusy flips false instantly; assert WorkerSupportsCancellation instead -- [QfcItemController pump harness needs SaveParameters](project_qfcitemcontroller_pump_harness_needs_saveparameters.md) — SetField-only injection leaves the `??=` factory defaults null (NRE in LoadFolderHandlerAsync); InitializeAsync also needs AF.CtfMap/UseLcppnPredictor/FolderPredictor/RecentsList mocks -- [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 -- [#328 Rebuild-threading breaks OlObjectsProxy](project_328_rebuild_threading_olobjectsproxy_conflict.md) — threading Parent.Ol.StoresWrapper into AppToDoObjects.Rebuild fails LoadProjInfoAsync_Rebuilds; fix = return null for get_StoresWrapper +- [#349 breadcrumb WebView2 gotchas](project_349_breadcrumb_webview2_gotchas.md) — retyped Designer field breaks reflection-injected tests; aggregate async d__ classes for >=90% +- QuickFiler #227 cycle notes: [cycle-4 ToggleFocus](project_qfc227_cycle4_toggle_focus_genuine_test_gotchas.md), [cycle-3 Theme/FolderPredictor seam](project_theme_folderpredictor_seam_retrofit_gotchas.md) +- [ObjectListView TreeListView headless selection](project_objectlistview_treelistview_headless_selection.md) — selection needs a native handle; cache the node via SelectionChanged +- [QfcDatamodel BackgroundWorker async-void IsBusy race](project_qfc_backgroundworker_async_void_race.md) — IsBusy flips false instantly; assert WorkerSupportsCancellation +- [QfcItemController pump harness needs SaveParameters](project_qfcitemcontroller_pump_harness_needs_saveparameters.md) — SetField-only injection leaves `??=` factory defaults null (NRE in LoadFolderHandlerAsync) +- [TaskController (#297) unit-test gotchas](project_taskvisualization_taskcontroller_test_gotchas.md) — ApplyChanges hangs over Moq; get-only MailItem.TaskSubject throws; STA harness needs TableLayoutPanel parenting - [ProjectEntry setter raw MessageBox](project_projectentry_setter_raw_messagebox.md) — the ProjectID setter uses RAW un-seamed MessageBox.Show and hangs STA tests; CompareTo tie-break needs a Moq IProjectEntry with shifting ProjectID - [IApplicationGlobals member forces implementers](project_iapplicationglobals_member_forces_implementers.md) — adding a member breaks 7 hand-written test-double stubs beyond scope lock; Moq mocks auto-implement - [TimeProvider seam gotchas](project_timeprovider_seam_gotchas.md) — Moq can't mock non-virtual GetLocalNow (use FakeTimeProvider); an optional TimeProvider param forces a Bcl.TimeProvider `` on every consumer (CS0012) diff --git a/.claude/agent-memory/atomic-executor/project_incremental_build_vacuous_baseline.md b/.claude/agent-memory/atomic-executor/project_incremental_build_vacuous_baseline.md index 6c0d21e62..c5a57b631 100644 --- a/.claude/agent-memory/atomic-executor/project_incremental_build_vacuous_baseline.md +++ b/.claude/agent-memory/atomic-executor/project_incremental_build_vacuous_baseline.md @@ -29,5 +29,21 @@ leaves outputs cleaned, so follow it with a passing analyzer Rebuild to restore any coverage run; and `Invoke-VSBuild.ps1` also runs `Sync-PackageReferences.ps1` on every invocation, so verify `git status` afterwards. +**This is NOT specific to `Invoke-VSBuild.ps1`.** Re-measured 2026-08-08 (#505 preflight) with a +direct `MSBuild.exe` call — the exact `CLAUDE.md` analyzer command +(`TaskMaster.sln /t:Build /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true +/p:EnforceCodeStyleInBuild=true`) run immediately after a plain `/t:Build` returned `EXIT_CODE=0` +with **18 `Skipping target "CoreCompile"` notices and 0 `csc.exe` invocations**. The analyzer gate +therefore analyzed nothing. Measure it with +`/fl /flp:"logfile=;verbosity=normal"` then count `Skipping target "CoreCompile"` and +`csc.exe` occurrences — `csc.exe = 0` is the unambiguous vacuity signal. + +**Plan-review consequence:** in any final-QC phase the analyzer step almost always follows an +earlier build of the same tree (and, on a loop restart, follows the type-check `/t:Rebuild`), so a +`/t:Build` analyzer gate is vacuous by construction. Require `/t:Rebuild` for the analyzer gate, or +require the acceptance artifact to record a non-zero `csc.exe`/`CoreCompile` count for +`TaskMaster` and `TaskMaster.Test`. CI does not hit this because it always starts from a clean +checkout; `ci.yml` states the same rationale in a comment on its type-check step. + Related: [[project_repo_sdk_and_nullable_rebuild]], [[project_364_nullable_gate_preexisting_blockers]], -[[project_vs18_build_toolchain_paths]]. +[[project_vs18_build_toolchain_paths]], [[project_nullable_build_gate_is_vacuous_incremental]]. diff --git a/.claude/agent-memory/atomic-executor/project_koverage_cobertura_postprocessing_shape.md b/.claude/agent-memory/atomic-executor/project_koverage_cobertura_postprocessing_shape.md new file mode 100644 index 000000000..d2d89ff2b --- /dev/null +++ b/.claude/agent-memory/atomic-executor/project_koverage_cobertura_postprocessing_shape.md @@ -0,0 +1,39 @@ +--- +name: koverage-cobertura-postprocessing-shape +description: Invoke-MSTestWithCoverage.ps1 post-processes the Cobertura dump - backslash filenames, classes already merged per file, test packages stripped, root attributes recomputed - so per-file coverage queries pinned to forward slashes or to summing sibling nodes silently match nothing +metadata: + type: project +--- + +`scripts/vscode/Invoke-MSTestWithCoverage.ps1` does not emit a raw `dotnet-coverage` Cobertura +dump. Before writing `-CoverageOutput` it calls `ConvertTo-KoverageCoberturaXml` +(`Invoke-MSTestWithCoverage.Helpers.ps1`) with **no `-PathSeparator` argument**, so four +transformations apply: + +1. **`filename` uses `\`, not `/`.** `-PathSeparator` defaults to + `[System.IO.Path]::DirectorySeparatorChar`, which is `\` on Windows. Attributes read + `TaskMaster\Ribbon\EngineToggleStateCoordinator.cs`. A query pinned to + `TaskMaster/Ribbon/...` matches **zero** rows and a coverage gate built on it reports 0 or + nothing while appearing to run. +2. **`` nodes are already merged per file.** `Merge-CoberturaClassesByFilename` collapses + the `d__N` async state-machine classes and `<>c` closure classes into one `` per + filename and rewrites its `line-rate`/`branch-rate`. Read that attribute directly; the advice to + "sum `lines-covered`/`lines-valid` across all `` elements sharing the filename" applies + only to an unprocessed raw dump. +3. **Test packages are stripped.** `Get-KoverageProjectAllowlist` enumerates every non-`*.Test` + `*.csproj`/`*.vbproj`/`*.fsproj` assembly name under the repo root; packages outside that set are + removed from ``. First-party production projects (including `TaskMaster`) are in. +4. **Root `` attributes are recomputed** after stripping (`line-rate`, `branch-rate`, + `lines-covered`, `lines-valid`, `branches-covered`, `branches-valid`), so the headline figure is + already a production-only denominator — do not re-derive it. + +Also: `$repoRoot` is `Resolve-Path "$PSScriptRoot\..\.."`, i.e. the **worktree** root, and the +`*.Test.dll` glob applies no `\.claude\` filter, so the script is safe to run from an agent +worktree rooted under `.claude/worktrees/`. It hard-fails if `vswhere.exe`, the vswhere-resolved +`Common7\IDE\Extensions\TestPlatform\vstest.console.exe`, or a global `dotnet-coverage` is missing — +note that vstest path differs from the `CommonExtensions\Microsoft\TestWindow` one used for direct +`vstest.console.exe` calls. + +Related: [[project_coverage_delta_reproduce_baseline_counting_method]], +[[project_csharp_canonical_coverage_artifact_conversion]], +[[project_dotnet_coverage_denominator_nondeterminism]]. diff --git a/.claude/agent-memory/atomic-executor/project_missing_vsto_runtime_breaks_baseline_gates.md b/.claude/agent-memory/atomic-executor/project_missing_vsto_runtime_breaks_baseline_gates.md index a67f7054a..b9a94213f 100644 --- a/.claude/agent-memory/atomic-executor/project_missing_vsto_runtime_breaks_baseline_gates.md +++ b/.claude/agent-memory/atomic-executor/project_missing_vsto_runtime_breaks_baseline_gates.md @@ -1,10 +1,23 @@ --- name: missing-vsto-runtime-breaks-baseline-gates -description: On this host the VSTO Office Tools runtime assemblies are absent, so the analyzer and nullable solution builds fail with 4x CS0234 in TaskMaster/ThisAddIn.Designer.cs and UtilitiesCS.Test/TaskMaster.Test never build - collapsing repo-wide coverage +description: HISTORICAL (2026-08-04) - absent VSTO Office Tools assemblies once broke the solution build and collapsed repo-wide coverage; NOT reproducing as of 2026-08-08, so always re-verify by building before citing this metadata: type: project --- +> **RE-VERIFIED 2026-08-08 (issue #505 worktree) — DOES NOT REPRODUCE.** The two named +> assemblies (`Microsoft.Office.Tools.Common.v4.0.Utilities`, +> `Microsoft.Office.Tools.Outlook.v4.0.Utilities`) are still absent from the GAC, the VS +> install, and `Common Files\Microsoft Shared\VSTO` — yet +> `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug "/p:Platform=Any CPU"` after +> `nuget restore TaskMaster.sln` completed **EXIT 0 with zero errors** and produced all +> 18 project outputs, including `TaskMaster.dll` and all **9** `*.Test.dll`. The +> `Microsoft.Office.Tools.*` (non-`v4.0.Utilities`) assemblies present in +> `C:\Windows\Microsoft.NET\assembly\GAC_MSIL` are apparently sufficient for +> `ThisAddIn.Designer.cs` to bind. **Never assert the CS0234/6-assembly failure below from +> memory — build first.** Treat the text that follows as a record of one historical +> environment state. + `msbuild TaskMaster.sln` (both the analyzer gate and the nullable gate) fails on this machine with 4 `CS0234` errors in `TaskMaster/ThisAddIn.Designer.cs`, naming `Microsoft.Office.Tools.Outlook.OutlookAddInBase`, diff --git a/.claude/agent-memory/atomic-executor/project_vs18_build_toolchain_paths.md b/.claude/agent-memory/atomic-executor/project_vs18_build_toolchain_paths.md index 0afb6a2fc..752e241ed 100644 --- a/.claude/agent-memory/atomic-executor/project_vs18_build_toolchain_paths.md +++ b/.claude/agent-memory/atomic-executor/project_vs18_build_toolchain_paths.md @@ -17,4 +17,5 @@ Building/testing the TaskMaster net48 VSTO solution in a fresh worktree on this - Format: csharpier is a local tool v1.x → `dotnet csharpier check .` / `dotnet csharpier format ` (subcommands; bare `dotnet csharpier .` is v0 syntax). Needs `DOTNET_ROOT=.dotnet-sdk` on PATH. - Coverage: `dotnet-coverage` global exe. It re-parses the command string and splits on spaces, so a quoted `"C:\Program Files\...vstest.console.exe"` breaks with `'C:\Program' ... parameter is incorrect`. Use the `--` separator form: `dotnet-coverage collect --output OUT --output-format cobertura -- "$VSTEST" "UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll" /InIsolation`. Cobertura ``/`` elements put `line-rate`/`branch-rate` BEFORE `name`. - Nullable gate (`/p:Nullable=enable /p:TreatWarningsAsErrors=true`): incremental `/t:Build` after an analyzer build is a no-op (0/0). A genuine `/t:Rebuild` surfaces ~34 distinct pre-existing nullable errors confined to vendored `SVGControl.csproj` (see [[project_repo_sdk_and_nullable_rebuild]]); first-party UtilitiesCS/UtilitiesCS.Test are 0. Rebuild-with-dependencies double-reports (34→68) under parallel; use `-m:1` for distinct counts. +- `pwsh -NoProfile -Command` tokenization for MSBuild switches (measured 2026-08-08): `/p:Platform='Any CPU'` and `/flp:'logfile=.log;verbosity=normal'` each collapse to ONE argument with the quotes stripped, and the embedded `;` does NOT split the pwsh statement — the plan-style quoting is safe as written. `/fl /flp:logfile=...` needs the target directory to already exist. Inside a pwsh `-Command` string use `| Out-Null`, never `> /dev/null` (pwsh resolves it as the literal path `C:\dev\null` and the whole statement dies). - Under coverage instrumentation ~17 Deedle/DataFrame/ETL tests (FromDefaultFolder_*, FromArray2D_*, DeedleDoodles, etc.) flake; they pass with 0 failures when instrumentation is off. Pre-existing, unrelated to Folder scoring. diff --git a/.claude/agent-memory/atomic-planner/MEMORY.md b/.claude/agent-memory/atomic-planner/MEMORY.md index adc263acc..469faa2a5 100644 --- a/.claude/agent-memory/atomic-planner/MEMORY.md +++ b/.claude/agent-memory/atomic-planner/MEMORY.md @@ -17,6 +17,7 @@ - [Repo-wide csharpier format breaks zero-diff ACs](csharpier-repowide-format-breaks-zero-diff-acs.md) — scope the mutating pass to the plan's own path list; keep `check .` read-only; re-verify the zero-line diff AFTER formatting - [Embedded-resource fail-proof needs a rebuild gate](embedded-resource-failproof-rebuild-gate.md) — edit → rebuild → assert embedded bytes → `[expect-fail]` run; skipping the assert makes the fail-proof itself vacuous - [#503 ribbon readiness plan seams](project_503_ribbon_readiness_plan_seams.md) — RibbonViewer 487/500 forces a 26-member region move; 6+4 Compile entries; compile-time red + dossier; #504-#508 already promoted +- [#505 toggle-state guards plan seams](project_505_toggle_state_guards_plan_seams.md) — runtime red (no dossier); R4 red between catalog and XML tasks; raw cobertura to gitignored coverage/; #522 CI type-check cmd; manual-verification kind accepted - [CSharpier gate: format not pipe-files](csharpier-format-not-pipe-files-gate.md) — formatting tasks must use `csharpier format` + scoped `csharpier check` exit 0; `pipe-files` is stdout-only/non-enforcing and masked a 500-line overflow in #400 - [#400 partial-class headroom placement](project_400_partial_class_headroom_placement.md) — put new coverage cases in existing `.Part2.cs` `[TestClass] partial` files to keep the 17-class filter/count assertions stable - [Manager AsyncLazy shared seam](project_manager_asynclazy_shared_seam.md) — Globals.AF.Manager is shared across all classifier subsystems; use a key-specific accessor, never retype the dictionary value for one key diff --git a/.claude/agent-memory/atomic-planner/project_505_toggle_state_guards_plan_seams.md b/.claude/agent-memory/atomic-planner/project_505_toggle_state_guards_plan_seams.md new file mode 100644 index 000000000..f616cfb61 --- /dev/null +++ b/.claude/agent-memory/atomic-planner/project_505_toggle_state_guards_plan_seams.md @@ -0,0 +1,22 @@ +--- +name: project-505-toggle-state-guards-plan-seams +description: "#505/#506/#518 bundled full-bug plan facts: runtime red (not compile red), R4 red captured between catalog and XML tasks, coordinator+EngineToggleCatalog seam, raw cobertura to gitignored coverage/, manual-verification evidence kind accepted" +metadata: + type: project +--- + +Plan seams for the #505/#506/#518 bundled `full-bug` delivery (plan.2026-08-08T19-22.md in feature 2026-08-08-ribbon-engine-toggle-state-guards-505). + +**Why:** three causally coupled defects in `RibbonViewer.EngineCommands.cs`; the spec pins a two-guard-shape split (4 toggle sites -> new `EngineToggleStateCoordinator`, 6 command sites -> existing `RunEngineCommandAsync` gate + 6 new `EngineCommandCatalog` entries + XML `getEnabled`). + +**How to apply:** +- Unlike #503 (compile-time red + exception dossier), #505's red is a RUNTIME red: reflection shape-pin tests compile against pre-fix code and fail on execution. No fail-before exception dossier needed when a genuine failing run exists. Reflection-invoke keeps R2 compiling across the signature change. +- R4 (existing catalog-derived XML set-equality tests) has no test-code change; its red is captured as a dedicated `[expect-fail]` run task BETWEEN the catalog-extension task and the XML-edit task. "Land atomically" means same commit, not same task — a captured red between the two edits satisfies AC-15 and the atomicity risk. +- `EngineCommandCatalogTests.ControlIds_ContainsExactlyTheEightEngineBackedControlIds` hard-codes the 8-entry set; extending the catalog requires renaming/extending it (research §8 predicted this). +- The `RibbonViewer(RibbonController)` public ctor is field-assignment-only (`Controller.Try` is inside an uninvoked lambda) — safe to construct in a unit test; `RibbonController()` parameterless ctor exists and #507 tests already use it. +- Raw Cobertura goes to the gitignored `coverage\` dir (`.gitignore` `coverage/*`), never under docs/features (81 MB incident) and never `artifacts/csharp/coverage.xml` (SubagentStop hook hard-codes an 85% floor there vs the real 80% policy). +- `evidence/manual-verification/` is an accepted evidence kind (spec AC-22 requires it verbatim; #503 executed with it; not on the forbidden list). +- Type-check gate is CI's `msbuild /t:Rebuild /m ... /p:TreatWarningsAsErrors=true` WITHOUT `/p:Nullable=enable` (issue #522, known-defective); cite #522 in every type-check task so the executor does not "correct" it back to the CLAUDE.md form. +- `Invoke-MSTestWithCoverage.ps1` applies no `\.claude\` filter and derives repoRoot internally, so it is safe in an agent worktree rooted under `.claude\worktrees\`; expected assembly count 9, and 0 discovered = filter bug, never an empty suite. + +Related: [[project-503-ribbon-readiness-plan-seams]], [[async-state-machine-coverage-aggregation]], [[csharpier-format-not-pipe-files-gate]]. diff --git a/.claude/agent-memory/prd-feature/MEMORY.md b/.claude/agent-memory/prd-feature/MEMORY.md index d20dd3431..c35afb5aa 100644 --- a/.claude/agent-memory/prd-feature/MEMORY.md +++ b/.claude/agent-memory/prd-feature/MEMORY.md @@ -3,3 +3,4 @@ - [Test disposition: grep for old-overload pins](feedback_test_disposition_overload_pins.md) — grep test project for Setup/Verify of retired overloads before marking any test file "unchanged"; loose mocks fail at run time - [AC gates: verify satisfiability + fresh reads](feedback_ac_gates_verify_satisfiability.md) — check baseline evidence before encoding repo-wide coverage floors as blocking AC; re-read spec from disk before reporting tallies - [full-bug means spec.md only](feedback_full_bug_spec_only.md) — never create user-story.md in full-bug mode; the agent's Expected Outputs header conflicts with the AC-tracking skill +- [#522 nullable type-check deviation](project_522_nullable_typecheck_deviation.md) — CLAUDE.md's /p:Nullable=enable gate is defective; bind spec verification to CI's msbuild command and cite #522 diff --git a/.claude/agent-memory/prd-feature/project_522_nullable_typecheck_deviation.md b/.claude/agent-memory/prd-feature/project_522_nullable_typecheck_deviation.md new file mode 100644 index 000000000..d8bd547d5 --- /dev/null +++ b/.claude/agent-memory/prd-feature/project_522_nullable_typecheck_deviation.md @@ -0,0 +1,12 @@ +--- +name: nullable-typecheck-deviation-522 +description: CLAUDE.md's type-check command with /p:Nullable=enable is known-defective (issue #522); specs must bind verification to CI's command and record the deviation explicitly +metadata: + type: project +--- + +The `CLAUDE.md` type-check command `msbuild ... /p:Nullable=enable /p:TreatWarningsAsErrors=true` is known-defective and tracked as issue #522. Nullable is per-file opt-in in this solution (no `TaskMaster/Ribbon/` file carries a `#nullable` pragma; only five `AppGlobals` files do), so forcing `/p:Nullable=enable` solution-wide reports 200-414 errors that are red on `main` regardless of any change. CI (`.github/workflows/ci.yml`) deliberately omits the flag. + +**Why:** Encoding the CLAUDE.md command verbatim into a spec's toolchain AC creates an unsatisfiable dead gate (same failure class as [[ac-gates-verify-satisfiability]]). First recorded in the #505/#506/#518 spec (2026-08-08). + +**How to apply:** In any spec's Verification section and toolchain AC, use CI's actual command — `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true` — and record it as a deliberate, documented deviation from CLAUDE.md with the #522 citation, so reviewers do not flag it as non-compliance. Re-verify against CLAUDE.md before relying on this: once #522 is fixed, this memory is stale and should be removed. diff --git a/.claude/agent-memory/task-researcher/MEMORY.md b/.claude/agent-memory/task-researcher/MEMORY.md index f6e6c9aa9..4c6abdfa7 100644 --- a/.claude/agent-memory/task-researcher/MEMORY.md +++ b/.claude/agent-memory/task-researcher/MEMORY.md @@ -27,3 +27,4 @@ - [winforms-pump-seam-230](project_winforms_pump_seam_230.md) — #230: WinFormsPumpHost design decided; CreateAsync factory-seam gap; InitializeWebViewAsync stays exempt; 19 -> 11 max (2026-08-07) - [qfc438-search-focus-steal](project_qfc438_search_focus_steal.md) — #438: TWO focus-steal mechanisms (open _focusPending + close _focusAnchor via per-keystroke Clear); CancelSelector emits no SelectionChanged -> stale _selectedFolder (2026-08-08) - [ribbon-engine-readiness-503](project_ribbon_engine_readiness_503.md) — #503: whole TaskMaster Ribbon layer is coverage-excluded; net481 blocks default interface members; 5 orphan onAction callbacks in RibbonExplorer.xml (2026-08-08) +- [ribbon-toggle-state-guards-505](project_ribbon_toggle_state_guards_505.md) — #505/#506/#518: toggle(config) vs command(InboxEngines) guard asymmetry; catalog<->XML set-equality tests; MessageBox in notify sink blocks viewer-level tests (2026-08-08) diff --git a/.claude/agent-memory/task-researcher/project_ribbon_toggle_state_guards_505.md b/.claude/agent-memory/task-researcher/project_ribbon_toggle_state_guards_505.md new file mode 100644 index 000000000..1ac44e31d --- /dev/null +++ b/.claude/agent-memory/task-researcher/project_ribbon_toggle_state_guards_505.md @@ -0,0 +1,21 @@ +--- +name: ribbon-toggle-state-guards-505 +description: "#505/#506/#518 bundle research: toggle vs command guard asymmetry (config vs InboxEngines), EngineCommandCatalog<->XML test coupling forces atomic changes, MessageBox in NotifyEngineCommandNotReady blocks viewer-level tests" +metadata: + type: project +--- + +Bundled bug research (#505 async getPressed, #506 fire-and-forget toggle, #518 ten unguarded `Controller.Engines.` sites), 2026-08-08. Recommended: new host-neutral `EngineToggleStateCoordinator` (last-known-state cache, lazy prime-on-read, update-cache-then-invalidate) for the 4 toggle/getPressed sites; route the 6 ShowDiskDialog/ShowSaveInfo sites through the existing #503 `RunEngineCommandAsync` gate by adding 6 button ids to `EngineCommandCatalog`. + +**Why (non-obvious facts, expensive to rediscover):** + +1. **Guard asymmetry is semantic, not stylistic.** `ToggleEngineAsync`/`EngineActiveAsync` operate on `Globals.AF.Manager.Configuration` (`AppItemEngines.cs:92-109`), while `InitAsync` filters `config.Value.Engine` before populating `InboxEngines` — so a readiness gate keyed on `InboxEngines` would permanently block re-enabling a disabled engine. `ShowSaveInfo`/`ShowDiskDialog` DO require the `InboxEngines` key (no-op otherwise), so the readiness gate is exactly right for them. +2. **`EngineCommandCatalog` membership is load-bearing in three `RibbonExplorerXmlTests`:** every catalog id must declare `getEnabled="EngineCommand_GetEnabled"` in the XML (set-EQUALITY both ways) and must be a `button` element. Adding catalog ids forces XML edits in the same task; `checkBox` ids (the two enable toggles) can never be added. +3. **`NotifyEngineCommandNotReady` calls `MessageBox.Show`** (`RibbonController.EngineCommands.cs:100`), so any unit test that drives a gate-closed path through viewer/controller glue hangs vstest. Behavioral tests must sit at the seam with injected sinks; viewer-level red tests are limited to reflection pins and the getPressed no-throw repro. +4. **Signature pins must compare parameter types by `Type.FullName == "Microsoft.Office.Core.IRibbonControl"`** — TaskMaster.Test has no Office PIA compile reference (`RibbonExplorerXmlTests.cs:280-287`). +5. `AsyncLazy` (`UtilitiesCS/ReusableTypeClasses/AsyncLazy/AsyncLazy.cs`) has NO non-triggering completed-value probe, so "read config synchronously when materialized" is not implementable without new `IAppItemEngines` surface (which bodies in the excluded `AppItemEngines` — the net481 no-DIM trap from [[ribbon-engine-readiness-503]]). +6. XML control ids diverge from method names: the "current location" buttons are `GetSaveState`/`TriageGetSaveState`, not `*SaveLocation*`; toggles are `SpamBayesEnabledToggle`/`TriageEnabledToggle` (checkBox). `SpamBayes.GroupName == "Spam"` (SpamBayes.cs:328). + +**How to apply:** for any future ribbon guard work, first ask whether the operation needs the engine *instance* (InboxEngines-keyed gate) or the engine *configuration* (availability-only guard + cached read); and check whether the proposed test path can reach a MessageBox before writing viewer-level tests. + +Research artifact: `docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/research/2026-08-08T19-30-ribbon-engine-toggle-state-guards-research.md` diff --git a/docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/csharpier-check.2026-08-08T20-43.md b/docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/csharpier-check.2026-08-08T20-43.md new file mode 100644 index 000000000..f90da77a5 --- /dev/null +++ b/docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/csharpier-check.2026-08-08T20-43.md @@ -0,0 +1,27 @@ +# P0-T6 — Merge-Base CSharpier State (read-only) + +Timestamp: 2026-08-08T20-43 + +Command: + +``` +pwsh -NoProfile -Command "Set-Location ''; & 'C:\Users\DanMoisan\.dotnet\tools\csharpier.exe' check ." +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +Checked 1512 files in 3601ms. +``` + +The merge-base tree is fully CSharpier-clean: 1512 files checked, **zero** reported unformatted. + +**Baseline unformatted-file set: EMPTY.** This is the comparison basis for P5-T2, which therefore +requires `EXIT_CODE: 0` on the repo-wide check — any file reported unformatted at P5-T2 is a +regression introduced by this change and restarts the phase at P5-T1. + +`csharpier format` was not run in this task (read-only baseline, per the task text). + +Binary outcome: PASS. diff --git a/docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/file-line-counts.2026-08-08T20-46.md b/docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/file-line-counts.2026-08-08T20-46.md new file mode 100644 index 000000000..b8acbd8b2 --- /dev/null +++ b/docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/evidence/baseline/file-line-counts.2026-08-08T20-46.md @@ -0,0 +1,56 @@ +# P0-T11 — Branch-Head Line Counts (ADVISORY) + +> **ADVISORY.** The authoritative 500-line audit is **P5-T3**, measured after the final format +> pass. This artifact records the pre-change state only. + +Timestamp: 2026-08-08T20-46 + +Command (as written in the plan): + +``` +pwsh -NoProfile -Command "Set-Location ''; '' | ForEach-Object { '{0}={1}' -f $_, (Get-Content $_ | Measure-Object -Line).Lines }" +``` + +EXIT_CODE: 0 + +(`$LASTEXITCODE` was unset — the command invokes only cmdlets. The `pwsh` process exited 0.) + +## Output Summary + +| Path | `Measure-Object -Line` | True physical lines | Group | +|---|---|---|---| +| `TaskMaster\Ribbon\EngineCommandCatalog.cs` | 83 | **88** | scope lock (modified) | +| `TaskMaster\Ribbon\RibbonController.EngineCommands.cs` | 97 | **103** | scope lock (modified) | +| `TaskMaster\Ribbon\RibbonViewer.EngineCommands.cs` | 168 | **207** | scope lock (modified) | +| `TaskMaster\Ribbon\RibbonExplorer.xml` | 539 | **539** | scope lock (modified) — see note | +| `TaskMaster.Test\Ribbon\EngineCommandCatalogTests.cs` | 103 | **116** | scope lock (modified) | +| `TaskMaster.Test\Ribbon\RibbonExplorerXmlTests.cs` | 288 | **318** | scope lock (modified) | +| `TaskMaster\Ribbon\RibbonController.Intelligence.cs` | 360 | **412** | protected, zero-line diff | +| `TaskMaster\AppGlobals\AppItemEngines.cs` | 263 | **286** | protected, zero-line diff | +| `UtilitiesCS\Interfaces\IGlobals\IAppItemEngines.cs` | 16 | **18** | protected, zero-line diff | +| `TaskMaster\ThisAddIn.cs` | 271 | **307** | protected, zero-line diff | +| `TaskMaster\Ribbon\RibbonViewer.cs` | 299 | **388** | protected, zero-line diff | + +### `RibbonExplorer.xml` — accepted pre-existing overage + +`TaskMaster\Ribbon\RibbonExplorer.xml` is **539 lines**, above the 500-line cap. This is a +**pre-existing accepted overage** carried forward from #503: the file is a declarative embedded UI +resource (the Office CustomUI document), not production or test code, and AC-21 grants it an +explicit carve-out. This delivery adds only six `getEnabled` attributes to existing `